diff --git a/L1.md b/L1.md new file mode 100644 index 0000000..e5dd9e9 --- /dev/null +++ b/L1.md @@ -0,0 +1,57 @@ +# 第一章:伤害计算函数(getDamageInfo) + +要求:读懂脚本编辑 - 伤害计算函数(getDamageInfo)的全部脚本代码(无需理解支援的实现)。 + +回答如下几个问题: + +1. 如下几个局部变量分别是什么意义? + + `mon_atk`, `init_damage`, `per_damage`, `hero_per_damage`, `turn`, `damage` + +2. 如何理解下面这几句话? + + `if (core.hasSpecial(mon_special, 2)) per_damage = mon_atk;` + + `var turn = Math.ceil(mon_hp / hero_per_damage);` + + `var damage = init_damage + (turn - 1) * per_damage + turn * counterDamage;` + +3. 负伤在哪里实现的? + +依次实现如下几个怪物属性(仅允许修改`getDamageInfo`函数): + +1. **闪避(编号31):** 受到伤害降低40%。 + +2. **穿刺(编号32):** 无视角色70%防御力。 + +3. **冰冻(编号33):** 怪物首先冰冻角色3回合,冰冻期间每回合额外造成角色护盾的20%伤害。 + +4. **中子束(编号34):** 每回合普攻两次,魔攻一次。 + +5. **残暴斩杀(编号35):** 战斗开始时,如果角色血量不大于怪物血量的200%,则直接暴毙。 + +6. **窥血为攻(编号36):** 战斗开始时,自身攻击力变为角色当前生命值的10%,向下取整。 + +7. **匙之力(编号37):** 角色身上每存在一把黄钥匙,最终伤害提升5%;蓝钥匙视为三把黄钥匙,红钥匙视为九把黄钥匙,线性叠加。 + +8. **崩甲(编号38):** 怪物每回合附加勇士战斗开始时的护盾数值的0.1倍作为伤害。 + +9. **混乱(编号39):** 战斗中,勇士攻防互换。 + +10. **强击(编号40):** 怪物第一回合三倍攻击。 + +11. **暗影庇护(编号41):** 处于无敌状态,但每回合损耗自身2%的最大生命值。 + +再依次实现如下几个角色技能: + +1. 角色每回合回复`flag:x1`%倍护盾的生命值。(如,`flag:x1`是10时,每回合回复10%护盾值的生命) + +2. 角色每回合造成和受到的伤害均提升`flag:x2`%。(如,`flag:x2`为10时,每回合造成和受到伤害都提升10%) + +3. 角色攻击在战斗时减少`flag:x3`,防御力增加`flag:x3`。 + +4. 角色无视怪物的`flag:x4`%的防御力。(如,`flag:x4`是10时,无视怪物的10%防御力) + +5. 角色额外抢攻`flag:x5`回合。 + +[点此查看答案](L1_answer) diff --git a/L1_answer.md b/L1_answer.md new file mode 100644 index 0000000..2619e75 --- /dev/null +++ b/L1_answer.md @@ -0,0 +1,244 @@ +```js +function (enemy, hero, x, y, 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'), + origin_hero_hp = core.getStatusOrDefault(hero, 'hp'), + origin_hero_atk = core.getStatusOrDefault(hero, 'atk'), + origin_hero_def = core.getStatusOrDefault(hero, 'def'); + + // 勇士的负属性都按0计算 + hero_hp = Math.max(0, hero_hp); + hero_atk = Math.max(0, hero_atk); + hero_def = Math.max(0, hero_def); + hero_mdef = Math.max(0, hero_mdef); + + // 怪物的各项数据 + // 对坚固模仿等处理扔到了脚本编辑-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_special = enemyInfo.special; + + // 混乱(编号39):战斗中,勇士攻防互换。 + if (core.hasSpecial(mon_special, 39)) { + var temp = hero_atk; + hero_atk = hero_def; + hero_def = temp; + } + + // 残暴斩杀(编号35):战斗开始时,如果角色血量不大于怪物血量的200%,则直接暴毙。 + if (core.hasSpecial(mon_special, 35) && hero_hp < mon_hp * 2) { + return null; + } + + // 窥血为攻(编号36):战斗开始时,自身攻击力变为角色当前生命值的10%,向下取整。 + if (core.hasSpecial(mon_special, 36)) { + mon_atk = Math.floor(hero_hp / 10); + } + + // 技能3:角色攻击在战斗时减少`flag:x3`,防御力增加`flag:x3`。 + if (core.getFlag('skill', 0) == 3) { + // 注意这里直接改变的面板攻击力(经过装备增幅后的)而不是基础攻击力。 + hero_atk -= core.getFlag('x3', 0); + hero_def += core.getFlag('x3', 0); + } + + // 技能4:角色无视怪物的`flag:x4`%的防御力。(如,`flag:x4`是10时,无视怪物的10%防御力) + if (core.getFlag('skill', 0) == 4) { + mon_def -= Math.floor(core.getFlag('x4', 0) / 100); + } + + // 如果是无敌属性,且勇士未持有十字架 + if (core.hasSpecial(mon_special, 20) && !core.hasItem("cross")) + return null; // 不可战斗 + + // 战前造成的额外伤害(可被护盾抵消) + var init_damage = 0; + + // 吸血 + if (core.hasSpecial(mon_special, 11)) { + var vampire_damage = hero_hp * enemy.value; + + // 如果有神圣盾免疫吸血等可以在这里写 + // 也可以用hasItem和hasEquip来判定装备 + // if (core.hasFlag('shield5')) vampire_damage = 0; + + vampire_damage = Math.floor(vampire_damage) || 0; + // 加到自身 + if (enemy.add) // 如果加到自身 + mon_hp += vampire_damage; + + init_damage += vampire_damage; + } + + // 每回合怪物对勇士造成的战斗伤害 + var per_damage = mon_atk - hero_def; + // 魔攻:战斗伤害就是怪物攻击力 + if (core.hasSpecial(mon_special, 2)) per_damage = mon_atk; + // 穿刺(编号32):无视角色70%防御力。 + if (core.hasSpecial(mon_special, 32)) per_damage = Math.floor(mon_atk - 0.3 * hero_def); + // 战斗伤害不能为负值 + if (per_damage < 0) per_damage = 0; + + // 中子束(编号34):每回合普攻两次,魔攻一次。 + if (core.hasSpecial(mon_special, 34)) { + per_damage *= 2; + per_damage += mon_atk; + } + + // 崩甲(编号38):怪物每回合附加勇士战斗开始时的护盾数值的0.1倍作为伤害。 + if (core.hasSpecial(mon_special, 38)) { + per_damage += Math.floor(hero_mdef * 0.1); + } + + // 2连击 & 3连击 & N连击 + if (core.hasSpecial(mon_special, 4)) per_damage *= 2; + if (core.hasSpecial(mon_special, 5)) per_damage *= 3; + if (core.hasSpecial(mon_special, 6)) per_damage *= (enemy.n || 4); + + // 每回合的反击伤害;反击是按照勇士的攻击次数来计算回合 + var counterDamage = 0; + if (core.hasSpecial(mon_special, 8)) + counterDamage += Math.floor((enemy.atkValue || core.values.counterAttack) * hero_atk); + + // 先攻 + if (core.hasSpecial(mon_special, 1)) init_damage += per_damage; + + // 破甲 + if (core.hasSpecial(mon_special, 7)) + init_damage += Math.floor((enemy.defValue || core.values.breakArmor) * hero_def); + + // 净化 + if (core.hasSpecial(mon_special, 9)) + init_damage += Math.floor((enemy.n || core.values.purify) * hero_mdef); + + // 冰冻(编号33):怪物首先冰冻角色3回合,冰冻期间每回合额外造成角色护盾的20%伤害。 + if (core.hasSpecial(mon_special, 33)) { + init_damage += Math.floor(3 * (per_damage + 0.2 * hero_mdef)); + } + + // 勇士每回合对怪物造成的伤害 + var hero_per_damage = Math.max(hero_atk - mon_def, 0); + + // 技能2:角色每回合造成和受到的伤害均提升`flag:x2`%。(如,`flag:x2`为10时,每回合造成和受到伤害都提升10%) + if (core.getFlag("skill", 0) == 2) { + per_damage += Math.floor(per_damage * core.getFlag('x2', 0) / 100); + hero_per_damage += Math.floor(hero_per_damage * core.getFlag('x2', 0) / 100); + } + + // 闪避(编号31):受到伤害降低40%。 + if (core.hasSpecial(mon_special, 31)) { + hero_per_damage = Math.floor(hero_per_damage * 0.6); + } + + // 如果没有破防,则不可战斗 + if (hero_per_damage <= 0) return null; + + // 技能5:角色额外抢攻`flag:x5`回合。 + if (core.getFlag("skill", 0) == 5) { + mon_hp -= core.getFlag('x5', 0) * hero_per_damage; + // 判定是否已经秒杀 -> 注意mon_hp不能小于等于0否则回合计算会出问题 + if (mon_hp <= 0) mon_hp = 1; + } + + // 暗影庇护(编号41):处于无敌状态,但每回合损耗自身2%的最大生命值。 + if (core.hasSpecial(mon_special, 41)) { + hero_per_damage = Math.floor(mon_hp * 0.02); + } + + // 强击(编号40):怪物第一回合三倍攻击。 + // 注意:需要判定角色是否秒杀怪物 + if (core.hasSpecial(mon_special, 40) && hero_per_damage < mon_hp) { + init_damage += 2 * mon_atk; + } + + + // 勇士的攻击回合数;为怪物生命除以每回合伤害向上取整 + var turn = Math.ceil(mon_hp / hero_per_damage); + + // ------ 支援 ----- // + // 这个递归最好想明白为什么,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) { + if (!guard_before_current_enemy) { // --- 先打当前怪物,记录当前回合数 + core.setFlag("__extraTurn__", turn); + } + // 获得那些怪物组成小队战斗 + for (var i = 0; i < guards.length; i++) { + var gx = guards[i][0], + gy = guards[i][1], + gid = guards[i][2]; + // 递归计算支援怪伤害信息,这里不传x,y保证不会重复调用 + // 这里的mdef传0,因为护盾应该只会被计算一次 + var info = core.enemys.getDamageInfo(core.material.enemys[gid], { hp: origin_hero_hp, atk: origin_hero_atk, def: origin_hero_def, mdef: 0 }); + if (info == null) { // 小队中任何一个怪物不可战斗,直接返回null + core.removeFlag("__extraTurn__"); + return null; + } + // 已经进行的回合数 + core.setFlag("__extraTurn__", info.turn); + init_damage += info.damage; + } + if (guard_before_current_enemy) { // --- 先打支援怪物,增加当前回合数 + turn += core.getFlag("__extraTurn__", 0); + } + } + core.removeFlag("__extraTurn__"); + // ------ 支援END ------ // + + // 最终伤害:初始伤害 + 怪物对勇士造成的伤害 + 反击伤害 + var damage = init_damage + (turn - 1) * per_damage + turn * counterDamage; + // 再扣去护盾 + damage -= hero_mdef; + + // 匙之力(编号37):角色身上每存在一把黄钥匙,最终伤害提升5%;蓝钥匙视为三把黄钥匙,红钥匙视为九把黄钥匙,线性叠加。 + // 需要判定是否负伤 + if (core.hasSpecial(mon_special, 37) && damage > 0) { + var cnt = core.itemCount("yellowKey") + 3 * core.itemCount("blueKey") + 9 * core.itemCount("redKey"); + damage += Math.floor(damage * 0.05 * cnt); + } + + // 检查是否允许负伤 + 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; + } + + // 技能1:角色每回合回复`flag:x1`%倍护盾的生命值。(如,`flag:x1`是10时,每回合回复10%护盾值的生命) + if (core.getFlag("skill", 0) == 1) { + damage -= Math.floor(core.getFlag('x1', 0) / 100 * hero_mdef * turn); + } + + return { + "mon_hp": Math.floor(mon_hp), + "mon_atk": Math.floor(mon_atk), + "mon_def": Math.floor(mon_def), + "init_damage": Math.floor(init_damage), + "per_damage": Math.floor(per_damage), + "hero_per_damage": Math.floor(hero_per_damage), + "turn": Math.floor(turn), + "damage": Math.floor(damage) + }; +} +``` diff --git a/L2.md b/L2.md new file mode 100644 index 0000000..f4423ff --- /dev/null +++ b/L2.md @@ -0,0 +1,102 @@ +# 第二章:怪物特殊属性定义(getSpecials),表格配置 + +要求:读懂脚本编辑 - 怪物特殊属性定义(getSpecials)的实现。理解如何定义一个新的怪物属性。同时,还需要理解如何给怪物表格增加新项目。 + +回答如下问题: + +1. 如何定义一个新的怪物属性? + +2. 如何取得当前怪物的各项数值? + +3. 第五项参数在哪些情况下写1?为什么需要? + +4. 如何给怪物表格定义新项目?如何在伤害计算等地方取用定义的新项目? + +5. 当前,有一部分怪物属性使用了相同的表格项,如阻激夹域都使用了`value`等;这样无法同时给怪物增加吸血和领域两个属性。那应该怎么做才能同时支持这两项? + +6. 理解《咕工智障》的下述怪物特殊属性并回答: + - 如果一个该属性怪物设置了`hpValue = 10, range = 2`,那么怪物手册中显示该怪物的特殊属性名和描述是什么? + - 如果一个该属性怪物设置了`hpValue = -20, defValue = 30`,那么怪物手册中显示该怪物的特殊属性名和描述是什么? + - 如果一个该属性怪物设置了`hpValue = 10, atkValue = 20, defValue = 30, add = true`,那么怪物手册中显示该怪物的特殊属性名和描述是什么? + - 如果怪物手册中的描述是`周围方形范围内5格的友军生命提升10%,攻击降低20%,线性叠加`,那么该怪物的哪些项被设置了成了多少?该怪物的特殊属性名是什么? + - 如果怪物手册中的描述是`周围十字范围内3格的友军生命提升20%,攻击提升10%,防御提升5%,不可叠加`,那么该怪物的哪些项被设置了成了多少?该怪物的特殊属性名是什么? + + ```js + [25, function (enemy) { + if (enemy.auraName) return enemy.auraName; + if (enemy.hpValue && enemy.atkValue && enemy.defValue) return "魔力光环"; + if (enemy.hpValue) { + if (enemy.hpValue > 0) return "活力光环"; + return "懒散光环"; + } + if (enemy.atkValue) { + if (enemy.atkValue > 0) return "狂暴光环"; + return "静谧光环"; + } + if (enemy.defValue) { + if (enemy.defValue > 0) return "坚毅光环"; + return "软弱光环"; + } + return "光环"; + }, function (enemy) { + var str; + if (!enemy.range) { + str = "同楼层所有友军"; + } else { + str = "周围" + (enemy.zoneSquare ? "方形" : "十字") + + "范围内\r[yellow]" + (enemy.range || 1) + "\r格的友军"; + } + if (enemy.hpValue) { + if (enemy.hpValue > 0) str += "生命提升\r[yellow]" + enemy.hpValue + "%\r,"; + else str += "生命降低\r[yellow]" + (-enemy.hpValue) + "%\r,"; + } + if (enemy.atkValue) { + if (enemy.atkValue > 0) str += "攻击提升\r[yellow]" + enemy.atkValue + "%\r,"; + else str += "攻击降低\r[yellow]" + (-enemy.atkValue) + "%\r,"; + } + if (enemy.defValue) { + if (enemy.defValue > 0) str += "防御提升\r[yellow]" + enemy.defValue + "%\r,"; + else str += "防御降低\r[yellow]" + (-enemy.defValue) + "%\r,"; + } + str += (enemy.add ? "线性叠加" : "不可叠加"); + return str; + }, "#e6e099", 1], + ``` + +7. 理解《咕工智障》的下述怪物特殊属性并回答: + - 吸血数值是如何计算在怪物手册之中的? + - 如何根据角色当前拥有的道具或者变量来修改怪物属性描述? + - 如果要让简单难度下,怪物吸血值减半,特殊属性定义中应该怎么修改? + + ```js + [11, "吸血", function (enemy) { + var str = "\r[\#e525ff]【红海技能】\r战斗前,首先吸取对方的" + + Math.floor(100 * enemy.value || 0) + "%生命(约" + + Math.floor((enemy.value || 0) * core.getRealStatus('hp')) + "点)作为伤害" + + (enemy.add ? ",并把伤害数值加到自身生命上" : ""); + if (core.hasItem("I_noVampire")) str += "\r[yellow](对你无效)\r"; + return str; + }, "#ff00d2"], + ``` + +8. 定义同时满足如下条件的特殊属性(编号`28`),并和《咕工智障》的写法进行比较。 + - 当怪物的 `value28 > 0` 时,特殊属性名为`迅捷光环` + - 当怪物的 `value28 <= 0` 时,特殊属性名为`怠惰光环` + - 当难度为简单(`flag:hard = 1`)时,描述为`此技能在简单难度下无效!` + - 当角色拥有道具 `I123` 时,描述为`此技能已被魔杖抵消!` + - 当怪物的 `range > 0` 时,描述存在`周围[方形/十字]范围内[?]格的友军`;其中这`[]`里面内容由怪物的 `zoneSquare` 和 `range` 决定。 + - 当怪物的 `range = null` 时,描述存在`当前楼层的友军` + - 当怪物的 `value28 > 0` 时,描述存在`先攻X回合`,其中X由`value28`决定(如,`value28 = 5`时,描述存在`先攻5回合`) + - 当怪物的 `value28 <= 0` 时,描述存在`被先攻X回合`,其中X由`value28`决定(如,`value28 = -5`时,描述存在`被先攻5回合`) + +9. 定义同时满足如下条件的特殊属性(编号`30`),并和《咕工智障》的写法进行比较。 + - 特殊属性名:匙之力 + - 当怪物的 `range > 0` 时,描述存在`周围[方形/十字]范围内[?]格内`;其中这`[]`里面内容由怪物的 `zoneSquare` 和 `range` 决定。 + - 当怪物的 `range = null` 时,描述存在`同楼层内` + - 描述存在 `每存在一个黄钥匙,生命增加X点,攻击增加Y点,防御增加Z点`,其中`X, Y, Z`由怪物的`value, atkValue, defValue`决定。如果其中某一项为0或null则不显示对应项。(例如,如果`value = 30, defValue = 20`则只显示`生命增加30点,防御增加20点`) + +10. 将阻击、激光、领域、吸血所使用的特殊属性值(当前均为`value`)分离,并实现一个怪物同时拥有`阻击伤害100点,激光伤害50点,领域伤害200点,且吸血20%`这四个特殊属性。 + - 需在怪物手册正确显示 + - 需正确实现对应的效果(需要修改`伤害计算(getDamageInfo)和阻激夹域伤害(updateCheckBlock)`两个函数) + +[点此查看答案](L2_answer) diff --git a/L2_answer.md b/L2_answer.md new file mode 100644 index 0000000..227fd1c --- /dev/null +++ b/L2_answer.md @@ -0,0 +1,153 @@ +1. 在怪物特殊属性中增加一行,分别定义特殊属性的数字、名字、描述、颜色,以及是否是地图类技能(如光环)。 + +2. 使用`function (enemy) {}`,里面可以取用`enemy.xxx`调用数值;例如`enemy.value`可以调用怪物的value值。 + +3. 仅在地图类技能时需要写1。当怪物的该项为1时,意味着该怪物的技能是和地图上其他图块有关的(而不是怪物自身独立的)。例如,先攻魔攻这些技能就是自身独立(和地图上其他图块无关),而光环,匙之力(根据周边钥匙数量提升属性)等这些技能就是与地图上其他怪物有关的。 + +4. 点击「配置表格」,找到怪物相关项目,并照葫芦画瓢即可。常见写法如下。 + + ```js + "value": { + "_leaf": true, + "_type": "textarea", + "_docs": "特殊属性数值", + "_data": "特殊属性的数值\n如:领域/阻激/激光怪的伤害值;吸血怪的吸血比例;光环怪增加生命的比例" + }, + ``` + +5. 可定义不同项目如v1, v2, v3等分别表示伤害值;然后修改特殊描述和实际效果以取用v1, v2, v3等来代替原来的value。参见第10题解答。 + +6. + - 活力光环;周围十字范围内2格的友军生命提升10%,不可叠加。 + - 懒散光环;同楼层所有友军生命降低20%,防御提升30%,不可叠加。(注意,不会显示坚毅光环!) + - 魔力光环:同楼层所有友军生命提升10%,攻击提升20%,防御提升30%,线性叠加。 + - range=5, zoneSquare = true, hpValue=10, atkValue=-20, add=true;活力光环。 + - range=3, hpValue = 20, atkValue = 10, defValue = 5;魔力光环。 + +7. + - 直接使用`enemy.value`获得当前吸血比例,乘以玩家当前生命值`core.getRealStatus('hp')`获得吸血数值,并取整再显示。 + - 可以通过额外的if来修改具体的描述。 + - 可以在简单难度下将value减半以达到效果。 + + ```js + [11, "吸血", function (enemy) { + var value = enemy.value || 0; // 怪物吸血比例 + if (flags.hard == 1) value /= 2; // 简单难度下吸血值减半 + // 使用局部变量 value 代替 enemy.value + var str = "\r[\#e525ff]【红海技能】\r战斗前,首先吸取对方的" + + Math.floor(100 * value) + "%生命(约" + + Math.floor(value * core.getRealStatus('hp')) + "点)作为伤害" + + (enemy.add ? ",并把伤害数值加到自身生命上" : ""); + if (core.hasItem("I_noVampire")) str += "\r[yellow](对你无效)\r"; + return str; + }, "#ff00d2"], + ``` + +8. + ```js + [28, function (enemy) { + if (enemy.value28 > 0) return "迅捷光环"; + return "怠惰光环"; + }, function (enemy) { + var str; + // 直接 return 可以不显示具体技能内容。 + if (flags.hard == 1) return '此技能在简单难度下无效!'; + if (core.hasItem('I123')) return '此技能已被魔杖抵消!'; + if (!enemy.range) { + str = "同楼层所有友军"; + } else { + str = "周围" + (enemy.zoneSquare ? "方形" : "十字") + "范围内\r[yellow]" + (enemy.range || 1) + "\r格的友军"; + } + if (enemy.value28 > 0) { + str += "先攻\r[yellow]" + (enemy.value28 || 0) + "\r回合。"; + } else { + str += "被先攻\r[yellow]" + (-enemy.value28 || 0) + "\r回合。"; + } + return str; + }, "#00dd00", 1], + ``` + +9. + ```js + [30, "匙之力", function (enemy) { + var str = ""; + if (enemy.range) { + str += "周围" + (enemy.zoneSquare ? "方形" : "十字") + "范围内\r[yellow]" + enemy.range + "\r格,每存在一把黄钥匙,"; + } + else str += "同楼层每存在一把黄钥匙,"; + if (enemy.value) str += "生命增加\r[yellow]" + enemy.value + "\r点,"; + if (enemy.atkValue) str += "攻击增加\r[yellow]" + enemy.atkValue + "\r点,"; + if (enemy.defValue) str += "防御增加\r[yellow]" + enemy.defValue + "\r点,"; + str += "线性叠加。1把蓝钥匙=3把黄钥匙,1把红钥匙=10把黄钥匙。"; + return str; + }, "#A0A000", 1], + ``` + +10. 使用配置表格增设如下项目(也可以使用更好的名称如laserValue): + ```js + "v1": { + "_leaf": true, + "_type": "textarea", + "_docs": "阻击伤害" + }, + "v2": { + "_leaf": true, + "_type": "textarea", + "_docs": "激光伤害" + }, + "v3": { + "_leaf": true, + "_type": "textarea", + "_docs": "领域伤害" + }, + "v4": { + "_leaf": true, + "_type": "textarea", + "_docs": "吸血比例" + }, + ``` + 然后,特殊描述可以如下修改(分别使用v1, v2, v3, v4代替value): + ```js + [11, "吸血", function (enemy) { return "战斗前,怪物首先吸取角色的" + Math.floor(100 * enemy.v4 || 0) + "%生命(约" + Math.floor((enemy.v4 || 0) * core.getStatus('hp')) + "点)作为伤害" + (enemy.add ? ",并把伤害数值加到自身生命上" : ""); }, "#dd4448"], + [15, "领域", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "范围内" + (enemy.range || 1) + "格时自动减生命" + (enemy.v3 || 0) + "点"; }, "#c677dd"], + [18, "阻击", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "时自动减生命" + (enemy.v1 || 0) + "点,同时怪物后退一格"; }, "#8888e6"], + [24, "激光", function (enemy) { return "经过怪物同行或同列时自动减生命" + (enemy.v2 || 0) + "点"; }, "#dda0dd"], + ``` + 最后,修改实际效果。 + ```js + // 吸血:getDamageInfo(节选) + if (core.hasSpecial(mon_special, 11)) { + /** !!使用enemy.v4替代enemy.value!! **/ + var vampire_damage = hero_hp * enemy.v4; + + // 如果有神圣盾免疫吸血等可以在这里写 + // 也可以用hasItem和hasEquip来判定装备 + // if (core.hasFlag('shield5')) vampire_damage = 0; + + vampire_damage = Math.floor(vampire_damage) || 0; + // 加到自身 + if (enemy.add) // 如果加到自身 + mon_hp += vampire_damage; + + init_damage += vampire_damage; + } + ``` + ```js + // 领域:updateCheckBlock(节选) + for (var dx = -range; dx <= range; dx++) { + for (var dy = -range; dy <= range; dy++) { + if (dx == 0 && dy == 0) continue; + var nx = x + dx, + ny = y + dy, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + // 如果是十字领域,则还需要满足 |dx|+|dy|<=range + if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; + /** !!使用enemy.v3替代enemy.value!! **/ + damage[currloc] = (damage[currloc] || 0) + (enemy.v3 || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["领域伤害"] = true; + } + } + ``` + 激光和阻击同领域实现。 diff --git a/L3.md b/L3.md new file mode 100644 index 0000000..548eb2a --- /dev/null +++ b/L3.md @@ -0,0 +1,38 @@ +# 第三章:怪物真实属性获取(getEnemyInfo) + +要求:读懂脚本编辑 - 怪物真实属性(getEnemyInfo)的实现(无需理解支援)。理解如何动态修改怪物的三维,理解光环效果的实现并知晓如何新增光环效果,理解如何定义新变量并在getDamageInfo中取用。 + +回答如下问题: + +1. 为什么要存在`getEnemyInfo`函数以获得怪物真实属性?所有东西都写在`getDamageInfo`里面不好吗? + +2. 实现**仿攻(编号45)**:怪物攻击不低于勇士攻击。 + +3. 实现**窥血为攻(编号36)**: 战斗开始时,自身攻击力变为角色当前生命值的10%,向下取整。这种写法比第一章中在`getDamageInfo`中写法效果更好,为什么? + +4. 仅修改`getEnemyInfo`,实现一个怪物`E345`满足: + - 简单难度(flag:hard==1)下,无特殊属性。 + - 普通难度(flag:hard==2)下,拥有先攻属性。 + - 困难难度(flag:hard==3)下,拥有三连击属性。 + - 噩梦难度(flag:hard==4)下,同时拥有先攻、魔攻、二连击属性。 + - 如果玩家拥有道具`I123`,则直接无视此怪物所有属性。 + +5. 仅修改`getEnemyInfo`,实现一个道具`I234`:**当玩家持有此道具时,怪物的血攻防均降低10%。** + +6. 仅修改`getEnemyInfo`,实现特殊属性 **「上位压制」(特殊编号46)**:玩家每低于怪物一个等级(通过`core.getStatus('lv')`获得当前玩家等级,通过`enemy.level`获得该怪物的等级),怪物三维提升10%;玩家每高于怪物一个等级,怪物三维降低5%。 + +7. 解释光环的缓存原理。为什么光环需要缓存? + +8. `index`是什么意思? `var cache = core.status.checkBlock.cache[index];` 是干什么的? + +9. 在默认光环的实现中,`hp_buff`, `atk_buff`, `def_buff`, `usedEnemyIds` 分别是干什么的? + +10. 在默认光环的实现中,怪物属性的`value`, `atkValue`和`defValue`用于控制怪物的比例增幅。将其效果修改成数值增幅(例如 `value = 50` 表示生命提升50点,而不是50%)。 + +11. 局部光环的实现原理是什么?十字和九宫格光环是如何区分的? + +12. 实现 **「协同作战」(特殊属性47)**:同楼层/周围X格(由range和zoneSquare决定),每存在一个该属性的队友(包括自身),生命和攻防对应比例提升(提升比例由怪物value, atkValue, defValue决定,线性叠加)。并在游戏中实际验证效果。(注意这个效果和默认光环的区别)。 + +13. 实现 **「迅捷光环」(特殊属性28)**:同楼层/周围X格(由range和zoneSquare决定)的怪物额外先攻X回合(由此怪物的value28决定)。并在游戏中实际验证效果。 + +14. 实现 **「吸血光环」(特殊属性48)**:同楼层/周围X格(由range和zoneSquare决定)的怪物拥有「吸血」特殊属性,吸血比例由光环怪的`vampireValue`项决定。并在游戏中实际验证效果。 diff --git a/MotaAction.g4 b/MotaAction.g4 new file mode 100644 index 0000000..bcf8bfe --- /dev/null +++ b/MotaAction.g4 @@ -0,0 +1,4242 @@ +// 编辑此文件用的vscode插件: https://marketplace.visualstudio.com/items?itemName=zhaouv.vscode-mota-js-extension +// 此文件通过antlr-blockly生成编辑器中的图块, 相关帮助说明: https://zhaouv.github.io/antlr-blockly/docs/#/README +// 添加和修改图块的说明见 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改事件编辑器 + +/* +特殊注入demo +doubleclicktext : EvalString_1 +previewBlock : true +// [x, y, floorId, forceFloor] +selectPoint : ["PosString_0", "PosString_1", "IdString_0", true] +// 自动补全 +allIds : ['EvalString_1'] +allEnemys : ['EvalString_1'] +allItems : ['EvalString_1'] +allImages : ['EvalString_1'] +allAnimates : ['EvalString_1'] +allBgms : ['EvalString_1'] +allSounds : ['EvalString_1'] +allShops : ['EvalString_1'] +allFloorIds : ['EvalString_1'] +// 选择素材 +material : ["./project/animates/", "IdString_0"] +*/ + + +grammar MotaAction; + +//===============parser=============== +//===blockly语句=== + +common_m + : '编辑事件' BGNL? Newline action+ BEND + + +/* common_m +tooltip : 编辑事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + + +//事件 事件编辑器入口之一 +event_m + : '事件' BGNL? Newline '覆盖触发器' Bool '启用' Bool '通行状态' B_0_List '显伤' Bool '不透明度' Number BGNL? Newline '该点特效' '虚化' Number '色相' Int '灰度' Number '反色' Bool '阴影' Number BGNL? Newline action+ BEND + + +/* event_m +tooltip : 编辑魔塔的事件 +helpUrl : /_docs/#/instruction +default : [false,true,null,true,1,0,0,0,false,0,null] +B_0_List_0=eval(B_0_List_0); +if (Number_0 < 0 || Number_0 > 1) throw '不透明度需要在0~1之间'; +if (Number_1 < 0) throw '虚化不得小于0;0为完全没有虚化'; +if (Int_0 < 0 || Int_0 >= 360) throw '色相需要在0~359之间'; +if (Number_2 < 0 || Number_2 > 1) throw '灰度需要在0~1之间'; +if (Number_3 < 0) throw '阴影不得小于0;0为完全没有阴影'; +var code = { + 'trigger': Bool_0?'action':null, + 'enable': Bool_1, + 'noPass': B_0_List_0, + 'displayDamage': Bool_2, + 'opacity': Number_0, + 'filter': { + 'blur': Number_1, + 'hue': Int_0, + 'grayscale': Number_2, + 'invert': Bool_3, + 'shadow': Number_3 + }, + 'data': 'data_asdfefw' +} +if (!Bool_0 && Bool_1 && B_0_List_0===null && Bool_2 && Number_0==1.0 && Number_1==0 && Int_0==0 && Number_2==0 && !Bool_3 && Number_3==0) + code = 'data_asdfefw'; +code=JSON.stringify(code,null,2).split('"data_asdfefw"').join('[\n'+action_0+']\n'); +return code; +*/; + + +//自动事件 事件编辑器入口之一 +autoEvent_m + : '自动事件:' '触发条件' EvalString_Multi '优先级' Int BGNL? Newline '仅在本层检测' Bool '事件流中延迟执行' Bool '允许多次执行' Bool BGNL? Newline action+ BEND + + +/* autoEvent_m +tooltip : 自动事件 +helpUrl : /_docs/#/instruction +default : ["flag:__door__===2",0,true,false,false,null] +var code = { + "condition": 'autoEvent_condition', // 条件不可为null + "currentFloor": Bool_0, // 是否仅在本层检测 + "priority": Int_0, // 优先级 + "delayExecute": Bool_1, // 延迟执行 + "multiExecute": Bool_2, // 是否允许多次执行 + "data": 'autoEvent_asdfefw', // 事件列表 +}; +code=JSON.stringify(code,null,2).replace('autoEvent_condition', EvalString_Multi_0).split('"autoEvent_asdfefw"').join('[\n'+action_0+']\n'); +return code; +*/; + +//升级 事件编辑器入口之一 +level_m + : '等级提升' BGNL? Newline levelCase+ BEND + + +/* level_m +tooltip : 升级事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+levelCase_0+']\n'; +return code; +*/; + +levelCase + : '需求' expression '称号' EvalString? '是否扣除经验' Bool BGNL? Newline action+ + + +/* levelCase +tooltip : 升级设定 +helpUrl : /_docs/#/instruction +default : [0,"",false,null] +colour : this.subColor +Bool_0 = Bool_0?', "clear": true':''; +var code = '{"need": "'+expression_0+'", "title": "'+EvalString_0+'"'+Bool_0+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +//商店 事件编辑器入口之一 +shop_m + : '全局商店列表' BGNL? Newline shoplist+ + +/* shop_m +tooltip : 全局商店列表 +helpUrl : /_docs/#/instruction +var code = '['+shoplist_0+']\n'; +return code; +*/; + +shoplist + : shopsub + | shopitem + | shopcommonevent + | emptyshop + ; + +emptyshop + : Newline + + +/* emptyshop +var code = ' \n'; +return code; +*/; + +shopsub + : '商店 id' IdString '标题' EvalString? '图像' IdString? BGNL? Newline '文字' EvalString_Multi? BGNL? Newline '快捷名称' EvalString '未开启不显示' Bool '不可预览' Bool BGNL? Newline shopChoices+ BEND + + +/* shopsub +tooltip : 全局商店 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +allIds : ['IdString_1'] +default : ["shop1","贪婪之神","moneyShop","勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:","金币商店",false,false] +var title=''; +if (EvalString_0==''){ + if (IdString_1=='') title=''; + else title='\t['+IdString_1+']'; +} else { + if (IdString_1=='')title='\t['+EvalString_0+']'; + else title='\t['+EvalString_0+','+IdString_1+']'; +} +title += EvalString_Multi_0; +var code = '{\n"id": "'+IdString_0+'",\n"text": "'+title+'",\n"textInList": "'+EvalString_1+'",\n"mustEnable": '+Bool_0+',\n"disablePreview": '+Bool_1+',\n"choices":[\n'+shopChoices_0+']},\n'; +return code; +*/; + +shopChoices + : '商店选项' EvalString '使用条件' EvalString BGNL? Newline '图标' IdString? '颜色' ColorString? Colour '出现条件' EvalString? BGNL? Newline action+ BEND + + +/* shopChoices +tooltip : 商店选项 +helpUrl : /_docs/#/instruction +default : ["攻击+1","status:money>=20+2*flag:shop1","","","rgba(255,255,255,1)",""] +allIds : ['IdString_0'] +colour : this.subColor +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +EvalString_2 = EvalString_2 && (', "condition": "'+EvalString_2+'"') +IdString_0 = IdString_0? (', "icon": "'+IdString_0+'"'):''; +var code = '{"text": "'+EvalString_0+'", "need": "'+EvalString_1+'"'+IdString_0+ColorString_0+EvalString_2+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +shopitem + : '道具商店 id' IdString '快捷名称' EvalString '使用' ShopUse_List '未开启不显示' Bool BGNL? Newline shopItemChoices+ BEND + + +/* shopitem +tooltip : 道具商店 +helpUrl : /_docs/#/instruction +default : ["itemShop","道具商店",false] +var code = { + 'id': IdString_0, + 'item': true, + 'textInList': EvalString_0, + 'use': ShopUse_List_0 || 'money', + 'mustEnable': Bool_0, + 'choices': 'choices_aqwedsa' +} +code=JSON.stringify(code,null,2).split('"choices_aqwedsa"').join('[\n'+shopItemChoices_0+']')+',\n'; +return code; +*/; + +shopItemChoices + : '道具名' IdString '存量' IntString? '买入价格' EvalString? '卖出价格' EvalString? '出现条件' EvalString? BEND + + + +/* shopItemChoices +tooltip : 道具商店选项,每一项是道具名;买入或卖出可以不填表示只能卖出或买入 +helpUrl : /_docs/#/instruction +default : ["yellowKey","","10","",""] +colour : this.subColor +IntString_0 = IntString_0 ? (', "number": '+IntString_0) : ''; +EvalString_0 = EvalString_0 ? (', "money": "'+EvalString_0+'"') : ''; +EvalString_1 = EvalString_1 ? (', "sell": "'+EvalString_1+'"') : ''; +if (!EvalString_0 && !EvalString_1) throw "买入金额和卖出金额至少需要填写一个"; +EvalString_2 = EvalString_2 ? (', "condition": "'+EvalString_2+'"') : ''; +var code = '{"id": "' + IdString_0 + '"' + IntString_0 + EvalString_0 + EvalString_1 + EvalString_2 + '},\n'; +return code; +*/; + +shopcommonevent + : '公共事件商店 id' IdString '快捷名称' EvalString '未开启不显示' Bool BGNL? '执行的公共事件名' EvalString '参数列表' JsonEvalString? + +/* shopcommonevent +tooltip : 全局商店, 执行一个公共事件 +helpUrl : /_docs/#/instruction +default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); +} +var code = { + 'id': IdString_0, + 'textInList': EvalString_0, + 'mustEnable': Bool_0, + 'commonEvent': EvalString_1 +} +if (JsonEvalString_0) code.args = JSON.parse(JsonEvalString_0); +code=JSON.stringify(code,null,2)+',\n'; +return code; +*/; + +//beforeBattle 事件编辑器入口之一 +beforeBattle_m + : '战斗开始前' BGNL? Newline action+ BEND + + +/* beforeBattle_m +tooltip : 战斗开始前 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//afterBattle 事件编辑器入口之一 +afterBattle_m + : '战斗结束后' BGNL? Newline action+ BEND + + +/* afterBattle_m +tooltip : 系统引发的战后 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//afterGetItem 事件编辑器入口之一 +afterGetItem_m + : '获取道具后' '轻按时不触发' Bool BGNL? Newline action+ BEND + + +/* afterGetItem_m +tooltip : 系统引发的道具后事件 +helpUrl : /_docs/#/instruction +if (Bool_0) { + return '{"disableOnGentleClick": true, "data": [\n'+action_0+']\n}'; +} else { + return '[\n'+action_0+']\n'; +} +*/; + +//afterOpenDoor 事件编辑器入口之一 +afterOpenDoor_m + : '打开门后' BGNL? Newline action+ BEND + + +/* afterOpenDoor_m +tooltip : 系统引发的自定义事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//firstArrive 事件编辑器入口之一 +firstArrive_m + : '首次到达楼层' BGNL? Newline action+ BEND + + +/* firstArrive_m +tooltip : 首次到达楼层 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//eachArrive 事件编辑器入口之一 +eachArrive_m + : '每次到达楼层' BGNL? Newline action+ BEND + + +/* eachArrive_m +tooltip : 每次到达楼层 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//changeFloor 事件编辑器入口之一 +changeFloor_m + : '楼梯, 传送门' BGNL? Newline Floor_List IdString? Stair_List 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List '动画时间' IntString? '穿透性' IgnoreChangeFloor_List BEND + + +/* changeFloor_m +tooltip : 楼梯, 传送门, 如果目标楼层有多个楼梯, 写upFloor或downFloor可能会导致到达的楼梯不确定, 这时候请使用loc方式来指定具体的点位置 +helpUrl : /_docs/#/instruction +default : [null,"MTx",null,"","",null,"",null] +selectPoint : ["PosString_0", "PosString_1", "IdString_0", true] +allFloorIds : ['IdString_0'] +var toFloorId = IdString_0; +if (Floor_List_0!='floorId') toFloorId = Floor_List_0; +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "loc": ['+PosString_0+', '+PosString_1+']'; +} +if (Stair_List_0===':now') loc = ''; +else if (Stair_List_0!=='loc')loc = ', "stair": "'+Stair_List_0+'"'; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +if (IgnoreChangeFloor_List_0!='null') { + IgnoreChangeFloor_List_0 = ', "ignoreChangeFloor": '+IgnoreChangeFloor_List_0; +} else { + IgnoreChangeFloor_List_0 = ''; +} +var code = '{"floorId": "'+toFloorId+'"'+loc+DirectionEx_List_0+IntString_0+IgnoreChangeFloor_List_0+' }\n'; +return code; +*/; + +//commonEvent 事件编辑器入口之一 +commonEvent_m + : '公共事件' BGNL? Newline action+ BEND + + +/* commonEvent_m +tooltip : 公共事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//item 事件编辑器入口之一 +item_m + : '使用道具事件' BGNL? Newline action+ BEND + + +/* item_m +tooltip : 使用道具事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +// levelChoose 事件编辑器入口之一 +levelChoose_m + : '难度分歧' BGNL? levelChooseList+ BEND + + +/* levelChoose_m +tooltip : 难度分歧 +helpUrl : /_docs/#/instruction +var code = '[\n'+levelChooseList_0+']\n'; +return code; +*/; + +levelChooseList + : levelChooseChoice + | levelChooseEmpty; + +levelChooseEmpty + : Newline + +/* levelChooseEmpty +var code = ' \n'; +return code; +*/; + +levelChooseChoice + : '难度分歧项' '名称' EvalString '简写' EvalString '变量:hard值' NInt '颜色' ColorString? Colour BGNL Newline action+ BEND + +/* levelChooseChoice +tooltip : 难度分歧项 +helpUrl : /_docs/#/instruction +default : ['简单','Easy',1,''] +ColorString_0 = ColorString_0 ? (', "color": [' + ColorString_0 + ']') : ''; +var code = '{"title": "'+EvalString_0+'", "name": "'+EvalString_1+'", "hard": '+NInt_0+ColorString_0+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +floorPartition_m + : '高层塔分区管理' BGNL? floorPartitionList+ BEND + + +/* floorPartition_m +tooltip : 高层塔分区管理 +helpUrl : /_docs/#/instruction +var code = '[\n'+floorPartitionList_0+']\n'; +return code; +*/; + +floorPartitionList + : floorPartitionItem + | floorPartitionEmpty; + +floorPartitionEmpty + : Newline + +/* floorPartitionEmpty +var code = ' \n'; +return code; +*/; + +floorPartitionItem + : '分区项' '起始楼层ID' IdString '终止楼层ID(不填代表到最后一层)' IdString? BEND + +/* floorPartitionItem +tooltip : 难度分歧项 +helpUrl : /_docs/#/instruction +default : ['MTx',''] +IdString_1 = IdString_1 ? (', "'+IdString_1+'"') : ''; +var code = '["'+IdString_0+'"'+IdString_1+'],\n'; +return code; +*/; + + +// equip 事件编辑器入口之一 +equip_m + : '装备' '类型' EvalString '装备动画(第一个装备格有效)' IdString? BGNL? '数值提升项' equipList+ '百分比提升项' equipList+ '穿上时事件' action+ '脱下时事件' action+ '此道具cls须为equips并设置canUseItemEffect' BEND + + +/* equip_m +tooltip : 装备 +default : ['0', ''] +helpUrl : /_docs/#/instruction +allAnimates : ['IdString_0'] +if (!/^\d+$/.test(EvalString_0)) { + EvalString_0 = '"' + EvalString_0 + '"'; +} +IdString_0 = IdString_0 && (', "animate": "'+IdString_0+'"'); +if (action_0.trim()) action_0 = ', "equipEvent": [\n' + action_0 + ']'; +if (action_1.trim()) action_1 = ', "unequipEvent": [\n' + action_1 + ']'; +var code = '{"type": '+EvalString_0+IdString_0+', "value": {\n'+equipList_0+'\n}, "percentage": {\n'+equipList_1+'\n}'+action_0+action_1+'}'; +return code; +*/; + +equipList + : equipKnown + | equipUnknown + | equipEmpty; + + +equipKnown + : Equip_List ':' EvalString BEND + + +/* equipKnown +tooltip : 装备项 +default : ['atk', 10] +helpUrl : /_docs/#/instruction +if (!/^[+-]?\d+(\.\d+)?$/.test(EvalString_0)) EvalString_0 = '"' + EvalString_0 + '"'; +return '"'+Equip_List_0+'": '+EvalString_0+', '; +*/; + +equipUnknown + : EvalString ':' EvalString BEND + + +/* equipUnknown +tooltip : 装备项 +default : ['speed', 10] +helpUrl : /_docs/#/instruction +if (!/^[+-]?\d+(\.\d+)?$/.test(EvalString_1)) EvalString_1 = '"' + EvalString_1 + '"'; +return '"'+EvalString_0+'": '+EvalString_1+', '; +*/; + + +equipEmpty + : Newline + +/* equipEmpty +var code = ' \n'; +return code; +*/; + +floorImage_m + : '楼层贴图' BGNL? Newline floorImageList+ BEND + + +/* floorImage_m +tooltip : 楼层贴图 +helpUrl : /_docs/#/instruction +var code = '[\n'+floorImageList_0+']\n'; +return code; +*/; + +floorImageList + : floorOneImage + | floorEmptyImage; + +floorOneImage + : '图片名' EvalString '翻转' Reverse_List '图层' Bg_Fg2_List '绘制坐标' 'x' NInt 'y' NInt '初始禁用' Bool BGNL? Newline + '裁剪起点坐标' 'x' IntString? 'y' IntString? '宽' IntString? '高' IntString? '帧数' IntString? BEND + + +/* floorOneImage +tooltip : 楼层贴图 +default : ["bg.jpg","null","bg",0,0,false,"","","","",""] +helpUrl : /_docs/#/instruction +allImages : ['EvalString_0'] +previewBlock : true +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +Bool_0 = Bool_0 ? (', "disable": true') : ''; +IntString_0 = IntString_0 && (', "sx": '+IntString_0); +IntString_1 = IntString_1 && (', "sy": '+IntString_1); +IntString_2 = IntString_2 && (', "w": '+IntString_2); +IntString_3 = IntString_3 && (', "h": '+IntString_3); +IntString_4 = IntString_4 && (', "frame": '+IntString_4); +return '{"name": "'+EvalString_0+'"'+Reverse_List_0+', "canvas": "'+Bg_Fg2_List_0+'", "x": '+NInt_0+', "y": '+NInt_1+Bool_0+IntString_0+IntString_1+IntString_2+IntString_3+IntString_4+'},\n'; +*/; + +floorEmptyImage + : Newline + +/* floorEmptyImage +var code = ' \n'; +return code; +*/; + + +// doorInfo 事件编辑器入口之一 +doorInfo_m + : '门信息' '开关门时间' Int '开门音效' EvalString? '关门音效' EvalString? BGNL? Newline '需要钥匙' doorKeyList+ '如需撞到开门还需要把图块触发器改成 openDoor' BGNL? Newline '开门后事件' action+ BEND + + +/* doorInfo_m +tooltip : 开门信息 +default : [160, 'door.mp3', 'door.mp3'] +helpUrl : /_docs/#/instruction +EvalString_0 = EvalString_0 && (', "openSound": "' + EvalString_0 + '"'); +EvalString_1 = EvalString_1 && (', "closeSound": "' + EvalString_1 + '"'); +if (action_0.trim()) action_0 = ', "afterOpenDoor": [\n' + action_0 + ']'; +var code = '{"time": '+Int_0+EvalString_0+EvalString_1+', "keys": {\n'+doorKeyList_0+'\n}'+action_0.trim()+'}'; +return code; +*/; + +doorKeyList + : doorKeyKnown + | doorKeyUnknown + | doorKeyEmpty; + + +doorKeyKnown + : Key_List ':' Int '需要但不消耗' Bool BEND + + +/* doorKeyKnown +tooltip : 开门需要钥匙 +default : ['yellowKey', 1, false] +helpUrl : /_docs/#/instruction +if (Bool_0) Key_List_0 += ':o'; +return '"'+Key_List_0+'": '+Int_0+', '; +*/; + +doorKeyUnknown + : IdString ':' Int '需要但不消耗' Bool BEND + + +/* doorKeyUnknown +tooltip : 开门需要钥匙 +default : ['orangeKey', 1, false] +helpUrl : /_docs/#/instruction +allItems : ['IdString_0'] +if (Bool_0) IdString_0 += ':o'; +return '"'+IdString_0+'": '+Int_0+', '; +*/; + + +doorKeyEmpty + : Newline + +/* doorKeyEmpty +var code = ' \n'; +return code; +*/; + + +faceIds_m + : '行走图朝向:' BGNL? Newline '向下ID' IdString? '向左ID' IdString? '向右ID' IdString? '向上ID' IdString? BEND + + +/* faceIds_m +tooltip : 行走图朝向 +default : ["","","",""] +allIds : ['IdString_0','IdString_1','IdString_2','IdString_3'] +helpUrl : /_docs/#/instruction +return '{' + [ + IdString_0 && ('"down": "' + IdString_0 +'"'), + IdString_1 && ('"left": "' + IdString_1 +'"'), + IdString_2 && ('"right": "' + IdString_2 +'"'), + IdString_3 && ('"up": "' + IdString_3 +'"'), +].filter(function (x) { return x; }).join(', ') + '}\n'; +*/; + + +mainStyle_m + : '主要样式设置:' '标题界面背景图:' EvalString BGNL? Newline + '竖屏标题界面背景图:' EvalString BGNL? Newline + '标题样式;可写 display: none 隐藏标题' EvalString BGNL? Newline + '标题按钮样式:' EvalString BGNL? Newline + '横屏状态栏背景;url(...) 0 0/100% 100% no-repeat 可将图片拉伸自适配' BGNL? Newline EvalString BGNL? Newline + '竖屏状态栏背景:' EvalString BGNL? Newline + '竖屏工具栏背景:' EvalString BGNL? Newline + '楼层切换样式:' EvalString BGNL? Newline + '状态栏颜色' ColorString Colour '边框颜色' ColorString Colour BGNL? Newline + '选中框颜色' ColorString Colour '全局字体' EvalString BEND + +/* mainStyle_m +tooltip : 主要样式设置 +default : ["project/images/bg.jpg", "project/images/bg.jpg", "color: white", "background-color: #32369F; opacity: 0.85; color: #FFFFFF; border: #FFFFFF 2px solid; caret-color: #FFD700;", "url(project/materials/ground.png) repeat", "url(project/materials/ground.png) repeat", "url(project/materials/ground.png) repeat", "background-color: black; color: white", "255,255,255,1", "rgba(255,255,255,1)", "204,204,204,1", "rgba(204,204,204,1)", "255,215,0,1", "rgba(255,215,0,1)", "Verdana"] +helpUrl : /_docs/#/instruction +var code = { + startBackground: EvalString_0, + startVerticalBackground: EvalString_1, + startLogoStyle: EvalString_2, + startButtonsStyle: EvalString_3, + statusLeftBackground: EvalString_4, + statusTopBackground: EvalString_5, + toolsBackground: EvalString_6, + floorChangingStyle: EvalString_7, + statusBarColor: JSON.parse('['+ColorString_0+']'), + borderColor: JSON.parse('['+ColorString_1+']'), + selectColor: JSON.parse('['+ColorString_2+']'), + font: EvalString_8 +}; +return JSON.stringify(code); +*/; + +nameMap_m + : '文件别名设置' '(可以游戏中使用此别名代替原始文件名)' BGNL? Newline nameMapList+ BEND + +/* nameMap_m +tooltip : 文件别名设置 +helpUrl : /_docs/#/instruction +var value = nameMapList_0.trim(); +if (value.startsWith(',')) value = value.substring(1); +return '{'+value+'}'; +*/; + +nameMapList + : nameMapBgm + | nameMapSoundKnown + | nameMapSoundUnknown + | nameMapImage + | nameMapAnimate + | nameMapUnknown + | nameMapEmpty; + +nameMapBgm + : '映射背景音乐' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapBgm +tooltip : 映射背景音乐 +default : ['背景音乐', 'bgm.mp3'] +allBgms : ['EvalString_1'] +material : ["./project/bgms/", "EvalString_1"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapSoundKnown + : '映射系统音效' '名称' NameMap_List '映射到文件' EvalString BEND + +/* nameMapSoundKnown +tooltip : 映射系统音效 +default : ['确定', 'confirm.mp3'] +allSounds : ['EvalString_0'] +material : ["./project/sounds/", "EvalString_0"] +helpUrl : /_docs/#/instruction +return ',"'+NameMap_List_0+'":"'+EvalString_0+'"'; +*/; + +nameMapSoundUnknown + : '映射音效' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapSoundUnknown +tooltip : 映射音效 +default : ['攻击', 'attack.mp3'] +allSounds : ['EvalString_1'] +material : ["./project/sounds/", "EvalString_1"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapImage + : '映射图片' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapImage +tooltip : 映射图片 +default : ['背景图', 'bg.jpg'] +allImages : ['EvalString_1'] +material : ["./project/images/:images", "EvalString_1"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapAnimate + : '映射动画' '名称' EvalString '映射到文件' IdString BEND + +/* nameMapAnimate +tooltip : 映射图片 +default : ['领域', 'zone'] +allAnimates : ['IdString_0'] +material : ["./project/animates/", "IdString_0"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+IdString_0+'"'; +*/; + +nameMapUnknown + : '未知映射' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapUnknown +tooltip : 未知映射 +default : ['文件名', 'file.jpg'] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapEmpty + : Newline + +/* nameMapEmpty +return ' \n'; +*/; + + +splitImages_m + : '图片切分(你可以将一张png格式的大图切分为若干小图)' BGNL? splitImagesList+ BEND + + +/* splitImages_m +tooltip: 图片裁剪 +helpUrl : /_docs/#/instruction +var code = '[\n'+splitImagesList_0+']\n'; +return code; +*/; + +splitImagesList + : splitImagesOne + | splitImagesEmpty; + + +splitImagesOne + : '图片切分项' '图片名' EvalString '每个小图宽度' Int '高度' Int '生成小图的前缀' EvalString BEND + +/* splitImagesOne +tooltip : 图片裁剪项 +helpUrl : /_docs/#/instruction +default : ['hero.png', 32, 32, 'hero_'] +material : ["./project/images/:images", "EvalString_0"] +allImages : ['EvalString_0'] +var code = '{"name": "'+EvalString_0+'", "width": '+Int_0+', "height": '+Int_1+', "prefix": "'+EvalString_1+'"},\n'; +return code; +*/; + +splitImagesEmpty + : Newline + +/* splitImagesEmpty +var code = ' \n'; +return code; +*/; + +//为了避免关键字冲突,全部加了_s +//动作 +action + : text_0_s + | text_1_s + | text_2_s + | moveTextBox_s + | clearTextBox_s + | comment_s + | autoText_s + | scrollText_s + | setText_s + | tip_s + | setValue_s + | setEnemy_s + | setEnemyOnPoint_s + | resetEnemyOnPoint_s + | moveEnemyOnPoint_s + | moveEnemyOnPoint_1_s + | setEquip_s + | setFloor_s + | setGlobalAttribute_s + | setGlobalValue_s + | setGlobalFlag_s + | setNameMap_s + | show_s + | hide_s + | setBlockOpacity_s + | setBlockFilter_s + | trigger_s + | insert_1_s + | insert_2_s + | exit_s + | setBlock_s + | turnBlock_s + | showFloorImg_s + | hideFloorImg_s + | showBgFgMap_s + | hideBgFgMap_s + | setBgFgBlock_s + | setHeroIcon_s + | update_s + | showStatusBar_s + | hideStatusBar_s + | setHeroOpacity_s + | sleep_s + | wait_s + | waitAsync_s + | stopAsync_s + | battle_s + | battle_1_s + | openDoor_s + | closeDoor_s + | changeFloor_s + | changePos_s + | setViewport_s + | setViewport_1_s + | lockViewport_s + | useItem_s + | loadEquip_s + | unloadEquip_s + | openShop_s + | disableShop_s + | follow_s + | unfollow_s + | animate_s + | animate_1_s + | stopAnimate_s + | vibrate_s + | showImage_s + | showImage_1_s + | hideImage_s + | showTextImage_s + | moveImage_s + | rotateImage_s + | scaleImage_s + | showGif_s + | setCurtain_0_s + | setCurtain_1_s + | screenFlash_s + | setWeather_s + | move_s + | moveAction_s + | moveHero_s + | jump_s + | jump_1_s + | jumpHero_s + | jumpHero_1_s + | playBgm_s + | pauseBgm_s + | resumeBgm_s + | loadBgm_s + | freeBgm_s + | playSound_s + | playSound_1_s + | stopSound_s + | setVolume_s + | setBgmSpeed_s + | win_s + | lose_s + | restart_s + | if_s + | if_1_s + | switch_s + | for_s + | forEach_s + | while_s + | dowhile_s + | break_s + | continue_s + | input_s + | input2_s + | choices_s + | confirm_s + | callBook_s + | callSave_s + | autoSave_s + | forbidSave_s + | callLoad_s + | previewUI_s + | clearMap_s + | setAttribute_s + | setFilter_s + | fillText_s + | fillBoldText_s + | drawTextContent_s + | fillRect_s + | strokeRect_s + | drawLine_s + | drawArrow_s + | fillPolygon_s + | strokePolygon_s + | fillEllipse_s + | strokeEllipse_s + | fillArc_s + | strokeArc_s + | drawImage_s + | drawImage_1_s + | drawIcon_s + | drawBackground_s + | drawSelector_s + | drawSelector_1_s + | unknown_s + | function_s + | pass_s + ; + +text_0_s + : '显示文章' ':' EvalString_Multi Newline + + +/* text_0_s +tooltip : text:显示一段文字(剧情) +helpUrl : /_docs/#/instruction +previewBlock : true +default : ["欢迎使用事件编辑器(双击方块可直接预览)"] +var code = '"'+EvalString_Multi_0+'"'; +if (block.isCollapsed() || !block.isEnabled()) { + code = '{"type": "text", "text": '+code; + if (block.isCollapsed()) code += ', "_collapsed": true'; + if (!block.isEnabled()) code += ', "_disabled": true'; + code += '}'; +} +return code+',\n'; +*/; + +text_1_s + : '标题' EvalString? '图像' EvalString? '对话框效果' EvalString? '起点 px' PosString? 'py' PosString? '宽' PosString? '编号' Int '不等待操作' Bool BGNL? Newline EvalString_Multi Newline + + +/* text_1_s +tooltip : text:显示一段文字(剧情),选项较多请右键点击帮助 +helpUrl : /_docs/#/instruction +previewBlock : true +allIds : ['EvalString_1'] +default : ["小妖精","fairy","","","","",0,false,"欢迎使用事件编辑器(双击方块可直接预览)"] +var title=''; +if (EvalString_0==''){ + if (EvalString_1=='' )title=''; + else title='\\t['+EvalString_1+']'; +} else { + if (EvalString_1=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+EvalString_1+']'; +} +var pos = ''; +if (PosString_0 || PosString_1) { + if (EvalString_2) throw new Error('对话框效果和起点像素位置只能设置一项!'); + pos = '[' + (PosString_0||0) + ',' + (PosString_1||0); + if (PosString_2) pos += ',' + PosString_2; + pos += ']'; +} +if(EvalString_2 && !(/^(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_2))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_2 = EvalString_2 && ('\\b['+EvalString_2+']'); +var code = '"'+title+EvalString_2+EvalString_Multi_0+'"'; +if (block.isCollapsed() || !block.isEnabled() || pos || Int_0 || Bool_0) { + code = '{"type": "text", "text": '+code; + if (pos) code += ', "pos": ' + pos; + if (Int_0) code += ', "code": ' + Int_0; + if (Bool_0) code += ', "async": true'; + if (block.isCollapsed()) code += ', "_collapsed": true'; + if (!block.isEnabled()) code += ', "_disabled": true'; + code += '}'; +} +return code+',\n'; +*/; + +text_2_s + : '标题' EvalString? '图像' EvalString? '对话框效果' EvalString? '起点 px' PosString? 'py' PosString? '宽' PosString? '编号' Int '不等待操作' Bool BGNL? Newline EvalString_Multi BGNL? Newline textDrawingList* Newline + + +/* text_2_s +tooltip : text:显示一段文字(剧情),选项较多请右键点击帮助 +helpUrl : /_docs/#/instruction +previewBlock : true +allIds : ['EvalString_1'] +default : ["小妖精","fairy","","","","",0,"欢迎使用事件编辑器(双击方块可直接预览)",null] +var title=''; +if (EvalString_0==''){ + if (EvalString_1=='' )title=''; + else title='\\t['+EvalString_1+']'; +} else { + if (EvalString_1=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+EvalString_1+']'; +} +var pos = ''; +if (PosString_0 || PosString_1) { + if (EvalString_2) throw new Error('对话框效果和起点像素位置只能设置一项!'); + pos = '[' + (PosString_0||0) + ',' + (PosString_1||0); + if (PosString_2) pos += ',' + PosString_2; + pos += ']'; +} +if(EvalString_2 && !(/^(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_2))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_2 = EvalString_2 && ('\\b['+EvalString_2+']'); +var code = '"'+title+EvalString_2+textDrawingList_0.replace(/\s/g, '')+EvalString_Multi_0+'"'; +if (block.isCollapsed() || !block.isEnabled() || pos || Int_0 || Bool_0) { + code = '{"type": "text", "text": '+code; + if (pos) code += ', "pos": ' + pos; + if (Int_0) code += ', "code": ' + Int_0; + if (Bool_0) code += ', "async": true'; + if (block.isCollapsed()) code += ', "_collapsed": true'; + if (!block.isEnabled()) code += ', "_disabled": true'; + code += '}'; +} +return code+',\n'; +*/; + +textDrawingList + : textDrawing + | textDrawingEmpty; + + +textDrawing + : '立绘' EvalString '翻转' Reverse_List '绘制坐标' 'x' IntString 'y' IntString '宽' IntString? '高' IntString? BGNL? Newline + '裁剪坐标' 'x' IntString? 'y' IntString? '宽' IntString? '高' IntString? '不透明度' EvalString? '旋转角度' IntString? + +/* textDrawing +tooltip : 立绘 +helpUrl : /_docs/#/instruction +default : ["fairy.png","null","0","0","","","","","","","",""] +colour : this.subColor +previewBlock : true +allImages : ['EvalString_0'] +if (Reverse_List_0 && Reverse_List_0 != 'null') EvalString_0 += Reverse_List_0; +var list = [EvalString_0, IntString_0, IntString_1]; +if (IntString_2 || IntString_3) { + if (list.length != 3 || !IntString_2 || !IntString_3) { + throw "绘制的宽和高需同时设置"; + } + list.push(IntString_2); + list.push(IntString_3); +} +if (IntString_4 || IntString_5 || IntString_6 || IntString_7) { + if (list.length != 5) throw "如设置裁剪区域,请先设置绘制区域的宽高"; + if (!IntString_4 || !IntString_5 || !IntString_6 || !IntString_7) { + throw "如设置裁剪区域,请同时设置全部的裁剪坐标和宽高"; + } + list.splice(1, 0, IntString_4, IntString_5, IntString_6, IntString_7); +} +if (EvalString_1) { + if (list.length != 9) throw "如设置不透明度,需填满所有坐标和宽高"; + var opacity = parseFloat(EvalString_1); + if (isNaN(opacity) || opacity < 0 || opacity > 1) throw "不合法的不透明度,必须是0到1之间" + list.push(opacity); +} +if (IntString_8) { + if (list.length != 10) throw "如设置旋转角度,需填满所有坐标和宽高,以及不透明度"; + list.push(IntString_8); +} +return "\\f[" + list.join(",")+"]"; +*/; + +textDrawingEmpty + : Newline + +/* textDrawingEmpty +var code = ''; +return code; +*/; + +moveTextBox_s + : '移动对话框' ':' Int 'px' PosString 'py' PosString '使用增量' Bool '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline + +/* moveTextBox_s +tooltip : 移动对话框 +helpUrl : /_docs/#/instruction +default : [1,"0","0",false,'',500,false] +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +Bool_0 = Bool_0 ?', "relative": true':''; +Bool_1 = Bool_1 ?', "async": true':''; +var code = '{"type": "moveTextBox", "code": '+Int_0+', "loc": ['+PosString_0+','+PosString_1+']'+Bool_0+MoveMode_List_0+', "time": '+Int_1+Bool_1+'},\n'; +return code; +*/; + +clearTextBox_s + : '清除对话框' ':' EvalString? Newline + +/* clearTextBox_s +tooltip : 清除对话框 +helpUrl : /_docs/#/instruction +default : ["1"] +if (EvalString_0 && !/^\d+(,\d+)*$/.test(EvalString_0)) throw new Error('对话框编号需要以逗号分隔'); +EvalString_0 = EvalString_0 ? (', "code": ['+EvalString_0+']') : ''; +var code = '{"type": "clearTextBox"'+EvalString_0+'},\n'; +return code; +*/; + + +comment_s + : '添加注释' ':' EvalString_Multi Newline + + +/* comment_s +tooltip : comment:添加一段会被游戏跳过的注释内容 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +default : ["可以在这里写添加任何注释内容"] +colour : this.commentColor +var code = '{"type": "comment", "text": "'+EvalString_Multi_0+'"},\n'; +return code; +*/; + +autoText_s + : '自动剧情文本: 标题' EvalString? '图像' EvalString? '对话框效果' EvalString? '时间' Int BGNL? EvalString_Multi Newline + + +/* autoText_s +tooltip : autoText:自动剧情文本,用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +allIds : ['EvalString_1'] +default : ["小妖精","fairy","",3000,"用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示"] +var title=''; +if (EvalString_0==''){ + if (EvalString_1=='' )title=''; + else title='\\t['+EvalString_1+']'; +} else { + if (EvalString_1=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+EvalString_1+']'; +} +if(EvalString_2 && !(/^(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_2))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_2 = EvalString_2 && ('\\b['+EvalString_2+']'); +var code = '{"type": "autoText", "text": "'+title+EvalString_2+EvalString_Multi_0+'", "time": '+Int_0+'},\n'; +return code; +*/; + +scrollText_s + : '滚动剧情文本:' '时间' Int '行距' Number '不等待执行完毕' Bool? BGNL? EvalString_Multi Newline + + +/* scrollText_s +tooltip : scrollText:滚动剧情文本,将从下到上进行滚动显示。 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +default : [5000,1.4,false,"时间是总时间,可以使用setText事件来控制字体、颜色、大小、偏移量等"] +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "scrollText", "text": "'+EvalString_Multi_0+'"'+Bool_0+', "time" :'+Int_0+', "lineHeight": '+Number_0+'},\n'; +return code; +*/; + +setText_s + : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' IntString? '对齐' TextAlign_List? '粗体' B_1_List? BGNL? '标题颜色' ColorString? Colour '正文颜色' ColorString? Colour '背景色' EvalString? Colour BGNL? '标题大小' IntString? '正文大小' IntString? '行距' IntString? '打字间隔' IntString? '字符间距' IntString? '淡入淡出时间' IntString? Newline + + +/* setText_s +tooltip : setText:设置剧情文本的属性,颜色为RGB三元组或RGBA四元组,打字间隔为剧情文字添加的时间间隔,为整数或不填,字符间距为字符之间的距离,为整数或不填。 +helpUrl : /_docs/#/instruction +previewBlock : true +default : [null,"",null,null,"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"","","","","",""] +SetTextPosition_List_0 =SetTextPosition_List_0==='null'?'': ', "position": "'+SetTextPosition_List_0+'"'; +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +var colorRe = MotaActionFunctions.pattern.colorRe; +IntString_0 = IntString_0 ? (', "offset": '+IntString_0) : ''; +ColorString_0 = ColorString_0 ? (', "title": ['+ColorString_0+']') : ''; +ColorString_1 = ColorString_1 ? (', "text": ['+ColorString_1+']') : ''; +if (EvalString_0) { + if (colorRe.test(EvalString_0)) { + EvalString_0 = ', "background": ['+EvalString_0+']'; + } + else if (/^\w+\.png$/.test(EvalString_0)) { + EvalString_0 = ', "background": "'+EvalString_0+'"'; + } + else { + throw new Error('背景格式错误,必须是形如0~255,0~255,0~255,0~1的颜色,或一个WindowSkin的png图片名称'); + } +} +IntString_1 = IntString_1 ? (', "titlefont": '+IntString_1) : ''; +IntString_2 = IntString_2 ? (', "textfont": '+IntString_2) : ''; +IntString_3 = IntString_3 ? (', "lineHeight": '+IntString_3) : ''; +IntString_4 = IntString_4 ? (', "time": '+IntString_4) : ''; +IntString_5 = IntString_5 ? (', "letterSpacing": '+IntString_5) : ''; +IntString_6 = IntString_6 ? (', "animateTime": ' + IntString_6) : ''; +B_1_List_0 = B_1_List_0==='null'?'':', "bold": '+B_1_List_0; +var code = '{"type": "setText"'+SetTextPosition_List_0+IntString_0+TextAlign_List_0+B_1_List_0+ColorString_0+ColorString_1+EvalString_0+IntString_1+IntString_2+IntString_3+IntString_4+IntString_5+IntString_6+'},\n'; +return code; +*/; + +tip_s + : '显示提示' ':' EvalString '图标ID' IdString? Newline + + +/* tip_s +tooltip : tip:显示一段提示文字 +helpUrl : /_docs/#/instruction +allIds : ['IdString_0'] +default : ["这段话将在左上角以气泡形式显示",""] +IdString_0 = IdString_0 && (', "icon": "' + IdString_0 + '"'); +var code = '{"type": "tip", "text": "'+EvalString_0+'"'+IdString_0+'},\n'; +return code; +*/; + +setValue_s + : '数值操作' ':' '名称' idString_e AssignOperator_List expression '不刷新状态栏' Bool Newline + + +/* setValue_s +tooltip : setValue:设置勇士的某个属性、道具个数, 或某个变量/Flag的值 +helpUrl : /_docs/#/instruction +default : ["","","",false] +colour : this.dataColor +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setValue", "name": "'+idString_e_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"' + Bool_0 + '},\n'; +return code; +*/; + + +setEnemy_s + : '设置怪物属性' ':' '怪物ID' IdString '的' EnemyId_List AssignOperator_List expression '不刷新显伤' Bool Newline + + +/* setEnemy_s +tooltip : setEnemy:设置某个怪物的属性 +helpUrl : /_docs/#/instruction +default : ["greenSlime", "atk", "=", "", false] +allEnemys : ['IdString_0'] +colour : this.dataColor +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setEnemy", "id": "'+IdString_0+'", "name": "'+EnemyId_List_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"'+Bool_0+'},\n'; +return code; +*/; + + +setEquip_s + : '设置装备属性' ':' '装备ID' IdString EquipValueType_List '的' EvalString AssignOperator_List expression Newline + + +/* setEquip_s +tooltip : setEquip:设置某个怪物的属性 +helpUrl : /_docs/#/instruction +default : ["sword1", "value", "atk", "="] +allEquips : ['IdString_0'] +colour : this.dataColor +EquipValueType_List_0 = EquipValueType_List_0 == 'percentage' ? ', "valueType": "percentage"' : ', "valueType": "value"'; +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +var code = '{"type": "setEquip", "id": "'+IdString_0+'"'+EquipValueType_List_0+', "name": "'+EvalString_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"},\n'; +return code; +*/; + + +setEnemyOnPoint_s + : '设置某点怪物属性' ':' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '的' EnemyPoint_List AssignOperator_List expression '不刷新显伤' Bool Newline + + +/* setEnemyOnPoint_s +tooltip : setEnemyOnPoint:设置某个点上怪物的属性 +helpUrl : /_docs/#/instruction +default : ["", "", "", "atk", "=", "", false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.dataColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setEnemyOnPoint"'+floorstr+IdString_0+', "name": "'+EnemyPoint_List_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"'+Bool_0+'},\n'; +return code; +*/; + +resetEnemyOnPoint_s + : '重置某点怪物属性' ':' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '不刷新显伤' Bool Newline + + +/* resetEnemyOnPoint_s +tooltip : resetEnemyOnPoint:重置某个点上怪物的属性 +helpUrl : /_docs/#/instruction +default : ["", "", "", false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.dataColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "resetEnemyOnPoint"'+floorstr+IdString_0+Bool_0+'},\n'; +return code; +*/; + +moveEnemyOnPoint_s + : '移动某点怪物属性' ':' '起点' 'x' PosString? ',' 'y' PosString? '终点' 'x' PosString? 'y' PosString? '楼层' IdString? '不刷新显伤' Bool Newline + + +/* moveEnemyOnPoint_s +tooltip : moveEnemyOnPoint:移动某个点上怪物的属性到其他点 +helpUrl : /_docs/#/instruction +default : ["", "", "", "", "", false] +allFloorIds : ['IdString_0'] +selectPoint : ["PosString_2", "PosString_3"] +menu : [['选择起点位置','editor_blockly.selectPoint(block,["PosString_0", "PosString_1"])']] +colour : this.dataColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var floorstr = PosString_0 && PosString_1 ? ', "from": ['+PosString_0+','+PosString_1+']' : ''; +if (PosString_2 && PosString_3) floorstr += ', "to": ['+PosString_2+','+PosString_3+']' +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "moveEnemyOnPoint"'+floorstr+IdString_0+Bool_0+'},\n'; +return code; +*/; + +moveEnemyOnPoint_1_s + : '移动某点怪物属性' ':' '起点' 'x' PosString? ',' 'y' PosString? '增量' 'dx' PosString? 'dy' PosString? '楼层' IdString? '不刷新显伤' Bool Newline + + +/* moveEnemyOnPoint_1_s +tooltip : moveEnemyOnPoint:移动某个点上怪物的属性到其他点 +helpUrl : /_docs/#/instruction +default : ["", "", "", "", "", false] +allFloorIds : ['IdString_0'] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.dataColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var floorstr = PosString_0 && PosString_1 ? ', "from": ['+PosString_0+','+PosString_1+']' : ''; +if (PosString_2 && PosString_3) floorstr += ', "dxy": ['+PosString_2+','+PosString_3+']' +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "moveEnemyOnPoint"'+floorstr+IdString_0+Bool_0+'},\n'; +return code; +*/; + +setFloor_s + : '设置楼层属性' ':' Floor_Meta_List '楼层名' IdString? '为' JsonEvalString Newline + + +/* setFloor_s +tooltip : setFloor:设置楼层属性;该楼层属性和编辑器中的楼层属性一一对应 +helpUrl : /_docs/#/instruction +default : ["title","","\"新楼层名\""] +allFloorIds : ['IdString_0'] +colour : this.dataColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "setFloor", "name": "'+Floor_Meta_List_0+'"'+IdString_0+', "value": '+JsonEvalString_0+'},\n'; +return code; +*/; + + +setGlobalAttribute_s + : '设置全局属性' ':' Global_Attribute_List '为' EvalString Newline + + +/* setGlobalAttribute_s +tooltip : setGlobalAttribute:设置全局属性 +helpUrl : /_docs/#/instruction +default : ["font","Verdana"] +colour : this.dataColor +var code = '{"type": "setGlobalAttribute", "name": "'+Global_Attribute_List_0+'", "value": "'+EvalString_0+'"},\n'; +return code; +*/; + + +setGlobalValue_s + : '设置全局数值' ':' Global_Value_List '为' EvalString Newline + + +/* setGlobalValue_s +tooltip : setGlobalValue:设置全局属性 +helpUrl : /_docs/#/instruction +default : ["lavaDamage","100"] +colour : this.dataColor +var code = '{"type": "setGlobalValue", "name": "'+Global_Value_List_0+'", "value": '+EvalString_0+'},\n'; +return code; +*/; + + +setGlobalFlag_s + : '设置系统开关' ':' Global_Flag_List Bool Newline + + +/* setGlobalFlag_s +tooltip : setGlobalFlag:设置系统开关 +helpUrl : /_docs/#/instruction +default : ["s:enableFloor","true"] +colour : this.dataColor +var code = '{"type": "setGlobalFlag", "name": "'+Global_Flag_List_0+'", "value": '+Bool_0+'},\n'; +return code; +*/; + + +setNameMap_s + : '设置文件别名' ':' EvalString '为' EvalString? Newline + + +/* setNameMap_s +tooltip : setNameMap:设置文件别名 +helpUrl : /_docs/#/instruction +default : ["背景音乐",""] +colour : this.dataColor +EvalString_1 = EvalString_1 ? (', "value": "' + EvalString_1 + '"') : ''; +var code = '{"type": "setNameMap", "name": "'+EvalString_0+'"'+EvalString_1+'},\n'; +return code; +*/; + +show_s + : '显示事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' IntString? '不等待执行完毕' Bool? Newline + + +/* show_s +tooltip : show: 将禁用事件启用,楼层和动画时间可不填,xy可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["","","","",false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0 ?', "async": true':''; +var code = '{"type": "show"'+floorstr+IdString_0+''+IntString_0+Bool_0+'},\n'; +return code; +*/; + +hide_s + : '隐藏事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '同时删除' Bool '动画时间' IntString? '不等待执行完毕' Bool? Newline + + +/* hide_s +tooltip : hide: 隐藏事件,同时可删除 +helpUrl : /_docs/#/instruction +default : ["","","",true,"",false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0 ?', "remove": true':''; +Bool_1 = Bool_1 ?', "async": true':''; +var code = '{"type": "hide"'+floorstr+IdString_0+Bool_0+IntString_0+Bool_1+'},\n'; +return code; +*/; + +setBlockOpacity_s + : '设置图块不透明度' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '不透明度' Number '动画时间' IntString? '不等待执行完毕' Bool? Newline + + +/* setBlockOpacity_s +tooltip : setBlockOpacity: 设置图块不透明度 +helpUrl : /_docs/#/instruction +default : ["","","",1.0,"",false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (Number_0 < 0 || Number_0 > 1) throw new Error('不透明度需要在0~1之间'); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0 ?', "async": true':''; +var code = '{"type": "setBlockOpacity"'+floorstr+IdString_0+', "opacity": '+Number_0+IntString_0+Bool_0+'},\n'; +return code; +*/; + +setBlockFilter_s + : '设置图块特效' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '虚化' Number '色相' Int '灰度' Number '反色' Bool '阴影' Number Newline + + +/* setBlockFilter_s +tooltip : setBlockFilter: 设置图块特效 +helpUrl : /_docs/#/instruction +default : ["","","",0,0,0,false,0] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (Number_0 < 0) throw '虚化不得小于0;0为完全没有虚化'; +if (Int_0 < 0 || Int_0 >= 360) throw '色相需要在0~359之间'; +if (Number_1 < 0 || Number_1 > 1) throw '灰度需要在0~1之间'; +if (Number_2 < 0) throw '阴影不得小于0;0为完全没有阴影'; + +var code = '{"type": "setBlockFilter"'+floorstr+IdString_0+', "blur": '+Number_0+', "hue": '+Int_0+', "grayscale": '+Number_1+', "invert": '+Bool_0+', "shadow": '+Number_2+'},\n'; +return code; +*/; + + +trigger_s + : '触发系统事件' 'x' PosString? ',' 'y' PosString? Newline + + +/* trigger_s +tooltip : trigger: 立即触发另一个地点的事件 +helpUrl : /_docs/#/instruction +default : ["",""] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.eventColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "trigger"'+floorstr+'},\n'; +return code; +*/; + +insert_1_s + : '插入公共事件' EvalString '参数列表' JsonEvalString? Newline + + +/* insert_1_s +tooltip : insert: 插入公共事件并执行 +helpUrl : /_docs/#/instruction +allEvents : ['EvalString_0'] +default : ["加点事件", ""] +colour : this.eventColor +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); + JsonEvalString_0 = ', "args": ' +JsonEvalString_0; +} +var code = '{"type": "insert", "name": "'+EvalString_0+'"'+JsonEvalString_0+'},\n'; +return code; +*/; + +insert_2_s + : '插入事件' 'x' PosString? ',' 'y' PosString? Event_List? '楼层' IdString? '参数列表' JsonEvalString? Newline + + +/* insert_2_s +tooltip : insert: 立即插入另一个地点的事件执行,当前事件不会中断,事件坐标不会改变 +helpUrl : /_docs/#/instruction +default : ["0","0",null,"",""] +colour : this.eventColor +allFloorIds : ['IdString_0'] +selectPoint : ["PosString_0", "PosString_1", "IdString_0"] +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); + JsonEvalString_0 = ', "args": ' +JsonEvalString_0; +} +if (Event_List_0 && Event_List_0 !=='null') + Event_List_0 = ', "which": "'+Event_List_0+'"'; +else Event_List_0 = ''; +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "insert"'+floorstr+Event_List_0+IdString_0+JsonEvalString_0+'},\n'; +return code; +*/; + +exit_s + : '立刻结束当前事件' Newline + + +/* exit_s +tooltip : exit: 立刻结束当前事件 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = '{"type": "exit"},\n'; +return code; +*/; + +setBlock_s + : '转变图块为' EvalString 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' IntString? '不等待执行完毕' Bool Newline + + +/* setBlock_s +tooltip : setBlock:设置某个图块,忽略坐标楼层则为当前事件 +helpUrl : /_docs/#/instruction +colour : this.mapColor +allFloorIds : ['IdString_0'] +allIds : ['EvalString_0'] +default : ["yellowDoor","","","","",false] +selectPoint : ["EvalString_1", "EvalString_2", "IdString_0"] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_1, EvalString_2); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 && (', "time": ' + IntString_0); +Bool_0 = Bool_0 ? (', "async": true') : ''; +var code = '{"type": "setBlock", "number": "'+EvalString_0+'"'+floorstr+IdString_0+IntString_0+Bool_0+'},\n'; +return code; +*/; + +turnBlock_s + : '事件转向' DirectionEx_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* turnBlock_s +tooltip : turnBlock:事件转向;自动检索faceIds +helpUrl : /_docs/#/instruction +colour : this.mapColor +allFloorIds : ['IdString_0'] +default : [null,"","",""] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "turnBlock"'+DirectionEx_List_0+floorstr+IdString_0+'},\n'; +return code; +*/; + +showFloorImg_s + : '显示贴图' '像素坐标' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* showFloorImg_s +tooltip : showFloorImg: 显示一个贴图,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["","",""] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "showFloorImg"'+floorstr+IdString_0+'},\n'; +return code; +*/; + +hideFloorImg_s + : '隐藏贴图' '像素坐标' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* hideFloorImg_s +tooltip : hideFloorImg: 隐藏一个贴图,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["","",""] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "hideFloorImg"'+floorstr+IdString_0+'},\n'; +return code; +*/; + +showBgFgMap_s + : '显示图层块' Bg_Fg_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* showBgFgMap_s +tooltip : showBgFgMap: 显示图层块,即背景图层/前景图层的某些图块,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["bg","","",""] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "showBgFgMap", "name": "' + Bg_Fg_List_0 + '"' +floorstr+IdString_0+'},\n'; +return code; +*/; + +hideBgFgMap_s + : '隐藏图层块' Bg_Fg_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* hideBgFgMap_s +tooltip : hideBgFgMap: 隐藏图层块,即背景图层/前景图层的某些图块,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["bg","","",""] +allFloorIds : ['IdString_0'] +colour : this.mapColor +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "hideBgFgMap", "name": "' + Bg_Fg_List_0 + '"' +floorstr+IdString_0+'},\n'; +return code; +*/; + +setBgFgBlock_s + : '转变图层块' Bg_Fg_List '为' EvalString 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* setBgFgBlock_s +tooltip : setBgFgBlock:设置某个图层块,忽略坐标楼层则为当前点 +helpUrl : /_docs/#/instruction +colour : this.mapColor +selectPoint : ["EvalString_1", "EvalString_2", "IdString_0"] +allIds : ['EvalString_0'] +allFloorIds : ['IdString_0'] +default : ["bg","yellowDoor","","",""] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_1, EvalString_2); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "setBgFgBlock", "name": "' + Bg_Fg_List_0 + '", "number": "'+EvalString_0+'"'+floorstr+IdString_0+'},\n'; +return code; +*/; + +setHeroIcon_s + : '更改角色行走图' EvalString? '不重绘' Bool Newline + + +/* setHeroIcon_s +tooltip : setHeroIcon:更改角色行走图 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : ["hero.png", false] +allImages : ['EvalString_0'] +material : ["./project/images/:images", "EvalString_0"] +EvalString_0 = EvalString_0 && (', "name": "'+EvalString_0+'"'); +Bool_0 = Bool_0 ? (', "noDraw": true') : ''; +var code = '{"type": "setHeroIcon"'+EvalString_0+Bool_0+'},\n'; +return code; +*/; + +update_s + : '更新状态栏和地图显伤' '不检查自动事件' Bool Newline + + +/* update_s +tooltip : update: 立刻更新状态栏和地图显伤 +helpUrl : /_docs/#/instruction +default : [false] +colour : this.dataColor +Bool_0 = Bool_0 ? (', "doNotCheckAutoEvents": true') : '' +var code = '{"type": "update"'+Bool_0+'},\n'; +return code; +*/; + +showStatusBar_s + : '显示状态栏' Newline + + +/* showStatusBar_s +tooltip : showStatusBar: 显示状态栏 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "showStatusBar"},\n'; +return code; +*/; + +hideStatusBar_s + : '隐藏状态栏' '不隐藏竖屏工具栏' Bool Newline + + +/* hideStatusBar_s +tooltip : hideStatusBar: 隐藏状态栏 +helpUrl : /_docs/#/instruction +colour : this.soundColor +default : [false] +Bool_0 = Bool_0?', "toolbox": true':''; +var code = '{"type": "hideStatusBar"'+Bool_0+'},\n'; +return code; +*/; + +setHeroOpacity_s + : '设置勇士不透明度' Number '渐变方式' MoveMode_List '动画时间' IntString? '不等待执行完毕' Bool Newline + +/* setHeroOpacity_s +tooltip : setHeroOpacity: 设置勇士不透明度 +helpUrl : /_docs/#/instruction +default : [1,'','',false] +colour : this.soundColor +if (Number_0 < 0 || Number_0 > 1) throw new Error('不透明度需要在0~1之间'); +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +IntString_0 = IntString_0 && (', "time": ' + IntString_0); +Bool_0 = Bool_0 ? (', "async": true') : ''; +var code = '{"type": "setHeroOpacity", "opacity": '+Number_0+MoveMode_List_0+IntString_0+Bool_0+'},\n'; +return code; +*/; + +sleep_s + : '等待' Int '毫秒' '不可被Ctrl跳过' Bool Newline + + +/* sleep_s +tooltip : sleep: 等待多少毫秒 +helpUrl : /_docs/#/instruction +default : [500, false] +colour : this.soundColor +Bool_0 = Bool_0?', "noSkip": true':''; +var code = '{"type": "sleep", "time": '+Int_0+Bool_0+'},\n'; +return code; +*/; + + +battle_s + : '强制战斗' IdString Newline + + +/* battle_s +tooltip : battle: 强制战斗 +helpUrl : /_docs/#/instruction +default : ["greenSlime"] +allEnemys : ['IdString_0'] +colour : this.dataColor +var code = '{"type": "battle", "id": "'+IdString_0+'"},\n'; +return code; +*/; + + +battle_1_s + : '强制战斗' 'x' PosString? ',' 'y' PosString? Newline + + +/* battle_1_s +tooltip : battle: 强制战斗 +helpUrl : /_docs/#/instruction +default : ["","",""] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "battle"'+floorstr+'},\n'; +return code; +*/; + +openDoor_s + : '开门' 'x' PosString? ',' 'y' PosString? '楼层' IdString? '需要钥匙' Bool? '不等待执行完毕' Bool Newline + + +/* openDoor_s +tooltip : openDoor: 开门,楼层可不填表示当前层 +helpUrl : /_docs/#/instruction +default : ["","","",false,false] +selectPoint : ["PosString_0", "PosString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Bool_0 = Bool_0 ? ', "needKey": true' : ''; +Bool_1 = Bool_1 ? ', "async": true' : ''; +var code = '{"type": "openDoor"'+floorstr+IdString_0+Bool_0+Bool_1+'},\n'; +return code; +*/; + +closeDoor_s + : '关门' 'x' PosString? ',' 'y' PosString? 'ID' IdString '不等待执行完毕' Bool Newline + + +/* closeDoor_s +tooltip : closeDoor: 关门事件,需要该点本身无事件 +helpUrl : /_docs/#/instruction +default : ["","","yellowDoor",false] +selectPoint : ["PosString_0", "PosString_1"] +allDoors : ['IdString_0'] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Bool_0 = Bool_0 ? ', "async": true' : ''; +var code = '{"type": "closeDoor", "id": "'+IdString_0+'"'+floorstr+Bool_0+'},\n'; +return code; +*/; + +changeFloor_s + : '楼层切换' Floor_List IdString? Stair_List 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List '动画时间' IntString? Newline + + +/* changeFloor_s +tooltip : changeFloor: 楼层切换,动画时间可不填 +helpUrl : /_docs/#/instruction +default : [null,"",null,"","",null,"",null] +selectPoint : ["PosString_0", "PosString_1", "IdString_0", true] +allFloorIds : ['IdString_0'] +colour : this.dataColor +var toFloorId = IdString_0; +if (Floor_List_0!='floorId') toFloorId = Floor_List_0; +toFloorId = toFloorId ? (', "floorId": "' + toFloorId +'"') : ''; +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "loc": ['+PosString_0+', '+PosString_1+']'; +} +if (Stair_List_0===':now') loc = ''; +else if (Stair_List_0!=='loc')loc = ', "stair": "'+Stair_List_0+'"'; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +var code = '{"type": "changeFloor"'+toFloorId+loc+DirectionEx_List_0+IntString_0+' },\n'; +return code; +*/; + +changePos_s + : '位置朝向切换' 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List Newline + + +/* changePos_s +tooltip : changePos: 当前位置切换 +helpUrl : /_docs/#/instruction +default : ["","",null] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.dataColor +var loc = (PosString_0 && PosString_1) ? (', "loc": ['+PosString_0+','+PosString_1+']') : ''; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +var code = '{"type": "changePos"'+loc+DirectionEx_List_0+'},\n'; +return code; +*/; + +useItem_s + : '使用道具' IdString Newline + + +/* useItem_s +tooltip : useItem: 使用道具 +helpUrl : /_docs/#/instruction +colour : this.dataColor +allItems : ['IdString_0'] +default : ["pickaxe"] +var code = '{"type": "useItem", "id": "'+IdString_0+'"},\n'; +return code; +*/; + +loadEquip_s + : '装上装备' IdString Newline + + +/* loadEquip_s +tooltip : loadEquip: 装上装备 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : ["sword1"] +allEquips : ['IdString_0'] +var code = '{"type": "loadEquip", "id": "'+IdString_0+'"},\n'; +return code; +*/; + +unloadEquip_s + : '卸下第' Int '格装备孔的装备' Newline + + +/* unloadEquip_s +tooltip : unloadEquip: 卸下装备 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : [0] +var code = '{"type": "unloadEquip", "pos": '+Int_0+'},\n'; +return code; +*/; + +openShop_s + : '启用全局商店' IdString '同时打开' Bool Newline + + +/* openShop_s +tooltip : 全局商店 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : ["shop1", true] +allShops : ['IdString_0'] +Bool_0 = Bool_0 ? (', "open": true') : ''; +var code = '{"type": "openShop", "id": "'+IdString_0+'"'+Bool_0+'},\n'; +return code; +*/; + +disableShop_s + : '禁用全局商店' IdString Newline + + +/* disableShop_s +tooltip : 全局商店 +helpUrl : /_docs/#/instruction +default : ["shop1"] +allShops : ['IdString_0'] +colour : this.dataColor +var code = '{"type": "disableShop", "id": "'+IdString_0+'"},\n'; +return code; +*/; + +follow_s + : '跟随勇士' '行走图' EvalString Newline + + +/* follow_s +tooltip : follow: 跟随勇士 +helpUrl : /_docs/#/instruction +default : ["npc.png"] +allImages : ['EvalString_0'] +material : ["./project/images/:images", "EvalString_0"] +colour : this.dataColor +var code = '{"type": "follow", "name": "'+EvalString_0+'"},\n'; +return code; +*/; + +unfollow_s + : '取消跟随' '行走图' EvalString? Newline + + +/* unfollow_s +tooltip : unfollow: 取消跟随 +helpUrl : /_docs/#/instruction +default : [""] +allImages : ['EvalString_0'] +material : ["./project/images/:images", "EvalString_0"] +colour : this.dataColor +EvalString_0 = EvalString_0 ? (', "name": "' + EvalString_0 + '"') : ""; +var code = '{"type": "unfollow"' + EvalString_0 + '},\n'; +return code; +*/; + +vibrate_s + : '画面震动' '方向' Vibrate_List '时间' Int '速度' Int '振幅' Int '不等待执行完毕' Bool Newline + + +/* vibrate_s +tooltip : vibrate: 画面震动 +helpUrl : /_docs/#/instruction +default : ["horizontal",2000,10,10,false] +colour : this.soundColor +var async = Bool_0?', "async": true':'' +var code = '{"type": "vibrate", "direction": "'+Vibrate_List_0+'", "time": '+Int_0+', "speed": '+Int_1+', "power": '+Int_2+async+'},\n'; +return code; +*/; + +animate_s + : '显示动画' EvalString '位置' 'x' PosString? 'y' PosString? '相对窗口坐标' Bool '不等待执行完毕' Bool Newline + + +/* animate_s +tooltip : animate:显示动画,位置填hero或者1,2形式的位置,或者不填代表当前事件点 +helpUrl : /_docs/#/instruction +default : ["zone","","",false,false] +allAnimates : ['EvalString_0'] +material : ["./project/animates/", "EvalString_0"] +menu : [['选择位置', 'editor_blockly.selectPoint(block, ["PosString_0", "PosString_1"])']] +colour : this.soundColor +var loc = PosString_0&&PosString_1?(', "loc": ['+PosString_0+','+PosString_1+']'):''; +Bool_0 = Bool_0?', "alignWindow": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "animate", "name": "'+EvalString_0+'"'+loc+Bool_0+Bool_1+'},\n'; +return code; +*/; + +animate_1_s + : '显示动画并跟随角色' EvalString '不等待执行完毕' Bool Newline + + +/* animate_1_s +tooltip : animate:显示动画并跟随角色 +helpUrl : /_docs/#/instruction +default : ["zone",false] +allAnimates : ['EvalString_0'] +material : ["./project/animates/", "EvalString_0"] +colour : this.soundColor +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "animate", "name": "'+EvalString_0+'", "loc": "hero"'+Bool_0+'},\n'; +return code; +*/; + +stopAnimate_s + : '停止所有动画' '执行动画回调' Bool Newline + +/* stopAnimate_s +tooltip : stopAnimate:停止所有动画 +helpUrl : /_docs/#/instruction +default : [false] +colour : this.soundColor +Bool_0 = Bool_0?', "doCallback": true':''; +var code = '{"type": "stopAnimate"'+Bool_0+'},\n'; +return code; +*/; + +setViewport_s + : '设置视角' '左上角坐标' 'x' PosString? ',' 'y' PosString? '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline + + +/* setViewport_s +tooltip : setViewport: 设置视角 +helpUrl : /_docs/#/instruction +default : ["","","",0,false] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.soundColor +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Int_0 = Int_0 ?(', "time": '+Int_0):''; +Bool_0 = Bool_0?', "async": true':''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var code = '{"type": "setViewport"'+loc+MoveMode_List_0+Int_0+Bool_0+'},\n'; +return code; +*/; + +setViewport_1_s + : '设置视角' '增量坐标' 'dx' PosString? ',' 'dy' PosString? '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline + + +/* setViewport_1_s +tooltip : setViewport: 设置视角 +helpUrl : /_docs/#/instruction +default : ["0","0","",0,false] +colour : this.soundColor +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "dxy": ['+PosString_0+','+PosString_1+']'; +} +Int_0 = Int_0 ?(', "time": '+Int_0):''; +Bool_0 = Bool_0?', "async": true':''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var code = '{"type": "setViewport"'+loc+MoveMode_List_0+Int_0+Bool_0+'},\n'; +return code; +*/; + +lockViewport_s + : '是否锁定视角' Bool Newline + +/* lockViewport_s +tooltip : lockViewport: 是否锁定视角 +helpUrl : /_docs/#/instruction +default : [false] +colour : this.soundColor +Bool_0 = Bool_0 ? (', "lock": true') : ''; +var code = '{"type": "lockViewport"'+Bool_0+'},\n'; +return code; +*/; + +showImage_s + : '显示图片' '图片编号' NInt '图片' EvalString '翻转' Reverse_List BGNL? + '绘制的起点像素' 'x' PosString 'y' PosString '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline + + +/* showImage_s +tooltip : showImage:显示图片 +helpUrl : /_docs/#/instruction +default : [1,"bg.jpg","null","0","0",1,0,false] +allImages : ['EvalString_0'] +menu : [['选择图片','editor_blockly.selectMaterial(block, ["./project/images/:images", "EvalString_0"])']] +previewBlock : true +colour : this.imageColor +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "showImage", "code": '+NInt_0+', "image": "'+EvalString_0+'"'+Reverse_List_0+', "loc": ['+PosString_0+','+PosString_1+'], "opacity": '+Number_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +showImage_1_s + : '显示图片' '图片编号' NInt '图片' EvalString '翻转' Reverse_List BGNL? + '裁剪的起点像素' 'x' PosString 'y' PosString '宽' PosString? '高' PosString? '不透明度' Number BGNL? + '绘制的起点像素' 'x' PosString 'y' PosString '宽' PosString? '高' PosString? '时间' Int '不等待执行完毕' Bool Newline + + +/* showImage_1_s +tooltip : showImage_1:显示图片 +helpUrl : /_docs/#/instruction +default : [1,"bg.jpg","null","0","0","","",1,"0","0","","",0,false] +allImages : ['EvalString_0'] +menu : [['选择图片','editor_blockly.selectMaterial(block, ["./project/images/:images", "EvalString_0"])']] +previewBlock : true +colour : this.imageColor +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "showImage", "code": '+NInt_0+', "image": "'+EvalString_0+'"'+Reverse_List_0+', '+ + '"sloc": ['+PosString_0+','+PosString_1+','+PosString_2+','+PosString_3+'], '+ + '"loc": ['+PosString_4+','+PosString_5+','+PosString_6+','+PosString_7+'], '+ + '"opacity": '+Number_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +showTextImage_s + : '显示图片化文本' EvalString_Multi BGNL? + '图片编号' NInt '起点像素' 'x' PosString 'y' PosString '行距' Number '翻转' Reverse_List '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline + + +/* showTextImage_s +tooltip : showTextImage:显示图片化文本 +helpUrl : /_docs/#/instruction +colour : this.imageColor +doubleclicktext : EvalString_Multi_0 +default : ["可以使用setText事件来控制字体、颜色、大小、偏移量等",1,"0","0",1.4,"null",1,0,false] +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "showTextImage", "code": '+NInt_0+', "text": "'+EvalString_Multi_0+'", "loc": ['+PosString_0+','+PosString_1+'], "lineHeight": '+Number_0+Reverse_List_0+', "opacity": '+Number_1+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +hideImage_s + : '清除图片' '图片编号' NInt '时间' Int '不等待执行完毕' Bool Newline + + +/* hideImage_s +tooltip : hideImage:清除图片 +helpUrl : /_docs/#/instruction +default : [1,0,false] +colour : this.imageColor +var async = Bool_0?', "async": true':''; +var code = '{"type": "hideImage", "code": '+NInt_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +showGif_s + : '显示或清除动图' EvalString? '起点像素位置' 'x' PosString? 'y' PosString? Newline + + +/* showGif_s +tooltip : showGif:显示动图 +helpUrl : /_docs/#/instruction +default : ["","",""] +allImages : ['EvalString_0'] +previewBlock : true +colour : this.imageColor +EvalString_0 = EvalString_0 ? (', "name": "'+EvalString_0+'"') : ''; +var loc = (PosString_0 && PosString_1) ? (', "loc": ['+PosString_0+','+PosString_1+']') : ''; +var code = '{"type": "showGif"'+EvalString_0+loc+'},\n'; +return code; +*/; + +moveImage_s + : '图片移动' '图片编号' NInt '终点像素位置' 'x' PosString? 'y' PosString? BGNL? + '不透明度' EvalString? '移动方式' MoveMode_List '移动时间' Int '不等待执行完毕' Bool Newline + + +/* moveImage_s +tooltip : moveImage:图片移动 +helpUrl : /_docs/#/instruction +default : [1,'','','','',500,false] +colour : this.imageColor +var toloc = ''; +if (PosString_0 && PosString_1) + toloc = ', "to": ['+PosString_0+','+PosString_1+']'; +EvalString_0 = (EvalString_0!=='') ? (', "opacity": '+EvalString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "moveImage", "code": '+NInt_0+toloc+MoveMode_List_0+EvalString_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +rotateImage_s + : '图片旋转' '图片编号' NInt '中心点像素' 'x' PosString? 'y' PosString? '移动方式' MoveMode_List BGNL? '旋转度数(正数顺时针,负数逆时针)' NInt '旋转时间' Int '不等待执行完毕' Bool Newline + + +/* rotateImage_s +tooltip : rotateImage:图片旋转 +helpUrl : /_docs/#/instruction +default : [1,'','','',90,500,false] +colour : this.imageColor +var loc = ''; +if (PosString_0 && PosString_1) + loc = ', "center": ['+PosString_0+','+PosString_1+']'; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "rotateImage", "code": '+NInt_0+loc+', "angle": '+NInt_1+MoveMode_List_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +scaleImage_s + : '图片放缩' '图片编号' NInt '中心点像素' 'x' PosString? 'y' PosString? '移动方式' MoveMode_List BGNL? '放缩比例' Number '动画时间' Int '不等待执行完毕' Bool Newline + + +/* scaleImage_s +tooltip : scaleImage:图片放缩 +helpUrl : /_docs/#/instruction +default : [1,'','','',0.8,0,false] +colour : this.imageColor +if (Number_0 <= 0) throw new Error('放缩比例需要大于0'); +var loc = ''; +if (PosString_0 && PosString_1) + loc = ', "center": ['+PosString_0+','+PosString_1+']'; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "scaleImage", "code": '+NInt_0+loc+', "scale": '+Number_0+MoveMode_List_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +setCurtain_0_s + : '更改画面色调' ColorString Colour '动画时间' IntString? BGNL? Newline '渐变方式' MoveMode_List '持续到下一个本事件' Bool '不等待执行完毕' Bool Newline + + +/* setCurtain_0_s +tooltip : setCurtain: 更改画面色调,动画时间可不填 +helpUrl : /_docs/#/instruction +default : ["255,255,255,1",'rgba(255,255,255,1)',500,'',true,false] +colour : this.soundColor +previewBlock : true +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +Bool_0 = Bool_0 ? ', "keep": true' : ''; +var async = Bool_1?', "async": true':''; +var code = '{"type": "setCurtain", "color": ['+ColorString_0+']'+IntString_0+MoveMode_List_0+Bool_0+async+'},\n'; +return code; +*/; + +setCurtain_1_s + : '恢复画面色调' '动画时间' IntString? '渐变方式' MoveMode_List '不等待执行完毕' Bool Newline + + +/* setCurtain_1_s +tooltip : setCurtain: 恢复画面色调,动画时间可不填 +helpUrl : /_docs/#/instruction +default : [500,'',false] +colour : this.soundColor +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "setCurtain"'+IntString_0+MoveMode_List_0 +async+'},\n'; +return code; +*/; + +screenFlash_s + : '画面闪烁' ColorString Colour '单次时间' Int '执行次数' IntString? '渐变方式' MoveMode_List '不等待执行完毕' Bool Newline + +/* screenFlash_s +tooltip : screenFlash: 画面闪烁,动画时间可不填 +helpUrl : /_docs/#/instruction +default : ["255,255,255,1",'rgba(255,255,255,1)',500,1,'',false] +colour : this.soundColor +if (ColorString_0 == '') throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); +IntString_0 = IntString_0 ? (', "times": '+IntString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "screenFlash", "color": ['+ColorString_0+'], "time": '+Int_0 +IntString_0+MoveMode_List_0+async+'},\n'; +return code; +*/; + +setWeather_s + : '更改天气' Weather_List '强度' Int '持续到下个本事件' Bool Newline + + +/* setWeather_s +tooltip : setWeather:更改天气 +helpUrl : /_docs/#/instruction +default : [null,1,true] +colour : this.soundColor +if(Int_0<1 || Int_0>10) throw new Error('天气的强度等级, 在1-10之间'); +Bool_0 = Bool_0 ? ', "keep": true' : '' +var code = '{"type": "setWeather", "name": "'+Weather_List_0+'", "level": '+Int_0+Bool_0+'},\n'; +if(Weather_List_0===''||Weather_List_0==='null'||Weather_List_0==null)code = '{"type": "setWeather"},\n'; +return code; +*/; + +move_s + : '移动事件' 'x' PosString? ',' 'y' PosString? '动画时间' IntString? '不消失' Bool '不等待执行完毕' Bool BGNL? moveDirection+ Newline + + +/* move_s +tooltip : move: 让某个NPC/怪物移动,位置可不填代表当前事件 +helpUrl : /_docs/#/instruction +default : ["","",500,true,false,null] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "keep": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "move"'+floorstr+IntString_0+Bool_0+Bool_1+', "steps": ['+moveDirection_0.trim().substring(2)+']},\n'; +return code; +*/; + +moveDirection + : '移动方向' Move_List '格数' Int Newline + +/* moveDirection +tooltip : 移动方向 +helpUrl : /_docs/#/instruction +default : ["up", 0] +colour : this.subColor +if (Move_List_0 == 'speed' && Int_0 < 16) throw '设置的移动速度值不得小于16'; +return ', "' + Move_List_0 + ':' + Int_0 + '"'; +*/; + +moveAction_s + : '勇士前进一格或撞击' Newline + + +/* moveAction_s +tooltip : moveAction: 前进一格或撞击 +helpUrl : /_docs/#/instruction +colour : this.dataColor +return '{"type": "moveAction"},\n'; +*/; + + + +moveHero_s + : '无视地形移动勇士' '动画时间' IntString? '不等待执行完毕' Bool BGNL? moveDirection+ Newline + + +/* moveHero_s +tooltip : moveHero:移动勇士,用这种方式移动勇士的过程中将无视一切地形, 无视一切事件, 中毒状态也不会扣血 +helpUrl : /_docs/#/instruction +default : ["",false,"上右3下2后4左前2"] +colour : this.mapColor +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "moveHero"'+IntString_0+Bool_0+', "steps": ['+moveDirection_0.trim().substring(2)+']},\n'; +return code; +*/; + +jump_s + : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? '动画时间' IntString? '不消失' Bool '不等待执行完毕' Bool Newline + + +/* jump_s +tooltip : jump: 让某个NPC/怪物跳跃 +helpUrl : /_docs/#/instruction +default : ["","","","",500,true,false] +selectPoint : ["PosString_2", "PosString_3"] +menu : [['选择起点位置','editor_blockly.selectPoint(block,["PosString_0", "PosString_1"])']] +colour : this.mapColor + +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr += ', "from": ['+PosString_0+','+PosString_1+']'; +} +if (PosString_2 && PosString_3) { + floorstr += ', "to": ['+PosString_2+','+PosString_3+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "keep": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "jump"'+floorstr+''+IntString_0+Bool_0+Bool_1+'},\n'; +return code; +*/; + +jump_1_s + : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '增量 dx' PosString? ',' 'dy' PosString? '动画时间' IntString? '不消失' Bool '不等待执行完毕' Bool Newline + + +/* jump_1_s +tooltip : jump: 让某个NPC/怪物跳跃,给定增量 +helpUrl : /_docs/#/instruction +default : ["","","0","0",500,true,false] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor + +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr += ', "from": ['+PosString_0+','+PosString_1+']'; +} +if (PosString_2 && PosString_3) { + floorstr += ', "dxy": ['+PosString_2+','+PosString_3+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "keep": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "jump"'+floorstr+''+IntString_0+Bool_0+Bool_1+'},\n'; +return code; +*/; + +jumpHero_s + : '跳跃勇士' 'x' PosString? ',' 'y' PosString? '动画时间' IntString? '不等待执行完毕' Bool Newline + + +/* jumpHero_s +tooltip : jumpHero: 跳跃勇士 +helpUrl : /_docs/#/instruction +default : ["","",500,false] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "jumpHero"'+floorstr+IntString_0+Bool_0+'},\n'; +return code; +*/; + +jumpHero_1_s + : '跳跃勇士' '增量 dx' PosString? ',' 'dy' PosString? '动画时间' IntString? '不等待执行完毕' Bool Newline + + +/* jumpHero_1_s +tooltip : jumpHero: 跳跃勇士,给定增量 +helpUrl : /_docs/#/instruction +default : ["0","0",500,false] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "dxy": ['+PosString_0+','+PosString_1+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "jumpHero"'+floorstr+IntString_0+Bool_0+'},\n'; +return code; +*/; + +playBgm_s + : '播放背景音乐' EvalString '开始播放秒数' Int '持续到下个本事件' Bool Newline + + +/* playBgm_s +tooltip : playBgm: 播放背景音乐 +helpUrl : /_docs/#/instruction +default : ["bgm.mp3", 0, true] +allBgms : ['EvalString_0'] +material : ["./project/bgms/", "EvalString_0"] +colour : this.imageColor +Int_0 = Int_0 ? (', "startTime": '+Int_0) : ''; +Bool_0 = Bool_0 ? ', "keep": true' : ''; +var code = '{"type": "playBgm", "name": "'+EvalString_0+'"'+Int_0+Bool_0+'},\n'; +return code; +*/; + +pauseBgm_s + : '暂停背景音乐' Newline + + +/* pauseBgm_s +tooltip : pauseBgm: 暂停背景音乐 +helpUrl : /_docs/#/instruction +colour : this.imageColor +var code = '{"type": "pauseBgm"},\n'; +return code; +*/; + +resumeBgm_s + : '恢复背景音乐' '从暂停位置继续播放' Bool Newline + + +/* resumeBgm_s +tooltip : resumeBgm: 恢复背景音乐 +helpUrl : /_docs/#/instruction +colour : this.imageColor +Bool_0 = Bool_0 ? ', "resume": true' : ''; +var code = '{"type": "resumeBgm"' + Bool_0 + '},\n'; +return code; +*/; + +loadBgm_s + : '预加载背景音乐' EvalString Newline + + +/* loadBgm_s +tooltip : loadBgm: 预加载某个背景音乐,之后可以直接播放 +helpUrl : /_docs/#/instruction +default : ["bgm.mp3"] +allBgms : ['EvalString_0'] +material : ["./project/bgms/", "EvalString_0"] +colour : this.imageColor +var code = '{"type": "loadBgm", "name": "'+EvalString_0+'"},\n'; +return code; +*/; + +freeBgm_s + : '释放背景音乐的缓存' EvalString Newline + + +/* freeBgm_s +tooltip : freeBgm: 释放背景音乐的缓存 +helpUrl : /_docs/#/instruction +default : ["bgm.mp3"] +allBgms : ['EvalString_0'] +colour : this.imageColor +var code = '{"type": "freeBgm", "name": "'+EvalString_0+'"},\n'; +return code; +*/; + +playSound_s + : '播放音效' EvalString '停止之前音效' Bool? '音调' IntString? '等待播放完毕' Bool? Newline + + +/* playSound_s +tooltip : playSound: 播放音效 +helpUrl : /_docs/#/instruction +default : ["item.mp3",false,"",false] +colour : this.imageColor +allSounds : ['EvalString_0'] +material : ["./project/sounds/", "EvalString_0"] +if (IntString_0) { + if (parseInt(IntString_0) < 30 || parseInt(IntString_0) > 300) throw '音调设置只能在30-300之间;100为正常音调。'; + IntString_0 = ', "pitch": ' + IntString_0; +} else IntString_0 = ''; +Bool_0 = Bool_0 ? ', "stop": true' : ''; +Bool_1 = Bool_1 ? ', "sync": true' : ''; +var code = '{"type": "playSound", "name": "'+EvalString_0+'"'+Bool_0+IntString_0+Bool_1+'},\n'; +return code; +*/; + +playSound_1_s + : '播放系统音效' NameMap_List '停止之前音效' Bool? '音调' IntString? '等待播放完毕' Bool? Newline + + +/* playSound_1_s +tooltip : playSound: 播放系统音效 +helpUrl : /_docs/#/instruction +default : ["确定",false,"",false] +colour : this.imageColor +if (IntString_0) { + if (parseInt(IntString_0) < 30 || parseInt(IntString_0) > 300) throw '音调设置只能在30-300之间;100为正常音调。'; + IntString_0 = ', "pitch": ' + IntString_0; +} else IntString_0 = ''; +Bool_0 = Bool_0 ? ', "stop": true' : ''; +Bool_1 = Bool_1 ? ', "sync": true' : ''; +var code = '{"type": "playSound", "name": "'+NameMap_List_0+'"'+Bool_0+IntString_0+Bool_1+'},\n'; +return code; +*/; + +stopSound_s + : '停止所有音效' Newline + + +/* stopSound_s +tooltip : stopSound: 停止所有音效 +helpUrl : /_docs/#/instruction +colour : this.imageColor +var code = '{"type": "stopSound"},\n'; +return code; +*/; + +setVolume_s + : '设置音量' Int '渐变时间' IntString? '不等待执行完毕' Bool Newline + + +/* setVolume_s +tooltip : setVolume: 设置音量 +helpUrl : /_docs/#/instruction +default : [90, 500, false] +colour : this.imageColor +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "setVolume", "value": '+Int_0+IntString_0+async+'},\n'; +return code; +*/; + +setBgmSpeed_s + : '设置背景音乐播放速度' Int '同时改变音调' Bool Newline + + +/* setBgmSpeed_s +tooltip : setSpeed: 设置背景音乐播放速度 +helpUrl : /_docs/#/instruction +default : [100, true] +colour : this.imageColor +if (Int_0 < 30 || Int_0 > 300) throw '速度只能设置只能在30-300之间;100为正常速度。'; +Bool_0 = Bool_0?', "pitch": true':''; +var code = '{"type": "setBgmSpeed", "value": '+Int_0+Bool_0+'},\n'; +return code; +*/; + +win_s + : '游戏胜利,结局' ':' EvalString? '不计入榜单' Bool '不结束游戏' Bool Newline + + +/* win_s +tooltip : win: 获得胜利, 该事件会显示获胜页面, 并重新游戏 +helpUrl : /_docs/#/instruction +default : ["",false, false] +Bool_0 = Bool_0?', "norank": 1':''; +Bool_1 = Bool_1?', "noexit": 1':''; +var code = '{"type": "win", "reason": "'+EvalString_0+'"'+Bool_0+Bool_1+'},\n'; +return code; +*/; + +lose_s + : '游戏失败,结局' ':' EvalString? Newline + + +/* lose_s +tooltip : lose: 游戏失败, 该事件会显示失败页面, 并重新开始游戏 +helpUrl : /_docs/#/instruction +default : [""] +var code = '{"type": "lose", "reason": "'+EvalString_0+'"},\n'; +return code; +*/; + +restart_s + : '直接回到标题界面' Newline + + +/* restart_s +tooltip : restart: 直接回到标题界面 +helpUrl : /_docs/#/instruction +var code = '{"type": "restart"},\n'; +return code; +*/; + +input_s + : '接受用户输入数字,提示' ':' EvalString Newline + + +/* input_s +tooltip : input:接受用户输入数字, 事件只能接受非负整数输入, 所有非法的输入将全部变成0 +helpUrl : /_docs/#/instruction +default : ["请输入一个数"] +colour : this.dataColor +var code = '{"type": "input", "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +input2_s + : '接受用户输入文本,提示' ':' EvalString Newline + + +/* input2_s +tooltip : input2:接受用户输入文本, 允许用户输入任何形式的文本 +helpUrl : /_docs/#/instruction +default : ["请输入文本"] +colour : this.dataColor +var code = '{"type": "input2", "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +if_s + : '如果' ':' expression BGNL? Newline action+ '否则' ':' BGNL? Newline action+ BEND Newline + + +/* if_s +tooltip : if: 条件判断 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "if", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"true": [\n',action_0,'],\n', + '"false": [\n',action_1,']', +'},\n'].join(''); +return code; +*/; + +if_1_s + : '如果' ':' expression BGNL? Newline action+ BEND Newline + + +/* if_1_s +tooltip : if: 条件判断 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "if", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"true": [\n',action_0,']', +'},\n'].join(''); +return code; +*/; + +switch_s + : '多重分歧 条件判定' ':' expression BGNL? Newline switchCase+ BEND Newline + + +/* switch_s +tooltip : switch: 多重条件分歧 +helpUrl : /_docs/#/instruction +default : ["判别值"] +colour : this.eventColor +var code = ['{"type": "switch", "condition": "',expression_0,'", ', + block.isCollapsed()?'"_collapsed": true, ':'', + block.isEnabled()?'':'"_disabled": true, ', + '"caseList": [\n', + switchCase_0, +'], },\n'].join(''); +return code; +*/; + +switchCase + : '如果是' expression '的场合' '不跳出' Bool BGNL? Newline action+ + + +/* switchCase +tooltip : 选项的选择 +helpUrl : /_docs/#/instruction +default : ["", false] +colour : this.subColor +Bool_0 = Bool_0?', "nobreak": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "'+expression_0+'"'+Bool_0+collapsed+disabled+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +choices_s + : '选项' ':' EvalString_Multi? BGNL? '标题' EvalString? '图像' IdString? '超时毫秒数' Int '宽度' IntString? BGNL? Newline choicesContext+ BEND Newline + + +/* choices_s +tooltip : choices: 给用户提供选项 +helpUrl : /_docs/#/instruction +previewBlock : true +default : ["","流浪者","trader",0,''] +allIds : ['IdString_0'] +var title=''; +if (EvalString_0==''){ + if (IdString_0=='')title=''; + else title='\\t['+IdString_0+']'; +} else { + if (IdString_0=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+IdString_0+']'; +} +EvalString_Multi_0 = title+EvalString_Multi_0; +EvalString_Multi_0 = EvalString_Multi_0 ?(', "text": "'+EvalString_Multi_0+'"'):''; +Int_0 = Int_0 ? (', "timeout": '+Int_0) : ''; +IntString_0 = IntString_0 ? (', "width": ' + IntString_0) : ''; +var code = ['{"type": "choices"',EvalString_Multi_0,Int_0,IntString_0, + block.isCollapsed()?', "_collapsed": true':'', + block.isEnabled()?'':', "_disabled": true', + ', "choices": [\n', + choicesContext_0, +']},\n'].join(''); +return code; +*/; + +choicesContext + : '子选项' EvalString '图标' IdString? '颜色' ColorString? Colour '启用条件' EvalString? '出现条件' EvalString? BGNL? Newline action+ + + +/* choicesContext +tooltip : 选项的选择 +helpUrl : /_docs/#/instruction +default : ["提示文字:红钥匙","","","",""] +allIds : ['IdString_0'] +colour : this.subColor +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +EvalString_1 = EvalString_1 && (', "need": "'+EvalString_1+'"'); +EvalString_2 = EvalString_2 && (', "condition": "'+EvalString_2+'"'); +IdString_0 = IdString_0?(', "icon": "'+IdString_0+'"'):''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"text": "'+EvalString_0+'"'+IdString_0+ColorString_0+EvalString_1+EvalString_2+collapsed+disabled+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +confirm_s + : '显示确认框' ':' EvalString_Multi '超时毫秒数' Int BGNL? '确定的场合' ':' '(默认选中' Bool ')' BGNL? Newline action+ '取消的场合' ':' BGNL? Newline action+ BEND Newline + +/* confirm_s +tooltip : 弹出确认框 +helpUrl : /_docs/#/instruction +default : ["确认要xxx吗?",0,false] +previewBlock : true +Bool_0 = Bool_0?', "default": true':'' +Int_0 = Int_0 ? (', "timeout": '+Int_0) : ''; +var code = ['{"type": "confirm"'+Int_0+Bool_0+', "text": "',EvalString_Multi_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"yes": [\n',action_0,'],\n', + '"no": [\n',action_1,']\n', +'},\n'].join(''); +return code; +*/; + +for_s + : '循环遍历' ': ' expression '从' EvalString '到' EvalString '步增' EvalString BGNL? Newline action+ BEND Newline + +/* for_s +tooltip : for:循环遍历 +helpUrl : /_docs/#/instruction +colour : this.eventColor +if (!/^temp:[A-Z]$/.test(expression_0)) { + throw new Error('循环遍历仅允许使用临时变量!'); +} +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +return '{"type": "for", "name": "'+expression_0+'", "from": "'+EvalString_0+'", "to": "'+EvalString_1+'", "step": "'+EvalString_2+'"'+collapsed+disabled+',\n"data": [\n'+action_0+']},\n'; +*/; + +forEach_s + : '循环遍历' ': 以' expression '逐项读取列表' JsonEvalString BGNL? Newline action+ BEND Newline + +/* forEach_s +tooltip : forEach:循环遍历列表 +helpUrl : /_docs/#/instruction +colour : this.eventColor +if (!/^temp:[A-Z]$/.test(expression_0)) { + throw new Error('循环遍历仅允许使用临时变量!'); +} +if (JsonEvalString_0 == '' || !(JSON.parse(JsonEvalString_0) instanceof Array)) { + throw new Error('参数列表必须是个有效的数组!'); +} +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +return '{"type": "forEach", "name": "'+expression_0+'", "list": '+JsonEvalString_0 + collapsed+disabled+',\n"data": [\n'+action_0+']},\n'; +*/; + +while_s + : '前置条件循环' ':' '当' expression '时' BGNL? Newline action+ BEND Newline + +/* while_s +tooltip : while:前置条件循环 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "while", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"data": [\n',action_0,'],\n', +'},\n'].join(''); +return code; +*/; + +dowhile_s + : '后置条件循环' ':' BGNL? Newline action+ BEND '当' expression '时' Newline + +/* dowhile_s +tooltip : dowhile:后置条件循环 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "dowhile", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"data": [\n',action_0,'],\n', +'},\n'].join(''); +return code; +*/; + +break_s + : '跳出循环或公共事件' '层数' Int Newline + +/* break_s +tooltip : break:跳出循环或公共事件! +helpUrl : /_docs/#/instruction +colour : this.eventColor +default : [1] +if (Int_0 <= 0) throw "层数至少为1!"; +var code = '{"type": "break", "n": '+Int_0+'},\n'; +return code; +*/; + +continue_s + : '提前结束循环或跳出公共事件' '层数' Int Newline + +/* continue_s +tooltip : continue:提前结束循环或跳出公共事件,或跳出公共事件! +helpUrl : /_docs/#/instruction +colour : this.eventColor +default : [1] +if (Int_0 <= 0) throw "层数至少为1!"; +var code = '{"type": "continue", "n": '+Int_0+'},\n'; +return code; +*/; + + +wait_s + : '等待用户操作并获得按键或点击信息' '仅检测子块' Bool '超时毫秒数' Int BGNL? Newline waitContext* BEND Newline + + +/* wait_s +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +default : [true,0] +colour : this.soundColor +Bool_0 = Bool_0?(', "forceChild": true'):''; +Int_0 = Int_0?(', "timeout": ' + Int_0):''; +waitContext_0 = waitContext_0 ? (', "data": [\n' + waitContext_0 + ']') : ''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"type": "wait"' + Bool_0 + Int_0 + collapsed + disabled + waitContext_0 + '},\n'; +return code; +*/; + + +waitContext + : waitContext_1 + | waitContext_2 + | waitContext_3 + | waitContext_4 + | waitContext_empty; + + +waitContext_1 + : '按键的场合:' '键值(右键查表)' EvalString '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_1 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +colour : this.subColor +default : ["",false] +menu : [["查询键值表", "editor_blockly.showKeyCodes()"]] +if (!/^\d+(,\d+)*$/.test(EvalString_0)) { + throw new Error('键值必须是正整数,可以以逗号分隔'); +} +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "keyboard", "keycode": "' + EvalString_0 + '"'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + + +waitContext_2 + : '点击的场合:' '像素x范围' PosString '~' PosString '; y范围' PosString '~' PosString '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_2 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +default : [0,32,0,32,false] +previewBlock : true +colour : this.subColor +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "mouse", "px": [' + PosString_0 + ',' + PosString_1 + '], "py": [' + PosString_2 + ',' + PosString_3 + ']'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_3 + : '自定义条件的场合:' expression '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_3 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +default : ["true",false] +colour : this.subColor +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "condition", "condition": "'+expression_0+'"'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_4 + : '超时的场合:' '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_4 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +colour : this.subColor +default : [false] +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "timeout"'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_empty : Newline + +/* waitContext_empty +return ''; +*/; + + +waitAsync_s + : '等待所有异步事件执行完毕' '不等待动画' Bool '等待音效' Bool + + +/* waitAsync_s +tooltip : waitAsync: 等待所有异步事件执行完毕 +helpUrl : /_docs/#/instruction +default : [false, false] +colour : this.soundColor +Bool_0 = Bool_0 ? ', "excludeAnimates": true' : ''; +Bool_1 = Bool_1 ? ', "includeSounds": true' : ''; +var code = '{"type": "waitAsync"'+Bool_0+Bool_1+'},\n'; +return code; +*/; + + +stopAsync_s + : '立刻结束所有异步事件' BGNL Newline + + +/* stopAsync_s +tooltip : stopAsync: 立刻结束所有异步事件 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "stopAsync"},\n'; +return code; +*/; + + +callBook_s + : '呼出怪物手册' + + +/* callBook_s +tooltip : callBook: 呼出怪物手册;返回游戏后将继续执行后面的事件 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "callBook"},\n'; +return code; +*/; + + +callSave_s + : '呼出存档页面' + + +/* callSave_s +tooltip : callSave: 呼出存档页面 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "callSave"},\n'; +return code; +*/; + + +autoSave_s + : '自动存档' '读档到触发前' Bool Newline + + +/* autoSave_s +tooltip : autoSave: 自动存档 +helpUrl : /_docs/#/instruction +colour : this.soundColor +default : [false] +Bool_0 = Bool_0 ? (', "removeLast": true') : ''; +var code = '{"type": "autoSave"'+Bool_0+'},\n'; +return code; +*/; + + +forbidSave_s + : '是否禁止存档' Bool Newline + + +/* forbidSave_s +tooltip : forbidSave: 禁止存档 +helpUrl : /_docs/#/instruction +colour : this.soundColor +default : [false] +Bool_0 = Bool_0 ? (', "forbid": true') : ''; +var code = '{"type": "forbidSave"'+Bool_0+'},\n'; +return code; +*/; + + +callLoad_s + : '呼出读档页面' Newline + + +/* callLoad_s +tooltip : callLoad: 呼出存档页面;返回游戏后将继续执行后面的事件 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "callLoad"},\n'; +return code; +*/; + + +previewUI_s + : 'ui绘制并预览' '(双击此项可进行预览)' BGNL? Newline action+ BEND Newline + + +/* previewUI_s +tooltip : previewUI: ui绘制并预览 +helpUrl : /_docs/#/instruction +previewBlock : true +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = ['{"type": "previewUI"'+collapsed+disabled+', "action": [\n', action_0,']},\n'].join(''); +return code; +*/; + + +clearMap_s + : '清除画布' '起点像素' 'x' PosString? 'y' PosString? '宽' PosString? '高' PosString? Newline + +/* clearMap_s +tooltip : clearMap: 清除画布 +helpUrl : /_docs/#/instruction +colour : this.uiColor +default : ["", "", "", ""] +previewBlock : true +PosString_0 = PosString_0 && (', "x": ' + PosString_0); +PosString_1 = PosString_1 && (', "y": ' + PosString_1); +PosString_2 = PosString_2 && (', "width": ' + PosString_2); +PosString_3 = PosString_3 && (', "height": ' + PosString_3); +var code = '{"type": "clearMap"'+PosString_0+PosString_1+PosString_2+PosString_3+'},\n'; +return code; +*/; + + +setAttribute_s + : '设置画布属性' '字体' FontString? '填充样式' ColorString? Colour '边框样式' ColorString? Colour BGNL? '线宽度' IntString? '不透明度' EvalString? '对齐' TextAlign_List '基准线' TextBaseline_List 'z值' IntString? Newline + +/* setAttribute_s +tooltip : setAttribute:设置画布属性 +helpUrl : /_docs/#/instruction +previewBlock : true +colour : this.uiColor +default : ["","",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"","",null,null,""] +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +TextBaseline_List_0 = TextBaseline_List_0==='null'?'': ', "baseline": "'+TextBaseline_List_0+'"'; +FontString_0 = FontString_0 ? (', "font": "' + FontString_0 + '"') : ''; +ColorString_0 = ColorString_0 ? (', "fillStyle": ['+ColorString_0+']') : ''; +ColorString_1 = ColorString_1 ? (', "strokeStyle": ['+ColorString_1+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +if (EvalString_0) { + var f = parseFloat(EvalString_0); + if (isNaN(f) || f<0 || f>1) throw new Error('不透明度必须是0到1的浮点数或不填'); + EvalString_0 = ', "alpha": '+EvalString_0; +} +IntString_1 = IntString_1 ? (', "z": '+IntString_1) : ''; +var code = '{"type": "setAttribute"'+FontString_0+ColorString_0+ColorString_1+IntString_0+ + EvalString_0+TextAlign_List_0+TextBaseline_List_0+IntString_1+'},\n'; +return code; +*/; + + +setFilter_s + : '设置画布特效' '虚化' Number '色相' Int '灰度' Number '反色' Bool '阴影' Number Newline + + +/* setFilter_s +tooltip : setFilter: 设置画布特效 +helpUrl : /_docs/#/instruction +default : [0,0,0,false,0] +previewBlock : true +colour : this.uiColor +if (Number_0 < 0) throw '虚化不得小于0;0为完全没有虚化'; +if (Int_0 < 0 || Int_0 >= 360) throw '色相需要在0~359之间'; +if (Number_1 < 0 || Number_1 > 1) throw '灰度需要在0~1之间'; +if (Number_2 < 0) throw '阴影不得小于0;0为完全没有阴影'; +var code = '{"type": "setFilter", "blur": '+Number_0+', "hue": '+Int_0+', "grayscale": '+Number_1+', "invert": '+Bool_0+', "shadow": '+Number_2+'},\n'; +return code; +*/; + + +fillText_s + : '绘制文本' 'x' PosString 'y' PosString '样式' ColorString? Colour '字体' FontString? '最大宽度' IntString? BGNL? EvalString Newline + +/* fillText_s +tooltip : fillText:绘制一行文本;可以设置最大宽度进行放缩 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","",'rgba(255,255,255,1)',"","","绘制一行文本"] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +FontString_0 = FontString_0 ? (', "font": "' + FontString_0 + '"') : ''; +IntString_0 = IntString_0 ? (', "maxWidth": '+IntString_0) : ''; +var code = '{"type": "fillText", "x": '+PosString_0+', "y": '+PosString_1+ColorString_0+FontString_0+IntString_0+', "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +fillBoldText_s + : '绘制描边文本' 'x' PosString 'y' PosString '样式' ColorString? Colour '描边颜色' ColorString? Colour '字体' FontString? BGNL? EvalString Newline + +/* fillBoldText_s +tooltip : fillBoldText:绘制一行描边文本 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","",'rgba(255,255,255,1)',"",'rgba(0,0,0,1)',"","绘制一行描边文本"] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +ColorString_1 = ColorString_1 ? (', "strokeStyle": ['+ColorString_1+']') : ''; +FontString_0 = FontString_0 ? (', "font": "' + FontString_0 + '"') : ''; +var code = '{"type": "fillBoldText", "x": '+PosString_0+', "y": '+PosString_1+ColorString_0+ColorString_1+FontString_0+', "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +drawTextContent_s + : '绘制多行文本' EvalString_Multi BGNL? '起点像素' 'x' PosString 'y' PosString '最大宽度' IntString? '颜色' ColorString? Colour BGNL? '对齐' TextAlign_List '字体大小' IntString? '行距' IntString? '粗体' Bool Newline + +/* drawTextContent_s +tooltip : drawTextContent:绘制多行文本 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +menu : [['预览多行文本','editor_blockly.previewBlock(block)']] +colour : this.uiColor +default : ["绘制多行文本\\n可双击编辑","0","0","","",'rgba(255,255,255,1)',null,"","",false] +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +Bool_0 = Bool_0 ? (', "bold": true') : ''; +IntString_0 = IntString_0 ? (', "maxWidth": '+IntString_0) : ''; +IntString_1 = IntString_1 ? (', "fontSize": '+IntString_1) : ''; +IntString_2 = IntString_2 ? (', "lineHeight": '+IntString_2) : ''; +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +var code = '{"type": "drawTextContent", "text": "'+EvalString_Multi_0+'", "left": '+PosString_0+', "top": '+PosString_1+TextAlign_List_0+IntString_0+IntString_1+IntString_2+ColorString_0+Bool_0+'},\n'; +return code; +*/; + +fillRect_s + : '绘制矩形' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString '圆角半径' PosString? '旋转度数' PosString? '颜色' ColorString? Colour Newline + +/* fillRect_s +tooltip : fillRect:绘制矩形 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","","","rgba(255,255,255,1)"] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +PosString_4 = PosString_4 ? (', "radius": '+PosString_4) : ''; +PosString_5 = PosString_5 ? (', "angle": ' + PosString_5) : ''; +var code = '{"type": "fillRect", "x": '+PosString_0+', "y": '+PosString_1+', "width": '+PosString_2+', "height": '+PosString_3+PosString_4+PosString_5+ColorString_0+'},\n'; +return code; +*/; + +strokeRect_s + : '绘制矩形边框' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString '圆角半径' PosString? '旋转度数' PosString? '颜色' ColorString? Colour '线宽' IntString? Newline + +/* strokeRect_s +tooltip : strokeRect:绘制矩形边框 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","","","rgba(255,255,255,1)",""] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +PosString_4 = PosString_4 ? (', "radius": '+PosString_4) : ''; +PosString_5 = PosString_5 ? (', "angle": ' + PosString_5) : ''; +var code = '{"type": "strokeRect", "x": '+PosString_0+', "y": '+PosString_1+', "width": '+PosString_2+', "height": '+PosString_3+PosString_4+PosString_5+ColorString_0+IntString_0+'},\n'; +return code; +*/; + +drawLine_s + : '绘制线段' '起点像素' 'x' PosString 'y' PosString '终点像素' 'x' PosString 'y' PosString '颜色' ColorString? Colour '线宽' IntString? Newline + +/* drawLine_s +tooltip : drawLine:绘制线段 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","rgba(255,255,255,1)",""] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +var code = '{"type": "drawLine", "x1": '+PosString_0+', "y1": '+PosString_1+', "x2": '+PosString_2+', "y2": '+PosString_3+ColorString_0+IntString_0+'},\n'; +return code; +*/; + +drawArrow_s + : '绘制箭头' '起点像素' 'x' PosString 'y' PosString '终点像素' 'x' PosString 'y' PosString '颜色' ColorString? Colour '线宽' IntString? Newline + +/* drawArrow_s +tooltip : drawArrow:绘制箭头 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","rgba(255,255,255,1)",""] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +var code = '{"type": "drawArrow", "x1": '+PosString_0+', "y1": '+PosString_1+', "x2": '+PosString_2+', "y2": '+PosString_3+ColorString_0+IntString_0+'},\n'; +return code; +*/; + + +fillPolygon_s + : '绘制多边形' '顶点像素列表' 'x' EvalString 'y' EvalString '颜色' ColorString? Colour Newline + +/* fillPolygon_s +tooltip : fillPolygon:绘制多边形 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0,0,100","0,100,0","","rgba(255,255,255,1)"] +var pattern2 = /^([+-]?\d+)(,[+-]?\d+)*$/; +if(!pattern2.test(EvalString_0) || !pattern2.test(EvalString_1))throw new Error('坐标格式错误,请右键点击帮助查看格式'); +EvalString_0=EvalString_0.split(','); +EvalString_1=EvalString_1.split(','); +if(EvalString_0.length!==EvalString_1.length)throw new Error('坐标格式错误,请右键点击帮助查看格式'); +for(var ii=0;ii': Blockly.JavaScript.ORDER_RELATIONAL, + '<': Blockly.JavaScript.ORDER_RELATIONAL, + '>=': Blockly.JavaScript.ORDER_RELATIONAL, + '<=': Blockly.JavaScript.ORDER_RELATIONAL, + '&&': Blockly.JavaScript.ORDER_LOGICAL_AND, + '||': Blockly.JavaScript.ORDER_LOGICAL_OR, + '^': Blockly.JavaScript.ORDER_BITWISE_XOR, + 'min': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'max': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'startsWith': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'endsWith': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'includes': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA +} +return [code, orders[Arithmetic_List_0]]; +*/; + +negate_e + : '非' expression + + +/* negate_e +//todo 修改recieveOrder : ORDER_LOGICAL_NOT 修改 inputsInline +var code = '!'+expression_0; +return [code, Blockly.JavaScript.ORDER_LOGICAL_NOT]; +*/; + +unaryOperation_e + : UnaryOperator_List expression + + +/* unaryOperation_e +var code = UnaryOperator_List_0 + '(' + expression_0 + ')'; +return [code, Blockly.JavaScript.ORDER_MEMBER]; +*/; + + +bool_e + : ':' Bool + + +/* bool_e +tooltip : 逻辑是否 +var code = Bool_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +idString_e + : IdString + + +/* idString_e +colour : this.idstring_eColor +default : ["变量:生命"] +var code = IdString_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + +idIdList_e + : Id_List ':' IdText + + +/* idIdList_e +colour : this.idstring_eColor +default : [null,"自定义flag"] +var code = MotaActionFunctions.replaceFromName(MotaActionFunctions.replaceToName(Id_List_0+':'+IdText_0)); +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + +idFixedList_e + : FixedId_List + + +/* idFixedList_e +colour : this.idstring_eColor +var code = FixedId_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +enemyattr_e + : '怪物' IdString '的' EnemyId_List + + +/* enemyattr_e +default : ['greenSlime',"hp"] +allEnemys : ['IdString_0'] +var code = 'enemy:'+IdString_0+':'+EnemyId_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +blockId_e + : '图块ID:' PosString ',' PosString + + +/* blockId_e +default : [0,0] +if (/^\d+$/.test(PosString_0) && /^\d+$/.test(PosString_1)) { + return ['blockId:'+PosString_0+','+PosString_1, Blockly.JavaScript.ORDER_ATOMIC]; +} +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +return ['core.getBlockId('+PosString_0+','+PosString_1+')', Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +blockNumber_e + : '图块数字:' PosString ',' PosString + + +/* blockNumber_e +default : [0,0] +if (/^\d+$/.test(PosString_0) && /^\d+$/.test(PosString_1)) { + return ['blockNumber:'+PosString_0+','+PosString_1, Blockly.JavaScript.ORDER_ATOMIC]; +} +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +return ['core.getBlockNumber('+PosString_0+','+PosString_1+')', Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +blockCls_e + : '图块类别:' PosString ',' PosString + + +/* blockCls_e +default : [0,0] +if (/^\d+$/.test(PosString_0) && /^\d+$/.test(PosString_1)) { + return ['blockCls:'+PosString_0+','+PosString_1, Blockly.JavaScript.ORDER_ATOMIC]; +} +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +return ['core.getBlockCls('+PosString_0+','+PosString_1+')', Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +nextXY_e + : '前方' NInt '格的' NextXY_List + +/* nextXY_e +default : [1, 'nextX'] +var code = NextXY_List_0 == 'nextY' ? ('core.nextY('+NInt_0+')') : ('core.nextX('+NInt_0+')'); +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +isReplaying_e + : '录像播放中' + +/* isReplaying_e +var code = 'core.isReplaying()'; +return [code, Blockly.JavaScript.ORDER_ATOMIC];; +*/; + + +hasVisitedFloor_e + : '访问过楼层' IdString + +/* hasVisitedFloor_e +default : ['MT0'] +allFloorIds : ['IdString_0'] +var code = 'core.hasVisitedFloor(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +isShopVisited_e + : '开启过商店' IdString + +/* isShopVisited_e +default : ['shop1'] +allShops : ['IdString_0'] +var code = 'core.isShopVisited(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +hasEquip_e + : '当前正在装备' IdString + +/* hasEquip_e +default : ['sword1'] +allEquips : ['IdString_0'] +var code = 'core.hasEquip(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +canBattle_e + : '当前能否战斗' IdString + +/* canBattle_e +default : ['greenSlime'] +allEnemys : ['IdString_0'] +var code = 'core.canBattle(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +damage_e + : '战斗伤害' '怪物ID' IdString + +/* damage_e +default : ['greenSlime'] +allEnemys : ['IdString_0'] +var code = 'core.getDamage(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +damage_1_e + : '战斗伤害' '点坐标' 'x' PosString 'y' PosString + +/* damage_1_e +default : [0, 0] +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +var code = 'core.getDamage(null, ' + PosString_0 + ',' + PosString_1 + ')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +rand_e + : '随机数 [0, ' Int ')' + +/* rand_e +default : ['10'] +var code = 'core.rand(' + Int_0 + ')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +equip_e + : '第' Int '格装备孔' + + +/* equip_e +default : [0] +var code = 'equip:'+Int_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +idFlag_e + : '独立开关' Letter_List + + +/* idFlag_e +colour : this.idstring_eColor +default : ["A"] +var code = "switch:"+Letter_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +idTemp_e + : '临时变量' Letter_List + + +/* idTemp_e +colour : this.idstring_eColor +default : ["A"] +var code = "temp:"+Letter_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +evalString_e + : EvalString_Multi + + +/* evalString_e +default : ["值"] +var code = EvalString_Multi_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + +//===============lexer=============== + +IdText + : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 + ; + +RawEvalString + : 'sdeirughvuiyasdbe'+ //为了被识别为复杂词法规则 + ; + +JsonEvalString + : 'sdeirughvuiyasdbe'+ //为了被识别为复杂词法规则 + ; + +PosString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +IntString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +ColorString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +FontString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +Floor_List + : '楼层ID'|'前一楼'|'后一楼'|'当前楼' + /*Floor_List ['floorId',':before',':next',':now']*/; + +Stair_List + : '坐标'|'上楼梯'|'下楼梯'|'保持不变'|'中心对称点'|'x对称点'|'y对称点'|'楼传落点' + /*Stair_List ['loc','upFloor','downFloor',':now',':symmetry',':symmetry_x',':symmetry_y','flyPoint']*/; + +SetTextPosition_List + : '不改变'|'距离顶部'|'居中'|'距离底部' + /*SetTextPosition_List ['null','up','center','down']*/; + +TextAlign_List + : '不改变'|'左对齐'|'左右居中'|'右对齐' + /*TextAlign_List ['null','left','center','right']*/; + +TextBaseline_List + : '不改变'|'顶部'|'悬挂'|'居中'|'标准值'|'ideographic'|'底部' + /*TextBaseline_List ['null','top','hanging','middle','alphabetic','ideographic','bottom']*/; + +Reverse_List + : '不改变'|'左右翻转'|'上下翻转'|'中心翻转' + /*Reverse_List ['null',':x',':y',':o']*/; + +ShopUse_List + : '金币' | '经验' + /*ShopUse_List ['money','exp']*/; + +Arithmetic_List + : '加'|'减'|'乘'|'除'|'取余'|'乘方'|'等于'|'不等于'|'大于'|'小于'|'大于等于'|'小于等于'|'且'|'或'|'异或'|'取较大'|'取较小'|'弱相等'|'弱不相等'|'开始于'|'结束于'|'包含' + /*Arithmetic_List ['+','-','*','/','%','**','===','!==','>','<','>=','<=','&&','||','^','max','min','==','!=','startsWith','endsWith','includes']*/; + +AssignOperator_List + : '设为'|'增加'|'减少'|'乘以'|'除以'|'乘方'|'除以并取商'|'除以并取余'|'设为不小于'|'设为不大于' + /*AssignOperator_List ['=','+=','-=','*=','/=','**=','//=','%=','max=','min=']*/; + +UnaryOperator_List + : '向下取整'|'向上取整'|'四舍五入'|'整数截断'|'绝对值'|'开方'|'变量类型' + /*UnaryOperator_List ['Math.floor', 'Math.ceil', 'Math.round', 'Math.trunc', 'Math.abs', 'Math.sqrt', 'typeof']*/; + +Weather_List + : '无'|'雨'|'雪'|'晴'|'雾'|'云' + /*Weather_List ['null','rain','snow','sun','fog','cloud']*/; + +B_0_List + : '不改变'|'不可通行'|'可以通行' + /*B_0_List ['null','true','false']*/; + +B_1_List + : '不改变'|'设为粗体'|'取消粗体' + /*B_1_List ['null','true','false']*/; + +Bg_Fg_List + : '背景层'|'前景层' + /*Bg_Fg_List ['bg','fg']*/; + +Bg_Fg2_List + : '背景层'|'前景层'|'自适配' + /*Bg_Fg2_List ['bg','fg','auto']*/; + +IgnoreChangeFloor_List + : '全局默认值' | '可穿透' | '不可穿透' + /*IgnoreChangeFloor_List ['null','true','false']*/; + +Event_List + : '普通事件'|'战前事件'|'战后事件'|'道具后事件'|'开门后事件' + /*Event_List ['null','beforeBattle','afterBattle','afterGetItem','afterOpenDoor']*/; + +Floor_Meta_List + : '楼层中文名'|'状态栏名称'|'能否楼传飞到'|'能否楼传飞出'|'能否打开快捷商店'|'是否不可浏览地图'|'是否不可瞬间移动'|'默认地面ID'|'宝石血瓶效果'|'上楼点坐标'|'下楼点坐标'|'楼传落点坐标'|'背景音乐'|'画面色调'|'天气和强度'|'是否地下层' + /*Floor_Meta_List ['title','name','canFlyTo', 'canFlyFrom', 'canUseQuickShop', 'cannotViewMap', 'cannotMoveDirectly', 'defaultGround', 'ratio', 'upFloor', 'downFloor', 'flyPoint', 'bgm', 'color', 'weather', 'underGround']*/; + +Global_Attribute_List + : '全局字体'|'横屏左侧状态栏背景'|'竖屏上方状态栏背景'|'竖屏下方道具栏背景'|'边框颜色'|'状态栏文字色'|'选中框颜色'|'楼层转换样式'|'装备列表' + /*Global_Attribute_List ['font','statusLeftBackground','statusTopBackground', 'toolsBackground', 'borderColor', 'statusBarColor', 'selectColor', 'floorChangingStyle', 'equipName']*/; + +Global_Value_List + : '血网伤害'|'中毒伤害'|'衰弱效果'|'红宝石效果'|'蓝宝石效果'|'绿宝石效果'|'红血瓶效果'|'蓝血瓶效果'|'黄血瓶效果'|'绿血瓶效果'|'破甲比例'|'反击比例'|'净化比例'|'仇恨增加值'|'图块每帧时间'|'上下楼时间' + /*Global_Value_List ['lavaDamage','poisonDamage','weakValue', 'redGem', 'blueGem', 'greenGem', 'redPotion', 'bluePotion', 'yellowPotion', 'greenPotion', 'breakArmor', 'counterAttack', 'purify', 'hatred', 'animateSpeed', 'floorChangeTime']*/; + + +Global_Flag_List + : '显示当前楼层'|'显示勇士图标'|'显示当前等级'|'启用生命上限'|'显示生命值'|'显示魔力值'|'显示攻击力'|'显示防御力'|'显示护盾值'|'显示金币值'|'显示经验值'|'允许等级提升'|'升级扣除模式'|'显示钥匙数量'|'显示绿钥匙'|'显示破炸飞'|'显示毒衰咒'|'显示当前技能'|'横屏底部工具栏'|'楼梯边才能楼传'|'楼传平面塔模式'|'开启加点'|'开启负伤'|'夹击不超伤害值'|'二分计算临界'|'允许轻按'|'允许走到将死领域'|'允许瞬间移动'|'阻激夹域后禁用快捷商店'|'虚化前景层' + /*Global_Flag_List ['s:enableFloor','s:enableName','s:enableLv', 's:enableHPMax', 's:enableHP', 's:enableMana', 's:enableAtk', 's:enableDef', 's:enableMDef', 's:enableMoney', 's:enableExp', 's:enableLevelUp', 's:levelUpLeftMode', 's:enableKeys', 's:enableGreenKey', 's:enablePZF', 's:enableDebuff', 's:enableSkill', 'extendToolbar', 'flyNearStair', 'flyRecordPosition', 'enableAddPoint', 'enableNegativeDamage', 'betweenAttackMax', 'useLoop', 'enableGentleClick', 'canGoDeadZone', 'enableMoveDirectly', 'disableShopOnDamage', 'blurFg']*/; + +NextXY_List + : '横坐标'|'纵坐标' + /*NextXY_List ['nextX','nextY']*/; + +EquipValueType_List + : '数值项'|'百分比项' + /*EquipValueType_List ['value','percentage']*/; + +Vibrate_List + : '左右'|'上下'|'左上-右下'|'左下-右上'|'随机' + /*Vibrate_List ['horizontal','vertical','diagonal1','diagonal2','random']*/; + +Colour + : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 + ; + +Angle + : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 + ; + +Bool: 'TRUE' + | 'FALSE' + ; + +Int : '0' | [1-9][0-9]* ; // no leading zeros + +NInt : '0' | '-'? [1-9][0-9]* ; + +Letter_List + : 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z' + /*Letter_List ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']*/; + + +Number + : '-'? Int '.' Int EXP? // 1.35, 1.35E-9, 0.3, -4.5 + | '-'? Int EXP // 1e10 -3e4 + | '-'? Int // -3, 45 + ; +fragment EXP : [Ee] [+\-]? Int ; // \- since - means "range" inside [...] + +Direction_List + : '上'|'下'|'左'|'右' + /*Direction_List ['up','down','left','right']*/; + +DirectionEx_List + : '不变'|'朝上'|'朝下'|'朝左'|'朝右'|'左转'|'右转'|'背对'|'角色同向'|'角色反向' + /*DirectionEx_List ['null','up','down','left','right',':left',':right',':back',':hero',':backhero']*/; + +StepString + : (Direction_List Int?)+ + ; + +IdString + : [0-9a-zA-Z_][0-9a-zA-Z_:]* + ; + +FixedId_List + : '生命'|'生命上限'|'攻击'|'防御'|'护盾'|'黄钥匙'|'蓝钥匙'|'红钥匙'|'金币'|'经验'|'魔力'|'魔力上限'|'当前横坐标'|'当前纵坐标'|'当前朝向'|'攻击增益'|'防御增益'|'护盾增益' + /*FixedId_List ['status:hp','status:hpmax','status:atk','status:def','status:mdef','item:yellowKey','item:blueKey','item:redKey','status:money','status:exp','status:mana','status:manamax','status:x','status:y','status:direction','buff:atk','buff:def','buff:mdef']*/; + +Id_List + : '变量' | '状态' | '物品' | '增益' | '独立开关' | '临时变量' |'全局存储' + /*Id_List ['flag','status','item', 'buff', 'switch', 'temp', 'global']*/; + +EnemyId_List + : '生命'|'攻击'|'防御'|'金币'|'经验'|'加点'|'特殊属性'|'名称'|'映射名'|'属性值'|'退化扣攻'|'退化扣防'|'不可炸'|'九宫格领域'|'领域范围'|'连击数'|'吸血到自身'|'固伤值' + /*EnemyId_List ['hp','atk','def','money','exp','point','special','name','displayInBook','value','atkValue','defValue','notBomb','zoneSquare','range','n','add','damage']*/; + +EnemyPoint_List + : '生命'|'攻击'|'防御'|'金币'|'经验'|'加点'|'名称' + /*EnemyPoint_List ['hp','atk','def','money','exp','point','name']*/; + +Equip_List + : '生命'|'生命上限'|'攻击'|'防御'|'护盾'|'魔力'|'魔力上限' + /*Equip_List ['hp','hpmax','atk','def','mdef','mana','manamax']*/; + +Key_List + : '黄钥匙'|'蓝钥匙'|'红钥匙'|'绿钥匙'|'铁门钥匙' + /*Key_List ['yellowKey','blueKey','redKey','greenKey','steelKey']*/; + +Move_List + : '上'|'下'|'左'|'右'|'前'|'后'|'左上'|'左下'|'右上'|'右下'|'设置速度' + /*Move_List ['up','down','left','right','forward','backward','leftup','leftdown','rightup','rightdown','speed']*/; + +MoveMode_List + : '匀速移动'|'缓入快出'|'快入缓出'|'缓入缓出'|'随机' + /*MoveMode_List ['', 'easeIn', 'easeOut', 'easeInOut', 'random']*/; + +NameMap_List + : '确定'|'取消'|'操作失败'|'光标移动'|'打开界面'|'读档'|'存档'|'获得道具'|'回血'|'宝石'|'炸弹'|'飞行器'|'开关门'|'上下楼'|'跳跃'|'破墙镐'|'破冰镐'|'阻激夹域'|'穿脱装备'|'商店' + /*NameMap_List ['确定','取消','操作失败','光标移动','打开界面','读档','存档','获得道具','回血','宝石','炸弹','飞行器','开关门','上下楼','跳跃','破墙镐','破冰镐','阻激夹域','穿脱装备','商店']*/; + +//转blockly后不保留需要加" +EvalString + : Equote_double (ESC_double | ~["\\])* Equote_double + ; + +EvalString_Multi + : Equote_double (ESC_double | ~["\\])* Equote_double + ; + +fragment ESC_double : '\\' (["\\/bfnrt] | UNICODE) ; +fragment UNICODE : 'u' HEX HEX HEX HEX ; +fragment HEX : [0-9a-fA-F] ; + +BGNL + : 'BGNLaergayergfuybgv' + ; + +MeaningfulSplit : '=== meaningful ^ ===' ; + +fragment Equote_double : '"' ; + +BSTART + : '开始' + ; + +BEND: '结束' + ; + +Newline + : ('\r' '\n'?| '\n')// -> skip + ; + +WhiteSpace + : [ \t]+ -> skip + ; + +BlockComment + : '/*' .*? '*/' -> skip + ; + +LineComment + : '//' ~[\r\n]* -> skip + ; + +/* Function_0 +//this.evisitor.recieveOrder='ORDER_NONE'; +this.evisitor.valueColor=330; +this.evisitor.statementColor=70; +this.evisitor.entryColor=250; + +this.evisitor.idstring_eColor=310; +this.evisitor.subColor=250; +this.evisitor.dataColor=130; +this.evisitor.eventColor=220; +this.evisitor.soundColor=20; +this.evisitor.commentColor=285; +this.evisitor.mapColor=175; +this.evisitor.uiColor=359; +this.evisitor.imageColor=45; +*/ + +/* Function_1 +delete(this.block('negate_e').inputsInline); +this.block('idIdList_e').output='idString_e'; +this.block('idFixedList_e').output='idString_e'; +this.block('idFlag_e').output='idString_e'; +this.block('idTemp_e').output='idString_e'; +*/ + +/* Functions + +MotaActionParser() + +*/ diff --git a/_sidebar.md b/_sidebar.md new file mode 100644 index 0000000..35d7f25 --- /dev/null +++ b/_sidebar.md @@ -0,0 +1,10 @@ + +- [快速上手](start) +- [元件说明](element) +- [事件编辑](event) +- [事件指令](instruction) +- [个性化](personalization) +- [脚本](script) +- [修改编辑器](editor) +- [UI编辑器](ui-editor) +- [附录:API列表](api) diff --git a/action.d.ts b/action.d.ts new file mode 100644 index 0000000..05ec671 --- /dev/null +++ b/action.d.ts @@ -0,0 +1,145 @@ +/** + * 鼠标与触屏操作的函数 + */ +type MotaMouseFunc = (x: number, y: number, px: number, py: number) => boolean; + +/** + * 按键操作的函数 + */ +type MotaKeyboardFunc = (e: KeyboardEvent) => boolean; + +/** + * 没有最乱,只有更乱 + */ +interface RegisteredActionMap { + keyDown: (keyCode: number) => boolean; + keyDownCtrl: () => boolean; + keyUp: (keyCode: number, altKey: boolean, fromReplay: boolean) => boolean; + longClick: MotaMouseFunc; + onStatusBarClick: (px: number, py: number, vertical: boolean) => boolean; + ondown: MotaMouseFunc; + onkeyDown: MotaKeyboardFunc; + onkeyUp: MotaKeyboardFunc; + onmousewheel: (direct: 1 | -1) => boolean; + onmove: MotaMouseFunc; + onup: MotaMouseFunc; + pressKey: (keyCode: number) => boolean; +} + +type ActionKey = keyof RegisteredActionMap; + +/** + * 将注册的函数的返回值变成void就变成了actions上的函数... + */ +type VoidedActionFuncs = { + [P in ActionKey]: (...params: Parameters) => void; +}; + +/** + * 点击位置 + */ +interface ClickLoc extends Loc { + /** + * 格子的大小(这不是32还能是其它的吗?? + */ + size: 32; +} + +interface RegisteredActionOf { + /** + * 交互的类型 + */ + action: K; + + /** + * 交互的唯一标识符 + */ + name: string; + + /** + * 优先级,越高越优先执行 + */ + priority: number; + + /** + * 交互函数 + */ + func: RegisteredActionMap[K]; +} + +/** + * 交互模块 + */ +interface Actions extends VoidedActionFuncs { + /** + * 横向的最后一个格子的横坐标 + */ + readonly LAST: number; + + /** + * 格子长度的一半 + */ + readonly _HX_: number; + + /** + * 格子高度的一半 + */ + readonly _HY_: number; + + /** + * 所有已注册的交互操作 + */ + readonly actions: { + [P in ActionKey]: RegisteredActionOf

[]; + }; + + /** + * 此函数将注册一个用户交互行为。 + * @param action 要注册的交互类型 + * @param name 自定义名称,可被注销使用 + * @param func 执行函数,如果func返回true,则不会再继续执行其他的交互函数 + * @param priority 优先级,优先级高的将会被执行。此项可不填,默认为0 + */ + registerAction( + action: K, + name: string, + func: RegisteredActionMap[K], + priority?: number + ): void; + + /** + * 注销一个用户交互行为 + * @param action 要注销的交互类型 + * @param name 要注销的自定义名称 + */ + unregisterAction(action: ActionKey, name: string): void; + + /** + * 执行一个用户交互行为 + */ + doRegisteredAction( + action: K, + ...params: Parameters + ): void; + + /** + * 判断一个横坐标是否在(_HX_ - 2, _HX_ + 2)范围外 + * @param x 要判断的横坐标 + */ + _out(x: number): boolean; + + _getNextFlyFloor(delta: number, index: number): number; + + _clickGameInfo_openComments(); + + _getClickLoc( + x: number, + y: number + ): { + x: number; + y: number; + size: number; + }; +} + +declare const actions: new () => Actions; diff --git a/actions.js b/actions.js new file mode 100644 index 0000000..3a922c9 --- /dev/null +++ b/actions.js @@ -0,0 +1,3222 @@ + +/* +actions.js:用户交互的事件的处理 +键盘、鼠标、触摸屏事件相关 + */ + +"use strict"; + +function actions() { + this._init(); + this._HX_ = core._HALF_WIDTH_; + this._HY_ = core._HALF_HEIGHT_; + this._out = function (x) { return x < this._HX_ - 2 || this._HX_ + 2 < x; }; + this.LAST = core._WIDTH_ - 1; +} + +actions.prototype._init = function () { + this.actionsdata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.actions; + this.actions = {}; + // --- onkeyDown注册 + this.registerAction('onkeyDown', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('onkeyDown', '_sys_onkeyDown', this._sys_onkeyDown, 0); + // --- onkeyUp注册 + this.registerAction('onkeyUp', '_sys_onkeyUp_replay', this._sys_onkeyUp_replay, 100); + this.registerAction('onkeyUp', '_sys_onkeyUp', this._sys_onkeyUp, 0); + // --- pressKey注册 + this.registerAction('pressKey', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('pressKey', '_sys_pressKey', this._sys_pressKey, 0); + // --- keyDown注册 + this.registerAction('keyDown', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('keyDown', '_sys_keyDown_lockControl', this._sys_keyDown_lockControl, 50); + this.registerAction('keyDown', '_sys_keyDown', this._sys_keyDown, 0); + // --- keyUp注册 + this.registerAction('keyUp', '_sys_keyUp_replay', this._sys_keyUp_replay, 100); + this.registerAction('keyUp', '_sys_keyUp_lockControl', this._sys_keyUp_lockControl, 50); + this.registerAction('keyUp', '_sys_keyUp', this._sys_keyUp, 0); + // --- ondown注册 + this.registerAction('ondown', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('ondown', '_sys_ondown_lockControl', this._sys_ondown_lockControl, 30); + this.registerAction('ondown', '_sys_ondown', this._sys_ondown, 0); + // --- onmove注册 + this.registerAction('onmove', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('onmove', '_sys_onmove_choices', this._sys_onmove_choices, 30); + this.registerAction('onmove', '_sys_onmove', this._sys_onmove, 0); + // --- onup注册 + this.registerAction('onup', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('onup', '_sys_onup', this._sys_onup, 0); + // --- onclick已废弃,将视为ondown + // --- onmousewheel注册 + this.registerAction('onmousewheel', '_sys_onmousewheel', this._sys_onmousewheel, 0); + // --- keyDownCtrl注册 + this.registerAction('keyDownCtrl', '_sys_keyDownCtrl', this._sys_keyDownCtrl, 0); + // --- longClick注册 + this.registerAction('longClick', '_sys_longClick_lockControl', this._sys_longClick_lockControl, 50); + // --- onStatusBarClick注册 + this.registerAction('onStatusBarClick', '_sys_onStatusBarClick', this._sys_onStatusBarClick, 0); + +} + +////// 注册一个用户交互行为 ////// +/* + * 此函数将注册一个用户交互行为。 + * action:要注册的交互类型,如 ondown, onup, keyDown 等等。 + * name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 + * func:执行函数。 + * priority:优先级;优先级高的将会被执行。此项可不填,默认为0。 + * 返回:如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 + */ +actions.prototype.registerAction = function (action, name, func, priority) { + if (!name || !func) + return; + // 将onclick视为ondown处理 + if (action == 'onclick') action = 'ondown'; + priority = priority || 0; + if (!this.actions[action]) { + this.actions[action] = []; + } + this.unregisterAction(action, name); + this.actions[action].push( + { "action": action, "name": name, "func": func, "priority": priority } + ); + this.actions[action] = this.actions[action].sort(function (a, b) { + return b.priority - a.priority; + }); +} + +////// 注销一个用户交互行为 ////// +actions.prototype.unregisterAction = function (action, name) { + // 将onclick视为ondown处理 + if (action == 'onclick') action = 'ondown'; + if (!this.actions[action]) return; + this.actions[action] = this.actions[action].filter(function (x) { + return x.name != name; + }); +} + +////// 执行一个用户交互行为 ////// +actions.prototype.doRegisteredAction = function (action) { + var actions = this.actions[action]; + if (!actions) return false; + for (var i = 0; i < actions.length; ++i) { + try { + if (core.doFunc.apply(core, [actions[i].func, this].concat(Array.prototype.slice.call(arguments, 1)))) + return true; + } + catch (e) { + console.error(e); + console.error("ERROR in actions[" + actions[i].name + "]."); + } + } + return false; +} + +actions.prototype._checkReplaying = function () { + if (core.isReplaying() && + ['save', 'book', 'book-detail', 'viewMaps', 'toolbox', 'equipbox', 'text'].indexOf(core.status.event.id) < 0) + return true; + return false; +} + +////// 检查是否在录像播放中,如果是,则停止交互 +actions.prototype._sys_checkReplay = function () { + if (this._checkReplaying()) return true; +} + +////// 检查左手模式 +actions.prototype.__checkLeftHandPrefer = function (e) { + if (!core.flags.leftHandPrefer) return e; + var map = { + 87: 38, // W -> up + 83: 40, // S -> down + 65: 37, // A -> left + 68: 39, // D -> right + 73: 87, // I -> W + 74: 65, // J -> A + 75: 83, // K -> S + 76: 68, // L -> D + } + var newEvent = {}; + for (var one in e) { + if (!(e[one] instanceof Function)) { + newEvent[one] = e[one]; + } + }; + ["stopPropagation", "stopImmediatePropagation", "preventDefault"].forEach(function (one) { + newEvent[one] = function () { + return e[one](); + } + }); + newEvent.keyCode = map[e.keyCode] || e.keyCode; + return newEvent; +} + +////// 按下某个键时 ////// +actions.prototype.onkeyDown = function (e) { + this.doRegisteredAction('onkeyDown', this.__checkLeftHandPrefer(e)); +} + +actions.prototype._sys_onkeyDown = function (e) { + core.status.holdingKeys = core.status.holdingKeys || [] + var isArrow = { 65: true, 87: true, 68: true, 83: true }[e.keyCode] + if (isArrow /*&& !core.status.lockControl*/) { + for (var ii = 0; ii < core.status.holdingKeys.length; ii++) { + if (core.status.holdingKeys[ii] === e.keyCode) { + return; + } + } + if (e.preventDefault) e.preventDefault(); + core.status.holdingKeys.push(e.keyCode); + this.pressKey(e.keyCode); + } else { + if (e.keyCode == 17) core.status.ctrlDown = true; + this.keyDown(e.keyCode); + } +} + +////// 放开某个键时 ////// +actions.prototype.onkeyUp = function (e) { + this.doRegisteredAction('onkeyUp', this.__checkLeftHandPrefer(e)); +} + +actions.prototype._sys_onkeyUp_replay = function (e) { + if (this._checkReplaying()) { + if (e.keyCode == 27) // ESCAPE + core.stopReplay(); + else if (e.keyCode == 90) // Z + core.speedDownReplay(); + else if (e.keyCode == 67) // C + core.speedUpReplay(); + else if (e.keyCode == 32) // SPACE + core.triggerReplay(); + else if (e.keyCode == 65) // A + core.rewindReplay(); + else if (e.keyCode == 83) // S + core.control._replay_SL(); + else if (e.keyCode == 88) // X + core.control._replay_book(); + else if (e.keyCode == 33 || e.keyCode == 34) // PgUp/PgDn + core.control._replay_viewMap(); + else if (e.keyCode == 78) // N + core.stepReplay(); + else if (e.keyCode == 84) // T + core.control._replay_toolbox(); + else if (e.keyCode == 81) // Q + core.control._replay_equipbox(); + else if (e.keyCode == 66) // B + core.ui._drawStatistics(); + else if (e.keyCode >= 49 && e.keyCode <= 51) // 1-3 + core.setReplaySpeed(e.keyCode - 48); + else if (e.keyCode == 52) // 4 + core.setReplaySpeed(6); + else if (e.keyCode == 53) // 5 + core.setReplaySpeed(12); + else if (e.keyCode == 54) // 6 + core.setReplaySpeed(24); + return true; + } +} + +actions.prototype._sys_onkeyUp = function (e) { + var isArrow = { 65: true, 87: true, 68: true, 83: true }[e.keyCode] + if (isArrow /*&& !core.status.lockControl*/) { + for (var ii = 0; ii < core.status.holdingKeys.length; ii++) { + if (core.status.holdingKeys[ii] === e.keyCode) { + core.status.holdingKeys = core.status.holdingKeys.slice(0, ii).concat(core.status.holdingKeys.slice(ii + 1)); + if (ii === core.status.holdingKeys.length && core.status.holdingKeys.length !== 0) core.pressKey(core.status.holdingKeys.slice(-1)[0]); + break; + } + } + if (e.preventDefault) e.preventDefault(); + this.keyUp(e.keyCode, e.altKey); + } else { + if (e.keyCode == 17) core.status.ctrlDown = false; + this.keyUp(e.keyCode, e.altKey); + } +} + +////// 按住某个键时 ////// +actions.prototype.pressKey = function (keyCode) { + this.doRegisteredAction('pressKey', keyCode); +} + +actions.prototype._sys_pressKey = function (keyCode) { + if (keyCode === core.status.holdingKeys.slice(-1)[0]) { + this.keyDown(keyCode); + window.setTimeout(function () { + core.pressKey(keyCode); + }, 30); + } +} + +////// 根据按下键的code来执行一系列操作 ////// +actions.prototype.keyDown = function (keyCode) { + this.doRegisteredAction('keyDown', keyCode); +} + +actions.prototype._sys_keyDown_lockControl = function (keyCode) { + if (!core.status.lockControl) return false; + // Ctrl跳过对话 + if (keyCode == 17) { + this.keyDownCtrl(); + return true; + } + switch (core.status.event.id) { + case 'action': + this._keyDownAction(keyCode); + break; + case 'book': + this._keyDownBook(keyCode); + break; + case 'fly': + this._keyDownFly(keyCode); + break; + case 'viewMaps': + this._keyDownViewMaps(keyCode); + break; + case 'equipbox': + this._keyDownEquipbox(keyCode); + break; + case 'toolbox': + this._keyDownToolbox(keyCode); + break; + case 'save': + case 'load': + case 'replayLoad': + case 'replayRemain': + case 'replaySince': + this._keyDownSL(keyCode); + break; + case 'selectShop': + case 'switchs': + case 'switchs-sounds': + case 'switchs-display': + case 'switchs-action': + case 'notes': + case 'settings': + case 'syncSave': + case 'syncSelect': + case 'localSaveSelect': + case 'storageRemove': + case 'replay': + case 'gameInfo': + this._keyDownChoices(keyCode); + break; + case 'cursor': + this._keyDownCursor(keyCode); + break; + } + return true; +} + +actions.prototype._sys_keyDown = function (keyCode) { + if (!core.status.played) + return true; + /*var up=flags.keylist[0]; + var left=flags.keylist[1]; + var down=flags.keylist[2]; + var right=flags.keylist[3]; + switch (keyCode) { + case left: + core.moveHero('left'); + break; + case up: + core.moveHero('up'); + break; + case right: + core.moveHero('right'); + break; + case down: + core.moveHero('down'); + break; + }*/ + return true; +} + +////// 根据放开键的code来执行一系列操作 ////// +actions.prototype.keyUp = function (keyCode, altKey, fromReplay) { + this.doRegisteredAction('keyUp', keyCode, altKey, fromReplay); +} + +actions.prototype._sys_keyUp_replay = function (keyCode, altKey, fromReplay) { + if (!fromReplay && this._checkReplaying()) return true; +} + +actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) { + if (!core.status.lockControl) return false; + + var ok = function () { + return keyCode == 27 || keyCode == 88 || keyCode == 13 || keyCode == 32 || keyCode == 67; + } + + core.status.holdingKeys = []; + switch (core.status.event.id) { + case 'text': + ok() && core.drawText(); + break; + case 'confirmBox': + this._keyUpConfirmBox(keyCode); + break; + case 'action': + this._keyUpAction(keyCode); + break; + case 'about': + ok() && core.closePanel(); + break; + case 'help': + ok() && core.closePanel(); + break; + case 'book': + this._keyUpBook(keyCode); + break; + case 'book-detail': + ok() && this._clickBookDetail(); + break; + case 'fly': + this._keyUpFly(keyCode); + break; + case 'viewMaps': + this._keyUpViewMaps(keyCode); + break; + case 'selectShop': + this._keyUpQuickShop(keyCode); + break; + case 'toolbox': + this._keyUpToolbox(keyCode); + break; + case 'equipbox': + this._keyUpEquipbox(keyCode, altKey); + break; + case 'save': + case 'load': + case 'replayLoad': + case 'replayRemain': + case 'replaySince': + this._keyUpSL(keyCode); + break; + case 'keyBoard': + ok() && core.closePanel(); + break; + case 'switchs': + this._keyUpSwitchs(keyCode); + break; + case 'switchs-sounds': + this._keyUpSwitchs_sounds(keyCode); + break; + case 'switchs-display': + this._keyUpSwitchs_display(keyCode); + break; + case 'switchs-action': + this._keyUpSwitchs_action(keyCode); + break; + case 'settings': + this._keyUpSettings(keyCode); + break; + case 'notes': + this._keyUpNotes(keyCode); + break; + case 'syncSave': + this._keyUpSyncSave(keyCode); + break; + case 'syncSelect': + this._keyUpSyncSelect(keyCode); + break; + case 'localSaveSelect': + this._keyUpLocalSaveSelect(keyCode); + break; + case 'storageRemove': + this._keyUpStorageRemove(keyCode); + break; + case 'cursor': + this._keyUpCursor(keyCode); + break; + case 'replay': + this._keyUpReplay(keyCode); + break; + case 'gameInfo': + this._keyUpGameInfo(keyCode); + break; + case 'centerFly': + this._keyUpCenterFly(keyCode); + break; + } + return true; +} + +actions.prototype._sys_keyUp = function (keyCode, altKey) { + if (!core.status.played) + return true; + this.actionsdata.onKeyUp(keyCode, altKey); + if (core.status.automaticRoute && core.status.automaticRoute.autoHeroMove) { + core.stopAutomaticRoute(); + } + core.status.heroStop = true; + return true; +} + +////// 点击(触摸)事件按下时 ////// +actions.prototype.ondown = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('ondown', x, y, px, py); +} + +actions.prototype._sys_ondown_lockControl = function (x, y, px, py) { + if (core.status.played && !core.status.lockControl) return false; + + switch (core.status.event.id) { + case 'centerFly': + this._clickCenterFly(x, y, px, py); + break; + case 'book': + this._clickBook(x, y, px, py); + break; + case 'book-detail': + this._clickBookDetail(x, y, px, py); + break; + case 'fly': + this._clickFly(x, y, px, py); + break; + case 'viewMaps': + this._clickViewMaps(x, y, px, py); + break; + case 'switchs': + this._clickSwitchs(x, y, px, py); + break; + case 'switchs-sounds': + this._clickSwitchs_sounds(x, y, px, py); + break; + case 'switchs-display': + this._clickSwitchs_display(x, y, px, py); + break; + case 'switchs-action': + this._clickSwitchs_action(x, y, px, py); + break; + case 'settings': + this._clickSettings(x, y, px, py); + break; + case 'selectShop': + this._clickQuickShop(x, y, px, py); + break; + case 'equipbox': + this._clickEquipbox(x, y, px, py); + break; + case 'toolbox': + this._clickToolbox(x, y, px, py); + break; + case 'save': + case 'load': + case 'replayLoad': + case 'replayRemain': + case 'replaySince': + this._clickSL(x, y, px, py); + break; + case 'confirmBox': + this._clickConfirmBox(x, y, px, py); + break; + case 'keyBoard': + this._clickKeyBoard(x, y, px, py); + break; + case 'action': + this._clickAction(x, y, px, py); + break; + case 'text': + core.drawText(); + break; + case 'notes': + this._clickNotes(x, y, px, py); + break; + case 'syncSave': + this._clickSyncSave(x, y, px, py); + break; + case 'syncSelect': + this._clickSyncSelect(x, y, px, py); + break; + case 'localSaveSelect': + this._clickLocalSaveSelect(x, y, px, py); + break; + case 'storageRemove': + this._clickStorageRemove(x, y, px, py); + break; + case 'cursor': + this._clickCursor(x, y, px, py); + break; + case 'replay': + this._clickReplay(x, y, px, py); + break; + case 'gameInfo': + this._clickGameInfo(x, y, px, py); + break; + case 'about': + case 'help': + core.ui.closePanel(); + break; + } + + // --- 长按判定 + if (core.timeout.onDownTimeout == null) { + core.timeout.onDownTimeout = setTimeout(function () { + if (core.interval.onDownInterval == null) { + core.interval.onDownInterval = setInterval(function () { + if (!core.actions.longClick(x, y, px, py)) { + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = null; + } + }, 40) + } + }, 500); + } + return true; +} + +actions.prototype._sys_ondown = function (x, y, px, py) { + if (core.status.lockControl) return false; + core.status.downTime = new Date(); + core.deleteCanvas('route'); + var pos = { 'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32) }; + core.status.stepPostfix = []; + core.status.stepPostfix.push(pos); + //core.fillRect('ui', pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8, '#bfbfbf'); + + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + core.status.preview.prepareDragging = false; + if (!core.hasFlag('__lockViewport__') && (core.status.thisMap.width > core._WIDTH_ || core.status.thisMap.height > core._HEIGHT_)) { + core.status.preview.prepareDragging = true; + core.status.preview.px = px; + core.status.preview.py = py; + /*core.timeout.onDownTimeout = setTimeout(function () { + core.clearMap('ui'); + core.status.preview.prepareDragging = false; + core.status.preview.enabled = true; + core.status.preview.dragging = true; + core.drawTip('已进入预览模式,可直接拖动大地图'); + core.status.stepPostfix = []; + }, 500);*/ + } +} + +////// 当在触摸屏上滑动时 ////// +actions.prototype.onmove = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('onmove', x, y, px, py); +} + +actions.prototype._sys_onmove_choices = function (x, y, px, py) { + if (!core.status.lockControl) return false; + + switch (core.status.event.id) { + case 'action': + if (core.status.event.data.type == 'choices') { + this._onMoveChoices(x, y); + return true; + } + if (core.status.event.data.type == 'confirm') { + this._onMoveConfirmBox(x, y, px, py); + return true; + } + break; + case 'selectShop': + case 'switchs': + case 'switchs-sounds': + case 'switchs-display': + case 'switchs-action': + case 'notes': + case 'settings': + case 'syncSave': + case 'syncSelect': + case 'localSaveSelect': + case 'storageRemove': + case 'replay': + case 'gameInfo': + this._onMoveChoices(x, y); + return true; + case 'confirmBox': + this._onMoveConfirmBox(x, y, px, py); + return true; + default: + break; + } + return false; +} + +actions.prototype._sys_onmove = function (x, y, px, py) { + if (core.status.lockControl) return false; + + if (core.status.preview.dragging) { + core.setViewport(core.bigmap.offsetX - px + core.status.preview.px, core.bigmap.offsetY - py + core.status.preview.py); + core.status.preview.px = px; + core.status.preview.py = py; + return true; + } + if (core.status.preview.prepareDragging) { + if (Math.abs(px - core.status.preview.px) <= 20 && Math.abs(py - core.status.preview.py) <= 20) + return true; + else core.status.preview.prepareDragging = false; + } + + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + + if ((core.status.stepPostfix || []).length > 0) { + var pos = { 'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32) }; + var pos0 = core.status.stepPostfix[core.status.stepPostfix.length - 1]; + var directionDistance = [pos.y - pos0.y, pos0.x - pos.x, pos0.y - pos.y, pos.x - pos0.x]; + var max = 0, index = 4; + for (var ii = 0; ii < 4; ii++) { + if (directionDistance[ii] > max) { + index = ii; + max = directionDistance[ii]; + } + } + pos = [{ 'x': 0, 'y': 1 }, { 'x': -1, 'y': 0 }, { 'x': 0, 'y': -1 }, { 'x': 1, 'y': 0 }, false][index] + if (pos) { + pos.x += pos0.x; + pos.y += pos0.y; + core.status.stepPostfix.push(pos); + //core.fillRect('ui', pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8, '#bfbfbf'); + } + } + return true; +} + +////// 当点击(触摸)事件放开时 ////// +actions.prototype.onup = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('onup', x, y, px, py); +} + +actions.prototype._sys_onup = function (x, y, px, py) { + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = null; + + if (core.isPlaying()) { + core.status.preview.prepareDragging = false; + if (core.status.preview.dragging) { + core.status.preview.dragging = false; + return true; + } + } + + if ((core.status.stepPostfix || []).length == 0) return false; + + var stepPostfix = []; + var direction = { '0': { '1': 'down', '-1': 'up' }, '-1': { '0': 'left' }, '1': { '0': 'right' } }; + for (var ii = 1; ii < core.status.stepPostfix.length; ii++) { + var pos0 = core.status.stepPostfix[ii - 1]; + var pos = core.status.stepPostfix[ii]; + stepPostfix.push({ + 'direction': direction[pos.x - pos0.x][pos.y - pos0.y], + 'x': pos.x, + 'y': pos.y + }); + } + var posx = core.status.stepPostfix[0].x; + var posy = core.status.stepPostfix[0].y; + core.status.stepPostfix = []; + if (!core.status.lockControl) { + core.clearMap('ui'); + } + + // 长按 + if (!core.status.lockControl && stepPostfix.length == 0 && core.status.downTime != null && new Date() - core.status.downTime >= 1000) { + core.actions.longClick(x, y, px, py); + } + else { + //posx,posy是寻路的目标点,stepPostfix是后续的移动 + core.setAutomaticRoute(posx, posy, stepPostfix); + } + core.status.downTime = null; + return true; +} + +////// 获得点击事件相对左上角的坐标 ////// +actions.prototype._getClickLoc = function (x, y) { + + var statusBar = { 'x': 0, 'y': 0 }; + var size = 32; + size = size * core.domStyle.scale; + + if (core.domStyle.isVertical) { + statusBar.x = 3; + statusBar.y = core.dom.statusBar.offsetHeight + 3; + } + else { + statusBar.x = core.dom.statusBar.offsetWidth + 3; + statusBar.y = 3; + } + + var left = core.dom.gameGroup.offsetLeft + statusBar.x; + var top = core.dom.gameGroup.offsetTop + statusBar.y; + var loc = { 'x': Math.max(x - left), 'y': Math.max(y - top, 0), 'size': size }; + return loc; +} + + +////// 滑动鼠标滚轮时的操作 ////// +actions.prototype.onmousewheel = function (direct) { + this.doRegisteredAction('onmousewheel', direct); +} + +actions.prototype._sys_onmousewheel = function (direct) { + // 向下滚动是 -1 ,向上是 1 + + if (this._checkReplaying()) { + // 滚轮控制速度 + if (direct == 1) core.speedUpReplay(); + if (direct == -1) core.speedDownReplay(); + return; + } + + // 楼层飞行器 + if (core.status.lockControl && core.status.event.id == 'fly') { + if (direct == 1) core.ui.drawFly(this._getNextFlyFloor(1)); + if (direct == -1) core.ui.drawFly(this._getNextFlyFloor(-1)); + return; + } + + // 怪物手册 + if (core.status.lockControl && core.status.event.id == 'book') { + var pageinfo = core.ui._drawBook_pageinfo(); + if (direct == 1) core.ui.drawBook(core.status.event.data - pageinfo.per_page); + if (direct == -1) core.ui.drawBook(core.status.event.data + pageinfo.per_page); + return; + } + + // 存读档 + if (core.status.lockControl && (core.status.event.id == 'save' || core.status.event.id == 'load')) { + var index = core.status.event.data.page * 10 + core.status.event.data.offset; + if (direct == 1) core.ui._drawSLPanel(index - 10); + if (direct == -1) core.ui._drawSLPanel(index + 10); + return; + } + + // 浏览地图 + /*if (core.status.lockControl && core.status.event.id == 'viewMaps') { + if (direct == 1) this._clickViewMaps(this._HX_, this._HY_ - 3, core._PX_ / 2, core._PY_ / 5 * 1.5); + if (direct == -1) this._clickViewMaps(this._HX_, this._HY_ + 3, core._PX_ / 2, core._PY_ / 5 * 3.5); + return; + }*/ + + // wait事件 + if (core.status.lockControl && core.status.event.id == 'action' && core.status.event.data.type == 'wait') { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag('type', 0); + var keycode = direct == 1 ? 33 : 34; + core.setFlag('keycode', keycode); + core.setFlag('timeout', timeout); + var executed = core.events.__action_wait_afterGet(core.status.event.data.current); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + keycode)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + +} + +////// 长按Ctrl键时 ////// +actions.prototype.keyDownCtrl = function () { + this.doRegisteredAction('keyDownCtrl'); +} + +actions.prototype._sys_keyDownCtrl = function () { + if (core.status.event.id == 'text') { + core.drawText(); + return true; + } + if (core.status.event.id == 'action' && core.status.event.data.type == 'text') { + core.doAction(); + return true; + } + if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' + && !core.status.event.data.current.noSkip) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { + clearTimeout(core.timeout.sleepTimeout); + core.timeout.sleepTimeout = null; + core.doAction(); + } + return true; + } +} + +////// 长按 ////// +actions.prototype.longClick = function (x, y, px, py) { + if (!core.isPlaying()) return false; + return this.doRegisteredAction('longClick', x, y, px, py); +} + +actions.prototype._sys_longClick_lockControl = function (x, y, px, py) { + if (!core.status.lockControl) return false; + if (core.status.event.id == 'text') { + core.drawText(); + return true; + } + if (core.status.event.id == 'action' && core.status.event.data.type == 'text') { + core.doAction(); + return true; + } + // 长按楼传器的箭头可以快速翻页 + if (core.status.event.id == 'fly') { + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && (y == this._HY_ - 1 || y == this._HY_ + 3)) { + this._clickFly(x, y); + return true; + } + } + // 长按SL上下页快速翻页 + if (["save", "load", "replayLoad", "replayRemain", "replaySince"].indexOf(core.status.event.id) >= 0) { + if ([this._HX_ - 2, this._HX_ - 3, this._HX_ + 2, this._HX_ + 3].indexOf(x) >= 0 && y === core._HEIGHT_ - 1) { + this._clickSL(x, y); + return true; + } + } + // 长按可以跳过等待事件 + if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' + && !core.status.event.data.current.noSkip) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { + clearTimeout(core.timeout.sleepTimeout); + core.timeout.sleepTimeout = null; + core.doAction(); + return true; + } + } + return false; +} + +actions.prototype.onStatusBarClick = function (e) { + if (!core.isPlaying()) return false; + var left = core.dom.gameGroup.offsetLeft + 3; + var top = core.dom.gameGroup.offsetTop + 3; + var px = parseInt((e.clientX - left) / core.domStyle.scale), py = parseInt((e.clientY - top) / core.domStyle.scale); + return this.doRegisteredAction('onStatusBarClick', Math.max(px, 0), Math.max(py, 0)); +} + +actions.prototype._sys_onStatusBarClick = function (px, py, vertical) { + if (this.actionsdata.onStatusBarClick) + return this.actionsdata.onStatusBarClick(px, py, vertical); +} + +/////////////////// 在某个界面时的按键点击效果 /////////////////// + +actions.prototype._getChoicesTopIndex = function (length) { + return this._HY_ - parseInt((length - 1) / 2) + (core.status.event.ui.offset || 0); +} + +// 数字键快速选择选项 +actions.prototype._selectChoices = function (length, keycode, callback) { + var topIndex = this._getChoicesTopIndex(length); + if (keycode == 13 || keycode == 32 || keycode == 67) { + callback.apply(this, [this._HX_, topIndex + core.status.event.selection]); + } + + if (keycode >= 49 && keycode <= 57) { + var index = keycode - 49; + if (index < length) { + callback.apply(this, [this._HX_, topIndex + index]); + } + } +} + +// 上下键调整选项 +actions.prototype._keyDownChoices = function (keycode) { + if (keycode == 38) { + core.status.event.selection--; + core.playSound('光标移动'); + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width); + } + if (keycode == 40) { + core.status.event.selection++; + core.playSound('光标移动'); + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width); + } +} + +// 移动光标 +actions.prototype._onMoveChoices = function (x, y) { + //if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + if (selection == core.status.event.selection) return; + core.status.event.selection = selection; + core.playSound('光标移动'); + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width); + } +} + +////// 点击中心对称飞行器时 +actions.prototype._clickCenterFly = function (x, y) { + var posX = core.status.event.data.posX, posY = core.status.event.data.posY; + core.ui.closePanel(); + if (x == posX && y == posY) { + if (core.canUseItem('centerFly')) { + core.useItem('centerFly'); + } + else { + core.playSound('操作失败'); + core.drawTip('当前不能使用' + core.material.items['centerFly'].name, 'centerFly'); + } + } +} + +actions.prototype._keyUpCenterFly = function (keycode) { + core.ui.closePanel(); + if (keycode == 51 || keycode == 13 || keycode == 32 || keycode == 67) { + if (core.canUseItem('centerFly')) { + core.useItem('centerFly'); + } + else { + core.playSound('操作失败'); + core.drawTip('当前不能使用' + core.material.items['centerFly'].name, 'centerFly'); + } + } +} + +////// 点击确认框时 ////// +actions.prototype._clickConfirmBox = function (x, y, px, py) { + if (px >= core._PX_ / 2 - 70 && px <= core._PX_ / 2 - 10 + && py >= core._PY_ / 2 && py <= core._PY_ / 2 + 64 && core.status.event.data.yes) + core.status.event.data.yes(); + if (px >= core._PX_ / 2 + 10 && px <= core._PX_ / 2 + 70 + && py >= core._PY_ / 2 && py <= core._PY_ / 2 + 64 && core.status.event.data.no) + core.status.event.data.no(); +} + +////// 键盘操作确认框时 ////// +actions.prototype._keyUpConfirmBox = function (keycode) { + if (keycode == 37 || keycode == 39) { + core.status.event.selection = 1 - core.status.event.selection; + core.playSound('光标移动'); + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67) { + if (core.status.event.selection == 0 && core.status.event.data.yes) { + // core.playSound('确定'); + core.status.event.selection = null; + core.status.event.data.yes(); + return; + } + if (core.status.event.selection == 1 && core.status.event.data.no) { + // core.playSound('确定'); + core.status.event.selection = null; + core.status.event.data.no(); + return; + } + } +} + +////// 鼠标在确认框上移动时 ////// +actions.prototype._onMoveConfirmBox = function (x, y, px, py) { + if (py >= core._PY_ / 2 && py <= core._PY_ / 2 + 64) { + if (px >= core._PX_ / 2 - 70 && px <= core._PX_ / 2 - 10) { + if (core.status.event.selection != 0) { + core.status.event.selection = 0; + core.playSound('光标移动'); + if (core.status.event.id == 'action') { + core.ui.drawConfirmBox(core.status.event.ui.text); + } else { + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + } + } + return; + } + if (px >= core._PX_ / 2 + 10 && px <= core._PX_ / 2 + 70) { + if (core.status.event.selection != 1) { + core.status.event.selection = 1; + core.playSound('光标移动'); + if (core.status.event.id == 'action') { + core.ui.drawConfirmBox(core.status.event.ui.text); + } else { + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + } + } + return; + } + } +} + +actions.prototype._clickAction_text = function () { + // 正在淡入淡出的话不执行 + if (core.status.event.animateUI) return; + + var data = core.clone(core.status.event.data.current); + if (typeof data == 'string') data = { "type": "text", "text": data }; + + // 打字机效果显示全部文字 + if (core.status.event.interval != null) { + data.showAll = true; + core.insertAction(data); + core.doAction(); + return; + } + + if (!data.code) { + core.ui._animateUI('hide', null, core.doAction); + } else { + // 不清除对话框 + core.doAction(); + } +} + +////// 自定义事件时的点击操作 ////// +actions.prototype._clickAction = function (x, y, px, py) { + if (core.status.event.data.type == 'text') { + return this._clickAction_text(); + } + + if (core.status.event.data.type == 'wait') { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag('type', 1); + core.setFlag('x', x); + core.setFlag('y', y); + core.setFlag('px', px); + core.setFlag('py', py); + core.setFlag('timeout', timeout); + var executed = core.events.__action_wait_afterGet(core.status.event.data.current); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + 1000000 + 1000 * px + py)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + + if (core.status.event.data.type == 'choices') { + // 选项 + var data = core.status.event.data.current; + var choices = data.choices; + if (choices.length == 0) return; + if (this._out(x)) return; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var choice = choices[y - topIndex]; + if (choice.need != null && choice.need != '' && !core.calValue(choice.need)) { + core.playSound('操作失败'); + core.drawTip("无法选择此项"); + return; + } + clearTimeout(core.status.event.interval); + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + // 对全局商店特殊处理 + var index = y - topIndex; + if (index == choices.length - 1 && core.hasFlag('@temp@shop')) { + index = -1; + } + core.status.route.push("choices:" + (100 * timeout + index)); + core.insertAction(choice.action); + core.doAction(); + } + return; + } + + if (core.status.event.data.type == 'confirm') { + if ((x == this._HX_ - 2 || x == this._HX_ - 1) && y == this._HY_ + 1) { + clearTimeout(core.status.event.interval); + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + core.status.route.push("choices:" + 100 * timeout); + core.insertAction(core.status.event.ui.yes); + core.doAction(); + } + else if ((x == this._HX_ + 2 || x == this._HX_ + 1) && y == this._HY_ + 1) { + clearTimeout(core.status.event.interval); + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + core.status.route.push("choices:" + (100 * timeout + 1)); + core.insertAction(core.status.event.ui.no); + core.doAction(); + } + return; + } +} + +////// 自定义事件时,按下某个键的操作 ////// +actions.prototype._keyDownAction = function (keycode) { + if (core.status.event.data.type == 'choices') { + this._keyDownChoices(keycode); + return; + } + if (core.status.event.data.type == 'confirm' && (keycode == 37 || keycode == 39)) { + core.status.event.selection = 1 - core.status.event.selection; + core.playSound('光标移动'); + core.drawConfirmBox(core.status.event.ui.text); + return; + } +} + +////// 自定义事件时,放开某个键的操作 ////// +actions.prototype._keyUpAction = function (keycode) { + if (core.status.event.data.type == 'text' && (keycode == 13 || keycode == 32 || keycode == 67)) { + return this._clickAction_text(); + } + if (core.status.event.data.type == 'wait') { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag('type', 0); + core.setFlag('keycode', keycode); + core.setFlag('timeout', timeout); + var executed = core.events.__action_wait_afterGet(core.status.event.data.current); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + keycode)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + if (core.status.event.data.type == 'choices') { + var data = core.status.event.data.current; + var choices = data.choices; + if (choices.length > 0) { + this._selectChoices(choices.length, keycode, this._clickAction); + } + return; + } + if (core.status.event.data.type == 'confirm' && (keycode == 13 || keycode == 32 || keycode == 67)) { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + core.status.route.push("choices:" + (100 * timeout + core.status.event.selection)); + if (core.status.event.selection == 0) + core.insertAction(core.status.event.ui.yes); + else core.insertAction(core.status.event.ui.no); + core.doAction(); + 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 = []; + 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; +} + +////// 怪物手册界面时,按下某个键的操作 ////// +actions.prototype._keyDownBook = function (keycode) { + var pageinfo = core.ui._drawBook_pageinfo(); + if (keycode == 37) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data - pageinfo.per_page); } + if (keycode == 38) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data - 1); } + if (keycode == 39) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data + pageinfo.per_page); } + if (keycode == 40) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data + 1); } + if (keycode == 33) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data - pageinfo.per_page); } + if (keycode == 34) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data + pageinfo.per_page); } + return; +} + +////// 怪物手册界面时,放开某个键的操作 ////// +actions.prototype._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 = []; + 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._clickBookDetail = function () { + core.clearMap('data'); + core.playSound('取消'); + core.status.event.id = 'book'; +} + +////// 楼层传送器界面时的点击操作 ////// +actions.prototype._clickFly = function (x, y) { + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ + 3) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-1)); } + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ - 1) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(1)); } + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ + 4) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-10)); } + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ - 2) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(10)); } + if (x >= this._HX_ - 1 && x <= this._HX_ + 1 && y === core._HEIGHT_ - 1) { core.playSound('取消'); core.ui.closePanel(); } + if (x >= 0 && x <= this._HX_ + 3 && y >= 3 && y <= core._HEIGHT_ - 1 - 1) + core.flyTo(core.floorIds[core.status.event.data]); + return; +} + +////// 楼层传送器界面时,按下某个键的操作 ////// +actions.prototype._keyDownFly = function (keycode) { + if (keycode == 37) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-10)); } + else if (keycode == 38) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(1)); } + else if (keycode == 39) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(10)); } + else if (keycode == 40) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-1)); } + return; +} + +actions.prototype._getNextFlyFloor = function (delta, index) { + if (index == null) index = core.status.event.data; + if (delta == 0) return index; + var sign = Math.sign(delta); + delta = Math.abs(delta); + var ans = index; + while (true) { + index += sign; + if (index < 0 || index >= core.floorIds.length) break; + var floorId = core.floorIds[index]; + if (core.status.maps[floorId].canFlyTo && core.hasVisitedFloor(floorId)) { + delta--; + ans = index; + } + if (delta == 0) break; + } + return ans; +} + +////// 楼层传送器界面时,放开某个键的操作 ////// +actions.prototype._keyUpFly = function (keycode) { + if (keycode == 71 || keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + } + if (keycode == 13 || keycode == 32 || keycode == 67) + this._clickFly(this._HX_ - 1, this._HY_ - 1); + return; +} + +////// 查看地图界面时的点击操作 ////// +actions.prototype._clickViewMaps = function (x, y, px, py) { + if (core.status.event.data == null) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + return; + } + var now = core.floorIds.indexOf(core.status.floorId); + var index = core.status.event.data.index; + var cx = core.status.event.data.x, cy = core.status.event.data.y; + var floorId = core.floorIds[index], mh = core.floors[floorId].height; + var perpx = core._PX_ / 5, cornerpx = perpx * 3 / 4, perpy = core._PY_ / 5, cornerpy = perpy * 3 / 4; + + /*if (px <= cornerpx && py <= cornerpy) { + core.status.event.data.damage = !core.status.event.data.damage; + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy); + return; + } + if (px <= cornerpx && py >= core._PY_ - cornerpy) { + if (core.markedFloorIds[floorId]) delete core.markedFloorIds[floorId]; + else core.markedFloorIds[floorId] = true; + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy); + return; + } + if (px >= core._PX_ - cornerpx && py <= cornerpy) { + core.status.event.data.all = !core.status.event.data.all; + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy); + return; + } + + if (px >= perpx && px <= core._PX_ - perpx && py <= perpy && (!core.status.event.data.all && mh > core._HEIGHT_)) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy - 1); + return; + } + if (px >= perpx && px <= core._PX_ - perpx && py >= core._PY_ - perpy && (!core.status.event.data.all && mh > core._HEIGHT_)) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy + 1); + return; + } + if (px <= perpx && py >= perpy && py <= core._PY_ - perpy) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx - 1, cy); + return; + } + if (px >= core._PX_ - perpx && py >= perpy && py <= core._PY_ - perpy) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx + 1, cy); + return; + } + + if (py <= 2 * perpy && (mh == core._HEIGHT_ || (px >= perpx && px <= core._PX_ - perpx))) { + core.playSound('光标移动'); + index++; + while (index < core.floorIds.length && index != now && core.status.maps[core.floorIds[index]].cannotViewMap) + index++; + if (index < core.floorIds.length) + core.ui._drawViewMaps(index); + return; + } + if (py >= 3 * perpy && (mh == core._HEIGHT_ || (px >= perpx && px <= core._PX_ - perpx))) { + core.playSound('光标移动'); + index--; + while (index >= 0 && index != now && core.status.maps[core.floorIds[index]].cannotViewMap) + index--; + if (index >= 0) + core.ui._drawViewMaps(index); + return; + } + if (px >= perpx && px <= core._PX_ - perpx && py >= perpy * 2 && py <= perpy * 3) { + core.clearMap('data'); + core.playSound('取消'); + core.ui.closePanel(); + return; + }*/ + if (px >= 0 && px <= 416 && py >= 0 && py <= 416) { + core.clearMap('data'); + core.playSound('取消'); + core.ui.closePanel(); + core.drawMap(); + return; + } + +} + +////// 查看地图界面时,按下某个键的操作 ////// +actions.prototype._keyDownViewMaps = function (keycode) { + if (core.status.event.data == null) return; + + var floorId = core.floorIds[core.status.event.data.index], mh = core.floors[floorId].height; + + /*if (keycode == 38 || keycode == 33) this._clickViewMaps(this._HX_ , this._HY_ - 3 , core._PX_ / 2, core._PY_ / 5 * 1.5); + if (keycode == 40 || keycode == 34) this._clickViewMaps(this._HX_ , this._HY_ + 3 , core._PX_ / 2, core._PY_ / 5 * 3.5); + if (keycode == 87 && mh > core._HEIGHT_) this._clickViewMaps(this._HX_ , 0 , core._PX_ / 2, 1 ); + if (keycode == 65) this._clickViewMaps(0 , this._HY_ , 1 , core._PY_ / 2 ); + if (keycode == 83 && mh > core._HEIGHT_) this._clickViewMaps(this._HX_ , core._HEIGHT_ - 1, core._PX_ / 2, core._PY_ - 1 ); + if (keycode == 68) this._clickViewMaps(core._WIDTH_ - 1, this._HY_ , core._PX_ , core._PY_ / 2 - 1 ); + */ + return; +} + +////// 查看地图界面时,放开某个键的操作 ////// +actions.prototype._keyUpViewMaps = function (keycode) { + if (core.status.event.data == null) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + return; + } + var floorId = core.floorIds[core.status.event.data.index]; + + /*if (keycode == 27 || keycode == 13 || keycode == 32 || (!core.isReplaying() && keycode == 67)) { + core.clearMap('data'); + core.playSound('取消'); + core.ui.closePanel(); + return; + } + if (keycode == 86) { + core.status.event.data.damage = !core.status.event.data.damage; + core.playSound('光标移动'); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 90) { + core.status.event.data.all = !core.status.event.data.all; + core.playSound('光标移动'); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 66) { + if (core.markedFloorIds[floorId]) delete core.markedFloorIds[floorId]; + else core.markedFloorIds[floorId] = true; + core.playSound('光标移动'); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 88 || (core.isReplaying() && keycode == 67)) { + if (core.isReplaying()) { + core.control._replay_book(); + } else { + core.openBook(false); + } + return; + } + if (keycode == 71 && !core.isReplaying()) { + core.useFly(false); + return; + }*/ + return; +} + +////// 快捷商店界面时的点击操作 ////// +actions.prototype._clickQuickShop = function (x, y) { + var shopIds = core.listShopIds(); + if (this._out(x)) return; + var topIndex = this._HY_ - parseInt(shopIds.length / 2) + (core.status.event.ui.offset || 0); + if (y >= topIndex && y < topIndex + shopIds.length) { + var shopId = shopIds[y - topIndex]; + if (!core.canOpenShop(shopId)) { + core.playSound('操作失败'); + core.drawTip('当前项尚未开启'); + return; + } + var message = core.canUseQuickShop(shopId); + if (message == null) { + // core.ui.closePanel(); + core.openShop(shopIds[y - topIndex], false); + } else { + core.playSound('操作失败'); + core.drawTip(message); + } + } + // 离开 + else if (y == topIndex + shopIds.length) { + core.playSound('取消'); + core.ui.closePanel(); + } +} + +////// 快捷商店界面时,放开某个键的操作 ////// +actions.prototype._keyUpQuickShop = function (keycode) { + if (keycode == 27 || keycode == 75 || keycode == 88 || keycode == 86) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + this._selectChoices(core.listShopIds().length + 1, keycode, this._clickQuickShop); + return; +} + +////// 工具栏界面时的点击操作 ////// +actions.prototype._clickToolbox = function (x, y) { + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + + // 装备栏 + if (x >= this.LAST - 2 && y == 0) { + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_equipbox(); + else + core.openEquipbox(); + return; + } + if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + + var toolsPage = core.status.event.data.toolsPage; + var constantsPage = core.status.event.data.constantsPage; + // 上一页 + if (x == this._HX_ - 2 || x == this._HX_ - 3) { + if (y === core._HEIGHT_ - 1 - 5 && toolsPage > 1) { + core.status.event.data.toolsPage--; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + if (y === core._HEIGHT_ - 1 && constantsPage > 1) { + core.status.event.data.constantsPage--; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + } + // 下一页 + if (x == this._HX_ + 2 || x == this._HX_ + 3) { + if (y === core._HEIGHT_ - 1 - 5 && toolsPage < Math.ceil(tools.length / this.LAST)) { + core.status.event.data.toolsPage++; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + if (y === core._HEIGHT_ - 1 && constantsPage < Math.ceil(constants.length / this.LAST)) { + core.status.event.data.constantsPage++; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + } + + var index = parseInt(x / 2); + if (y === core._HEIGHT_ - 1 - 8) index += 0; + else if (y === core._HEIGHT_ - 1 - 6) index += this._HX_; + else if (y === core._HEIGHT_ - 1 - 3) index += this.LAST; + else if (y === core._HEIGHT_ - 1 - 1) index += this.LAST + this._HX_; + else index = -1; + if (index >= 0) + this._clickToolboxIndex(index); +} + +////// 选择工具栏界面中某个Index后的操作 ////// +actions.prototype._clickToolboxIndex = function (index) { + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + + var items = null; + var select; + if (index < this.LAST) { + select = index + this.LAST * (core.status.event.data.toolsPage - 1); + items = tools; + } + else { + select = index % this.LAST + this.LAST * (core.status.event.data.constantsPage - 1); + items = constants; + } + if (items == null) return; + if (select >= items.length) return; + var itemId = items[select]; + if (itemId == core.status.event.data.selectId) { + if (core.isReplaying()) return; + core.events.tryUseItem(itemId); + } + else { + core.playSound('光标移动'); + core.ui._drawToolbox(index); + } +} + +////// 工具栏界面时,按下某个键的操作 ////// +actions.prototype._keyDownToolbox = function (keycode) { + if (core.status.event.data == null) return; + + var last_index = this.LAST - 1; + + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + var index = core.status.event.selection; + var toolsPage = core.status.event.data.toolsPage; + var constantsPage = core.status.event.data.constantsPage; + var toolsTotalPage = Math.ceil(tools.length / this.LAST); + var constantsTotalPage = Math.ceil(constants.length / this.LAST); + var toolsLastIndex = toolsPage < toolsTotalPage ? last_index : (tools.length + last_index) % this.LAST; + var constantsLastIndex = this.LAST + (constantsPage < constantsTotalPage ? last_index : (constants.length + last_index) % this.LAST); + + if (keycode == 37) { // left + if (index == 0) { // 处理向前翻页 + if (toolsPage > 1) { + core.status.event.data.toolsPage--; + index = last_index; + } + else return; // 第一页不向前翻 + } + else if (index == this.LAST) { + if (constantsPage == 1) { + if (toolsTotalPage == 0) return; + core.status.event.data.toolsPage = toolsTotalPage; + index = (tools.length + last_index) % this.LAST; + } + else { + core.status.event.data.constantsPage--; + index = 2 * this.LAST - 1; + } + } + else index -= 1; + this._clickToolboxIndex(index); + return; + } + if (keycode == 38) { // up + if (index >= this.LAST && index < this.LAST + this._HX_) { // 进入tools + if (toolsTotalPage == 0) return; + if (toolsLastIndex >= this._HX_) index = Math.min(toolsLastIndex, index - this._HX_); + else index = Math.min(toolsLastIndex, index - this.LAST); + } + else if (index < this._HX_) return; // 第一行没有向上 + else index -= this._HX_; + this._clickToolboxIndex(index); + return; + } + if (keycode == 39) { // right + if (toolsPage < toolsTotalPage && index == last_index) { + core.status.event.data.toolsPage++; + index = 0; + } + else if (constantsPage < constantsTotalPage && index == 2 * this.LAST - 1) { + core.status.event.data.constantsPage++; + index = this.LAST; + } + else if (index == toolsLastIndex) { + if (constantsTotalPage == 0) return; + core.status.event.data.constantsPage = 1; + index = this.LAST; + } + else if (index == constantsLastIndex) // 一个物品无操作 + return; + else index++; + this._clickToolboxIndex(index); + return; + } + if (keycode == 40) { // down + var nextIndex = null; + if (index < this._HX_) { + if (toolsLastIndex >= this._HX_) nextIndex = Math.min(toolsLastIndex, index + this._HX_); + else index += this._HX_; + } + if (nextIndex == null && index < this.LAST) { + if (constantsTotalPage == 0) return; + nextIndex = Math.min(index + this._HX_, constantsLastIndex); + } + if (nextIndex == null && index < this.LAST + this._HX_) { + if (constantsLastIndex >= this.LAST + this._HX_) + nextIndex = Math.min(constantsLastIndex, index + this._HX_); + } + if (nextIndex != null) { + this._clickToolboxIndex(nextIndex); + } + return; + } +} + +////// 工具栏界面时,放开某个键的操作 ////// +actions.prototype._keyUpToolbox = function (keycode) { + if (keycode == 81) { + core.playSound('确定'); + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_equipbox(); + else + core.openEquipbox(); + return; + } + if (keycode == 84 || keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + if (core.status.event.data == null) return; + + if (keycode == 13 || keycode == 32 || keycode == 67) { + this._clickToolboxIndex(core.status.event.selection); + return; + } +} + +////// 装备栏界面时的点击操作 ////// +actions.prototype._clickEquipbox = function (x, y, px, py) { + if (x >= this.LAST - 2 && y == 0) { + core.playSound('确定'); + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_toolbox(); + else + core.openToolbox(); + return; + } + if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + if ((x == this._HX_ - 2 || x == this._HX_ - 3) && y === core._HEIGHT_ - 1) { + if (core.status.event.data.page > 1) { + core.status.event.data.page--; + core.playSound('光标移动'); + core.ui._drawEquipbox(core.status.event.selection); + } + return; + } + if ((x == this._HX_ + 2 || x == this._HX_ + 3) && y === core._HEIGHT_ - 1) { + var lastPage = Math.ceil(core.getToolboxItems('equips').length / this.LAST); + if (core.status.event.data.page < lastPage) { + core.status.event.data.page++; + core.playSound('光标移动'); + core.ui._drawEquipbox(core.status.event.selection); + } + return; + } + var per_page = this._HX_ - 3, v = core._WIDTH_ / per_page; + if (y === core._HEIGHT_ - 9) { + for (var i = 0; i < per_page; ++i) if (x >= i * v && x <= (i + 1) * v) return this._clickEquipboxIndex(i); + } else if (y === core._HEIGHT_ - 7) { + for (var i = 0; i < per_page; ++i) if (x >= i * v && x <= (i + 1) * v) return this._clickEquipboxIndex(per_page + i); + } else if (Math.abs(core._HEIGHT_ - 5 - py / 32) < 0.5) { + for (var i = 0; i < per_page; ++i) if (x >= i * v && x <= (i + 1) * v) return this._clickEquipboxIndex(2 * per_page + i); + } else if (y === core._HEIGHT_ - 4) this._clickEquipboxIndex(this.LAST + parseInt(x / 2)) + else if (y === core._HEIGHT_ - 2) this._clickEquipboxIndex(this.LAST + this._HX_ + parseInt(x / 2)); +} + +////// 选择装备栏界面中某个Index后的操作 ////// +actions.prototype._clickEquipboxIndex = function (index) { + if (index < this.LAST) { + if (index >= core.status.globalAttribute.equipName.length) return; + if (index == core.status.event.selection && core.status.hero.equipment[index]) { + if (core.isReplaying()) return; + core.unloadEquip(index); + core.status.route.push("unEquip:" + index); + } else core.playSound('光标移动'); + } + else { + var equips = core.getToolboxItems('equips'); + if (index == core.status.event.selection) { + if (core.isReplaying()) return; + var equipId = equips[index - this.LAST + (core.status.event.data.page - 1) * this.LAST]; + core.loadEquip(equipId); + core.status.route.push("equip:" + equipId); + } else core.playSound('光标移动'); + } + core.ui._drawEquipbox(index); +} + +////// 装备栏界面时,按下某个键的操作 ////// +actions.prototype._keyDownEquipbox = function (keycode) { + if (core.status.event.data == null) return; + + var last_index = this.LAST - 1; + var per_line = this._HX_ - 3; + var equipCapacity = core.status.globalAttribute.equipName.length; + var ownEquipment = core.getToolboxItems('equips'); + var index = core.status.event.selection; + var page = core.status.event.data.page; + var totalPage = Math.ceil(ownEquipment.length / this.LAST); + var totalLastIndex = this.LAST + (page < totalPage ? last_index : (ownEquipment.length + last_index) % this.LAST); + + if (keycode == 37) { // left + if (index == 0) return; + if (index == this.LAST) { + if (page > 1) { + core.status.event.data.page--; + core.playSound('光标移动'); + index = this.LAST + last_index; + } + else if (page == 1) + index = equipCapacity - 1; + else return; + } + else index -= 1; + this._clickEquipboxIndex(index); + return; + } + if (keycode == 38) { // up + if (index < per_line) return; + else if (index < 2 * per_line) index -= per_line; + else if (index < this.LAST + this._HX_) { + index = parseInt((index - this.LAST) / 2); + if (equipCapacity > per_line) index = Math.min(equipCapacity - 1, index + per_line); + else index = Math.min(equipCapacity - 1, index); + } + else index -= this._HX_; + this._clickEquipboxIndex(index); + return; + } + if (keycode == 39) { // right + if (page < totalPage && index == this.LAST + last_index) { + core.status.event.data.page++; + core.playSound('光标移动'); + index = this.LAST; + } + else if (index == equipCapacity - 1) { + if (totalPage == 0) return; + index = this.LAST; + } + else if (index == totalLastIndex) + return; + else index++; + this._clickEquipboxIndex(index); + return; + } + if (keycode == 40) { // down + if (index < per_line) { + if (equipCapacity > per_line) index = Math.min(index + per_line, equipCapacity - 1); + else { + if (totalPage == 0) return; + index = Math.min(2 * index + 1 + this.LAST, totalLastIndex); + } + } + else if (index < 2 * per_line) { + if (totalPage == 0) return; + index = Math.min(2 * (index - per_line) + 1 + this.LAST, totalLastIndex); + } + else if (index < this.LAST + this._HX_) + index = Math.min(index + this._HX_, totalLastIndex); + else return; + this._clickEquipboxIndex(index); + return; + } +} + +////// 装备栏界面时,放开某个键的操作 ////// +actions.prototype._keyUpEquipbox = function (keycode, altKey) { + if (altKey && keycode >= 48 && keycode <= 57) { + core.items.quickSaveEquip(keycode - 48); + return; + } + if (keycode == 84) { + core.playSound('确定'); + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_toolbox(); + else + core.openToolbox(); + return; + } + if (keycode == 81 || keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + if (!core.status.event.data.selectId) return; + + if (keycode == 13 || keycode == 32 || keycode == 67) { + this._clickEquipboxIndex(core.status.event.selection); + return; + } +} + +////// 存读档界面时的点击操作 ////// +actions.prototype._clickSL = function (x, y) { + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; + + // 上一页 + if ((x == this._HX_ - 2 || x == this._HX_ - 3) && y === core._HEIGHT_ - 1) { + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page - 1) + offset); + return; + } + // 下一页 + if ((x == this._HX_ + 2 || x == this._HX_ + 3) && y === core._HEIGHT_ - 1) { + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page + 1) + offset); + return; + } + // 返回 + if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) { + core.playSound('取消'); + if (core.events.recoverEvents(core.status.event.interval)) + return; + core.ui.closePanel(); + delete core.status.tempRoute; + if (!core.isPlaying()) + core.showStartAnimate(true); + return; + } + // 删除 + if (x >= 0 && x <= 2 && y === core._HEIGHT_ - 1) { + if (core.status.event.id == 'save') { + core.status.event.selection = !core.status.event.selection; + core.ui._drawSLPanel(index); + } + else { // 显示收藏 + core.status.event.data.mode = core.status.event.data.mode == 'all' ? 'fav' : 'all'; + if (core.status.event.data.mode == 'fav') + core.ui._drawSLPanel(1, true); + else { + page = parseInt((core.saves.saveIndex - 1) / 5); + offset = core.saves.saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset, true); + } + } + return; + } + // 点存档名 + var xLeft = parseInt(core._WIDTH_ / 3), xRight = parseInt(core._WIDTH_ * 2 / 3); + var topY1 = 0, topY2 = this._HY_; + if (y >= topY1 && y <= topY1 + 1) { + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 1); + if (x >= xRight) return this._clickSL_favorite(page, 2); + } + if (y >= topY2 && y <= topY2 + 1) { + if (x < xLeft) return this._clickSL_favorite(page, 3); + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 4); + if (x >= xRight) return this._clickSL_favorite(page, 5); + } + + var id = null; + if (y >= topY1 + 2 && y < this._HY_ - 1) { + if (x < xLeft) id = "autoSave"; + if (x >= xLeft && x < xRight) id = 5 * page + 1; + if (x >= xRight) id = 5 * page + 2; + } + if (y >= topY2 + 2 && y < core._HEIGHT_ - 1) { + if (x < xLeft) id = 5 * page + 3; + if (x >= xLeft && x < xRight) id = 5 * page + 4; + if (x >= xRight) id = 5 * page + 5; + } + if (id != null) { + if (core.status.event.selection) { + if (id == 'autoSave') { + core.playSound('操作失败'); + core.drawTip("无法删除自动存档!"); + } else { + core.removeSave(id, function () { + core.ui._drawSLPanel(index, true); + }); + } + } + else { + if (core.status.event.data.mode == 'fav' && id != 'autoSave') + id = core.saves.favorite[id - 1]; + core.doSL(id, core.status.event.id); + } + } +} + +actions.prototype._clickSL_favorite = function (page, offset) { + if (offset == 0) return; + var index = 5 * page + offset; + /*if (!core.status.event.data){ + if (core.hasSave(index)) { + core.saves.favorite.push(index); + core.saves.favorite = core.saves.favorite.sort(function (a, b) { return a - b; }); + } + core.control._updateFavoriteSaves(); + return; + }*/ + if (core.status.event.data.mode == 'fav') { // 收藏模式下点击的下标直接对应favorite + index = core.saves.favorite[index - 1]; + core.myprompt("请输入想要显示的存档名(长度不超过5字符)", null, function (value) { + if (value && value.length <= 5) { + core.saves.favoriteName[index] = value; + core.control._updateFavoriteSaves(); + core.ui._drawSLPanel(10 * page + offset); + } else if (value) { + alert("无效的输入!"); + } + }); + } else { + var v = core.saves.favorite.indexOf(index); + core.playSound('确定'); + if (v >= 0) { // 已经处于收藏状态:取消收藏 + core.saves.favorite.splice(v, 1); + delete core.saves.favoriteName[index]; + } + else if (core.hasSave(index)) { // 存在存档则进行收藏 + core.saves.favorite.push(index); + core.saves.favorite = core.saves.favorite.sort(function (a, b) { return a - b; }); // 保证有序 + core.drawTip("收藏成功!"); + } + core.control._updateFavoriteSaves(); + core.ui._drawSLPanel(10 * page + offset); + } +} + +////// 存读档界面时,按下某个键的操作 ////// +actions.prototype._keyDownSL = function (keycode) { + + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; + + if (keycode == 37) { // left + core.playSound('光标移动'); + if (offset == 0) { + core.ui._drawSLPanel(10 * (page - 1) + 5); + } + else { + core.ui._drawSLPanel(index - 1); + } + return; + } + if (keycode == 38) { // up + core.playSound('光标移动'); + if (offset < 3) { + core.ui._drawSLPanel(10 * (page - 1) + offset + 3); + } + else { + core.ui._drawSLPanel(index - 3); + } + return; + } + if (keycode == 39) { // right + core.playSound('光标移动'); + if (offset == 5) { + core.ui._drawSLPanel(10 * (page + 1) + 1); + } + else { + core.ui._drawSLPanel(index + 1); + } + return; + } + if (keycode == 40) { // down + core.playSound('光标移动'); + if (offset >= 3) { + core.ui._drawSLPanel(10 * (page + 1) + offset - 3); + } + else { + core.ui._drawSLPanel(index + 3); + } + return; + } + if (keycode == 33) { // PAGEUP + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page - 1) + offset); + return; + } + if (keycode == 34) { // PAGEDOWN + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page + 1) + offset); + return; + } +} + +////// 存读档界面时,放开某个键的操作 ////// +actions.prototype._keyUpSL = function (keycode) { + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; + + if (keycode == 27 || keycode == 88 || (core.status.event.id == 'save' && keycode == 83) + || (core.status.event.id == 'load' && keycode == 68)) { + this._clickSL(core._WIDTH_ - 1, core._HEIGHT_ - 1); + return; + } + if (keycode >= 48 && keycode <= 57) { + if (keycode == 48) keycode = 58; + core.ui._drawSLPanel((keycode - 49) * 1000 + 1); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67) { + if (offset == 0) + core.doSL("autoSave", core.status.event.id); + else { + var id = 5 * page + offset; + if (core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.doSL(id, core.status.event.id); + } + return; + } + if (keycode == 69 && core.status.event.id != 'save') { // E 收藏切换 + this._clickSL(0, core._HEIGHT_ - 1); + return; + } + if (keycode == 46) { + if (offset == 0) { + core.playSound('操作失败'); + core.drawTip("无法删除自动存档!"); + } + else { + var id = 5 * page + offset; + if (core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.removeSave(id, function () { + core.ui._drawSLPanel(index, true); + }); + } + } + if (keycode == 70 && core.status.event.data.mode == 'all') { // F + this._clickSL_favorite(page, offset); + } +} + + +////// 系统设置界面时的点击操作 ////// +actions.prototype._clickSwitchs = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) return; + if (selection >= 0 && selection < choices.length) { + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound('确定'); + return core.ui._drawSwitchs_sounds(); + case 1: + core.status.event.selection = 0; + core.playSound('确定'); + return core.ui._drawSwitchs_display(); + case 2: + core.status.event.selection = 0; + core.playSound('取消'); + return core.ui._drawSettings(); + /*case 2: + core.status.event.selection = 0; + core.playSound('确定'); + return core.ui._drawSwitchs_action(); + case 3: + core.status.event.selection = 0; + core.playSound('取消'); + return core.ui._drawSettings();*/ + } + } +} + +////// 系统设置界面时,放开某个键的操作 ////// +actions.prototype._keyUpSwitchs = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSettings(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs); +} + +actions.prototype._clickSwitchs_sounds = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) { + //if (selection != 2) return; + } + if (selection >= 0 && selection < choices.length) { + var width = choices[selection].width; + var leftPos = (core._PX_ - width) / 2, rightPos = (core._PX_ + width) / 2; + var leftGrid = parseInt(leftPos / 32), rightGrid = parseInt(rightPos / 32) - 1; + core.status.event.selection = selection; + switch (selection) { + case 0: + return this._clickSwitchs_sounds_bgm(); + case 1: + return this._clickSwitchs_sounds_se(); + case 2: + if (x == leftGrid || x == leftGrid + 1) return this._clickSwitchs_sounds_userVolume(-1); + if (x == rightGrid || x == rightGrid + 1) return this._clickSwitchs_sounds_userVolume(1); + return; + case 3: + if (x == leftGrid || x == leftGrid + 1) return this._clickSwitchs_sounds_userVolume2(-1); + if (x == rightGrid || x == rightGrid + 1) return this._clickSwitchs_sounds_userVolume2(1); + return; + case 4: + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + } +} + +actions.prototype._clickSwitchs_sounds_bgm = function () { + core.triggerBgm(); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +actions.prototype._clickSwitchs_sounds_se = function () { + core.musicStatus.soundStatus = !core.musicStatus.soundStatus; + core.setLocalStorage('soundStatus', core.musicStatus.soundStatus); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +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; + if (core.musicStatus.playingBgm) core.material.bgms[core.musicStatus.playingBgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; + core.setLocalStorage('userVolume', core.musicStatus.userVolume); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +actions.prototype._clickSwitchs_sounds_userVolume2 = function (delta) { + var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume2)); + if (value == 0 && delta < 0) return; + core.musicStatus.userVolume2 = core.clamp(Math.pow(value + delta, 2) / 100, 0, 1); + //audioContext 音效 不受designVolume 影响 + if (core.musicStatus.gainNode != null) core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume2; + core.setLocalStorage('userVolume2', core.musicStatus.userVolume2); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +actions.prototype._keyUpSwitchs_sounds = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + if (keycode == 37) { + switch (core.status.event.selection) { + case 2: core.playSound('确定'); return this._clickSwitchs_sounds_userVolume(-1); + case 3: core.playSound('确定'); return this._clickSwitchs_sounds_userVolume2(-1); + } + } else if (keycode == 39) { + switch (core.status.event.selection) { + case 2: core.playSound('确定'); return this._clickSwitchs_sounds_userVolume(1); + case 3: core.playSound('确定'); return this._clickSwitchs_sounds_userVolume2(1); + } + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs_sounds); +} + +actions.prototype._clickSwitchs_display = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) { + if (selection != 0) return; + } + if (selection >= 0 && selection < choices.length) { + var width = choices[selection].width; + var leftPos = (core._PX_ - width) / 2, rightPos = (core._PX_ + width) / 2; + var leftGrid = parseInt(leftPos / 32), rightGrid = parseInt(rightPos / 32) - 1; + core.status.event.selection = selection; + switch (selection) { + case 0: + if (x == leftGrid || x == leftGrid + 1) return this._clickSwitchs_display_setSize(-1); + if (x == rightGrid || x == rightGrid + 1) return this._clickSwitchs_display_setSize(1); + return; + case 1: + core.playSound('确定'); + return this._clickSwitchs_display_enableHDCanvas(); + case 2: + core.playSound('确定'); + return this._clickSwitchs_display_enableEnemyPoint(); + case 3: + core.playSound('确定'); + return this._clickSwitchs_display_enemyDamage(); + case 4: + core.playSound('确定'); + return this._clickSwitchs_display_critical(); + case 5: + core.playSound('确定'); + return this._clickSwitchs_display_extraDamage(); + case 6: + core.playSound('确定'); + return this._clickSwitchs_display_extraDamageType(); + case 7: + core.playSound('确定'); + core.setLocalStorage('autoScale', core.getLocalStorage('autoScale') ? false : true); + core.ui._drawSwitchs_display(); + break; + case 8: + core.status.event.selection = 1; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + } +} + +actions.prototype._clickSwitchs_display_setSize = function (delta) { + core.setDisplayScale(delta); + var currentRatio = Math.max(window.devicePixelRatio || 1, core.domStyle.scale); + if (currentRatio > core.domStyle.ratio) { + core.drawTip("需刷新页面以调整UI清晰度"); + } + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_enableHDCanvas = function () { + core.flags.enableHDCanvas = !core.flags.enableHDCanvas; + core.setLocalStorage('enableHDCanvas', core.flags.enableHDCanvas); + core.drawTip("开关高清UI,需刷新页面方可生效"); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_enableEnemyPoint = function () { + core.flags.enableEnemyPoint = !core.flags.enableEnemyPoint; + core.setLocalStorage('enableEnemyPoint', core.flags.enableEnemyPoint); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_enemyDamage = function () { + core.flags.displayEnemyDamage = !core.flags.displayEnemyDamage; + core.updateDamage(); + core.setLocalStorage('enemyDamage', core.flags.displayEnemyDamage); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_critical = function () { + core.flags.displayCritical = !core.flags.displayCritical; + core.updateDamage(); + core.setLocalStorage('critical', core.flags.displayCritical); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_extraDamage = function () { + core.flags.displayExtraDamage = !core.flags.displayExtraDamage; + core.updateDamage(); + core.setLocalStorage('extraDamage', core.flags.displayExtraDamage); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_extraDamageType = function () { + core.flags.extraDamageType = (core.flags.extraDamageType + 1) % 3; + core.updateDamage(); + core.setLocalStorage('extraDamageType', core.flags.extraDamageType); + core.ui._drawSwitchs_display(); +} + +actions.prototype._keyUpSwitchs_display = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 1; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + if (keycode == 37) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_display_setSize(-1); + } + } else if (keycode == 39) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_display_setSize(1); + } + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs_display); +} + +actions.prototype._clickSwitchs_action = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) { + if (selection != 0 && selection != 1) return; + } + if (selection >= 0 && selection < choices.length) { + var width = choices[selection].width; + var leftPos = (core._PX_ - width) / 2, rightPos = (core._PX_ + width) / 2; + var leftGrid = parseInt(leftPos / 32), rightGrid = parseInt(rightPos / 32) - 1; + core.status.event.selection = selection; + switch (selection) { + case 0: + if (x == leftGrid || x == leftGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(-10); } + if (x == rightGrid || x == rightGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(10); } + return; + case 1: + if (x == leftGrid || x == leftGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(-100); } + if (x == rightGrid || x == rightGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(100); } + case 2: + core.playSound('确定'); + return this._clickSwitchs_action_potionNoRouting(); + case 3: + core.playSound('确定'); + return this._clickSwitchs_action_clickMove(); + case 4: + core.playSound('确定'); + return this._clickSwitchs_action_leftHandPrefer(); + case 5: + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + } +} + +actions.prototype._clickSwitchs_action_moveSpeed = function (delta) { + core.values.moveSpeed = core.clamp(core.values.moveSpeed + delta, 50, 200); + core.setLocalStorage("moveSpeed", core.values.moveSpeed); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_floorChangeTime = function (delta) { + core.values.floorChangeTime = core.clamp(core.values.floorChangeTime + delta, 0, 2000); + core.setLocalStorage("floorChangeTime", core.values.floorChangeTime); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_potionNoRouting = function () { + if (core.hasFlag('__potionNoRouting__')) core.removeFlag('__potionNoRouting__'); + else core.setFlag('__potionNoRouting__', true); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_clickMove = function () { + if (core.hasFlag('__noClickMove__')) core.removeFlag('__noClickMove__'); + else core.setFlag('__noClickMove__', true); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_leftHandPrefer = function () { + core.flags.leftHandPrefer = !core.flags.leftHandPrefer; + core.setLocalStorage('leftHandPrefer', core.flags.leftHandPrefer); + if (core.flags.leftHandPrefer) { + core.myconfirm("左手模式已开启!\n此模式下WASD将用于移动勇士,IJKL对应于原始的WASD进行存读档等操作。") + } + core.ui._drawSwitchs_action(); +} + +actions.prototype._keyUpSwitchs_action = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + if (keycode == 37) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(-10); + case 1: core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(-100); + } + } else if (keycode == 39) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(10); + case 1: core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(100); + } + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs_action); +} + +////// 系统菜单栏界面时的点击操作 ////// +actions.prototype._clickSettings = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawSwitchs(); + break; + case 1: + // core.playSound('确定'); + core.ui._drawKeyBoard(); + break; + case 2: + // core.playSound('确定'); + core.clearUI(); + core.ui._drawViewMaps(); + break; + case 3: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawNotes(); + break; + case 4: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawSyncSave(); + break; + case 5: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawGameInfo(); + break; + case 6: + return core.confirmRestart(); + case 7: + core.playSound('取消'); + core.ui.closePanel(); + break; + } + } + return; +} + +////// 系统菜单栏界面时,放开某个键的操作 ////// +actions.prototype._keyUpSettings = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSettings); +} + +////// 存档笔记页面时的点击操作 ////// +actions.prototype._clickNotes = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.playSound('确定'); + this._clickNotes_new(); + break; + case 1: + // core.playSound('确定'); + this._clickNotes_show(); + break; + case 2: + core.playSound('确定'); + this._clickNotes_edit(); + break; + case 3: + core.playSound('确定'); + this._clickNotes_delete(); + break; + case 4: + core.status.event.selection = 3; + core.playSound('取消'); + core.ui._drawSettings(); + break; + } + } +} + +actions.prototype.__clickNotes_replaceText = function (data) { + data = (data || "").replace(/[\${}]/g, "_") + .replace(/(\t|\\t)\[.*?\]/g, "") + .replace("\b", "\\b") + .replace(/\\b\[.*?\]/g, "") + .replace(/\n|\\n/g, " "); + if (data.length > 45) data = data.substring(0, 43) + "..."; + return data; +} + +actions.prototype._clickNotes_new = function () { + core.status.hero.notes = core.status.hero.notes || []; + core.myprompt("请输入一段笔记,不超过45字", null, function (data) { + data = core.actions.__clickNotes_replaceText(data); + if (data) { + core.status.hero.notes.push(data); + core.drawText("存档笔记新增成功!"); + } else { + core.ui.closePanel(); + } + }); +} + +actions.prototype._clickNotes_show = function () { + core.playSound('确定'); + core.status.hero.notes = core.status.hero.notes || []; + var result = []; + for (var i = 0; i < core.status.hero.notes.length; i += 5) { + var v = []; + for (var j = i; j < i + 5 && j < core.status.hero.notes.length; ++j) { + v.push(j + 1 + ". " + this.__clickNotes_replaceText(core.status.hero.notes[j])); + } + result.push("\t[存档笔记]" + v.join("\n")); + } + if (result.length == 0) result.push("当前没有存档笔记,试着新增一个吧!\n(菜单栏 -> 存档笔记 -> 新增存档笔记)"); + core.drawText(result); +} + +actions.prototype._clickNotes_edit = function () { + core.status.hero.notes = core.status.hero.notes || []; + if (core.status.hero.notes.length == 0) { + core.drawText("当前没有存档笔记,试着新增一个吧!"); + } else { + core.myprompt("请输入要编辑的存档笔记编号(1 - " + core.status.hero.notes.length + ")", "1", function (data) { + if (!data) core.ui.closePanel(); + var value = parseInt(data) || 0; + if (!value || value <= 0 || value > core.status.hero.notes.length) { + core.drawText("不合法的输入!"); + } else { + core.myprompt("请输入新内容,不超过45字", core.status.hero.notes[value - 1], function (data) { + data = core.actions.__clickNotes_replaceText(data); + if (data) { + core.status.hero.notes[value - 1] = data; + core.drawText("存档笔记编辑成功!"); + } else { + core.ui.closePanel(); + } + }); + } + }) + } +} + +actions.prototype._clickNotes_delete = function () { + core.status.hero.notes = core.status.hero.notes || []; + if (core.status.hero.notes.length == 0) { + core.stopSound(); + core.playSound('操作失败'); + core.drawText("当前没有存档笔记,无法删除!"); + } else { + core.myprompt("请输入要删除的所有存档笔记编号,以逗号分隔。不填则代表删除全部笔记。", null, function (data) { + if (data == null) { + core.ui.closePanel(); + return; + } + else if (!data) { + core.status.hero.notes = []; + core.drawText("所有存档笔记删除成功!"); + } else { + data = data.split(",").map(function (one) { return parseInt(one); }) + .filter(function (one) { return one && one > 0 && one <= core.status.hero.notes.length }); + if (data.length == 0) { + core.drawText("没有要删除的笔记!"); + } else { + data.sort(function (a, b) { return b - a; }) + .forEach(function (index) { + core.status.hero.notes.splice(index - 1, 1); + }); + core.drawText("已删除 " + data.sort().join(",") + " 号笔记"); + } + } + }) + } +} + +////// 存档笔记页面时,放开某个键的操作 ////// +actions.prototype._keyUpNotes = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 3; + core.playSound('取消'); + core.ui._drawSettings(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickNotes); +} + +////// 同步存档界面时的点击操作 ////// +actions.prototype._clickSyncSave = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawSyncSelect(); + break; + case 1: + core.playSound('确定'); + core.syncLoad(); + break; + case 2: + core.playSound('确定'); + core.status.event.selection = 0; + core.ui._drawLocalSaveSelect(); + break; + case 3: + core.playSound('确定'); + return this._clickSyncSave_readFile(); + case 4: + core.status.event.selection = 4; + core.playSound('取消'); + core.ui._drawSettings(); + break; + /*case 4: + // core.playSound('确定'); + return this._clickSyncSave_replay(); + case 5: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawStorageRemove(); + break; + case 6: + core.status.event.selection = 4; + core.playSound('取消'); + core.ui._drawSettings(); + break;*/ + + } + } + return; +} + +actions.prototype._clickSyncSave_readFile = function () { + core.readFile(function (obj) { + if (obj.name != core.firstData.name) return alert("存档和游戏不一致!"); + if (obj.version != core.firstData.version) return alert("游戏版本不一致!"); + if (!obj.data) return alert("无效的存档!"); + core.control._syncLoad_write(obj.data); + }, null, ".h5save"); +} + +actions.prototype._clickSyncSave_replay = function () { + core.ui._drawReplay(); +} + +////// 同步存档界面时,放开某个键的操作 ////// +actions.prototype._keyUpSyncSave = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 4; + core.playSound('取消'); + core.ui._drawSettings(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSyncSave); +} + +////// 同步存档选择界面时的点击操作 ////// +actions.prototype._clickSyncSelect = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.playSound('确定'); + core.myconfirm('你确定要同步全部存档么?\n这可能在存档较多的时候比较慢。', function () { + core.syncSave('all'); + }); + break; + case 1: + core.playSound('确定'); + core.syncSave(); + break; + case 2: + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSyncSave(); + break; + } + } +} + +////// 同步存档选择界面时,放开某个键的操作 ////// +actions.prototype._keyUpSyncSelect = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSyncSave(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSyncSelect); +} + +////// 存档下载界面时的点击操作 ////// +actions.prototype._clickLocalSaveSelect = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + if (selection < 2) { + var callback = function (saves) { + if (saves) { + var content = { + "name": core.firstData.name, + "version": core.firstData.version, + "data": saves + } + core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5save", + LZString.compressToBase64(JSON.stringify(content))); + } + }; + if (selection == 0) core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); + } + + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSyncSave(); + } +} + +////// 存档下载界面时,放开某个键的操作 ////// +actions.prototype._keyUpLocalSaveSelect = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSyncSave(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickLocalSaveSelect); +} + +////// 存档删除界面时的点击操作 ////// +actions.prototype._clickStorageRemove = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + return this._clickStorageRemove_all(); + case 1: + return this._clickStorageRemove_current(); + case 2: + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSyncSave(); + break; + } + } +} + +actions.prototype._clickStorageRemove_all = function () { + core.myconfirm("你确定要清除【全部游戏】的所有本地存档?\n此行为不可逆!!!", function () { + core.ui.drawWaiting("正在清空,请稍候..."); + core.clearLocalForage(function () { + core.saves.ids = {}; + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + core.saves.autosave.now = 0; + core.saves.cache = {}; + core.ui.closePanel(); + core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); + core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]你的所有存档已被清空。"); + }); + }); +} + +actions.prototype._clickStorageRemove_current = function () { + core.myconfirm("你确定要清除本游戏的所有本地存档?\n此行为不可逆!!!", function () { + var done = function () { + core.saves.ids = {}; + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + core.saves.autosave.now = 0; + core.ui.closePanel(); + core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); + core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]当前塔的存档已被清空。"); + } + core.ui.drawWaiting("正在清空,请稍候..."); + Object.keys(core.saves.ids).forEach(function (v) { + core.removeLocalForage("save" + v); + }); + core.removeLocalForage("autoSave", done); + }); +} + +////// 存档删除界面时,放开某个键的操作 ////// +actions.prototype._keyUpStorageRemove = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSyncSave(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickStorageRemove); +} + +////// 回放选择界面时的点击操作 ////// +actions.prototype._clickReplay = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: core.playSound('确定'); return this._clickReplay_fromBeginning(); + case 1: core.playSound('确定'); return this._clickReplay_fromLoad(); + case 2: core.playSound('确定'); return this._clickReplay_replayRemain(); + case 3: core.playSound('确定'); return this._clickReplay_replaySince(); + case 4: core.playSound('确定'); return core.chooseReplayFile(); + case 5: core.playSound('确定'); return this._clickReplay_download(); + case 6: core.playSound('取消'); return core.ui.closePanel(); + } + } +} + +actions.prototype._clickReplay_fromBeginning = function () { + core.ui.closePanel(); + core.startGame(core.status.hard, core.getFlag('__seed__'), core.cloneArray(core.status.route)); +} + +actions.prototype._clickReplay_fromLoad = function () { + core.status.event.id = 'replayLoad'; + core.status.event.selection = null; + core.clearUI(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); +} + +actions.prototype._clickReplay_replayRemain = function () { + core.closePanel(); + core.drawText([ + "\t[接续播放录像]该功能允许你播放\r[yellow]两个存档之间的录像\r,常常用于\r[yellow]区域优化\r。\n" + + "例如,有若干个区,已经全部通关;之后重打一区并进行了优化,则可以对剩余区域直接播放录像而无需全部重打。\n\n" + + "详细使用方法参见露珠录制的视频教程:\n\r[yellow]https://bilibili.com/video/BV1az4y1C78x", + "\t[步骤1]请选择一个存档。\n\r[yellow]该存档的坐标必须和当前勇士坐标完全相同。\r\n将尝试从此处开始回放。", + ], function () { + core.status.event.id = 'replayRemain'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); + }); +} + +actions.prototype._clickReplay_replaySince = function () { + core.closePanel(); + core.drawText([ + "\t[播放存档剩余录像]该功能为【接续播放录像】的简化版本,允许你播放\r[yellow]一个存档中剩余的录像\r,常常用于\r[yellow]录像局部优化\r。\n" + + "在录像正常播放中,你随时可以暂停并按S键进行存档;此时\r[yellow]剩余录像\r也会被记在存档中(在读档界面用\r[yellow][R]\r标识。)\n" + + "之后,你可以选择在路线优化后直接播放该存档的\r[yellow]剩余录像\r,而无需再像接续播放一样选择录像起点和终点。\n\n" + + "详细使用方法参见露珠录制的视频教程:\n\r[yellow]https://bilibili.com/video/BV1az4y1C78x", + "请选择一个存档。\n\n\r[yellow]该存档需为录像播放中存的,且坐标必须和当前勇士坐标完全相同。\r\n将尝试播放此存档的剩余录像。", + ], function () { + core.status.event.id = 'replaySince'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); + }); +} + +actions.prototype._clickReplay_download = function () { + // if (core.hasFlag('debug')) return core.drawText("\t[系统提示]调试模式下无法下载录像"); + core.download(core.firstData.name + "_" + core.formatDate2() + ".h5route", + LZString.compressToBase64(JSON.stringify({ + 'name': core.firstData.name, + 'hard': core.status.hard, + 'seed': core.getFlag('__seed__'), + 'route': core.encodeRoute(core.status.route) + }))); + +} + +////// 回放选择界面时,放开某个键的操作 ////// +actions.prototype._keyUpReplay = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickReplay); +} + +////// 游戏信息界面时的点击操作 ////// +actions.prototype._clickGameInfo = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: return this._clickGameInfo_openProject(); + case 1: return this._clickGameInfo_openComments(); + case 2: return core.ui._drawAbout(); + case 3: return this._clickGameInfo_download(); + case 4: + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSettings(); + break; + /*case 0: return core.ui._drawStatistics(); + case 1: return this._clickGameInfo_openProject(); + case 2: return this._clickGameInfo_openComments(); + case 3: return core.ui._drawHelp(); + case 4: return core.ui._drawAbout(); + case 5: return this._clickGameInfo_download(); + case 6: + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSettings(); + break; + */ + } + } +} + +actions.prototype._clickGameInfo_openProject = function () { + if (core.platform.isPC) + window.open("editor.html", "_blank"); + else { + core.myconfirm("即将离开本游戏,跳转至工程页面,确认?", function () { + window.location.href = "editor-mobile.html"; + }); + } +} + +actions.prototype._clickGameInfo_openComments = function () { + if (core.platform.isPC) { + window.open("/score.php?name=" + core.firstData.name, "_blank"); + } + else { + core.myconfirm("即将离开本游戏,跳转至评论页面,确认?", function () { + window.location.href = "/score.php?name=" + core.firstData.name; + }); + } +} + +actions.prototype._clickGameInfo_download = function () { + if (core.platform.isPC) + window.open(core.firstData.name + ".zip"); + else + window.location.href = core.firstData.name + ".zip"; +} + +////// 游戏信息界面时,放开某个键的操作 ////// +actions.prototype._keyUpGameInfo = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 5; + core.playSound('取消'); + return core.ui._drawSettings(); + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickGameInfo); +} + +////// “虚拟键盘”界面时的点击操作 ////// +actions.prototype._clickKeyBoard = function (x, y) { + var m = this._HX_; + if (y == this._HY_ - 3 && x >= m - 5 && x <= m + 5) { + core.ui.closePanel(); + core.keyUp(112 + x + 5 - m); + } + if (y == this._HY_ - 2 && x >= m - 5 && x <= m + 4) { + core.ui.closePanel(); + core.keyUp(x == m + 4 ? 48 : 49 + x + 5 - m); // 1-9: 49-57; 0: 48 + } + // 字母 + var lines = [ + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Z", "X", "C", "V", "B", "N", "M"], + ]; + if (y == this._HY_ - 1 && x >= m - 5 && x <= m + 4) { + core.ui.closePanel(); + core.keyUp(lines[0][x + 5 - m].charCodeAt(0)); + } + if (y == this._HY_ && x >= m - 5 && x <= m + 3) { + core.ui.closePanel(); + core.keyUp(lines[1][x + 5 - m].charCodeAt(0)); + } + if (y == this._HY_ + 1 && x >= m - 5 && x <= m + 1) { + core.ui.closePanel(); + core.keyUp(lines[2][x + 5 - m].charCodeAt(0)); + } + if (y == this._HY_ + 2 && x >= m - 5 && x <= m + 5) { + core.ui.closePanel(); + if (x == m - 5) core.keyUp(189); // - + if (x == m - 4) core.keyUp(187); // = + if (x == m - 3) core.keyUp(219); // [ + if (x == m - 2) core.keyUp(221); // ] + if (x == m - 1) core.keyUp(220); // \ + if (x == m) core.keyUp(186); // ; + if (x == m + 1) core.keyUp(222); // ' + if (x == m + 2) core.keyUp(188); // , + if (x == m + 3) core.keyUp(190); // . + if (x == m + 4) core.keyUp(191); // / + if (x == m + 5) core.keyUp(192); // ` + } + if (y == this._HY_ + 3 && x >= m - 5 && x <= m + 4) { + core.ui.closePanel(); + if (x == m - 5) core.keyUp(27); // ESC + if (x == m - 4) core.keyUp(9); // TAB + if (x == m - 3) core.keyUp(20); // CAPS + if (x == m - 2) core.keyUp(16); // SHIFT + if (x == m - 1) core.keyUp(17); // CTRL + if (x == m) core.keyUp(18); // ALT + if (x == m + 1) core.keyUp(32); // SPACE + if (x == m + 2) core.keyUp(8); // BACKSPACE + if (x == m + 3) core.keyUp(13); // ENTER + if (x == m + 4) core.keyUp(46); // DEL + } + if (y == this._HY_ + 4 && x >= m + 3 && x <= m + 5) { + core.playSound('取消'); + core.ui.closePanel(); + } +} + +////// 光标界面时的点击操作 ////// +actions.prototype._clickCursor = function (x, y, px, py) { + if (x == core.status.automaticRoute.cursorX && y == core.status.automaticRoute.cursorY) { + core.ui.closePanel(); + // 视为按下再放起 + this.doRegisteredAction('ondown', x, y, px, py); + this.doRegisteredAction('onup', x, y, px, py); + return; + } + core.status.automaticRoute.cursorX = x; + core.status.automaticRoute.cursorY = y; + core.ui._drawCursor(); +} + +////// 光标界面时,按下某个键的操作 ////// +actions.prototype._keyDownCursor = function (keycode) { + if (keycode == 37) { // left + core.status.automaticRoute.cursorX--; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } + if (keycode == 38) { // up + core.status.automaticRoute.cursorY--; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } + if (keycode == 39) { // right + core.status.automaticRoute.cursorX++; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } + if (keycode == 40) { // down + core.status.automaticRoute.cursorY++; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } +} + +////// 光标界面时,放开某个键的操作 ////// +actions.prototype._keyUpCursor = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67 || keycode == 69) { + core.playSound('确定'); + core.ui.closePanel(); + var x = core.status.automaticRoute.cursorX; + var y = core.status.automaticRoute.cursorY; + // 视为按下再放起 + this.doRegisteredAction('ondown', x, y, 32 * x + 16, 32 * y + 16); + this.doRegisteredAction('onup', x, y, 32 * x + 16, 32 * y + 16); + return; + } +} diff --git a/api.md b/api.md new file mode 100644 index 0000000..e9991ca --- /dev/null +++ b/api.md @@ -0,0 +1,2415 @@ +# 附录:API列表 + +?> 样板全部的API列表都在这里了! + +这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 + +本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 + +如有任何疑问,请联系小艾寻求帮助。 + +## core.js + +core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 + +但是,core中定义了很多游戏运行时的状态,这些状态很多都会被使用到。 + +```text +core.__SIZE__, core.__PIXELS__ +游戏窗口大小;对于13x13的游戏而言这两个值分别是13和416,15x15来说分别是15和480。 + + +core.material +游戏中的所有资源列表,具体分为如下内容: +core.material.animates (动画) +core.material.bgms (背景音乐) +core.material.enemys (怪物信息,来自于 project/enemys.js) +core.material.icons (图标信息,来自于 project/icons.js) +core.material.images (图片素材,存放了各项素材图片如items.png等) + core.material.images.autotile (所有的自动元件图片) + core.material.images.tilesets (所有的额外素材图片) + core.material.images.images (用户引入的其他图片) +core.material.items (道具信息) +core.material.sounds (音效) + + +core.animateFrame +主要是记录和requestAnimationFrame相关的一些数据,常用的如下: +core.animateFrame.totalTime (游戏总的运行时时间) +core.animateFrame.weather (当前的天气信息) +core.animateFrame.asyncId (当前的异步处理事件的内容) + + +core.musicStatus +主要是记录和音效相关的内容,常用的如下: +core.musicStatus.bgmStatus (音乐开启状态) +core.musicStatus.soundStatus (音效开启状态) +core.musicStatus.playingBgm (当前正在播放的BGM) +core.musicStatus.lastBgm (最近一次尝试播放的BGM) +core.musicStatus.volume (当前的音量) +core.musicStatus.cachedBgms (背景音乐的缓存内容) +core.musicStatus.cacheBgmCount (背景音乐的缓存数量,默认值是4) + + +core.platform +游戏平台相关信息,常见的几个如下: +core.platform.isPC (是否是电脑端) +core.platform.isAndroid (是否是安卓端) +core.platform.isIOS (是否是iOS端) + + +core.domStyle +游戏的界面信息,包含如下几个: +core.domStyle.scale (当前的放缩比) +core.domStyle.ratio (高清UI的放缩比) +core.domStyle.isVertical (当前是否是竖屏状态) +core.domStyle.showStatusBar (当前是否显示状态栏) +core.domStyle.toolbarBtn (当前是否显示工具栏) + + +core.bigmap +当前的地图的尺寸信息,主要包含如下几个 +core.bigmap.width (当前地图的宽度) +core.bigmap.height (当前地图的高度) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素x) +core.bigmap.offsetY (当前地图针对窗口左上角的偏移像素y) +core.bigmap.tempCanvas (一个临时画布,可以用来临时绘制很多东西) + + +core.saves +和存档相关的信息,包含如下几个: +core.saves.saveIndex (上次保存或读取的存档编号) +core.saves.ids (当前存在存档的编号列表) +core.saves.autosave (自动存档的信息) +core.saves.favorite (收藏的存档) +core.saves.favoriteNames (自定义存档的名称) + + +core.status +游戏的状态相关,是整个游戏中最重要的东西,其核心是如下几条: +请注意,每次重新开始、存档或读档时,core.status都会重新初始化。 +core.status.played (当前是否在游戏中) +core.status.gameOver (当前是否已经游戏结束,即win或lose) +core.status.hero (勇士信息;此项和全塔属性中的hero大体是对应的) + core.status.hero.name 勇士名 + core.status.hero.lv 当前等级 + core.status.hero.hpmax 当前生命上限 + core.status.hero.hp 当前生命值 + core.status.hero.manamax 当前魔力上限 + core.status.hero.mana 当前魔力值 + core.status.hero.atk 当前攻击力 + core.status.hero.def 当前防御力 + core.status.hero.mdef 当前护盾值 + core.status.hero.money 当前金币值 + core.status.hero.exp 当前经验值 + core.status.hero.loc 当前的位置信息 + core.status.hero.equipment 当前装上的装备 + core.status.hero.items 当前拥有的道具信息 + core.status.hero.flags 当前的各项flag信息 + core.status.hero.step 当前的步数值 + core.status.hero.statistics 当前的统计信息 +core.status.floorId (当前所在的楼层) +core.status.maps (所有的地图信息) +core.status.thisMap (当前的地图信息,等价于core.status.maps[core.status.floorId]) +core.status.bgmaps (所有背景层的信息) +core.status.fgmaps (所有的前景层的信息) +core.status.checkBlock (地图上的阻激夹域信息,也作为光环的缓存) +core.status.lockControl (当前是否是控制锁定状态) +core.status.automaticRoute (当前的自动寻路信息) +core.status.route (当前记录的录像) +core.status.replay (录像回放时要用到的信息) +core.status.shops (所有全局商店信息) +core.status.textAttribute (当前的文字属性,如颜色、背景等信息,和setText事件对应) +core.status.globalAttribute (当前的全局属性,如边框色、装备栏等) +core.status.curtainColor (当前色调层的颜色) +core.status.globalAnimateObjs (当前的全局帧动画效果) +core.status.floorAnimateObjs (当前的楼层贴图帧动画效果) +core.status.boxAnimateObjs (当前的盒子帧动画效果,例如怪物手册中的怪物) +core.status.autotileAnimateObjs (当前楼层的自动元件动画效果) +core.status.globalAnimateStatus (当前的帧动画的状态) +core.status.animateObjs (当前的播放动画信息) + + +core.floorIds +一个数组,表示所有的楼层ID,和全塔属性中的floorIds一致。 + + +core.floors +从楼层文件中读取全部的地图数据。 +和core.status.maps不同的是,后者在每次重新开始和读档时都会重置,也允许被修改(会存入存档)。 +而core.floors全程唯一,不允许被修改。 + + +core.statusBar +状态栏信息,例如状态栏图片,图标,以及各个内容的DOM定义等。 +core.statusBar.images (所有的系统图标,和icons.png对应) +core.statusBar.icons (状态栏中绘制的图标内容) + + +core.values +所有的全局数值信息,和全塔属性中的values一致。 +此项允许被直接修改,会存入存档。 + + +core.flags +所有的全塔开关,和全塔属性中的flags一致。 +此项不允许被直接修改,如有需要请使用“设置系统开关”事件,或者调用core.setGlobalFlag这个API。 + + +core.plugin +定义的插件函数。 + + +core.doFunc(func, _this) +执行一个函数,func为函数体或者插件中的函数名,_this为使用的this。 +如果func为一个字符串,则视为插件中的函数名,同时_this将被设置成core.plugin。 +此函数剩余参数将作为参数被传入func。 +``` + +## actions.js + +主要是处理一些和用户交互相关的内容。 + +```text +doRegisteredAction: fn(action: string, params: ?) +执行一个用户交互行为 + +keyDown: fn(keyCode: number) +根据按下键的code来执行一系列操作 + +keyDownCtrl: fn() -> bool +长按Ctrl键时 + +keyUp: fn(keyCode: number, altKey?: bool, fromReplay?: bool) +根据放开键的code来执行一系列操作 + +longClick: fn(x: number, y: number, px: number, py: number, fromEvent?: bool) +长按 + +onStatusBarClick: fn(e?: Event) +点击自绘状态栏时 + +onclick: fn(x: number, y: number, px: number, py: number, stepPostfix?: [?]) +具体点击屏幕上(x,y)点时,执行的操作 + +ondown: fn(loc: {x: number, y: number, size: number}) +点击(触摸)事件按下时 + +onkeyDown: fn(e: Event) +按下某个键时 + +onkeyUp: fn(e: Event) +放开某个键时 + +onmousewheel: fn(direct: number) +滑动鼠标滚轮时的操作 + +onmove: fn(loc: {x: number, y: number, size: number}) +当在触摸屏上滑动时 + +onup: fn(loc: {x: number, y: number, size: number}) +当点击(触摸)事件放开时 + +pressKey: fn(keyCode: number) +按住某个键时 + +registerAction: fn(action: string, name: string, func: string|fn(params: ?), priority?: number) +此函数将注册一个用户交互行为。 +action: 要注册的交互类型,如 ondown, onclick, keyDown 等等。 +name: 你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +func: 执行函数。 +如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 +priority: 优先级;优先级高的将会被执行。此项可不填,默认为0 + +unregisterAction: fn(action: string, name: string) +注销一个用户交互行为 +``` + +## control.js + +负责整个游戏的核心控制系统,分为如下几个部分: +- requestAnimationFrame相关 +- 标题界面,开始和重新开始游戏 +- 自动寻路和人物行走相关 +- 画布、位置、阻激夹域、显伤等相关 +- 录像的回放相关 +- 存读档,自动存档,同步存档等相关 +- 人物属性和状态、位置、变量等相关 +- 天气、色调、音乐和音效的播放 +- 状态栏和工具栏相关 +- 界面resize相关 + +```text +addBuff: fn(name: string, value: number) +增减主角某个属性的百分比修正倍率,加减法叠加和抵消。等价于 core.setBuff(name, core.getBuff(name) + value) +例如:core.addBuff('atk', -0.1); // 主角获得一层“攻击力减一成”的负面效果 +name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN +value: 倍率的增量 + +addFlag: fn(name: string, value: number) +增减一个flag变量,等价于 core.setFlag(name, core.getFlag(name, 0) + value) +例如:core.addFlag('hatred', 1); // 增加1点仇恨值 +name: 变量名,支持中文 +value: 变量的增量 + +addGameCanvasTranslate: fn(x?: number, y?: number) +加减画布偏移 + +addStatus: fn(name: string, value: number) +增减主角的某个属性,等价于core.setStatus(name, core.getStatus(name) + value) +例如:core.addStatus('atk', 100'); // 给主角攻击力加100 +name: 属性的英文名 +value: 属性的增量 + +addSwitch: fn(x: number, y: number, floorId: string, name: string, value: number) +增加某个独立开关的值 + +autosave: fn(removeLast?: bool) +自动存档 + +checkAutosave: fn() +实际将自动存档写入存储 + +checkBgm: fn() +检查bgm状态 + +checkBlock: fn() +检查并执行领域、夹击、阻击事件 + +checkRouteFolding: fn() +检查录像折叠信息 + +chooseReplayFile: fn() +选择录像文件 + +clearAutomaticRouteNode: fn(x?: number, y?: number) +清除自动寻路路线 + +clearContinueAutomaticRoute: fn(callback?: fn()) +清空剩下的自动寻路列表 + +clearRouteFolding: fn() +清空录像折叠信息 + +clearStatus: fn() +清除游戏状态和数据 + +clearStatusBar: fn() +清空状态栏 + +continueAutomaticRoute: fn() +继续剩下的自动寻路操作 + +debug: fn() +开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。 +此模式下不可回放录像和上传成绩。 + +doSL: fn(id?: string, type?: string) +实际进行存读档事件 + +drawDamage: fn(string|CanvasRenderingContext2D) +仅绘制地图显伤 + +drawHero: fn(status?: string, offset?: number, frame?: number) +绘制主角和跟随者并重置视野到以主角为中心 +例如:core.drawHero(); // 原地绘制主角的静止帧并重置视野野 +status: 只能为 stop, leftFoot 和 rightFoot,不填用stop。 +offset: 相对主角逻辑位置的偏移量,不填视为无偏移。 +frame: 绘制的第几帧 + +gatherFollowers: fn() +立刻聚集所有的跟随者 + +getAllSaves: fn(callback?: fn()) +获得所有存档内容 + +getBuff: fn(name: string) -> number +读取主角某个属性的百分比修正倍率,初始值为1 +例如:core.getBuff('atk'); // 主角当前能发挥出多大比例的攻击力 +name: 属性的英文名 + +getFlag: fn(name: string, defaultValue?: ?) +读取一个flag变量 +name: 变量名,支持中文 +defaultValue: 当变量不存在时的返回值,可选(事件流中默认填0)。 + +getHeroLoc: fn(name: string) -> string|number +读取主角的位置和/或朝向 +例如:core.getHeroLoc(); // 读取主角的位置和朝向 +name: 要读取横坐标还是纵坐标还是朝向还是都读取 +返回值:name ? core.status.hero.loc[name] : core.status.hero.loc + +getLvName: fn(lv?: number) -> string|number +根据级别的数字获取对应的名称,后者定义在全塔属性 +例如:core.getLvName(); // 获取主角当前级别的名称,如“下级佣兵” +lv: 级别的数字,不填则视为主角当前的级别 +返回值:级别的名称,如果不存在就还是返回数字 + +getMappedName: fn(name: string) -> string +获得映射文件名 + +getNakedStatus: fn(name: string) +获得勇士原始属性(无装备和衰弱影响) + +getNextLvUpNeed: fn() -> number +获得下次升级需要的经验值。 +升级扣除模式下会返回经验差值;非扣除模式下会返回总共需要的经验值。 +如果无法进行下次升级,返回null。 + +getPlayingSounds: fn(name?: string) -> [number] +获得当前正在播放的所有(指定)音效的id列表 +name: 音效名,可用别名;不填代表返回正在播放的全部音效 +返回值: 一个列表,每一项为一个正在播放的音效id;可用core.stopSound立刻停止播放 + +getRealStatus: fn(name: string) +计算主角的某个属性,包括百分比修正 +例如:core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值 +name: 属性的英文名,请注意只能用于数值类属性哦,否则乘法会得到NaN + +getRealStatusOrDefault: fn(status?: ?, name?: string) +从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 + +getSave: fn(index?: number, callback?: fn(data: ?)) +获得某个存档内容 + +getSaveIndexes: fn(callback?: fn()) +获得所有存在存档的存档位 + +getSaves: fn(ids?: ?, callback?: fn()) +获得某些存档内容 + +getStatus: fn(name: string) -> number +读取主角的某个属性,不包括百分比修正 +例如:core.getStatus('atk'); // 读取主角的攻击力 +name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.getHeroLoc(name),其他的会直接读取 core.status.hero[name] + +getStatusLabel: fn(name: string) -> string +获得某个状态的名字,如atk->攻击,def->防御等 + +getStatusOrDefault: fn(status?: ?, name?: string) +从status中获得属性,如果不存在则从勇士属性中获取 + +getSwitch: fn(x: number, y: number, floorId: string, name: string, defaultValue?: ?) +获得某个独立开关的值 + +hasFlag: fn(name: string) -> bool +判定一个flag变量是否存在且不为false、0、''、null、undefined和NaN +例如:core.hasFlag('poison'); // 判断主角当前是否中毒 +name: 变量名,支持中文 +此函数等价于 !!core.getFlag(name) + +hasSave: fn(index?: number) -> bool +判断某个存档位是否存在存档 + +hasSwitch: fn(x: number, y: number, floorId: string, name: string) -> bool +判定某个独立开关的值 + +hideStartAnimate: fn(callback?: fn()) +淡出标题画面 +例如:core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择 +callback: 标题画面完全淡出后的回调函数 + +hideStatusBar: fn(showToolbox?: bool) +隐藏状态栏 +showToolbox: 是否不隐藏竖屏工具栏 + +isMoving: fn() -> bool +当前是否正在移动 + +isPlaying: fn() -> bool +游戏是否已经开始 + +isReplaying: fn() -> bool +是否正在播放录像 + +loadData: fn(data?: ?, callback?: fn()) +从本地读档 + +lockControl: fn() +锁定用户控制,常常用于事件处理 + +moveAction: fn(callback?: fn()) +尝试前进一步,如果面前不可被踏入就会直接触发该点事件 +请勿直接使用此函数,如有需要请使用「勇士前进一步或撞击」事件 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +moveDirectly: fn(destX?: number, destY?: number, ignoreSteps?: number) +瞬间移动 + +moveHero: fn(direction?: string, callback?: fn()) +连续前进,不撞南墙不回头 +例如:core.moveHero(); // 连续前进 +direction: 可选,如果设置了就会先转身到该方向 +callback: 可选,如果设置了就只走一步 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +moveOneStep: fn(callback?: fn()) +每移动一格后执行的事件 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +moveViewport: fn(x: number, y: number, time?: number, callback?: fn()) +移动视野范围 + +nearHero: fn(x: number, y: number, n?: number) -> bool +判定主角是否身处某个点的锯齿领域(取曼哈顿距离) +例如:core.nearHero(6, 6, 6); // 判定主角是否身处点(6,6)的半径为6的锯齿领域 +x: 领域的中心横坐标 +y: 领域的中心纵坐标 +n: 领域的半径,不填视为1 + +nextX: fn(n?: number) -> number +获取主角面前第n格的横坐标 +例如:core.closeDoor(core.nextX(), core.nextY(), 'yellowDoor', core.turnHero); // 在主角面前关上一扇黄门,然后主角顺时针旋转90° +n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1 + +nextY: fn(n?: number) -> number +获取主角面前第n格的纵坐标 +例如:core.jumpHero(core.nextX(2), core.nextY(2)); // 主角向前跃过一格,即跳跃靴道具的使用效果 +n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1 + +pauseBgm: fn() +暂停背景音乐的播放 + +pauseReplay: fn() +暂停播放 + +playBgm: fn(bgm: string, startTime?: number) +播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__ +例如:core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟 +bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名 +startTime: 跳过前多少秒,不填则不跳过 + +playSound: fn(sound: string, pitch?: number, callback?: fn()) -> number +播放一个音效 +sound: 音效名;可以使用文件别名。 +pitch: 播放的音调;可选,如果设置则为30-300之间的数值;100为正常音调。 +callback: 可选,播放完毕后执行的回调函数。 +返回:一个数字,可用于core.stopSound的参数来只停止该音效。 + +registerAnimationFrame: fn(name: string, needPlaying: bool, func?: fn(timestamp: number)) +注册一个 animationFrame +name: 名称,可用来作为注销使用 +needPlaying: 是否只在游戏运行时才执行(在标题界面不执行) +func: 要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数 + +registerReplayAction: fn(name: string, func: fn(action?: string) -> bool) +注册一个录像行为 +name: 自定义名称,可用于注销使用 +func: 具体执行录像的函数,可为一个函数或插件中的函数名; +需要接受一个action参数,代表录像回放时的下一个操作 +func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。 + +registerResize: fn(name: string, func: fn(obj: ?)) +注册一个resize函数 +name: 名称,可供注销使用 +func: 可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。 + +registerWeather: fn(name: string, initFunc: fn(level: number), frameFunc?: fn(timestamp: number, level: number)) +注册一个天气 +name: 要注册的天气名 +initFunc: 当切换到此天气时的初始化;接受level(天气等级)为参数;可用于创建多个节点(如初始化雪花) +frameFunc: 每帧的天气效果变化;可接受timestamp(从页面加载完毕到当前所经过的时间)和level(天气等级)作为参数 +天气应当仅在weather层进行绘制,推荐使用core.animateFrame.weather.nodes用于节点信息。 + +removeFlag: fn(name: string) +删除某个flag/变量 + +removeSave: fn(index?: number, callback?: fn()) +删除某个存档 + +removeSwitch: fn(x: number, y: number, floorId: string, name: string) +删除某个独立开关 + +replay: fn() +回放下一个操作 + +resize: fn() +屏幕分辨率改变后重新自适应 + +resumeBgm: fn(resumeTime?: number) +恢复背景音乐的播放 +resumeTime: 从哪一秒开始恢复播放 + +resumeReplay: fn() +恢复播放 + +rewindReplay: fn() +回退到上一个录像节点 + +saveAndStopAutomaticRoute: fn() +保存剩下的寻路,并停止 + +saveData: fn() +存档到本地 + +screenFlash: fn(color: [number], time: number, times?: number, moveMode?: string, callback?: fn()) +画面闪烁 +例如:core.screenFlash([255, 0, 0, 1], 3); // 红屏一闪而过 +color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若填负数则会被视为0)的颜色数组,必填 +time: 单次闪烁时长,实际闪烁效果为先花其三分之一的时间渐变到目标色调,再花剩余三分之二的时间渐变回去 +times: 闪烁的总次数,不填或填0都视为1 +moveMode: 渐变方式 +callback: 闪烁全部完毕后的回调函数,可选 + +setAutoHeroMove: fn(steps: [?]) +连续行走 +例如:core.setAutoHeroMove([{direction: "up", step: 1}, {direction: "left", step: 3}]); // 上左左左 +steps: 压缩的步伐数组,每项表示朝某方向走多少步 + +setAutomaticRoute: fn(destX: number, destY: number, stepPostfix: [{x: number, y: number, direction: string}]) +半自动寻路,用于鼠标或手指拖动 +例如:core.setAutomaticRoute(0, 0, [{direction: "right", x: 4, y: 9}, {direction: "right", x: 5, y: 9}]); +destX: 鼠标或手指的起拖点横坐标 +destY: 鼠标或手指的起拖点纵坐标 +stepPostfix: 拖动轨迹的数组表示,每项为一步的方向和目标点。 + +setBgmSpeed: fn(speed: number, usePitch?: bool) +设置背景音乐的播放速度和音调 +speed: 播放速度,必须为30-300中间的值。100为正常速度。 +usePitch: 是否同时改变音调(部分设备可能不支持) + +setBuff: fn(name: string, value: number) +设置主角某个属性的百分比修正倍率,初始值为1, +倍率存放在flag: '__'+name+'_buff__' 中 +例如:core.setBuff('atk', 0.5); // 主角能发挥出的攻击力减半 +name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN +value: 新的百分比修正倍率,不填(效果上)视为1 + +setCurtain: fn(color?: [number], time?: number, moveMode?: string, callback?: fn()) +更改画面色调,不计入存档。如需长期生效请使用core.events._action_setCurtain()函数 +例如:core.setCurtain(); // 恢复画面色调,用时四分之三秒 +color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若为负数则会被视为0)的颜色数组,不填视为[0, 0, 0, 0] +time: 渐变时间,单位为毫秒。不填视为750ms,负数视为0(无渐变,立即更改) +moveMode: 渐变方式 +callback: 更改完毕后的回调函数,可选。事件流中常取core.doAction + +setDisplayScale: fn(delta: number) +设置屏幕放缩 + +setFlag: fn(name: string, value: ?) +设置一个flag变量 +例如:core.setFlag('poison', true); // 令主角中毒 +name: 变量名,支持中文 +value: 变量的新值,不填或填null视为删除 + +setGameCanvasTranslate: fn(ctx: string|CanvasRenderingContext2D, x: number, y: number) +设置大地图的偏移量 + +setHeroLoc: fn(name: string, value: string|number, noGather?: bool) +设置勇士位置 +值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制; +如需立刻重新绘制地图还需调用:core.clearMap('hero'); core.drawHero(); 来对界面进行更新。 +例如:core.setHeroLoc('x', 5) // 将勇士当前位置的横坐标设置为5。 +name: 要设置的坐标属性 +value: 新值 +noGather: 是否聚集跟随者 + +setHeroMoveInterval: fn(callback?: fn()) +设置行走的效果动画 + +setHeroOpacity: fn(opacity?: number, moveMode?: string, time?: number, callback?: fn()) +改变勇士的不透明度 + +setMusicBtn: fn() +设置音乐图标的显隐状态 + +setReplaySpeed: fn(speed: number) +设置播放速度 + +setStatus: fn(name: string, value: number) +设置主角的某个属性 +例如:core.setStatus('atk', 100); // 设置攻击力为100 +name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.setHeroLoc(name, value),其他的会直接对 core.status.hero[name] 赋值 +value: 属性的新值 + +setSwitch: fn(x: number, y: number, floorId: string, name: string, value?: ?) +设置某个独立开关的值 + +setToolbarButton: fn(useButton?: bool) +改变工具栏为按钮1-8 + +setViewport: fn(px?: number, py?: number) +设置视野范围 +px,py: 左上角相对大地图的像素坐标,不需要为32倍数 + +setWeather: fn(type?: string, level?: number) +设置天气,不计入存档。如需长期生效请使用core.events._action_setWeather()函数 +例如:core.setWeather('fog', 10); // 设置十级大雾天 +type: 新天气的类型,不填视为晴天 +level: 新天气(晴天除外)的级别,必须为不大于10的正整数,不填视为5 + +showStartAnimate: fn(noAnimate?: bool, callback?: fn()) +进入标题画面 +例如:core.showStartAnimate(); // 重启游戏但不重置bgm +noAnimate: 可选,true表示不由黑屏淡入而是立即亮屏 +callback: 可选,完全亮屏后的回调函数 + +showStatusBar: fn() +显示状态栏 + +speedDownReplay: fn() +减速播放 + +speedUpReplay: fn() +加速播放 + +startReplay: fn(list: [string]) +开始播放录像 + +stepReplay: fn() +单步播放 + +stopAutomaticRoute: fn() +停止自动寻路操作 + +stopReplay: fn(force?: bool) +停止播放 + +stopSound: fn(id?: number) +停止播放音效。如果未指定id则停止所有音效,否则只停止指定的音效。 + +syncLoad: fn() +从服务器加载存档 + +syncSave: fn(type?: string) +同步存档到服务器 + +triggerBgm: fn() +开启或关闭背景音乐的播放 + +triggerDebuff: fn(action: string, type: string|[string]) +获得或移除毒衰咒效果 +action: 要获得还是移除,'get'为获得,'remove'为移除 +type: 获得或移除的内容(poison/weak/curse),可以为字符串或数组 + +triggerReplay: fn() +播放或暂停录像回放 + +tryMoveDirectly: fn(destX: number, destY: number) +尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致 +例如:core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件 +destX: 目标点的横坐标 +destY: 目标点的纵坐标 + +turnHero: fn(direction?: string) +主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心 +例如:core.turnHero(); // 主角顺时针旋转90°,即单击主角或按下Z键的效果 +direction: 主角的新朝向,可为 up, down, left, right, :left, :right, :back 七种之一 + +unlockControl: fn() +解锁用户控制行为 + +unregisterAnimationFrame: fn(name: string) +注销一个animationFrame + +unregisterReplayAction: fn(name: string) +注销一个录像行为 + +unregisterResize: fn(name: string) +注销一个resize函数 + +unregisterWeather: fn(name: string) +注销一个天气 + +updateCheckBlock: fn(floorId?: string) +更新领域、夹击、阻击的伤害地图 + +updateDamage: fn(floorId?: string, ctx?: string|CanvasRenderingContext2D) +重算并绘制地图显伤 +例如:core.updateDamage(); // 更新当前地图的显伤,绘制在显伤层(废话) +floorId: 地图id,不填视为当前地图。预览地图时填写 +ctx: 绘制到的画布,如果填写了就会画在该画布而不是显伤层 + +updateFollowers: fn() +更新跟随者坐标 + +updateHeroIcon: fn(name: string) +更新状态栏的勇士图标 + +updateStatusBar: fn(doNotCheckAutoEvents?: bool) +立刻刷新状态栏和地图显伤 +doNotCheckAutoEvents: 是否不检查自动事件 + +updateViewport: fn() +更新大地图的可见区域 + +waitHeroToStop: fn(callback?: fn()) +等待主角停下 +例如:core.waitHeroToStop(core.vibrate); // 等待主角停下,然后视野左右抖动1秒 +callback: 主角停止后的回调函数 +``` + +## enemys.js + +定义了一系列和怪物相关的API函数。 + +```text +canBattle: fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> bool +判定主角当前能否打败某只敌人 +例如:core.canBattle('greenSlime',0,0,'MT0') // 能否打败主塔0层左上角的绿头怪(假设有) +enemy: 敌人id或敌人对象 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +floorId: 敌人所在的地图,可选 +返回值:true表示可以打败,false表示无法打败 + +getCurrentEnemys: fn(floorId?: string) -> [enemy] +获得某张地图的敌人集合,用于手册绘制 +例如:core.getCurrentEnemys('MT0') // 主塔0层的敌人集合 +floorId: 地图id,可选 +返回值:敌人集合,按伤害升序排列,支持多朝向怪合并 + +getDamage: fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> number +获得某只敌人对主角的总伤害 +例如:core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害 +enemy: 敌人id或敌人对象 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +floorId: 敌人所在的地图,可选 +返回值:总伤害,如果因为没有破防或无敌怪等其他原因无法战斗,则返回null + +getDamageInfo: fn(enemy: string|enemy, hero?: ?, x?: number, y?: number, floorId?: string) -> {damage: number, per_damage: number, hero_per_damage: number, init_damage: number, mon_hp: number, mon_atk: number, mon_def: number, turn: number} +获得战斗伤害信息 +例如:core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害 +enemy: 敌人id或敌人对象 +hero: 可选,此时的勇士属性 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +floorId: 敌人所在的地图,可选 +返回值:伤害计算信息,如果因为没有破防或无敌怪等其他原因无法战斗,则返回null + +getDamageString: fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> {color: string, damage: string} +获得某只敌人的地图显伤,包括颜色 +例如:core.getDamageString('greenSlime', 0, 0, 'MT0') // 绿头怪的地图显伤 +enemy: 敌人id或敌人对象 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +floorId: 敌人所在的地图,可选 +返回值:damage: 表示伤害值或为'???',color: 形如'#RrGgBb' + +getDefDamage: fn(enemy: string|enemy, k?: number, x?: number, y?: number, floorId?: string) -> number +计算再加若干点防御能使某只敌人对主角的总伤害降低多少 +例如:core.getDefDamage('greenSlime', 10, 0, 0, 'MT0') // 再加10点防御能使绿头怪的伤害降低多少 +enemy: 敌人id或敌人对象 +k: 假设主角增加的防御力,可选,默认为1 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +floorId: 敌人所在的地图,可选 + +getEnemyInfo: fn(enemy: string|enemy, hero?: ?, x?: number, y?: number, floorId?: string) -> {hp: number, atk: number, def: number, money: number, exp: number, special: [number], point: number, guards: [?]} +获得怪物真实属性 +hero: 可选,此时的勇士属性 +此函数将会计算包括坚固、模仿、光环等若干效果,将同时被怪物手册和伤害计算调用 + +getEnemys: fn() +获得所有怪物原始数据的一个副本。 +请使用core.material.enemys获得当前各项怪物属性。 + +getEnemyValue: fn(enemy?: string|enemy, name: string, x?: number, y?: number, floorId?: string) +获得某个点上怪物的某个属性值 + +getSpecialColor: fn(enemy: string|enemy) -> [string] +获得某个怪物所有特殊属性的颜色 + +getSpecialFlag: fn(enemy: string|enemy) -> number +获得某个怪物所有特殊属性的额外标记。 + +例如,1为全图性技能,需要进行遍历全图(光环/支援等) + +getSpecialHint: fn(enemy: string|enemy, special: number) -> string +获得某种敌人的某种特殊属性的介绍 +例如:core.getSpecialHint('bat', 1) // '先攻:怪物首先攻击' +enemy: 敌人id或敌人对象,用于确定属性的具体数值,否则可选 +special: 属性编号,可以是该敌人没有的属性 +返回值:属性的介绍,以属性名加中文冒号开头 + +getSpecialText: fn(enemy: string|enemy) -> [string] +获得某种敌人的全部特殊属性名称 +例如:core.getSpecialText('greenSlime') // ['先攻', '3连击', '破甲', '反击'] +enemy: 敌人id或敌人对象,如core.material.enemys.greenSlime +返回值:字符串数组 + +getSpecials: fn() -> [[?]] +获得所有特殊属性的定义 + +hasEnemyLeft: fn(enemyId?: string, floorId?: string|[string]) -> bool +检查某些楼层是否还有漏打的(某种)敌人 +例如:core.hasEnemyLeft('greenSlime', ['sample0', 'sample1']) // 样板0层和1层是否有漏打的绿头怪 +enemyId: 敌人id,可选,null表示任意敌人 +floorId: 地图id或其数组,可选,不填为当前地图 +返回值:地图中是否还存在该种敌人 + +hasSpecial: fn(special: number|[number]|string|number, test: number) -> bool +判定某种特殊属性的有无 +例如:core.hasSpecial('greenSlime', 1) // 判定绿头怪有无先攻属性 +special: 敌人id或敌人对象或正整数数组或自然数 +test: 待检查的属性编号 + + +nextCriticals: fn(enemy: string|enemy, number?: number, x?: number, y?: number, floorId?: string) -> [[number]] +获得某只敌人接下来的若干个临界及其减伤,算法基于useLoop开关选择回合法或二分法 +例如:core.nextCriticals('greenSlime', 9, 0, 0, 'MT0') // 绿头怪接下来的9个临界 +enemy: 敌人id或敌人对象 +number: 要计算的临界数量,可选,默认为1 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +floorId: 敌人所在的地图,可选 +返回:两列的二维数组,每行表示一个临界及其减伤 +``` + +## events.js + +events.js将处理所有和事件相关的操作,主要分为五个部分: +- 游戏的开始和结束 +- 系统事件的处理 +- 自定义事件的处理 +- 点击状态栏图标所进行的操作 +- 一些具体事件的执行内容 + +```text +afterBattle: fn(enemyId?: string, x?: number, y?: number) +战斗结束后触发的事件 + +afterChangeFloor: fn(floorId?: string) +转换楼层结束的事件 + +afterGetItem: fn(id?: string, x?: number, y?: number, isGentleClick?: bool) +获得一个道具后的事件 + +afterOpenDoor: fn(doorId?: string, x?: number, y?: number) +开一个门后触发的事件 + +afterPushBox: fn() +推箱子后的事件 + +autoEventExecuted: fn(symbol?: string, value?: ?) -> bool +当前是否执行过某个自动事件 + +autoEventExecuting: fn(symbol?: string, value?: ?) -> bool +当前是否在执行某个自动事件 + +battle: fn(id: string, x?: number, y?: number, force?: bool, callback?: fn()) +战斗,如果填写了坐标就会删除该点的敌人并触发战后事件 +例如:core.battle('greenSlime'); // 和从天而降的绿头怪战斗(如果打得过) +id: 敌人id,必填 +x: 敌人的横坐标,可选 +y: 敌人的纵坐标,可选 +force: true表示强制战斗,可选 +callback: 回调函数,可选 + +beforeBattle: fn(enemyId?: string, x?: number, y?: number) -> bool +战斗前触发的事件;返回false代表不进行战斗 + +changeFloor: fn(floorId: string, stair?: string, heroLoc?: {x?: number, y?: number, direction?: string}, time?: number, callback?: fn()) +场景切换 +例如:core.changeFloor('MT0'); // 传送到主塔0层,主角坐标和朝向不变,黑屏时间取用户定义的值 +floorId: 传送的目标地图id,可以填':before'和':next'分别表示楼下或楼上 +stair: 传送的位置 +heroLoc: 传送的坐标;会覆盖stair +time: 传送的黑屏时间,单位为毫秒;不填为用户设置值 +callback: 传送的回调函数 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +changingFloor: fn(floorId?: string, heroLoc?: {x: number, y: number, direction: string}) +楼层转换中 + +checkAutoEvents: fn() +检测自动事件 + +checkLvUp: fn() +检查升级事件 + +clearTextBox: fn(code: number) +清除对话框 + +closeDoor: fn(x: number, y: number, id: string, callback?: fn()) +关门,目标点必须为空地 +例如:core.closeDoor(0, 0, 'yellowWall', core.jumpHero); // 在左上角关掉一堵黄墙,然后主角原地跳跃半秒 +x: 横坐标 +y: 纵坐标 +id: 门的id,也可以用三种基础墙 +callback: 门完全关上后的回调函数,可选 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +confirmRestart: fn() +询问是否需要重新开始 + +doAction: fn() +执行下一个事件指令,常作为回调 +例如:core.setCurtain([0,0,0,1], undefined, null, core.doAction); // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果 + +doEvent: fn(data?: ?, x?: number, y?: number, prefix?: string) +执行一个自定义事件 + +doSystemEvent: fn(type: string, data?: ?, callback?: fn()) +执行一个系统事件 + +eventMoveHero: fn(steps: [step], time?: number, callback?: fn()) +强制移动主角(包括后退),这个函数的作者已经看不懂这个函数了 +例如:core.eventMoveHero(['forward'], 125, core.jumpHero); // 主角强制前进一步,用时1/8秒,然后主角原地跳跃半秒 +steps: 步伐数组,注意后退时跟随者的行为会很难看 +time: 每步的用时,单位为毫秒。0或不填则取主角的移速,如果后者也不存在就取0.1秒 +callback: 移动完毕后的回调函数,可选 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +flyTo: fn(toId?: string, callback?: fn()) -> bool +飞往某一层 + +follow: fn(name: string) +跟随 +name: 要跟随的一个合法的4x4的行走图名称,需要在全塔属性注册 + +gameOver: fn(ending?: string, fromReplay?: bool, norank?: bool) +游戏结束 +例如:core.gameOver(); // 游戏失败 +ending: 结局名,省略表示失败 +fromReplay: true表示在播放录像,可选 +norank: true表示不计入榜单,可选 + +getCommonEvent: fn(name: string) -> [?] +获得一个公共事件 + +getItem: fn(id: string, num?: number, x?: number, y?: number, callback?: fn()) +获得道具并提示,如果填写了坐标就会删除该点的该道具 +例如:core.getItem('book'); // 获得敌人手册并提示 +id: 道具id,必填 +num: 获得的数量,不填视为1,填了就别填坐标了 +x: 道具的横坐标,可选 +y: 道具的纵坐标,可选 +callback: 回调函数,可选 + +getNextItem: fn(noRoute?: bool) +轻按获得面前的物品或周围唯一物品 +noRoute: 若为true则不计入录像 + +hasAsync: fn() -> bool +当前是否有未处理完毕的异步事件(不包含动画和音效) + +hasVisitedFloor: fn(floorId?: string) -> bool +是否到达过某个楼层 + +hideImage: fn(code: number, time?: number, callback?: fn()) +隐藏一张图片 +例如:core.hideImage(1, 1000, core.jumpHero); // 1秒内淡出1号图片,然后主角原地跳跃半秒 +code: 图片编号 +time: 淡出时间,单位为毫秒 +callback: 图片完全消失后的回调函数,可选 + +insertAction: fn(action: string|?|[?], x?: number, y?: number, callback?: fn(), addToLast?: bool) +插入一段事件;此项不可插入公共事件,请用 core.insertCommonEvent +例如:core.insertAction('一段文字'); // 插入一个显示文章 +action: 单个事件指令,或事件指令数组 +x: 新的当前点横坐标,可选 +y: 新的当前点纵坐标,可选 +callback: 新的回调函数,可选 +addToLast: 插入的位置,true表示插入到末尾,否则插入到开头 + +insertCommonEvent: fn(name?: string, args?: [?], x?: number, y?: number, callback?: fn(), addToLast?: bool) +插入一个公共事件 +例如:core.insertCommonEvent('加点事件', [3]); +name: 公共事件名;如果公共事件不存在则直接忽略 +args: 参数列表,为一个数组,将依次赋值给 flag:arg1, flag:arg2, ... +x: 新的当前点横坐标,可选 +y: 新的当前点纵坐标,可选 +callback: 新的回调函数,可选 +addToLast: 插入的位置,true表示插入到末尾,否则插入到开头 + +jumpHero: fn(ex?: number, ey?: number, time?: number, callback?: fn()) +主角跳跃,跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。 +例如:core.jumpHero(); // 主角原地跳跃半秒 +ex: 跳跃后的横坐标 +ey: 跳跃后的纵坐标 +time: 跳跃时长,单位为毫秒。不填视为半秒 +callback: 跳跃完毕后的回调函数,可选 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +load: fn(fromUserAction?: bool) +点击读档按钮时的打开操作 + +lose: fn(reason?: string) +游戏失败事件 + +moveEnemyOnPoint: fn(fromX: number, fromY: number, toX: number, toY: number, floorId?: string) +将某个点已经设置的敌人属性移动到其他点 + +moveImage: fn(code: number, to?: [number], opacityVal?: number, moveMode?: string, time?: number, callback?: fn()) +移动一张图片并/或改变其透明度 +例如:core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明 +code: 图片编号 +to: 新的左上角坐标,省略表示原地改变透明度 +opacityVal: 新的透明度,省略表示不变 +moveMode: 移动模式 +time: 移动用时,单位为毫秒。不填视为1秒 +callback: 图片移动完毕后的回调函数,可选 + +moveTextBox: fn(code: number, loc: [number], relative?: bool, moveMode?: string, time?: number, callback?: fn()) +移动对话框 + +onSki: fn(number?: number) -> bool +当前是否在冰上 + +openBook: fn(fromUserAction?: bool) +点击怪物手册时的打开操作 + +openDoor: fn(x: number, y: number, needKey?: bool, callback?: fn()) +开门(包括三种基础墙) +例如:core.openDoor(0, 0, true, core.jumpHero); // 打开左上角的门,需要钥匙,然后主角原地跳跃半秒 +x: 门的横坐标 +y: 门的纵坐标 +needKey: true表示需要钥匙,会导致机关门打不开 +callback: 门完全打开后或打不开时的回调函数,可选 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +openEquipbox: fn(fromUserAction?: bool) +点击装备栏时的打开操作 + +openKeyBoard: fn(fromUserAction?: bool) +点击虚拟键盘时的打开操作 + +openQuickShop: fn(fromUserAction?: bool) +点击快捷商店按钮时的打开操作 + +openSettings: fn(fromUserAction?: bool) +点击设置按钮时的操作 + +openToolbox: fn(fromUserAction?: bool) +点击工具栏时的打开操作 + +popEventLoc: fn() +将当前点坐标入栈 + +precompile: fn(data?: ?) +预编辑事件 + +pushBox: fn(data?: ?) +推箱子 + +pushEventLoc: fn(x?: number, y?: number, floorId?: string) -> bool +将当前点坐标入栈 + +recoverEvents: fn(data?: ?) +恢复一个事件 + +registerEvent: fn(type: string, func: fn(data: ?, x?: number, y?: number, prefix?: string)) +注册一个自定义事件 +type: 事件类型 +func: 事件的处理函数,可接受(data, x, y, prefix)参数 +data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀 + +registerSystemEvent: fn(type: string, func: fn(data?: ?, callback?: fn())) +注册一个系统事件 +type: 事件名 +func: 为事件的处理函数,可接受(data,callback)参数 + +resetEnemyOnPoint: fn(x: number, y: number, floorId?: string) +重置某个点的怪物属性 + +resetGame: fn(hero?: ?, hard?: ?, floorId?: string, maps?: ?, values?: ?) +初始化游戏 + +restart: fn() +重新开始游戏;此函数将回到标题页面 + +rotateImage: fn(code: number, center?: [number], angle?: number, moveMode?: string, time?: number, callback?: fn()) +旋转一张图片 +code: 图片编号 +center: 旋转中心像素坐标(以屏幕为基准);不填视为图片本身中心 +angle: 旋转角度;正数为顺时针,负数为逆时针 +moveMode: 旋转模式 +time: 旋转用时,单位为毫秒。不填视为1秒 +callback: 图片旋转完毕后的回调函数,可选 + +save: fn(fromUserAction?: bool) +点击存档按钮时的打开操作 + +scaleImage: fn(code: number, center?: [number], scale?: number, moveMode?: string, time?: number, callback?: fn()) +放缩一张图片 + +setEnemy: fn(id: string, name: string, value: ?, operator?: string, prefix?: string) +设置一项敌人属性并计入存档 +例如:core.setEnemy('greenSlime', 'def', 0); // 把绿头怪的防御设为0 +id: 敌人id +name: 属性的英文缩写 +value: 属性的新值,可选 +operator: 运算操作符,可选 +prefix: 独立开关前缀,一般不需要,下同 + +setEnemyOnPoint: fn(x: number, y: number, floorId?: string, name: string, value: ?, operator?: string, prefix?: string) +设置某个点的敌人属性。如果该点不是怪物,则忽略此函数。 +例如:core.setEnemyOnPoint(3, 5, null, 'atk', 100, '+='); // 仅将(3,5)点怪物的攻击力加100。 + +setEvents: fn(list?: [?], x?: number, y?: number, callback?: fn()) +直接设置事件列表 + +setFloorInfo: fn(name: string, values: ?, floorId?: string, prefix?: string) +设置一项楼层属性并刷新状态栏 +例如:core.setFloorInfo('ratio', 2, 'MT0'); // 把主塔0层的血瓶和宝石变为双倍效果 +name: 要修改的属性名 +values: 属性的新值。 +floorId: 楼层id,不填视为当前层 +prefix: 独立开关前缀,一般不需要 + +setGlobalAttribute: fn(name: string, value: string) +设置全塔属性 + +setGlobalFlag: fn(name: string, value: bool) +设置一个系统开关 +例如:core.setGlobalFlag('steelDoorWithoutKey', true); // 使全塔的所有铁门都不再需要钥匙就能打开 +name: 系统开关的英文名 +value: 开关的新值,您可以用!core.flags[name]简单地表示将此开关反转 + +setHeroIcon: fn(name: string, noDraw?: bool) +更改主角行走图 +例如:core.setHeroIcon('npc48.png', true); // 把主角从阳光变成样板0层左下角的小姐姐,但不立即刷新 +name: 新的行走图文件名,可以是全塔属性中映射前的中文名。映射后会被存入core.status.hero.image +noDraw: true表示不立即刷新(刷新会导致大地图下视野重置到以主角为中心) + +setNameMap: fn(name: string, value?: string) +设置文件别名 + +setTextAttribute: fn(data: ?) +设置剧情文本的属性 + +setValue: fn(name: string, operator: string, value: ?, prefix?: string) +数值操作 + +setVolume: fn(value: number, time?: number, callback?: fn()) +调节bgm的音量 +例如:core.setVolume(0, 100, core.jumpHero); // 0.1秒内淡出bgm,然后主角原地跳跃半秒 +value: 新的音量,为0或不大于1的正数。注意系统设置中是这个值的平方根的十倍 +time: 渐变用时,单位为毫秒。不填或小于100毫秒都视为0 +callback: 渐变完成后的回调函数,可选 + +showGif: fn(name?: string, x?: number, y?: number) +绘制一张动图或擦除所有动图 +例如:core.showGif(); // 擦除所有动图 +name: 动图文件名,可以是全塔属性中映射前的中文名 +x: 动图在视野中的左上角横坐标 +y: 动图在视野中的左上角纵坐标 + +showImage: fn(code: number, image: string|image, sloc?: [number], loc?: [number], opacityVal?: number, time?: number, callback?: fn()) +显示一张图片 +例如:core.showImage(1, core.material.images.images['winskin.png'], [0,0,128,128], [0,0,416,416], 0.5, 1000); // 裁剪winskin.png的最左边128×128px,放大到铺满整个视野,1秒内淡入到50%透明,编号为1 +code: 图片编号,为不大于50的正整数,加上100后就是对应画布层的z值,较大的会遮罩较小的,注意色调层的z值为125,UI层为140 +image: 图片文件名(可以是全塔属性中映射前的中文名)或图片对象(见上面的例子) +sloc: 一行且至多四列的数组,表示从原图裁剪的左上角坐标和宽高,可选 +loc: 一行且至多四列的数组,表示图片在视野中的左上角坐标和宽高,可选 +opacityVal: 不透明度,为小于1的正数。不填视为1 +time: 淡入时间,单位为毫秒。不填视为0 +callback: 图片完全显示出来后的回调函数,可选 + +startEvents: fn(list?: [?], x?: number, y?: number, callback?: fn()) +开始执行一系列自定义事件 + +startGame: fn(hard: string, seed: number, route: string, callback?: fn()) +开始新游戏 +例如:core.startGame('咸鱼乱撞', 0, ''); // 开始一局咸鱼乱撞难度的新游戏,随机种子为0 +hard: 难度名,会显示在左下角(横屏)或右下角(竖屏) +seed: 随机种子,相同的种子保证了录像的可重复性 +route: 经由base64压缩后的录像,用于从头开始的录像回放 +callback: 回调函数,可选 + +stopAsync: fn() +立刻停止所有正在进行的异步事件 + +trigger: fn(x?: number, y?: number, callback?: fn()) +触发(x,y)点的系统事件;会执行该点图块的script属性,同时支持战斗(会触发战后)、道具(会触发道具后)、楼层切换等等 +callback: 执行完毕的回调函数 +【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】 + +tryUseItem: fn(itemId: string) +尝试使用一个道具 +例如:core.tryUseItem('pickaxe'); // 尝试使用破墙镐 +itemId: 道具id,其中敌人手册、传送器和飞行器会被特殊处理 + +unfollow: fn(name?: string) +取消跟随 +name: 取消跟随的行走图,不填则取消全部跟随者 + +unregisterEvent: fn(type: string) +注销一个自定义事件 + +unregisterSystemEvent: fn(type: string) +注销一个系统事件 + +useFly: fn(fromUserAction?: bool) +点击楼层传送器时的打开操作 + +vibrate: fn(direction?: string, time?: number, speed?: number, power?: number, callback?: fn()) +视野抖动 +例如:core.vibrate(); // 视野左右抖动1秒 +direction: 抖动方向;可填 horizontal(左右),vertical(上下),diagonal1(左上右下),diagonal2(左下右上) +time: 抖动时长,单位为毫秒 +speed: 抖动速度 +power: 抖动幅度 +callback: 抖动平息后的回调函数,可选 + +visitFloor: fn(floorId?: string) +到达某楼层 + +win: fn(reason?: string, norank?: bool, noexit?: bool) +游戏获胜事件 +``` + +## icons.js + +图标信息 + +```text +getAllIconIds: fn() -> [string] +获得所有图标的ID + +getClsFromId: fn(id?: string) -> string +根据ID获得其图标类型 + +getIcons: fn() +获得所有图标类型 + +getTilesetOffset: fn(id?: string) -> {image: ?, x: number, y: number} +根据图块数字或ID获得所在的tileset和坐标信息 +``` + +## items.js + +道具相关的函数 + +```text +addItem: fn(itemId: string, itemNum?: number) +静默增减某种道具的持有量 不会更新游戏画面或是显示提示 +例如:core.addItem('yellowKey', -2) // 没收两把黄钥匙 +itemId: 道具id +itemNum: 增加量,负数表示没收 + +canEquip: fn(equipId: string, hint?: bool) -> bool +检查能否穿上某件装备 +例如:core.canEquip('sword5', true) // 主角可以装备神圣剑吗,如果不能会有提示 +equipId: 装备id +hint: 无法穿上时是否提示(比如是因为未持有还是别的什么原因) +返回值:true表示可以穿上,false表示无法穿上 + +canUseItem: fn(itemId: string) -> bool +检查能否使用某种道具 +例如:core.canUseItem('pickaxe') // 能否使用破墙镐 +itemId: 道具id +返回值:true表示可以使用 + +compareEquipment: fn(compareEquipId: string, beComparedEquipId: string) -> {value: ?, percentage: ?} +比较两件(类型可不同)装备的优劣 +例如:core.compareEquipment('sword5', 'shield5') // 比较神圣剑和神圣盾的优劣 +compareEquipId: 装备甲的id +beComparedEquipId: 装备乙的id +返回值:两装备的各属性差,甲减乙,0省略 + +getEquip: fn(equipType: number) -> string +检查主角某种类型的装备目前是什么 +例如:core.getEquip(1) // 主角目前装备了什么盾牌 +equipType: 装备类型,自然数 +返回值:装备id,null表示未穿戴 + +getEquipTypeById: fn(equipId: string) -> number +判定某件装备的类型 +例如:core.getEquipTypeById('shield5') // 1(盾牌) +equipId: 装备id +返回值:类型编号,自然数 + +getEquipTypeByName: fn(name?: string) +根据类型获得一个可用的装备孔 + +getItemEffect: fn(itemId: string, itemNum?: number) +即捡即用类的道具获得时的效果 +例如:core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果 +itemId: 道具id +itemNum: 道具数量,可选,默认为1 + +getItemEffectTip: fn(itemId: string) -> string +即捡即用类的道具获得时的额外提示 +例如:core.getItemEffectTip(redPotion) // (获得 红血瓶)',生命+100' +itemId: 道具id +返回值:图块属性itemEffectTip的内容 + +getItems: fn() +获得所有道具 + +hasEquip: fn(itemId: string) -> bool +检查主角是否穿戴着某件装备 +例如:core.hasEquip('sword5') // 主角是否装备了神圣剑 +itemId: 装备id +返回值:true表示已装备 + +hasItem: fn(itemId: string) -> bool +检查主角是否持有某种道具(不包括已穿戴的装备) +例如:core.hasItem('yellowKey') // 主角是否持有黄钥匙 +itemId: 道具id +返回值:true表示持有 + +itemCount: fn(itemId: string) -> number +统计某种道具的持有量 +例如:core.itemCount('yellowKey') // 持有多少把黄钥匙 +itemId: 道具id +返回值:该种道具的持有量,不包括已穿戴的装备 + +loadEquip: fn(equipId: string, callback?: fn()) +尝试穿上某件背包里面的装备并提示 +例如:core.loadEquip('sword5') // 尝试装备上背包里面的神圣剑,无回调 +equipId: 装备id +callback: 穿戴成功或失败后的回调函数 + +quickLoadEquip: fn(index: number) +快速换装 +例如:core.quickLoadEquip(1) // 快速换上1号套装 +index: 套装编号,自然数 + +quickSaveEquip: fn(index: number) +保存当前套装 +例如:core.quickSaveEquip(1) // 将当前套装保存为1号套装 +index: 套装编号,自然数 + +removeItem: fn(itemId?: string, itemNum?: number) +删除某个物品 + +setEquip: fn(equipId: string, valueType: string, name: string, value: ?, operator?: string, prefix?: string) +设置某个装备的属性并计入存档 +例如:core.setEquip('sword1', 'value', 'atk', 300, '+='); // 设置铁剑的攻击力数值再加300 +equipId: 装备id +valueType: 增幅类型,只能是value(数值)或percentage(百分比) +name: 要修改的属性名称,如atk +value: 要修改到的属性数值 +operator: 操作符,可选,如+=表示在原始值上增加 +prefix: 独立开关前缀,一般不需要 + +setItem: fn(itemId: string, itemNum?: number) +设置某种道具的持有量 +例如:core.setItem('yellowKey', 3) // 设置黄钥匙为3把 +itemId: 道具id +itemNum: 新的持有量,可选,自然数,默认为0 + +unloadEquip: fn(equipType: number, callback?: fn()) +脱下某个类型的装备 +例如:core.unloadEquip(1) // 卸下盾牌,无回调 +equipType: 装备类型编号,自然数 +callback: 卸下装备后的回调函数 + +useItem: fn(itemId: string, noRoute?: bool, callback?: fn()) +使用一个道具 +例如:core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调 +itemId: 道具id +noRoute: 是否不计入录像,快捷键使用的请填true,否则可省略 +callback: 道具使用完毕或使用失败后的回调函数 +``` + +## loader.js + +资源加载相关的函数 + +```text +freeBgm: fn(name: string) +释放一个bgm的缓存 + +loadBgm: fn(name: string) +加载一个bgm + +loadImage: fn(dir: name, imgName: name, callback?: fn()) +加载某一张图片 + +loadImages: fn(dir: string, names: [string], toSave: ?, callback?: fn()) +加载一系列图片 + +loadImagesFromZip: fn(url: string, names: [string], toSave?: ?, onprogress?: ?, onfinished?: ?) +从zip中加载一系列图片 + +loadOneMusic: fn(name: string) +加载一个音乐或音效 + +loadOneSound: fn(name: string) +加载一个音效 +``` + +## maps.js + +负责一切和地图相关的处理内容,包括如下几个方面: +- 地图的初始化,保存和读取,地图数组的生成 +- 是否可移动或瞬间移动的判定 +- 地图的绘制 +- 获得某个点的图块信息 +- 启用和禁用图块,改变图块 +- 移动/跳跃图块,淡入淡出图块 +- 全局动画控制,动画的绘制 + +```text +addGlobalAnimate: fn(block?: block) +添加一个全局动画 + +animateBlock: fn(loc?: [number]|[[number]], type?: string|number, time?: number, callback?: fn()) +显示/隐藏某个块时的动画效果 + +animateSetBlock: fn(number: number|string, x: number, y: number, floorId?: string, time?: number, callback?: fn()) +动画形式转变某点图块 + +animateSetBlocks: fn(number: number|string, locs: [?], floorId?: string, time?: number, callback?: fn()) +动画形式同时转变若干点图块 + +automaticRoute: fn(destX: number, destY: number) -> [{x: number, y: number, direction: string}] +自动寻路 +例如:core.automaticRoute(0, 0); // 自动寻路到地图左上角 +destX: 目标点的横坐标 +destY: 目标点的纵坐标 +返回值:每步走完后主角的loc属性组成的一维数组 + +canMoveDirectly: fn(destX: number, destY: number) -> number +能否瞬移到某点,并求出节约的步数。 +例如:core.canMoveDirectly(0, 0); // 能否瞬移到地图左上角 +destX: 目标点的横坐标 +destY: 目标点的纵坐标 +返回值:正数表示节约的步数,-1表示不可瞬移 + +canMoveDirectlyArray: fn(locs?: [[number]]) +获得某些点可否通行的信息 + +canMoveHero: fn(x?: number, y?: number, direction?: string, floorId?: string) -> bool +单点单朝向的可通行性判定;受各图层cannotInOut、起点cannotMove和canGoDeadZone影响,不受canPass和noPass影响 +x: 起点横坐标,不填视为主角当前的 +y: 起点纵坐标,不填视为主角当前的 +direction: 移动的方向,不填视为主角面对的方向 +floorId: 地图id,不填视为当前地图 + +compressMap: fn(mapArr: [[number]], floorId?: string) -> [[number]] +压缩地图 + +decompressMap: fn(mapArr: [[number]], floorId?: string) -> [[number]] +解压缩地图 + +drawAnimate: fn(name: string, x: number, y: number, alignWindow: bool, callback?: fn()) -> number +播放动画,注意即使指定了主角的坐标也不会跟随主角移动,如有需要请使用core.drawHeroAnimate(name, callback)函数 +例如:core.drawAnimate('attack', core.nextX(), core.nextY(), false, core.vibrate); // 在主角面前一格播放普攻动画,动画停止后视野左右抖动1秒 +name: 动画文件名,不含后缀 +x: 横坐标 +y: 纵坐标 +alignWindow: 是否是相对窗口的坐标 +callback: 动画停止后的回调函数,可选 +返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数) + +drawBg: fn(floorId?: string, ctx?: CanvasRenderingContext2D) +绘制背景层(含贴图,其与背景层矩阵的绘制顺序可通过复写此函数来改变) +例如:core.drawBg(); // 绘制当前地图的背景层 +floorId: 地图id,不填视为当前地图 +ctx: 某画布的ctx,用于绘制缩略图,一般不需要 + +drawBlock: fn(block?: block, animate?: number) +绘制一个图块 + +drawBoxAnimate: fn() +绘制UI层的box动画 + +drawEvents: fn(floorId?: string, blocks?: [block], ctx?: CanvasRenderingContext2D) +绘制事件层 +例如:core.drawEvents(); // 绘制当前地图的事件层 +floorId: 地图id,不填视为当前地图 +blocks: 一般不需要 +ctx: 某画布的ctx,用于绘制缩略图,一般不需要 + +drawFg: fn(floorId?: string, ctx?: CanvasRenderingContext2D) +绘制前景层(含贴图,其与前景层矩阵的绘制顺序可通过复写此函数来改变) +例如:core.drawFg(); // 绘制当前地图的前景层 +floorId: 地图id,不填视为当前地图 +ctx: 某画布的ctx,用于绘制缩略图,一般不需要 + +drawHeroAnimate: fn(name: string, callback?: fn()) -> number +播放跟随勇士的动画 +name: 动画名 +callback: 动画停止后的回调函数,可选 +返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数) + +drawMap: fn(floorId?: string) +地图重绘 +例如:core.drawMap(); // 重绘当前地图,常用于更改贴图或改变自动元件后的刷新 +floorId: 地图id,可省略表示当前楼层 +callback: 重绘完毕后的回调函数,可选 + +drawThumbnail: fn(floorId?: string, blocks?: [block], options?: ?) +绘制缩略图 +例如:core.drawThumbnail(); // 绘制当前地图的缩略图 +floorId: 地图id,不填视为当前地图 +blocks: 一般不需要 +options: 绘制信息,可选。可以增绘主角位置和朝向、采用不同于游戏中的主角行走图、增绘显伤、提供flags用于存读档,同时包含要绘制到的画布名或画布的ctx或还有其他信息,如起绘坐标、绘制大小、是否绘制全图、截取中心 + +enemyExists: fn(x: number, y: number, id?: string, floorId?: string) -> bool +某个点是否存在(指定的)怪物 + +extractBlocks: fn(map?: ?) +根据需求解析出blocks + +extractBlocksForUI: fn(map?: ?, flags?: ?) +根据需求为UI解析出blocks + +generateGroundPattern: fn(floorId?: string) +生成groundPattern + +generateMovableArray: fn(floorId?: string) -> [[[string]]] +可通行性判定 +例如:core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动 +floorId: 地图id,不填视为当前地图 +返回值:从各点可移动方向的三维数组 + +getBgMapArray: fn(floorId?: string, noCache?: bool) -> [[number]] +生成背景层矩阵 +例如:core.getBgMapArray('MT0'); // 生成主塔0层的背景层矩阵,使用缓存 +floorId: 地图id,不填视为当前地图 +noCache: 可选,true表示不使用缓存 +返回值:背景层矩阵,注意对其阵元的访问是[y][x] + +getBgNumber: fn(x?: number, y?: number, floorId?: string, noCache?: bool) -> number +判定某点的背景层的数字 +例如:core.getBgNumber(); // 判断主角脚下的背景层图块的数字 +x: 横坐标,不填为勇士坐标 +y: 纵坐标,不填为勇士坐标 +floorId: 地图id,不填视为当前地图 +noCache: 可选,true表示不使用缓存而强制重算 + +getBlock: fn(x: number, y: number, floorId?: string, showDisable?: bool) -> block +获得某个点的block + +getBlockById: fn(id: string) -> block +根据ID获得图块 + +getBlockByNumber: fn(number: number) -> block +根据数字获得图块 + +getBlockCls: fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string +判定某个点的图块类型 +例如:if(core.getBlockCls(x1, y1) != 'enemys' && core.getBlockCls(x2, y2) != 'enemy48') core.openDoor(x3, y3); // 另一个简单的机关门事件,打败或炸掉这一对不同身高的敌人就开门 +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 +showDisable: 隐藏点是否不返回null,true表示不返回null +返回值:图块类型,即“地形、四帧动画、矮敌人、高敌人、道具、矮npc、高npc、自动元件、额外地形”之一 + +getBlockFilter: fn(x: number, y: number, floorId?: string, showDisable?: bool) -> ? +获得某个点的图块特效 + +getBlockId: fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string +判定某个点的图块id +例如:if(core.getBlockId(x1, y1) != 'greenSlime' && core.getBlockId(x2, y2) != 'redSlime') core.openDoor(x3, y3); // 一个简单的机关门事件,打败或炸掉这一对绿头怪和红头怪就开门 +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 +showDisable: 隐藏点是否不返回null,true表示不返回null +返回值:图块id,该点无图块则返回null + +getBlockInfo: fn(block?: number|string|block) -> blockInfo +获得某个图块或素材的信息,包括ID,cls,图片,坐标,faceIds等等 + +getBlockNumber: fn(x: number, y: number, floorId?: string, showDisable?: bool) -> number +判定某个点的图块数字 +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 +showDisable: 隐藏点是否不返回null,true表示不返回null +返回值:图块数字,该点无图块则返回null + +getBlockOpacity: fn(x: number, y: number, floorId?: string, showDisable?: bool) -> number +判定某个点的不透明度。如果该点无图块则返回null。 + +getFaceDownId: fn(block?: string|number|block) -> string +获得某个图块对应行走图朝向向下的那一项的id;如果不存在行走图绑定则返回自身id。 + +getFgMapArray: fn(floorId?: string, noCache?: bool) -> [[number]] +生成前景层矩阵 +例如:core.getFgMapArray('MT0'); // 生成主塔0层的前景层矩阵,使用缓存 +floorId: 地图id,不填视为当前地图 +noCache: 可选,true表示不使用缓存 +返回值:前景层矩阵,注意对其阵元的访问是[y][x] + +getFgNumber: fn(x: number, y: number, floorId?: string, noCache?: bool) -> number +判定某点的前景层的数字 +例如:core.getFgNumber(); // 判断主角脚下的前景层图块的数字 +x: 横坐标,不填为勇士坐标 +y: 纵坐标,不填为勇士坐标floorId: 地图id,不填视为当前地图 +noCache: 可选,true表示不使用缓存而强制重算 + +getIdOfThis: fn(id?: string) -> string +获得当前事件点的ID + +getMapArray: fn(floorId?: string, noCache?: bool) -> [[number]] +生成事件层矩阵 +例如:core.getMapArray('MT0'); // 生成主塔0层的事件层矩阵,隐藏的图块视为0 +floorId: 地图id,不填视为当前地图 +showDisable: 可选,true表示隐藏的图块也会被表示出来 +返回值:事件层矩阵,注意对其阵元的访问是[y][x] + +getMapBlocksObj: fn(floorId?: string, noCache?: bool) +以x,y的形式返回每个点的事件 + +getMapNumber: fn(x: number, y: number, floorId?: string, noCache?: bool) -> number +获得事件层某个点的数字 + +getNumberById: fn(id: string) -> number +根据图块id得到数字(地图矩阵中的值) +例如:core.getNumberById('yellowWall'); // 1 +id: 图块id +返回值:图块的数字,定义在project\maps.js(请注意和project\icons.js中的“图块索引”相区分!) + +getPlayingAnimates: fn(name?: string) -> [number] +获得当前正在播放的所有(指定)动画的id列表 +name: 动画名;不填代表返回全部正在播放的动画 +返回值: 一个数组,每一项为一个正在播放的动画;可用core.stopAnimate停止播放。 + +hideBgFgMap: fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn()) +隐藏前景/背景地图 + +hideBlock: fn(x: number, y: number, floorId?: string) +隐藏一个图块,对应于「隐藏事件」且不删除 +例如:core.hideBlock(0, 0); // 隐藏地图左上角的图块 +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 + +hideBlockByIndex: fn(index?: number, floorId?: string) +根据图块的索引来隐藏图块 + +hideBlockByIndexes: fn(indexes?: [number], floorId?: string) +一次性隐藏多个block + +hideFloorImage: fn(loc?: [number]|[[number]], floorId?: string, callback?: fn()) +隐藏一个楼层贴图 + +initBlock: fn(x: number, y: number, id: string|number, addInfo?: bool, eventFloor?: ?) -> block +初始化一个图块 + +isMapBlockDisabled: fn(floorId?: string, x?: number, y?: number, flags?: ?) -> bool +某个点图块是否被强制启用或禁用 + +jumpBlock: fn(sx: number, sy: number, ex: number, ey: number, time?: number, keep?: bool, callback?: fn()) +跳跃图块;从V2.7开始不再有音效 +例如:core.jumpBlock(0, 0, 0, 0); // 令地图左上角的图块原地跳跃半秒,再花半秒淡出 +sx: 起点的横坐标 +sy: 起点的纵坐标 +ex: 终点的横坐标 +ey: 终点的纵坐标 +time: 单步和淡出用时,单位为毫秒。不填视为半秒 +keep: 是否不淡出,true表示不淡出 +callback: 落地或淡出后的回调函数,可选 + +loadFloor: fn(floorId?: string, map?: ?) +从文件或存档中加载某个楼层 + +loadMap: fn(data?: ?, floorId?: string, flags?: ?) +将存档中的地图信息重新读取出来 + +moveBlock: fn(x: number, y: number, steps: [string], time?: number, keep?: bool, callback?: fn()) +移动图块 +例如:core.moveBlock(0, 0, ['down']); // 令地图左上角的图块下移一格 +x: 起点的横坐标 +y: 起点的纵坐标 +steps: 步伐数组 +time: 单步和淡出用时,单位为毫秒。不填视为半秒 +keep: 是否不淡出,true表示不淡出 +callback: 移动或淡出后的回调函数,可选 + +nearStair: fn() -> bool +当前位置是否在楼梯边;在楼传平面塔模式下对箭头也有效 + +noPass: fn(x: number, y: number, floorId?: string) -> bool +判定某个点是否不可被踏入(不基于主角生命值和图块cannotIn属性) +例如:core.noPass(0, 0); // 判断地图左上角能否被踏入 +x: 目标点的横坐标 +y: 目标点的纵坐标 +floorId: 目标点所在的地图id,不填视为当前地图 +返回值:true表示可踏入 + +npcExists: fn(x: number, y: number, floorId?: string) -> bool +某个点是否存在NPC + +removeBlock: fn(x: number, y: number, floorId?: string) +删除一个图块,对应于「隐藏事件」并同时删除 +例如:core.removeBlock(0, 0); // 尝试删除地图左上角的图块 +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 + +removeBlockByIndex: fn(index: number, floorId?: string) +根据block的索引删除该块 + +removeBlockByIndexes: fn(indexes?: [number], floorId?: string) +一次性删除多个block + +removeGlobalAnimate: fn(x?: number, y?: number, name?: string) +删除一个或所有全局动画 + +replaceBlock: fn(fromNumber: number, toNumber: number, floorId?: string|[string]) +批量替换图块 +例如:core.replaceBlock(21, 22, core.floorIds); // 把游戏中地上当前所有的黄钥匙都变成蓝钥匙 +fromNumber: 旧图块的数字 +toNumber: 新图块的数字 +floorId: 地图id或其数组,不填视为当前地图 + +resetMap: fn(floorId?: string|[string]) +重置地图 + +resizeMap: fn(floorId?: string) +更改地图画布的尺寸 + +saveMap: fn(floorId?: string) +将当前地图重新变成数字,以便于存档 + +searchBlock: fn(id: string, floorId?: string|[string], showDisable?: bool) -> [{floorId: string, index: number, x: number, y: number, block: block}] +搜索图块, 支持通配符和正则表达式 +例如:core.searchBlock('*Door'); // 搜索当前地图的所有门 +id: 图块id,支持星号表示任意多个(0个起)字符 +floorId: 地图id或数组,不填视为当前地图 +showDisable: 隐藏点是否计入,true表示计入 +返回值:一个详尽的数组,一般只用到其长度 + +searchBlockWithFilter: fn(blockFilter: fn(block: block) -> bool, floorId?: string|[string], showDisable?: bool): [{floorId: string, index: number, x: number, y: number, block: block}] +根据给定的筛选函数搜索全部满足条件的图块 +例如:core.searchBlockWithFilter(function (block) { return block.event.id.endsWith('Door'); }); // 搜索当前地图的所有门 +blockFilter: 筛选函数,可接受block输入,应当返回一个boolean值 +floorId: 地图id或数组,不填视为当前地图 +showDisable: 隐藏点是否计入,true表示计入 +返回值:一个详尽的数组 + +setBgFgBlock: fn(name: string, number: number|string, x: number, y: number, floorId?: string) +转变图层块 +例如:core.setBgFgBlock('bg', 167, 6, 6); // 把当前地图背景层的中心块改为滑冰 +name: 背景还是前景 +number: 新图层块的数字(也支持纯数字字符串如'1')或id +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 + +setBlock: fn(number: number|string, x: number, y: number, floorId?: string) +转变图块 +例如:core.setBlock(1, 0, 0); // 把地图左上角变成黄墙 +number: 新图块的数字(也支持纯数字字符串如'1')或id +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 + +setBlockFilter: fn(filter?: ?, x?: number, y?: number, floorId?: string) +设置某个点图块的特效 + +setBlockOpacity: fn(opacity?: number, x?: number, y?: number, floorId?: string) +设置某个点图块的不透明度 + +setMapBlockDisabled: fn(floorId?: string, x?: number, y?: number, disabled?: bool) +设置某个点图块的强制启用或禁用状态 + +showBgFgMap: fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn()) +显示前景/背景地图 + +showBlock: fn(x: number, y: number, floorId?: string) +显示(隐藏或显示的)图块,此函数将被“显示事件”指令和勾选了“不消失”的“移动/跳跃事件”指令(如阻击怪)的终点调用 +例如:core.showBlock(0, 0); // 显示地图左上角的图块 +x: 横坐标 +y: 纵坐标 +floorId: 地图id,不填视为当前地图 + +showFloorImage: fn(loc?: [number]|[[number]], floorId?: string, callback?: fn()) +显示一个楼层贴图 + +stairExists: fn(x: number, y: number, floorId?: string) -> bool +某个点是否存在楼梯 + +stopAnimate: fn(id?: number, doCallback?: bool) +立刻停止一个动画播放 +id: 播放动画的编号,即drawAnimate或drawHeroAnimate的返回值;不填视为停止所有动画 +doCallback: 是否执行该动画的回调函数 + +terrainExists: fn(x: number, y: number, id?: string, floorId?: string) -> bool +某个点是否存在(指定的)地形 + +turnBlock: fn(direction?: string, x?: number, y?: number, floorId?: string) +事件转向 +``` + +## ui.js + +负责一切UI界面的绘制。主要包括三个部分: +- 设置某个画布的属性与在某个画布上绘制的相关API +- 具体的某个UI界面的绘制 +- 动态创建画布相关的API + +```text +calWidth: fn(name: string|CanvasRenderingContext2D, text: string, font?: string) -> number +计算某段文字的宽度 +参考资料:https://www.w3school.com.cn/tags/canvas_measuretext.asp + +clearMap: fn(name: string|CanvasRenderingContext2D, x?: number, y?: number, width?: number, height?: number) +清空某个画布图层 +name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。 +如果name也可以是'all',若为all则为清空所有系统画布。 +参考资料:https://www.w3school.com.cn/tags/canvas_clearrect.asp + +clearUI: fn() +清空UI层内容 + +clearUIEventSelector: fn(codes?: number|[number]) +清除若干个自绘的选择光标 +codes: 清除的光标编号;可以是单个编号或编号数组;不填则清除所有光标 + +closePanel: fn() +结束一切事件和绘制,关闭UI窗口,返回游戏进程 + +createCanvas: fn(name: string, x: number, y: number, width: number, height: number, zIndex: number) -> CanvasRenderingContext2D +动态创建一个画布。 +name: 要创建的画布名,如果已存在则会直接取用当前存在的。 +x,y: 创建的画布相对窗口左上角的像素坐标 +width,height: 创建的长宽。 +zIndex: 创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。 +返回创建的画布的context,也可以通过core.dymCanvas[name]调用。 + +deleteAllCanvas: fn() +清空所有的自定义画布 + +deleteCanvas: fn(name: string|fn(name: string) -> bool) +删除一个自定义画布 +name: 画布名;也可以传入一个filter对画布名进行筛选。 + +drawArrow: fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number) +在某个canvas上绘制一个箭头 + +drawBackground: fn(left: string, top: string, right: string, bottom: string, posInfo?: {px: number, py: number, direction: string}) +绘制一个背景图,可绘制winskin或纯色背景;支持小箭头绘制 + +drawBook: fn(index?: ?) +绘制怪物手册 + +drawChoices: fn(content?: string, choices?: [?], width?: number, ctx?: string|CanvasRenderingContext2D) +绘制一个选项界面 + +drawConfirmBox: fn(text: string, yesCallback?: fn(), noCallback?: fn()) +绘制一个确认框 +此项会打断事件流,如需不打断版本的请使用core.myconfirm() +text: 要绘制的内容,支持 ${} 语法 +yesCallback: 点击确认后的回调 +noCallback: 点击取消后的回调 + +drawFly: fn(page?: ?) +绘制楼层传送器 + +drawIcon: fn(name: string|CanvasRenderingContext2D, id: string, x: number, y: number, w?: number, h?: number, frame?: number) +在某个canvas上绘制一个图标 + +drawImage: fn(name: string|CanvasRenderingContext2D, image: string|image, x: number, y: number, w?: number, h?: number, x1?: number, y1?: number, w1?: number, h1?: number, angle?: number) +在一个画布上绘制图片 +后面的8个坐标参数与canvas的drawImage的八个参数完全相同。 +name: 可以是系统画布之一,也可以是任意自定义动态创建的画布名 画布名称或者画布的context +image: 要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取;支持加':x',':y',':o'翻转),图片本身,或者一个画布。 +angle:旋转角度 +参考资料:http://www.w3school.com.cn/html5/canvas_drawimage.asp + +drawLine: fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number) +在某个canvas上绘制一条线 +参考资料:https://www.w3school.com.cn/tags/canvas_lineto.asp + +drawPagination: fn(page?: ?, totalPage?: ?, y?: number) +绘制分页 + +drawScrollText: fn(content: string, time: number, lineHeight?: number, callback?: fn()) +绘制滚动字幕 + +drawStatusBar: fn() +绘制状态栏 + +drawText: fn(contents: string, callback?: fn()) +地图中间绘制一段文字 + +drawTextBox: fn(content: string, showAll?: bool) +绘制一个对话框 + +drawTextContent: fn(ctx: string|CanvasRenderingContext2D, content: string, config: ?) +绘制一段文字到某个画布上面 +ctx: 要绘制到的画布 +content: 要绘制的内容;转义字符不允许保留 \t, \b 和 \f +config: 绘制配置项,目前暂时包含如下内容(均为可选) +left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右 +fontSize:字体大小;lineHeight:行高;time:打字机间隔;font:字体名 +返回值:绘制信息 + +drawTip: fn(text: string, id?: string, frame?: number) +左上角绘制一段提示 +text: 要提示的字符串,支持${}语法 +id: 要绘制的图标ID +frame: 要绘制该图标的第几帧 + +drawUIEventSelector: fn(code: number, background: string, x: number, y: number, w: number, h: number, z?: number) +自绘一个闪烁的选择光标 +code: 选择光标的编号,必填 +background: 要绘制的光标背景,必须是一个合法的WindowSkin +x, y, w, h: 绘制的坐标和长宽 +z: 可选,光标的的z值 + +drawWaiting: fn(text: string) +绘制等待界面 + +drawWindowSkin: fn(background: string, ctx: string|CanvasRenderingContext2D, x: number, y: number, w: string, h: string, direction?: string, px?: number, py?: number) +绘制WindowSkin + +fillArc: fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string) +在某个canvas上绘制一个扇形 +参考资料:https://www.w3school.com.cn/tags/canvas_arc.asp + +fillBoldText: fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, strokeStyle?: string, font?: string, maxWidth?: number) +在某个画布上绘制一个描边文字 +text: 要绘制的文本 +style: 绘制的样式 +strokeStyle: 要绘制的描边颜色 +font: 绘制的字体 +maxWidth: 最大宽度,超过此宽度会自动放缩 + +fillCircle: fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, style?: string) +在某个canvas上绘制一个圆 +参考资料:https://www.w3school.com.cn/tags/canvas_arc.asp + +fillEllipse: fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string) +在某个canvas上绘制一个椭圆 + +fillPolygon: fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string) +在某个canvas上绘制一个多边形 + +fillRect: fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, angle?: number) +绘制一个矩形。 +x,y: 绘制的坐标 +width,height: 绘制的长宽 +style: 绘制的样式 +angle: 旋转的角度,弧度制,如Math.PI/2代表90度 +参考资料:https://www.w3school.com.cn/tags/canvas_fillrect.asp + +fillRoundRect: fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, angle?: number) +在某个canvas上绘制一个圆角矩形 + +fillText: fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, font?: string, maxWidth?: number) +在某个画布上绘制一段文字 +text: 要绘制的文本 +style: 绘制的样式 +font: 绘制的字体 +maxWidth: 最大宽度,超过此宽度会自动放缩 +参考资料:https://www.w3school.com.cn/tags/canvas_filltext.asp + +getContextByName: fn(canvas: string|CanvasRenderingContext2D) -> CanvasRenderingContext2D +根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。 +也可以传画布的context自身,则返回自己。 + +getTextContentHeight: fn(content: string, config?: ?) +获得某段文字的预计绘制高度;参数说明详见 drawTextContent + +getToolboxItems: fn(cls: string) -> [string] +获得所有应该在道具栏显示的某个类型道具 + +loadCanvas: fn(name: string|CanvasRenderingContext2D) +加载某个canvas状态 + +relocateCanvas: fn(name: string, x: number, y: number, useDelta: bool) +重新定位一个自定义画布 + +resizeCanvas: fn(name: string, x: number, y: number) +重新设置一个自定义画布的大小 + +rotateCanvas: fn(name: string, angle: number, centerX?: number, centerY?: number) +设置一个自定义画布的旋转角度 +centerX, centerY: 旋转中心(以屏幕像素为基准);不填视为图片正中心。 + +saveCanvas: fn(name: string|CanvasRenderingContext2D) +保存某个canvas状态 + +setAlpha: fn(name: string|CanvasRenderingContext2D, alpha: number) -> number +设置某个canvas接下来绘制的不透明度;不会影响已经绘制的内容 +返回设置之前画布的不透明度。 +如果需要修改画布本身的不透明度请使用setOpacity +参考资料:https://www.w3school.com.cn/tags/canvas_globalalpha.asp + +setFillStyle: fn(name: string|CanvasRenderingContext2D, style: string) +设置某个canvas的绘制属性(如颜色等) +参考资料:https://www.w3school.com.cn/tags/canvas_fillstyle.asp + +setFilter: fn(name: string|CanvasRenderingContext2D, filter: any) +设置某个canvas接下来绘制的filter + +setFont: fn(name: string|CanvasRenderingContext2D, font: string) +设置某个canvas的文字字体 +参考资料:https://www.w3school.com.cn/tags/canvas_font.asp + +setFontForMaxWidth: fn(name: string|CanvasRenderingContext2D, text: string, maxWidth: number, font?: ?) -> string +根据最大宽度自动缩小字体 + +setLineWidth: fn(name: string|CanvasRenderingContext2D, lineWidth: number) +设置某个canvas的线宽度 +参考资料:https://www.w3school.com.cn/tags/canvas_linewidth.asp + +setOpacity: fn(name: string|CanvasRenderingContext2D, opacity: number) +设置某个canvas整体的透明度;此函数直接改变画布本身,对已经绘制的内容也生效 +如果仅想对接下来的绘制生效请使用setAlpha + +setStrokeStyle: fn(name: string|CanvasRenderingContext2D, style: string) +设置某个canvas边框属性 +参考资料:https://www.w3school.com.cn/tags/canvas_strokestyle.asp + +setTextAlign: fn(name: string|CanvasRenderingContext2D, align: string) +设置某个canvas的对齐 +参考资料:https://www.w3school.com.cn/tags/canvas_textalign.asp + +setTextBaseline: fn(name: string|CanvasRenderingContext2D, baseline: string) +设置某个canvas的基准线 +baseline: 可为alphabetic, top, hanging, middle, ideographic, bottom +参考资料:https://www.w3school.com.cn/tags/canvas_textbaseline.asp + +splitLines: fn(name: string|CanvasRenderingContext2D, text: string, maxWidth?: number, font?: string) +字符串自动换行的分割 + +strokeArc: fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string, lineWidth?: number) +在某个canvas上绘制一段弧 +参考资料:https://www.w3school.com.cn/tags/canvas_arc.asp + +strokeCircle: fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: ?, style?: string, lineWidth?: number) +在某个canvas上绘制一个圆的边框 +参考资料:https://www.w3school.com.cn/tags/canvas_arc.asp + +strokeEllipse: fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string, lineWidth?: number) +在某个canvas上绘制一个椭圆的边框 + +strokePolygon: fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string, lineWidth?: number) +在某个canvas上绘制一个多边形的边框 + +strokeRect: fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, lineWidth?: number, angle?: number) +绘制一个矩形的边框 +style: 绘制的样式 +lineWidth: 线宽 +angle: 旋转角度,弧度制,如Math.PI/2为90度 +参考资料:https://www.w3school.com.cn/tags/canvas_strokerect.asp + +strokeRoundRect: fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, lineWidth?: number, angle?: number) +在某个canvas上绘制一个圆角矩形的边框 + +textImage: fn(content: string, lineHeight?: number) -> image +文本图片化 +``` + +## utils.js + +工具函数库,里面有各个样板中使用到的工具函数。 + +```text +applyEasing: fn(mode?: string) -> fn(t: number) -> number +获得变速移动曲线 + +arrayToRGB: fn(color: [number]) -> string +颜色数组转字符串 +例如:core.arrayToRGB([102, 204, 255]); // "#66ccff" +color: 一行三列的数组,必须为不大于255的自然数 +返回值:该颜色的#xxxxxx字符串表示 + +arrayToRGBA: fn(color: [number]) -> string +颜色数组转字符串 +例如:core.arrayToRGBA([102, 204, 255, 0.3]); // "rgba(102,204,255,0.3)" +color: 一行三列或一行四列的数组,前三个元素必须为不大于255的自然数。第四个元素(如果有)必须为0或不大于1的数字,第四个元素不填视为1 +返回值:该颜色的rgba(...)字符串表示 + +calValue: fn(value: string, prefix?: string) +计算一个表达式的值,支持status:xxx等的计算。 +例如:core.calValue('status:hp + status:def'); // 计算主角的生命值加防御力 +value: 待求值的表达式 +prefix: 独立开关前缀,一般可省略 +返回值:求出的值 + +clamp: fn(x: number, a: number, b: number) -> number +将x限定在[a,b]区间内,注意a和b可交换 +例如:core.clamp(1200, 1, 1000); // 1000 +x: 原始值,!x为true时x一律视为0 +a: 下限值,大于b将导致与b交换 +b: 上限值,小于a将导致与a交换 + +clone: fn(data?: ?, filter?: fn(name: string, value: ?) -> bool, recursion?: bool) +深拷贝一个对象(函数将原样返回) +例如:core.clone(core.status.hero, (name, value) => (name == 'items' || typeof value == 'number'), false); // 深拷贝主角的属性和道具 +data: 待拷贝对象 +filter: 过滤器,可选,表示data为数组或对象时拷贝哪些项或属性,true表示拷贝 +recursion: 过滤器是否递归,可选。true表示过滤器也被递归 +返回值:拷贝的结果,注意函数将原样返回 + +cloneArray: fn(data?: [number]|[[number]]) -> [number]|[[number]] +深拷贝一个1D或2D数组对象 +例如:core.cloneArray(core.status.thisMap.map) + +copy: fn(data: string) -> bool +尝试复制一段文本到剪切板。 + +decodeBase64: fn(str: string) -> string +base64解密 +例如:core.decodeBase64('YWJjZA=='); // "abcd" +str: 密文 +返回值:明文 + +decodeRoute: fn(route: string) -> [string] +录像解压的最后一步,即一压的逆过程 +例如:core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-| +route: 录像解压倒数第二步的结果,即一压的结果 +返回值:原始录像 + +decompress: fn(value: ?) +解压缩一个数据 + +download: fn(filename: string, content: string) +弹窗请求下载一个文本文件 +例如:core.download('route.txt', JSON.stringify(core.status.route)); // 弹窗请求下载录像 +filename: 文件名 +content: 文件内容 + +encodeBase64: fn(str: string) -> string +base64加密 +例如:core.encodeBase64('abcd'); // 'YWJjZA==' +str: 明文 +返回值:密文 + +encodeRoute: fn(route: [string]) -> string +录像压缩缩 +例如:core.encodeRoute(core.status.route); // 压缩当前录像 +route: 原始录像,自定义内容(不予压缩,原样写入)必须由0-9A-Za-z和下划线、冒号组成,所以中文和数组需要用JSON.stringify预处理再base64压缩才能交由一压 +返回值:一压的结果 + +formatBigNumber: fn(x: number, onMap?: bool) -> string +大数字格式化,单位为10000的倍数(w,e,z,j,g),末尾四舍五入 +例如:core.formatBigNumber(123456789, false); // "12346w" +x: 原数字 +onMap: 可选,true表示用于地图显伤,结果总字符数最多为5,否则最多为6 +返回值:格式化结果 + +formatDate: fn(date: ?) -> string +格式化日期为字符串 + +formatDate2: fn(date: ?) -> string +格式化日期为最简字符串 + +formatSize: fn(size: number) -> string +格式化文件大小 + +formatTime: fn(time: number) -> string +格式化时间 + +getCookie: fn(name: string) -> string +访问浏览器cookie + +getGlobal: fn(key: string, defaultValue?: ?) +读取一个全局存储,适用于global:xxx,支持录像。 +例如:if (core.getGlobal('一周目已通关', false) === true) core.getItem('dagger'); // 二周目游戏进行到此处时会获得一把屠龙匕首 +key: 全局变量名称,支持中文 +defaultValue: 可选,当此全局变量不存在或值为null、undefined时,用此值代替 +返回值:全局变量的值 + +getGuid: fn() -> string +获得或生成浏览器唯一的guid + +getLocalForage: fn(key: string, defaultValue?: ?, successCallback?: fn(data: ?), errorCallback?: fn()) +从本地数据库读出一段数据 + +getLocalStorage: fn(key: string, defaultValue?: ?) +获得本地存储 + +hideWithAnimate: fn(obj?: ?, speed?: number, callback?: fn()) +动画使某对象消失 + +http: fn(type: string, url: string, formData: ?, success?: fn(data: string), error?: fn(message: string), mimeType?: string, responseType?: string, onprogress?: fn(loaded: number, total: number)) +发送一个HTTP请求 [异步] +type: 请求类型,只能为GET或POST +url: 目标地址 +formData: 如果是POST请求则为表单数据 +success: 成功后的回调 +error: 失败后的回调 + +inArray: fn(array?: ?, element?: ?) -> bool +判定array是不是一个数组,以及element是否在该数组中。 +array: 可能的数组,不为数组或不填将导致返回值为false +element: 待查找的元素 +返回值:如果array为数组且具有element这项,就返回true,否则返回false + +isset: fn(v?: ?) -> bool +判断一个值是否不为null,undefined和NaN +例如:core.isset(0/0); // false,因为0/0等于NaN +v: 待测值,可选 +返回值:false表示待测值为null、undefined、NaN或未填写,true表示为其他值。 + +matchRegex: fn(pattern: string, string: string) -> string +是否满足正则表达式 + +matchWildcard: fn(pattern: string, string: string) -> bool +通配符匹配,用于搜索图块等批量处理。 +例如:core.playSound(core.matchWildcard('*Key', itemId) ? 'item.mp3' : 'door.mp3'); // 判断捡到的是钥匙还是别的道具,从而播放不同的音效 +pattern: 模式串,每个星号表示任意多个(0个起)字符 +string: 待测串 +返回值:true表示匹配成功,false表示匹配失败 + +myconfirm: fn(hint: string, yesCallback?: fn(), noCallback?: fn()) +显示确认框,类似core.drawConfirmBox(),但不打断事件流 +例如:core.myconfirm('重启游戏?', core.restart); // 弹窗询问玩家是否重启游戏 +hint: 弹窗的内容,支持 ${} 语法 +yesCallback: 确定后的回调函数 +noCallback: 取消后的回调函数,可选 + +myprompt: fn(hint: string, value: string, callback?: fn(data?: string)) +让用户输入一段文字 + +push: fn(a: [?], b: ?) -> [?] +将b(可以是另一个数组)插入数组a的末尾,此函数用于弥补a.push(b)中b只能是单项的不足。 +例如:core.push(todo, {type: 'unfollow'}); // 在事件指令数组todo的末尾插入“取消所有跟随者”指令 +a: 原数组 +b: 待插入的新末项或后缀数组 +返回值:插入完毕后的新数组,它是改变原数组a本身得到的 + +rand: fn(num?: number) -> number +不支持SL的随机数 +例如:1 + core.rand(6); // 随机生成一个小于7的正整数,模拟骰子的效果 +num: 填正数表示生成小于num的随机自然数,否则生成小于1的随机正数 +返回值:随机数,即使读档也不会改变结果 + +rand2: fn(num?: number) -> number +支持SL的随机数,并计入录像 +例如:1 + core.rand2(6); // 随机生成一个小于7的正整数,模拟骰子的效果 +num: 正整数,0或不填会被视为2147483648 +返回值:属于 [0, num) 的随机数 + +readFile: fn(success?: fn(data: string), error?: fn(message: string), readType?: bool) +尝试请求读取一个本地文件内容 [异步] +success: 成功后的回调 +error: 失败后的回调 +readType: 不设置则以文本读取,否则以DataUrl形式读取 + +readFileContent: fn(content: string) +文件读取完毕后的内容处理 [异步] + +removeLocalForage: fn(key: string, successCallback?: fn(), errorCallback?: fn()) +移除本地数据库的数据 + +removeLocalStorage: fn(key: string) +移除本地存储 + +replaceText: fn(text: string, prefix?: string) -> string +将一段文字中的${}(表达式)进行替换。 +例如:core.replaceText('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); // 把主角的生命值和持有的黄钥匙数量代入这句话 +text: 模板字符串,可以使用${}计算js表达式,支持“状态、物品、变量、独立开关、全局存储、图块id、图块类型、敌人数据、装备id”等量参与运算 +返回值:替换完毕后的字符串 + +replaceValue: fn(value: string) -> string +对一个表达式中的特殊规则进行替换,如status:xxx等。 +例如:core.replaceValue('status:atk+item:yellowKey'); // 把这两个冒号表达式替换为core.getStatus('hp')和core.itemCount('yellowKey')这样的函数调用 +value: 模板字符串,注意独立开关不会被替换 +返回值:替换完毕后的字符串 + +same: fn(a?: ?, b?: ?) -> bool +判定深层相等, 会逐层比较每个元素 +例如:core.same(['1', 2], ['1', 2]); // true + +setGlobal: fn(key: string, value?: ?) +设置一个全局存储,适用于global:xxx,录像播放时将忽略此函数。 +例如:core.setBlobal('一周目已通关', true); // 设置全局存储“一周目已通关”为true,方便二周目游戏中的新要素。 +key: 全局变量名称,支持中文 +value: 全局变量的新值,不填或null表示清除此全局存储 + +setLocalForage: fn(key: string, value?: ?, successCallback?: fn(), errorCallback?: fn()) +往数据库写入一段数据 + +setLocalStorage: fn(key: string, value?: ?) +设置本地存储 + +setStatusBarInnerHTML: fn(name: string, value: ?, css?: string) +填写非自绘状态栏 +例如:core.setStatusBarInnerHTML('hp', core.status.hero.hp, 'color: #66CCFF'); // 更新状态栏中的主角生命,使用加载画面的宣传色 +name: 状态栏项的名称,如'hp', 'atk', 'def'等。必须是core.statusBar中的一个合法项 +value: 要填写的内容,大数字会被格式化为至多6个字符,无中文的内容会被自动设为斜体 +css: 额外的css样式,可选。如更改颜色等 + +setTwoDigits: fn(x: number) -> string +两位数显示 + +showWithAnimate: fn(obj?: ?, speed?: number, callback?: fn()) +动画显示某对象 + +splitImage: fn(image?: string|image, width?: number, height?: number) -> [image] +等比例切分一张图片 +例如:core.splitImage(core.material.images.images['npc48.png'], 32, 48); // 把npc48.png切分成若干32×48px的小人 +image: 图片名(支持映射前的中文名)或图片对象(参见上面的例子),获取不到时返回[] +width: 子图的宽度,单位为像素。原图总宽度必须是其倍数,不填视为32 +height: 子图的高度,单位为像素。原图总高度必须是其倍数,不填视为正方形 +返回值:子图组成的数组,在原图中呈先行后列,从左到右、从上到下排列。 + +strlen: fn(str: string) -> number +求字符串的国标码字节数,也可用于等宽字体下文本的宽度测算。请注意样板的默认字体Verdana不是等宽字体 +例如:core.strlen('无敌ad'); // 6 +str: 待测字符串 +返回值:字符串的国标码字节数,每个汉字为2,每个ASCII字符为1 + +subarray: fn(a?: [?], b?: [?]) -> [?]|null +判定一个数组是否为另一个数组的前缀,用于录像接续播放。请注意函数名没有大写字母 +例如:core.subarray(['ad', '米库', '小精灵', '小破草', '小艾'], ['ad', '米库', '小精灵']); // ['小破草', '小艾'] +a: 可能的母数组,不填或比b短将返回null +b: 可能的前缀,不填或比a长将返回null +返回值:如果b不是a的前缀将返回null,否则将返回a去掉此前缀后的剩余数组 + +turnDirection: fn(turn: string, direction?: string) -> string +计算应当转向某个方向 +turn: 转向的方向,可为 up,down,left,right,:left,:right,:back 七种 +direction: 当前方向 + +unshift: fn(a: [?], b: ?) -> [?] +将b(可以是另一个数组)插入数组a的开头,此函数用于弥补a.unshift(b)中b只能是单项的不足。 +例如:core.unshift(todo, {type: 'unfollow'}); // 在事件指令数组todo的开头插入“取消所有跟随者”指令 +a: 原数组 +b: 待插入的新首项或前缀数组 +返回值:插入完毕后的新数组,它是改变原数组a本身得到的 + +unzip: fn(blobOrUrl?: ?, success?: fn(data: ?), error?: fn(error: string), convertToText?: bool, onprogress?: fn(loaded: number, total: number)) +解压一段内容 +``` + +## plugin.js + +插件编写中内置了一些常用的插件。 + +```text +autoRemoveMaps: fn(floorId: string) +根据楼层分区信息自动砍层与恢复 + +canOpenShop: fn(id: string) -> bool +当前能否打开某个商店 + +canUseQuickShop: fn(id: string) -> string +当前能否使用某个快捷商店 +如果返回一个字符串,则代表不能,返回的字符串作为不能的提示;返回null表示可以使用 + +drawLight: fn(name: string|CanvasRenderingContext2D, color?: number, lights?: [[number]], lightDec?: number) +绘制一段灯光效果 +name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建 +color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。 +lights:可选,一个数组,定义了每个独立的灯光。其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。 +lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。 +例如:core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 +core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 + +isShopVisited: fn(id: string) -> bool +某个全局商店是否被访问过 + +listShopIds: fn() -> [string] +列出所有应当显示的快捷商店列表 + +openItemShop: fn(itemShopId: string) +打开一个道具商店 + +openShop: fn(shopId: string, noRoute?: bool) +打开一个全局商店 +shopId: 要开启的商店ID +noRoute: 打开行为是否不计入录像 + +removeMaps: fn(fromId: string, toId?: string) +删除某一些楼层;删除后不会存入存档,不可浏览地图也不可飞到。 +fromId: 开始删除的楼层ID +toId: 删除到的楼层编号;可选,不填则视为fromId +例如:core.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层 +core.removeMaps("MT10") 只删除MT10层 + +resumeMaps: fn(fromId: string, toId?: string) +恢复某一些被删除楼层。 +fromId: 开始恢复的楼层ID +toId: 恢复到的楼层编号;可选,不填则视为fromId +例如:core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层 +core.resumeMaps("MT10") 只删恢复MT10层 + +setShopVisited: fn(id: string, visited?: bool) +设置某个商店的访问状态 +``` diff --git a/blocksdemo.md b/blocksdemo.md new file mode 100644 index 0000000..8cec8c8 --- /dev/null +++ b/blocksdemo.md @@ -0,0 +1,55 @@ +http://127.0.0.1:1055/_docs/#/blocksdemo + +方便测试用 + +这种写法不会生成按钮 +```js +'run'; +return bg.parseList(['行内']); +// return bg.parse(['asdasd','df'],'event')+'
'+bg.parseList(['asfewg','sty']); +``` +并且是行内的 + +这种写法之后的版本会补一个按钮'添加到常用事件', 目前先这样吧 + +``` MotaAction.event +[ + '显示文章', + '...' +] +``` + +``` MotaAction.shop + [{ + "id": "shop1", + "text": "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", + "textInList": "1F金币商店", + "choices": [ + {"text": "生命+800", "need": "status:money>=20+2*flag:shop1", "action": [ + {"type": "setValue", "name": "status:money", "operator": "-=", "value": "20+2*flag:shop1"}, + {"type": "setValue", "name": "flag:shop1", "operator": "+=", "value": "1"}, + {"type": "setValue", "name": "status:hp", "operator": "+=", "value": "800"} + ]} + ] + },{ + "id": "itemShop", + "item": true, + "textInList": "道具商店", + "choices": [ + {"id": "yellowKey", "number": 10, "money": 10} + ] + },{ + "id": "keyShop1", + "textInList": "回收钥匙商店", + "commonEvent": "回收钥匙商店", + "args": "" + }] +``` + +``` MotaAction.action +'显示文章' +``` + +``` MotaAction +['MotaAction.action的anction可以省略','只写MotaAction'] +``` \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..0102bc2 --- /dev/null +++ b/config.json @@ -0,0 +1 @@ +{"viewportLoc":[0,0],"lastUsed":[{"idnum":302,"id":"T302","images":"terrains","y":22,"recent":1736779168172,"frequent":17},{"idnum":303,"id":"T303","images":"terrains","y":23,"recent":1736779154668,"frequent":11},{"idnum":391,"id":"T391","images":"terrains","y":95,"recent":1736185774079,"frequent":3},{"idnum":392,"id":"T392","images":"terrains","y":96,"recent":1736185771097,"frequent":3},{"idnum":339,"id":"T339","images":"terrains","y":43,"recent":1736012831117,"frequent":18},{"idnum":352,"id":"T352","images":"terrains","y":56,"recent":1736012819912,"frequent":6},{"idnum":1046,"id":"E17","images":"enemy48","y":16,"recent":1735480869303,"frequent":2},{"idnum":327,"id":"E5","images":"enemy48","y":4,"recent":1735476039916,"frequent":4},{"idnum":1102,"id":"E73","images":"enemy48","y":72,"recent":1735472859218,"frequent":1},{"idnum":1098,"id":"E69","images":"enemy48","y":68,"recent":1735472855845,"frequent":1},{"idnum":1094,"id":"E65","images":"enemy48","y":64,"recent":1735472853572,"frequent":1},{"idnum":1090,"id":"E61","images":"enemy48","y":60,"recent":1735472850814,"frequent":1},{"idnum":1086,"id":"E57","images":"enemy48","y":56,"recent":1735472848430,"frequent":1},{"idnum":1082,"id":"E53","images":"enemy48","y":52,"recent":1735472844966,"frequent":1},{"idnum":1074,"id":"E45","images":"enemy48","y":44,"recent":1735472841460,"frequent":1},{"idnum":1070,"id":"E41","images":"enemy48","y":40,"recent":1735472838218,"frequent":1},{"idnum":1066,"id":"E37","images":"enemy48","y":36,"recent":1735472835399,"frequent":1},{"idnum":1062,"id":"E33","images":"enemy48","y":32,"recent":1735472831463,"frequent":1},{"idnum":1058,"id":"E29","images":"enemy48","y":28,"recent":1735472829345,"frequent":1},{"idnum":1054,"id":"E25","images":"enemy48","y":24,"recent":1735472825443,"frequent":1},{"idnum":1050,"id":"E21","images":"enemy48","y":20,"recent":1735472821950,"frequent":1},{"idnum":1042,"id":"E13","images":"enemy48","y":12,"recent":1735472788446,"frequent":1},{"idnum":1038,"id":"E9","images":"enemy48","y":8,"recent":1735472785088,"frequent":3},{"idnum":261,"id":"E1","images":"enemy48","y":0,"recent":1735472780290,"frequent":3},{"idnum":1048,"id":"E19","images":"enemy48","y":18,"recent":1735472735149,"frequent":1},{"idnum":328,"id":"E6","images":"enemy48","y":5,"recent":1735469022623,"frequent":1},{"idnum":1039,"id":"E10","images":"enemy48","y":9,"recent":1735468455619,"frequent":1},{"idnum":1030,"id":"E1030","images":"enemys","y":145,"recent":1735463812376,"frequent":1},{"idnum":1026,"id":"E1026","images":"enemys","y":141,"recent":1735463810368,"frequent":1},{"idnum":1022,"id":"E1022","images":"enemys","y":137,"recent":1735463808560,"frequent":1},{"idnum":1018,"id":"E1018","images":"enemys","y":133,"recent":1735463806534,"frequent":1},{"idnum":1014,"id":"E1014","images":"enemys","y":129,"recent":1735463804332,"frequent":1},{"idnum":1010,"id":"E1010","images":"enemys","y":125,"recent":1735463801805,"frequent":1},{"idnum":1003,"id":"E1003","images":"enemys","y":118,"recent":1735463798777,"frequent":1},{"idnum":998,"id":"E998","images":"enemys","y":113,"recent":1735463796783,"frequent":1},{"idnum":994,"id":"E994","images":"enemys","y":109,"recent":1735463794182,"frequent":1},{"idnum":990,"id":"E990","images":"enemys","y":105,"recent":1735463792188,"frequent":1},{"idnum":462,"id":"E462","images":"enemys","y":101,"recent":1735463767416,"frequent":1},{"idnum":458,"id":"E458","images":"enemys","y":97,"recent":1735463765243,"frequent":1},{"idnum":454,"id":"E454","images":"enemys","y":93,"recent":1735463763173,"frequent":1},{"idnum":450,"id":"E450","images":"enemys","y":89,"recent":1735463760520,"frequent":1},{"idnum":446,"id":"E446","images":"enemys","y":85,"recent":1735463757175,"frequent":1},{"idnum":442,"id":"E442","images":"enemys","y":81,"recent":1735463755047,"frequent":1},{"idnum":438,"id":"E438","images":"enemys","y":77,"recent":1735463752876,"frequent":1},{"idnum":434,"id":"E434","images":"enemys","y":73,"recent":1735463749711,"frequent":2},{"idnum":366,"id":"T366","images":"terrains","y":70,"recent":1734630289017,"frequent":16},{"idnum":367,"id":"T367","images":"terrains","y":71,"recent":1734630270050,"frequent":30},{"idnum":924,"id":"T924","images":"terrains","y":166,"recent":1734630264211,"frequent":16},{"idnum":907,"id":"T907","images":"terrains","y":149,"recent":1734630235528,"frequent":113},{"idnum":906,"id":"T906","images":"terrains","y":148,"recent":1734630230354,"frequent":3},{"idnum":901,"id":"T901","images":"terrains","y":143,"recent":1734630225799,"frequent":5},{"idnum":900,"id":"T900","images":"terrains","y":142,"recent":1734630221216,"frequent":6},{"idnum":902,"id":"T902","images":"terrains","y":144,"recent":1734630218166,"frequent":3},{"idnum":905,"id":"T905","images":"terrains","y":147,"recent":1734630214189,"frequent":6},{"idnum":371,"id":"T371","images":"terrains","y":75,"recent":1734630209079,"frequent":16},{"idnum":361,"id":"T361","images":"terrains","y":65,"recent":1734630197680,"frequent":8},{"idnum":360,"id":"T360","images":"terrains","y":64,"recent":1734630186830,"frequent":63},{"idnum":332,"id":"T332","images":"terrains","y":36,"recent":1734539275717,"frequent":2},{"idnum":166,"id":"darkLight","images":"terrains","y":16,"recent":1734443357480,"frequent":2},{"idnum":671,"id":"I671","images":"items","y":256,"recent":1734282086098,"frequent":1},{"idnum":672,"id":"I672","images":"items","y":257,"recent":1734282083016,"frequent":1},{"idnum":910,"id":"T910","images":"terrains","y":152,"recent":1734109787344,"frequent":26},{"idnum":415,"id":"T415","images":"terrains","y":119,"recent":1734109260799,"frequent":11},{"idnum":916,"id":"T916","images":"terrains","y":158,"recent":1734109257386,"frequent":96},{"idnum":365,"id":"T365","images":"terrains","y":69,"recent":1734109091433,"frequent":46},{"idnum":420,"id":"T420","images":"terrains","y":124,"recent":1734109068167,"frequent":22},{"idnum":346,"id":"T346","images":"terrains","y":50,"recent":1734018715802,"frequent":7},{"idnum":922,"id":"T922","images":"terrains","y":164,"recent":1734018702622,"frequent":14},{"idnum":409,"id":"T409","images":"terrains","y":113,"recent":1734018667280,"frequent":5},{"idnum":909,"id":"T909","images":"terrains","y":151,"recent":1734018664004,"frequent":47},{"idnum":4,"id":"star","images":"animates","y":0,"recent":1734018582240,"frequent":46},{"idnum":921,"id":"T921","images":"terrains","y":163,"recent":1734018528209,"frequent":11},{"idnum":917,"id":"T917","images":"terrains","y":159,"recent":1734018447849,"frequent":8},{"idnum":345,"id":"T345","images":"terrains","y":49,"recent":1733768675063,"frequent":6},{"idnum":908,"id":"T908","images":"terrains","y":150,"recent":1733768193751,"frequent":61},{"idnum":904,"id":"T904","images":"terrains","y":146,"recent":1733767640877,"frequent":4},{"idnum":923,"id":"T923","images":"terrains","y":165,"recent":1733764083956,"frequent":74},{"idnum":926,"id":"T926","images":"terrains","y":168,"recent":1733764080927,"frequent":87},{"idnum":927,"id":"T927","images":"terrains","y":169,"recent":1733763919696,"frequent":8},{"idnum":897,"id":"T897","images":"terrains","y":139,"recent":1733762366125,"frequent":67},{"idnum":928,"id":"T928","images":"terrains","y":170,"recent":1733762355992,"frequent":35},{"idnum":89,"id":"portal","images":"animates","y":17,"recent":1733762182522,"frequent":3},{"idnum":925,"id":"T925","images":"terrains","y":167,"recent":1733761266447,"frequent":20},{"idnum":1,"id":"yellowWall","images":"animates","y":10,"recent":1733760628481,"frequent":3},{"idnum":930,"id":"T930","images":"terrains","y":172,"recent":1733760159751,"frequent":1},{"idnum":929,"id":"T929","images":"terrains","y":171,"recent":1733760156996,"frequent":1},{"idnum":362,"id":"T362","images":"terrains","y":66,"recent":1733758642041,"frequent":75},{"idnum":424,"id":"T424","images":"terrains","y":128,"recent":1733758349274,"frequent":10},{"idnum":896,"id":"T896","images":"terrains","y":138,"recent":1733677492412,"frequent":24},{"idnum":406,"id":"T406","images":"terrains","y":110,"recent":1733676210426,"frequent":7},{"idnum":405,"id":"T405","images":"terrains","y":109,"recent":1733676208116,"frequent":9},{"idnum":404,"id":"T404","images":"terrains","y":108,"recent":1733676204924,"frequent":9},{"idnum":349,"id":"T349","images":"terrains","y":53,"recent":1733676092970,"frequent":16},{"idnum":379,"id":"T379","images":"terrains","y":83,"recent":1733675498379,"frequent":8},{"idnum":375,"id":"T375","images":"terrains","y":79,"recent":1733675493536,"frequent":9},{"idnum":374,"id":"T374","images":"terrains","y":78,"recent":1733675491510,"frequent":9},{"idnum":373,"id":"T373","images":"terrains","y":77,"recent":1733675489187,"frequent":9},{"idnum":372,"id":"T372","images":"terrains","y":76,"recent":1733675486692,"frequent":14},{"idnum":898,"id":"T898","images":"terrains","y":140,"recent":1733675392035,"frequent":3},{"idnum":414,"id":"T414","images":"terrains","y":118,"recent":1733675319463,"frequent":5},{"idnum":419,"id":"T419","images":"terrains","y":123,"recent":1733675315630,"frequent":5},{"idnum":342,"id":"T342","images":"terrains","y":46,"recent":1733675266475,"frequent":29},{"idnum":423,"id":"T423","images":"terrains","y":127,"recent":1733674790597,"frequent":6},{"idnum":920,"id":"T920","images":"terrains","y":162,"recent":1733674656198,"frequent":3},{"idnum":919,"id":"T919","images":"terrains","y":161,"recent":1733674654536,"frequent":3},{"idnum":918,"id":"T918","images":"terrains","y":160,"recent":1733674652459,"frequent":6},{"idnum":348,"id":"T348","images":"terrains","y":52,"recent":1733674624674,"frequent":14},{"idnum":426,"id":"T426","images":"terrains","y":130,"recent":1733674618172,"frequent":10},{"idnum":343,"id":"T343","images":"terrains","y":47,"recent":1733674549187,"frequent":43},{"idnum":430,"id":"T430","images":"terrains","y":134,"recent":1733672989519,"frequent":2},{"idnum":429,"id":"T429","images":"terrains","y":133,"recent":1733672987289,"frequent":2},{"idnum":428,"id":"T428","images":"terrains","y":132,"recent":1733672969312,"frequent":5},{"idnum":381,"id":"T381","images":"terrains","y":85,"recent":1733672643226,"frequent":26},{"idnum":377,"id":"T377","images":"terrains","y":81,"recent":1733672631164,"frequent":9},{"idnum":376,"id":"T376","images":"terrains","y":80,"recent":1733672628015,"frequent":11},{"idnum":356,"id":"T356","images":"terrains","y":60,"recent":1733672607091,"frequent":4},{"idnum":355,"id":"T355","images":"terrains","y":59,"recent":1733672605024,"frequent":9},{"idnum":684,"id":"I684","images":"items","y":269,"recent":1733669572342,"frequent":5},{"idnum":685,"id":"I685","images":"items","y":270,"recent":1733669566585,"frequent":5},{"idnum":538,"id":"I538","images":"items","y":123,"recent":1733669263096,"frequent":2},{"idnum":536,"id":"I536","images":"items","y":121,"recent":1733669260899,"frequent":2},{"idnum":537,"id":"I537","images":"items","y":122,"recent":1733669259034,"frequent":1},{"idnum":539,"id":"I539","images":"items","y":124,"recent":1733669254595,"frequent":1},{"idnum":540,"id":"I540","images":"items","y":125,"recent":1733669252526,"frequent":1},{"idnum":543,"id":"I543","images":"items","y":128,"recent":1733669249422,"frequent":3},{"idnum":542,"id":"I542","images":"items","y":127,"recent":1733669247376,"frequent":8},{"idnum":541,"id":"I541","images":"items","y":126,"recent":1733669245726,"frequent":3},{"idnum":707,"id":"I707","images":"items","y":292,"recent":1733669237150,"frequent":1},{"idnum":704,"id":"I704","images":"items","y":289,"recent":1733669233303,"frequent":1},{"idnum":703,"id":"I703","images":"items","y":288,"recent":1733669230687,"frequent":2},{"idnum":702,"id":"I702","images":"items","y":287,"recent":1733669227815,"frequent":1},{"idnum":677,"id":"I677","images":"items","y":262,"recent":1733669202564,"frequent":1},{"idnum":676,"id":"I676","images":"items","y":261,"recent":1733669200570,"frequent":2},{"idnum":661,"id":"I661","images":"items","y":246,"recent":1733669190666,"frequent":3},{"idnum":638,"id":"I638","images":"items","y":223,"recent":1733669183353,"frequent":1},{"idnum":637,"id":"I637","images":"items","y":222,"recent":1733669180921,"frequent":4},{"idnum":630,"id":"I630","images":"items","y":215,"recent":1733669158834,"frequent":2},{"idnum":629,"id":"I629","images":"items","y":214,"recent":1733669154756,"frequent":1},{"idnum":620,"id":"I620","images":"items","y":205,"recent":1733669146960,"frequent":1},{"idnum":614,"id":"I614","images":"items","y":199,"recent":1733669143296,"frequent":1},{"idnum":616,"id":"I616","images":"items","y":201,"recent":1733669141833,"frequent":1},{"idnum":615,"id":"I615","images":"items","y":200,"recent":1733669139755,"frequent":1},{"idnum":549,"id":"I549","images":"items","y":134,"recent":1733669097584,"frequent":4},{"idnum":519,"id":"I519","images":"items","y":104,"recent":1733669075454,"frequent":2},{"idnum":535,"id":"I535","images":"items","y":120,"recent":1733669071876,"frequent":1},{"idnum":534,"id":"I534","images":"items","y":119,"recent":1733669069596,"frequent":6},{"idnum":531,"id":"I531","images":"items","y":116,"recent":1733668990346,"frequent":2},{"idnum":530,"id":"I530","images":"items","y":115,"recent":1733668988518,"frequent":1},{"idnum":533,"id":"I533","images":"items","y":118,"recent":1733668985682,"frequent":1},{"idnum":532,"id":"I532","images":"items","y":117,"recent":1733668983879,"frequent":1},{"idnum":529,"id":"I529","images":"items","y":114,"recent":1733668972911,"frequent":1},{"idnum":528,"id":"I528","images":"items","y":113,"recent":1733668970284,"frequent":1},{"idnum":527,"id":"I527","images":"items","y":112,"recent":1733668967722,"frequent":1},{"idnum":522,"id":"I522","images":"items","y":107,"recent":1733668964597,"frequent":2},{"idnum":521,"id":"I521","images":"items","y":106,"recent":1733668962691,"frequent":1},{"idnum":520,"id":"I520","images":"items","y":105,"recent":1733668959446,"frequent":1},{"idnum":518,"id":"I518","images":"items","y":103,"recent":1733668954166,"frequent":1},{"idnum":517,"id":"I517","images":"items","y":102,"recent":1733668951705,"frequent":1},{"idnum":516,"id":"I516","images":"items","y":101,"recent":1733668949759,"frequent":2},{"idnum":515,"id":"I515","images":"items","y":100,"recent":1733668946848,"frequent":1},{"idnum":514,"id":"I514","images":"items","y":99,"recent":1733668943602,"frequent":1},{"idnum":513,"id":"I513","images":"items","y":98,"recent":1733668938063,"frequent":1},{"idnum":512,"id":"I512","images":"items","y":97,"recent":1733668935836,"frequent":1},{"idnum":511,"id":"I511","images":"items","y":96,"recent":1733668933098,"frequent":2},{"idnum":678,"id":"I678","images":"items","y":263,"recent":1733667629067,"frequent":1},{"idnum":664,"id":"I664","images":"items","y":249,"recent":1733667557835,"frequent":1},{"idnum":663,"id":"I663","images":"items","y":248,"recent":1733667555063,"frequent":1},{"idnum":662,"id":"I662","images":"items","y":247,"recent":1733667550917,"frequent":1},{"idnum":653,"id":"I653","images":"items","y":238,"recent":1733667538404,"frequent":1},{"idnum":634,"id":"I634","images":"items","y":219,"recent":1733667527016,"frequent":3},{"idnum":635,"id":"I635","images":"items","y":220,"recent":1733667523880,"frequent":1},{"idnum":598,"id":"I598","images":"items","y":183,"recent":1733667519774,"frequent":2},{"idnum":566,"id":"I566","images":"items","y":151,"recent":1733667509920,"frequent":2},{"idnum":560,"id":"I560","images":"items","y":145,"recent":1733667507335,"frequent":2},{"idnum":421,"id":"T421","images":"terrains","y":125,"recent":1733667497426,"frequent":6},{"idnum":561,"id":"I561","images":"items","y":146,"recent":1733667484755,"frequent":1},{"idnum":559,"id":"I559","images":"items","y":144,"recent":1733667423139,"frequent":1},{"idnum":555,"id":"I555","images":"items","y":140,"recent":1733667416679,"frequent":1},{"idnum":551,"id":"I551","images":"items","y":136,"recent":1733667413743,"frequent":2},{"idnum":550,"id":"I550","images":"items","y":135,"recent":1733667411717,"frequent":1},{"idnum":548,"id":"I548","images":"items","y":133,"recent":1733667406209,"frequent":1},{"idnum":547,"id":"I547","images":"items","y":132,"recent":1733667404357,"frequent":1},{"idnum":546,"id":"I546","images":"items","y":131,"recent":1733667402488,"frequent":1},{"idnum":545,"id":"I545","images":"items","y":130,"recent":1733667400495,"frequent":1},{"idnum":544,"id":"I544","images":"items","y":129,"recent":1733667398246,"frequent":1},{"idnum":388,"id":"T388","images":"terrains","y":92,"recent":1733663932100,"frequent":4},{"idnum":387,"id":"T387","images":"terrains","y":91,"recent":1733663914144,"frequent":6},{"idnum":370,"id":"T370","images":"terrains","y":74,"recent":1733663885313,"frequent":9},{"idnum":915,"id":"T915","images":"terrains","y":157,"recent":1733663357339,"frequent":9},{"idnum":914,"id":"T914","images":"terrains","y":156,"recent":1733663350681,"frequent":13},{"idnum":913,"id":"T913","images":"terrains","y":155,"recent":1733663336331,"frequent":5},{"idnum":912,"id":"T912","images":"terrains","y":154,"recent":1733663304626,"frequent":4},{"idnum":911,"id":"T911","images":"terrains","y":153,"recent":1733663302123,"frequent":4},{"idnum":903,"id":"T903","images":"terrains","y":145,"recent":1733663280891,"frequent":2},{"idnum":899,"id":"T899","images":"terrains","y":141,"recent":1733663269046,"frequent":3},{"idnum":433,"id":"T433","images":"terrains","y":137,"recent":1733663195127,"frequent":4},{"idnum":432,"id":"T432","images":"terrains","y":136,"recent":1733663191189,"frequent":6},{"idnum":431,"id":"T431","images":"terrains","y":135,"recent":1733663187499,"frequent":4},{"idnum":427,"id":"T427","images":"terrains","y":131,"recent":1733663176736,"frequent":1},{"idnum":425,"id":"T425","images":"terrains","y":129,"recent":1733663162579,"frequent":5},{"idnum":422,"id":"T422","images":"terrains","y":126,"recent":1733663151131,"frequent":3},{"idnum":417,"id":"T417","images":"terrains","y":121,"recent":1733663129907,"frequent":2},{"idnum":416,"id":"T416","images":"terrains","y":120,"recent":1733663127823,"frequent":3},{"idnum":418,"id":"T418","images":"terrains","y":122,"recent":1733663125108,"frequent":3},{"idnum":411,"id":"T411","images":"terrains","y":115,"recent":1733662794991,"frequent":10},{"idnum":403,"id":"T403","images":"terrains","y":107,"recent":1733662782943,"frequent":6},{"idnum":410,"id":"T410","images":"terrains","y":114,"recent":1733662778484,"frequent":5},{"idnum":408,"id":"T408","images":"terrains","y":112,"recent":1733662772343,"frequent":5},{"idnum":407,"id":"T407","images":"terrains","y":111,"recent":1733662768888,"frequent":5},{"idnum":401,"id":"T401","images":"terrains","y":105,"recent":1733662751135,"frequent":9},{"idnum":400,"id":"T400","images":"terrains","y":104,"recent":1733662742733,"frequent":8},{"idnum":399,"id":"T399","images":"terrains","y":103,"recent":1733662708769,"frequent":6},{"idnum":402,"id":"T402","images":"terrains","y":106,"recent":1733662703363,"frequent":3},{"idnum":413,"id":"T413","images":"terrains","y":117,"recent":1733662479697,"frequent":4},{"idnum":412,"id":"T412","images":"terrains","y":116,"recent":1733662477353,"frequent":4},{"idnum":390,"id":"T390","images":"terrains","y":94,"recent":1733662474621,"frequent":9},{"idnum":389,"id":"T389","images":"terrains","y":93,"recent":1733662472455,"frequent":10},{"idnum":347,"id":"T347","images":"terrains","y":51,"recent":1733662467305,"frequent":7},{"idnum":380,"id":"T380","images":"terrains","y":84,"recent":1733662420544,"frequent":9},{"idnum":364,"id":"T364","images":"terrains","y":68,"recent":1733661379162,"frequent":6},{"idnum":363,"id":"T363","images":"terrains","y":67,"recent":1733661375092,"frequent":10},{"idnum":344,"id":"T344","images":"terrains","y":48,"recent":1733661311128,"frequent":4},{"idnum":452,"id":"E452","images":"enemys","y":91,"recent":1733583446386,"frequent":2},{"idnum":383,"id":"T383","images":"terrains","y":87,"recent":1732962386767,"frequent":40},{"idnum":382,"id":"T382","images":"terrains","y":86,"recent":1732962382921,"frequent":7},{"idnum":378,"id":"T378","images":"terrains","y":82,"recent":1732962371397,"frequent":4},{"idnum":357,"id":"T357","images":"terrains","y":61,"recent":1732962362349,"frequent":11},{"idnum":359,"id":"T359","images":"terrains","y":63,"recent":1732962360685,"frequent":6},{"idnum":358,"id":"T358","images":"terrains","y":62,"recent":1732962359193,"frequent":8},{"idnum":354,"id":"T354","images":"terrains","y":58,"recent":1732950406791,"frequent":10},{"idnum":351,"id":"T351","images":"terrains","y":55,"recent":1732950400937,"frequent":7},{"idnum":340,"id":"T340","images":"terrains","y":44,"recent":1732946219188,"frequent":4},{"idnum":202,"id":"redSlime","images":"enemys","y":1,"recent":1732943403849,"frequent":4},{"idnum":204,"id":"slimelord","images":"enemys","y":3,"recent":1732943400171,"frequent":1},{"idnum":594,"id":"I594","images":"items","y":179,"recent":1732038736259,"frequent":3},{"idnum":595,"id":"I595","images":"items","y":180,"recent":1732038733792,"frequent":4},{"idnum":602,"id":"I602","images":"items","y":187,"recent":1732038730022,"frequent":2},{"idnum":601,"id":"I601","images":"items","y":186,"recent":1732038726078,"frequent":1},{"idnum":600,"id":"I600","images":"items","y":185,"recent":1732038724333,"frequent":1},{"idnum":599,"id":"I599","images":"items","y":184,"recent":1732038722308,"frequent":1},{"idnum":350,"id":"T350","images":"terrains","y":54,"recent":1731334812231,"frequent":10},{"idnum":716,"id":"I716","images":"items","y":301,"recent":1730895704826,"frequent":2},{"idnum":715,"id":"I715","images":"items","y":300,"recent":1730895700816,"frequent":2},{"idnum":714,"id":"I714","images":"items","y":299,"recent":1730895695540,"frequent":1},{"idnum":709,"id":"I709","images":"items","y":294,"recent":1730895692828,"frequent":1},{"idnum":710,"id":"I710","images":"items","y":295,"recent":1730895690183,"frequent":1},{"idnum":711,"id":"I711","images":"items","y":296,"recent":1730895688309,"frequent":1},{"idnum":713,"id":"I713","images":"items","y":298,"recent":1730895686445,"frequent":1},{"idnum":712,"id":"I712","images":"items","y":297,"recent":1730895682857,"frequent":2},{"idnum":705,"id":"I705","images":"items","y":290,"recent":1730822345895,"frequent":2},{"idnum":395,"id":"T395","images":"terrains","y":99,"recent":1730821036607,"frequent":7},{"idnum":93,"id":"downPortal","images":"animates","y":23,"recent":1729930900212,"frequent":43},{"idnum":94,"id":"rightPortal","images":"animates","y":25,"recent":1729930639399,"frequent":43},{"idnum":92,"id":"leftPortal","images":"animates","y":24,"recent":1729929987511,"frequent":24},{"idnum":311,"id":"ground2","images":"terrains","y":3,"recent":1729880171245,"frequent":27},{"idnum":88,"id":"downFloor","images":"terrains","y":5,"recent":1729880114349,"frequent":3},{"idnum":91,"id":"upPortal","images":"animates","y":26,"recent":1729878947156,"frequent":20},{"idnum":304,"id":"T304","images":"terrains","y":24,"recent":1729867595956,"frequent":1},{"idnum":341,"id":"T341","images":"terrains","y":45,"recent":1729867160889,"frequent":2},{"idnum":398,"id":"T398","images":"terrains","y":102,"recent":1729864216220,"frequent":2},{"idnum":397,"id":"T397","images":"terrains","y":101,"recent":1729864214246,"frequent":2},{"idnum":396,"id":"T396","images":"terrains","y":100,"recent":1729864212210,"frequent":5},{"idnum":394,"id":"T394","images":"terrains","y":98,"recent":1729864208710,"frequent":7},{"idnum":393,"id":"T393","images":"terrains","y":97,"recent":1729864207028,"frequent":4},{"idnum":386,"id":"T386","images":"terrains","y":90,"recent":1729864195062,"frequent":6},{"idnum":353,"id":"T353","images":"terrains","y":57,"recent":1729864155600,"frequent":7},{"idnum":477,"id":"I477","images":"items","y":62,"recent":1729688309510,"frequent":1},{"idnum":385,"id":"T385","images":"terrains","y":89,"recent":1729617277304,"frequent":28},{"idnum":384,"id":"T384","images":"terrains","y":88,"recent":1729617275539,"frequent":2},{"idnum":369,"id":"T369","images":"terrains","y":73,"recent":1729617020155,"frequent":11},{"idnum":335,"id":"T335","images":"terrains","y":39,"recent":1729611587485,"frequent":3},{"idnum":368,"id":"T368","images":"terrains","y":72,"recent":1729611570201,"frequent":4},{"idnum":305,"id":"grass","images":"terrains","y":1,"recent":1729611255861,"frequent":8},{"idnum":46,"id":"fly","images":"items","y":12,"recent":1729527727412,"frequent":2},{"idnum":308,"id":"grass2","images":"terrains","y":2,"recent":1729523411012,"frequent":2},{"idnum":129,"id":"sign","images":"npcs","y":8,"recent":1729341369638,"frequent":9},{"idnum":201,"id":"greenSlime","images":"enemys","y":0,"recent":1729341269999,"frequent":21},{"idnum":7,"id":"blueShopLeft","images":"terrains","y":7,"recent":1705065342547,"frequent":10},{"idnum":87,"id":"upFloor","images":"terrains","y":6,"recent":1704816516960,"frequent":5},{"idnum":301,"id":"sWallT","images":"terrains","y":21,"recent":1704721637969,"frequent":2},{"idnum":336,"id":"T336","images":"terrains","y":40,"recent":1704716124389,"frequent":4},{"idnum":338,"id":"T338","images":"terrains","y":42,"recent":1704550522685,"frequent":1},{"idnum":337,"id":"T337","images":"terrains","y":41,"recent":1704550520845,"frequent":1},{"idnum":334,"id":"T334","images":"terrains","y":38,"recent":1704550245134,"frequent":1},{"idnum":333,"id":"T333","images":"terrains","y":37,"recent":1704550242837,"frequent":1},{"idnum":331,"id":"T331","images":"terrains","y":35,"recent":1704550238958,"frequent":1},{"idnum":696,"id":"I696","images":"items","y":281,"recent":1704537744917,"frequent":1},{"idnum":695,"id":"I695","images":"items","y":280,"recent":1704537742988,"frequent":1},{"idnum":694,"id":"I694","images":"items","y":279,"recent":1704537740612,"frequent":1},{"idnum":691,"id":"I691","images":"items","y":276,"recent":1704537614792,"frequent":10},{"idnum":689,"id":"I689","images":"items","y":274,"recent":1704537587048,"frequent":1},{"idnum":688,"id":"I688","images":"items","y":273,"recent":1704537557963,"frequent":1},{"idnum":666,"id":"I666","images":"items","y":251,"recent":1704537520992,"frequent":6},{"idnum":495,"id":"I495","images":"items","y":80,"recent":1704537515175,"frequent":1},{"idnum":493,"id":"I493","images":"items","y":78,"recent":1704537512935,"frequent":1},{"idnum":203,"id":"blackSlime","images":"enemys","y":2,"recent":1703258457747,"frequent":1},{"idnum":669,"id":"I669","images":"items","y":254,"recent":1703171856837,"frequent":1},{"idnum":612,"id":"I612","images":"items","y":197,"recent":1703170846897,"frequent":1},{"idnum":607,"id":"I607","images":"items","y":192,"recent":1703170839814,"frequent":1},{"idnum":300,"id":"ground","images":"terrains","y":0,"recent":1702834871017,"frequent":10},{"idnum":47,"id":"pickaxe","images":"items","y":45,"recent":1701362272031,"frequent":1},{"idnum":48,"id":"icePickaxe","images":"items","y":44,"recent":1701361941936,"frequent":1},{"idnum":611,"id":"I611","images":"items","y":196,"recent":1701098423607,"frequent":1},{"idnum":610,"id":"I610","images":"items","y":195,"recent":1701098421218,"frequent":1},{"idnum":609,"id":"I609","images":"items","y":194,"recent":1701098419169,"frequent":1},{"idnum":608,"id":"I608","images":"items","y":193,"recent":1701098416085,"frequent":1},{"idnum":596,"id":"I596","images":"items","y":181,"recent":1701098413326,"frequent":2},{"idnum":593,"id":"I593","images":"items","y":178,"recent":1701098406740,"frequent":2},{"idnum":503,"id":"I503","images":"items","y":88,"recent":1701098184341,"frequent":3},{"idnum":483,"id":"I483","images":"items","y":68,"recent":1701097957801,"frequent":2},{"idnum":64,"id":"amulet","images":"items","y":47,"recent":1701018158598,"frequent":4},{"idnum":35,"id":"sword1","images":"items","y":50,"recent":1701018058412,"frequent":1},{"idnum":504,"id":"I504","images":"items","y":89,"recent":1700584291017,"frequent":1},{"idnum":502,"id":"I502","images":"items","y":87,"recent":1700584287023,"frequent":1},{"idnum":501,"id":"I501","images":"items","y":86,"recent":1700584285239,"frequent":1},{"idnum":486,"id":"I486","images":"items","y":71,"recent":1700584263093,"frequent":1},{"idnum":485,"id":"I485","images":"items","y":70,"recent":1700584261216,"frequent":1},{"idnum":484,"id":"I484","images":"items","y":69,"recent":1700584259849,"frequent":1},{"idnum":482,"id":"I482","images":"items","y":67,"recent":1700584257939,"frequent":1},{"idnum":53,"id":"coin","images":"items","y":11,"recent":1700582201012,"frequent":1},{"idnum":45,"id":"book","images":"items","y":9,"recent":1700488684039,"frequent":1},{"idnum":16,"id":"water","images":"animates","y":3,"recent":1700150722182,"frequent":5},{"idnum":15,"id":"blueLava","images":"animates","y":2,"recent":1700150712035,"frequent":4},{"idnum":5,"id":"lava","images":"animates","y":1,"recent":1700150703264,"frequent":6},{"idnum":257,"id":"dragon","images":"enemys","y":56,"recent":1700147859474,"frequent":1},{"idnum":211,"id":"skeletonCaptain","images":"enemys","y":10,"recent":1673440202872,"frequent":1}],"editor_multi.fontSize":21,"editorLastFloorId":"MT0","foldPerCol":18,"folded":true,"alertTileModeV2.7":true,"theme":"editor_color","shortcut":{"48":0,"49":{"idnum":92,"id":"leftPortal","images":"animates","y":24},"50":{"idnum":93,"id":"downPortal","images":"animates","y":23},"51":{"idnum":94,"id":"rightPortal","images":"animates","y":25},"52":{"idnum":91,"id":"upPortal","images":"animates","y":26},"53":0,"54":0,"55":0,"56":0,"57":0}} \ No newline at end of file diff --git a/control.d.ts b/control.d.ts new file mode 100644 index 0000000..b05ed99 --- /dev/null +++ b/control.d.ts @@ -0,0 +1,1095 @@ +/** + * 帧动画函数 + */ +type FrameFunc = (time: number) => void; + +/** + * 录像操作函数,返回true表示执行成功 + */ +type ReplayFunc = (action: string) => boolean; + +/** + * 游戏画面大小变化时执行的函数 + */ +type ResizeFunc = (obj: DeepReadonly) => void; + +/** + * 勇士属性中的数字属性 + */ +type NumbericHeroStatus = SelectType; + +/** + * 存读档类型 + */ +type SLType = + | 'save' + | 'load' + | 'reload' + | 'replayLoad' + | 'replayRemain' + | 'replaySince'; + +/** + * 天气等级 + */ +type WeatherLevel = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; + +/** + * resize函数的参数 + */ +interface ResizeObj { + /** + * body元素的宽度 + */ + clientWidth: number; + + /** + * body元素的高度 + */ + clientHeight: number; + + /** + * 边框的宽度 + */ + BORDER: 3; + + /** + * 状态栏的宽度 + */ + BAR_WIDTH: number; + + /** + * 工具栏的高度 + */ + TOOLBAR_HEIGHT: 38; + + /** + * 计算边框之后的游戏画面的宽度 + */ + outerWidth: number; + + /** + * 计算边框之后的游戏画面的高度 + */ + outerHeight: number; + + /** + * 全局属性 + */ + globalAttribute: GlobalAttribute; + + /** + * 边框样式,css字符串 + */ + border: string; + + /** + * 状态栏显示的状态项 + */ + statusDisplayArr: string[]; + + /** + * 状态栏显示的状态项数 + */ + count: number; + + /** + * 状态栏显示的行数 + */ + col: number; + + /** + * 竖屏下状态栏的高度 + */ + statusBarHeightInVertical: number; + + /** + * 竖屏下工具栏的高度 + */ + toolbarHeightInVertical: number; + + /** + * 是否开启底部工具栏 + */ + extendToolbar: number; + + /** + * @deprecated + * 是否是15x15 + */ + is15x15: false; +} + +interface RenderFrame { + /** + * 帧动画的名称 + */ + name: string; + + /** + * 是否需要进入游戏后才执行 + */ + needPlaying: boolean; + + /** + * 每帧执行的函数 + */ + func: FrameFunc; +} + +interface ReplayAction { + /** + * 录像操作的名称 + */ + name: string; + + /** + * 录像操作执行的函数 + */ + func: ReplayFunc; +} + +interface ResizeAction { + /** + * resize操作的名称 + */ + name: string; + + /** + * 游戏画面变化时执行的函数 + */ + func: ResizeFunc; +} + +interface WeatherAction { + /** + * 天气每帧执行的函数 + */ + frameFunc?: (time: number, level: WeatherLevel) => void; + + /** + * 天气的初始化函数 + */ + initFunc: (level: WeatherLevel) => void; +} + +interface FrameObj { + angle: number; + index: number; + mirror: number; + opacity: number; + x: number; + y: number; + zoom: number; +} + +/** + * 主要用来进行游戏控制,比如行走控制、自动寻路、存读档等等游戏核心内容 + */ +interface Control { + /** + * 刷新状态栏时是否不执行自动事件 + */ + readonly noAutoEvent: boolean; + + /** + * 注册的帧动画 + */ + readonly renderFrameFunc: RenderFrame[]; + + /** + * 注册的录像操作 + */ + readonly replayActions: ReplayAction[]; + + /** + * 注册的resize操作 + */ + readonly resizes: ResizeAction[]; + + /** + * 注册的天气 + */ + readonly weathers: Record; + + /** + * 脚本编辑的control函数列表 + */ + readonly controlData: ControlData; + + /** + * 注册一个animationFrame + * @param name 名称,可用来作为注销使用 + * @param needPlaying 是否只在游戏运行时才执行(在标题界面不执行) + * @param func 要执行的函数,传入time(从页面加载完毕到当前所经过的时间)作为参数 + */ + registerAnimationFrame( + name: string, + needPlaying: boolean, + func: FrameFunc + ): void; + + /** + * 注销一个animationFrame + * @param name 要注销的函数名称 + */ + unregisterAnimationFrame(name: string): void; + + /** + * 进入标题画面 + * @example core.showStartAnimate(); // 重启游戏但不重置bgm + * @param noAnimate 是否不由黑屏淡入而是立即亮屏 + * @param callback 完全亮屏后的回调函数 + */ + showStartAnimate(noAnimate?: boolean, callback?: () => void): void; + + /** + * 淡出标题画面 + * @example core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择 + * @param callback 标题画面完全淡出后的回调函数 + */ + hideStartAnimate(callback?: () => void): void; + + /** + * 判断游戏是否已经开始 + */ + isPlaying(): boolean; + + /** + * 清除游戏状态和数据 + */ + clearStatus(): void; + + /** + * 清除地图上绘制的自动寻路路线 + */ + clearAutomaticRouteNode(x: number, y: number): void; + + /** + * 停止自动寻路操作 + */ + stopAutomaticRoute(): void; + + /** + * 保存剩下的寻路,并停止 + */ + saveAndStopAutomaticRoute(): void; + + /** + * 继续剩下的自动寻路操作 + */ + continueAutomaticRoute(): void; + + /** + * 清空剩下的自动寻路列表 + */ + clearContinueAutomaticRoute(callback?: () => any): void; + + /** + * 半自动寻路,用于鼠标或手指拖动 + * @param destX 鼠标或手指的起拖点横坐标 + * @param destY 鼠标或手指的起拖点纵坐标 + * @param stepPostfix 拖动轨迹的数组表示,每项为一步的方向和目标点。 + */ + setAutomaticRoute(destX: number, destY: number, stepPostfix: Loc[]): void; + + /** + * 连续行走 + * @param steps 压缩的步伐数组,每项表示朝某方向走多少步 + */ + setAutoHeroMove(steps: CompressedStep[]): void; + + /** + * 设置行走的效果动画 + */ + setHeroMoveInterval(callback?: () => any): void; + + /** + * 每移动一格后执行的函数 + */ + moveOneStep(callback?: () => any): void; + + /** + * 尝试前进一步,如果面前不可被踏入就会直接触发该点事件 + * @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理 + * @param callback 走一步后的回调函数 + */ + moveAction(callback?: () => void): void; + + /** + * 连续前进,不撞南墙不回头 + * @example core.moveHero(); // 连续前进 + * @param direction 移动的方向,不设置就是勇士当前的方向 + * @param callback 回调函数,设置了就只走一步 + */ + moveHero(direction?: Dir, callback?: () => void): void; + + /** + * 当前是否正在移动 + */ + isMoving(): boolean; + + /** + * 停止勇士的一切行动并等待勇士停下 + * @example core.waitHeroToStop(core.vibrate); // 等待勇士停下,然后视野左右抖动1秒 + * @param callback 勇士停止后的回调函数 + */ + waitHeroToStop(callback?: () => void): void; + + /** + * 主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心 + * @example core.turnHero(); // 主角顺时针旋转,即单击主角或按下Z键的效果 + * @param direction 主角的新朝向,可为up, down, left, right, :left, :right, :back七种之一,不填视为:right + */ + turnHero(direction?: TurnDir): void; + + /** + * 瞬移到某一点 + * @param x 瞬移至的横坐标 + * @param y 瞬移至的纵坐标 + * @param ignoreSteps 忽略的步数,不填则会自动计算 + */ + moveDirectly(x: number, y: number, ignoreSteps?: number): boolean; + + /** + * 尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致 + * @example core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件 + * @param destX 目标点的横坐标 + * @param destY 目标点的纵坐标 + */ + tryMoveDirectly(destX: number, destY: number): boolean; + + /** + * 绘制主角和跟随者并重置视野到以主角为中心 + * @example core.drawHero(); // 原地绘制主角的静止帧 + * @param status 绘制状态 + * @param offset 相对主角逻辑位置的偏移量,不填视为无偏移 + * @param frame 绘制第几帧 + */ + drawHero( + status?: Exclude, + offset?: number, + frame?: number + ): void; + + /** + * 改变勇士的不透明度 + * @param opacity 要设置成的不透明度 + * @param moveMode 动画的缓动模式 + * @param time 动画时间,不填视为无动画 + * @param callback 动画执行完毕的回调函数 + */ + setHeroOpacity( + opacity: number, + moveMode?: EaseMode, + time?: any, + callback?: () => any + ): void; + + /** + * 设置游戏系统画布的偏移量 + * @param canvasId 字符串或数字,根据ts的说法应该只能填数字,但是浏览器会提供字符串的方式。 + * 但是还是建议填数字,排列顺序一般是纵深从低到高排列 + * @param x 偏移横坐标 + * @param y 偏移纵坐标 + */ + setGameCanvasTranslate( + canvasId: string | number, + x: number, + y: number + ): void; + + /** + * 加减所有游戏系统画布的偏移 + * @param x 增加的横坐标 + * @param y 增加的纵坐标 + */ + addGameCanvasTranslate(x: number, y: number): void; + + /** + * @deprecated 下一个大版本会添加新的视角控制 + * 更新大地图的可见区域 + */ + updateViewport(): void; + + /** + * @deprecated 下一个大版本会添加新的视角控制 + * 设置视野范围 + * @param px 相对大地图左上角的偏移横坐标,单位像素 + * @param py 相对大地图左上角的偏移纵坐标,单位像素 + */ + setViewport(px?: number, py?: number): void; + + /** + * @deprecated 下一个大版本会添加新的视角控制 + * 移动视野范围,这东西真的有人用吗...高级动画 + setViewport就完事了( + * @param x 移动的横坐标,单位格子 + * @param y 移动的纵坐标,单位格子 + * @param moveMode 缓动方式 + * @param time 动画时间 + * @param callback 动画完毕后的回调函数 + */ + moveViewport( + x: number, + y: number, + moveMode?: EaseMode, + time?: number, + callback?: () => void + ): void; + + /** + * 获取主角面前第n格的横坐标 + * @example core.closeDoor(core.nextX(), core.nextY(), 'yellowDoor', core.turnHero); // 在主角面前关上一扇黄门,然后主角顺时针旋转90° + * @param n 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1 + */ + nextX(n?: number): number; + + /** + * 获取主角面前第n格的纵坐标 + * @example core.jumpHero(core.nextX(2), core.nextY(2)); // 主角向前跃过一格,即跳跃靴道具的使用效果 + * @param n 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1 + */ + nextY(n?: number): number; + + /** + * 判定主角是否身处某个点的锯齿领域(取曼哈顿距离) + * @example core.nearHero(6, 6, 6); // 判定主角是否身处点(6,6)的半径为6的锯齿领域 + * @param x 领域的中心横坐标 + * @param y 领域的中心纵坐标 + * @param n 领域的半径,不填视为1 + */ + nearHero(x: number, y: number, n?: number): boolean; + + /** + * 立刻聚集所有的跟随者 + */ + gatherFollowers(): void; + + /** + * 更新跟随者坐标 + */ + updateFollowers(): void; + + /** + * 检查并执行领域、夹击、阻击事件 + */ + checkBlock(): void; + + /** + * @deprecated 使用core.updateStatusBar代替。重算并绘制地图显伤 + * @example core.updateDamage(); // 更新当前地图的显伤,绘制在显伤层(废话) + * @param floorId 地图id,不填视为当前地图。预览地图时填写 + * @param ctx 绘制到的画布,如果填写了就会画在该画布而不是显伤层 + */ + updateDamage(floorId?: FloorIds, ctx?: CtxRefer): void; + + /** + * 重绘地图显伤 + * @param ctx 绘制到的画布 + */ + drawDamage(ctx?: CtxRefer, floorId: FloorIds): void; + + /** + * 选择录像文件 + */ + chooseReplayFile(): void; + + /** + * 开始播放一个录像 + */ + startReplay(list: string[]): void; + + /** + * 更改播放状态,暂停还是播放 + */ + triggerReplay(): void; + + /** + * 暂停播放 + */ + pauseReplay(): void; + + /** + * 恢复播放 + */ + resumeReplay(): void; + + /** + * 单步播放 + */ + stepReplay(): void; + + /** + * 加速播放 + */ + speedUpReplay(): void; + + /** + * 减速播放 + */ + speedDownReplay(): void; + + /** + * 设置播放速度 + */ + setReplaySpeed(speed: number): void; + + /** + * 停止录像播放 + * @param force 是否是强制停止播放(例如点击停止播放按钮) + */ + stopReplay(force?: boolean): void; + + /** + * 回退录像播放 + */ + rewindReplay(): void; + + /** + * 是否正在播放录像 + */ + isReplaying(): boolean; + + /** + * 回放下一个操作 + */ + replay(): void; + + /** + * 注册一个录像行为 + * @param name 自定义名称,可用于注销使用 + * @param func 具体执行录像的函数,可为一个函数或插件中的函数名; + * 需要接受一个action参数,代表录像回放时的下一个操作 + * func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。 + */ + registerReplayAction(name: string, func: ReplayFunc): void; + + /** + * 注销一个录像行为 + * @param 要注销的录像行为 + */ + unregisterReplayAction(name: string): void; + + /** + * 自动存档 + * @param removeLast 是否移除位于自动存档栈底部的存档 + */ + autosave(removeLast?: any): void; + + /** + * 实际进行自动存档 + */ + checkAutosave(): void; + + /** + * 实际进行存读档事件 + */ + doSL(id: string, type: SLType): void; + + /** + * 同步存档到服务器 + * @param type 同步的类型,填all表示所有都同步,否则只同步当前存档 + */ + syncSave(type?: 'all'): void; + + /** + * 从服务器加载存档 + */ + syncLoad(): void; + + /** + * 存档到本地 + */ + saveData(): Save; + + /** + * 从本地读档 + */ + loadData(data: Save, callback?: () => void): void; + + /** + * 获得某个存档内容 + */ + getSave(index: number, callback?: (data?: Save) => void): void; + + /** + * 获得某些存档内容 + */ + getSaves(ids: number, callback?: (data?: Save) => void): void; + getSaves( + ids: number[], + callback?: (data?: Record) => void + ): void; + + /** + * 获得所有存档内容 + */ + getAllSaves(callback?: (data?: Save[]) => void): void; + + /** + * 获得所有存在存档的存档位 + */ + getSaveIndexes(callback?: (data: Record) => void): void; + + /** + * 判断某个存档位是否存在存档 + */ + hasSave(index: number): boolean; + + /** + * 删除某个存档 + */ + removeSave(index: number, callback?: () => void): void; + + /** + * @deprecated + * 设置主角的某个属性 + * @example core.setStatus('loc', {x : 0, y : 0, direction : 'up'}); // 设置主角位置为地图左上角,脸朝上 + * @param name 属性名 + * @param value 属性的新值 + */ + setStatus(name: K, value: HeroStatus[K]): void; + + /** + * @deprecated + * 增减主角的某个属性,等价于core.setStatus(name, core.getStatus(name) + value) + * @example core.addStatus('name', '酱'); // 在主角的名字后加一个“酱”字 + * @param name 属性名 + * @param value 属性的增量,请注意旧量和增量中只要有一个是字符串就会把两者连起来成为一个更长的字符串 + */ + addStatus>( + name: K, + value: HeroStatus[K] + ): void; + + /** + * @deprecated + * 读取主角的某个属性,不包括百分比修正 + * @example core.getStatus('loc'); // 读取主角的坐标和朝向 + * @param name 属性名 + * @returns 属性值 + */ + getStatus(name: K): HeroStatus[K]; + + /** + * @deprecated + * 从status中获得属性,如果不存在则从勇士属性中获取 + * @param status 要从中获取的属性对象 + * @param name 属性名 + */ + getStatusOrDefault( + status?: DeepPartial, + name?: K + ): HeroStatus[K]; + + /** + * @deprecated + * 计算主角的某个属性,包括百分比修正 + * @example core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值 + * @param name 属性名,注意只能用于数值类属性 + */ + getRealStatus(name: K): number; + + /** + * @deprecated + * 从status中获得增幅后的属性,如果不存在则从勇士属性中获取 + * @param status 要从中获取的属性对象 + * @param name 属性名 + */ + getRealStatusOrDefault( + status?: DeepPartial, + name?: K + ): number; + + /** + * @deprecated + * 获得勇士原始属性(无装备和衰弱影响) + * @param name 获取的属性名 + */ + getNakedStatus(name?: keyof NumbericHeroStatus): number; + + /** + * 设置主角某个属性的百分比修正倍率,初始值为1, + * 倍率存放在flag: `__${name}_${buff}__` 中 + * @example core.setBuff('atk', 0.5); // 主角能发挥出的攻击力减半 + * @param name 属性名,注意只能用于数值类属性 + * @param value 新的百分比修正倍率,不填(效果上)视为1 + */ + setBuff(name: K, value?: number): void; + + /** + * 增减主角某个属性的百分比修正倍率,加减法叠加和抵消。等价于 core.setBuff(name, core.getBuff(name) + value) + * @example core.addBuff('atk', -0.1); // 主角获得一层“攻击力减一成”的负面效果 + * @param name 属性名,注意只能用于数值类属性 + * @param value 倍率的增量 + */ + addBuff(name: K, value: number): void; + + /** + * 读取主角某个属性的百分比修正倍率,初始值为1 + * @example core.getBuff('atk'); // 主角当前能发挥出多大比例的攻击力 + * @param name 属性的英文名 + */ + getBuff(name: keyof NumbericHeroStatus): number; + + /** + * 设置勇士位置 + * 值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制; + * 如需立刻重新绘制地图还需调用:core.clearMap('hero'); core.drawHero(); 来对界面进行更新。 + * @example core.setHeroLoc('x', 5) // 将勇士当前位置的横坐标设置为5。 + * @param name 要设置的坐标属性 + * @param value 新值 + * @param noGather 是否聚集跟随者 + */ + setHeroLoc(name: 'x' | 'y', value: number, noGather?: boolean): void; + setHeroLoc(name: 'direction', value: Dir, noGather?: boolean): void; + + /** + * 获取主角的位置,朝向 + * @example core.getHeroLoc(); // 获取主角的位置和朝向 + * @param name 要读取横坐标还是纵坐标还是朝向还是都读取 + */ + getHeroLoc(): DiredLoc; + getHeroLoc(name: K): DiredLoc[K]; + + /** + * 根据级别的数字获取对应的名称,后者定义在全塔属性 + * @example core.getLvName(); // 获取主角当前级别的名称,如“下级佣兵” + * @param lv 级别的数字,不填则视为主角当前的级别 + * @returns 级别的名称,如果不存在就还是返回字符串类型的数字 + */ + getLvName(lv?: number): string; + + /** + * 获得下次升级需要的经验值。 + * 升级扣除模式下会返回经验差值;非扣除模式下会返回总共需要的经验值。 + * 如果无法进行下次升级,返回null。 + */ + getNextLvUpNeed(): number | null; + + /** + * 设置一个flag变量 + * @example core.setFlag('poison', true); // 令主角中毒 + * @param name 变量名,支持中文,这东西用中文就是不规范( + * @param value 变量的新值,不填或填null视为删除 + */ + setFlag(name: string, value?: any): void; + + /** + * 增减一个flag变量,等价于 core.setFlag(name, core.getFlag(name, 0) + value) + * @example core.addFlag('hatred', 1); // 增加1点仇恨值 + * @param name 变量名,支持中文 + * @param value 变量的增量 + */ + addFlag(name: string, value: number | string): void; + + /** + * 获取一个flag变量 + * @param name 变量名,支持中文,这东西用中文就是不规范( + * @param defaultValue 当变量不存在时的返回值,可选(事件流中默认填0)。 + * @returns flags[name] ?? defaultValue + */ + getFlag(name: string, defaultValue?: T): T; + + /** + * 判定一个flag变量是否不为falsy + * @example core.hasFlag('poison'); // 判断主角当前是否中毒 + * @param name 变量名,支持中文,这东西用中文就是不规范( + */ + hasFlag(name: string): boolean; + + /** + * 删除某个flag + * @param name 要删除的变量名 + */ + removeFlag(name: string): void; + + /** + * 设置某个独立开关 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param name 独立开关的名称 + * @param value 要设置成的值 + */ + setSwitch( + x?: number, + y?: number, + floorId?: FloorIds, + name?: string, + value?: any + ): void; + + /** + * 获得某个独立开关 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param name 独立开关的名称 + * @param value 默认值 + */ + getSwitch( + x?: number, + y?: number, + floorId?: FloorIds, + name?: string, + defaultValue?: T + ): T; + + /** + * 增加某个独立开关 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param name 独立开关的名称 + * @param value 增加的值 + */ + addSwitch( + x?: number, + y?: number, + floorId?: FloorIds, + name?: string, + value?: number | string + ): void; + + /** + * 是否存在某个独立开关 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param name 独立开关的名称 + */ + hasSwitch( + x?: number, + y?: number, + floorId?: FloorIds, + name?: string + ): boolean; + + /** + * 删除某个独立开关 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param name 独立开关的名称 + */ + removeSwitch( + x?: number, + y?: number, + floorId?: FloorIds, + name?: string + ): void; + + /** + * 锁定用户控制,常常用于事件处理 + */ + lockControl(): void; + + /** + * 解锁用户控制 + */ + unlockControl(): void; + + /** + * 开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。 + * 此模式下不可回放录像和上传成绩。 + */ + debug(): void; + + /** + * 清空录像折叠信息 + */ + clearRouteFolding(): void; + + /** + * 检查录像折叠信息 + */ + checkRouteFolding(): void; + + /** + * 获得映射文件名 + */ + getMappedName(name: K): NameMap[K]; + + /** + * 设置天气,不计入存档。如需长期生效请使用core.events._action_setWeather()函数 + * @example core.setWeather('fog', 10); // 设置十级大雾天 + * @param type 新天气的类型,不填视为无天气 + * @param level 新天气(晴天除外)的级别,必须为不大于10的正整数,不填视为5 + */ + setWeather(type?: string, level?: WeatherLevel): void; + + /** + * 注册一个天气 + * @param name 天气的名称 + * @param initFunc 初始化函数 + * @param frameFunc 每帧执行的函数 + */ + registerWeather( + name: string, + initFunc: WeatherAction['initFunc'], + frameFunc?: WeatherAction['frameFunc'] + ): void; + + /** + * 注销一个天气 + * @param name 要注销的天气名称 + */ + unregisterWeather(name: string): void; + + /** + * 更改画面色调,不计入存档。如需长期生效请使用core.events._action_setCurtain()函数 + * @example core.setCurtain(); // 恢复画面色调,用时四分之三秒 + * @param color 颜色数组,不填视为[0, 0, 0, 0] + * @param time 渐变时间,单位为毫秒。不填视为750ms + * @param moveMode 缓动模式 + * @param callback 更改完毕后的回调函数 + */ + setCurtain( + color?: RGBArray, + time?: number, + moveMode?: EaseMode, + callback?: () => void + ): void; + + /** + * 画面闪烁 + * @example core.screenFlash([255, 0, 0, 1], 3); // 红屏一闪而过 + * @param color 颜色数组 + * @param time 单次闪烁时长,实际闪烁效果为先花其三分之一的时间渐变到目标色调,再花剩余三分之二的时间渐变回去 + * @param times 闪烁的总次数,不填或填0都视为1 + * @param moveMode 缓动模式 + * @param callback 闪烁全部完毕后的回调函数 + */ + screenFlash( + color: RGBArray, + time: number, + times?: number, + moveMode?: string, + callback?: () => void + ): void; + + /** + * + * 播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__ + * @example core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟 + * @param bgm 背景音乐的文件名,支持全塔属性中映射前的中文名 + * @param startTime 跳过前多少秒 + */ + playBgm(bgm: BgmIds | NameMapIn, startTime?: number): void; + + /** + * @deprecated + * 暂停背景音乐的播放 + */ + pauseBgm(): void; + + /** + * @deprecated + * 恢复背景音乐的播放 + */ + resumeBgm(resumeTime?: number): void; + + /** + * @deprecated + * 设置音乐图标的开启关闭状态 + */ + setMusicBtn(): void; + + /** + * @deprecated + * 开启或关闭背景音乐的播放 + */ + triggerBgm(): void; + + /** + * + * 播放一个音效 + * @param sound 音效名 + * @param pitch 音调,同时会修改播放速度,100为原速 + * @param callback 回调函数 + * @returns 音效的唯一标识符,用于停止音效等操作 + */ + playSound( + sound: SoundIds | NameMapIn, + pitch?: number, + callback?: () => void + ): number; + + /** + * @deprecated + * 停止音频 + * @param id 停止的音频标识符,不填则停止所有 + */ + stopSound(id?: number): void; + + /** + * @deprecated + * 获得正在播放的所有音效的id列表 + * @param name 要获得的音效名 + */ + getPlayingSounds(name?: SoundIds | NameMapIn): number[]; + + /** + * @deprecated + * 检查bgm状态,没有播放的话就播放 + */ + checkBgm(): void; + + /** + * 设置屏幕放缩 + * @param delta 在所有可用放缩数组中增加的下标数 + */ + setDisplayScale(delta: number): void; + + /** + * 清空状态栏 + */ + clearStatusBar(): void; + + /** + * 更新状态栏和地图显伤,会在下一个动画帧更新 + * @param doNotCheckAutoEvents 是否不检查自动事件 + * @param immediate 是否立刻刷新,而非延迟到下一动画帧刷新 + */ + updateStatusBar(doNotCheckAutoEvents?: boolean, immediate?: boolean): void; + + /** + * 显示状态栏 + */ + showStatusBar(): void; + + /** + * 隐藏状态栏 + * @param showToolbox 是否显示工具栏 + */ + hideStatusBar(showToolbox?: boolean): void; + + /** + * 改变工具栏为按钮1-8 + * @param useButton 是否显示为按钮1-8 + */ + setToolbarButton(useButton?: boolean): void; + + /** + * 注册一个resize函数 + * @param name 名称,可供注销使用 + * @param func 游戏画面发生变化时执行的函数 + */ + registerResize(name: string, func: ResizeFunc): void; + + /** + * 注销一个resize函数 + */ + unregisterResize(name: string): void; + + /** + * 屏幕分辨率改变后执行的函数 + */ + resize(): void; + + _drawHero_updateViewport(x: number, y: number, offset: Loc): void; + _moveAction_moving(callback: () => void): void; +} + +declare const control: new () => Control; diff --git a/control.js b/control.js new file mode 100644 index 0000000..8d5f3e5 --- /dev/null +++ b/control.js @@ -0,0 +1,3693 @@ + +/* +control.js:游戏主要逻辑控制 +主要负责status相关内容,以及各种变量获取/存储 +寻路算法和人物行走也在此文件内 + */ + +"use strict"; + +function control() { + this._init(); +} + +control.prototype._init = function () { + this.controldata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.control; + this.renderFrameFuncs = []; + this.replayActions = []; + this.weathers = {}; + this.resizes = []; + this.noAutoEvents = true; + this.updateNextFrame = false; + // --- 注册系统的animationFrame + this.registerAnimationFrame("totalTime", false, this._animationFrame_totalTime); + this.registerAnimationFrame("autoSave", true, this._animationFrame_autoSave); + this.registerAnimationFrame("globalAnimate", true, this._animationFrame_globalAnimate); + this.registerAnimationFrame("animate", true, this._animationFrame_animate); + this.registerAnimationFrame("heroMoving", true, this._animationFrame_heroMoving); + this.registerAnimationFrame("weather", true, this._animationFrame_weather); + this.registerAnimationFrame("tip", true, this._animateFrame_tip); + this.registerAnimationFrame("parallelDo", false, this._animationFrame_parallelDo); + // --- 注册系统的天气 + this.registerWeather("rain", this._weather_rain, this._animationFrame_weather_rain); + this.registerWeather("snow", this._weather_snow, this._animationFrame_weather_snow); + this.registerWeather("fog", this._weather_fog, this.__animateFrame_weather_image); + this.registerWeather("cloud", this._weather_cloud, this.__animateFrame_weather_image); + this.registerWeather("sun", this._weather_sun, this._animationFrame_weather_sun); + // --- 注册系统的replay + this.registerReplayAction("move", this._replayAction_move); + this.registerReplayAction("item", this._replayAction_item); + this.registerReplayAction("equip", this._replayAction_equip); + this.registerReplayAction("unEquip", this._replayAction_unEquip); + this.registerReplayAction("saveEquip", this._replayAction_saveEquip); + this.registerReplayAction("loadEquip", this._replayAction_loadEquip); + this.registerReplayAction("fly", this._replayAction_fly); + this.registerReplayAction("shop", this._replayAction_shop); + this.registerReplayAction("turn", this._replayAction_turn); + this.registerReplayAction("getNext", this._replayAction_getNext); + this.registerReplayAction("moveDirectly", this._replayAction_moveDirectly); + this.registerReplayAction("key", this._replayAction_key); + this.registerReplayAction("click", this._replayAction_click); + this.registerReplayAction("ignoreInput", this._replayAction_ignoreInput); + this.registerReplayAction("no", this._replayAction_no); + // --- 注册系统的resize + this.registerResize("gameGroup", this._resize_gameGroup); + this.registerResize("canvas", this._resize_canvas); + this.registerResize("statusBar", this._resize_statusBar); + this.registerResize("status", this._resize_status); + this.registerResize("toolBar", this._resize_toolBar); + this.registerResize("tools", this._resize_tools); +} + +// ------ requestAnimationFrame 相关 ------ // + +////// 注册一个 animationFrame ////// +// name:名称,可用来作为注销使用;needPlaying:是否只在游戏运行时才执行(在标题界面不执行) +// func:要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数 +control.prototype.registerAnimationFrame = function (name, needPlaying, func) { + this.unregisterAnimationFrame(name); + this.renderFrameFuncs.push({ name: name, needPlaying: needPlaying, func: func }); +} + +////// 注销一个 animationFrame ////// +control.prototype.unregisterAnimationFrame = function (name) { + this.renderFrameFuncs = this.renderFrameFuncs.filter(function (x) { return x.name != name; }); +} + +////// 设置requestAnimationFrame ////// +control.prototype._setRequestAnimationFrame = function () { + this._checkRequestAnimationFrame(); + core.animateFrame.totalTime = Math.max(core.animateFrame.totalTime, core.getLocalStorage('totalTime', 0)); + var loop = function (timestamp) { + core.control.renderFrameFuncs.forEach(function (b) { + if (b.func) { + try { + if (core.isPlaying() || !b.needPlaying) + core.doFunc(b.func, core.control, timestamp); + } + catch (e) { + console.error(e); + console.error("ERROR in requestAnimationFrame[" + b.name + "]:已自动注销该项。"); + core.unregisterAnimationFrame(b.name); + } + } + }) + window.requestAnimationFrame(loop); + } + window.requestAnimationFrame(loop); +} + +control.prototype._checkRequestAnimationFrame = function () { + (function () { + var lastTime = 0; + var vendors = ['webkit', 'moz']; + for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // Webkit中此取消方法的名字变了 + window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function (callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16.7 - (currTime - lastTime)); + var id = window.setTimeout(function () { + callback(currTime + timeToCall); + }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function (id) { + clearTimeout(id); + }; + } + }()); +} + +control.prototype._animationFrame_totalTime = function (timestamp) { + core.animateFrame.totalTime += timestamp - core.animateFrame.totalTimeStart; + core.animateFrame.totalTimeStart = timestamp; + if (core.isPlaying()) { + core.status.hero.statistics.totalTime = core.animateFrame.totalTime; + core.status.hero.statistics.currTime += timestamp - (core.status.hero.statistics.start || timestamp); + core.status.hero.statistics.start = timestamp; + } +} + +control.prototype._animationFrame_autoSave = function (timestamp) { + if (timestamp - core.saves.autosave.time <= 5000) return; + core.control.checkAutosave(); + core.saves.autosave.time = timestamp; +} + +control.prototype._animationFrame_globalAnimate = function (timestamp) { + if (timestamp - core.animateFrame.globalTime <= core.values.animateSpeed) return; + core.status.globalAnimateStatus++; + if (core.status.floorId) { + // Global Animate + core.status.globalAnimateObjs.forEach(function (block) { + core.drawBlock(block, core.status.globalAnimateStatus); + }); + + // Global floor images + core.maps._drawFloorImages(core.status.floorId, core.canvas.bg, 'bg', core.status.floorAnimateObjs || [], core.status.globalAnimateStatus); + core.maps._drawFloorImages(core.status.floorId, core.canvas.fg, 'fg', core.status.floorAnimateObjs || [], core.status.globalAnimateStatus); + + // Global Autotile Animate + core.status.autotileAnimateObjs.forEach(function (block) { + core.maps._drawAutotileAnimate(block, core.status.globalAnimateStatus); + }); + + // Global hero animate + if ((core.status.hero || {}).animate && core.status.heroMoving == 0 && main.mode == 'play' && !core.status.preview.enabled) { + core.drawHero('stop', null, core.status.globalAnimateStatus); + } + } + // Box animate + core.drawBoxAnimate(); + core.animateFrame.globalTime = timestamp; +} + +control.prototype._animationFrame_animate = function (timestamp) { + if (timestamp - core.animateFrame.animateTime < 50 || !core.status.animateObjs || core.status.animateObjs.length == 0) return; + core.clearMap('animate'); + // 更新帧 + for (var i = 0; i < core.status.animateObjs.length; i++) { + var obj = core.status.animateObjs[i]; + if (obj.index == obj.animate.frames.length) { + (function (callback) { + setTimeout(function () { + if (callback) callback(); + }); + })(obj.callback); + } + } + core.status.animateObjs = core.status.animateObjs.filter(function (obj) { + return obj.index < obj.animate.frames.length; + }); + core.status.animateObjs.forEach(function (obj) { + if (obj.hero) { + core.maps._drawAnimateFrame('animate', obj.animate, core.status.heroCenter.px, core.status.heroCenter.py, obj.index++); + } else { + core.maps._drawAnimateFrame('animate', obj.animate, obj.centerX, obj.centerY, obj.index++); + } + }); + core.animateFrame.animateTime = timestamp; +} + +control.prototype._animationFrame_heroMoving = function (timestamp) { + if (core.status.heroMoving <= 0) return; + // 换腿 + if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) { + core.animateFrame.leftLeg = !core.animateFrame.leftLeg; + core.animateFrame.moveTime = timestamp; + } + core.drawHero(core.animateFrame.leftLeg ? 'leftFoot' : 'rightFoot', 4 * core.status.heroMoving); +} + +control.prototype._animationFrame_weather = function (timestamp) { + var weather = core.animateFrame.weather, type = weather.type; + if (!core.dymCanvas.weather || !core.control.weathers[type] || !core.control.weathers[type].frameFunc) return; + try { + core.doFunc(core.control.weathers[type].frameFunc, core.control, timestamp, core.animateFrame.weather.level); + } catch (e) { + console.error(e); + console.error("ERROR in weather[" + type + "]:已自动注销该项。"); + core.unregisterWeather(type); + } +} + +control.prototype._animationFrame_weather_rain = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + ctx.strokeStyle = 'rgba(174,194,224,0.8)'; + ctx.lineWidth = 1; + ctx.lineCap = 'round'; + + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.beginPath(); + ctx.moveTo(p.x - ox, p.y - oy); + ctx.lineTo(p.x + p.l * p.xs - ox, p.y + p.l * p.ys - oy); + ctx.stroke(); + + p.x += p.xs; + p.y += p.ys; + if (p.x > core.bigmap.width * 32 || p.y > core.bigmap.height * 32) { + p.x = Math.random() * core.bigmap.width * 32; + p.y = -10; + } + + }); + + ctx.fill(); + core.animateFrame.weather.time = timestamp; +} + +control.prototype._animationFrame_weather_snow = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + ctx.fillStyle = "rgba(255, 255, 255, 0.8)"; + ctx.beginPath(); + core.animateFrame.weather.data = core.animateFrame.weather.data || 0; + core.animateFrame.weather.data += 0.01; + + var angle = core.animateFrame.weather.data; + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.moveTo(p.x - ox, p.y - oy); + ctx.arc(p.x - ox, p.y - oy, p.r, 0, Math.PI * 2, true); + // update + p.x += Math.sin(angle) * core.animateFrame.weather.level; + p.y += Math.cos(angle + p.d) + 1 + p.r / 2; + if (p.x > core.bigmap.width * 32 + 5 || p.x < -5 || p.y > core.bigmap.height * 32) { + if (Math.random() > 1 / 3) { + p.x = Math.random() * core.bigmap.width * 32; + p.y = -10; + } + else { + if (Math.sin(angle) > 0) + p.x = -5; + else + p.x = core.bigmap.width * 32 + 5; + p.y = Math.random() * core.bigmap.height * 32; + } + } + }); + ctx.fill(); + core.animateFrame.weather.time = timestamp; +} + +control.prototype.__animateFrame_weather_image = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var node = core.animateFrame.weather.nodes[0]; + var image = node.image; + if (!image) return; + core.clearMap('weather'); + core.setAlpha('weather', node.level / 500); + var wind = 1.5; + var width = image.width, height = image.height; + node.x += node.dx * wind; + node.y += (2 * node.dy - 1) * wind; + if (node.x + 3 * width <= core._PX_) { + node.x += 4 * width; + while (node.x > 0) node.x -= width; + } + node.dy += node.delta; + if (node.dy >= 1) { + node.delta = -0.001; + } else if (node.dy <= 0) { + node.delta = 0.001; + } + if (node.y + 3 * height <= core._PY_) { + node.y += 4 * height; + while (node.y > 0) node.y -= height; + } + else if (node.y >= 0) { + node.y -= height; + } + for (var i = 0; i < 3; ++i) { + for (var j = 0; j < 3; ++j) { + if (node.x + (i + 1) * width <= 0 || node.x + i * width >= core._PX_ + || node.y + (j + 1) * height <= 0 || node.y + j * height >= core._PY_) + continue; + core.drawImage('weather', image, node.x + i * width, node.y + j * height); + } + } + core.setAlpha('weather', 1); + core.animateFrame.weather.time = timestamp; +} + +control.prototype._animationFrame_weather_sun = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var node = core.animateFrame.weather.nodes[0]; + var opacity = node.opacity + node.delta; + if (opacity > level / 10 + 0.3 || opacity < level / 10 - 0.3) + node.delta = -node.delta; + node.opacity = opacity; + core.setOpacity('weather', core.clamp(opacity, 0, 1)); + core.animateFrame.weather.time = timestamp; +} + +control.prototype._animateFrame_tip = function (timestamp) { + if (core.animateFrame.tip == null) return; + var tip = core.animateFrame.tip; + if (timestamp - tip.time <= 30) return; + var delta = timestamp - tip.time; + tip.time = timestamp; + + core.setFont('data', "16px Arial"); + core.setTextAlign('data', 'left'); + core.clearMap('data', 0, 0, core._PX_, 50); + core.ui._drawTip_drawOne(tip); + if (tip.stage == 1) { + tip.opacity += 0.05; + if (tip.opacity >= 0.6) { + tip.stage = 2; + tip.displayTime = 0; + } + } else if (tip.stage == 2) { + tip.displayTime += delta; + if (tip.displayTime >= 1000) tip.stage = 3; + } else tip.opacity -= 0.05; + + if (tip.opacity <= 0) { + core.animateFrame.tip = null; + } +} + +control.prototype._animationFrame_parallelDo = function (timestamp) { + core.control.controldata.parallelDo(timestamp); +} + +// ------ 标题界面的处理 ------ // + +////// 显示游戏开始界面 ////// +control.prototype.showStartAnimate = function (noAnimate, callback) { + this._showStartAnimate_resetDom(); + if (core.flags.startUsingCanvas || noAnimate) + return this._showStartAnimate_finished(core.flags.startUsingCanvas, callback); + core.hideWithAnimate(core.dom.startTop, 20, function () { + core.control._showStartAnimate_finished(false, callback); + }); +} + +control.prototype._showStartAnimate_resetDom = function () { + core.dom.startPanel.style.opacity = 1; + core.dom.startPanel.style.display = "block"; + core.dom.startTop.style.opacity = 1; + core.dom.startTop.style.display = "block"; + core.dom.startButtonGroup.style.display = 'none'; + core.dom.startButtons.style.display = 'block'; + core.dom.levelChooseButtons.style.display = 'none'; + core.status.played = false; + core.clearStatus(); + core.clearMap('all'); + core.dom.musicBtn.style.display = 'block'; + core.setMusicBtn(); + // 重置音量 + core.events.setVolume(1, 0); + core.updateStatusBar(); +} + +control.prototype._showStartAnimate_finished = function (start, callback) { + core.dom.startTop.style.display = 'none'; + core.dom.startButtonGroup.style.display = 'block'; + main.selectedButton = null; + main.selectButton(0); + if (start) core.startGame(); + if (callback) callback(); +} + +////// 隐藏游戏开始界面 ////// +control.prototype.hideStartAnimate = function (callback) { + core.hideWithAnimate(core.dom.startPanel, 20, callback); +} + +////// 游戏是否已经开始 ////// +control.prototype.isPlaying = function () { + return core.status.played; +} + +////// 清除游戏状态和数据 ////// +control.prototype.clearStatus = function () { + // 停止各个Timeout和Interval + for (var i in core.timeout) { + clearTimeout(core.timeout[i]); + core.timeout[i] = null; + } + for (var i in core.interval) { + clearInterval(core.interval[i]); + core.interval[i] = null; + } + core.status = {}; + core.clearStatusBar(); + core.deleteAllCanvas(); + core.status.played = false; +} + +control.prototype._initStatistics = function (totalTime) { + if (!core.isset(core.status.hero.statistics)) + core.status.hero.statistics = { + 'totalTime': totalTime, + 'currTime': 0, + 'hp': 0, + "battle": 0, + 'money': 0, + 'exp': 0, + 'battleDamage': 0, + 'poisonDamage': 0, + 'extraDamage': 0, + 'moveDirectly': 0, + 'ignoreSteps': 0, + } +} + +// ------ 自动寻路,人物行走 ------ // + +////// 清除自动寻路路线 ////// +control.prototype.clearAutomaticRouteNode = function (x, y) { + core.clearMap('route', x * 32 + 5 - core.status.automaticRoute.offsetX, y * 32 + 5 - core.status.automaticRoute.offsetY, 27, 27); +} + +////// 停止自动寻路操作 ////// +control.prototype.stopAutomaticRoute = function () { + if (!core.status.played) return; + core.status.automaticRoute.autoHeroMove = false; + core.status.automaticRoute.autoStep = 0; + core.status.automaticRoute.destStep = 0; + core.status.automaticRoute.movedStep = 0; + core.status.automaticRoute.autoStepRoutes = []; + core.status.automaticRoute.destX = null; + core.status.automaticRoute.destY = null; + core.status.automaticRoute.lastDirection = null; + core.status.heroStop = true; + if (core.status.automaticRoute.moveStepBeforeStop.length == 0) + core.deleteCanvas('route'); +} + +////// 保存剩下的寻路,并停止 ////// +control.prototype.saveAndStopAutomaticRoute = function () { + var automaticRoute = core.status.automaticRoute; + if (automaticRoute.moveStepBeforeStop.length == 0) { + automaticRoute.moveStepBeforeStop = automaticRoute.autoStepRoutes.slice(automaticRoute.autoStep - 1); + if (automaticRoute.moveStepBeforeStop.length >= 1) + automaticRoute.moveStepBeforeStop[0].step -= automaticRoute.movedStep; + } + this.stopAutomaticRoute(); +} + +////// 继续剩下的自动寻路操作 ////// +control.prototype.continueAutomaticRoute = function () { + // 此函数只应由events.afterOpenDoor和events.afterBattle调用 + var moveStep = core.status.automaticRoute.moveStepBeforeStop; + //core.status.automaticRoute.moveStepBeforeStop = []; + if (moveStep.length === 0 || (moveStep.length === 1 && moveStep[0].step === 1)) { + core.status.automaticRoute.moveStepBeforeStop = []; + } + else { + core.setAutoHeroMove(moveStep); + } +} + +////// 清空剩下的自动寻路列表 ////// +control.prototype.clearContinueAutomaticRoute = function (callback) { + core.deleteCanvas('route'); + core.status.automaticRoute.moveStepBeforeStop = []; + if (callback) callback(); +} + +////// 设置自动寻路路线 ////// +control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { + /*if (!core.status.played ||core.status.lockControl) return; + if (this._setAutomaticRoute_isMoving(destX, destY)) return; + if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix)) return; + if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix)) return; + // 找寻自动寻路路线 + var moveStep = core.automaticRoute(destX, destY); + if (moveStep.length == 0 && (destX != core.status.hero.loc.x || destY != core.status.hero.loc.y || stepPostfix.length == 0)) + return; + moveStep = moveStep.concat(stepPostfix); + core.status.automaticRoute.destX = destX; + core.status.automaticRoute.destY = destY; + this._setAutomaticRoute_drawRoute(moveStep); + this._setAutomaticRoute_setAutoSteps(moveStep); + // 立刻移动 + core.setAutoHeroMove();*/ +} + +control.prototype._setAutomaticRoute_isMoving = function (destX, destY) { + if (core.status.automaticRoute.autoHeroMove) { + var lastX = core.status.automaticRoute.destX, lastY = core.status.automaticRoute.destY; + core.stopAutomaticRoute(); + // 双击瞬移 + if (lastX == destX && lastY == destY) { + core.status.automaticRoute.moveDirectly = true; + setTimeout(function () { + if (core.status.automaticRoute.moveDirectly && core.status.heroMoving == 0) { + core.control.tryMoveDirectly(destX, destY); + } + core.status.automaticRoute.moveDirectly = false; + }, core.values.moveSpeed); + } + return true; + } + return false; +} + +control.prototype._setAutomaticRoute_isTurning = function (destX, destY, stepPostfix) { + if (destX == core.status.hero.loc.x && destY == core.status.hero.loc.y && stepPostfix.length == 0) { + if (core.timeout.turnHeroTimeout == null) { + var routeLength = core.status.route.length; + core.timeout.turnHeroTimeout = setTimeout(function () { + if (core.status.route.length == routeLength) core.turnHero(); + clearTimeout(core.timeout.turnHeroTimeout); + core.timeout.turnHeroTimeout = null; + }, 250); + } + else { + clearTimeout(core.timeout.turnHeroTimeout); + core.timeout.turnHeroTimeout = null; + core.getNextItem(); + } + return true; + } + if (core.timeout.turnHeroTimeout != null) return true; + return false; +} + +control.prototype._setAutomaticRoute_clickMoveDirectly = function (destX, destY, stepPostfix) { + // 单击瞬间移动 + if (core.status.heroStop && core.status.heroMoving == 0) { + if (stepPostfix.length <= 1 && !core.hasFlag('__noClickMove__') && core.control.tryMoveDirectly(destX, destY)) + return true; + } + return false; +} + +control.prototype._setAutomaticRoute_drawRoute = function (moveStep) { + // 计算绘制区域的宽高,并尽可能小的创建route层 + var sx = core.bigmap.width * 32, sy = core.bigmap.height * 32, dx = 0, dy = 0; + moveStep.forEach(function (t) { + sx = Math.min(sx, t.x * 32); dx = Math.max(dx, t.x * 32); + sy = Math.min(sy, t.y * 32); dy = Math.max(dy, t.y * 32); + }); + core.status.automaticRoute.offsetX = sx; + core.status.automaticRoute.offsetY = sy; + var ctx = core.createCanvas('route', sx - core.bigmap.offsetX, sy - core.bigmap.offsetY, dx - sx + 32, dy - sy + 32, 95); + ctx.fillStyle = '#bfbfbf'; + ctx.strokeStyle = '#bfbfbf'; + ctx.lineWidth = 8; + for (var m = 0; m < moveStep.length; m++) { + if (m == moveStep.length - 1) { + ctx.fillRect(moveStep[m].x * 32 + 10 - sx, moveStep[m].y * 32 + 10 - sy, 12, 12); + } + else { + ctx.beginPath(); + var cx = moveStep[m].x * 32 + 16 - sx, cy = moveStep[m].y * 32 + 16 - sy; + var currDir = moveStep[m].direction, nextDir = moveStep[m + 1].direction; + ctx.moveTo(cx - core.utils.scan[currDir].x * 11, cy - core.utils.scan[currDir].y * 11); + ctx.lineTo(cx, cy); + ctx.lineTo(cx + core.utils.scan[nextDir].x * 11, cy + core.utils.scan[nextDir].y * 11); + ctx.stroke(); + } + } +} + +control.prototype._setAutomaticRoute_setAutoSteps = function (moveStep) { + // 路线转autoStepRoutes + var step = 0, currStep = null; + moveStep.forEach(function (t) { + var dir = t.direction; + if (currStep == null || currStep == dir) + step++; + else { + core.status.automaticRoute.autoStepRoutes.push({ 'direction': currStep, 'step': step }); + step = 1; + } + currStep = dir; + }); + core.status.automaticRoute.autoStepRoutes.push({ 'direction': currStep, 'step': step }); +} + +////// 设置勇士的自动行走路线 ////// +control.prototype.setAutoHeroMove = function (steps) { + steps = steps || core.status.automaticRoute.autoStepRoutes; + if (steps.length == 0) return; + core.status.automaticRoute.autoStepRoutes = steps; + core.status.automaticRoute.autoHeroMove = true; + core.status.automaticRoute.autoStep = 1; + core.status.automaticRoute.destStep = steps[0].step; + core.moveHero(steps[0].direction); +} + +////// 设置行走的效果动画 ////// +control.prototype.setHeroMoveInterval = function (callback) { + if (core.status.heroMoving > 0) return; + if (core.status.replay.speed == 24) { + if (callback) callback(); + return; + } + + core.status.heroMoving = 1; + + var toAdd = 1; + if (core.status.replay.speed > 3) toAdd = 2; + if (core.status.replay.speed > 6) toAdd = 4; + if (core.status.replay.speed > 12) toAdd = 8; + + core.interval.heroMoveInterval = window.setInterval(function () { + core.status.heroMoving += toAdd; + if (core.status.heroMoving >= 8) { + clearInterval(core.interval.heroMoveInterval); + core.status.heroMoving = 0; + if (callback) callback(); + } + }, core.values.moveSpeed / 8 * toAdd / core.status.replay.speed); +} + +////// 每移动一格后执行的事件 ////// +control.prototype.moveOneStep = function (callback) { + return this.controldata.moveOneStep(callback); +} + +////// 实际每一步的行走过程 ////// +control.prototype.moveAction = function (callback) { + if (core.status.heroMoving > 0) return; + var noPass = core.noPass(core.nextX(), core.nextY()), canMove = core.canMoveHero(); + // 下一个点如果不能走 + if (noPass || !canMove) return this._moveAction_noPass(canMove, callback); + this._moveAction_moving(callback); +} + +control.prototype._moveAction_noPass = function (canMove, callback) { + core.status.route.push(core.getHeroLoc('direction')); + core.status.automaticRoute.moveStepBeforeStop = []; + core.status.automaticRoute.lastDirection = core.getHeroLoc('direction'); + if (canMove) core.trigger(core.nextX(), core.nextY()); + core.drawHero(); + + if (core.status.automaticRoute.moveStepBeforeStop.length == 0) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + if (callback) callback(); +} + +control.prototype._moveAction_moving = function (callback) { + core.setHeroMoveInterval(function () { + core.setHeroLoc('x', core.nextX(), true); + core.setHeroLoc('y', core.nextY(), true); + + var direction = core.getHeroLoc('direction'); + core.control._moveAction_popAutomaticRoute(); + core.status.route.push(direction); + + core.moveOneStep(); + core.checkRouteFolding(); + if (callback) callback(); + }); +} + +control.prototype._moveAction_popAutomaticRoute = function () { + var automaticRoute = core.status.automaticRoute; + // 检查自动寻路是否被弹出 + if (automaticRoute.autoHeroMove) { + automaticRoute.movedStep++; + automaticRoute.lastDirection = core.getHeroLoc('direction'); + if (automaticRoute.destStep == automaticRoute.movedStep) { + if (automaticRoute.autoStep == automaticRoute.autoStepRoutes.length) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + else { + automaticRoute.movedStep = 0; + automaticRoute.destStep = automaticRoute.autoStepRoutes[automaticRoute.autoStep].step; + core.setHeroLoc('direction', automaticRoute.autoStepRoutes[automaticRoute.autoStep].direction); + core.status.automaticRoute.autoStep++; + } + } + } +} + +////// 让勇士开始移动 ////// +control.prototype.moveHero = function (direction, callback) { + // 如果正在移动,直接return + if (core.status.heroMoving != 0) return; + if (core.isset(direction)) + core.setHeroLoc('direction', direction); + + if (callback) return this.moveAction(callback); + this._moveHero_moving(); +} + +control.prototype._moveHero_moving = function () { + // ------ 我已经看不懂这个函数了,反正好用就行23333333 + core.status.heroStop = false; + core.status.automaticRoute.moveDirectly = false; + var move = function () { + if (!core.status.heroStop) { + if (core.hasFlag('debug') && core.status.ctrlDown) { + if (core.status.heroMoving != 0) return; + // 检测是否穿出去 + var nx = core.nextX(), ny = core.nextY(); + if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height) return; + core.eventMoveHero([core.getHeroLoc('direction')], core.values.moveSpeed, move); + } + else { + core.moveAction(); + setTimeout(move, 50); + } + } + } + move(); +} + +////// 当前是否正在移动 ////// +control.prototype.isMoving = function () { + return !core.status.heroStop || core.status.heroMoving > 0; +} + +////// 停止勇士的一切行动,等待勇士行动结束后,再执行callback ////// +control.prototype.waitHeroToStop = function (callback) { + var lastDirection = core.status.automaticRoute.lastDirection; + core.stopAutomaticRoute(); + core.clearContinueAutomaticRoute(); + if (callback) { + core.status.replay.animate = true; + core.lockControl(); + core.status.automaticRoute.moveDirectly = false; + setTimeout(function () { + core.status.replay.animate = false; + if (core.isset(lastDirection)) + core.setHeroLoc('direction', lastDirection); + core.drawHero(); + callback(); + }, core.status.replay.speed == 24 ? 1 : 30); + } +} + +////// 转向 ////// +control.prototype.turnHero = function (direction) { + if (direction) { + core.setHeroLoc('direction', direction); + core.drawHero(); + core.status.route.push("turn:" + direction); + return; + } + core.setHeroLoc('direction', core.turnDirection(':right')); + core.drawHero(); + core.status.route.push("turn"); + core.checkRouteFolding(); +} + +////// 瞬间移动 ////// +control.prototype.moveDirectly = function (destX, destY, ignoreSteps) { + return this.controldata.moveDirectly(destX, destY, ignoreSteps); +} + +////// 尝试瞬间移动 ////// +control.prototype.tryMoveDirectly = function (destX, destY) { + if (this.nearHero(destX, destY)) return false; + var canMoveArray = core.maps.generateMovableArray(); + var dirs = [[destX, destY], [destX - 1, destY, "right"], [destX, destY - 1, "down"], [destX, destY + 1, "up"], [destX + 1, destY, "left"]]; + var canMoveDirectlyArray = core.canMoveDirectlyArray(dirs, canMoveArray); + + for (var i = 0; i < dirs.length; ++i) { + var d = dirs[i], dx = d[0], dy = d[1], dir = d[2]; + if (dx < 0 || dx >= core.bigmap.width || dy < 0 || dy >= core.bigmap.height) continue; + if (dir && !core.inArray(canMoveArray[dx][dy], dir)) continue; + if (canMoveDirectlyArray[i] < 0) continue; + if (core.control.moveDirectly(dx, dy, canMoveDirectlyArray[i])) { + if (dir) core.moveHero(dir, function () { }); + return true; + } + } + return false; +} + +////// 绘制勇士 ////// +control.prototype.drawHero = function (status, offset, frame) { + if (!core.isPlaying() || !core.status.floorId || core.status.gameOver) return; + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), direction = core.getHeroLoc('direction'); + status = status || 'stop'; + if (!offset) offset = 0; + + var way = core.utils.scan2[direction]; + var dx = way.x, dy = way.y; + var offsetX = typeof offset == 'number' ? dx * offset : (offset.x || 0); + var offsetY = typeof offset == 'number' ? dy * offset : (offset.y || 0); + offset = { x: offsetX, y: offsetY, offset: offset }; + + core.clearAutomaticRouteNode(x + dx, y + dy); + core.clearMap('hero'); + core.status.heroCenter.px = 32 * x + offsetX + 16; + core.status.heroCenter.py = 32 * y + offsetY + 32 - core.material.icons.hero.height / 2; + + // 重置hero层画布 + core.setGameCanvasTranslate('hero', 0, 0); + delete core.canvas.hero._px; + delete core.canvas.hero._py; + core.status.preview.enabled = false; + if (!core.hasFlag('__lockViewport__')) { + this._drawHero_updateViewport(x, y, offset); + } + + this._drawHero_draw(direction, x, y, status, offset, frame); +} + +control.prototype._drawHero_updateViewport = function (x, y, offset) { + core.bigmap.offsetX = core.clamp((x - core._HALF_WIDTH_) * 32 + offset.x, 0, Math.max(32 * core.bigmap.width - core._PX_, 0)); + core.bigmap.offsetY = core.clamp((y - core._HALF_HEIGHT_) * 32 + offset.y, 0, Math.max(32 * core.bigmap.height - core._PY_, 0)); + core.control.updateViewport(); +} + +control.prototype._drawHero_draw = function (direction, x, y, status, offset, frame) { + offset = offset || { x: 0, y: 0, offset: 0, px: 0, py: 0 }; + var opacity = core.setAlpha('hero', core.getFlag('__heroOpacity__', 1)) + this._drawHero_getDrawObjs(direction, x, y, status, offset).forEach(function (block) { + core.drawImage('hero', block.img, (block.heroIcon[block.status] + (frame || 0)) % 4 * block.width, + block.heroIcon.loc * block.height, block.width, block.height, + block.posx + (32 - block.width) / 2, block.posy + 32 - block.height, block.width, block.height); + }); + core.setAlpha('hero', opacity); +} + +control.prototype._drawHero_getDrawObjs = function (direction, x, y, status, offset) { + var heroIconArr = core.material.icons.hero, drawObjs = [], index = 0; + drawObjs.push({ + "img": core.material.images.hero, + "width": core.material.icons.hero.width || 32, + "height": core.material.icons.hero.height, + "heroIcon": heroIconArr[direction], + "posx": x * 32 - core.bigmap.offsetX + offset.x, + "posy": y * 32 - core.bigmap.offsetY + offset.y, + "status": status, + "index": index++, + }); + if (typeof offset.offset == 'number') { + core.status.hero.followers.forEach(function (t) { + drawObjs.push({ + "img": core.material.images.images[t.name], + "width": core.material.images.images[t.name].width / 4, + "height": core.material.images.images[t.name].height / 4, + "heroIcon": heroIconArr[t.direction], + "posx": 32 * t.x - core.bigmap.offsetX + (t.stop ? 0 : core.utils.scan2[t.direction].x * Math.abs(offset.offset)), + "posy": 32 * t.y - core.bigmap.offsetY + (t.stop ? 0 : core.utils.scan2[t.direction].y * Math.abs(offset.offset)), + "status": t.stop ? "stop" : status, + "index": index++ + }); + }); + } + return drawObjs.sort(function (a, b) { + return a.posy == b.posy ? b.index - a.index : a.posy - b.posy; + }); +} + +control.prototype.setHeroOpacity = function (opacity, moveMode, time, callback) { + time = time || 0; + if (time == 0) { + core.setFlag('__heroOpacity__', opacity); + core.drawHero(); + if (callback) callback(); + return; + } + time /= Math.max(core.status.replay.speed, 1) + + var fromOpacity = core.getFlag('__heroOpacity__', 1); + var step = 0, steps = parseInt(time / 10); + if (steps <= 0) steps = 1; + var moveFunc = core.applyEasing(moveMode); + + var animate = setInterval(function () { + step++; + core.setFlag('__heroOpacity__', fromOpacity + (opacity - fromOpacity) * moveFunc(step / steps)); + core.drawHero(); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, 10); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +// ------ 画布、位置、阻激夹域,显伤 ------ // + +////// 设置画布偏移 +control.prototype.setGameCanvasTranslate = function (canvas, x, y) { + var c = core.dom.gameCanvas[canvas]; + x = x * core.domStyle.scale; + y = y * core.domStyle.scale; + c.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + c.style.webkitTransform = 'translate(' + x + 'px,' + y + 'px)'; + c.style.OTransform = 'translate(' + x + 'px,' + y + 'px)'; + c.style.MozTransform = 'translate(' + x + 'px,' + y + 'px)'; + if (main.mode === 'editor' && editor.isMobile) { + c.style.transform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + c.style.webkitTransform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + c.style.OTransform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + c.style.MozTransform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + } +}; + +////// 加减画布偏移 +control.prototype.addGameCanvasTranslate = function (x, y) { + for (var ii = 0, canvas; canvas = core.dom.gameCanvas[ii]; ii++) { + var id = canvas.getAttribute('id'); + if (id == 'ui' || id == 'data') continue; // UI层和data层不移动 + var offsetX = x, offsetY = y; + if (core.bigmap.canvas.indexOf(id) >= 0) { + if (core.bigmap.v2) { + offsetX -= (core.bigmap.offsetX - 32 * core.bigmap.posX) + 32; + offsetY -= (core.bigmap.offsetY - 32 * core.bigmap.posY) + 32; + } else { + offsetX -= core.bigmap.offsetX; + offsetY -= core.bigmap.offsetY; + } + } + core.control.setGameCanvasTranslate(id, offsetX, offsetY); + } +} + +////// 更新视野范围 ////// +control.prototype.updateViewport = function () { + // 当前是否应该重绘? + if (core.bigmap.v2) { + if (core.bigmap.offsetX >= core.bigmap.posX * 32 + 32 + || core.bigmap.offsetX <= core.bigmap.posX * 32 - 32 + || core.bigmap.offsetY >= core.bigmap.posY * 32 + 32 + || core.bigmap.offsetY <= core.bigmap.posY * 32 - 32) { + core.bigmap.posX = parseInt(core.bigmap.offsetX / 32); + core.bigmap.posY = parseInt(core.bigmap.offsetY / 32); + core.redrawMap(); + } + } else { + core.bigmap.posX = core.bigmap.posY = 0; + } + var offsetX = core.bigmap.v2 ? -(core.bigmap.offsetX - 32 * core.bigmap.posX) - 32 : -core.bigmap.offsetX; + var offsetY = core.bigmap.v2 ? -(core.bigmap.offsetY - 32 * core.bigmap.posY) - 32 : -core.bigmap.offsetY; + + core.bigmap.canvas.forEach(function (cn) { + core.control.setGameCanvasTranslate(cn, offsetX, offsetY); + }); + // ------ 路线 + core.relocateCanvas('route', core.status.automaticRoute.offsetX - core.bigmap.offsetX, core.status.automaticRoute.offsetY - core.bigmap.offsetY); + // ------ 所有的大怪物也都需要重定位 + for (var one in core.dymCanvas) { + if (one.startsWith('_bigImage_')) { + var ox = core.dymCanvas[one].canvas.getAttribute('_ox'); + var oy = core.dymCanvas[one].canvas.getAttribute('_oy'); + if (ox != null && oy != null) { + core.relocateCanvas(one, parseInt(ox) - core.bigmap.offsetX, parseInt(oy) - core.bigmap.offsetY); + } + } + } + +} + +////// 设置视野范围 ////// +control.prototype.setViewport = function (px, py) { + var originOffsetX = core.bigmap.offsetX, originOffsetY = core.bigmap.offsetY; + core.bigmap.offsetX = core.clamp(px, 0, 32 * core.bigmap.width - core._PX_); + core.bigmap.offsetY = core.clamp(py, 0, 32 * core.bigmap.height - core._PY_); + this.updateViewport(); + // ------ hero层也需要! + var px = parseFloat(core.canvas.hero._px) || 0; + var py = parseFloat(core.canvas.hero._py) || 0; + px += originOffsetX - core.bigmap.offsetX; + py += originOffsetY - core.bigmap.offsetY; + core.control.setGameCanvasTranslate('hero', px, py); + core.canvas.hero._px = px; + core.canvas.hero._py = py; +} + +////// 移动视野范围 ////// +control.prototype.moveViewport = function (x, y, moveMode, time, callback) { + time = time || 0; + time /= Math.max(core.status.replay.speed, 1) + var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) { + this.setViewport(32 * x, 32 * y); + if (callback) callback(); + return; + } + var px = core.clamp(32 * x, 0, 32 * core.bigmap.width - core._PX_); + var py = core.clamp(32 * y, 0, 32 * core.bigmap.height - core._PY_); + var cx = core.bigmap.offsetX; + var cy = core.bigmap.offsetY; + var moveFunc = core.applyEasing(moveMode); + + var animate = window.setInterval(function () { + step++; + core.setViewport(cx + moveFunc(step / steps) * (px - cx), cy + moveFunc(step / steps) * (py - cy)); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + core.setViewport(px, py); + if (callback) callback(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 获得勇士面对位置的x坐标 ////// +control.prototype.nextX = function (n) { + if (n == null) n = 1; + return core.getHeroLoc('x') + core.utils.scan[core.getHeroLoc('direction')].x * n; +} + +////// 获得勇士面对位置的y坐标 ////// +control.prototype.nextY = function (n) { + if (n == null) n = 1; + return core.getHeroLoc('y') + core.utils.scan[core.getHeroLoc('direction')].y * n; +} + +////// 某个点是否在勇士旁边 ////// +control.prototype.nearHero = function (x, y, n) { + if (n == null) n = 1; + return Math.abs(x - core.getHeroLoc('x')) + Math.abs(y - core.getHeroLoc('y')) <= n; +} + +////// 聚集跟随者 ////// +control.prototype.gatherFollowers = function () { + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), dir = core.getHeroLoc('direction'); + core.status.hero.followers.forEach(function (t) { + t.x = x; + t.y = y; + t.stop = true; + t.direction = dir; + }); +} + +////// 更新跟随者坐标 ////// +control.prototype.updateFollowers = function () { + core.status.hero.followers.forEach(function (t) { + if (!t.stop) { + t.x += core.utils.scan2[t.direction].x; + t.y += core.utils.scan2[t.direction].y; + } + }) + + var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); + core.status.hero.followers.forEach(function (t) { + t.stop = true; + var dx = nowx - t.x, dy = nowy - t.y; + for (var dir in core.utils.scan2) { + if (core.utils.scan2[dir].x == dx && core.utils.scan2[dir].y == dy) { + t.stop = false; + t.direction = dir; + } + } + nowx = t.x; nowy = t.y; + }) +} + +////// 瞬移更新跟随者坐标 ////// +control.prototype._moveDirectyFollowers = function (x, y) { + var route = core.automaticRoute(x, y); + if (route.length == 0) route = [{ x: x, y: y, direction: core.getHeroLoc('direction') }]; + + var nowx = x, nowy = y; + for (var i = 0; i < core.status.hero.followers.length; ++i) { + var t = core.status.hero.followers[i]; + var index = route.length - i - 2; + if (index < 0) index = 0; + t.stop = true; + t.x = route[index].x; + t.y = route[index].y; + t.direction = route[index].direction; + var dx = nowx - t.x, dy = nowy - t.y; + for (var dir in core.utils.scan2) { + if (core.utils.scan2[dir].x == dx && core.utils.scan2[dir].y == dy) { + t.stop = false; + t.direction = dir; + } + } + nowx = t.x; nowy = t.y; + } +} + +////// 更新领域、夹击、阻击的伤害地图 ////// +control.prototype.updateCheckBlock = function (floorId) { + return this.controldata.updateCheckBlock(floorId); +} + +////// 检查并执行领域、夹击、阻击事件 ////// +control.prototype.checkBlock = function () { + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), loc = x + "," + y; + var damage = core.status.checkBlock.damage[loc]; + if (damage) { + core.status.hero.hp -= damage; + var text = (Object.keys(core.status.checkBlock.type[loc] || {}).join(",")) || "伤害"; + core.drawTip("受到" + text + damage + "点"); + core.drawHeroAnimate("zone"); + this._checkBlock_disableQuickShop(); + core.status.hero.statistics.extraDamage += damage; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(false, true); + core.events.lose(); + return; + } else { + core.updateStatusBar(false, true); + } + } + this._checkBlock_ambush(core.status.checkBlock.ambush[loc]); + this._checkBlock_repulse(core.status.checkBlock.repulse[loc]); +} + +control.prototype._checkBlock_disableQuickShop = function () { + // 禁用快捷商店 + if (core.flags.disableShopOnDamage) { + Object.keys(core.status.shops).forEach(function (shopId) { + core.setShopVisited(shopId, false); + }); + } +} + +////// 阻击 ////// +control.prototype._checkBlock_repulse = function (repulse) { + if (!repulse || repulse.length == 0) return; + var actions = []; + repulse.forEach(function (t) { + actions.push({ "type": "move", "loc": [t[0], t[1]], "steps": [t[3]], "time": 250, "keep": true, "async": true }); + }); + actions.push({ "type": "waitAsync" }); + core.insertAction(actions); +} + +////// 捕捉 ////// +control.prototype._checkBlock_ambush = function (ambush) { + if (!ambush || ambush.length == 0) return; + // 捕捉效果 + var actions = []; + ambush.forEach(function (t) { + actions.push({ "type": "move", "loc": [t[0], t[1]], "steps": [t[3]], "time": 250, "keep": false, "async": true }); + }); + actions.push({ "type": "waitAsync" }); + // 强制战斗 + ambush.forEach(function (t) { + actions.push({ + "type": "function", "function": "function() { " + + "core.battle('" + t[2] + "', " + t[0] + "," + t[1] + ", true, core.doAction); " + + "}", "async": true + }); + }); + core.insertAction(actions); +} + +////// 更新全地图显伤 ////// +control.prototype.updateDamage = function (floorId, ctx) { + floorId = floorId || core.status.floorId; + if (!floorId || core.status.gameOver || main.mode != 'play') return; + var onMap = ctx == null; + + // 没有怪物手册 + if (!core.hasItem('book')) return; + core.status.damage.posX = core.bigmap.posX; + core.status.damage.posY = core.bigmap.posY; + if (!onMap) { + var width = core.floors[floorId].width, height = core.floors[floorId].height; + // 地图过大的缩略图不绘制显伤 + if (width * height > core.bigmap.threshold) return; + } + this._updateDamage_damage(floorId, onMap); + this._updateDamage_extraDamage(floorId, onMap); + this.drawDamage(ctx); +} + +control.prototype._updateDamage_damage = function (floorId, onMap) { + core.status.damage.data = []; + if (!core.flags.displayEnemyDamage && !core.flags.displayExtraDamage) return; + + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + var x = block.x, y = block.y; + + // v2优化,只绘制范围内的部分 + if (onMap && core.bigmap.v2) { + if (x < core.bigmap.posX - core.bigmap.extend || x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend + || y < core.bigmap.posY - core.bigmap.extend || y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend) { + return; + } + } + + if (!block.disable && block.event.cls.indexOf('enemy') == 0 && block.event.displayDamage !== false) { + if (core.flags.displayEnemyDamage) { + var damageString = core.enemys.getDamageString(block.event.id, x, y, floorId); + core.status.damage.data.push({ text: damageString.damage, px: 32 * x + 1, py: 32 * (y + 1) - 1, color: damageString.color }); + } + if (core.flags.displayCritical) { + var critical = core.enemys.nextCriticals(block.event.id, 1, x, y, floorId); + critical = core.formatBigNumber((critical[0] || [])[0], true); + if (critical == '???') critical = '?'; + core.status.damage.data.push({ text: critical, px: 32 * x + 1, py: 32 * (y + 1) - 11, color: '#FFFFFF' }); + } + } + }); +} + +control.prototype._updateDamage_extraDamage = function (floorId, onMap) { + core.status.damage.extraData = []; + if (!core.flags.displayExtraDamage) return; + + var width = core.floors[floorId].width, height = core.floors[floorId].height; + var startX = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posX - core.bigmap.extend) : 0; + var endX = onMap && core.bigmap.v2 ? Math.min(width, core.bigmap.posX + core._WIDTH_ + core.bigmap.extend + 1) : width; + var startY = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posY - core.bigmap.extend) : 0; + var endY = onMap && core.bigmap.v2 ? Math.min(height, core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend + 1) : height; + + for (var x = startX; x < endX; x++) { + for (var y = startY; y < endY; y++) { + var alpha = 1; + if (core.noPass(x, y, floorId)) { + if (core.flags.extraDamageType == 2) alpha = 0; + else if (core.flags.extraDamageType == 1) alpha = 0.6; + } + var damage = core.status.checkBlock.damage[x + "," + y] || 0; + if (damage > 0) { // 该点伤害 + damage = core.formatBigNumber(damage, true); + core.status.damage.extraData.push({ text: damage, px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha }); + } + else { // 检查捕捉 + if (core.status.checkBlock.ambush[x + "," + y]) { + core.status.damage.extraData.push({ text: '!', px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha }); + } + } + } + } +} + +////// 重绘地图显伤 ////// +control.prototype.drawDamage = function (ctx) { + if (core.status.gameOver || !core.status.damage || main.mode != 'play') return; + var onMap = false; + if (ctx == null) { + ctx = core.canvas.damage; + core.clearMap('damage'); + onMap = true; + } + + if (onMap && core.bigmap.v2) { + // 检查是否需要重算... + if (Math.abs(core.bigmap.posX - core.status.damage.posX) >= core.bigmap.extend - 1 + || Math.abs(core.bigmap.posY - core.status.damage.posY) >= core.bigmap.extend - 1) { + return this.updateDamage(); + } + } + return this._drawDamage_draw(ctx, onMap); +} + +control.prototype._drawDamage_draw = function (ctx, onMap) { + if (!core.hasItem('book')) return; + + core.setFont(ctx, "bold 11px Arial"); + core.setTextAlign(ctx, 'left'); + core.status.damage.data.forEach(function (one) { + var px = one.px, py = one.py; + if (onMap && core.bigmap.v2) { + px -= core.bigmap.posX * 32; + py -= core.bigmap.posY * 32; + if (px < -32 * 2 || px > core._PX_ + 32 || py < -32 || py > core._PY_ + 32) + return; + } + core.fillBoldText(ctx, one.text, px, py, one.color); + }); + + core.setTextAlign(ctx, 'center'); + core.status.damage.extraData.forEach(function (one) { + var px = one.px, py = one.py; + if (onMap && core.bigmap.v2) { + px -= core.bigmap.posX * 32; + py -= core.bigmap.posY * 32; + if (px < -32 || px > core._PX_ + 32 || py < -32 || py > core._PY_ + 32) + return; + } + var alpha = core.setAlpha(ctx, one.alpha); + core.fillBoldText(ctx, one.text, px, py, one.color); + core.setAlpha(ctx, alpha); + }); +} + +// ------ 录像相关 ------ // + +////// 选择录像文件 ////// +control.prototype.chooseReplayFile = function () { + core.readFile(function (obj) { + if (obj.name != core.firstData.name) return alert("存档和游戏不一致!"); + if (!obj.route) return alert("无效的录像!"); + var _replay = function () { + core.startGame(core.flags.startUsingCanvas ? '' : obj.hard || '', obj.seed, core.decodeRoute(obj.route)); + } + if (obj.version && obj.version != core.firstData.version) { + core.myconfirm("游戏版本不一致!\n你仍然想播放录像吗?", _replay); + return; + } + _replay(); + }, null, ".h5route"); +} + +////// 开始播放 ////// +control.prototype.startReplay = function (list) { + if (!core.isPlaying()) return; + core.status.replay.replaying = true; + core.status.replay.pausing = true; + core.status.replay.failed = false; + core.status.replay.speed = 1.0; + core.status.replay.toReplay = core.cloneArray(list); + core.status.replay.totalList = core.status.route.concat(list); + core.status.replay.steps = 0; + core.status.replay.save = []; + core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); + core.setOpacity('replay', 0.6); + this._replay_drawProgress(); + core.updateStatusBar(false, true); + core.drawTip("开始播放"); + this.replay(); +} + +////// 更改播放状态 ////// +control.prototype.triggerReplay = function () { + if (core.status.replay.pausing) this.resumeReplay(); + else this.pauseReplay(); +} + +////// 暂停播放 ////// +control.prototype.pauseReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + core.status.replay.pausing = true; + core.updateStatusBar(false, true); + core.drawTip("暂停播放"); +} + +////// 恢复播放 ////// +control.prototype.resumeReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + core.status.replay.pausing = false; + core.updateStatusBar(false, true); + core.drawTip("恢复播放"); + core.replay(); +} + +////// 单步播放 ////// +control.prototype.stepReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + core.replay(true); +} + +////// 加速播放 ////// +control.prototype.speedUpReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24]; + for (var i = speeds.length - 2; i >= 0; i--) { + if (speeds[i] <= core.status.replay.speed) { + core.status.replay.speed = speeds[i + 1]; + break; + } + } + core.drawTip("x" + core.status.replay.speed + "倍"); +} + +////// 减速播放 ////// +control.prototype.speedDownReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24]; + for (var i = 1; i <= speeds.length; i++) { + if (speeds[i] >= core.status.replay.speed) { + core.status.replay.speed = speeds[i - 1]; + break; + } + } + core.drawTip("x" + core.status.replay.speed + "倍"); +} + +////// 设置播放速度 ////// +control.prototype.setReplaySpeed = function (speed) { + if (!core.isPlaying() || !core.isReplaying()) return; + core.status.replay.speed = speed; + core.drawTip("x" + core.status.replay.speed + "倍"); +} + +////// 停止播放 ////// +control.prototype.stopReplay = function (force) { + if (!core.isPlaying()) return; + if (!core.isReplaying() && !force) return; + core.status.replay.toReplay = []; + core.status.replay.totalList = []; + core.status.replay.replaying = false; + core.status.replay.pausing = false; + core.status.replay.failed = false; + core.status.replay.speed = 1.0; + core.status.replay.steps = 0; + core.status.replay.save = []; + core.deleteCanvas('replay'); + core.updateStatusBar(false, true); + core.drawTip("停止播放并恢复游戏"); +} + +////// 回退 ////// +control.prototype.rewindReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + if (core.status.replay.save.length == 0) { + core.playSound('操作失败'); + return core.drawTip("无法再回到上一个节点"); + } + var save = core.status.replay.save, data = save.pop(); + core.loadData(data.data, function () { + core.removeFlag('__fromLoad__'); + core.status.replay = { + "replaying": true, + "pausing": true, + "animate": false, + "toReplay": data.replay.toReplay, + "totalList": data.replay.totalList, + "speed": core.status.replay.speed, + "steps": data.replay.steps, + "save": save + } + core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); + core.setOpacity('replay', 0.6); + core.control._replay_drawProgress(); + core.updateStatusBar(false, true); + core.drawTip("成功回退到上一个节点"); + }); +} + +////// 回放时存档 ////// +control.prototype._replay_SL = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + if (core.hasFlag('__forbidSave__')) { + core.playSound('操作失败'); + return core.drawTip('当前禁止存档'); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'save'; + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + + core.ui._drawSLPanel(10 * page + offset); +} + +////// 回放时查看怪物手册 ////// +control.prototype._replay_book = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || (core.status.event.id && core.status.event.id != 'viewMaps')) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + if (!core.hasItem('book')) { + core.playSound('操作失败'); + return core.drawTip('你没有' + core.material.items['book'].name, 'book'); + } + this._replay_hideProgress(); + + // 从“浏览地图”页面打开 + if (core.status.event.id == 'viewMaps') + core.status.event.ui = core.status.event.data; + + core.lockControl(); + core.status.event.id = 'book'; + core.useItem('book', true); +} + +////// 回放录像时浏览地图 ////// +control.prototype._replay_viewMap = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'viewMaps'; + core.ui._drawViewMaps(); +} + +control.prototype._replay_toolbox = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'toolbox'; + core.ui._drawToolbox(); +} + +control.prototype._replay_equipbox = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'equipbox'; + core.ui._drawEquipbox(); +} + +////// 是否正在播放录像 ////// +control.prototype.isReplaying = function () { + return (core.status.replay || {}).replaying; +} + +////// 回放 ////// +control.prototype.replay = function (force) { + if (!core.isPlaying() || !core.isReplaying() + || core.status.replay.animate || core.status.event.id || core.status.replay.failed) return; + if (core.status.replay.pausing && !force) return; + this._replay_drawProgress(); + if (core.status.replay.toReplay.length == 0) + return this._replay_finished(); + this._replay_save(); + var action = core.status.replay.toReplay.shift(); + if (this._doReplayAction(action)) return; + this._replay_error(action); +} + +////// 注册一个录像行为 ////// +// name:自定义名称,可用于注销使用 +// func:具体执行录像的函数,可为一个函数或插件中的函数名; +// 需要接受一个action参数,代表录像回放时的下一个操作 +// func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。 +control.prototype.registerReplayAction = function (name, func) { + this.unregisterReplayAction(name); + this.replayActions.push({ name: name, func: func }); +} + +////// 注销一个录像行为 ////// +control.prototype.unregisterReplayAction = function (name) { + this.replayActions = this.replayActions.filter(function (b) { return b.name != name; }); +} + +////// 执行录像行为,会在注册的函数中依次执行直到得到true为止 ////// +control.prototype._doReplayAction = function (action) { + for (var i in this.replayActions) { + try { + if (core.doFunc(this.replayActions[i].func, this, action)) return true; + } catch (e) { + console.error(e); + console.error("ERROR in replayActions[" + this.replayActions[i].name + "]:已自动注销该项。"); + core.unregisterReplayAction(this.replayActions[i].name); + } + } + return false; +} + +control.prototype._replay_finished = function () { + core.status.replay.replaying = false; + core.status.replay.failed = false; + core.status.event.selection = 0; + var str = "录像播放完毕,你想退出播放吗?"; + if (core.status.route.length != core.status.replay.totalList.length + || core.subarray(core.status.route, core.status.replay.totalList) == null) { + str = "录像播放完毕,但记录不一致。\n请检查录像播放时的二次记录问题。\n你想退出播放吗?"; + } + core.ui.drawConfirmBox(str, function () { + core.ui.closePanel(); + core.stopReplay(true); + }, function () { + core.status.replay.replaying = true; + core.ui.closePanel(); + core.pauseReplay(); + }); +} + +control.prototype._replay_save = function () { + core.status.replay.steps++; + if (core.status.replay.steps % 40 == 1) { + if (core.status.replay.save.length == 30) + core.status.replay.save.shift(); + core.status.replay.save.push({ + "data": core.saveData(), "replay": { + "totalList": core.cloneArray(core.status.replay.totalList), + "toReplay": core.cloneArray(core.status.replay.toReplay), + "steps": core.status.replay.steps + } + }); + } +} + +control.prototype._replay_error = function (action, callback) { + core.ui.closePanel(); + core.status.replay.replaying = false; + core.status.replay.failed = true; + var len = core.status.replay.toReplay.length; + var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1); + var nextList = core.status.replay.toReplay.slice(0, 10); + console.log("录像文件出错,当前操作:" + action); + console.log("之前的10个操作是:\n" + prevList.toString()); + console.log("接下来10个操作是:\n" + nextList.toString()); + core.ui.drawConfirmBox("录像文件出错,你想回到上个节点吗?", function () { + core.status.replay.failed = false; + core.ui.closePanel(); + if (core.status.replay.save.length > 0) { + core.status.replay.replaying = true; + core.status.replay.pausing = true; + core.rewindReplay(); + } + else { + core.playSound('操作失败'); + core.stopReplay(true); + core.drawTip("无法回到上一个节点"); + if (callback) callback(); + } + }, function () { + core.status.replay.failed = false; + core.ui.closePanel(); + core.stopReplay(true); + if (callback) callback(); + }); +} + +control.prototype._replay_hideProgress = function () { + if (core.dymCanvas.replay) core.dymCanvas.replay.canvas.style.display = 'none'; +} + +control.prototype._replay_drawProgress = function () { + if (!core.dymCanvas.replay) return; + if (core.dymCanvas.replay.canvas.style.display == 'none') core.dymCanvas.replay.canvas.style.display = 'block'; + var total = core.status.replay.totalList.length, left = total - core.status.replay.toReplay.length; + var content = '播放进度:' + left + ' / ' + total + '(' + (left / total * 100).toFixed(2) + '%)'; + var width = 26 + core.calWidth('replay', content, "16px Arial"); + core.clearMap('replay'); + core.fillRect('replay', 0, 0, width, 40, '#000000'); + core.fillText('replay', content, 16, 27, '#FFFFFF'); +} + +control.prototype.__replay_getTimeout = function () { + if (core.status.replay.speed == 24) return 0; + return 750 / Math.max(1, core.status.replay.speed); +} + +control.prototype._replayAction_move = function (action) { + if (["up", "down", "left", "right"].indexOf(action) < 0) return false; + core.moveHero(action, core.replay); + return true; +} + +control.prototype._replayAction_item = function (action) { + if (action.indexOf("item:") != 0) return false; + var itemId = action.substring(5); + if (!core.canUseItem(itemId)) return false; + if (core.material.items[itemId].hideInReplay || core.status.replay.speed == 24) { + core.useItem(itemId, false, core.replay); + return true; + } + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + var index, per = core._WIDTH_ - 1; + if ((index = tools.indexOf(itemId)) >= 0) { + core.status.event.data = { "toolsPage": Math.floor(index / per) + 1, "constantsPage": 1 }; + index = index % per; + } + else if ((index = constants.indexOf(itemId)) >= 0) { + core.status.event.data = { "toolsPage": 1, "constantsPage": Math.floor(index / per) + 1 }; + index = index % per + per; + } + if (index < 0) return false; + core.ui._drawToolbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.useItem(itemId, false, core.replay); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_equip = function (action) { + if (action.indexOf("equip:") != 0) return false; + var equipId = action.substring(6); + var ownEquipment = core.getToolboxItems('equips'); + var index = ownEquipment.indexOf(equipId), per = core._WIDTH_ - 1; + if (index < 0) { + core.removeFlag('__doNotCheckAutoEvents__'); + return false; + } + + var cb = function () { + var next = core.status.replay.toReplay[0] || ""; + if (!next.startsWith('equip:') && !next.startsWith('unEquip:')) { + core.removeFlag('__doNotCheckAutoEvents__'); + core.checkAutoEvents(); + } + core.replay(); + } + core.setFlag('__doNotCheckAutoEvents__', true); + + core.status.route.push(action); + if (core.material.items[equipId].hideInReplay || core.status.replay.speed == 24) { + core.loadEquip(equipId, cb); + return true; + } + core.status.event.data = { "page": Math.floor(index / per) + 1, "selectId": null }; + index = index % per + per; + core.ui._drawEquipbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.loadEquip(equipId, cb); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_unEquip = function (action) { + if (action.indexOf("unEquip:") != 0) return false; + var equipType = parseInt(action.substring(8)); + if (!core.isset(equipType)) { + core.removeFlag('__doNotCheckAutoEvents__'); + return false; + } + + var cb = function () { + var next = core.status.replay.toReplay[0] || ""; + if (!next.startsWith('equip:') && !next.startsWith('unEquip:')) { + core.removeFlag('__doNotCheckAutoEvents__'); + core.checkAutoEvents(); + } + core.replay(); + } + core.setFlag('__doNotCheckAutoEvents__', true); + + core.ui._drawEquipbox(equipType); + core.status.route.push(action); + if (core.status.replay.speed == 24) { + core.unloadEquip(equipType, cb); + return true; + } + setTimeout(function () { + core.ui.closePanel(); + core.unloadEquip(equipType, cb); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_saveEquip = function (action) { + if (action.indexOf('saveEquip:') != 0) return false; + core.quickSaveEquip(parseInt(action.substring(10))); + core.replay(); + return true; +} + +control.prototype._replayAction_loadEquip = function (action) { + if (action.indexOf('loadEquip:') != 0) return false; + core.quickLoadEquip(parseInt(action.substring(10))); + core.replay(); + return true; +} + +control.prototype._replayAction_fly = function (action) { + if (action.indexOf("fly:") != 0) return false; + var floorId = action.substring(4); + var toIndex = core.floorIds.indexOf(floorId); + if (!core.canUseItem('fly') || (core.flags.flyNearStair && !core.nearStair())) return false; + core.ui.drawFly(toIndex); + if (core.status.replay.speed == 24) { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + return true; + } + setTimeout(function () { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_shop = function (action) { + if (action.indexOf("shop:") != 0) return false; + var shopId = action.substring(5); + if (core.canUseQuickShop(shopId) != null || !core.canOpenShop(shopId)) { + this._replay_error(shopId); + return true; + } + core.openShop(shopId, false); + core.replay(); + return true; +} + +control.prototype._replayAction_turn = function (action) { + if (action != 'turn' && action.indexOf('turn:') != 0) return false; + if (action == 'turn') core.turnHero(); + else core.turnHero(action.substring(5)); + core.replay(); + return true; +} + +control.prototype._replayAction_getNext = function (action) { + if (action != "getNext") return false; + core.getNextItem(); + core.replay(); + return true; +} + +control.prototype._replayAction_moveDirectly = function (action) { + if (action.indexOf("move:") != 0) return false; + // 忽略连续的瞬移事件;如果大地图某一边超过计算范围则不合并 + if (!core.hasFlag('poison') && core.status.thisMap.width < 2 * core.bigmap.extend + core._WIDTH_ + && core.status.thisMap.height < 2 * core.bigmap.extend + core._HEIGHT_) { + while (core.status.replay.toReplay.length > 0 && + core.status.replay.toReplay[0].indexOf('move:') == 0) { + core.status.route.push(action); + action = core.status.replay.toReplay.shift(); + } + } + + var pos = action.substring(5).split(":"); + var x = parseInt(pos[0]), y = parseInt(pos[1]); + var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); + var ignoreSteps = core.canMoveDirectly(x, y); + if (!core.moveDirectly(x, y, ignoreSteps)) return false; + if (core.status.replay.speed == 24) { + core.replay(); + return true; + } + + core.ui.drawArrow('ui', 32 * nowx + 16 - core.bigmap.offsetX, 32 * nowy + 16 - core.bigmap.offsetY, + 32 * x + 16 - core.bigmap.offsetX, 32 * y + 16 - core.bigmap.offsetY, '#FF0000', 3); + var timeout = this.__replay_getTimeout(); + if (ignoreSteps < 10) timeout = timeout * ignoreSteps / 10; + setTimeout(function () { + core.clearMap('ui'); + core.replay(); + }, timeout); + return true; +} + +control.prototype._replayAction_key = function (action) { + if (action.indexOf("key:") != 0) return false; + core.actions.keyUp(parseInt(action.substring(4)), false, true); + core.replay(); + return true; +} + +control.prototype._replayAction_click = function (action) { + if (action.indexOf("click:") != 0) return false; + var p = action.split(":"); + if (p.length != 4) return false; + core.actions.doRegisteredAction("onStatusBarClick", parseInt(p[2]), parseInt(p[3]), parseInt(p[1])); + core.replay(); + return true; +} + +control.prototype._replayAction_ignoreInput = function (action) { + if (action.indexOf('input:') == 0 || action.indexOf('input2:') == 0 || action.indexOf('choices:') == 0 || action.indexOf('random:') == 0) { + console.warn('警告!录像播放中出现了未知的 ' + action + '!'); + core.replay(); + return true; + } + return false; +} + +control.prototype._replayAction_no = function (action) { + if (action != 'no') return false; + core.status.route.push(action); + core.replay(); + return true; +} + +// ------ 存读档相关 ------ // + +////// 自动存档 ////// +control.prototype.autosave = function (removeLast) { + if (core.hasFlag('__forbidSave__')) return; + var x = null; + if (removeLast) { + x = core.status.route.pop(); + core.status.route.push("turn:" + core.getHeroLoc('direction')); + } + if (core.status.event.id == 'action' && !removeLast) // 事件中自动存档,读档后是否回到事件触发前 + core.setFlag("__events__", core.clone(core.status.event.data)); + if (core.saves.autosave.data == null) { + core.saves.autosave.data = []; + } + core.saves.autosave.data.splice(core.saves.autosave.now, 0, core.saveData()); + core.saves.autosave.now += 1; + if (core.saves.autosave.data.length > core.saves.autosave.max) { + if (core.saves.autosave.now < core.saves.autosave.max / 2) + core.saves.autosave.data.pop(); + else { + core.saves.autosave.data.shift(); + core.saves.autosave.now = core.saves.autosave.now - 1; + } + } + core.saves.autosave.updated = true; + core.saves.ids[0] = true; + core.removeFlag("__events__"); + if (removeLast) { + core.status.route.pop(); + if (x) core.status.route.push(x); + } +} + +/////// 实际进行自动存档 ////// +control.prototype.checkAutosave = function () { + if (!core.animateFrame || !core.saves || !core.saves.autosave) return; + core.setLocalStorage('totalTime', core.animateFrame.totalTime); + var autosave = core.saves.autosave; + if (autosave.data == null || !autosave.updated || !autosave.storage) return; + autosave.updated = false; + if (autosave.data.length >= 1) { + core.setLocalForage("autoSave", autosave.data[autosave.now - 1]); + } +} + +////// 实际进行存读档事件 ////// +control.prototype.doSL = function (id, type) { + switch (type) { + case 'save': this._doSL_save(id); break; + case 'load': this._doSL_load(id, this._doSL_load_afterGet); break; + case 'reload': this._doSL_reload(id, this._doSL_load_afterGet); break; + case 'replayLoad': this._doSL_load(id, this._doSL_replayLoad_afterGet); break; + case 'replayRemain': this._doSL_load(id, this._doSL_replayRemain_afterGet); break; + case 'replaySince': this._doSL_load(id, this._doSL_replaySince_afterGet); break; + } +} + +control.prototype._doSL_save = function (id) { + if (id == 'autoSave') { + core.playSound('操作失败'); + return core.drawTip('不能覆盖自动存档!'); + } + // 在事件中的存档 + if (core.status.event.interval != null) + core.setFlag("__events__", core.status.event.interval); + var data = core.saveData(); + if (core.isReplaying() && core.status.replay.toReplay.length > 0) { + data.__toReplay__ = core.encodeRoute(core.status.replay.toReplay); + } + core.setLocalForage("save" + id, data, function () { + core.saves.saveIndex = id; + core.setLocalStorage('saveIndex', core.saves.saveIndex); + // 恢复事件 + if (!core.events.recoverEvents(core.status.event.interval)) + core.ui.closePanel(); + //core.playSound('存档'); + //core.drawTip('存档成功!'); + }, function (err) { + console.error(err); + alert("存档失败,错误信息:\n" + err); + }); + core.removeFlag("__events__"); + return; +} + +control.prototype._doSL_load = function (id, callback) { + if (id == 'autoSave' && core.saves.autosave.data != null) { + core.saves.autosave.now -= 1; + var data = core.saves.autosave.data.splice(core.saves.autosave.now, 1)[0]; + if (core.isPlaying() && !core.status.gameOver) { + core.control.autosave(0); + core.saves.autosave.now -= 1; + } + if (core.saves.autosave.now == 0) { + core.saves.autosave.data.unshift(core.clone(data)); + core.saves.autosave.now += 1; + } + callback(id, data); + } + else { + core.getLocalForage(id == 'autoSave' ? id : "save" + id, null, function (data) { + if (id == 'autoSave' && data != null) { + core.saves.autosave.data = data; + if (!(core.saves.autosave.data instanceof Array)) { + core.saves.autosave.data = [core.saves.autosave.data]; + } + core.saves.autosave.now = core.saves.autosave.data.length; + return core.control._doSL_load(id, callback); + } + callback(id, data); + }, function (err) { + console.error(err); + alert("无效的存档"); + }) + } + return; +} + +control.prototype._doSL_reload = function (id, callback) { + if (core.saves.autosave.data != null && core.saves.autosave.now < core.saves.autosave.data.length) { + var data = core.saves.autosave.data.splice(core.saves.autosave.now, 1)[0]; + core.control.autosave(false); + callback(id, data); + } + return; +} + +control.prototype._doSL_load_afterGet = function (id, data) { + if (!data) return alert("无效的存档"); + var _replay = function () { + core.startGame(data.hard, data.hero.flags.__seed__, core.decodeRoute(data.route)); + }; + if (data.version != core.firstData.version) { + core.myconfirm("存档版本不匹配!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。", _replay); + return; + } + if (data.hero.flags.__events__ && data.guid != core.getGuid()) { + core.myconfirm("此存档可能存在风险,你想要播放录像么?", _replay); + return; + } + core.ui.closePanel(); + core.loadData(data, function () { + core.removeFlag('__fromLoad__'); + //core.drawTip("读档成功"); + if (id != "autoSave") { + core.saves.saveIndex = id; + core.setLocalStorage('saveIndex', core.saves.saveIndex); + } + }); +} + +control.prototype._doSL_replayLoad_afterGet = function (id, data) { + if (!data) { + core.playSound('操作失败'); + return core.drawTip("无效的存档"); + } + if (data.version != core.firstData.version) { + core.playSound('操作失败'); + return core.drawTip("存档版本不匹配"); + } + if (data.hero.flags.__events__ && data.guid != core.getGuid()) { + core.playSound('操作失败'); + return core.drawTip("此存档可能存在风险,无法读档"); + } + var route = core.subarray(core.status.route, core.decodeRoute(data.route)); + if (route == null) { + core.playSound('操作失败'); + return core.drawTip("无法从此存档回放录像"); + } + core.loadData(data, function () { + core.removeFlag('__fromLoad__'); + core.startReplay(route); + core.drawTip("回退到存档节点"); + }); +} + +control.prototype._doSL_replayRemain_afterGet = function (id, data) { + if (!data) { + core.playSound('操作失败'); + return core.drawTip("无效的存档"); + } + var route = core.decodeRoute(data.route); + if (core.status.tempRoute) { + var remainRoute = core.subarray(route, core.status.tempRoute); + if (remainRoute == null) + return alert("无法接续播放录像!\n该存档必须是前一个选择的存档的后续内容。"); + delete core.status.tempRoute; + core.ui.closePanel(); + core.startReplay(remainRoute); + core.drawTip("接续播放录像"); + return; + } + else if (data.floorId != core.status.floorId || data.hero.loc.x != core.getHeroLoc('x') || data.hero.loc.y != core.getHeroLoc('y')) + return alert("楼层或坐标不一致!"); + + core.status.tempRoute = route; + core.ui.closePanel(); + core.drawText("\t[步骤2]请选择第二个存档。\n\r[yellow]该存档必须是前一个存档的后续。\r\n将尝试播放到此存档。", function () { + core.status.event.id = 'replayRemain'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); + }); +} + +control.prototype._doSL_replaySince_afterGet = function (id, data) { + if (data.floorId != core.status.floorId || data.hero.loc.x != core.getHeroLoc('x') || data.hero.loc.y != core.getHeroLoc('y')) + return alert("楼层或坐标不一致!"); + if (!data.__toReplay__) return alert('该存档没有剩余录像!'); + core.ui.closePanel(); + core.startReplay(core.decodeRoute(data.__toReplay__)); + core.drawTip("播放存档剩余录像"); + return; +} + +////// 同步存档到服务器 ////// +control.prototype.syncSave = function (type) { + core.ui.drawWaiting("正在同步,请稍候..."); + var callback = function (saves) { + core.control._syncSave_http(type, saves); + } + if (type == 'all') core.getAllSaves(callback); + else { + if (core.saves.saveIndex % 5 == 0) { + core.saves.saveIndex -= 4; + } + core.getSave(core.saves.saveIndex, callback); + } +} + +control.prototype._syncSave_http = function (type, saves) { + if (!saves) return core.drawText("没有要同步的存档"); + var formData = new FormData(); + formData.append('type', 'save'); + formData.append('name', core.firstData.name); + formData.append('data', LZString.compressToBase64(JSON.stringify(saves))); + formData.append('shorten', '1'); + + core.http("POST", "/games/sync.php", formData, function (data) { + var response = JSON.parse(data); + if (response.code < 0) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:" + response.msg); + } + else { + core.drawText((type == 'all' ? "所有存档" : "存档" + core.saves.saveIndex) + "同步成功!\n\n您的存档编号+密码: \r[yellow]" + + response.code + response.msg + + "\r\n\n请牢记以上信息(如截图等),在从服务器\n同步存档时使用。\n\r[yellow]另外请注意,存档同步只会保存一个月的时间。\r") + } + }, function (e) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:" + e); + }) +} + +////// 从服务器加载存档 ////// +control.prototype.syncLoad = function () { + core.myprompt("请输入存档编号+密码", null, function (idpassword) { + if (!idpassword) return core.ui._drawSyncSave(); + if (!/^\d{6}\w{4}$/.test(idpassword) && !/^\d{4}\w{3}$/.test(idpassword)) { + core.drawText("不合法的存档编号+密码!"); + return; + } + core.ui.drawWaiting("正在同步,请稍候..."); + if (idpassword.length == 7) { + core.control._syncLoad_http(idpassword.substring(0, 4), idpassword.substring(4)); + } else { + core.control._syncLoad_http(idpassword.substring(0, 6), idpassword.substring(6)); + } + }); +} + +control.prototype._syncLoad_http = function (id, password) { + var formData = new FormData(); + formData.append('type', 'load'); + formData.append('name', core.firstData.name); + formData.append('id', id); + formData.append('password', password); + + core.http("POST", "/games/sync.php", formData, function (data) { + var response = JSON.parse(data); + if (response.code == 0) { + var msg = null; + try { + msg = JSON.parse(LZString.decompressFromBase64(response.msg)); + } catch (e) { } + if (!msg) { + try { + msg = JSON.parse(response.msg); + } catch (e) { } + } + if (msg) { + core.control._syncLoad_write(msg); + } else { + core.drawText("出错啦!\n存档解析失败!"); + } + } + else { + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:" + response.msg); + } + }, function (e) { + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:" + e); + }); +} + +control.prototype._syncLoad_write = function (data) { + if (data instanceof Array) { + core.status.event.selection = 1; + core.ui.drawConfirmBox("所有本地存档都将被覆盖,确认?", function () { + for (var i = 1; i <= 5 * (main.savePages || 30); i++) { + if (i <= data.length) + core.setLocalForage("save" + i, data[i - 1]); + else if (core.saves.ids[i]) + core.removeLocalForage("save" + i); + } + core.ui.closePanel(); + core.drawText("同步成功!\n你的本地所有存档均已被覆盖。"); + }, function () { + core.status.event.selection = 0; + core.ui._drawSyncSave(); + }); + } + else { + // 只覆盖单存档 + core.setLocalForage("save" + core.saves.saveIndex, data, function () { + core.drawText("同步成功!\n单存档已覆盖至存档" + core.saves.saveIndex); + }); + } +} + +////// 存档到本地 ////// +control.prototype.saveData = function () { + return this.controldata.saveData(); +} + +////// 从本地读档 ////// +control.prototype.loadData = function (data, callback) { + return this.controldata.loadData(data, callback); +} + +control.prototype.getSave = function (index, callback) { + if (index == 0) { + // --- 自动存档先从缓存中获取 + if (core.saves.autosave.data != null) + callback(core.saves.autosave.data); + else { + core.getLocalForage("autoSave", null, function (data) { + if (data != null) { + core.saves.autosave.data = data; + if (!(core.saves.autosave.data instanceof Array)) { + core.saves.autosave.data = [core.saves.autosave.data]; + } + core.saves.autosave.now = core.saves.autosave.data.length; + } + callback(core.saves.autosave.data); + }, function (err) { + console.error(err); + callback(null); + }); + } + return; + } + core.getLocalForage("save" + index, null, function (data) { + if (callback) callback(data); + }, function (err) { + console.error(err); + if (callback) callback(null); + }); +} + +control.prototype.getSaves = function (ids, callback) { + if (!(ids instanceof Array)) return this.getSave(ids, callback); + var count = ids.length, data = {}; + for (var i = 0; i < ids.length; ++i) { + (function (i) { + core.getSave(ids[i], function (result) { + data[i] = result; + if (Object.keys(data).length == count) + callback(data); + }) + })(i); + } +} + +control.prototype.getAllSaves = function (callback) { + var ids = Object.keys(core.saves.ids).filter(function (x) { return x != 0; }) + .sort(function (a, b) { return a - b; }), saves = []; + this.getSaves(ids, function (data) { + for (var i = 0; i < ids.length; ++i) { + if (data[i] != null) + saves.push(data[i]); + } + callback(saves); + }); +} + +////// 获得所有存在存档的存档位 ////// +control.prototype.getSaveIndexes = function (callback) { + var indexes = {}; + core.keysLocalForage(function (err, keys) { + if (err) { + console.error(err); + return callback(indexes); + } + keys.forEach(function (key) { + core.control._getSaveIndexes_getIndex(indexes, key); + }); + callback(indexes); + }); +} + +control.prototype._getSaveIndexes_getIndex = function (indexes, name) { + var e = new RegExp('^' + core.firstData.name + "_(save\\d+|autoSave)$").exec(name); + if (e) { + if (e[1] == 'autoSave') indexes[0] = true; + else indexes[parseInt(e[1].substring(4))] = true; + } +} + +////// 判断某个存档位是否存在存档 ////// +control.prototype.hasSave = function (index) { + return core.saves.ids[index] || false; +} + +////// 删除某个存档 +control.prototype.removeSave = function (index, callback) { + if (index == 0 || index == "autoSave") { + index = "autoSave"; + core.removeLocalForage(index, function () { + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + if (callback) callback(); + }); + return; + } + core.removeLocalForage("save" + index, function () { + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + delete core.saves.favoriteName[index]; + core.control._updateFavoriteSaves(); + if (callback) callback(); + }, function () { + core.playSound('操作失败'); + core.drawTip("无法删除存档!"); + if (callback) callback(); + }); +} + +////// 读取收藏信息 +control.prototype._loadFavoriteSaves = function () { + core.saves.favorite = core.getLocalStorage("favorite", []); + // --- 移除不存在的收藏 + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + core.saves.favoriteName = core.getLocalStorage("favoriteName", {}); +} + +control.prototype._updateFavoriteSaves = function () { + core.setLocalStorage("favorite", core.saves.favorite); + core.setLocalStorage("favoriteName", core.saves.favoriteName); +} + +// ------ 属性,状态,位置,buff,变量,锁定控制等 ------ // + +////// 设置勇士属性 ////// +control.prototype.setStatus = function (name, value) { + if (!core.status.hero) return; + if (name == 'x' || name == 'y' || name == 'direction') + this.setHeroLoc(name, value); + else + core.status.hero[name] = value; +} + +////// 增减勇士属性 ////// +control.prototype.addStatus = function (name, value) { + this.setStatus(name, this.getStatus(name) + value); +} + +////// 获得勇士属性 ////// +control.prototype.getStatus = function (name) { + if (!core.status.hero) return null; + if (name == 'x' || name == 'y' || name == 'direction') + return this.getHeroLoc(name); + if (main.mode == 'editor' && !core.hasFlag('__statistics__')) { + return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name]; + } + return core.status.hero[name]; +} + +////// 从status中获得属性,如果不存在则从勇士属性中获取 ////// +control.prototype.getStatusOrDefault = function (status, name) { + if (status && name in status) + return Math.floor(status[name]); + return Math.floor(this.getStatus(name)); +} + +////// 获得勇士实际属性(增幅后的) ////// +control.prototype.getRealStatus = function (name) { + return this.getRealStatusOrDefault(null, name); +} + +////// 从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 ////// +control.prototype.getRealStatusOrDefault = function (status, name) { + return Math.floor(this.getStatusOrDefault(status, name) * this.getBuff(name)); +} + +////// 获得勇士原始属性(无装备和衰弱影响) ////// +control.prototype.getNakedStatus = function (name) { + var value = this.getStatus(name); + if (value == null) return value; + // 装备增幅 + core.status.hero.equipment.forEach(function (v) { + if (!v || !(core.material.items[v] || {}).equip) return; + value -= core.material.items[v].equip.value[name] || 0; + }); + // 衰弱扣除 + if (core.hasFlag('weak') && core.values.weakValue >= 1 && (name == 'atk' || name == 'def')) { + value += core.values.weakValue; + } + return value; +} + +////// 获得某个属性的名字 ////// +control.prototype.getStatusLabel = function (name) { + if (this.controldata.getStatusLabel) { + return this.controldata.getStatusLabel(name) || name; + } + return { + name: "名称", lv: "等级", hpmax: "生命上限", hp: "生命", manamax: "魔力上限", mana: "魔力", + atk: "攻击", def: "防御", mdef: "护盾", money: "金币", exp: "经验", point: "加点", steps: "步数" + }[name] || name; +} + +////// 设置某个属性的增幅值 ////// +control.prototype.setBuff = function (name, value) { + // 仅保留三位有效buff值 + value = parseFloat(value.toFixed(3)); + this.setFlag('__' + name + '_buff__', value); +} + +////// 加减某个属性的增幅值 ////// +control.prototype.addBuff = function (name, value) { + var buff = this.getBuff(name) + value; + // 仅保留三位有效buff值 + buff = parseFloat(buff.toFixed(3)); + this.setFlag('__' + name + '_buff__', buff); +} + +////// 获得某个属性的增幅值 ////// +control.prototype.getBuff = function (name) { + return core.getFlag('__' + name + '_buff__', 1); +} + +////// 获得或移除毒衰咒效果 ////// +control.prototype.triggerDebuff = function (action, type) { + return this.controldata.triggerDebuff(action, type); +} + +////// 设置勇士的位置 ////// +control.prototype.setHeroLoc = function (name, value, noGather) { + if (!core.status.hero) return; + core.status.hero.loc[name] = value; + if ((name == 'x' || name == 'y') && !noGather) { + this.gatherFollowers(); + } + core.ui.drawStatusBar(); +} + +////// 获得勇士的位置 ////// +control.prototype.getHeroLoc = function (name) { + if (!core.status.hero) return; + if (main.mode == 'editor') { + if (name == null) return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero.loc; + return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero.loc[name]; + } + if (name == null) return core.status.hero.loc; + return core.status.hero.loc[name]; +} + +////// 获得某个等级的名称 ////// +control.prototype.getLvName = function (lv) { + if (!core.status.hero) return null; + if (lv == null) lv = core.status.hero.lv; + return ((core.firstData.levelUp || [])[lv - 1] || {}).title || lv; +} + +////// 获得下个等级所需经验;如果不存在下个等级,返回null。 ////// +control.prototype.getNextLvUpNeed = function () { + if (!core.status.hero) return null; + if (core.status.hero.lv >= core.firstData.levelUp.length) return null; + var need = core.calValue(core.firstData.levelUp[core.status.hero.lv].need); + if (core.flags.statusBarItems.indexOf('levelUpLeftMode') >= 0) + return Math.max(need - core.getStatus('exp'), 0); + else return need; +} + +////// 设置某个自定义变量或flag ////// +control.prototype.setFlag = function (name, value) { + if (value == null) return this.removeFlag(name); + if (!core.status.hero) return; + core.status.hero.flags[name] = value; +} + +////// 增加某个flag数值 ////// +control.prototype.addFlag = function (name, value) { + if (!core.status.hero) return; + core.setFlag(name, core.getFlag(name, 0) + value); +} + +////// 获得某个自定义变量或flag ////// +control.prototype.getFlag = function (name, defaultValue) { + if (!core.status.hero) return defaultValue; + var value = core.status.hero.flags[name]; + return value != null ? value : defaultValue; +} + +////// 是否存在某个自定义变量或flag,且值为true ////// +control.prototype.hasFlag = function (name) { + return !!core.getFlag(name); +} + +////// 删除某个自定义变量或flag ////// +control.prototype.removeFlag = function (name) { + if (!core.status.hero) return; + delete core.status.hero.flags[name]; +} + +////// 获得某个点的独立开关 ////// +control.prototype.getSwitch = function (x, y, floorId, name, defaultValue) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.getFlag(prefix + "@" + name, defaultValue); +} + +////// 设置某个点的独立开关 ////// +control.prototype.setSwitch = function (x, y, floorId, name, value) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.setFlag(prefix + "@" + name, value); +} + +////// 增加某个点的独立开关 ////// +control.prototype.addSwitch = function (x, y, floorId, name, value) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.addFlag(prefix + "@" + name, value); +} + +////// 判定某个点的独立开关 ////// +control.prototype.hasSwitch = function (x, y, floorId, name) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.hasFlag(prefix + "@" + name); +} + +////// 删除某个点的独立开关 ////// +control.prototype.removeSwitch = function (x, y, floorId, name) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.removeFlag(prefix + "@" + name); +} + +////// 锁定状态栏,常常用于事件处理 ////// +control.prototype.lockControl = function () { + core.status.lockControl = true; +} + +////// 解锁状态栏 ////// +control.prototype.unlockControl = function () { + core.status.lockControl = false; +} + +////// 开启debug模式 ////// +control.prototype.debug = function () { + core.setFlag('debug', true); + core.drawText("\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n此模式下将无法上传成绩。"); +} + +control.prototype._bindRoutePush = function () { + core.status.route.push = function (element) { + // 忽视移动、转向、瞬移 + if (["up", "down", "left", "right", "turn"].indexOf(element) < 0 && !element.startsWith("move:")) { + core.clearRouteFolding(); + } + Array.prototype.push.call(core.status.route, element); + } +} + +////// 清除录像折叠信息 ////// +control.prototype.clearRouteFolding = function () { + core.status.routeFolding = {}; +} + +////// 检查录像折叠 ////// +control.prototype.checkRouteFolding = function () { + // 未开启、未开始游戏、录像播放中、正在事件中:不执行 + if (!core.flags.enableRouteFolding || !core.isPlaying() || core.isReplaying() || core.status.event.id) { + return this.clearRouteFolding(); + } + var hero = core.clone(core.status.hero, function (name, value) { + return name != 'steps' && typeof value == 'number'; + }); + var index = [core.getHeroLoc('x'), core.getHeroLoc('y'), core.getHeroLoc('direction').charAt(0)].join(','); + core.status.routeFolding = core.status.routeFolding || {}; + if (core.status.routeFolding[index]) { + var one = core.status.routeFolding[index]; + if (core.same(one.hero, hero) && one.length < core.status.route.length) { + Object.keys(core.status.routeFolding).forEach(function (v) { + if (core.status.routeFolding[v].length >= one.length) delete core.status.routeFolding[v]; + }); + core.status.route = core.status.route.slice(0, one.length); + this._bindRoutePush(); + } + } + core.status.routeFolding[index] = { hero: hero, length: core.status.route.length }; +} + +// ------ 天气,色调,BGM ------ // + +control.prototype.getMappedName = function (name) { + return core.getFlag('__nameMap__', {})[name] || (main.nameMap || {})[name] || name; +} + +////// 更改天气效果 ////// +control.prototype.setWeather = function (type, level) { + // 非雨雪 + if (type == null || !this.weathers[type]) { + core.deleteCanvas('weather') + core.animateFrame.weather.type = null; + core.animateFrame.weather.nodes = []; + return; + } + if (level == null) level = core.animateFrame.weather.level; + level = core.clamp(parseInt(level) || 5, 1, 10); + // 当前天气:则忽略 + if (type == core.animateFrame.weather.type && level == core.animateFrame.weather.level) return; + + // 计算当前的宽高 + core.createCanvas('weather', 0, 0, core._PX_, core._PY_, 80); + core.setOpacity('weather', 1.0); + core.animateFrame.weather.type = type; + core.animateFrame.weather.level = level; + core.animateFrame.weather.nodes = []; + try { + core.doFunc(this.weathers[type].initFunc, this, level); + } catch (e) { + console.error(e); + console.error("ERROR in weather[" + type + "]:已自动注销该项。"); + core.unregisterWeather(type); + } +} + +////// 注册一个天气 ////// +// name为天气类型,如 sun, rain, snow 等 +// initFunc 为设置为此天气时的初始化,接受level参数 +// frameFunc 为该天气下每帧的效果,接受和timestamp参数(从页面加载完毕到当前经过的时间) +control.prototype.registerWeather = function (name, initFunc, frameFunc) { + this.unregisterWeather(name); + this.weathers[name] = { initFunc: initFunc, frameFunc: frameFunc }; +} + +////// 取消注册一个天气 ////// +control.prototype.unregisterWeather = function (name) { + delete this.weathers[name]; + if (core.animateFrame.weather.type == name) { + this.setWeather(null); + } +} + +control.prototype._weather_rain = function (level) { + var number = level * parseInt(20 * core.bigmap.width * core.bigmap.height / (core._WIDTH_ * core._HEIGHT_)); + for (var a = 0; a < number; a++) { + core.animateFrame.weather.nodes.push({ + 'x': Math.random() * core.bigmap.width * 32, + 'y': Math.random() * core.bigmap.height * 32, + 'l': Math.random() * 2.5, + 'xs': -4 + Math.random() * 4 + 2, + 'ys': Math.random() * 10 + 10 + }) + } +} + +control.prototype._weather_snow = function (level) { + var number = level * parseInt(20 * core.bigmap.width * core.bigmap.height / (core._WIDTH_ * core._HEIGHT_)); + for (var a = 0; a < number; a++) { + core.animateFrame.weather.nodes.push({ + 'x': Math.random() * core.bigmap.width * 32, + 'y': Math.random() * core.bigmap.height * 32, + 'r': Math.random() * 5 + 1, + 'd': Math.random() * Math.min(level, 200), + }) + } +} + +control.prototype._weather_fog = function (level) { + if (!core.animateFrame.weather.fog) return; + core.animateFrame.weather.nodes = [{ + 'image': core.animateFrame.weather.fog, + 'level': 40 * level, + 'x': 0, + 'y': -core._PY_ / 2, + 'dx': -Math.random() * 1.5, + 'dy': Math.random(), + 'delta': 0.001, + }]; +} + +control.prototype._weather_cloud = function (level) { + if (!core.animateFrame.weather.cloud) return; + core.animateFrame.weather.nodes = [{ + 'image': core.animateFrame.weather.cloud, + 'level': 40 * level, + 'x': 0, + 'y': -core._PY_ / 2, + 'dx': -Math.random() * 1.5, + 'dy': Math.random(), + 'delta': 0.001, + }]; +} + +control.prototype._weather_sun = function (level) { + if (!core.animateFrame.weather.sun) return; + // 直接绘制 + core.clearMap('weather'); + core.drawImage( + 'weather', core.animateFrame.weather.sun, 0, 0, core.animateFrame.weather.sun.width, core.animateFrame.weather.sun.height, 0, 0, core._PX_, core._PY_ + ); + core.setOpacity('weather', level / 10); + core.animateFrame.weather.nodes = [{ opacity: level / 10, delta: 0.01 }]; +} + +////// 更改画面色调 ////// +control.prototype.setCurtain = function (color, time, moveMode, callback) { + if (time == null) time = 750; + if (time <= 0) time = 0; + if (!core.status.curtainColor) + core.status.curtainColor = [0, 0, 0, 0]; + if (!color) color = [0, 0, 0, 0]; + if (color[3] == null) color[3] = 1; + color[3] = core.clamp(color[3], 0, 1); + + if (time == 0) { + // 直接变色 + core.clearMap('curtain'); + core.fillRect('curtain', 0, 0, core._PX_, core._PY_, core.arrayToRGBA(color)); + core.status.curtainColor = color; + if (callback) callback(); + return; + } + + this._setCurtain_animate(core.status.curtainColor, color, time, moveMode, callback); +} + +control.prototype._setCurtain_animate = function (nowColor, color, time, moveMode, callback) { + time /= Math.max(core.status.replay.speed, 1) + var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) steps = 1; + var curr = nowColor; + var moveFunc = core.applyEasing(moveMode); + + var cb = function () { + core.status.curtainColor = curr; + if (callback) callback(); + } + var animate = setInterval(function () { + step++; + curr = [ + nowColor[0] + (color[0] - nowColor[0]) * moveFunc(step / steps), + nowColor[1] + (color[1] - nowColor[1]) * moveFunc(step / steps), + nowColor[2] + (color[2] - nowColor[2]) * moveFunc(step / steps), + nowColor[3] + (color[3] - nowColor[3]) * moveFunc(step / steps), + ] + core.clearMap('curtain'); + core.fillRect('curtain', 0, 0, core._PX_, core._PY_, core.arrayToRGBA(curr)); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +////// 画面闪烁 ////// +control.prototype.screenFlash = function (color, time, times, moveMode, callback) { + times = times || 1; + time = time / 3; + var nowColor = core.clone(core.status.curtainColor); + core.setCurtain(color, time, moveMode, function () { + core.setCurtain(nowColor, time * 2, moveMode, function () { + if (times > 1) + core.screenFlash(color, time * 3, times - 1, moveMode, callback); + else { + if (callback) callback(); + } + }); + }); +} + +////// 播放背景音乐 ////// +control.prototype.playBgm = function (bgm, startTime) { + bgm = core.getMappedName(bgm); + if (main.mode != 'play' || !core.material.bgms[bgm]) return; + // 如果不允许播放 + if (!core.musicStatus.bgmStatus) { + try { + core.musicStatus.playingBgm = bgm; + core.musicStatus.lastBgm = bgm; + core.material.bgms[bgm].pause(); + } + catch (e) { + console.error(e); + } + return; + } + this.setMusicBtn(); + + try { + this._playBgm_play(bgm, startTime); + } + catch (e) { + console.log("无法播放BGM " + bgm); + console.error(e); + core.musicStatus.playingBgm = null; + } +} + +control.prototype._playBgm_play = function (bgm, startTime) { + // 如果当前正在播放,且和本BGM相同,直接忽略 + if (core.musicStatus.playingBgm == bgm && !core.material.bgms[core.musicStatus.playingBgm].paused) { + return; + } + // 如果正在播放中,暂停 + if (core.musicStatus.playingBgm) { + core.material.bgms[core.musicStatus.playingBgm].pause(); + } + // 缓存BGM + core.loader.loadBgm(bgm); + // 播放当前BGM + core.material.bgms[bgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; + core.material.bgms[bgm].currentTime = startTime || 0; + core.material.bgms[bgm].play(); + core.musicStatus.playingBgm = bgm; + core.musicStatus.lastBgm = bgm; + core.setBgmSpeed(100); + + const bgm1 = core.material.bgms[bgm];//todo 后续新增的bgm同步到这里 + bgm1.addEventListener('ended', function () { + //console.log('当前播放的歌曲已结束'); + //背景音乐切换 + function playbgm() { + core.setVolume(0.8); + var bgm = ""; + if (core.status.floorId == null) { + if (core.rand2(2) == 0) { + bgm = 'menu1.mp3'; + } else { + bgm = 'menu3.mp3'; + } + } else { + var r1 = core.rand2(3); + if (r1 != 0) { + var x = core.status.hero.loc.x; + var y = core.status.hero.loc.y; + var desert = 0; + var forest = 0; + var ocean = 0; + var snowfield = 0; + for (var i = -6; i <= 6; i++) { + for (var j = -6; j <= 6; j++) { + var x1 = x + i; + var y1 = y + j; + if (x1 >= 0 && x1 <= 63 && y1 >= 0 && y1 <= 63) { + if (core.getBgNumber(x1, y1) == 343 || + core.getBgNumber(x1, y1) == 344) { + desert += 1; + } else if (core.getBgNumber(x1, y1) == 339 || + core.getBgNumber(x1, y1) == 340) { + forest += 1; + } else if (core.getBgNumber(x1, y1) == 345 || + core.getBgNumber(x1, y1) == 348) { + ocean += 1; + } else if (core.getBgNumber(x1, y1) == 341 || + core.getBgNumber(x1, y1) == 342) { + snowfield += 1; + } + } + } + } + if (Math.max(desert, forest, ocean, snowfield) == 0) { + r1 = 0; + } else { + if (desert == Math.max(desert, forest, ocean, snowfield)) { + if (core.rand2(2) == 0) { + bgm = 'desert1.mp3'; + } else { + bgm = 'desert2.mp3'; + } + } else if (forest == Math.max(desert, forest, ocean, snowfield)) { + if (core.rand2(2) == 0) { + bgm = 'forest2.mp3'; + } else { + bgm = 'forest5.mp3'; + } + } else if (ocean == Math.max(desert, forest, ocean, snowfield)) { + if (core.rand2(2) == 0) { + bgm = 'ocean1.mp3'; + } else { + bgm = 'ocean3.mp3'; + } + } else if (snowfield == Math.max(desert, forest, ocean, snowfield)) { + if (core.rand2(2) == 0) { + bgm = 'snowfield1.mp3'; + } else { + bgm = 'snowfield2.mp3'; + } + } + } + } + if (r1 == 0) { + var r2 = core.rand2(8); + switch (r2) { + case 0: + bgm = 'zhu1.mp3'; + break; + case 1: + bgm = 'zhu3.mp3'; + break; + case 2: + bgm = 'zhu4.mp3'; + break; + case 3: + bgm = 'zhu5.mp3'; + break; + case 4: + bgm = 'zhu10.mp3'; + break; + case 5: + bgm = 'zhu11.mp3'; + break; + case 6: + bgm = 'zhu12.mp3'; + break; + case 7: + bgm = 'zhu16.mp3'; + break; + default: + bgm = 'zhu1.mp3'; + break; + } + } + } + setTimeout(function () { + core.playBgm(bgm); + //console.log(bgm); + }, (core.rand2(21) + 10) * 1000); + } + playbgm(); + }, { once: true }); +} + +///// 设置当前背景音乐的播放速度 ////// +control.prototype.setBgmSpeed = function (speed, usePitch) { + var bgm = core.musicStatus.playingBgm; + if (main.mode != 'play' || !core.material.bgms[bgm]) return; + bgm = core.material.bgms[bgm]; + if (speed < 30 || speed > 300) return; + bgm.playbackRate = speed / 100; + core.musicStatus.bgmSpeed = speed; + + if (bgm.preservesPitch != null) { + if (bgm.__preservesPitch == null) bgm.__preservesPitch = bgm.preservesPitch; + if (usePitch == null) bgm.preservesPitch = bgm.__preservesPitch; + else if (usePitch) bgm.preservesPitch = false; + else bgm.preservesPitch = true; + core.musicStatus.bgmUsePitch = usePitch; + } +} + +////// 暂停背景音乐的播放 ////// +control.prototype.pauseBgm = function () { + if (main.mode != 'play') return; + try { + if (core.musicStatus.playingBgm) { + core.musicStatus.pauseTime = core.material.bgms[core.musicStatus.playingBgm].currentTime; + core.material.bgms[core.musicStatus.playingBgm].pause(); + core.musicStatus.playingBgm = null; + } + } + catch (e) { + console.log("无法暂停BGM"); + console.error(e); + } + this.setMusicBtn(); +} + +////// 恢复背景音乐的播放 ////// +control.prototype.resumeBgm = function (resumeTime) { + if (main.mode != 'play') return; + try { + var speed = core.musicStatus.bgmSpeed; + var usePitch = core.musicStatus.bgmUsePitch; + core.playBgm(core.musicStatus.playingBgm || core.musicStatus.lastBgm || main.startBgm, + resumeTime ? core.musicStatus.pauseTime : 0); + if (resumeTime) { + core.setBgmSpeed(speed, usePitch); + } + } + catch (e) { + console.log("无法恢复BGM"); + console.error(e); + } + this.setMusicBtn(); +} + +control.prototype.setMusicBtn = function () { + if (core.musicStatus.bgmStatus) + core.dom.musicBtn.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABWVBMVEX///9iYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmL///8AAAC5ubn+/v6xsbEtLS0MDAxmZmZoaGhvb2/c3Nzd3d38/Pz9/f0oKCgpKSl0dHR1dXW6urrb29v7+/v09PTv7+/39/cgICACAgImJibh4eGFhYWGhoaHh4eOjo5paWm7u7vDw8PMzMwyMjI7OztAQEDe3t5FRUVMTEzj4+Pl5eXm5ubp6enr6+tcXFzi4uL19fVeXl74+PgjIyNkZGQGBgaSkpKYmJiampqenp4DAwMwMDBnZ2cICAivr68eHh63t7cLCwsSEhLw8PBhYWEUFBQVFRXNzc3Pz8/Z2dna2toaGhqkpKSlpaWpqamrq6tFOUNAAAAAc3RSTlMAAwQFBhUWGxwkJSYyO0dISVBRUmpvj5CSk5SVoaOlpqiysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyA0IuUgAAAVdJREFUeF5NkVVbw0AQRTcQrLR4IIEGcidJoaUuQHF3d3d3+P/CkuxCzss8nG++mbnDBJXhNt2CpbeFK1kQpSEKidlc8S9qdATRa6UIdQMoxEpDA0Ov3wUAPfW+qLWACydNv9zMrzkJwPK6FB3oHyOfXfuNxvoBQ+GmBYinhHB77TmiVBxoYUw1AYcEq332AS8OYKosAuTT0nza9uU2USYPRJgGxEiSOFywJ3mNARozgBJJzkfLvfu8JgGDWcC9FEsjWzR+y80gYDEAA8QZ3N6kmP1Fs3fEASB7pob7Hh+Wz5L0ci17Or05J7bH6B6dZv05XWK3rG+myV05Ert592Qo55sPuoIr7hEZHHtieIPWy0RU9DLwc3Mnck/vi8/E8XNrDWQtEVnL/ySKMrv0jPwPp870fprcyYifmiEmqGpHkI5q9ofSFIUk2qiwIGpEMyxYhhZRRcMPz89RJ2s9W8wAAAAASUVORK5CYII="; + else + core.dom.musicBtn.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABYlBMVEX///9iYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmL////8/PwAAABmZmZoaGihoaGioqKxsbG5ubnb29vc3Nzd3d3h4eHi4uL9/f3+/v4tLS1nZ2d0dHSUlJSenp66uroMDAz7+/spKSkoKCgUFBRpaWkVFRVvb291dXU7OzuVlZWYmJhkZGQgICAjIyOkpKQCAgK3t7cGBgbv7++pqamrq6seHh4mJiZhYWGamprp6enr6+saGhpeXl7j4+Pl5eXm5uZKSkrw8PD09PT19fW7u7vDw8PMzMwICAgwMDAyMjILCwtAQECGhoaHh4eBgYGFhYUSEhJXV1dZWVlcXFyOjo6SkpLNzc339/fPz8/Z2dna2tqTk5OlpaWxOPeTAAAAdnRSTlMAAwQFBhUWGxwkJSYyO0dISVBRUmpvj5CSk5SVoaOlpqiysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyNuo+uwAAAWJJREFUeF5NkmV34zAQReUm7WbTuJBNunY3bvXGDjNTkZkZlpn5/9eR5FPfbzr3jGb0RkwRiMQMDm7EIgHmRxtLwMOaHHoQjwz4MUKeCM8AWMrmd7u7f/aXAMyOShHiQD1n04DtN5e5FMBFlSauIsm585dKi4CpuSYKJIv1tBDVmvOSqJgEoowFLSBHaQh10XHWiCgHWEGmAw2blPrvOK/KRJUGoLM4kCVSKrWz7HwgoiwQZyaQJ0+9PvxV23BNATAZB25IqX9b3+jTW9fcApwB6NLgUD5NY3mPXnwmFwBezff1ztzRFzTp94FXMy36HDuCa2RafdnnmZqtL818Gl9/qNnEeyrUk2aTPiKj3qMyWBVi/YSuWq5qiwxkbtX3vYWzdz/l8M0k8ERlvViiB1Ygslb7SbVtJezncj+Cx5bYaeGuonZqhZlieAp+no74/s5EAh6JcY35Cepxk4ObcT3IJPe/1lKsDpFCFQAAAABJRU5ErkJggg=="; +} + +////// 更改背景音乐的播放 ////// +control.prototype.triggerBgm = function () { + if (main.mode != 'play') return; + + core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; + if (core.musicStatus.bgmStatus) + this.resumeBgm(); + else + this.pauseBgm(); + core.setLocalStorage('bgmStatus', core.musicStatus.bgmStatus); +} + +////// 播放音频 ////// +control.prototype.playSound = function (sound, pitch, callback, volume) { + sound = core.getMappedName(sound); + if (main.mode != 'play' || !core.musicStatus.soundStatus || !core.material.sounds[sound]) return; + try { + if (core.musicStatus.audioContext != null) { + var source = core.musicStatus.audioContext.createBufferSource(); + source.__name = sound; + source.buffer = core.material.sounds[sound]; + source.connect(core.musicStatus.gainNode); + var id = setTimeout(null); + if (pitch && pitch >= 30 && pitch <= 300) { + source.playbackRate.setValueAtTime(pitch / 100, 0); + } + source.onended = function () { + delete core.musicStatus.playingSounds[id]; + if (callback) callback(); + } + core.musicStatus.playingSounds[id] = source; + if (source.start) source.start(0); + else if (source.noteOn) source.noteOn(0); + return id; + } + else { + core.material.sounds[sound].volume = core.musicStatus.userVolume2; + core.material.sounds[sound].play(); + if (callback) callback(); + } + } + catch (e) { + console.log("无法播放SE " + sound); + console.error(e); + } +} + +////// 停止所有音频 ////// +control.prototype.stopSound = function (id) { + if (id == null) { + Object.keys(core.musicStatus.playingSounds).forEach(function (id) { + core.control.stopSound(id); + }); + return; + } + var source = core.musicStatus.playingSounds[id]; + if (!source) return; + try { + if (source.stop) source.stop(); + else if (source.noteOff) source.noteOff(); + } + catch (e) { + console.error(e); + } + delete core.musicStatus.playingSounds[id]; +} + +////// 获得当前正在播放的所有(指定)音效的id列表 ////// +control.prototype.getPlayingSounds = function (name) { + name = core.getMappedName(name); + return Object.keys(core.musicStatus.playingSounds).filter(function (one) { + return name == null || core.musicStatus.playingSounds[one].__name == name + }); +} + +////// 检查bgm状态 ////// +control.prototype.checkBgm = function () { + core.playBgm(core.musicStatus.playingBgm || main.startBgm); +} + +///// 设置屏幕放缩 ////// +control.prototype.setDisplayScale = function (delta) { + var index = core.domStyle.availableScale.indexOf(core.domStyle.scale); + if (index < 0) return; + index = (index + delta + core.domStyle.availableScale.length) % core.domStyle.availableScale.length; + core.domStyle.scale = core.domStyle.availableScale[index]; + core.setLocalStorage('scale', core.domStyle.scale); + core.resize(); +} + +// ------ 状态栏,工具栏等相关 ------ // + +////// 清空状态栏 ////// +control.prototype.clearStatusBar = function () { + Object.keys(core.statusBar).forEach(function (e) { + if (core.statusBar[e].innerHTML != null) { + core.statusBar[e].innerHTML = " "; + core.statusBar[e].removeAttribute('_style'); + core.statusBar[e].removeAttribute('_value'); + } + }) + core.statusBar.image.book.style.opacity = 0.3; + if (!core.flags.equipboxButton) + core.statusBar.image.fly.style.opacity = 0.3; +} + +////// 更新状态栏 ////// +control.prototype.updateStatusBar = function (doNotCheckAutoEvents, immediate) { + if (!core.isPlaying()) return; + if (immediate) { + return this.updateStatusBar_update(); + } + if (!doNotCheckAutoEvents) this.noAutoEvents = false; + if (core.isReplaying()) return this.updateStatusBar_update(); + if (!core.control.updateNextFrame) { + core.control.updateNextFrame = true; + requestAnimationFrame(this.updateStatusBar_update); + } +}; + +control.prototype.updateStatusBar_update = function () { + core.control.updateNextFrame = false; + if (!core.isPlaying() || core.hasFlag('__statistics__')) return; + core.control.controldata.updateStatusBar(); + if (!core.control.noAutoEvents) core.checkAutoEvents(); + core.control._updateStatusBar_setToolboxIcon(); + core.clearRouteFolding(); + core.control.noAutoEvents = true; +}; + +control.prototype._updateStatusBar_setToolboxIcon = function () { + if (core.isReplaying()) { + core.statusBar.image.book.src = core.status.replay.pausing ? core.statusBar.icons.play.src : core.statusBar.icons.pause.src; + core.statusBar.image.book.style.opacity = 1; + core.statusBar.image.fly.src = core.statusBar.icons.stop.src; + core.statusBar.image.fly.style.opacity = 1; + core.statusBar.image.toolbox.src = core.statusBar.icons.rewind.src; + core.statusBar.image.keyboard.src = core.statusBar.icons.book.src; + core.statusBar.image.shop.src = core.statusBar.icons.floor.src; + core.statusBar.image.save.src = core.statusBar.icons.speedDown.src; + core.statusBar.image.save.style.opacity = 1; + core.statusBar.image.load.src = core.statusBar.icons.speedUp.src; + core.statusBar.image.settings.src = core.statusBar.icons.save.src; + } + else { + core.statusBar.image.book.src = core.statusBar.icons.book.src; + core.statusBar.image.book.style.opacity = core.hasItem('book') ? 1 : 0.3; + if (!core.flags.equipboxButton) { + core.statusBar.image.fly.src = core.statusBar.icons.fly.src; + core.statusBar.image.fly.style.opacity = core.hasItem('fly') ? 1 : 0.3; + } + else { + core.statusBar.image.fly.src = core.statusBar.icons.equipbox.src; + core.statusBar.image.fly.style.opacity = 1; + } + core.statusBar.image.toolbox.src = core.statusBar.icons.toolbox.src; + core.statusBar.image.keyboard.src = core.statusBar.icons.keyboard.src; + core.statusBar.image.shop.src = core.statusBar.icons.shop.src; + core.statusBar.image.save.src = core.statusBar.icons.save.src; + core.statusBar.image.save.style.opacity = core.hasFlag('__forbidSave__') ? 0.3 : 1; + core.statusBar.image.load.src = core.statusBar.icons.load.src; + core.statusBar.image.settings.src = core.statusBar.icons.settings.src; + } +} + +control.prototype.showStatusBar = function () { + if (main.mode == 'editor') return; + if (core.domStyle.showStatusBar) return; + var statusItems = core.dom.status; + core.domStyle.showStatusBar = true; + core.removeFlag('hideStatusBar'); + // 显示 + for (var i = 0; i < statusItems.length; ++i) + statusItems[i].style.opacity = 1; + this.setToolbarButton(false); + core.dom.tools.hard.style.display = 'block'; + core.dom.toolBar.style.display = 'block'; +} + +control.prototype.hideStatusBar = function (showToolbox) { + if (main.mode == 'editor') return; + + // 如果原本就是隐藏的,则先显示 + if (!core.domStyle.showStatusBar) + this.showStatusBar(); + if (core.isReplaying()) showToolbox = true; + + var statusItems = core.dom.status, toolItems = core.dom.tools; + core.domStyle.showStatusBar = false; + core.setFlag('hideStatusBar', true); + core.setFlag('showToolbox', showToolbox || null); + // 隐藏 + for (var i = 0; i < statusItems.length; ++i) + statusItems[i].style.opacity = 0; + if ((!core.domStyle.isVertical && !core.flags.extendToolbar) || !showToolbox) { + for (var i = 0; i < toolItems.length; ++i) + toolItems[i].style.display = 'none'; + } + if (!core.domStyle.isVertical && !core.flags.extendToolbar) { + core.dom.toolBar.style.display = 'none'; + } +} + +////// 更新状态栏的勇士图标 ////// +control.prototype.updateHeroIcon = function (name) { + name = name || "hero.png"; + if (core.statusBar.icons.name == name) return; + core.statusBar.icons.name = name; + + var image = core.material.images.hero; + // 全身图 + var w = core.material.icons.hero.width || 32; + var h = core.material.icons.hero.height || 48; + var ratio = Math.min(w / h, 1), width = 32 * ratio, left = 16 - width / 2; + + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext("2d"); + canvas.width = 32; + canvas.height = 32; + core.drawImage(ctx, image, 0, 0, w, h, left, 0, width, 32); + + core.statusBar.image.name.src = canvas.toDataURL("image/png"); +} + +////// 改变工具栏为按钮1-8 ////// +control.prototype.setToolbarButton = function (useButton) { + if (!core.domStyle.showStatusBar) { + // 隐藏状态栏时检查竖屏 + if (!core.domStyle.isVertical && !core.flags.extendToolbar) { + for (var i = 0; i < core.dom.tools.length; ++i) + core.dom.tools[i].style.display = 'none'; + return; + } + if (!core.hasFlag('showToolbox')) return; + else core.dom.tools.hard.style.display = 'block'; + } + + if (useButton == null) useButton = core.domStyle.toolbarBtn; + if ((!core.domStyle.isVertical && !core.flags.extendToolbar)) useButton = false; + core.domStyle.toolbarBtn = useButton; + + if (useButton) { + ["book", "fly", "toolbox", "keyboard", "shop", "save", "load", "settings"].forEach(function (t) { + core.statusBar.image[t].style.display = 'none'; + }); + ["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(function (t) { + core.statusBar.image[t].style.display = 'block'; + }) + main.statusBar.image.btn8.style.filter = core.getLocalStorage('altKey') ? 'sepia(1) contrast(1.5)' : ''; + } + else { + ["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(function (t) { + core.statusBar.image[t].style.display = 'none'; + }); + ["book", "fly", "toolbox", "save", "load", "settings"].forEach(function (t) { + core.statusBar.image[t].style.display = 'block'; + }); + core.statusBar.image.keyboard.style.display + = core.statusBar.image.shop.style.display + = core.domStyle.isVertical || core.flags.extendToolbar ? "block" : "none"; + } +} + +////// ------ resize处理 ------ // + +control.prototype._shouldDisplayStatus = function (id) { + if (id == null) { + var toDraw = [], status = core.dom.status; + for (var i = 0; i < status.length; ++i) { + var dom = core.dom.status[i], idCol = dom.id; + if (idCol.indexOf("Col") != idCol.length - 3) continue; + var id = idCol.substring(0, idCol.length - 3); + if (!this._shouldDisplayStatus(id)) continue; + toDraw.push(id); + } + return toDraw; + } + var obj = {}; + core.flags.statusBarItems.forEach(function (v) { obj[v] = true; }) + switch (id) { + case 'floor': return obj.enableFloor; + case 'name': return obj.enableName; + case 'lv': return obj.enableLv; + case 'hp': return obj.enableHP; + case 'hpmax': return obj.enableHPMax; + case 'mana': return obj.enableMana; + case 'atk': return obj.enableAtk; + case 'def': return obj.enableDef; + case 'mdef': return obj.enableMDef; + case 'money': return obj.enableMoney; + case 'exp': return obj.enableExp && !obj.levelUpLeftMode; + case 'up': return obj.enableLevelUp; + case 'skill': return obj.enableSkill; + case 'key': return obj.enableKeys; + case 'pzf': return obj.enablePZF; + case 'debuff': return obj.enableDebuff; + default: return true; + } +} + +////// 注册一个resize函数 ////// +// name为名称,可供注销使用 +// func可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。 +control.prototype.registerResize = function (name, func) { + this.unregisterResize(name); + this.resizes.push({ name: name, func: func }); +} + +////// 注销一个resize函数 ////// +control.prototype.unregisterResize = function (name) { + this.resizes = this.resizes.filter(function (b) { return b.name != name; }); +} + +control.prototype._doResize = function (obj) { + for (var i in this.resizes) { + try { + if (core.doFunc(this.resizes[i].func, this, obj)) return true; + } catch (e) { + console.error(e); + console.error("ERROR in resizes[" + this.resizes[i].name + "]:已自动注销该项。"); + this.unregisterResize(this.resizes[i].name); + } + } + return false; +} + +////// 屏幕分辨率改变后重新自适应 ////// +control.prototype.resize = function () { + if (main.mode == 'editor') return; + var clientWidth = main.dom.body.clientWidth, clientHeight = main.dom.body.clientHeight; + var BORDER = 3; + var extendToolbar = core.flags.extendToolbar; + let hideLeftStatusBar = core.flags.hideLeftStatusBar; + var BAR_WIDTH = hideLeftStatusBar ? 0 : Math.round(core._PY_ * 0.31); + + var horizontalMaxRatio = (clientHeight - 2 * BORDER - (hideLeftStatusBar ? BORDER : 0)) / (core._PY_ + (hideLeftStatusBar ? 38 : 0)); + + if (clientWidth - 3 * BORDER >= core._PX_ + BAR_WIDTH || (clientWidth > clientHeight && horizontalMaxRatio < 1)) { + // 横屏 + core.domStyle.isVertical = false; + + core.domStyle.availableScale = []; + [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5].forEach(function (v) { + if (clientWidth - 3 * BORDER >= v * (core._PX_ + BAR_WIDTH) && horizontalMaxRatio >= v) { + core.domStyle.availableScale.push(v); + } + }); + if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { + core.domStyle.scale = Math.min(1, horizontalMaxRatio); + } + } + else { + // 竖屏 + core.domStyle.isVertical = true; + core.domStyle.scale = Math.min((clientWidth - 2 * BORDER) / core._PX_); + core.domStyle.availableScale = []; + extendToolbar = false; + hideLeftStatusBar = false; + BAR_WIDTH = Math.round(core._PX_ * 0.3); + } + + var statusDisplayArr = this._shouldDisplayStatus(), count = statusDisplayArr.length; + var statusCanvas = core.flags.statusCanvas, statusCanvasRows = core.values.statusCanvasRowsOnMobile || 3; + var col = statusCanvas ? statusCanvasRows : Math.ceil(count / 3); + if (col > 5) { + if (statusCanvas) alert("自绘状态栏的在竖屏下的行数应不超过5!"); + else alert("当前状态栏数目(" + count + ")大于15,请调整到不超过15以避免手机端出现显示问题。"); + } + var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; + + var obj = { + clientWidth: clientWidth, + clientHeight: clientHeight, + BORDER: BORDER, + BAR_WIDTH: BAR_WIDTH, + TOOLBAR_HEIGHT: 38, + outerWidth: core._PX_ * core.domStyle.scale + 2 * BORDER, + outerHeight: core._PY_ * core.domStyle.scale + 2 * BORDER, + globalAttribute: globalAttribute, + border: '3px ' + core.arrayToRGBA(globalAttribute.borderColor) + ' solid', + statusDisplayArr: statusDisplayArr, + count: count, + col: col, + statusBarHeightInVertical: core.domStyle.isVertical ? (32 * col + 6) * core.domStyle.scale + 2 * BORDER : 0, + toolbarHeightInVertical: core.domStyle.isVertical ? 38 * core.domStyle.scale + 2 * BORDER : 0, + extendToolbar: extendToolbar, + is15x15: false, + hideLeftStatusBar + }; + + this._doResize(obj); + this.setToolbarButton(); + core.updateStatusBar(); +} + +control.prototype._resize_gameGroup = function (obj) { + var startBackground = core.domStyle.isVertical ? (main.styles.startVerticalBackground || main.styles.startBackground) : main.styles.startBackground; + if (main.dom.startBackground.getAttribute('__src__') != startBackground) { + main.dom.startBackground.setAttribute('__src__', startBackground); + main.dom.startBackground.src = startBackground; + } + + var gameGroup = core.dom.gameGroup; + var totalWidth, totalHeight; + if (core.domStyle.isVertical) { + totalWidth = obj.outerWidth; + totalHeight = obj.outerHeight + obj.statusBarHeightInVertical + obj.toolbarHeightInVertical + } + else { + totalWidth = obj.outerWidth + obj.BAR_WIDTH * core.domStyle.scale + (obj.hideLeftStatusBar ? 0 : obj.BORDER); + totalHeight = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0); + } + gameGroup.style.width = totalWidth + "px"; + gameGroup.style.height = totalHeight + "px"; + gameGroup.style.left = (obj.clientWidth - totalWidth) / 2 + "px"; + gameGroup.style.top = (obj.clientHeight - totalHeight) / 2 + "px"; + // floorMsgGroup + var floorMsgGroup = core.dom.floorMsgGroup; + floorMsgGroup.style = obj.globalAttribute.floorChangingStyle; + floorMsgGroup.style.width = obj.outerWidth - 2 * obj.BORDER + "px"; + floorMsgGroup.style.height = totalHeight - 2 * obj.BORDER + "px"; + floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; + // startPanel + core.dom.startPanel.style.fontSize = 16 * core.domStyle.scale + "px"; + // musicBtn + if (core.domStyle.isVertical || core.domStyle.scale < 1) { + core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px"; + } + else { + core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px"; + core.dom.musicBtn.style.bottom = (obj.clientHeight - totalHeight) / 2 - 27 + "px"; + } +} + +control.prototype._resize_canvas = function (obj) { + var innerWidth = (core._PX_ * core.domStyle.scale) + "px", innerHeight = (core._PY_ * core.domStyle.scale) + "px"; + if (!core.isPlaying()) { + for (var i = 0; i < core.dom.gameCanvas.length; ++i) { + var ctx = core.dom.gameCanvas[i].getContext('2d'); + core.resizeCanvas(ctx, core._PX_, core._PY_); + core.dom.gameCanvas[i].style.width = innerWidth; + core.dom.gameCanvas[i].style.height = innerHeight; + } + } else { + requestAnimationFrame(function () { + for (var i = 0; i < core.dom.gameCanvas.length; ++i) { + core.dom.gameCanvas[i].style.width = innerWidth; + core.dom.gameCanvas[i].style.height = innerHeight; + } + }); + } + core.dom.gif.style.width = innerWidth; + core.dom.gif.style.height = innerHeight; + core.dom.gif2.style.width = innerWidth; + core.dom.gif2.style.height = innerHeight; + core.dom.gameDraw.style.width = innerWidth; + core.dom.gameDraw.style.height = innerHeight; + core.dom.gameDraw.style.top = obj.statusBarHeightInVertical + "px"; + core.dom.gameDraw.style.right = 0; + core.dom.gameDraw.style.border = obj.border; + // resize bigmap + core.bigmap.canvas.forEach(function (cn) { + var ratio = core.canvas[cn].canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1; + core.canvas[cn].canvas.style.width = core.canvas[cn].canvas.width / ratio * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = core.canvas[cn].canvas.height / ratio * core.domStyle.scale + "px"; + }); + // resize dynamic canvas + if (!core.isPlaying()) { + for (var name in core.dymCanvas) { + var ctx = core.dymCanvas[name], canvas = ctx.canvas; + // core.maps._setHDCanvasSize(ctx, parseFloat(canvas.getAttribute('_width')), parseFloat(canvas.getAttribute('_height'))); + canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + var scale = canvas.getAttribute('_scale') || 1; + core.resizeCanvas(canvas, canvas.width * scale / core.domStyle.scale, canvas.height * scale / core.domStyle.scale); + } + } else { + for (var name in core.dymCanvas) { + var ctx = core.dymCanvas[name], canvas = ctx.canvas; + core.resizeCanvas(ctx, parseFloat(canvas.getAttribute("_width")), parseFloat(canvas.getAttribute("_height"))) + canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + } + } + // resize next + main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px"; + main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; + /*var title = document.getElementById('title'); + if (title) { + if (core.domStyle.isVertical) { + title.style.width = newCanvas.style.height = 422 * core.domStyle.scale + 'px'; + } else { + title.style.width = 554 * core.domStyle.scale + 'px'; + title.style.height = 422 * core.domStyle.scale + 'px'; + } + }*/ +} + +control.prototype._resize_statusBar = function (obj) { + // statusBar + var statusBar = core.dom.statusBar; + if (core.domStyle.isVertical) { + statusBar.style.width = obj.outerWidth + "px"; + statusBar.style.height = obj.statusBarHeightInVertical + "px"; + statusBar.style.background = obj.globalAttribute.statusTopBackground; + statusBar.style.fontSize = 16 * core.domStyle.scale + "px"; + } + else { + statusBar.style.width = (obj.BAR_WIDTH * core.domStyle.scale + obj.BORDER) + "px"; + statusBar.style.height = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0) + "px"; + statusBar.style.background = obj.globalAttribute.statusLeftBackground; + // --- 计算文字大小 + if (obj.hideLeftStatusBar) { + statusBar.style.fontSize = 16 * core.domStyle.scale + "px"; + } else { + statusBar.style.fontSize = 16 * Math.min(1, (core._HEIGHT_ - 4) / obj.count) * core.domStyle.scale + "px"; + } + } + statusBar.style.display = obj.hideLeftStatusBar ? 'none' : 'block'; + statusBar.style.borderTop = statusBar.style.borderLeft = obj.border; + statusBar.style.borderRight = core.domStyle.isVertical ? obj.border : ''; + statusBar.style.borderBottom = core.domStyle.isVertical ? '' : obj.border; + // 自绘状态栏 + if (core.domStyle.isVertical) { + core.dom.statusCanvas.style.width = core._PX_ * core.domStyle.scale + "px"; + core.dom.statusCanvas.style.height = obj.statusBarHeightInVertical - 3 + "px"; + core.maps._setHDCanvasSize(core.dom.statusCanvasCtx, core._PX_, obj.col * 32 + 9); + } + else { + core.dom.statusCanvas.style.width = obj.BAR_WIDTH * core.domStyle.scale + "px"; + core.dom.statusCanvas.style.height = obj.outerHeight - 2 * obj.BORDER + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0) + "px"; + core.maps._setHDCanvasSize(core.dom.statusCanvasCtx, obj.BAR_WIDTH, core._PY_ + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT + obj.BORDER : 0)); + } + core.dom.statusCanvas.style.display = core.flags.statusCanvas && !obj.hideLeftStatusBar ? "block" : "none"; +} + +control.prototype._resize_status = function (obj) { + var statusHeight; + if (core.domStyle.isVertical) { + statusHeight = 32 * core.domStyle.scale * 0.8; + } else { + statusHeight = (obj.hideLeftStatusBar ? core._HEIGHT_ : core._HEIGHT_ - 4) / obj.count * 32 * core.domStyle.scale * 0.8; + } + // status + for (var i = 0; i < core.dom.status.length; ++i) { + var id = core.dom.status[i].id, style = core.dom.status[i].style; + if (id.endsWith("Col")) id = id.substring(0, id.length - 3); + style.display = core.flags.statusCanvas || obj.statusDisplayArr.indexOf(id) < 0 ? 'none' : 'block'; + style.margin = 3 * core.domStyle.scale + "px"; + style.height = statusHeight + "px"; + style.maxWidth = obj.BAR_WIDTH * core.domStyle.scale * (core.domStyle.isVertical ? 0.95 : 1) + obj.BORDER + "px"; + if (obj.is15x15 && !core.domStyle.isVertical) + style.marginLeft = 11 * core.domStyle.scale + "px"; + } + // statusLabels, statusTexts + for (var i = 0; i < core.dom.statusLabels.length; ++i) { + core.dom.statusLabels[i].style.lineHeight = statusHeight + "px"; + core.dom.statusLabels[i].style.marginLeft = 6 * core.domStyle.scale + "px"; + } + for (var i = 0; i < core.dom.statusTexts.length; ++i) { + core.dom.statusTexts[i].style.color = core.arrayToRGBA(obj.globalAttribute.statusBarColor); + } + // keys + if (core.flags.statusBarItems.indexOf('enableGreenKey') >= 0) { + core.dom.keyCol.style.fontSize = '0.75em'; + core.statusBar.greenKey.style.display = ''; + } else { + core.dom.keyCol.style.fontSize = ''; + core.statusBar.greenKey.style.display = 'none'; + } +} + +control.prototype._resize_toolBar = function (obj) { + // toolBar + var toolBar = core.dom.toolBar; + if (core.domStyle.isVertical) { + toolBar.style.left = 0; + toolBar.style.right = ""; + toolBar.style.width = obj.outerWidth + "px"; + toolBar.style.top = obj.statusBarHeightInVertical + obj.outerHeight + "px"; + toolBar.style.height = obj.toolbarHeightInVertical + "px"; + toolBar.style.background = obj.globalAttribute.toolsBackground; + } + else { + if (obj.extendToolbar || obj.hideLeftStatusBar) { + toolBar.style.left = ""; + toolBar.style.right = 0; + toolBar.style.width = obj.outerWidth + "px"; + toolBar.style.top = obj.outerHeight + "px"; + toolBar.style.height = obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER + "px"; + toolBar.style.background = obj.globalAttribute.toolsBackground; + } else { + toolBar.style.left = 0; + toolBar.style.right = ""; + toolBar.style.width = obj.BAR_WIDTH * core.domStyle.scale + obj.BORDER + "px"; + toolBar.style.top = 0.75 * obj.outerHeight + "px"; + toolBar.style.height = 0.25 * obj.outerHeight + "px"; + toolBar.style.background = 'transparent'; + } + } + toolBar.style.borderLeft = obj.border; + toolBar.style.borderRight = toolBar.style.borderBottom = core.domStyle.isVertical || obj.extendToolbar ? obj.border : ''; + toolBar.style.fontSize = 16 * core.domStyle.scale + "px"; + + if (!core.domStyle.showStatusBar && !core.domStyle.isVertical && !obj.extendToolbar) { + toolBar.style.display = 'none'; + } else { + toolBar.style.display = 'block'; + } +} + +control.prototype._resize_tools = function (obj) { + var toolsHeight = 32 * core.domStyle.scale * ((core.domStyle.isVertical || obj.extendToolbar) && !obj.is15x15 ? 0.95 : 1); + var toolsMarginLeft; + if (core.domStyle.isVertical || obj.extendToolbar) + toolsMarginLeft = (core._HALF_WIDTH_ - 3) * 3 * core.domStyle.scale; + else + toolsMarginLeft = (obj.BAR_WIDTH * core.domStyle.scale - 9 - toolsHeight * 3) / 4; + for (var i = 0; i < core.dom.tools.length; ++i) { + var style = core.dom.tools[i].style; + style.height = toolsHeight + "px"; + style.marginLeft = toolsMarginLeft + "px"; + style.marginTop = 3 * core.domStyle.scale + "px" + } + core.dom.hard.style.lineHeight = toolsHeight + "px"; + if (core.domStyle.isVertical || obj.extendToolbar) { + core.dom.hard.style.width = obj.outerWidth - 9 * toolsMarginLeft - 8.5 * toolsHeight - 12 + "px"; + } + else { + core.dom.hard.style.width = obj.BAR_WIDTH * core.domStyle.scale - 9 - 2 * toolsMarginLeft + "px"; + if (!obj.is15x15) core.dom.hard.style.marginTop = 0; + } +} diff --git a/core.d.ts b/core.d.ts new file mode 100644 index 0000000..d96cc8b --- /dev/null +++ b/core.d.ts @@ -0,0 +1,2252 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +type MaterialIcon = { + [C in Exclude]: { + [P in AllIdsOf]: number; + }; +} & { + /** + * 勇士的图标信息 + */ + hero: { + /** + * 各个方向的图标信息 + */ + [D in Dir2]: { + /** + * 行数 + */ + loc: number; + + /** + * 第一列 + */ + stop: number; + + /** + * 第二列 + */ + leftFoot: number; + + /** + * 第三列 + */ + midFoot: number; + + /** + * 第四列 + */ + rightFoot: number; + }; + } & { + /** + * 勇士的宽度 + */ + width: number; + + /** + * 勇士的高度 + */ + height: number; + }; +}; + +type MaterialImages = { + /** + * 各个类型的图块的图片 + */ + [C in Exclude]: HTMLImageElement; +} & { + /** + * 空气墙 + */ + airwall: HTMLImageElement; + + /** + * 自动元件 + */ + autotile: Record, HTMLImageElement>; + + /** + * 全塔属性注册的图片 + */ + images: Record; + + /** + * 额外素材 + */ + tilesets: Record; + + hero: HTMLImageElement; +}; + +interface Material { + /** + * 动画信息 + */ + readonly animates: Record; + + /** + * 素材的图片信息 + */ + readonly images: MaterialImages; + + /** + * 音乐信息 + */ + readonly bgms: Record; + + /** + * 音效信息 + */ + readonly sounds: Record; + + /** + * 怪物信息 + * @example core.material.enemys.greenSlime // 获得绿色史莱姆的属性数据 + */ + readonly enemys: { + [P in EnemyIds]: Enemy

; + }; + + /** + * 道具信息 + */ + readonly items: { + [P in AllIdsOf<'items'>]: Item

; + }; + + /** + * 图标信息 + */ + readonly icons: DeepReadonly; + + /** + * @deprecated + * core定义了,但在代码中完全没有找到这个东西?用处未知 + */ + readonly ground: CanvasRenderingContext2D; + + /** + * 楼层背景的画布context + */ + readonly groundCanvas: CanvasRenderingContext2D; + + /** + * 楼层背景的canvas样式 + */ + readonly groundPattern: CanvasPattern; + + /** + * 自动元件的父子关系 + */ + readonly autotileEdges: Record< + AllNumbersOf<'autotile'>, + AllNumbersOf<'autotile'>[] + >; +} + +interface Timeout { + /** + * 单击勇士时的计时器,用于判断双击 + */ + turnHeroTimeout?: number; + + /** + * 按住500ms后进行长按判定的计时器 + */ + onDownTimeout?: number; + + /** + * 长按跳过等待事件的计时器 + */ + sleepTimeout?: number; +} + +interface Interval { + /** + * 勇士移动的定时器 + */ + heroMoveInterval?: number; + + /** + * 长按计时器 + */ + onDownInterval?: number; +} + +interface AnimateFrame { + /** + * 游戏总时间 + */ + readonly totalTime: number; + + /** + * 本次游戏开始的时间 + */ + readonly totalTimeStart: number; + + /** + * @deprecated + * 样板没有出现过 + */ + globalAnimate: boolean; + + /** + * 当前raf的时间戳,即从游戏加载完毕到现在经过的时间 + */ + readonly globalTime: number; + + /** + * @deprecated + * 样板没有出现过 + */ + selectorTime: number; + + /** + * @deprecated + * 样板没有出现过 + */ + selectorUp: boolean; + + /** + * 上一次全局动画的时间(怪物、npc等抖动一下的时间) + */ + readonly animateTime: number; + + /** + * 勇士移动的时候上一次的换腿时间 + */ + moveTime: number; + + /** + * @deprecated + * 样板没有出现过 + */ + lastLegTime: number; + + /** + * 当前是否在左腿上,使用了四帧插件时无效 + */ + leftLeg: boolean; + + /** + * 当前天气信息 + */ + readonly weather: Weather; + + /** + * 左上角提示 + */ + readonly tip?: Readonly; + + /** + * 异步信息,想不到吧,这玩意是一个以number为索引的回调函数列表 + */ + readonly asyncId: Record void>; + + /** + * 上一个异步事件的id + */ + readonly lastAsyncId: number; +} + +interface Weather { + /** + * 当前的raf时间戳,同globalTime,但只有部分天气有用 + */ + time: number; + + /** + * 当前天气类型 + */ + type: string; + + /** + * 谁会去用这个玩意??? + */ + nodes: any[]; + + /** + * 谁会去用这个玩意??? + */ + data: any; + + /** + * 当前的天气等级 + */ + readonly level: number; + + /** + * 雾的图片信息 + */ + readonly fog: HTMLImageElement; + + /** + * 多云的图片信息 + */ + readonly cloud: HTMLImageElement; + + /** + * 晴天的图片信息 + */ + readonly sun: HTMLImageElement; +} + +interface Tip { + /** + * 显示的文字 + */ + text: string; + + /** + * 文字的左边像素位置 + */ + textX: 21 | 45; + + /** + * 提示的宽度 + */ + width: number; + + /** + * 当前的不透明度,会在显示提示时不断变化 + */ + opacity: number; + + /** + * 在显示阶段还是常亮阶段还是消失阶段 + */ + stage: number; + + /** + * 图标的帧数,即显示图标的第几帧 + */ + frame: number; + + /** + * 当前的raf时间戳 + */ + time: number; + + /** + * 在提示进入常亮阶段后经过了多长时间 + */ + displayTime: number; +} + +interface MusicStatus { + /** + * AudioContext信息,注意如果浏览器不支持的话会是null + */ + audioContext: AudioContext; + + /** + * 是否允许播放BGM + */ + bgmStatus: boolean; + + /** + * 是否允许播放SE + */ + soundStatus: boolean; + + /** + * 正在播放的BGM + */ + playingBgm: string; + + /** + * 上次播放的bgm + */ + lastBgm: string; + + /** + * 音量控制节点,只对音效有效,但为什么样板只有一个呢 + */ + gainNode: GainNode; + + /** + * 正在播放的SE,这个__name是音效名 + */ + playingSounds: Record; + + /** + * 用户音量 + */ + userVolume: number; + + /** + * 设计音量,好吧其实不能设计,只有淡入淡出的时候有用 + */ + designVolume: number; + + /** + * 音乐播放速度 + */ + bgmSpeed: number; + + /** + * 修改音乐播放速度时是否修改音调 + */ + bgmUsePitch: boolean; + + /** + * 缓存过BGM内容 + */ + cachedBgms: string[]; + + /** + * 缓存的bgm数量 + */ + cachedBgmCount: 8; +} + +interface CorePlatform { + /** + * 是否http + */ + isOnline: boolean; + + /** + * 是否是PC + */ + isPC: boolean; + + /** + * 是否是Android + */ + isAndroid: boolean; + + /** + * 是否是iOS + */ + isIOS: boolean; + + /** + * 平台信息 + */ + string: 'PC' | 'Android' | 'IOS' | ''; + + /** + * 是否是微信 + */ + isWeChat: boolean; + + /** + * 是否是QQ + */ + isQQ: boolean; + + /** + * 是否是Chrome + */ + isChrome: boolean; + + /** + * 是否是safari浏览器 + */ + isSafari: boolean; + + /** + * 是否支持复制到剪切板 + */ + supportCopy: boolean; + + /** + * 读取文件时的input元素(我也不知道干啥的 + */ + fileInput: HTMLInputElement; + + /** + * FileReader示例 + */ + fileReader: FileReader; + + /** + * 读取成功 + */ + successCallback?: (obj: any) => void; + + /** + * 读取失败 + */ + errorCallback?: () => void; +} + +/** + * dom信息,没有全部标注,只标注了一部分特例 + */ +type MainDom = { + /** + * 所有的状态信息 + */ + status: HTMLCollectionOf; + + /** + * 所有的工具栏图片 + */ + tools: HTMLCollectionOf; + + /** + * 所有的游戏画布 + */ + gameCanvas: HTMLCollectionOf; + + /** + * 所有的状态显示信息,有的是

有的是就挺离谱 + */ + statusLabels: HTMLCollectionOf; + + /** + *

标签的状态显示文字 + */ + statusText: HTMLCollectionOf; + + /** + * 自绘状态栏画布的context + */ + statusCanvasCtx: CanvasRenderingContext2D; +} & { + [key: string]: HTMLElement; +}; + +interface DomStyle { + /** + * 当前缩放大小 + */ + scale: number; + + /** + * 就是window.devicePixelRatio + */ + ratio: number; + + /** + * 高清画布列表 + */ + hdCanvas: string[]; + + /** + * 可以缩放到的缩放比例,是 [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5] 的子数组 + */ + availableScale: number[]; + + /** + * 是否是竖屏 + */ + isVertical: boolean; + + /** + * 是否显示状态栏 + */ + showStatusBar: boolean; + + /** + * 当前道具栏是否是数字键 + */ + toolbarBtn: boolean; +} + +interface CoreBigmap { + /** + * 大地图中会跟随勇士移动的画布 + */ + canvas: string[]; + + /** + * 大地图的横向偏移量,单位像素 + */ + offsetX: number; + + /** + * 大地图的纵向偏移量,单位像素 + */ + offsetY: number; + + /** + * v2优化下的横向偏移格子数 + */ + posX: number; + + /** + * v2优化下的纵向偏移格子数 + */ + posY: number; + + /** + * 地图宽度,单位格子 + */ + width: number; + + /** + * 地图高度,单位格子 + */ + height: number; + + /** + * 是否使用v2优化 + */ + v2: boolean; + + /** + * 判定为超大的图的地图面积临界,使用了显示宝石血瓶详细信息插件的话是256 + */ + threshold: 1024; + + /** + * v2优化下,显示超出的格子数,例如样板是10,那么13\*13的样板就是33\*33,还用于判断是否进行更新等 + */ + extend: 10; + + /** + * @deprecated + * 又出现了!样板中没有的东西 + */ + scale: 1; + + /** + * 绘制缩略图时的临时画布 + */ + tempCanvas: CanvasRenderingContext2D; + + /** + * 绘制地图时的双缓冲层 + */ + cacheCanvas: CanvasRenderingContext2D; +} + +interface CoreSave { + /** + * 当前存档页面显示的页码数 + */ + saveIndex: number; + + /** + * 当前存在存档的存档位 + */ + ids: Record; + + /** + * 自动存档信息 + */ + autosave: Readonly; + + /** + * 收藏的存档 + */ + favorite: number[]; + + /** + * 保存的存档名称 + */ + favoriteName: Record; +} + +interface Autosave { + /** + * 当前自动存档位 + */ + now: number; + + /** + * 当前存档信息 + */ + data?: Save[]; + + /** + * 自动存档位的最大值 + */ + max: 20; + + /** + * 是否将自动存档写入本地文件 + */ + storage: true; + + /** + * @deprecated + * 每5秒钟会被设置一次的raf时间戳,不知道干什么的。。。 + */ + time: number; + + /** + * @deprecated + * 样板在不停设置这个东西,但不知道什么用处,因为没有调用它的地方 + */ + updated: boolean; + + /** + * 不太清楚干什么的,看起来好像与存档无关,是与本地存储有关的 + */ + cache: Record; +} + +interface CoreValues { + /** + * 血网伤害 + */ + lavaDamage: number; + + /** + * 中毒伤害 + */ + poisonDamage: number; + + /** + * 衰弱状态下攻防减少的数值。如果此项不小于1,则作为实际下降的数值(比如10就是攻防各下降10 + * 如果在0到1之间则为下降的比例(比如0.3就是下降30%的攻防) + */ + weakValue: number; + + /** + * 红宝石加攻数值 + */ + redGem: number; + + /** + * 蓝宝石加防数值 + */ + blueGem: number; + + /** + * 绿宝石加魔防数值 + */ + greenGem: number; + + /** + * 红血瓶加血数值 + */ + redPotion: number; + + /** + * 蓝血瓶加血数值 + */ + bluePotion: number; + + /** + * 黄血瓶加血数值 + */ + yellowPotion: number; + + /** + * 绿血瓶加血数值 + */ + greenPotion: number; + + /** + * 默认的破甲比例 + */ + breakArmor: number; + + /** + * 默认的反击比例 + */ + counterAttack: number; + + /** + * 默认的净化比例 + */ + purify: number; + + /** + * 仇恨属性中,每杀一个怪增加的仇恨值 + */ + hatred: number; + + /** + * 全局动画速度 + */ + animateSpeed: number; + + /** + * 勇士移动速度 + */ + moveSpeed: number; + + /** + * 竖屏下状态栏显示行数 + */ + statusCanvasRowsOnMobile: 1 | 2 | 3 | 4 | 5; + + /** + * 楼层切换时间 + */ + floorChangeTime: number; +} + +type CoreStatusBarElements = { + /** + * 状态栏图标信息 + */ + readonly icons: Record; + + /** + * 状态栏的图标元素 + */ + readonly image: Record; +} & { + readonly [key: string]: HTMLElement; +}; + +type Materials = [ + 'animates', + 'enemys', + 'items', + 'npcs', + 'terrains', + 'enemy48', + 'npc48', + 'icons' +]; + +type CoreFlagProperties = + | 'autoScale' + | 'betweenAttackMax' + | 'blurFg' + | 'canGoDeadZone' + | 'disableShopOnDamage' + | 'displayCritical' + | 'displayEnemyDamage' + | 'displayExtraDamage' + | 'enableAddPoint' + | 'enableEnemyPoint' + | 'enableGentleClick' + | 'enableHDCanvas' + | 'enableMoveDirectly' + | 'enableNegativeDamage' + | 'enableRouteFolding' + | 'equipboxButton' + | 'extendToolbar' + | 'flyNearStair' + | 'flyRecordPosition' + | 'ignoreChangeFloor' + | 'itemFirstText' + | 'leftHandPrefer' + | 'startUsingCanvas' + | 'statusCanvas'; + +type CoreFlags = { + [P in CoreFlagProperties]: boolean; +} & { + /** + * 地图伤害的显示模式 + */ + extraDamageType: number; + + /** + * 当前的状态栏显示项 + */ + statusBarItems: string[]; +}; + +type CoreDataFromMain = + | 'dom' + | 'statusBar' + | 'canvas' + | 'images' + | 'tilesets' + | 'materials' + | 'animates' + | 'bgms' + | 'sounds' + | 'floorIds' + | 'floors' + | 'floorPartitions'; + +/** + * 样板的core的类型,不得不感叹样板的结构真的很神奇(简称粪),两个看似毫无关联的东西都会有着千丝万缕的联系 + */ +interface Core extends Pick { + /** + * 地图的格子宽度 + */ + readonly _WIDTH_: number; + + /** + * 地图的格子高度 + */ + readonly _HEIGHT_: number; + + /** + * 地图的像素宽度 + */ + readonly _PX_: number; + + /** + * 地图的像素高度 + */ + readonly _PY_: number; + + /** + * 地图宽度的一半 + */ + readonly _HALF_WIDTH_: number; + + /** + * 地图高度的一半 + */ + readonly _HALF_HEIGHT_: number; + + /** + * @deprecated + * 地图可视部分大小 + */ + readonly __SIZE__: number; + + /** + * @deprecated + * 地图像素 + */ + readonly __PIXELS__: number; + + /** + * @deprecated + * 地图像素的一半 + */ + readonly __HALF_SIZE__: number; + + /** + * 游戏素材 + */ + readonly material: Material; + + /** + * 计时器(样板的神秘操作 + */ + readonly timeout: Timeout; + + /** + * 定时器 + */ + readonly interval: Interval; + + /** + * 全局动画信息 + */ + readonly animateFrame: AnimateFrame; + + /** + * + * 音乐状态 + */ + readonly musicStatus: Readonly; + + /** + * 当前游戏平台 + */ + readonly platform: Readonly; + + /** + * dom样式 + */ + readonly domStyle: Readonly; + + /** + * 大地图信息 + */ + readonly bigmap: CoreBigmap; + + /** + * 存档信息 + */ + readonly saves: Readonly; + + /** + * 全局数值信息 + */ + readonly values: CoreValues; + + /** + * 游戏的初始状态 + */ + readonly initStatus: DeepReadonly; + + /** + * 所有的自定义画布 + */ + readonly dymCanvas: Record; + + /** + * 游戏状态 + */ + readonly status: GameStatus; + + /** + * 设置信息 + */ + readonly flags: CoreFlags; + + readonly firstData: FirstData; + + /** + * 获得所有楼层的信息 + * @example core.floors[core.status.floorId].events // 获得本楼层的所有自定义事件 + */ + readonly floors: DeepReadonly<{ + [P in FloorIds]: ResolvedFloor

; + }>; + + /** + * 游戏主要逻辑模块 + */ + readonly control: Control; + + /** + * 游戏的全塔属性信息 + */ + readonly data: Omit; + + /** + * 游戏加载模块 + */ + readonly loader: Loader; + + /** + * 游戏的事件模块 + */ + readonly events: Events; + + /** + * 游戏的怪物模块 + */ + readonly enemys: Enemys; + + /** + * 游戏的物品模块 + */ + readonly items: Items; + + /** + * 游戏的地图模块 + */ + readonly maps: Maps; + + /** + * 游戏的ui模块 + */ + readonly ui: Ui; + + /** + * 游戏的工具模块 + */ + readonly utils: Utils; + + /** + * 游戏的图标模块 + */ + readonly icons: Icons; + + /** + * 游戏的交互模块 + */ + readonly actions: Actions; + + /** + * 进行游戏初始化 + * @param coreData 初始化信息 + * @param callback 初始化完成后的回调函数 + */ + init(coreData: MainData, callback?: () => void): Promise; + + /** + * @deprecated + * 在一个上下文下执行函数(真的有人会用这个东西吗? + * @param func 要执行的函数 + * @param _this 执行函数的上下文 + * @param params 函数的参数 + */ + doFunc any>( + func: F, + _this: any, + ...params: Parameters + ): ReturnType; +} + +type CoreMixin = Core & + Forward & + Forward & + Forward & + Forward & + Forward & + Forward & + Forward & + Forward & + Forward & + Forward; + +interface MainStyle extends Readonly { + /** + * 初始界面的背景图 + */ + readonly startBackground: string; + + /** + * 竖屏下初始界面的背景图 + */ + readonly startVerticalBackground: string; + + /** + * 初始界面的文字样式 + */ + readonly startLogoStyle: string; + + /** + * 初始界面的选项样式 + */ + readonly startButtonsStyle: string; +} + +interface SplitImageData { + /** + * 要切分的图片id + */ + readonly name: ImageIds; + + /** + * 每个小图的宽度 + */ + readonly width: number; + + /** + * 每个小图的高度 + */ + readonly height: number; + + /** + * 切分成的小图的前缀名 + */ + readonly prefix: string; +} + +interface Main extends MainData { + /** + * 是否在录像验证中 + */ + readonly replayChecking: boolean; + + /** + * 渲染进程是否加载完毕 + */ + renderLoaded: boolean; + + /** + * //@deprecated + * 就是core,应该没人会用main.core吧( + */ + readonly core: CoreMixin; + + /** + * 游戏的dom信息 + */ + readonly dom: Readonly; + + /** + * 游戏版本,发布后会被随机为数字,请勿使用该属性 + */ + readonly version: string; + + /** + * 是否使用压缩文件 + */ + readonly useCompress: boolean; + + /** + * 存档页数 + */ + readonly savePages: number; + + /** + * 循环临界的分界 + */ + readonly criticalUseLoop: number; + + /** + * 当前游戏模式,是编辑器还是游玩界面 + */ + readonly mode: 'play' | 'editor'; + + /** + * 是否使用远程bgm + */ + readonly bgmRemote: boolean; + + /** + * 远程bgm目录 + */ + readonly bgmRemoteRoot: string; + + /** + * 所有的系统画布 + */ + readonly canvas: Record; + + /** + * 获得所有楼层的信息,等同于core.floors,但两者不是引用关系 + */ + readonly floors: DeepReadonly<{ + [P in FloorIds]: ResolvedFloor

; + }>; + + /** + * 所有的素材图片名称 + */ + readonly materials: [ + 'animates', + 'enemys', + 'items', + 'npcs', + 'terrains', + 'enemy48', + 'npc48', + 'icons' + ]; + + /** + * 要加载的project目录下的文件 + */ + readonly pureData: string[]; + + /** + * 要加载的libs目录下的文件 + */ + readonly loadList: string[]; + + /** + * 开始界面中当前选中的按钮 + */ + readonly selectedButton: number; + + /** + * 当前启动服务是否支持高层塔优化 + */ + readonly supportBunch: boolean; + + /** + * 状态栏的图标信息 + */ + readonly statusBar: CoreStatusBarElements; + + /** + * 游戏版本 + */ + readonly __VERSION__: string; + + /** + * 游戏版本代码 + */ + readonly __VERSION_CODE__: number; + + readonly RESOURCE_INDEX: Record; + readonly RESOURCE_URL: string; + readonly RESOURCE_SYMBOL: string; + readonly RESOURCE_TYPE: 'dev' | 'dist' | 'gh' | 'local'; + + /** + * 初始化游戏 + * @param mode 初始化游戏的模式,游玩还是编辑器 + * @param callback 初始化完成后的回调函数 + */ + init(mode: 'play' | 'editor', callback?: () => void): void; + + /** + * 加载一个脚本 + * @param src 脚本路径 + */ + loadScript(src: string, module?: boolean): Promise; + + /** + * 动态加载js文件 + * @param dir 加载的js文件的目录 + * @param loadList 加载的js文件的文件名数组,不带后缀 + * @param callback 加载完毕后的回调函数 + */ + loadJs(dir: string, loadList: string[], callback: () => void): void; + + /** + * 动态加载一个js文件 + * @param dir 加载的js文件的目录 + * @param modName 加载的js文件的名称,不带后缀名,如果是使用压缩文件会自动加上.min + * @param callback 加载完毕后的回调函数,传入的参数是modName + */ + loadMod( + dir: string, + modName: string, + callback: (name: string) => void + ): void; + + /** + * 动态加载所有楼层 + * @param callback 加载完成后的回调函数 + */ + loadFloors(callback: () => void): void; + + /** + * 动态加载一个楼层 + * @param floorId 加载的楼层id + * @param callback 加载完成后的回调函数,传入的参数是加载的楼层id + */ + loadFloor( + floorId: F, + callback: (floorId: F) => void + ): void; + + /** + * 设置加载界面的加载提示文字 + */ + setMainTipsText(text: string): void; + + /** + * @deprecated + * 输出内容(极不好用,建议换成console,我甚至不知道样板为什么会有这个东西) + * @param e 输出内容 + * @param error 输出内容是否是报错 + */ + log(e: string | Error, error?: boolean): void; + + /** + * 生成选择光标的keyframes + */ + createOnChoiceAnimation(): void; + + /** + * 选中开始界面的一个按钮 + * @param index 要选中的按钮 + */ + selectButton(index: number): void; + + /** + * 加载一系列字体 + * @param fonts 要加载的字体列表 + */ + importFonts(fonts: FontIds[]): void; + + /** + * 执行样板的所有监听 + */ + listen(): void; + + /** + * 执行ts的插件转发 + */ + forward(): void; +} + +interface Flags { + /** + * 当前的难度代码 + */ + readonly hard: number; + + /** + * 本次游戏的种子 + */ + readonly __seed__: number; + + /** + * 当前的随机数 + */ + readonly __rand__: number; + + /** + * 难度的颜色,css字符串 + */ + readonly __hardColor__: Color; + + /** + * 平面楼传下,每个楼层的离开位置 + */ + readonly __leaveLoc__: Record; + + /** + * 剧情文本属性 + */ + readonly textAttribute: TextAttribute; + + /** + * 楼层是否到达过 + */ + readonly __visited__: Record; + + /** + * 鼠标位置 + */ + mouseLoc: LocArr; + + /** + * 鼠标的像素位置 + */ + clientLoc: LocArr; + + /** + * 世界1 2X2配方 + * ID:[["","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide11: any; + + /** + * 世界2 2X2配方 + * ID:[["","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide12: any; + + /** + * 世界3 2X2配方 + * ID:[["","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide13: any; + + /** + * 世界4 2X2配方 + * ID:[["","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide14: any; + + /** + * 世界5 2X2配方 + * ID:[["","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide15: any; + + /** + * 世界1 3X3配方 + * ID:[["","","","","","","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide21: any; + + /** + * 世界2 3X3配方 + * ID:[["","","","","","","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide22: any; + + /** + * 世界3 3X3配方 + * ID:[["","","","","","","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide23: any; + + /** + * 世界4 3X3配方 + * ID:[["","","","","","","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide24: any; + + /** + * 世界5 3X3配方 + * ID:[["","","","","","","","","",1],] + * 成品ID:[[配方1,成品数量],] + */ + craftGuide25: any; + + /** + * 武器属性表 + * ID:[20,1,-10,1] + * 道具ID:[耐久,攻击加成,攻速加成,攻击范围加成,音效] + */ + weapon: any; + + /** + * 工具耐久表 + * ID:[20,1,1] + * 道具ID:[耐久,挖掘等级,挖掘类型] + */ + tool: any; + + /** + * 装备耐久表 + * ID:[60,1] + * 道具ID:[耐久,防御加成] + */ + equip: any; + + /** + * 挖方块表 + * id:[["",1,100],] + * 图块数字:[[掉落1,数量,权重],] + */ + wfk: any; + + /** + * 放方块表 + * ID:[[id],[1,1,1],[0]] + * 道具ID:[[图块数字],[允许放置背景层,允许放置事件层,允许放置前景层],[特殊]] + * 特殊:0,无特殊条件[0] 1,要求背景图块[1,””,””,] 2,要求事件图块[2,””,””,] 3,要求前景图块[3,””,””,] 4,返还道具(若相同 道具不消失)[4,””] 5,耕地[5] 6,泥土[6] 7,水[7] 8,骨粉[8] + */ + ffk: any; + + /** + * 燃料表 + * ID:[5] + * 道具ID:[燃料值] + */ + fuelGuide: any; + + /** + * 熔炼表 + * ID:[""] + * 道具ID:[成品ID] + */ + furGuide: any; + + /** + * 农田表 + * ID:[4500,[id1,id2],[333]] + * 道具ID:[成熟值,[幼苗图块数字,成熟图块数字],[背景层要求]] + */ + cropslist: any; + + /** + * 敌怪表 + * ID:{1:[[10,4,2,85,80,1,4,0,11,15,0],["",1,50],["",1,50]],} + * 敌怪ID:{等级:[[血,攻,防,攻速,移速,攻击范围,探测范围,是否真伤,金,经,中立],[掉落1,数量,权重],[掉落2,数量,权重]],} + */ + enemyGuide: any; + + /** + * 科技提升表 + * 知识:[["",],["",],["",],["博闻强识"]] + * 科技名:[[外部描述],[内部描述1],[内部描述2],[备注]] + */ + tech2: any; + + /** + * 科技消耗表 + * 知识:[[1,],[1,],[1,]] + * 科技名:[[需求科技点],[需求铜币],[需求世界等级]] + */ + tech3: any; + + /** + * 挖掘等级表 + * 1:{1:[[6],[8]],} + * 挖掘类型:{挖掘等级:[[硬度1挖掘次数],[硬度2挖掘次数],]} + */ + digLevel: any; + + /** + * 稀有度表 + * 道具ID:品质 + */ + quality: any; + + /** + * 结构表 + * 水井:[[897,343,897],], + * 结构名称:[[第一行图块id,],[第二行图块id,],], + */ + structure: any; + + /** + * 结构背景表 + * 水井:[[897,343,898],], + * 结构名称:[[第一行背景id,],[第二行背景id,],], + */ + structurebg: any; + + /** + * 战利品表 + * 蘑菇宝箱:[[12,16],["I682",[1,3],200],], + * 宝箱ID: [[总数下限,总数上限],[道具ID,[数量下限,数量上限],道具权重],], + */ + chestloot: any; + + /** + * 附魔表 + * 木剑: [ + * ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ], + * ["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0]], + */ + enchantlist: any; + + /** + * 附魔等级表 + * 锋利: 5, + */ + enchantlv: any; + + /** + * 附魔互斥表 + * 锋利: ["海洋杀手","天空杀手"], + */ + enchantmutex: any; + + /** + * 任务表 + * 页签:[[描述,当前进度,总进度,[奖励类型,奖励名称,奖励ID,数量]],], + */ + task: any; + + /** + * 成就表 + * 成就名称:[描述,当前进度,总进度,成就类型], + */ + achievement: any; + + /** + * 自动农田 + * 种子索引:升级所需、升级信息1、升级信息2、升级信息3、产出1、产出2、容量 + */ + farmland1: any; + + /** + * 界面切换 + * 锁定,物品栏,背包,箱子,工作台,熔炉 + */ + interface: any; + + /** + * 右侧状态栏绘制的内容 + * [类型,ID] + * 类型:空,图块,敌怪 + * ID:图块数字或id + */ + rightstatus: any; + + /** + * 小地图标签显示 + * 0无标签,1房屋,2矿石,3树,4箱子,5,6,7,8,9,10, + */ + mapicon: any; + + /** + * 播放脚步声 + */ + step: any; + + /** + * 判断是否属性界面 + */ + attrui: any; + + /** + * 选择世界界面页码 + */ + worldpage: any; + + /** + * 饮食冷却 + */ + eatcool: any; + + /** + * 预览时限制视野的地图列表 + */ + mapview: any; + + /** + * 任务表 + * 页签:[[描述,当前进度,总进度,[奖励类型,奖励名称,奖励ID,数量]],], + */ + task1: any; + + /** + * 洞穴表 + * 洞穴名称:[[图块数字,权重],], + */ + cavelayer: any; + + /** + * 附魔表1 + * 木剑: ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ], + */ + enchantlist1: any; + + /** + * 附魔表2 + * 木剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0]], + */ + enchantlist2: any; + + /** + * 附魔表2 + * 木剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0]], + */ + enchantlist3: any; + + /** + * 维度 + * [当前维度,当前层] + */ + dimension: any; + + /** + * 在岩浆里的掉血状态占用 + */ + lavaocc: any; + + /** + * 在水里的掉血状态占用 + */ + breocc: any; + + /** + * 呼吸值 + * [当前,最大] + */ + breathe: any; + + /** + * 判断是否委托界面 + */ + taskui: any; + + /** + * 标题界面光标 + */ + worldcur: any; + + /** + * 标题界面光标(持续显示) + */ + worldcur2: any; + + /** + * 判断是否成就界面 + */ + achiui: any; + + /** + * 左侧状态栏提示 + * [""] + */ + tip: any; + + /** + * 播放挖掘声 + */ + digvolume: any; + + /** + * 世界种子,最初的随机数种子 + */ + seed1: any; + + /** + * 世界种子,基于seed1不断变化的随机数种子 + */ + seed2: any; + + /** + * rand3生成的随机数的范围限制,为小于limit的正整数 + */ + limit: any; + + /** + * 根据seed1生成随机数 + */ + rand3: any; + + /** + * 世界生成率 + */ + createrate: any; + + /** + * 植物额外生长值加成 + */ + extraGrow: any; + + /** + * 箱子额外存储空间加成 + */ + extraGrid: any; + + /** + * 额外耐久度加成 + * [装备,武器] + */ + extraDur: any; + + /** + * 背包列表 + * [["",0],] + * [[道具ID,数量],] + */ + bag: any; + + /** + * 备份背包列表 + * [["",0],] + * [[道具ID,数量],] + */ + bagbp: any; + + /** + * 物品栏界面光标:1 - 9 + */ + cursor1: any; + + /** + * MC界面光标 + * 背包界面:0 - 40 + * 箱子界面:0 - 90 + * 工作台界面:0 - 45 + * 熔炉界面:0 - 38 + */ + cursor2: any; + + /** + * MC界面光标缓存,用于交换道具,存储光标位置 + * [0,0] + */ + curCache2: any; + + /** + * 切换物品栏的文本透明累计 + */ + textalpha: any; + + /** + * 挖掘冷却,可选 + */ + digcold: any; + + /** + * 背包内交换物品缓存,存储物品 + * 0 + */ + bagCache: any; + + /** + * MC界面分物品光标 + * 背包界面:0 - 40 + * 箱子界面:0 - 90 + * 工作台界面:0 - 45 + * 熔炉界面:0 - 38 + */ + cursplit: any; + + /** + * MC界面双击物品光标 + * 背包界面:0 - 40 + * 箱子界面:0 - 90 + * 工作台界面:0 - 45 + * 熔炉界面:0 - 38 + */ + curdou: any; + + /** + * MC界面高亮光标 [[x, y],[x, y]] + * 背包界面:0 - 40 + * 箱子界面:0 - 90 + * 工作台界面:0 - 45 + * 熔炉界面:0 - 38 + */ + curlight: any; + + /** + * 光标数组,用于长按分物品时记录已分格子 + */ + curarr: any; + + /** + * 敏感检测,用于右键拖动分物品时不会多分一个 + */ + mingan: any; + + /** + * 左键长按 + */ + longleft: any; + + /** + * 右键长按 + */ + longright: any; + + /** + * 分物品时,与主手物品ID相同的项会以键值对存入该对象 + * 37:["",1] + */ + splitCache2: any; + + /** + * 主手物品缓存,用于分物品时记录可分物品总数 + */ + splitCache1: any; + + /** + * 武器当前耐久 + */ + weaDur: any; + + /** + * 工具当前耐久 + */ + toolDur: any; + + /** + * 装备当前耐久 + */ + equDur: any; + + /** + * 箱子列表 + * 001001MT0:[["",1],] + * 位置索引:[[道具ID,数量],] + */ + boxlist: any; + + /** + * 熔炉列表 + * 001001MT0:[["",1],["",1],["",1]] + * 位置索引:[[燃料ID,数量],[原料ID,数量],[成品ID,数量],[烧炼时间,燃料值]] + */ + furnacelist: any; + + /** + * 2X2合成列表 + * [["", 0],] + * [[道具ID, 数量],] + * flags.comlist1[4]为成品 + */ + comlist1: any; + + /** + * 3X3合成列表 + * [["", 0],] + * [[道具ID, 数量],] + * flags.comlist1[9]为成品 + */ + comlist2: any; + + /** + * 游戏内时间(0-1440) + */ + time: any; + + /** + * 游戏内时间段 + */ + timePeriod: any; + + /** + * 地图上敌怪列表 + * id: 敌怪ID, + * hp: 当前生命值, + * hpmax: 最大生命值, + * state: ["stand", "stand", "move", "chase", "attack", "observe",], 第一个为当前状态 + * move: [3, 0], 移动数,消失数 + */ + enemylist: any; + + /** + * 角色出生重生中心点 + * [出生点楼层,出生点x,出生点y,重生点楼层,重生点x,重生点y] + */ + revive: any; + + /** + * 角色重生随机点 + * [重生后楼层,重生后x,重生后y] + */ + revivePo: any; + + /** + * 用于重生后调整视野 + */ + die: any; + + /** + * 角色特殊属性 + * atkSpeed: [84, 1, 0], 攻速:[攻速值,攻速恢复,是否占用] + * touchRange: [1, 2], 触碰范围:[攻击范围,触碰范围] + * atkSea: 0, 海洋攻击 + * atkFly: 0, 飞行攻击 + * igdef1: 0, 无视防御 + * igdef2: 0, 横扫之刃 + * crit: 1, 暴击 + * reduceDa: 0, 减伤 + * reduceBoom: 0, 爆炸减伤 + * reduceBul: 0, 弹射物减伤 + * backDa: 0, 反伤 + * undead: 0, 无敌 + */ + property: any; + + /** + * 刷怪表 + * 世界等级:[草地:[时段:[[id1,权重1],[id2,权重2]],],], + */ + refenemy: any; + + /** + * 角色攻击范围显示 + */ + playerAr: any; + + /** + * 未睡眠时间累计 + */ + sleep: any; + + /** + * 爆炸中心点 + * [["zhu1",32,43],] + */ + bombcenter: any; + + /** + * 记录结构附近位置,用于检测是否刷结构怪 + * "zhu1": [[x, y, 半径, "海底神殿"]] + */ + nearstr: any; + + /** + * 科技界面开关 + */ + tech: any; + + /** + * 科技当前等级 + * 科技名称:[科技当前等级,科技最高等级] + */ + tech1: any; + + /** + * 持有科技点数量 + */ + tpointnum: any; + + /** + * 科技点总消耗数 + */ + tpointsum: any; + + /** + * 持有铜币数量 + */ + coppernum: any; + + /** + * 角色等级 + * [当前等级,当前经验,升级所需经验] + */ + level: any; + + /** + * 世界等级 + */ + worldlevel: any; + + /** + * 自定义按键界面 + */ + CustomKey: any; + + /** + * 自定义按键界面页码 + */ + pagenumber: any; + + /** + * 自定义按键界面选框 + */ + curKey: any; + + /** + * 自定义按键初始键码值 + * [["向上移动", 87],] + */ + keylist: any; + + /** + * 是否按住shift,用于快捷转移道具 + */ + transfer: any; + + /** + * 挖掘次数 + * [还需次数,递增变量,x坐标,y坐标,取消绘制累减] + */ + dig: any; + + /** + * 用于获取鼠标点击的格子坐标 + */ + box_xy: any; + + /** + * 农作物生长列表 + * 位置索引:[种子id,当前生长值,成熟值] + */ + crops: any; + + /** + * 地图上自动农田 + * 位置索引:[种子ID,等级,数量1,数量2] + */ + farmland2: any; + + /** + * 自动农田界面 + */ + farmUI: any; + + /** + * 饱食度 + * [当前饱食度,饱食度上限,当前饥饿值,饥饿值上限] + */ + hunger: any; + + /** + * 世界列表 + * 世界名称: "新的世界", + * 世界存档: [], + * 世界选项: ["默认", "开", "关", "中"], + * 游戏模式: "生存", + * 难度: "普通", + * 游戏规则: ["开", "开", "开", "开", "开", "开", "开", "开", "开"], + * 地形: {平原: [1,],}, + * 结构: {水井: [1,],}, + * 单一群系: ["随机",] + */ + worldlist: any; + + /** + * 创建世界界面描述框文本 + */ + worldDescribe: any; + + /** + * 当前所处世界编号 + */ + worldindex: any; + + /** + * 熔炉熔炼检测 + */ + smeltcheck: any; + + /** + * buff列表 + * 时间:ms + * 生命恢复,中毒,凋零 + */ + buff: any; + + /** + * 濒死检测,用于无规则心跳 + */ + dying: any; + + [key: string]: any; + +} + +interface MapDataOf { + /** + * 图块的id + */ + id: NumberToId[T]; + + /** + * 图块的类型 + */ + cls: ClsOf; +} + +/** + * 样板的主对象 + */ +declare const main: Main; + +/** + * 样板的核心对象 + */ +declare const core: CoreMixin; + +/** + * 所有的变量 + */ +declare let flags: Flags; + +/** + * 勇士信息 + */ +declare let hero: HeroStatus; + +/** + * 全塔属性 + */ +declare const data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d: DataCore; + +/** + * 所有的怪物信息 + */ +declare const enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80: { + [P in EnemyIds]: Enemy

; +}; + +/** + * 所有的公共事件 + */ +declare const events_c12a15a8_c380_4b28_8144_256cba95f760: { + commonEvent: Record; +}; + +/** + * 脚本编辑 + */ +declare const functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a: FunctionsData; + +/** + * 所有的图标信息 + */ +declare const icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1: MaterialIcon; + +/** + * 所有的道具信息 + */ +declare const items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a: { + [P in AllIdsOf<'items'>]: Item

; +}; + +/** + * 所有的图块信息 + */ +declare const maps_90f36752_8815_4be8_b32b_d7fad1d0542e: { + [P in keyof NumberToId]: MapDataOf

; +}; + +/** + * 插件信息 + */ +declare const plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1: PluginDeclaration; + +interface Window { + core: CoreMixin; + flags: Flags; + hero: HeroStatus; +} diff --git a/core.js b/core.js new file mode 100644 index 0000000..fff0be6 --- /dev/null +++ b/core.js @@ -0,0 +1,548 @@ + +/** + * 初始化 start + */ + +"use strict"; + +// /** +// * @type {CoreMixin} +// */ +// const core = (() => { + +function core () { + this._WIDTH_ = 13; + this._HEIGHT_ = 13; + this._PX_ = this._WIDTH_ * 32; + this._PY_ = this._HEIGHT_ * 32; + this._HALF_WIDTH_ = Math.floor(this._WIDTH_ / 2); + this._HALF_HEIGHT_ = Math.floor(this._HEIGHT_ / 2); + + this.__SIZE__ = main.mode == 'editor' ? 13 : this._HEIGHT_; + this.__PIXELS__ = this.__SIZE__ * 32; + this.__HALF_SIZE__ = Math.floor(this.__SIZE__ / 2); + this.material = { + 'animates': {}, + 'images': {}, + 'bgms': {}, + 'sounds': {}, + 'items': {}, + 'enemys': {}, + 'icons': {}, + 'ground': null, + 'grundCanvas': null, + 'groundPattern': null, + 'autotileEdges': {}, + } + this.timeout = { + 'turnHeroTimeout': null, + 'onDownTimeout': null, + 'sleepTimeout': null, + } + this.interval = { + 'heroMoveInterval': null, + 'onDownInterval': null, + } + this.animateFrame = { + 'totalTime': 0, + 'totalTimeStart': 0, + 'globalAnimate': false, + 'globalTime': 0, + 'selectorTime': 0, + 'selectorUp': true, + 'animateTime': 0, + 'moveTime': 0, + 'lastLegTime': 0, + 'leftLeg': true, + 'weather': { + 'time': 0, + 'type': null, + 'level': 1, + 'nodes': [], + 'data': null, + 'fog': null, + 'cloud': null, + 'sun': null + }, + "tip": null, + "asyncId": {}, + "lastAsyncId": null + } + this.musicStatus = { + 'audioContext': null, // WebAudioContext + 'bgmStatus': false, // 是否播放BGM + 'soundStatus': true, // 是否播放SE + 'playingBgm': null, // 正在播放的BGM + 'pauseTime': 0, // 上次暂停的时间 + 'lastBgm': null, // 上次播放的bgm + 'gainNode': null, + 'playingSounds': {}, // 正在播放的SE + 'userVolume': 1.0, // 用户音量 + 'designVolume': 1.0, //设计音量 + 'bgmSpeed': 100, // 背景音乐速度 + 'bgmUsePitch': null, // 是否同时修改音调 + 'cachedBgms': [], // 缓存BGM内容 + 'cachedBgmCount': 8, // 缓存的bgm数量 + } + this.platform = { + 'isOnline': true, // 是否http + 'isPC': true, // 是否是PC + 'isAndroid': false, // 是否是Android + 'isIOS': false, // 是否是iOS + 'string': 'PC', + 'isWeChat': false, // 是否是微信 + 'isQQ': false, // 是否是QQ + 'isChrome': false, // 是否是Chrome + 'supportCopy': false, // 是否支持复制到剪切板 + + 'fileInput': null, // FileInput + 'fileReader': null, // 是否支持FileReader + 'successCallback': null, // 读取成功 + 'errorCallback': null, // 读取失败 + } + // 样式 + this.domStyle = { + scale: 1.0, + ratio: 1.0, + hdCanvas: ["damage", "ui", "data"], + availableScale: [], + isVertical: false, + showStatusBar: true, + toolbarBtn: false, + } + this.bigmap = { + canvas: ["bg", "event", "event2", "fg", "damage"], + offsetX: 0, // in pixel + offsetY: 0, + posX: 0, // + posY: 0, + width: main.mode == 'editor' ? this.__SIZE__ : this._WIDTH_, // map width and height + height: main.mode == 'editor' ? this.__SIZE__ : this._HEIGHT_, + v2: false, + threshold: 1024, + extend: 10, + scale: 1.0, + tempCanvas: null, // A temp canvas for drawing + cacheCanvas: null, // A cache canvas + } + this.saves = { + "saveIndex": null, + "ids": {}, + "autosave": { + "data": null, + "time": 0, + "updated": false, + "storage": true, // 是否把自动存档写入文件a + "max": 20, // 自动存档最大回退数 + "now": 0, + }, + "favorite": [], + "favoriteName": {}, + "cache": {} + } + this.initStatus = { + 'played': false, + 'gameOver': false, + + // 勇士属性 + 'hero': {}, + 'heroCenter': { 'px': null, 'py': null }, + + // 当前地图 + 'floorId': null, + 'thisMap': null, + 'maps': null, + 'bgmaps': {}, + 'fgmaps': {}, + 'mapBlockObjs': {}, + 'checkBlock': {}, // 每个点的阻激夹域信息 + 'damage': { // 每个点的显伤绘制 + 'posX': 0, + 'posY': 0, + 'data': [], + 'extraData': [], + }, + + 'lockControl': false, + + // 勇士移动状态 + 'heroMoving': 0, + 'heroStop': true, + + // 自动寻路相关 + 'automaticRoute': { + 'autoHeroMove': false, + 'autoStep': 0, + 'movedStep': 0, + 'destStep': 0, + 'destX': null, + 'destY': null, + 'offsetX': null, + 'offsetY': null, + 'autoStepRoutes': [], + 'moveStepBeforeStop': [], + 'lastDirection': null, + 'cursorX': 0, + 'cursorY': 0, + "moveDirectly": false, + }, + + // 按下键的时间:为了判定双击 + 'downTime': null, + 'ctrlDown': false, + 'preview': { + 'enabled': false, + 'prepareDragging': false, + 'dragging': false, + 'px': 0, + 'py': 0, + }, + + // 路线&回放 + 'route': [], + 'replay': { + 'replaying': false, + 'pausing': false, + 'animate': false, // 正在某段动画中 + 'failed': false, + 'toReplay': [], + 'totalList': [], + 'speed': 1.0, + 'steps': 0, + 'save': [], + }, + // 录像折叠 + 'routeFolding': {}, + + // event事件 + 'shops': {}, + 'event': { + 'id': null, + 'data': null, + 'selection': null, + 'ui': null, + 'interval': null, + }, + 'autoEvents': [], + 'textAttribute': { + 'position': "center", + "offset": 0, + "title": [255, 215, 0, 1], + "background": [0, 0, 0, 0.85], + "text": [255, 255, 255, 1], + "titlefont": 22, + "textfont": 16, + "bold": false, + "time": 0, + "letterSpacing": 0, + "animateTime": 0, + }, + "globalAttribute": { + 'equipName': main.equipName || [], + "statusLeftBackground": main.styles.statusLeftBackground || "url(project/materials/ground.png) repeat", + "statusTopBackground": main.styles.statusTopBackground || "url(project/materials/ground.png) repeat", + "toolsBackground": main.styles.toolsBackground || "url(project/materials/ground.png) repeat", + "borderColor": main.styles.borderColor || [204, 204, 204, 1], + "statusBarColor": main.styles.statusBarColor || [255, 255, 255, 1], + "floorChangingStyle": main.styles.floorChangingStyle || "background-color: black; color: white", + "selectColor": main.styles.selectColor || [255, 215, 0, 1], + "font": main.styles.font || "Verdana" + }, + 'curtainColor': null, + + // 动画 + 'globalAnimateObjs': [], + 'floorAnimateObjs': [], + 'boxAnimateObjs': [], + 'autotileAnimateObjs': [], + "globalAnimateStatus": 0, + 'animateObjs': [], + }; + // 标记的楼层列表,用于数据统计 + this.markedFloorIds = {}; + this.status = {}; + this.dymCanvas = {}; + + if (main.mode == 'editor') { + document.documentElement.style.setProperty('--size', this.__SIZE__); + document.documentElement.style.setProperty('--pixel', this.__PIXELS__ + 'px'); + } +} + +/////////// 系统事件相关 /////////// + +////// 初始化 ////// +core.prototype.init = function (coreData, callback) { + this._forwardFuncs(); + for (var key in coreData) + core[key] = coreData[key]; + this._init_flags(); + this._init_platform(); + this._init_others(); + this._init_plugins(); + var b = main.mode == 'editor'; + // 初始化画布 + for (var name in core.canvas) { + if (core.domStyle.hdCanvas.indexOf(name) >= 0) + core.maps._setHDCanvasSize(core.canvas[name], b ? core.__PIXELS__ : core._PX_, b ? core.__PIXELS__ : core._PY_); + else { + core.canvas[name].canvas.width = (b ? core.__PIXELS__ : core._PX_); + core.canvas[name].canvas.height = (b ? core.__PIXELS__ : core._PY_); + } + } + + core.loader._load(function () { + core.extensions._load(function () { + core._afterLoadResources(callback); + }); + }); + core.dom.musicBtn.style.display = 'block'; + core.setMusicBtn(); +} + +core.prototype._init_flags = function () { + core.flags = core.clone(core.data.flags); + core.values = core.clone(core.data.values); + core.firstData = core.clone(core.data.firstData); + this._init_sys_flags(); + + // 让你总是拼错! + window.on = true; + window.off = false; + window.ture = true; + window.flase = false; + + core.dom.versionLabel.innerText = core.firstData.version; + core.dom.logoLabel.innerText = core.firstData.title; + document.title = core.firstData.title + " - HTML5魔塔"; + document.getElementById("startLogo").innerText = core.firstData.title; + (core.firstData.shops || []).forEach(function (t) { core.initStatus.shops[t.id] = t; }); + + core.maps._initFloors(); + // 初始化怪物、道具等 + core.material.enemys = core.enemys.getEnemys(); + core.material.items = core.items.getItems(); + core.material.icons = core.icons.getIcons(); + + // 初始化自动事件 + for (var floorId in core.floors) { + var autoEvents = core.floors[floorId].autoEvent || {}; + for (var loc in autoEvents) { + var locs = loc.split(","), x = parseInt(locs[0]), y = parseInt(locs[1]); + for (var index in autoEvents[loc]) { + var autoEvent = core.clone(autoEvents[loc][index]); + if (autoEvent && autoEvent.condition && autoEvent.data) { + autoEvent.floorId = floorId; + autoEvent.x = x; + autoEvent.y = y; + autoEvent.index = index; + autoEvent.symbol = floorId + "@" + x + "@" + y + "@" + index; + autoEvent.condition = core.replaceValue(autoEvent.condition); + autoEvent.data = core.precompile(autoEvent.data); + core.initStatus.autoEvents.push(autoEvent); + } + } + } + } + // 道具的穿上/脱下,视为自动事件 + for (var equipId in core.material.items) { + var equip = core.material.items[equipId]; + if (equip.cls != 'equips' || !equip.equip) continue; + if (!equip.equip.equipEvent && !equip.equip.unequipEvent) continue; + var equipFlag = '_equipEvent_' + equipId; + var autoEvent1 = { + symbol: "_equipEvent_" + equipId, + currentFloor: false, + multiExecute: true, + condition: "core.hasEquip('" + equipId + "') && !core.hasFlag('" + equipFlag + "')", + data: core.precompile([{ "type": "setValue", "name": "flag:" + equipFlag, "value": "true" }].concat(equip.equip.equipEvent || [])), + }; + var autoEvent2 = { + symbol: "_unequipEvent_" + equipId, + currentFloor: false, + multiExecute: true, + condition: "!core.hasEquip('" + equipId + "') && core.hasFlag('" + equipFlag + "')", + data: core.precompile([{ "type": "setValue", "name": "flag:" + equipFlag, "value": "null" }].concat(equip.equip.unequipEvent || [])), + }; + core.initStatus.autoEvents.push(autoEvent1); + core.initStatus.autoEvents.push(autoEvent2); + } + + core.initStatus.autoEvents.sort(function (e1, e2) { + if (e1.floorId == null) return 1; + if (e2.floorId == null) return -1; + if (e1.priority != e2.priority) return e2.priority - e1.priority; + if (e1.floorId != e2.floorId) return core.floorIds.indexOf(e1.floorId) - core.floorIds.indexOf(e2.floorId); + if (e1.x != e2.x) return e1.x - e2.x; + if (e1.y != e2.y) return e1.y - e2.y; + return e1.index - e2.index; + }) + +} + +core.prototype._init_sys_flags = function () { + if (core.flags.equipboxButton) core.flags.equipment = true; + core.flags.displayEnemyDamage = core.getLocalStorage('enemyDamage', true); + core.flags.displayCritical = core.getLocalStorage('critical', true); + core.flags.displayExtraDamage = core.getLocalStorage('extraDamage', true); + core.flags.enableEnemyPoint = core.getLocalStorage('enableEnemyPoint', core.flags.enableEnemyPoint); + core.flags.leftHandPrefer = core.getLocalStorage('leftHandPrefer', false); + core.flags.extraDamageType = core.getLocalStorage('extraDamageType', 2); + // 行走速度 + core.values.moveSpeed = core.getLocalStorage('moveSpeed', core.values.moveSpeed || 100); + core.values.floorChangeTime = core.getLocalStorage('floorChangeTime', core.values.floorChangeTime); + if (core.values.floorChangeTime == null) core.values.floorChangeTime = 500; + core.flags.enableHDCanvas = core.getLocalStorage('enableHDCanvas', !core.platform.isIOS); +} + +core.prototype._init_platform = function () { + core.platform.isOnline = location.protocol.indexOf("http") == 0; + if (!core.platform.isOnline) alert("请勿直接打开html文件!使用启动服务或者APP进行离线游戏。"); + window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; + core.musicStatus.bgmStatus = core.getLocalStorage('bgmStatus', true); + core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true); + //新增 userVolume 默认值0.7 + core.musicStatus.userVolume = core.getLocalStorage('userVolume', 0.7); + core.musicStatus.userVolume2 = core.getLocalStorage('userVolume2', 0.7); + try { + core.musicStatus.audioContext = new window.AudioContext(); + core.musicStatus.gainNode = core.musicStatus.audioContext.createGain(); + core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume2; + core.musicStatus.gainNode.connect(core.musicStatus.audioContext.destination); + } catch (e) { + console.log("该浏览器不支持AudioContext"); + core.musicStatus.audioContext = null; + } + ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"].forEach(function (t) { + if (navigator.userAgent.indexOf(t) >= 0) { + if (t == 'iPhone' || t == 'iPad' || t == 'iPod') core.platform.isIOS = true; + if (t == 'Android') core.platform.isAndroid = true; + core.platform.isPC = false; + } + }); + core.platform.string = core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""; + core.platform.supportCopy = document.queryCommandSupported && document.queryCommandSupported("copy"); + var chrome = /Chrome\/(\d+)\./i.exec(navigator.userAgent); + if (chrome && parseInt(chrome[1]) >= 50) core.platform.isChrome = true; + core.platform.isSafari = /Safari/i.test(navigator.userAgent) && !/Chrome/i.test(navigator.userAgent); + core.platform.isQQ = /QQ/i.test(navigator.userAgent); + core.platform.isWeChat = /MicroMessenger/i.test(navigator.userAgent); + if (window.FileReader) { + core.platform.fileReader = new FileReader(); + core.platform.fileReader.onload = function () { + core.readFileContent(core.platform.fileReader.result); + }; + core.platform.fileReader.onerror = function () { + if (core.platform.errorCallback) + core.platform.errorCallback(); + } + } + + core.flags.enableHDCanvas = core.getLocalStorage('enableHDCanvas', !core.platform.isIOS); + if (main.mode != 'editor') { + core.domStyle.scale = core.getLocalStorage('scale', 1); + if (core.flags.enableHDCanvas) core.domStyle.ratio = Math.max(window.devicePixelRatio || 1, core.domStyle.scale); + } +} + +core.prototype._init_others = function () { + // 一些额外的东西 + core.material.groundCanvas = document.createElement('canvas').getContext('2d'); + core.material.groundCanvas.canvas.width = core.material.groundCanvas.canvas.height = 32; + core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); + core.bigmap.tempCanvas = document.createElement('canvas').getContext('2d'); + core.bigmap.cacheCanvas = document.createElement('canvas').getContext('2d'); + core.loadImage("materials", 'fog', function (name, img) { core.animateFrame.weather.fog = img; }); + core.loadImage("materials", "cloud", function (name, img) { core.animateFrame.weather.cloud = img; }) + core.loadImage("materials", "sun", function (name, img) { core.animateFrame.weather.sun = img; }) + core.loadImage("materials", 'keyboard', function (name, img) { core.material.images.keyboard = img; }); + // 记录存档编号 + core.saves.saveIndex = core.getLocalStorage('saveIndex', 1); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); +} + +core.prototype._afterLoadResources = function (callback) { + // 初始化地图 + core.initStatus.maps = core.maps._initMaps(); + core.control._setRequestAnimationFrame(); + // 图片裁剪 + (main.splitImages || []).forEach(function (one) { + var name = core.getMappedName(one.name); + if (!core.material.images.images[name]) { + console.warn('找不到图片:' + name + ',无法裁剪'); + return; + } + if (!name.endsWith('.png')) { + console.warn('无法裁剪非png格式图片:' + name); + return; + } + var arr = core.splitImage(core.material.images.images[name], one.width, one.height); + for (var i = 0; i < arr.length; ++i) { + core.material.images.images[(one.prefix || "") + i + '.png'] = arr[i]; + } + }); + + if (core.plugin._afterLoadResources) + core.plugin._afterLoadResources(); + core.showStartAnimate(); + if (callback) callback(); +} + +core.prototype._init_plugins = function () { + core.plugin = new function () { }; + + for (var name in plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1) { + if (plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1[name] instanceof Function) { + try { + plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1[name].apply(core.plugin); + } + catch (e) { + console.error(e); + console.error("无法初始化插件" + name); + } + } + } + + core._forwardFunc("plugin"); +} + +core.prototype._forwardFuncs = function () { + for (var i = 0; i < main.loadList.length; ++i) { + var name = main.loadList[i]; + if (name == 'core') continue; + this._forwardFunc(name); + } +} + +core.prototype._forwardFunc = function (name, funcname) { + if (funcname == null) { + for (funcname in core[name]) { + if (funcname.charAt(0) != "_" && core[name][funcname] instanceof Function) { + this._forwardFunc(name, funcname); + } + } + return; + } + + if (core[funcname]) { + console.error("ERROR: 无法转发 " + name + " 中的函数 " + funcname + " 到 core 中!同名函数已存在。"); + return; + } + var parameterInfo = /^\s*function\s*[\w_$]*\(([\w_,$\s]*)\)\s*\{/.exec(core[name][funcname].toString()); + var parameters = (parameterInfo == null ? "" : parameterInfo[1]).replace(/\s*/g, '').replace(/,/g, ', '); + // core[funcname] = new Function(parameters, "return core."+name+"."+funcname+"("+parameters+");"); + eval("core." + funcname + " = function (" + parameters + ") {\n\treturn core." + name + "." + funcname + ".apply(core." + name + ", arguments);\n}"); +} + +core.prototype.doFunc = function (func, _this) { + if (typeof func == 'string') { + func = core.plugin[func]; + _this = core.plugin; + } + return func.apply(_this, Array.prototype.slice.call(arguments, 2)); +} + +// return new Core(); + +// })(); +var core = new core(); diff --git a/data.d.ts b/data.d.ts new file mode 100644 index 0000000..83b5566 --- /dev/null +++ b/data.d.ts @@ -0,0 +1,152 @@ +interface MainData { + /** + * 所有的楼层id + */ + readonly floorIds: FloorIds[]; + + /** + * 分区指定 + */ + readonly floorPartitions: [FloorIds, FloorIds?][]; + + /** + * 所有的额外素材 + */ + readonly tilesets: string[]; + + /** + * 所有的动画 + */ + readonly animates: AnimationIds[]; + + /** + * 所有的bgm + */ + readonly bgms: BgmIds[]; + + /** + * 所有的图片 + */ + readonly images: ImageIds[]; + + /** + * 所有的音效 + */ + readonly sounds: SoundIds[]; + + /** + * 所有的字体 + */ + readonly fonts: FontIds[]; + + /** + * 文件别名 + */ + readonly nameMap: NameMap; + + /** + * 难度选择 + */ + readonly levelChoose: LevelChooseEvent[]; + + /** + * 装备孔的名称 + */ + readonly equipName: string[]; + + /** + * 初始界面的bgm + */ + readonly startBgm: BgmIds; + + /** + * 主样式 + */ + readonly styles: MainStyle; + + /** + * 图片切分信息 + */ + readonly splitImages: SplitImageData; + + readonly plugin: string[]; +} + +interface FirstData { + /** + * 游戏标题 + */ + title: string; + + /** + * 游戏英文名,应当与mota.config.ts中的一致 + */ + name: string; + + /** + * 游戏版本 + */ + version: string; + + /** + * 初始地图 + */ + floorId: FloorIds; + + /** + * 勇士的初始信息 + */ + hero: HeroStatus; + + /** + * 标题界面事件化 + */ + startCanvas: MotaEvent; + + /** + * 初始剧情 + */ + startText: MotaEvent; + + /** + * 全局商店信息 + */ + shops: ShopEventOf[]; + + /** + * 升级事件 + */ + levelUp: LevelUpEvent; + + /** + * 游戏作者 + */ + author: string; +} + +/** + * 全塔属性信息 + */ +interface DataCore { + /** + * 全塔属性的main信息 + */ + readonly main: MainData; + + /** + * 初始化信息 + */ + readonly firstData: FirstData; + + /** + * 全局数值 + */ + readonly values: CoreValues; + + /** + * 全局变量 + */ + readonly flags: CoreFlags; +} + +declare const data: new () => Omit; diff --git a/data.js b/data.js new file mode 100644 index 0000000..28dea65 --- /dev/null +++ b/data.js @@ -0,0 +1,546 @@ +var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = +{ + "main": { + "floorIds": [ + "MT0", + "zzt1", + "zhu1", + "zhu2", + "zhu3", + "zhu4", + "zhu5", + "zhu6", + "zhu7", + "zhu8", + "zhu9", + "zhu10", + "zhu11", + "zhu12", + "zhu13", + "zhu14", + "zhu15", + "zhu16", + "zhu17", + "zhu18", + "zhu19", + "zhu20", + "zhu21", + "zhu22", + "zhu23", + "zhu24", + "zhu25", + "dong1", + "dong2", + "dong3", + "dong4", + "dong5", + "dong6", + "dong7", + "dong8", + "dong9" + ], + "floorPartitions": [], + "images": [ + "Anvil.png", + "BrewingStand.png", + "CraftingTable.png", + "EnchantmentTable.png", + "Furnace.png", + "Smoker-cookingfish.gif", + "achi.png", + "alex2.png", + "anjian.png", + "attribute.png", + "bag.png", + "bear.png", + "bg.jpg", + "blackheart.png", + "blackheart2.png", + "blackheart3.png", + "blackheartw.png", + "blackheartw2.png", + "blackheartw3.png", + "box1.png", + "box10.png", + "box2.png", + "box3.png", + "box4.png", + "box5.png", + "box6.png", + "box7.png", + "box8.png", + "box9.png", + "brave.png", + "breathe.png", + "buttonA.png", + "buttonB.png", + "dragon.png", + "goldheart.png", + "goldheart2.png", + "goldheart3.png", + "goldheartw.png", + "goldheartw2.png", + "goldheartw3.png", + "greenheart.png", + "greenheart2.png", + "greenheart3.png", + "greenheartw.png", + "greenheartw2.png", + "greenheartw3.png", + "head1.png", + "heart.png", + "heart2.png", + "heart3.png", + "heartw.png", + "heartw2.png", + "heartw3.png", + "hero.png", + "heropic1.png", + "hunger.png", + "hunger2.png", + "hunger3.png", + "hungerw.png", + "hungerw2.png", + "hungerw3.png", + "inventory.png", + "jz1.jpg", + "jz2.png", + "jz3.png", + "jz4.png", + "jz5.png", + "mc.png", + "nongtian.png", + "playarrow.png", + "steve2.png", + "task.png", + "tech1.png", + "tech2.png", + "tech3.png", + "winskin.png" + ], + "tilesets": [ + "magictower.png" + ], + "animates": [ + "atk_d", + "atk_l", + "atk_r", + "atk_u", + "guangquan", + "hand", + "sword", + "zone" + ], + "bgms": [ + "bgm.mp3", + "desert1.mp3", + "desert2.mp3", + "forest2.mp3", + "forest5.mp3", + "menu1.mp3", + "menu3.mp3", + "ocean1.mp3", + "ocean3.mp3", + "snowfield1.mp3", + "snowfield2.mp3", + "zhu1.mp3", + "zhu10.mp3", + "zhu11.mp3", + "zhu12.mp3", + "zhu3.mp3", + "zhu4.mp3", + "zhu5.mp3", + "zhu6.mp3" + ], + "sounds": [ + "E1_1.ogg", + "E1_2.ogg", + "E1_3.ogg", + "E1_4.ogg", + "E21_1.ogg", + "E21_2.ogg", + "E21_3.ogg", + "E21_4.ogg", + "E21_5.ogg", + "E21_6.ogg", + "E25_1.ogg", + "E25_2.ogg", + "E25_3.ogg", + "E25_4.ogg", + "E25_5.ogg", + "E25_6.ogg", + "E33_1.ogg", + "E33_2.ogg", + "E33_3.ogg", + "E33_4.ogg", + "E33_5.ogg", + "E37_1.ogg", + "E37_10.ogg", + "E37_2.ogg", + "E37_3.ogg", + "E37_4.ogg", + "E37_5.ogg", + "E37_6.ogg", + "E37_7.ogg", + "E37_8.ogg", + "E37_9.ogg", + "E41_1.ogg", + "E41_2.ogg", + "E41_3.ogg", + "E41_4.ogg", + "E45_1.ogg", + "E45_2.ogg", + "E45_3.ogg", + "E53_1.ogg", + "E53_2.ogg", + "E53_3.ogg", + "E53_4.ogg", + "E53_5.ogg", + "E53_6.ogg", + "E53_7.ogg", + "E57_1.ogg", + "E57_2.ogg", + "E57_3.ogg", + "E57_4.ogg", + "E57_5.ogg", + "E57_6.ogg", + "E57_7.ogg", + "E57_8.ogg", + "E69_1.ogg", + "E69_2.ogg", + "E69_3.ogg", + "E69_4.ogg", + "E69_5.ogg", + "E9_1.ogg", + "E9_2.ogg", + "E9_3.ogg", + "E9_4.ogg", + "E9_5.ogg", + "E9_6.ogg", + "Villager1.mp3", + "Villager2.mp3", + "Villager3.ogg", + "Villager4.mp3", + "Villager5.mp3", + "Villager6.ogg", + "armour.ogg", + "attack.mp3", + "attack1.ogg", + "attack2.ogg", + "attack3.ogg", + "bomb.mp3", + "break1.ogg", + "break2.ogg", + "cancel.ogg", + "centerFly.mp3", + "chestclosed.ogg", + "chestopen.ogg", + "confirm.ogg", + "cursor.ogg", + "dig_grass.ogg", + "dig_sand.ogg", + "dig_snow.ogg", + "dig_stone.ogg", + "dig_wood.ogg", + "door.mp3", + "door_open.ogg", + "eat1.ogg", + "eat2.ogg", + "equip.mp3", + "error.mp3", + "floor.mp3", + "gem.mp3", + "glass1.ogg", + "glass2.ogg", + "glass3.ogg", + "grass1.ogg", + "grass2.ogg", + "grass3.ogg", + "grass4.ogg", + "gravel1.ogg", + "gravel2.ogg", + "gravel3.ogg", + "gravel4.ogg", + "hit1.ogg", + "hit2.ogg", + "hit3.ogg", + "icePickaxe.mp3", + "item.mp3", + "jump.mp3", + "lava.ogg", + "levelup.ogg", + "load.mp3", + "open_ui.ogg", + "orb.ogg", + "pickaxe.mp3", + "pop.ogg", + "recovery.mp3", + "sand1.ogg", + "sand2.ogg", + "sand3.ogg", + "sand4.ogg", + "save.mp3", + "shop.mp3", + "snow1.ogg", + "snow2.ogg", + "snow3.ogg", + "snow4.ogg", + "step_grass1.ogg", + "step_grass2.ogg", + "step_grass3.ogg", + "step_grass4.ogg", + "step_grass5.ogg", + "step_grass6.ogg", + "step_gravel1.ogg", + "step_gravel2.ogg", + "step_gravel3.ogg", + "step_gravel4.ogg", + "step_sand1.ogg", + "step_sand2.ogg", + "step_sand3.ogg", + "step_sand4.ogg", + "step_sand5.ogg", + "step_snow1.ogg", + "step_snow2.ogg", + "step_snow3.ogg", + "step_snow4.ogg", + "step_stone1.ogg", + "step_stone2.ogg", + "step_stone3.ogg", + "step_stone4.ogg", + "step_stone5.ogg", + "step_stone6.ogg", + "step_swim1.ogg", + "step_swim2.ogg", + "step_swim3.ogg", + "step_swim4.ogg", + "step_wood1.ogg", + "step_wood2.ogg", + "step_wood3.ogg", + "step_wood4.ogg", + "step_wood5.ogg", + "step_wood6.ogg", + "stone1.ogg", + "stone2.ogg", + "stone3.ogg", + "stone4.ogg", + "wood1.ogg", + "wood2.ogg", + "wood3.ogg", + "wood4.ogg", + "worldlevelup.ogg", + "zone.mp3" + ], + "fonts": [ + "IPix", + "hanyifengshanghei45w", + "number", + "pixelmix" + ], + "nameMap": { + "确定": "confirm.ogg", + "取消": "cancel.ogg", + "操作失败": "error.mp3", + "光标移动": "cursor.ogg", + "打开界面": "open_ui.ogg", + "读档": "load.mp3", + "存档": "save.mp3", + "获得道具": "pop.ogg", + "回血": "recovery.mp3", + "炸弹": "bomb.mp3", + "飞行器": "centerFly.mp3", + "开关门": "door.mp3", + "上下楼": "floor.mp3", + "跳跃": "jump.mp3", + "破墙镐": "pickaxe.mp3", + "破冰镐": "icePickaxe.mp3", + "宝石": "gem.mp3", + "阻激夹域": "zone.mp3", + "穿脱装备": "equip.mp3", + "背景音乐": "bgm.mp3", + "攻击": "attack.mp3", + "背景图": "bg.jpg", + "商店": "shop.mp3", + "领域": "zone" + }, + "levelChoose": null, + "equipName": [ + "主手", + "副手", + "头盔", + "胸甲", + "护腿", + "靴子" + ], + "startBgm": null, + "styles": { + "startBackground": "project/images/bg.jpg", + "startVerticalBackground": "project/images/jz1.jpg", + "startLogoStyle": "color: black", + "startButtonsStyle": "background-color: #32369F; opacity: 0.85; color: #FFFFFF; border: #FFFFFF 2px solid; caret-color: #FFD700;", + "statusLeftBackground": "url(\"project/materials/wood1.png\") repeat", + "statusTopBackground": "url(project/images/bg.jpg)", + "toolsBackground": "black", + "floorChangingStyle": "background-color: black; color: white", + "statusBarColor": [ + 255, + 255, + 255, + 1 + ], + "borderColor": [ + 0, + 0, + 0, + 1 + ], + "selectColor": [ + 255, + 215, + 0, + 1 + ], + "font": "IPix" + }, + "splitImages": [ + { + "name": "dragon.png", + "width": 384, + "height": 96, + "prefix": "dragon_" + } + ] + }, + "firstData": { + "title": "我的世界:生存 体验版", + "name": "MC_survival0", + "version": "Ver 2.10.3", + "floorId": "zzt1", + "hero": { + "image": "steve2.png", + "animate": false, + "name": "史蒂夫", + "lv": 1, + "hpmax": 20, + "hp": 20, + "manamax": 0, + "mana": 0, + "atk": 2, + "def": 0, + "mdef": 0, + "money": 0, + "exp": 0, + "equipment": [], + "items": { + "constants": {}, + "tools": {}, + "equips": {} + }, + "loc": { + "direction": "up", + "x": 6, + "y": 7 + }, + "flags": {}, + "followers": [], + "steps": 0 + }, + "startCanvas": [], + "startText": [ + { + "type": "if", + "condition": "(!core.isReplaying())", + "true": [ + { + "type": "function", + "function": "function(){\ncore.plugin.title();\n}" + }, + { + "type": "setValue", + "name": "flag:start", + "value": "true" + }, + { + "type": "while", + "condition": "flag:start", + "data": [ + { + "type": "sleep", + "time": 1, + "noSkip": true + } + ] + } + ], + "false": [ + { + "type": "function", + "function": "function(){\ncore.plugin.replayHardSelect(core.status.replay.toReplay[0], true);\n}" + } + ] + }, + { + "type": "function", + "function": "function(){\n//core.hideStatusBar();\ncore.ui._createUIEvent();\n//侧边栏扩展插件让加的\nflags.showExtend = true;\n//extend.values.hideInVertical = true;\n}" + } + ], + "shops": [], + "levelUp": [ + { + "need": "值", + "title": "0", + "action": [] + } + ] + }, + "values": { + "lavaDamage": 100, + "poisonDamage": 10, + "weakValue": 20, + "redGem": 3, + "blueGem": 3, + "greenGem": 5, + "redPotion": 100, + "bluePotion": 250, + "yellowPotion": 500, + "greenPotion": 800, + "breakArmor": 0.9, + "counterAttack": 0.1, + "purify": 3, + "hatred": 2, + "animateSpeed": 300, + "moveSpeed": 140, + "statusCanvasRowsOnMobile": 3, + "floorChangeTime": 500 + }, + "flags": { + "statusBarItems": [ + "enableFloor", + "enableHPMax", + "enableHP", + "enableAtk", + "enableDef" + ], + "autoScale": true, + "extendToolbar": false, + "flyNearStair": false, + "flyRecordPosition": false, + "itemFirstText": false, + "equipboxButton": false, + "enableAddPoint": false, + "enableNegativeDamage": false, + "betweenAttackMax": false, + "useLoop": true, + "startUsingCanvas": true, + "statusCanvas": true, + "enableEnemyPoint": true, + "enableGentleClick": false, + "ignoreChangeFloor": true, + "canGoDeadZone": true, + "enableMoveDirectly": false, + "enableRouteFolding": true, + "disableShopOnDamage": false, + "blurFg": true, + "hideLeftStatusBar": false + } +} \ No newline at end of file diff --git a/docsify.min.js b/docsify.min.js new file mode 100644 index 0000000..4c22486 --- /dev/null +++ b/docsify.min.js @@ -0,0 +1,2 @@ +!function(){"use strict";function e(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}function t(e){return"string"==typeof e||"number"==typeof e}function n(){}function r(e){return"function"==typeof e}function i(e){var t=["init","mounted","beforeEach","afterEach","doneEach","ready"];e._hooks={},e._lifecycle={},t.forEach(function(t){var n=e._hooks[t]=[];e._lifecycle[t]=function(e){return n.push(e)}})}function o(e,t,r,i){void 0===i&&(i=n);var o=e._hooks[t],a=function(e){var t=o[e];if(e>=o.length)i(r);else if("function"==typeof t)if(2===t.length)t(r,function(t){r=t,a(e+1)});else{var n=t(r);r=void 0!==n?n:r,a(e+1)}else a(e+1)};a(0)}function a(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return s(e);e=t?s(e):fe[e]||(fe[e]=s(e))}return e}function s(e,t){return t?e.querySelector(t):ge.querySelector(e)}function l(e,t){return[].slice.call(t?e.querySelectorAll(t):ge.querySelectorAll(e))}function u(e,t){return e=ge.createElement(e),t&&(e.innerHTML=t),e}function c(e,t){return e.appendChild(t)}function h(e,t){return e.insertBefore(t,e.children[0])}function p(e,t,n){r(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function d(e,t,n){r(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function f(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}function g(e){c(ve,u("style",e))}function m(e){return e?(/\/\//.test(e)||(e="https://github.com/"+e),''):""}function v(e){var t='

';return(ke?t+"
":"
"+t)+'
\x3c!--main--\x3e
'}function y(){var e=", 100%, 85%";return'
'}function b(e,t){return void 0===t&&(t=""),e&&e.length?(e.forEach(function(e){t+='
  • '+e.title+"
  • ",e.children&&(t+='
    • '+b(e.children)+"
    ")}),t):""}function k(e,t){return'

    '+t.slice(5).trim()+"

    "}function w(e){return""}function x(){var e=u("div");e.classList.add("progress"),c(me,e),pe=e}function _(e,t){void 0===t&&(t=!1);var r=new XMLHttpRequest,i=function(){r.addEventListener.apply(r,arguments)},o=_e[e];return o?{then:function(e){return e(o.content,o.opt)},abort:n}:(r.open("GET",e),r.send(),{then:function(o,a){if(void 0===a&&(a=n),t){var s=setInterval(function(e){return xe({step:Math.floor(5*Math.random()+1)})},500);i("progress",xe),i("loadend",function(e){xe(e),clearInterval(s)})}i("error",a),i("load",function(t){var n=t.target;if(n.status>=400)a(n);else{var i=_e[e]={content:n.response,opt:{updatedAt:r.getResponseHeader("last-modified")}};o(i.content,i.opt)}})},abort:function(e){return 4!==r.readyState&&r.abort()}})}function S(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}function C(e,t){return t={exports:{}},e(t,t.exports),t.exports}function L(e,t){var n=[],r={};return e.forEach(function(e){var i=e.level||1,o=i-1;i>t||(r[o]?r[o].children=(r[o].children||[]).concat(e):n.push(e),r[i]=e)}),n}function E(e){return e.toLowerCase()}function T(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,E).replace(/<[^>\d]+>/g,"").replace(Oe,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=Pe[t];return n=Pe.hasOwnProperty(t)?n+1:0,Pe[t]=n,n&&(t=t+"-"+n),t}function $(e,t){return''+t+''}function A(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,be&&window.emojify||$).replace(/__colon__/g,":")}function P(e){var t={};return(e=e.trim().replace(/^(\?|#|&)/,""))?(e.split("&").forEach(function(e){var n=e.replace(/\+/g," ").split("=");t[n[0]]=n[1]&&je(n[1])}),t):t}function O(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)t.indexOf(r)>-1||n.push(e[r]?(Me(r)+"="+Me(e[r])).toLowerCase():Me(r));return n.length?"?"+n.join("&"):""}function j(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return Re(e.join("/"))}function M(e){void 0===e&&(e="");var t={};return e&&(e=e.replace(/:([\w-]+)=?([\w-]+)?/g,function(e,n,r){return t[n]=r||!0,""}).trim()),{str:e,config:t}}function q(e,t){var n=function(e){return me.classList.toggle("close")};e=a(e),p(e,"click",function(e){e.stopPropagation(),n()});var r=a(".sidebar");ke&&p(me,"click",function(e){return me.classList.contains("close")&&n()}),p(r,"click",function(e){return setTimeout(0)})}function N(){var e=a("section.cover");if(e){var t=e.getBoundingClientRect().height;window.pageYOffset>=t||e.classList.contains("hidden")?f(me,"add","sticky"):f(me,"remove","sticky")}}function R(e,t,n,r){t=a(t);var i,o=l(t,"a"),s=e.toURL(e.getCurrentPath());return o.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),r=n?e.parentNode:e;0!==s.indexOf(t)||i?f(r,"remove","active"):(i=e,f(r,"add","active"))}),r&&(ge.title=i?i.innerText+" - "+ze:ze),i}function F(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function H(e){Ue&&Ue.stop(),Ye=!1,Ue=new Ie({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){Ye=!0,Ue=null}).begin()}function z(e){if(Ye){for(var t,n=a(".sidebar"),r=l(".anchor"),i=s(n,".sidebar-nav"),o=s(n,"li.active"),u=document.documentElement,c=(u&&u.scrollTop||document.body.scrollTop)-Ze,h=0,p=r.length;hc){t||(t=d);break}t=d}if(t){var f=De[B(e,t.getAttribute("data-id"))];if(f&&f!==o&&(o&&o.classList.remove("active"),f.classList.add("active"),o=f,!We&&me.classList.contains("sticky"))){var g=n.clientHeight,m=o.offsetTop+o.clientHeight+40,v=o.offsetTop>=i.scrollTop&&m<=i.scrollTop+g,y=m-0script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Y(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?Ee(n)(new Date(t)):t,e.replace(/{docsify-updated}/g,t)}function Z(e){e||(e="not found"),this._renderTo(".markdown-section",e),!this.config.loadSidebar&&this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||U()?this.config.executeScript&&U():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function G(e){var n=a(".app-name-link"),r=e.config.nameLink,i=e.route.path;if(n)if(t(e.config.nameLink))n.setAttribute("href",r);else if("object"==typeof r){var o=Object.keys(r).filter(function(e){return i.indexOf(e)>-1})[0];n.setAttribute("href",r[o])}}function X(e){var t=e.config;e.compiler=new He(t,e.router);var n=t.el||"#app",r=s("nav")||u("nav"),i=s(n),o="",a=me;i?(t.repo&&(o+=m(t.repo)),t.coverpage&&(o+=y()),o+=v(t),e._renderTo(i,o,!0)):e.rendered=!0,t.mergeNavbar&&ke?a=s(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),h(a,r),t.themeColor&&(ge.head.appendChild(u("div",w(t.themeColor)).firstElementChild),Se(t.themeColor)),e._updateRender(),f(me,"ready")}function V(e,t,n){var r=Object.keys(t).filter(function(t){return(Xe[t]||(Xe[t]=new RegExp("^"+t+"$"))).test(e)&&e!==n})[0];return r?V(e.replace(Xe[r],t[r]),t,e):e}function J(e){return/\.(md|html)$/g.test(e)?e:/\/$/g.test(e)?e+"README.md":e+".md"}function Q(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,t>=0?t:0)+"#"+e)}function K(e){e.router.normalize(),e.route=e.router.parse(),me.setAttribute("data-page",e.route.file)}function ee(e){var t,n=e.config,r=n.routerMode||"hash";t="history"===r&&we?new Ke(n):new Qe(n),e.router=t,K(e),et=e.route,t.onchange(function(t){if(K(e),e._updateRender(),et.path===e.route.path)return void e.$resetEvents();e.$fetch(),et=e.route})}function te(e){q("button.sidebar-toggle",e.router),e.config.coverpage?!ke&&p("scroll",N):me.classList.add("sticky")}function ne(e,t,n,r,i,o){e=o?e:e.replace(/\/$/,""),(e=Ne(e))&&_(i.router.getFile(e+n)+t).then(r,function(o){return ne(e,t,n,r,i)})}function re(e){var t=e.config,n=t.loadSidebar;if(e.rendered){var r=R(e.router,".sidebar-nav",!0,!0);n&&r&&(r.parentNode.innerHTML+=window.__SUB_SIDEBAR__),e._bindEventOnRendered(r),e._fetchCover(),e.$resetEvents(),o(e,"doneEach"),o(e,"ready")}else e.$fetch(function(t){return o(e,"ready")})}function ie(e){[].concat(e.config.plugins).forEach(function(t){return r(t)&&t(e._lifecycle,e)})}function oe(){this._init()}var ae=e(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),se=Object.assign||function(e){for(var t=arguments,n=Object.prototype.hasOwnProperty,r=1;r80?80:t):t=Math.floor(n/r*100),pe.style.opacity=1,pe.style.width=t>=95?"100%":t+"%",t>=95&&(clearTimeout(de),de=setTimeout(function(e){pe.style.opacity=0,pe.style.width="0%"},200))},_e={},Se=function(e){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var t=l("style:not(.inserted),link");[].forEach.call(t,function(t){if("STYLE"===t.nodeName)S(t,e);else if("LINK"===t.nodeName){var n=t.getAttribute("href");if(!/\.css$/.test(n))return;_(n).then(function(t){var n=u("style",t);ve.appendChild(n),S(n,e)})}})}},Ce=/([^{]*?)\w(?=\})/g,Le={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds"},Ee=function(e){var t=[],n=0;return e.replace(Ce,function(r,i,o){t.push(e.substring(n,o-1)),n=o+=r.length+1,t.push(function(e){return("00"+("string"==typeof Le[r]?e[Le[r]]():Le[r](e))).slice(-r.length)})}),n!==e.length&&t.push(e.substring(n)),function(e){for(var n="",r=0,i=e||new Date;r/g,">").replace(/"/g,""").replace(/'/g,"'")}function a(e){return e.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g,function(e,t){return t=t.toLowerCase(),"colon"===t?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}function s(e,t){return e=e.source,t=t||"",function n(r,i){return r?(i=i.source||i,i=i.replace(/(^|[^\[])\^/g,"$1"),e=e.replace(r,i),n):new RegExp(e,t)}}function l(){}function u(e){for(var t,n,r=arguments,i=1;iAn error occured:

    "+o(e.message+"",!0)+"
    ";throw e}}var h={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:l,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:l,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:l,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};h.bullet=/(?:[*+-]|\d+\.)/,h.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,h.item=s(h.item,"gm")(/bull/g,h.bullet)(),h.list=s(h.list)(/bull/g,h.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+h.def.source+")")(),h.blockquote=s(h.blockquote)("def",h.def)(),h._tag="(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b",h.html=s(h.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,h._tag)(),h.paragraph=s(h.paragraph)("hr",h.hr)("heading",h.heading)("lheading",h.lheading)("blockquote",h.blockquote)("tag","<"+h._tag)("def",h.def)(),h.normal=u({},h),h.gfm=u({},h.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),h.gfm.paragraph=s(h.paragraph)("(?!","(?!"+h.gfm.fences.source.replace("\\1","\\2")+"|"+h.list.source.replace("\\1","\\3")+"|")(),h.tables=u({},h.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),t.rules=h,t.lex=function(e,n){return new t(n).lex(e)},t.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},t.prototype.token=function(e,t,n){for(var r,i,o,a,s,l,u,c,p,d=this,e=e.replace(/^ +$/gm,"");e;)if((o=d.rules.newline.exec(e))&&(e=e.substring(o[0].length),o[0].length>1&&d.tokens.push({type:"space"})),o=d.rules.code.exec(e))e=e.substring(o[0].length),o=o[0].replace(/^ {4}/gm,""),d.tokens.push({type:"code",text:d.options.pedantic?o:o.replace(/\n+$/,"")});else if(o=d.rules.fences.exec(e))e=e.substring(o[0].length),d.tokens.push({type:"code",lang:o[2],text:o[3]||""});else if(o=d.rules.heading.exec(e))e=e.substring(o[0].length),d.tokens.push({type:"heading",depth:o[1].length,text:o[2]});else if(t&&(o=d.rules.nptable.exec(e))){for(e=e.substring(o[0].length),l={type:"table",header:o[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3].replace(/\n$/,"").split("\n")},c=0;c ?/gm,""),d.token(o,t,!0),d.tokens.push({type:"blockquote_end"});else if(o=d.rules.list.exec(e)){for(e=e.substring(o[0].length),a=o[2],d.tokens.push({type:"list_start",ordered:a.length>1}),o=o[0].match(d.rules.item),r=!1,p=o.length,c=0;c1&&s.length>1||(e=o.slice(c+1).join("\n")+e,c=p-1)),i=r||/\n\n(?!\s*$)/.test(l),c!==p-1&&(r="\n"===l.charAt(l.length-1),i||(i=r)),d.tokens.push({type:i?"loose_item_start":"list_item_start"}),d.token(l,!1,n),d.tokens.push({type:"list_item_end"});d.tokens.push({type:"list_end"})}else if(o=d.rules.html.exec(e))e=e.substring(o[0].length),d.tokens.push({type:d.options.sanitize?"paragraph":"html",pre:!d.options.sanitizer&&("pre"===o[1]||"script"===o[1]||"style"===o[1]),text:o[0]});else if(!n&&t&&(o=d.rules.def.exec(e)))e=e.substring(o[0].length),d.tokens.links[o[1].toLowerCase()]={href:o[2],title:o[3]};else if(t&&(o=d.rules.table.exec(e))){for(e=e.substring(o[0].length),l={type:"table",header:o[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3].replace(/(?: *\| *)?\n$/,"").split("\n")},c=0;c])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:l,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:l,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/,p.link=s(p.link)("inside",p._inside)("href",p._href)(),p.reflink=s(p.reflink)("inside",p._inside)(),p.normal=u({},p),p.pedantic=u({},p.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),p.gfm=u({},p.normal,{escape:s(p.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:s(p.text)("]|","~]|")("|","|https?://|")()}),p.breaks=u({},p.gfm,{br:s(p.br)("{2,}","*")(),text:s(p.gfm.text)("{2,}","*")()}),n.rules=p,n.output=function(e,t,r){return new n(t,r).output(e)},n.prototype.output=function(e){for(var t,n,r,i,a=this,s="";e;)if(i=a.rules.escape.exec(e))e=e.substring(i[0].length),s+=i[1];else if(i=a.rules.autolink.exec(e))e=e.substring(i[0].length),"@"===i[2]?(n=":"===i[1].charAt(6)?a.mangle(i[1].substring(7)):a.mangle(i[1]),r=a.mangle("mailto:")+n):(n=o(i[1]),r=n),s+=a.renderer.link(r,null,n);else if(a.inLink||!(i=a.rules.url.exec(e))){if(i=a.rules.tag.exec(e))!a.inLink&&/^/i.test(i[0])&&(a.inLink=!1),e=e.substring(i[0].length),s+=a.options.sanitize?a.options.sanitizer?a.options.sanitizer(i[0]):o(i[0]):i[0];else if(i=a.rules.link.exec(e))e=e.substring(i[0].length),a.inLink=!0,s+=a.outputLink(i,{href:i[2],title:i[3]}),a.inLink=!1;else if((i=a.rules.reflink.exec(e))||(i=a.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),!(t=a.links[t.toLowerCase()])||!t.href){s+=i[0].charAt(0),e=i[0].substring(1)+e;continue}a.inLink=!0,s+=a.outputLink(i,t),a.inLink=!1}else if(i=a.rules.strong.exec(e))e=e.substring(i[0].length),s+=a.renderer.strong(a.output(i[2]||i[1]));else if(i=a.rules.em.exec(e))e=e.substring(i[0].length),s+=a.renderer.em(a.output(i[2]||i[1]));else if(i=a.rules.code.exec(e))e=e.substring(i[0].length),s+=a.renderer.codespan(o(i[2],!0));else if(i=a.rules.br.exec(e))e=e.substring(i[0].length),s+=a.renderer.br();else if(i=a.rules.del.exec(e))e=e.substring(i[0].length),s+=a.renderer.del(a.output(i[1]));else if(i=a.rules.text.exec(e))e=e.substring(i[0].length),s+=a.renderer.text(o(a.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(i[0].length),n=o(i[1]),r=n,s+=a.renderer.link(r,null,n);return s},n.prototype.outputLink=function(e,t){var n=o(t.href),r=t.title?o(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,o(e[1]))},n.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014\/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014\/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},n.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},r.prototype.code=function(e,t,n){if(this.options.highlight){var r=this.options.highlight(e,t);null!=r&&r!==e&&(n=!0,e=r)}return t?'
    '+(n?e:o(e,!0))+"\n
    \n":"
    "+(n?e:o(e,!0))+"\n
    "},r.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n){return"'+e+"\n"},r.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},r.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+"\n"},r.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},r.prototype.paragraph=function(e){return"

    "+e+"

    \n"},r.prototype.table=function(e,t){return"\n\n"+e+"\n\n"+t+"\n
    \n"},r.prototype.tablerow=function(e){return"\n"+e+"\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">")+e+"\n"},r.prototype.strong=function(e){return""+e+""},r.prototype.em=function(e){return""+e+""},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(this.options.sanitize){try{var r=decodeURIComponent(a(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:"))return""}var i='
    "},r.prototype.image=function(e,t,n){var r=''+n+'":">"},r.prototype.text=function(e){return e},i.parse=function(e,t,n){return new i(t,n).parse(e)},i.prototype.parse=function(e){var t=this;this.inline=new n(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var r="";this.next();)r+=t.tok();return r},i.prototype.next=function(){return this.token=this.tokens.pop()},i.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},i.prototype.parseText=function(){for(var e=this,t=this.token.text;"text"===this.peek().type;)t+="\n"+e.next().text;return this.inline.output(t)},i.prototype.tok=function(){var e=this;switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var t,n,r,i,o="",a="";for(r="",t=0;te.length)break e;if(!(b instanceof i)){c.lastIndex=0;var k=c.exec(b),w=1;if(!k&&d&&v!=o.length-1){if(c.lastIndex=y,!(k=c.exec(e)))break;for(var x=k.index+(p?k[1].length:0),_=k.index+k[0].length,S=v,C=y,L=o.length;S=C&&(++v,y=C);if(o[v]instanceof i||o[S-1].greedy)continue;w=S-v,b=e.slice(y,C),k.index-=y}if(k){p&&(f=k[1].length);var x=k.index+f,k=k[0].slice(f),_=x+k.length,E=b.slice(0,x),T=b.slice(_),$=[v,w];E&&$.push(E);var A=new i(s,h?r.tokenize(k,h):k,g,k,d);$.push(A),T&&$.push(T),Array.prototype.splice.apply(o,$)}}}}}return o},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var i,o=0;i=n[o++];)i(t)}}},i=r.Token=function(e,t,n,r,i){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!i};if(i.stringify=function(e,t,n){if("string"==typeof e)return e;if("Array"===r.util.type(e))return e.map(function(n){return i.stringify(n,t,e)}).join("");var o={type:e.type,content:i.stringify(e.content,t,n),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:n};if("comment"==o.type&&(o.attributes.spellcheck="true"),e.alias){var a="Array"===r.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(o.classes,a)}r.hooks.run("wrap",o);var s=Object.keys(o.attributes).map(function(e){return e+'="'+(o.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+o.tag+' class="'+o.classes.join(" ")+'"'+(s?" "+s:"")+">"+o.content+""},!t.document)return t.addEventListener?(t.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,o=n.code,a=n.immediateClose;t.postMessage(r.highlight(o,r.languages[i],i)),a&&t.close()},!1),t.Prism):t.Prism;var o=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return o&&(r.filename=o.src,document.addEventListener&&!o.hasAttribute("data-manual")&&("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(r.highlightAll):window.setTimeout(r.highlightAll,16):document.addEventListener("DOMContentLoaded",r.highlightAll))),t.Prism}();e.exports&&(e.exports=n),void 0!==Te&&(Te.Prism=n),n.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},n.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),n.languages.xml=n.languages.markup,n.languages.html=n.languages.markup,n.languages.mathml=n.languages.markup,n.languages.svg=n.languages.markup,n.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:{pattern:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},n.languages.css.atrule.inside.rest=n.util.clone(n.languages.css),n.languages.markup&&(n.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:n.languages.css,alias:"language-css"}}),n.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:n.languages.css}},alias:"language-css"}},n.languages.markup.tag)),n.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(true|false)\b/,function:/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},n.languages.javascript=n.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,function:/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*\*?|\/|~|\^|%|\.{3}/}),n.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}}),n.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:n.languages.javascript}},string:/[\s\S]+/}}}),n.languages.markup&&n.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:n.languages.javascript,alias:"language-javascript"}}),n.languages.js=n.languages.javascript,function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.forEach&&Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var r,i=t.getAttribute("data-src"),o=t,a=/\blang(?:uage)?-(?!\*)(\w+)\b/i;o&&!a.test(o.className);)o=o.parentNode;if(o&&(r=(t.className.match(a)||[,""])[1]),!r){var s=(i.match(/\.(\w+)$/)||[,""])[1];r=e[s]||s}var l=document.createElement("code");l.className="language-"+r,t.textContent="",l.textContent="Loading…",t.appendChild(l);var u=new XMLHttpRequest;u.open("GET",i,!0),u.onreadystatechange=function(){4==u.readyState&&(u.status<400&&u.responseText?(l.textContent=u.responseText,n.highlightElement(l)):u.status>=400?l.textContent="✖ Error "+u.status+" while fetching file: "+u.statusText:l.textContent="✖ Error: File does not exist or is empty")},u.send(null)})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))}()}),Pe={},Oe=/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,.\/:;<=>?@\[\]^`{|}~]/g;T.clear=function(){Pe={}};var je=decodeURIComponent,Me=encodeURIComponent,qe=e(function(e){return/(:|(\/{2}))/g.test(e)}),Ne=e(function(e){return/\/$/g.test(e)?e:(e=e.match(/(\S*\/)[^\/]+$/))?e[1]:""}),Re=e(function(e){return e.replace(/^\/+/,"/").replace(/([^:])\/{2,}/g,"$1/")}),Fe={},He=function(t,n){this.config=t,this.router=n,this.cacheTree={},this.toc=[],this.linkTarget=t.externalLinkTarget||"_blank",this.contentBase=n.getBasePath();var i,o=this._initRenderer(),a=t.markdown||{};r(a)?i=a($e,o):($e.setOptions(se(a,{renderer:se(o,a.renderer)})),i=$e),this.compile=e(function(e){var n="";return e?(n=i(e),n=t.noEmoji?n:A(n),T.clear(),n):e})};He.prototype.matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks,n=0;n'+e+""},a.code=e.code=function(e,t){return void 0===t&&(t=""),'
    '+Ae.highlight(e,Ae.languages[t]||Ae.languages.markup)+"
    "},a.link=e.link=function(e,t,i){void 0===t&&(t="");var a="",s=M(t),l=s.str,u=s.config;return t=l,/:|(\/{2})/.test(e)||o.matchNotCompileLink(e)||u.ignore?a+=' target="'+n+'"':e=r.toURL(e,null,r.getCurrentPath()),u.target&&(a+=" target="+u.target),u.disabled&&(a+=" disabled",e="javascript:void(0)"),t&&(a+=' title="'+t+'"'),'"+i+""},a.paragraph=e.paragraph=function(e){return/^!>/.test(e)?k("tip",e):/^\?>/.test(e)?k("warn",e):"

    "+e+"

    "},a.image=e.image=function(e,t,n){var r=e,o="",a=M(t),s=a.str,l=a.config;return t=s,l["no-zoom"]&&(o+=" data-no-zoom"),t&&(o+=' title="'+t+'"'),qe(e)||(r=j(i,e)),''+n+'"};var s=/^\[([ x])\] +/;return a.listitem=e.listitem=function(e){var t=s.exec(e);return t&&(e=e.replace(s,'")),""+e+"\n"},e.origin=a,e},He.prototype.sidebar=function(e,t){var n=this.router.getCurrentPath(),r="";if(e)r=this.compile(e),r=r&&r.match(/]*>([\s\S]+)<\/ul>/g)[0];else{var i=this.cacheTree[n]||L(this.toc,t);r=b(i,"
      "),this.cacheTree[n]=i}return r},He.prototype.subSidebar=function(e){if(!e)return void(this.toc=[]);var t=this.router.getCurrentPath(),n=this,r=n.cacheTree,i=n.toc;i[0]&&i[0].ignoreAllSubs&&i.splice(0),i[0]&&1===i[0].level&&i.shift();for(var o=0;o')},He.prototype.article=function(e){return this.compile(e)},He.prototype.cover=function(e){var t=this.toc.slice(),n=this.compile(e);return this.toc=t.slice(),n};var ze=ge.title,Be=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};F(this,e),this.duration=t.duration||1e3,this.ease=t.easing||this._defaultEase,this.start=t.start,this.end=t.end,this.frame=null,this.next=null,this.isRunning=!1,this.events={},this.direction=this.startthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),e}(),De={},We=!1,Ue=null,Ye=!0,Ze=0,Ge=ge.scrollingElement||ge.documentElement,Xe={},Ve=function(e){this.config=e};Ve.prototype.getBasePath=function(){return this.config.basePath},Ve.prototype.getFile=function(e,t){e=e||this.getCurrentPath();var n=this,r=n.config,i=this.getBasePath();return e=r.alias?V(e,r.alias):e,e=J(e),e="/README.md"===e?r.homepage||e:e,e=qe(e)?e:j(i,e),t&&(e=e.replace(new RegExp("^"+i),"")),e},Ve.prototype.onchange=function(e){void 0===e&&(e=n),e()},Ve.prototype.getCurrentPath=function(){},Ve.prototype.normalize=function(){},Ve.prototype.parse=function(){},Ve.prototype.toURL=function(){};var Je=e(function(e){return e.replace("#","?id=")}),Qe=function(e){function t(t){e.call(this,t),this.mode="hash"}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.getBasePath=function(){var e=window.location.pathname||"",t=this.config.basePath;return/^(\/|https?:)/g.test(t)?t:Re(e+"/"+t)},t.prototype.getCurrentPath=function(){var e=location.href,t=e.indexOf("#");return-1===t?"":e.slice(t+1)},t.prototype.onchange=function(e){void 0===e&&(e=n),p("hashchange",e)},t.prototype.normalize=function(){var e=this.getCurrentPath();if(e=Je(e),"/"===e.charAt(0))return Q(e);Q("/"+e)},t.prototype.parse=function(e){void 0===e&&(e=location.href);var t="",n=e.indexOf("#");n>=0&&(e=e.slice(n+1));var r=e.indexOf("?");return r>=0&&(t=e.slice(r+1),e=e.slice(0,r)),{path:e,file:this.getFile(e,!0),query:P(t)}},t.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(Je(e));if(i.query=se({},i.query,t),e=i.path+O(i.query),e=e.replace(/\.md(\?)|\.md$/,"$1"),r){var o=n.indexOf("?");e=(o>0?n.substr(0,o):n)+e}return Re("#/"+e)},t}(Ve),Ke=function(e){function t(t){e.call(this,t),this.mode="history"}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.getCurrentPath=function(){var e=this.getBasePath(),t=window.location.pathname;return e&&0===t.indexOf(e)&&(t=t.slice(e.length)),(t||"/")+window.location.search+window.location.hash},t.prototype.onchange=function(e){void 0===e&&(e=n),p("click",function(t){var n="A"===t.target.tagName?t.target:t.target.parentNode;if("A"===n.tagName&&!/_blank/.test(n.target)){t.preventDefault();var r=n.href;window.history.pushState({key:r},"",r),e()}}),p("popstate",e)},t.prototype.parse=function(e){void 0===e&&(e=location.href);var t="",n=e.indexOf("?");n>=0&&(t=e.slice(n+1),e=e.slice(0,n));var r=j(location.origin),i=e.indexOf(r);return i>-1&&(e=e.slice(i+r.length)),{path:e,file:this.getFile(e),query:P(t)}},t.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(e);return i.query=se({},i.query,t),e=i.path+O(i.query),e=e.replace(/\.md(\?)|\.md$/,"$1"),r&&(e=n+e),Re("/"+e)},t}(Ve),et={},tt=Object.freeze({cached:e,hyphenate:ae,merge:se,isPrimitive:t,noop:n,isFn:r,inBrowser:be,isMobile:ke,supportsPushState:we,parseQuery:P,stringifyQuery:O,getPath:j,isAbsolutePath:qe,getParentPath:Ne,cleanPath:Re}),nt=oe.prototype;!function(e){e._init=function(){var e=this;e.config=le||{},i(e),ie(e),o(e,"init"),ee(e),X(e),te(e),re(e),o(e,"mounted")}}(nt),function(e){e.route={}}(nt),function(e){e._renderTo=function(e,t,n){var r=a(e);r&&(r[n?"outerHTML":"innerHTML"]=t)},e._renderSidebar=function(e){var t=this.config,n=t.maxLevel,r=t.subMaxLevel,i=t.loadSidebar;this._renderTo(".sidebar-nav",this.compiler.sidebar(e,n));var o=R(this.router,".sidebar-nav",!0,!0);i&&o?o.parentNode.innerHTML+=this.compiler.subSidebar(r)||"":this.compiler.subSidebar(),this._bindEventOnRendered(o)},e._bindEventOnRendered=function(e){var t=this.config,n=t.autoHeader,r=t.auto2top;if(I(this.router),n&&e){var i=a("#main"),o=i.children[0];if(o&&"H1"!==o.tagName){var s=u("h1");s.innerText=e.innerText,h(i,s)}}r&&W(r)},e._renderNav=function(e){e&&this._renderTo("nav",this.compiler.compile(e)),R(this.router,"nav")},e._renderMain=function(e,t){var n=this;if(void 0===t&&(t={}),!e)return Z.call(this,e);o(this,"beforeEach",e,function(e){var r=n.isHTML?e:n.compiler.compile(e);t.updatedAt&&(r=Y(r,t.updatedAt,n.config.formatUpdated)),o(n,"afterEach",r,function(e){return Z.call(n,e)})})},e._renderCover=function(e){var t=a(".cover");if(!e)return void f(t,"remove","show");f(t,"add","show");var n=this.coverIsHTML?e:this.compiler.cover(e),r=n.trim().match('

      ([^<]*?)

      $');if(r){if("color"===r[2])t.style.background=r[1]+(r[3]||"");else{var i=r[1];f(t,"add","has-mask"),qe(r[1])||(i=j(this.router.getBasePath(),r[1])),t.style.backgroundImage="url("+i+")",t.style.backgroundSize="cover",t.style.backgroundPosition="center center"}n=n.replace(r[0],"")}this._renderTo(".cover-main",n),N()},e._updateRender=function(){G(this)}}(nt),function(e){var t;e._fetch=function(e){var r=this;void 0===e&&(e=n);var i=this.route,o=i.path,a=i.query,s=O(a,["id"]),l=this.config,u=l.loadNavbar,c=l.loadSidebar;t&&t.abort&&t.abort(),t=_(this.router.getFile(o)+s,!0),this.isHTML=/\.html$/g.test(o);var h=function(){if(!c)return e();ne(o,s,c,function(t){r._renderSidebar(t),e()},r,!0)};t.then(function(e,t){r._renderMain(e,t),h()},function(e){r._renderMain(null),h()}),u&&ne(o,s,u,function(e){return r._renderNav(e)},this,!0)},e._fetchCover=function(){var e=this,t=this.config,n=t.coverpage,r=this.route.query,i=Ne(this.route.path),o=this.router.getFile(i+n);if("/"!==this.route.path||!n)return void this._renderCover();this.coverIsHTML=/\.html$/g.test(o),_(o+O(r,["id"])).then(function(t){return e._renderCover(t)})},e.$fetch=function(e){var t=this;void 0===e&&(e=n),this._fetchCover(),this._fetch(function(n){t.$resetEvents(),o(t,"doneEach"),e()})}}(nt),function(e){e.$resetEvents=function(){D(this.route.path,this.route.query.id),R(this.router,"nav")}}(nt),function(){window.Docsify={util:tt,dom:ye,get:_,slugify:T},window.DocsifyCompiler=He,window.marked=$e,window.Prism=Ae}(),oe.version="4.5.5",function(e){var t=document.readyState;if("complete"===t||"interactive"===t)return setTimeout(e,0);document.addEventListener("DOMContentLoaded",e)}(function(e){return new oe})}(); \ No newline at end of file diff --git a/dynamicMapEditor.js b/dynamicMapEditor.js new file mode 100644 index 0000000..7401db6 --- /dev/null +++ b/dynamicMapEditor.js @@ -0,0 +1,514 @@ +/** + * 运行时可编辑地图的扩展,By fux2(黄鸡) + */ + +"use strict"; + +function dynamicMapEditor() { + // 所有显示的ID + this.displayIds = [ + 'none', 'yellowWall', 'blueWall', 'whiteWall', 'yellowDoor', 'blueDoor', 'redDoor', 'star', 'lava', 'lavaNet', + 'yellowKey', 'blueKey', 'redKey', 'redGem', 'blueGem', 'greenGem', 'yellowGem', + 'redPotion', 'bluePotion', 'yellowPotion', 'greenPotion', 'pickaxe', 'bomb', 'centerFly', + 'cls:autotile', 'cls:enemys', 'cls:enemy48' + ]; + this.items = []; + this.userChanged = []; + this.savedItems = []; + this.dom = null; + this.canvas = null; + this.mapRecord = {}; + this.enemyModified = false; + this.valueModified = false; + this.pageId = 0; + this.pageMaxItems = 21; + this.pageMax = 0; + this.selectedIndex = 0; + this.selectedItem = null; + this._init(); +} + +// ------ init + +dynamicMapEditor.prototype._init = function () { + this.dom = document.createElement("canvas"); + this.dom.id = 'dynamicMapEditor'; + this.dom.style.display = 'none'; + this.dom.style.position = 'absolute'; + this.dom.style.left = '3px'; + this.dom.style.top = '3px'; + this.dom.style.zIndex = 99999; + this.canvas = this.dom.getContext("2d"); + core.dom.gameGroup.appendChild(this.dom); + + this.initInfos(); + this.pageMax = Math.ceil(this.items.length / this.pageMaxItems); + core.registerAction('onkeyUp', 'plugin_dme_keydown', this.onKeyUp.bind(this), 200); + core.registerAction('onclick', 'plugin_dme_click', this.onMapClick.bind(this), 200); + this.dom.addEventListener("click",this.onBoxClick.bind(this)); + this.showInitHelp(); +} + +dynamicMapEditor.prototype.initInfos = function () { + this.items = []; + var ids = {}; + this.displayIds.forEach(function (v) { + if (v.startsWith("cls:")) { + var cls = v.substr(4); + for (var id in core.maps.blocksInfo) { + var u = core.maps.blocksInfo[id]; + if (u && u.cls == cls) { + if (ids[u.id]) continue; + this.items.push(core.getBlockInfo(u.id)); + ids[u.id] = true; + } + } + } else if (v == 'none') { + this.items.push({"number": 0, "id": "none", "name": "清除块"}); + } else { + this.items.push(core.getBlockInfo(v)); + } + }, this); + this.items = this.items.filter(function (v) { return v && v.id && v.number >= 0; }); + this.savedItems = core.getLocalStorage('_dynamicMapEditor_savedItems', []); +} + +// ------ bind actions + +dynamicMapEditor.prototype.isValid = function () { + return main.mode == 'play' && core.isPlaying() && !core.isReplaying() && !core.status.lockControl; +} + +dynamicMapEditor.prototype.onKeyUp = function(e) { + if (!this.isValid()) return false; + if (e.keyCode == 219) { + this.openToolBox(); + return true; + } + if (!this.isUsingTool) return false; + + if (e.keyCode == 220) { + this.undo(); + return true; + } else if (e.keyCode == 221) { + this.applyCurrentChange(); + return true; + } else if (e.keyCode >= 48 && e.keyCode <= 57) { + // 0-9 + if (e.altKey) { + this.savedItem(e.keyCode - 48); + } else { + this.loadItem(e.keyCode - 48); + } + return true; + } + return false; +} + +dynamicMapEditor.prototype.onMapClick = function(x, y) { + if (!this.isValid()) return false; + if (!this.isUsingTool || !this.selectedItem) return false; + x += parseInt(core.bigmap.offsetX / 32); + y += parseInt(core.bigmap.offsetY / 32); + var number = this.selectedItem.number; + this.addOperation('put', number, x, y, core.status.floorId); + return true; +} + +dynamicMapEditor.prototype.getClickLoc = function (e) { + return { + x: (e.clientX - core.dom.gameGroup.offsetLeft - 3) / core.domStyle.scale, + y: (e.clientY - core.dom.gameGroup.offsetTop - 3) / core.domStyle.scale, + }; +} + +dynamicMapEditor.prototype.onBoxClick = function (e) { + if (!this.isValid() || !this.isUsingTool) return false; + var loc = this.getClickLoc(e), x = loc.x, y = loc.y; + for(var i = 0; i < this.pageMaxItems; i++) { + var rect = this.itemRect(i); + if(x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h) { + this.onItemClick(i); + return; + } + } + if(y>=350 && y <= 370) { + if (x >= this.offsetX && x <= this.offsetX + 40) { + this.changePage(-1); + } else if (x >= this.offsetX + 40 && x <= this.offsetX + 80) { + this.showHelp(true); + } else if (x >= this.offsetX + 80 && x <= this.offsetX + 120) { + this.changePage(1); + } + } +} + +dynamicMapEditor.prototype.onItemClick = function(index) { + var startIndex = this.pageId * this.pageMaxItems; + var item = this.items[startIndex + index]; + if(!item) return; + if(index == this.selectedIndex) { + if (core.material.enemys[item.id]) { + var enemy = core.material.enemys[item.id]; + var nowData = [enemy.hp, enemy.atk, enemy.def, enemy.special].join(';'); + core.myprompt("请输入新怪物属性\n血;攻;防;能力,以分号分隔", nowData, function (result) { + if (result) { + try { + var finalData = result.split(';'); + if (finalData.length < 4) throw ""; + var hp = parseInt(finalData[0]) || 0; + var atk = parseInt(finalData[1]) || 0; + var def = parseInt(finalData[2]) || 0; + var special = finalData[3].replace(/[\[\]]/g, "") + .split(',').map(function (x) { return parseInt(x); }); + if (special.length == 0) special = 0; + else if (special.length == 1) special = special[0]; + dynamicMapEditor.addOperation('modify', item.id, hp, atk, def, special); + core.drawTip('已更新' + enemy.name + '的数据'); + return; + } catch (e) {} + } + core.drawTip('无效的输入数据'); + }); + return; + } + if (core.values[item.id] != null) { + var nowData = core.values[item.id]; + core.myprompt("请输入新" + (item.name || "") + "数值:", core.values[item.id], function (result) { + if (result) { + dynamicMapEditor.addOperation('value', item.id, parseInt(result) || 0, item.name || ""); + core.drawTip('已更新' + (item.name || "") + "的数值"); + return; + } + core.drawTip('无效的输入数据'); + }); + return; + } + } else { + this.selectedIndex = index; + this.selectedItem = item; + this.refreshToolBox(); + } +} + +// ------ methods + +dynamicMapEditor.prototype.openToolBox = function() { + if (!this.isUsingTool && core.domStyle.isVertical) { + core.drawTip("竖屏模式下暂不支持此功能。"); + return; + } + this.isUsingTool = !this.isUsingTool; + this.selectedItem = null; + this.selectedIndex = -1; + this.dom.style.display = this.isUsingTool ? 'block' : 'none'; + this.dom.style.width = core.dom.statusCanvas.style.width; + this.dom.width = core.dom.statusCanvas.width / core.domStyle.ratio; + this.dom.style.height = core.dom.statusCanvas.style.height; + this.dom.height = core.dom.statusCanvas.height / core.domStyle.ratio; + this.offsetX = this.dom.width / 2 - 60; + this.refreshToolBox(); + if (this.isUsingTool) this.showHelp(); +} + +dynamicMapEditor.prototype.addOperation = function() { + var operation = {}; + var type = arguments[0]; + operation.type = type; + if (type == 'put') { + operation.number = arguments[1]; + operation.x = arguments[2]; + operation.y = arguments[3]; + operation.floorId = arguments[4]; + operation.originNumber = core.floors[operation.floorId].map[operation.y][operation.x]; + core.floors[operation.floorId].map[operation.y][operation.x] = operation.number; + core.setBlock(operation.number, operation.x, operation.y, operation.floorId); + this.mapRecord[operation.floorId] = true; + } else if (type == 'modify') { + operation.enemyId = arguments[1]; + operation.hp = arguments[2]; + operation.atk = arguments[3]; + operation.def = arguments[4]; + operation.special = arguments[5]; + var enemy = core.material.enemys[operation.enemyId]; + operation.originHp = enemy.hp; + operation.originAtk = enemy.atk; + operation.originDef = enemy.def; + operation.originSpecial = enemy.special; + enemy.hp = operation.hp; + enemy.atk = operation.atk; + enemy.def = operation.def; + enemy.special = operation.special; + this.enemyModified = true; + } else if (type == 'value') { + operation.id = arguments[1]; + operation.value = arguments[2]; + operation.name = arguments[3]; + operation.originValue = core.values[operation.id]; + core.values[operation.id] = operation.value; + this.valueModified = true; + } + this.userChanged.push(operation); +} + +dynamicMapEditor.prototype.undo = function() { + var operation = this.userChanged.pop(); + if(!operation) { + core.drawTip('没有动作可以撤销'); + return; + } + var type = operation.type; + if(type == 'put') { // {originNumber, x, y, floorId} + var originNumber = operation.originNumber; + var x = operation.x; + var y = operation.y; + var floorId = operation.floorId; + core.floors[floorId].map[y][x] = originNumber; + core.setBlock(originNumber, x, y, floorId); + this.mapRecord[floorId] = true; + core.drawTip('已撤销' + floorId + '在(' + x + ',' + y + ')的图块操作'); + } else if (type == 'modify') { // {enemyId, originHp, originAtk, originDef, originSpecial} + var enemyId = operation.enemyId; + var hp = operation.originHp; + var atk = operation.originAtk; + var def = operation.originDef; + var special = operation.originSpecial; + var enemy = core.material.enemys[enemyId]; + enemy.hp = hp; + enemy.atk = atk; + enemy.def = def; + enemy.special = special; + core.drawTip('已撤销对' + enemy.name + '的属性修改'); + this.enemyModified = true; + } else if (type == 'value') { // {id, value, originValue} + var id = operation.id; + var value = operation.originValue; + core.values[operation.id] = operation.originValue; + core.drawTip('已撤销对' + operation.name + "数值的修改"); + this.valueModified = true; + } +} + +dynamicMapEditor.prototype.changePage = function(delta) { + var newId = this.pageId + delta; + if (newId < 0 || newId >= this.pageMax) return; + this.pageId = newId; + this.selectedItem = null; + this.selectedIndex = -1; + this.refreshToolBox(); +} + +dynamicMapEditor.prototype.savedItem = function (number) { + if (!this.isUsingTool || this.selectedItem < 0) return; + this.savedItems[number] = [this.pageId, this.selectedIndex]; + core.setLocalStorage('_dynamicMapEditor_savedItems', this.savedItems); + core.drawTip("已保存此图块"); +} + +dynamicMapEditor.prototype.loadItem = function (number) { + if (!this.isUsingTool) return; + var u = this.savedItems[number]; + if (!u) return core.drawTip("没有保存的图块!"); + this.pageId = u[0]; + this.selectedIndex = u[1]; + this.selectedItem = this.items[this.pageId * this.pageMaxItems + this.selectedIndex]; + this.refreshToolBox(); +} + +// ------ draw + +dynamicMapEditor.prototype.itemRect = function(index) { + return { + 'x' : this.offsetX + (index % 3) * 40, + 'y' : Math.floor(index / 3) * 50, + 'w' : 40, + 'h' : 50 + }; +} + +dynamicMapEditor.prototype.refreshToolBox = function() { + if (!this.isUsingTool) return; + core.fillRect(this.canvas, 0, 0, this.dom.width, this.dom.height, '#000000'); + var startIndex = this.pageId * this.pageMaxItems; + for (var i = 0; i < this.pageMaxItems; ++i) { + var item = this.items[startIndex + i]; + if (!item) break; + var rect = this.itemRect(i); + if (item.image) core.drawImage(this.canvas, item.image, 0, item.height * item.posY, 32, 32, rect.x + 4, rect.y, 32, 32); + if (item.name) { + this.canvas.textAlign = 'center'; + core.fillText(this.canvas, item.name, rect.x + 20, rect.y + 44, '#FFFFFF', '11px Verdana', 40); + } + if (core.material.enemys[item.id]) { + this.canvas.textAlign = 'left'; + var damageString = core.enemys.getDamageString(item.id); + core.fillBoldText(this.canvas, damageString.damage, rect.x + 5, rect.y + 31, damageString.color, null, '11px Verdana'); + var critical = core.enemys.nextCriticals(item.id, 1); + critical = core.formatBigNumber((critical[0]||[])[0], true); + if (critical == '???') critical = '?'; + core.fillBoldText(this.canvas, critical, rect.x+5, rect.y+21, '#FFFFFF'); + } + } + + this.canvas.textAlign = 'center'; + this.canvas.fillStyle = '#FFFFFF'; + if(this.pageId > 0) core.fillText(this.canvas, '上一页', this.offsetX + 20, 365, '#FFFFFF', '11px Verdana'); + if(this.pageId < this.pageMax-1) core.fillText(this.canvas, '下一页',this.offsetX + 100, 365, '#FFFFFF', '11px Verdana'); + core.fillText(this.canvas, '帮助', this.offsetX + 60, 365, '#FFFFFF'); + var text1 = core.formatBigNumber(core.getRealStatus('hp'), true) + "/" + + core.formatBigNumber(core.getRealStatus('atk'), true) + "/" + + core.formatBigNumber(core.getRealStatus("def"), true) + "/" + + core.formatBigNumber(core.getRealStatus("mdef"), true); + core.fillText(this.canvas, text1, this.offsetX + 60, 380, '#FF7F00', '11px Verdana', 120); + var text2 = core.formatBigNumber(core.getRealStatus('money', true)) + "/" + + core.formatBigNumber(core.getRealStatus('exp'), true) + "/" + + core.itemCount('yellowKey') + '/' + core.itemCount('blueKey') + '/' + + core.itemCount('redKey'); + core.fillText(this.canvas, text2, this.offsetX + 60, 395, '#FF7F00', '11px Verdana', 120); + var text3 = core.itemCount('pickaxe') + '/' + core.itemCount('bomb') + '/' + + core.itemCount('centerFly'); + if (core.hasFlag('poison')) text3 += "/毒"; + if (core.hasFlag('weak')) text3 += "/衰"; + if (core.hasFlag('curse')) text3 += "/咒"; + core.fillText(this.canvas, text3, this.offsetX + 60, 410, '#FF7F00', '11px Verdana', 120); + if(this.selectedItem) { + var rect = this.itemRect(this.selectedIndex); + core.strokeRect(this.canvas, rect.x, rect.y, rect.w, rect.h, '#FF7F00', 4); + } +} + +dynamicMapEditor.prototype.showInitHelp = function () { + if (main.mode != 'play' || core.getLocalStorage('_dynamicMapEditor_init')) return; + var text = "新拓展:运行时动态编辑地图!\n\n在此状态下你可以一边游戏一边编辑地图或者修改数据。\n\n"; + text += "进游戏后按 [ 键可以激活,快来尝试吧!\n\n"; + text += "点取消后将不再提示本页面。"; + core.myconfirm(text, null, function () { + core.setLocalStorage('_dynamicMapEditor_init', true); + if (core.firstData.name != 'template') { + localStorage.removeItem('template__dynamicMapEditor_init'); + localStorage.removeItem('template__dynamicMapEditor_help'); + } + }); +} + +dynamicMapEditor.prototype.showHelp = function (fromButton) { + if (main.mode != 'play' || (!fromButton && core.getLocalStorage('_dynamicMapEditor_help'))) return; + var text = "欢迎使用黄鸡编写的运行时编辑拓展!你可以一边游戏一边编辑地图或者修改数据。\n\n"; + text += "基本操作:\n - 点击图块再点地图可以放置;\n - 双击图块可以编辑数据;\n"; + text += " - [ 键将开关此模式;\n - ] 键将会把改动保存到文件;\n - \\ 键将撤销上步操作。\n"; + text += " - Alt+0~9 保存当前图块 \n - 0~9 读取当前图块\n"; + text += "最下面三行数据分别是:\n" + text += "血攻防护盾;金经黄蓝红;破炸飞和debuff。"; + if (!fromButton) text += "\n\n点取消将不再提示本页面。"; + core.myconfirm(text, null, function () { + if (!fromButton) core.setLocalStorage("_dynamicMapEditor_help", true); + }) +} + +// ------ save + +dynamicMapEditor.prototype.applyCurrentChange = function() { + this.saveEnemys(); + this.saveValues(); + this.saveFloors(); + this.enemyModified = false; + this.valueModified = false; + this.mapRecord = {}; + core.drawTip('已将所有改动应用到文件,记得刷新编辑器哦'); +} + +dynamicMapEditor.prototype.saveEnemys = function () { + if (!this.enemyModified) return; + core.enemys.enemys = core.clone(core.material.enemys); + var datastr = 'var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = \n'; + var emap = {}; + var estr = JSON.stringify(core.enemys.enemys, function (k, v) { + var t = core.clone(v); + if (t.hp != null) { + delete t.id; + var id_ = ":" + k + ":"; + emap[id_] = JSON.stringify(t); + return id_; + } else return t; + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + fs.writeFile('project/enemys.js', core.encodeBase64(datastr), 'base64', function (e, d) {}); +} + +dynamicMapEditor.prototype.saveValues = function () { + if (!this.valueModified) return; + core.data.values = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.values = core.clone(core.values); + var datastr = 'var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = \n' + + JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, null, '\t'); + fs.writeFile('project/data.js', core.encodeBase64(datastr), 'base64', function (e, d) {}); +} + +dynamicMapEditor.prototype.saveFloors = function () { + if (Object.keys(this.mapRecord).length == 0) return; + core.initStatus.maps = core.maps._initMaps(); + for (var floorId in this.mapRecord) { + if (this.mapRecord[floorId]) { + this.saveFloor(floorId); + } + } +} + +dynamicMapEditor.prototype.saveFloor = function (floorId) { + var floorData = core.floors[floorId]; + var filename = 'project/floors/' + floorId + '.js'; + var datastr = ['main.floors.', floorId, '=\n']; + + var tempJsonObj = core.clone(floorData); + var tempMap = [['map', ':map:'], ['bgmap', ':bgmap:'], ['fgmap', ':fgmap:']]; + tempMap.forEach(function (v) { + v[2] = tempJsonObj[v[0]]; + tempJsonObj[v[0]] = v[1]; + }); + var tempJson = JSON.stringify(tempJsonObj, null, 4); + tempMap.forEach(function (v) { + tempJson = tempJson.replace('"' + v[1] + '"', '[\n' + this.formatMap(v[2], v[0] != 'map') + '\n]') + }, this); + datastr = datastr.concat([tempJson]); + datastr = datastr.join(''); + fs.writeFile(filename, core.encodeBase64(datastr), 'base64', function (e, d) {}); +} + +dynamicMapEditor.prototype.formatMap = function (mapArr, trySimplify) { + if (!mapArr || JSON.stringify(mapArr) == JSON.stringify([])) return ''; + if (trySimplify) { + //检查是否是全0二维数组 + var jsoncheck = JSON.stringify(mapArr).replace(/\D/g, ''); + if (jsoncheck == Array(jsoncheck.length + 1).join('0')) return ''; + } + //把二维数组格式化 + var formatArrStr = ''; + var si = mapArr.length - 1, sk = mapArr[0].length - 1; + for (var i = 0; i <= si; i++) { + formatArrStr += ' ['; + for (var k = 0; k <= sk; k++) { + var num = parseInt(mapArr[i][k]); + formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk ? '' : ','); + } + formatArrStr += ']' + (i == si ? '' : ',\n'); + } + return formatArrStr; +} + +// ------ rewrite + +var dynamicMapEditor = new dynamicMapEditor(); + +dynamicMapEditor._resize_statusBar = core.control._resize_statusBar; +core.control._resize_statusBar = function (obj) { + dynamicMapEditor._resize_statusBar.call(this,obj); + dynamicMapEditor.refreshToolBox(); +} + +dynamicMapEditor.updateStatusBar = core.control.updateStatusBar; +core.control.updateStatusBar = function () { + dynamicMapEditor.refreshToolBox(); + dynamicMapEditor.updateStatusBar.apply(core.control, Array.prototype.slice.call(arguments)); +} \ No newline at end of file diff --git a/editor.js b/editor.js new file mode 100644 index 0000000..88a22f7 --- /dev/null +++ b/editor.js @@ -0,0 +1,1054 @@ +function editor() { + this.version = "2.0"; + this.brushMod = "line";//["line","rectangle","tileset"] + this.layerMod = "map";//["fgmap","map","bgmap"] + this.isMobile = false; + + this.dom={ + body:document.body, + eui:document.getElementById('eui'), + efg:document.getElementById('efg'), + ebm:document.getElementById('ebm'), + euiCtx:document.getElementById('eui').getContext('2d'), + efgCtx:document.getElementById('efg').getContext('2d'), + ebmCtx:document.getElementById('ebm').getContext('2d'), + mid:document.getElementById('mid'), + mapEdit:document.getElementById('mapEdit'), + selectFloor:document.getElementById('selectFloor'), + iconExpandBtn :document.getElementById('iconExpandBtn'), + dataSelection : document.getElementById('dataSelection'), + iconLib:document.getElementById('iconLib'), + midMenu:document.getElementById('midMenu'), + extraEvent: document.getElementById('extraEvent'), + chooseThis : document.getElementById('chooseThis'), + chooseInRight : document.getElementById('chooseInRight'), + copyLoc : document.getElementById('copyLoc'), + pasteLoc : document.getElementById('pasteLoc'), + clearEvent : document.getElementById('clearEvent'), + clearLoc : document.getElementById('clearLoc'), + brushMod:document.getElementById('brushMod'), + brushMod2:document.getElementById('brushMod2'), + brushMod3:document.getElementById('brushMod3'), + brushMod4:document.getElementById('brushMod4'), + layerMod:document.getElementById('layerMod'), + layerMod2:document.getElementById('layerMod2'), + layerMod3:document.getElementById('layerMod3'), + viewportButtons:document.getElementById('viewportButtons'), + appendPicCanvas : document.getElementById('appendPicCanvas'), + appendBgCtx : document.getElementById('appendPicCanvas').children[0].getContext('2d'), + appendSource : document.getElementById('appendPicCanvas').children[1], + appendPicClick : document.getElementById('appendPicCanvas').children[2], + appendSprite : document.getElementById('appendPicCanvas').children[3], + appendSourceCtx:document.getElementById('appendPicCanvas').children[1].getContext('2d'), + appendSpriteCtx:document.getElementById('appendPicCanvas').children[3].getContext('2d'), + appendPicSelection : document.getElementById('appendPicSelection'), + selectAppend : document.getElementById('selectAppend'), + selectFileBtn :document.getElementById('selectFileBtn'), + changeFloorId :document.getElementById('changeFloorId'), + changeFloorSize: document.getElementById('changeFloorSize'), + left1 : document.getElementById('left1'), + editModeSelect :document.getElementById('editModeSelect'), + mid2 : document.getElementById('mid2'), + clearLastUsedBtn: document.getElementById('clearLastUsedBtn'), + lastUsedTitle: document.getElementById('lastUsedTitle'), + lastUsedDiv: document.getElementById('lastUsedDiv'), + lastUsed: document.getElementById('lastUsed'), + lastUsedCtx: document.getElementById('lastUsed').getContext('2d'), + showMovable: document.getElementById('showMovable'), + gameInject: document.getElementById('gameInject'), + undoFloor: document.getElementById('undoFloor'), + selectFloorBtn: document.getElementById('selectFloorBtn'), + editorTheme: document.getElementById('editorTheme'), + bigmapBtn : document.getElementById('bigmapBtn'), + mapRowMark: document.getElementById('mapRowMark'), + mapColMark: document.getElementById('mapColMark'), + maps: ['bgmap', 'fgmap', 'map'], + canvas: ['bg', 'fg'], + }; + + this.uivalues={ + // 绘制区拖动有关 + holdingPath : 0, + stepPostfix : null,//用于存放寻路检测的第一个点之后的后续移动 + mouseOutCheck : 2, + startPos:null, + endPos:null, + lastMoveE:{buttons:0,clientX:0,clientY:0}, + selectedArea: null, + // 材料区拖动有关 + lastMoveMaterE:null, + tileSize: [1,1], + startLoc: null, + // 撤销/恢复 + preMapData : [], + preMapMax: 10, + postMapData: [], + // + shortcut:{}, + copyedInfo : null, + // 折叠素材 + scrollBarHeight :0, + folded:false, + foldPerCol: 50, + // + ratio : 1, + // 是否是大地图模式 + bigmap : false, + bigmapInfo: { + top: 0, + left: 0, + size: 32, + }, + // blockly转义 + disableBlocklyReplace: false, + // blockly展开比较 + disableBlocklyExpandCompare: false, + + // 绑定机关门事件相关 + bindSpecialDoor: { + loc: null, + n: -1, + enemys: [] + }, + + // 复制怪物或道具属性 + copyEnemyItem : { + type: null, + data: {} + }, + + // tile + lockMode: false, + + showMovable: false, + + // 最近使用的图块 + lastUsedType: null, + lastUsed: [], + + // 最近访问的楼层 + recentFloors: [] + }; + + window.onerror = function (msg, url, lineNo, columnNo, error) { + var string = msg.toLowerCase(); + var substring = "script error"; + var message; + if (string.indexOf(substring) > -1){ + message = 'Script Error: See Browser Console for Detail'; + } else { + if (url) url = url.substring(url.lastIndexOf('/')+1); + message = [ + 'Message: ' + msg, + 'URL: ' + url, + 'Line: ' + lineNo, + 'Column: ' + columnNo, + 'Error object: ' + JSON.stringify(error) + ].join(' - '); + // alert(message); + } + try { + printe(message) + } catch (e) { + alert(message); + } + return false; + }; +} + +editor.prototype.uifunctions={}; + +/* +editor.loc +editor.pos +editor.info +始终是最后一次点击的结果 +注意editor.info可能因为点击其他地方而被清空 +*/ + +/////////// 数据相关 /////////// + +editor.prototype.init = function (callback) { + + editor.airwallImg = new Image(); + editor.airwallImg.src = './project/materials/airwall.png'; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'index.html', true); + xhr.onload = function () { + if (xhr.status != 200) { + alert("HTTP " + xhr.status); + return; + } + var str = xhr.response.split(''); + if (str.length != 3) window.onerror("index.html格式不正确"); + editor.dom.gameInject.innerHTML = str[1]; + + var cvs = ['bg', 'event', 'event2', 'fg'].map(function(e) { + return document.getElementById(e); + }); + ['bg', 'ev', 'ev2', 'fg'].forEach(function(e, i) { + editor.dom[e+'c'] = cvs[i]; + editor.dom[e+'Ctx'] = cvs[i].getContext('2d'); + + editor.dom.mapEdit.insertBefore(cvs[i], editor.dom.ebm); + }); + + var mainScript = document.createElement('script'); + + mainScript.onload = function() { + + var useCompress = main.useCompress; + main.useCompress = false; + + main.init('editor', function () { + editor_util_wrapper(editor); + editor_game_wrapper(editor, main, core); + editor_file_wrapper(editor); + editor_table_wrapper(editor); + editor_ui_wrapper(editor); + editor_uievent_wrapper(editor); + editor_mappanel_wrapper(editor); + editor_datapanel_wrapper(editor); + editor_materialpanel_wrapper(editor); + editor_listen_wrapper(editor); + editor.printe=printe; + afterMainInit(); + }); + + var afterMainInit = function () { + editor.game.fixFunctionInGameData(); + editor.main = main; + editor.core = core; + editor.fs = fs; + editor_file = editor_file(editor, function () { + editor.file = editor_file; + editor_mode = editor_mode(editor); + editor.mode = editor_mode; + var canvases = document.getElementsByClassName('gameCanvas'); + for (var one in canvases) { + canvases[one].width = canvases[one].height = core.__PIXELS__; + } + core.resetGame(core.firstData.hero, null, core.firstData.floorId, core.cloneArray(core.initStatus.maps)); + var floorId = editor.config.get('editorLastFloorId', core.status.floorId); + if (core.floorIds.indexOf(floorId) < 0) floorId = core.status.floorId; + + core.status.floorId = floorId; + core.resizeMap(floorId); + core.clearMap('all'); + core.generateGroundPattern(floorId); + core.extractBlocks(floorId); + core.status.thisMap = core.status.maps[floorId]; + afterCoreReset(); + }); + } + + var afterCoreReset = function () { + + editor.game.idsInit(core.maps, core.icons.icons); // 初始化图片素材信息 + editor.drawInitData(core.icons.icons); // 初始化绘图 + + editor.game.fetchMapFromCore(); + editor.pos = {x: 0, y: 0}; + editor.updateMap(); + editor.buildMark(); + var viewportLoc = editor.config.get('viewportLoc', []); + editor.setViewport(viewportLoc[0] || 0, viewportLoc[1] || 0); + editor.drawEventBlock(); + + editor.mode.loc(); + editor.info = editor.ids[editor.indexs[201]]; + editor.mode.enemyitem(); + editor.mode.floor(); + editor.mode.tower(); + editor.mode.functions(); + editor.mode.commonevent(); + editor.mode.showMode('tower'); + + editor_multi = editor_multi(); + editor_blockly = editor_blockly(); + + // --- 所有用到的flags + editor.used_flags = {}; + editor.addUsedFlags(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d)); + // 楼层属性 + for (var floorId in editor.main.floors) { + editor.addUsedFlags(JSON.stringify(editor.main.floors[floorId])); + } + // 公共事件 + for (var name in events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent) { + editor.addUsedFlags(JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent[name])); + } + // 道具效果 + for (var id in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + editor.addUsedFlags(JSON.stringify(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id])); + } + // 全局商店 + editor.addUsedFlags(JSON.stringify(editor.main.core.firstData.shops)); + // 怪物战前战后 + for (var id in enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80) { + editor.addUsedFlags(JSON.stringify(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id])); + } + // 图块属性 + for (var id in maps_90f36752_8815_4be8_b32b_d7fad1d0542e) { + editor.addUsedFlags(JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e[id])); + } + + if (editor.useCompress == null) editor.useCompress = useCompress; + if (Boolean(callback)) callback(); + + } + } + + mainScript.id = "mainScript"; + mainScript.src = "main.js"; + editor.dom.gameInject.appendChild(mainScript); + }; + xhr.onabort = xhr.ontimeout = xhr.onerror = function () { + alert("无法访问index.html"); + } + + editor.config = new editor_config(); + editor.config.load(function() { + var theme = editor.config.get('theme', 'editor_color'); + document.getElementById('color_css').href = '_server/css/' + theme + '.css'; + editor.dom.editorTheme.value = theme; + xhr.send(); + }); +} + +editor.prototype.mapInit = function () { + var ec = editor.dom.evCtx; + ec.clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); + editor.dom.ev2Ctx.clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); + editor.map = []; + var sy=editor.currentFloorData.map.length,sx=editor.currentFloorData.map[0].length; + for (var y = 0; y < sy; y++) { + editor.map[y] = []; + for (var x = 0; x < sx; x++) { + editor.map[y][x] = 0; + } + } + editor.dom.maps.forEach(function (one) { + editor.currentFloorData[one] = editor[one] = JSON.parse(JSON.stringify(editor.map)); + }); + editor.currentFloorData.firstArrive = []; + editor.currentFloorData.eachArrive = []; + editor.currentFloorData.events = {}; + editor.currentFloorData.autoEvent = {}; + editor.currentFloorData.changeFloor = {}; + editor.currentFloorData.beforeBattle = {}; + editor.currentFloorData.afterBattle = {}; + editor.currentFloorData.afterGetItem = {}; + editor.currentFloorData.afterOpenDoor = {}; + editor.currentFloorData.cannotMove = {}; +} + +editor.prototype.changeFloor = function (floorId, callback) { + for(var ii=0,name;name=editor.dom.maps[ii];ii++){ + var mapArray=editor[name].map(function (v) { + return v.map(function (v) { + return v.idnum || v || 0 + }) + }); + editor.currentFloorData[name]=mapArray; + } + editor.uivalues.preMapData = []; + editor.uivalues.postMapData = []; + editor.uifunctions._extraEvent_bindSpecialDoor_doAction(true); + + core.status.floorId = floorId; + core.resizeMap(floorId); + core.clearMap('all'); + core.generateGroundPattern(floorId); + core.extractBlocks(floorId); + core.status.thisMap = core.status.maps[floorId]; + + editor.game.fetchMapFromCore(); + editor.updateMap(); + editor_mode.floor(); + editor.drawEventBlock(); + + editor.viewportLoc = editor.viewportLoc || {}; + var loc = editor.viewportLoc[floorId] || [], x = loc[0] || 0, y = loc[1] || 0; + editor.setViewport(x, y); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.config.set('editorLastFloorId', floorId); + if (callback) callback(); +} + +/////////// 游戏绘图相关 /////////// + +editor.prototype.drawEventBlock = function () { + var fg=editor.dom.efgCtx; + + fg.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + if (editor.uivalues.bigmap) return this._drawEventBlock_bigmap(); + + var firstData = editor.game.getFirstData(); + // 不可通行性 + var movableArray = {}; + if (editor.uivalues.showMovable) { + movableArray = core.generateMovableArray() || {}; + fg.fillStyle = "rgba(0,0,0,0.4)"; + fg.fillRect(0, 0, core.__PIXELS__, core.__PIXELS__); + for (var i=0;i= 0) { + fg.textAlign = 'right'; + editor.game.doCoreFunc("fillBoldText", fg, index + 1, + 32 * i + 28, 32 * j + 15, '#FF7F00', null, '14px Verdana'); + } + var offset = 0; + if (editor.currentFloorData.upFloor && editor.currentFloorData.upFloor.toString() == loc) { + fg.textAlign = 'left'; + editor.game.doCoreFunc("fillText", fg, "🔼", 32 * i + offset, 32 * j + 8, null, "8px Verdana"); + offset += 8; + } + if (editor.currentFloorData.downFloor && editor.currentFloorData.downFloor.toString() == loc) { + fg.textAlign = 'left'; + editor.game.doCoreFunc("fillText", fg, "🔽", 32 * i + offset, 32 * j + 8, null, "8px Verdana"); + offset += 8; + } + if (editor.currentFloorData.flyPoint && editor.currentFloorData.flyPoint.toString() == loc) { + fg.textAlign = 'left'; + editor.game.doCoreFunc("fillText", fg, "🔃", 32 * i + offset, 32 * j + 8, null, "8px Verdana"); + offset += 8; + } + } + } +} + +editor.prototype._drawEventBlock_bigmap = function () { + var fg=editor.dom.efgCtx; + var info = editor.uivalues.bigmapInfo, size = info.size, psize = size / 4; + + // 不可通行性 + var movableArray = {}; + if (editor.uivalues.showMovable) { + movableArray = core.generateMovableArray() || {}; + fg.fillStyle = "rgba(0,0,0,0.4)"; + fg.fillRect(0, 0, core.__PIXELS__, core.__PIXELS__); + for (var i = 0; i < editor.currentFloorData.width; ++i) { + for (var j = 0; j < editor.currentFloorData.height; ++j) { + var directions = (movableArray[i]||{})[j]; + if (directions == null) continue; + if (!directions.includes('left') && i != 0) { + var ndirections = (movableArray[i-1]||{})[j]; + if (ndirections != null && !ndirections.includes('right')) { + core.drawLine(fg, info.left + size * i, info.top + size * j + size / 4, info.left + size * i, info.top + size * j + size * 3 / 4, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i, info.top + size * j + size / 3, info.left + size * i, info.top + size * j + size * 2 / 3, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size / 4, info.top + size * j + size * 3 / 8], + [info.left + size * i, info.top + size * j + size / 2], + [info.left + size * i + size / 4, info.top + size * j + size * 5 / 8]], '#FF0000'); + } + } + if (!directions.includes('right') && i != editor.currentFloorData.width - 1) { + var ndirections = (movableArray[i+1]||{})[j]; + if (ndirections != null && !ndirections.includes('left')) { + core.drawLine(fg, info.left + size * i + size, info.top + size * j + size / 4, info.left + size * i + size, info.top + size * j + size * 3 / 4, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i + size, info.top + size * j + size / 3, info.left + size * i + size, info.top + size * j + size * 2 / 3, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size * 3 / 4, info.top + size * j + size * 3 / 8], + [info.left + size * i + size, info.top + size * j + size / 2], + [info.left + size * i + size * 3 / 4, info.top + size * j + size * 5 / 8]], '#FF0000'); + } + } + if (!directions.includes('up') && j != 0) { + var ndirections = movableArray[i][j-1]; + if (ndirections != null && !ndirections.includes('down')) { + core.drawLine(fg, info.left + size * i + size / 4, info.top + size * j, info.left + size * i + size * 3 / 4, info.top + size * j, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i + size / 3, info.top + size * j, info.left + size * i + size * 2 / 3, info.top + size * j, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size * 3 / 8, info.top + size * j + size / 4], + [info.left + size * i + size / 2, info.top + size * j], + [info.left + size * i + size * 5 / 8, info.top + size * j + size / 4]], '#FF0000'); + } + } + if (!directions.includes('down') && j != editor.currentFloorData.height - 1) { + var ndirections = movableArray[i][j+1]; + if (ndirections != null && !ndirections.includes('up')) { + core.drawLine(fg, info.left + size * i + size / 4, info.top + size * j + size, info.left + size * i + size * 3 / 4, info.top + size * j + size, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i + size / 3, info.top + size * j + size, info.left + size * i + size * 2 / 3, info.top + size * j + size, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size * 3 / 8, info.top + size * j + size * 3 / 4], + [info.left + size * i + size / 2, info.top + size * j + size], + [info.left + size * i + size * 5 / 8, info.top + size * j + size * 3 / 4]], '#FF0000'); + } + } + } + } + return; + } + + for (var i = 0; i < editor.currentFloorData.width; ++i) { + for (var j = 0; j < editor.currentFloorData.height; ++j) { + var color = this._drawEventBlock_getColor(i+","+j); + for(var kk=0,cc;cc=color[kk];kk++){ + fg.fillStyle = cc; + fg.fillRect(info.left + size * i + psize * kk, info.top + size * (j + 1) - psize, psize, psize); + } + } + } +} + +editor.prototype._drawEventBlock_getColor = function (loc) { + var color = []; + if (editor.currentFloorData.events[loc]) + color.push('#FF0000'); + if (editor.currentFloorData.autoEvent[loc]) { + var x = editor.currentFloorData.autoEvent[loc]; + for (var index in x) { + if (x[index] && x[index].data) { + color.push('#FFA500'); + break; + } + } + } + if (editor.currentFloorData.beforeBattle[loc]) + color.push('#0000FF'); + if (editor.currentFloorData.afterBattle[loc]) + color.push('#FFFF00'); + if (editor.currentFloorData.changeFloor[loc]) + color.push('#00FF00'); + if (editor.currentFloorData.afterGetItem[loc]) + color.push('#00FFFF'); + if (editor.currentFloorData.afterOpenDoor[loc]) + color.push('#FF00FF'); + return color; +} + +editor.prototype.drawPosSelection = function () { + this.drawEventBlock(); + var fg=editor.dom.efgCtx; + fg.strokeStyle = 'rgba(255,255,255,0.7)'; + if (editor.uivalues.bigmap) { + var info = editor.uivalues.bigmapInfo, size = info.size, psize = size / 8; + fg.lineWidth = psize; + fg.strokeRect(info.left + editor.pos.x * size + psize, info.top + editor.pos.y * size + psize, size - 2*psize, size - 2*psize); + } else { + fg.lineWidth = 4; + fg.strokeRect(32*editor.pos.x - core.bigmap.offsetX + 4, 32*editor.pos.y - core.bigmap.offsetY + 4, 24, 24); + } +} + +editor.prototype._updateMap_bigmap = function () { + var bm=editor.dom.ebmCtx; + bm.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + bm.fillStyle = '#000000'; + bm.fillRect(0, 0, core.__PIXELS__, core.__PIXELS__); + core.drawThumbnail(editor.currentFloorId, null, {ctx: bm, all: true}); + var width = editor.currentFloorData.width; + var height = editor.currentFloorData.height; + editor.uivalues.bigmapInfo.top = core.__PIXELS__ * Math.max(0, (1 - height / width) / 2); + editor.uivalues.bigmapInfo.left = core.__PIXELS__ * Math.max(0, (1 - width / height) / 2); + editor.uivalues.bigmapInfo.size = core.__PIXELS__ / Math.max(width, height); + this.drawEventBlock(); + this.updateLastUsedMap(); +} + +editor.prototype.updateMap = function () { + var blocks = core.maps._mapIntoBlocks(editor.map.map(function (v) { + return v.map(function (v) { + try { + return v.idnum || v || 0 + } + catch (e) { + console.log("Unable to read idnum from "+v); + return 0; + } + }); + }), null, editor.currentFloorId); + core.status.thisMap.blocks = blocks; + if (editor.uivalues.bigmap) return this._updateMap_bigmap(); + + var updateMap = function () { + core.removeGlobalAnimate(); + editor.dom.canvas.forEach(function (one) { + core.clearMap(one); + }); + core.clearMap('event'); + core.clearMap('event2'); + core.maps._drawMap_drawAll(); + } + updateMap(); + + var drawTile = function (ctx, x, y, tileInfo) { // 绘制一个普通块 + + //ctx.clearRect(x*32, y*32, 32, 32); + if (tileInfo == 0) return; + + if (typeof(tileInfo) == typeof([][0]) || !Object.prototype.hasOwnProperty.call(tileInfo, 'idnum')) {//未定义块画红块 + if (typeof(tileInfo) != typeof([][0]) && Object.prototype.hasOwnProperty.call(tileInfo, 'images')) { + ctx.drawImage(core.material.images[tileInfo.images], 0, tileInfo.y * 32, 32, 32, x * 32, y * 32, 32, 32); + } + ctx.strokeStyle = 'red'; + var OFFSET = 2; + ctx.lineWidth = OFFSET; + ctx.strokeRect(x * 32 + OFFSET, y * 32 + OFFSET, 32 - OFFSET * 2, 32 - OFFSET * 2); + ctx.font = "30px Verdana"; + ctx.textAlign = 'center'; + ctx.fillStyle = 'red'; + ctx.fillText("?", x * 32 + 16, y * 32 + 27); + return; + } + //ctx.drawImage(core.material.images[tileInfo.images], 0, tileInfo.y*32, 32, 32, x*32, y*32, 32, 32); + } + if (editor.map.length * editor.map[0].length < 4096) { + for (var y = 0; y < editor.map.length; y++) { + for (var x = 0; x < editor.map[0].length; x++) { + drawTile(editor.dom.evCtx, x, y, editor.map[y][x]); + editor.dom.canvas.forEach(function (one) { + drawTile(editor.dom[one + 'Ctx'], x, y, editor[one+'map'][y][x]); + }); + } + } + } + + // 绘制地图 end + + editor.drawEventBlock(); + this.updateLastUsedMap(); +} + +editor.prototype.setLastUsedType = function (type) { + if (type == editor.uivalues.lastUsedType) return; + editor.uivalues.lastUsedType = type; + var _buildHtml = function (type, text) { + if (type == null) return "" + text + ""; + else return `${text}`; + } + editor.dom.lastUsedTitle.innerHTML + = type == 'frequent' ? (_buildHtml('recent', '最近使用') + " | " + _buildHtml(null, '最常使用')) + : (_buildHtml(null, '最近使用') + " | " + _buildHtml('frequent', '最常使用')); + this.updateLastUsedMap(); + editor.dom.lastUsedDiv.scrollTop = 0; +} + +editor.prototype.updateLastUsedMap = function () { + var lastUsed = editor.uivalues.lastUsed.sort(function (a, b) { + if ((a.istop || 0) != (b.istop || 0)) return (b.istop || 0) - (a.istop || 0); + return (b[editor.uivalues.lastUsedType] || 0) - (a[editor.uivalues.lastUsedType] || 0); + }); + + // 绘制最近使用事件 + var ctx = editor.dom.lastUsedCtx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.strokeStyle = 'rgba(255,128,0,0.85)'; + ctx.fillStyle = 'rgba(255,0,0,0.85)'; + ctx.lineWidth = 4; + for (var i = 0; i < lastUsed.length; ++i) { + try { + var per_row = core.__SIZE__ - 1; + var x = i % per_row, y = parseInt(i / per_row); + var info = lastUsed[i]; + if (!info || !info.images) continue; + if (info.isTile && core.material.images.tilesets[info.images]) { + ctx.drawImage(core.material.images.tilesets[info.images], 32 * info.x, 32 * info.y, 32, 32, x*32, y*32, 32, 32); + } else if (info.images == 'autotile' && core.material.images.autotile[info.id]) { + ctx.drawImage(core.material.images.autotile[info.id], 0, 0, 32, 32, x * 32, y * 32, 32, 32); + } else { + var per_height = info.images.endsWith('48') ? 48 : 32; + ctx.drawImage(core.material.images[info.images], 0, info.y * per_height, 32, per_height, x * 32, y * 32, 32, 32); + } + if (info.istop) { + ctx.fillRect(32 * x, 32 * y + 24, 8, 8); + } + if (selectBox.isSelected() && editor.info.id == info.id) { + ctx.strokeRect(32 * x + 2, 32 * y + 2, 28, 28); + } + } catch (e) {} + } +} + +editor.prototype.setViewport=function (x, y) { + core.bigmap.offsetX = core.clamp(x, 0, 32*core.bigmap.width-core.__PIXELS__); + core.bigmap.offsetY = core.clamp(y, 0, 32*core.bigmap.height-core.__PIXELS__); + editor.viewportLoc = editor.viewportLoc || {}; + editor.viewportLoc[editor.currentFloorId] = [core.bigmap.offsetX, core.bigmap.offsetY]; + core.control.updateViewport(); + editor.config.set('viewportLoc', editor.viewportLoc[editor.currentFloorId]); + editor.buildMark(); + editor.drawPosSelection(); +} + +editor.prototype.moveViewport=function(x,y){ + editor.setViewport(core.bigmap.offsetX+32*x, core.bigmap.offsetY+32*y); + printi("你可以按【大地图】(或F键)快捷切换大地图模式"); +} + +/////////// 界面交互相关 /////////// + +editor.prototype.drawInitData = function (icons) { + var ratio = 1; + var images = core.material.images; + var maxHeight = 700; + var sumWidth = 0; + editor.widthsX = {}; + editor.uivalues.folded = editor.config.get('folded', false); + // editor.uivalues.folded = true; + editor.uivalues.foldPerCol = editor.config.get('foldPerCol', 50); + // var imgNames = Object.keys(images); //还是固定顺序吧; + editor.setLastUsedType(editor.config.get('lastUsedType', 'recent')); + var ids = editor.ids.map(function (x) {return x.id || "";}); + editor.uivalues.lastUsed = editor.config.get("lastUsed", []).filter(function (one) { + return ids.indexOf(one.id) >= 0; + }); + var imgNames = ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"]; + + var splitCanvas = document.createElement('canvas'); + var splitCtx = splitCanvas.getContext('2d'); + splitCtx.imageSmoothingEnabled = false; + + var splitImage = function (image, width, height) { + if (image.width == width && image.height == height) { + return [image]; + } + var ans = []; + for (var j = 0; j < image.height; j += h) { + var h = Math.min(height, image.height - j); + splitCanvas.width = width; + splitCanvas.height = h; + core.drawImage(splitCtx, image, 0, j, width, h, 0, 0, width, h); + var data = new Image(); + data.src = splitCanvas.toDataURL("image/png"); + ans.push(data); + } + return ans; + } + + for (var ii = 0; ii < imgNames.length; ii++) { + var img = imgNames[ii], tempy = 0; + if (img == 'autotile') { + var autotiles = images[img]; + for (var im in autotiles) { + tempy += editor.uivalues.folded ? 32 : autotiles[im].height; + } + var tempx = editor.uivalues.folded ? 32 : 3 * 32; + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + tempx) / 32, tempy]; + sumWidth += tempx; + maxHeight = Math.max(maxHeight, tempy); + continue; + } + var width = images[img].width, height = images[img].height, mh = height; + if (editor.uivalues.folded) { + if (img == 'terrains') { + width = Math.ceil((height / 32 + 2) / editor.uivalues.foldPerCol) * 32; + if (width > 32) mh = 32 * editor.uivalues.foldPerCol; + } else { + var per_height = (img == 'enemy48' || img == 'npc48' ? 48 : 32); + width = Math.ceil(height / per_height / editor.uivalues.foldPerCol) * 32; + if (width > 32) mh = per_height * editor.uivalues.foldPerCol; + } + } + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + width) / 32, height]; + sumWidth += width; + maxHeight = Math.max(maxHeight, mh + 64); + } + var tilesets = images.tilesets; + for (var ii in core.tilesets) { + var img = core.tilesets[ii]; + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + tilesets[img].width) / 32, tilesets[img].height]; + sumWidth += tilesets[img].width; + maxHeight = Math.max(maxHeight, tilesets[img].height); + } + + var fullWidth = ~~(sumWidth * ratio); + var fullHeight = ~~(maxHeight * ratio); + + /* + if (fullWidth > edata.width) edata.style.width = (edata.width = fullWidth) / ratio + 'px'; + edata.style.height = (edata.height = fullHeight) / ratio + 'px'; + */ + var iconImages = document.getElementById('iconImages'); + iconImages.style.width = (iconImages.width = fullWidth) / ratio + 'px'; + iconImages.style.height = (iconImages.height = fullHeight) / ratio + 'px'; + document.body.ondrop = function (e) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return false; + } + document.body.ondragover = function (e) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return false; + } + var drawImage = function (image, x, y, cls) { + image.style.left = x + 'px'; + image.style.top = y + 'px'; + image.ondrop = (function (cls) { return function (e) { + e.stopPropagation(); + e.preventDefault(); + if (!cls) return false; + var files = e.dataTransfer.files; + if (files.length >= 1) { + var file = files[0]; + if (file.type == 'image/png') { + editor.uifunctions.dragImageToAppend(file, cls); + } else { + printe('只支持png图片的快速追加!'); + } + } + return false; + }; })(cls); + image.ondragover = function(e) { + e.stopPropagation(); + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + return false; + } + iconImages.appendChild(image); + } + + var nowx = 0, nowy = 0; + for (var ii = 0; ii < imgNames.length; ii++) { + var img = imgNames[ii]; + if (img == 'terrains') { + (function(image,nowx){ + if (image.complete) { + drawImage(image, nowx, 32); + core.material.images.airwall = image; + delete(editor.airwallImg); + } else image.onload = function () { + drawImage(image, nowx, 32); + core.material.images.airwall = image; + delete(editor.airwallImg); + editor.updateMap(); + } + })(editor.airwallImg,nowx); + if (editor.uivalues.folded) { + // --- 单列 & 折行 + var canvas = document.createElement("canvas"); + canvas.width = 32; + canvas.height = images[img].height + 64; + canvas.getContext('2d').drawImage(images[img], 0, 64); + var subimgs = splitImage(canvas, 32, editor.uivalues.foldPerCol * 32); + for (var i = 0; i < subimgs.length; i++) { + drawImage(subimgs[i], nowx, 0, img); + nowx += 32; + } + } + else { + drawImage(images[img], nowx, 32*2, img); + nowx += images[img].width; + } + continue; + } + if (img == 'autotile') { + var autotiles = images[img]; + var tempx = editor.uivalues.folded ? 32 : 96; + for (var im in autotiles) { + var tempy = editor.uivalues.folded ? 32 : autotiles[im].height; + var subimgs = splitImage(autotiles[im], tempx, tempy); + drawImage(subimgs[0], nowx, nowy, img); + nowy += tempy; + } + nowx += tempx; + continue; + } + if (editor.uivalues.folded) { + // --- 单列 & 折行 + var per_height = img.endsWith('48') ? 48 : 32; + var subimgs = splitImage(images[img], 32, editor.uivalues.foldPerCol * per_height); + for (var i = 0; i < subimgs.length; i++) { + drawImage(subimgs[i], nowx, 0, img); + nowx += 32; + } + } + else { + drawImage(images[img], nowx, 0, img); + nowx += images[img].width; + } + } + for (var ii in core.tilesets) { + var img = core.tilesets[ii]; + drawImage(tilesets[img], nowx, 0); + nowx += tilesets[img].width; + } + //editor.mapInit(); +} + +editor.prototype.buildMark = function(){ + // 生成定位编号 + var arrColMark=document.getElementById('arrColMark'); + var arrRowMark=document.getElementById('arrRowMark'); + var mapColMark=document.getElementById('mapColMark'); + var mapRowMark=document.getElementById('mapRowMark'); + var buildMark = function (offsetX,offsetY) { + var colNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (i+offsetX) + '
      '; + colNum += tpl; + } + arrColMark.innerHTML = '' + colNum + ''; + mapColMark.innerHTML = '' + colNum + ''; + var rowNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (i+offsetY) + '
      '; + rowNum += tpl; + } + arrRowMark.innerHTML = rowNum; + mapRowMark.innerHTML = rowNum; + } + var buildMark_mobile = function (offsetX,offsetY) { + var colNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
      '; + colNum += tpl; + } + arrColMark.innerHTML = '' + colNum + ''; + //mapColMark.innerHTML = '' + colNum + ''; + var rowNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
      '; + rowNum += tpl; + } + arrRowMark.innerHTML = rowNum; + //mapRowMark.innerHTML = rowNum; + //===== + var colNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '
      ' + (' '+(i+offsetX)).slice(-2).replace(' ',' ') + '
      '; + colNum += tpl; + } + mapColMark.innerHTML = '
      ' + colNum + '
      '; + var rowNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '
      ' + (' '+(i+offsetY)).slice(-2).replace(' ',' ') + '
      '; + rowNum += tpl; + } + mapRowMark.innerHTML = rowNum; + } + if(editor.isMobile){ + buildMark_mobile(core.bigmap.offsetX/32,core.bigmap.offsetY/32); + } else { + buildMark(core.bigmap.offsetX/32,core.bigmap.offsetY/32); + } +} + +editor.prototype.setSelectBoxFromInfo=function(thisevent, scrollTo){ + var pos={x: 0, y: 0, images: "terrains"}; + var ysize = 32; + if(thisevent==0){ + } else if (thisevent.idnum==17){ + pos.y=1; + } else { + pos.x=editor.widthsX[thisevent.images][1]; + pos.y=thisevent.y; + if(thisevent.x)pos.x+=thisevent.x; + ysize = thisevent.images.endsWith('48') ? 48 : 32; + if (editor.uivalues.folded && core.tilesets.indexOf(thisevent.images)==-1) { + pos.x += Math.floor(pos.y / editor.uivalues.foldPerCol); + pos.y %= editor.uivalues.foldPerCol; + } + if(pos.x == 0) pos.y+=2; + } + if (!editor.isMobile && scrollTo) { + editor.dom.iconLib.scrollLeft = pos.x * 32 - editor.dom.iconLib.offsetWidth / 2; + editor.dom.iconLib.scrollTop = pos.y * ysize - editor.dom.iconLib.offsetHeight / 2; + } + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize - 6 + 'px'; + editor.dom.dataSelection.style.width = 32 - 6 + 'px'; + setTimeout(function(){ + selectBox.isSelected(true); + editor.updateLastUsedMap(); + }); + editor.info = JSON.parse(JSON.stringify(thisevent)); + editor.pos=pos; + editor_mode.onmode('nextChange'); + editor_mode.onmode('enemyitem'); + editor.uifunctions.showBlockInfo(JSON.parse(JSON.stringify(thisevent))); +} + +editor.prototype.addUsedFlags = function (s) { + s.replace(/flag:([a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]+)/g, function (s0, s1) { + editor.used_flags[s1] = true; return s0; + }); + s.replace(/flags\.([a-zA-Z_]\w*)/g, function (s0, s1) { + editor.used_flags[s1] = true; return s0; + }); + if (window.flags) { + for (var s in editor.used_flags) { + if (!(s in window.flags)) { + window.flags[s] = null; + } + } + } +} + +editor.prototype.listen = function () { + // 移动至 editor_listen.js +}//绑定事件 + +editor.prototype.mobile_listen=function(){ + // 移动至 editor_listen.js +} + + + + +editor = new editor(); \ No newline at end of file diff --git a/editor.md b/editor.md new file mode 100644 index 0000000..2ef0960 --- /dev/null +++ b/editor.md @@ -0,0 +1,322 @@ +# 修改编辑器 + +?> 在这一节中,让我们来了解如何修改编辑器(包括配置表格和事件编辑器) + +在改动core时, 有时会相应的更改project中存储数据的结构, 而表格和事件编辑器不做相应更改的话就无法顺畅的编辑改动了结构的数据了, 此文档帮助造塔者进行`配置表格`的修改, 以及修改_server/MotaAction.g4和其他相关文件来调整事件编辑器的图块. + +## 修改表格 + +### demo - 给怪物增加 _速度_ 属性 + +点击素材区的怪物后点击配置表格, 或者直接打开_server/table/comment.js +拉到`【怪物】相关的表格配置`的部分 +``` js +// --------------------------- 【怪物】相关的表格配置 --------------------------- // +"enemys": { + "_type": "object", + "_data": { + "id": { + "_leaf": true, + "_type": "disable", + "_docs": "怪物ID", + "_data": "怪物ID,可于页面底部修改" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "名称" + }, + "displayIdInBook": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "手册映射ID", + "_data": "在怪物手册中映射到的怪物ID。如果此项不为null,则在怪物手册中,将用目标ID来替换该怪物原本的ID。常被运用在同一个怪物的多朝向上。" + }, + "hp": { + "_leaf": true, + "_type": "textarea", + "_data": "生命值" + }, + "atk": { + "_leaf": true, + "_type": "textarea", + "_data": "攻击力" + }, +``` +可以看到, project/enemys.js 中怪物的属性 +```js +"greenSlime": {"name":"绿头怪","hp":100,"atk":120,"def":0,"money":1,"exp":1,"point":0,"special":[1,5,7,8]}, +``` +被逐条列出并解释 + +把hp的部分复制一份并修改 +```js +"speed": { + "_leaf": true, + "_type": "textarea", + "_data": "速度" +}, +``` +刷新之后, 怪物的表格在hp下面就多出了speed一项, 编辑后出现在了怪物属性中 +```js +"greenSlime": {"name":"绿头怪","hp":100,"atk":120,"def":0,"money":1,"exp":1,"point":0,"special":[1,5,7,8],"speed":123123}, +``` +### 表格配置项含义 + +可以看出表格配置中的对象是一层套一层的结构, `_`开头的和数据的名字间隔着展开, 忽略所有`_`的层级的话, 这个对象和project中的对象是有相同的结构 + +**_leaf** 为真的项对应的数据将作为表格的编辑的内容, 数字/字符串/空对象和数组会被自动计算为true, 非空的对象和数组会被计算为false, 手动填写_leaf的值可以强制不展开对象和数组来进行编辑 + +**_type** 决定了表格如何展示该数据 ++ select 使用下拉菜单展示 ++ checkbox 使用勾选框展示 ++ checkboxSet 使用勾选框组展示 ++ 其他 使用文本框展示, 根据类型区分, 双击或点编辑会有以下行为 + - textarea 文本编辑器(_type的默认值) + - event 事件编辑器 + - material 素材选取 + - color 取色器 + - point 地图选点 + - disable 不允许编辑 + - popCheckBoxSet 以弹窗形式多选框 + +当某个event例如 门信息编辑 无法适应修改后的数据结构, 可以修改事件编辑器, 也可以把_type改成textarea +以上类型的格式要如何写请搜索例如`"_type": "checkboxSet"`来查找例子, 此处不展示 + +**_range** 被编辑的值会作为`thiseval`来进行检测, 当字符串为真时才接收这个值, 同时当删除表格时会判定_range是否接收null + +**拓展性** + +注意这是js文件, 可以使用表达式, 如下例子引用了别处的数据作为下拉菜单 +```js +"bgm": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [null].concat(Object.keys(editor.core.material.bgms)) + }, + "_docs": "背景音乐", + "_data": "到达该层后默认播放的BGM" +}, +``` + +同时`_`开头的项可以使用函数. 同一级中`_data`最先被计算. 复杂的结构的注释可以利用函数动态生成 +`_data`的参数`key`是各子项的名字, 其他`_`开头的参数是`args`详见editor_table.prototype.objToTable的注释 +例如: 自动事件数组, `"_leaf": false`强制展开, 通过`_action`函数即使为空也显示两个空白的项, 同时`_data`函数给自动事件的每一项标记为event +```js +"autoEvent": { + "_type": "object", + "_leaf": false, + "_action": function (args) { + args.vobj = args.vobj || {}; + for (var ii = 0; ii < 2; ii++) { + args.vobj[ii] = args.vobj[ii] || null; + } + }, + "_data": function (key) { + return { + "_leaf": true, + "_type": "event", + "_event": "autoEvent", + "_data": "自动事件" + } + } +}, +``` + +## 修改事件编辑器 + +_type为event的表格项, 在双击时会进入事件编辑器. 是由[antlr-blockly](https://github.com/zhaouv/antlr-blockly)生成的图块式的可视化编辑器 + +_event的内容例如autoEvent则是使用的入口图块的类型 + +请先查看[antlr-blockly的文档](https://zhaouv.github.io/antlr-blockly/docs/#/README)来了解.g4语法文件和可视化编辑器blockly以及库antlr-blockly的基础知识 + +### 事件编辑器的运行机制简介 + +事件编辑器编辑一个表格项的流程有以下几步 ++ 加载表格中的json, 将其根据类别转化成图块 ++ 通过拖拽填写图块编辑内容 ++ 将图块转化回json置入表格 + +其中图块到json 是由_server/MotaAction.g4完成的 +json到图块 是由_server/MotaActionParser.js完成的, 并依赖图块在g4中的定义 + +图块到json首先由入口方块出发, 入口方块又依次去获取了嵌入在其中的方块和域, 嵌入的方块再递归访问其嵌入的内容, 从而访问了所有方块上域中的信息. +例如 event_m 通过 action_0 获取了其中嵌入的所有语句生成的json, 插入到其自身的键'data'中 + +```antlr +//事件 事件编辑器入口之一 +event_m + : '事件' BGNL? Newline '覆盖触发器' Bool '启用' Bool '通行状态' B_0_List '显伤' Bool BGNL? Newline action+ BEND + + +/* event_m +tooltip : 编辑魔塔的事件 +helpUrl : https://h5mota.com/games/template/_docs/#/event +default : [false,null,null,null,null] +B_0_List_0=eval(B_0_List_0); +var code = { + 'trigger': Bool_0?'action':null, + 'enable': Bool_1, + 'noPass': B_0_List_0, + 'displayDamage': Bool_2, + 'data': 'data_asdfefw' +} +if (!Bool_0 && Bool_1 && (B_0_List_0===null) && Bool_2) code = 'data_asdfefw'; +code=JSON.stringify(code,null,2).split('"data_asdfefw"').join('[\n'+action_0+']\n'); +return code; +*/; +``` + +值得注意的是shoplist和action这样的语句集合, 语句集合会选择其中的最后一项作为父方块的默认块, 所以在希望其允许空时, 要制作一个空项放在语句集合定义的末尾, 例如 +```antlr +shoplist + : shopsub + | shopitem + | shopcommonevent + | emptyshop + ; + +emptyshop + : Newline + + +/* emptyshop +var code = ' \n'; +return code; +*/; +``` + +json到图块则是从ActionParser.prototype.parse出发, 递归的将js对象变为图块, 例如event类型的json, 使用event_m入口图块, 将obj.trigger/enable/noPass/displayDamage填到域中, obj.data交给this.parseList转化为语句的图块, 与g4中注入的语句做相反的事情 +```js +ActionParser.prototype.parse = function (obj,type) { + switch (type) { + case 'event': + if(!obj)obj={}; + if(typeof(obj)===typeof('')) obj={'data':[obj]}; + if(obj instanceof Array) obj={'data':obj}; + return MotaActionBlocks['event_m'].xmlText([ + obj.trigger==='action',obj.enable,obj.noPass,obj.displayDamage,this.parseList(obj.data) + ]); +``` + +### demo - 给已有图块添加新域 + +例如给'播放背景音乐'图块再额外加一个参数列表 + +不熟悉时, 对任意图块来说这都算是最简单粗暴的修改方式了, 复杂的修改需求也只需要改这一下, 只是填图块时没那么方便 + +编辑g4时推荐使用vscode, 搜索并安装插件mota-js + +
      playBgm_s
      +    :   '播放背景音乐' EvalString '开始播放秒数' Int '持续到下个本事件' Bool '参数列表' JsonEvalString? Newline
      +    
      +
      +/* playBgm_s
      +tooltip : playBgm: 播放背景音乐
      +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=playbgm%EF%BC%9A%E6%92%AD%E6%94%BE%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90
      +default : ["bgm.mp3", 0, true]
      +allBgms : ['EvalString_0']
      +colour : this.soundColor
      +Int_0 = Int_0 ? (', "startTime": '+Int_0) : '';
      +Bool_0 = Bool_0 ? ', "keep": true' : '';
      +if (JsonEvalString_0) {
      +    JsonEvalString_0 = ', "args": ' +JsonEvalString_0;
      +}
      +var code = '{"type": "playBgm", "name": "'+EvalString_0+'"'+Int_0+Bool_0+JsonEvalString_0+'},\n';
      +return code;
      +*/;
      + +使用了类型JsonEvalString, 并在注入的代码中处理了其新增出的JsonEvalString_0要如何体现在这个方块产生的json +此处使用了antlr-blockly产生的'类型名_次数'的默认名称, 也可以开头加一行`name : [null,null,null,'args']`来把这个变量命名为args, 对于setText_s这种域比较多的块会很有帮助. +在MotaAction中, 除了默认的Int/Number等 +常规的字符串使用EvalString +ID类型的使用IdString +允许空的自然数使用IntString +位置坐标使用PosString +不转义的多行文本使用RawEvalString +json类型的文本使用JsonEvalString + +相应的也需要改json到图块的部分 + +
            break;
      +    case "playBgm":
      +      this.next = MotaActionBlocks['playBgm_s'].xmlText([
      +        data.name,data.startTime||0,data.keep||false,data.args,this.next]);
      +      break
      +    case "pauseBgm":
      + + +### demo - 增加新语句图块 + +假设已经制作了一个名为`陨石坠落`的公共事件, 其参数列表是长度3的数组, 依次是陨石颜色, 陨石位置, 爆炸范围. + +每次要使用插入公共事件并要填`陨石坠落`和形如`[[231,231,41],[5,7],1]`的长而且带有格式的内容, 并且无法使用取色器和地图选点功能. 此时就可以借助'注册一个自定义事件'并填加新语句图块来解决这个问题. + +首先注册成名为'meteorite'的事件, 在插件编写的init中添加以下内容 +```js +core.registerEvent('meteorite', function(data){ + core.insertAction({"type": "insert", "name": "陨石坠落", "args": [data.color, data.loc, data.range]}); + core.doAction(); +}) +``` + +然后在g4中修改以下位置(请善用搜索来定位) + +
          |   drawSelector_1_s
      +    |   unknown_s
      +    |   function_s
      +    |   meteorite_s
      +    |   pass_s
      +    ;
      +
      +text_0_s
      + +并在分号后面添加 + +```antlr +meteorite_s + : '陨石坠落' '颜色' ColorString? Colour 'x' PosString ',' 'y' PosString '爆炸范围' Int Newline + + +/* meteorite_s +tooltip : 陨石坠落 +helpUrl : https://h5mota.com/games/template/_docs/#/event +default : ["255,0,0,1",'rgba(255,0,0,1)',"0","0",1] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.soundColor +var loc = ', "loc": ['+PosString_0+','+PosString_1+']'; +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +var code = '{"type": "meteorite"'+ColorString_0+loc+', "range": '+Int_0+'},\n'; +return code; +*/; +``` + +希望是特效的颜色, 于是在g4搜索'画面闪烁', 找到了`colour : this.soundColor`, 抄出其中的颜色的写法, 地图选点的写法同理, 搜索'强制战斗'后模仿其写法 + +在MotaActionParser.js中添加json到图块的代码, 同理模仿'画面闪烁'和'强制战斗' + +
            break;
      +    case "playBgm":
      +      this.next = MotaActionBlocks['playBgm_s'].xmlText([
      +        data.name,data.startTime||0,data.keep||false,this.next]);
      +      break;
      +    case "meteorite":
      +      data.color = this.Colour(data.color)
      +      this.next = MotaActionBlocks['meteorite_s'].xmlText([
      +        data.color,'rgba('+data.color+')',data.loc[0],data.loc[1],data.range,this.next]);
      +      break;
      +    case "pauseBgm":
      + +最后在editor_blocklyconfig.js中将其加入到工具栏中 + +
          '特效/声音':[
      +      MotaActionBlocks['meteorite_s'].xmlText(),
      +      MotaActionBlocks['sleep_s'].xmlText(),
      + +========================================================================================== + +[继续阅读下一章:UI编辑器](ui-editor) \ No newline at end of file diff --git a/editor_blockly.js b/editor_blockly.js new file mode 100644 index 0000000..4f0f62d --- /dev/null +++ b/editor_blockly.js @@ -0,0 +1,1213 @@ +editor_blockly = function () { + + var editor_blockly = { entryType: 'event' }; + + editor.uivalues.disableBlocklyReplace = editor.config.get("disableBlocklyReplace", false); + var replaceCheckbox = document.getElementById('blocklyReplace'); + replaceCheckbox.checked = !editor.uivalues.disableBlocklyReplace; + + editor_blockly.triggerReplace = function () { + editor.uivalues.disableBlocklyReplace = !replaceCheckbox.checked; + editor.config.set("disableBlocklyReplace", !replaceCheckbox.checked); + if (MotaActionFunctions) MotaActionFunctions.disableReplace = !replaceCheckbox.checked; + alert("已" + (replaceCheckbox.checked ? "开启" : "关闭") + "中文变量名替换!\n关闭并重开事件编辑器以生效。"); + } + + editor.uivalues.disableBlocklyExpandCompare = editor.config.get("disableBlocklyExpandCompare", false); + var expandCompareCheckbox = document.getElementById('blocklyExpandCompare'); + expandCompareCheckbox.checked = !editor.uivalues.disableBlocklyExpandCompare; + + editor_blockly.triggerExpandCompare = function () { + editor.uivalues.disableBlocklyExpandCompare = !expandCompareCheckbox.checked; + editor.config.set("disableBlocklyExpandCompare", !expandCompareCheckbox.checked); + if (MotaActionFunctions) MotaActionFunctions.disableExpandCompare = !expandCompareCheckbox.checked; + } + + var input_ = ''; + editor_blockly.runOne = function () { + //var printf = console.log; + //var printf = function(){}; + var grammerFile = input_; + converter = new Converter().init(); + converter.generBlocks(grammerFile); + //printf(converter.blocks); + converter.renderGrammerName(); + //converter.generToolbox(); + converter.generMainFile(); + //printf(converter.mainFile.join('')); + //console.log(converter); + + + var script = document.createElement('script'); + script.innerHTML = converter.mainFile[5] + editor_blocklyconfig; + document.body.appendChild(script); + } + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState != 4) return; + if (xhr.status != 200) { + alert("图块描述文件加载失败, 请在'启动服务.exe'中打开编辑器"); + return; + } + input_ = xhr.responseText; + editor_blockly.runOne(); + MotaActionFunctions.disableReplace = editor.uivalues.disableBlocklyReplace; + MotaActionFunctions.disableExpandCompare = editor.uivalues.disableBlocklyExpandCompare; + } + xhr.open('GET', '_server/MotaAction.g4', true); + xhr.send(null); + + var codeAreaHL = CodeMirror.fromTextArea(document.getElementById("codeArea"), { + lineNumbers: true, + matchBrackets: true, + lineWrapping: true, + continueComments: "Enter", + extraKeys: { "Ctrl-Q": "toggleComment" }, + }); + codeAreaHL.on('changes', function () { + editor_blockly.highlightParse(!changeFromBlockly); + changeFromBlockly = false; + }); + var changeFromBlockly = false; + var shouldNotifyParse = false; + + editor_blockly.showXML = function () { + var xml = Blockly.Xml.workspaceToDom(editor_blockly.workspace); + var xml_text = Blockly.Xml.domToPrettyText(xml); + console.log(xml_text); + var xml_text = Blockly.Xml.domToText(xml); + console.log(xml_text); + console.log(xml); + } + + editor_blockly.runCode = function () { + // Generate JavaScript code and run it. + window.LoopTrap = 1000; + Blockly.JavaScript.INFINITE_LOOP_TRAP = + 'if (--window.LoopTrap == 0) throw "Infinite loop.";\n'; + var code = Blockly.JavaScript.workspaceToCode(editor_blockly.workspace); + Blockly.JavaScript.INFINITE_LOOP_TRAP = null; + try { + eval('obj=' + code); + console.log(obj); + } catch (e) { + alert(e); + } + } + + editor_blockly.setValue = function (value) { + changeFromBlockly = true; + codeAreaHL.setValue(value); + } + + editor_blockly.parse = function () { + MotaActionFunctions.parse( + eval('obj=' + codeAreaHL.getValue().replace(/[<>&]/g, function (c) { + return { '<': '<', '>': '>', '&': '&' }[c]; + }).replace(/\\(r|f|i|c|d|e|g|z)/g, '\\\\$1')), + editor_blockly.isCommonEntry() ? 'common' : editor_blockly.entryType + ); + } + + editor_blockly.id = ''; + + var _lastOpenPosition = {}; + + editor_blockly.import = function (id_, args) { + var thisTr = document.getElementById(id_); + if (!thisTr) return false; + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var type = args.type; + if (!type) return false; + editor_blockly.id = id_; + editor_blockly.setValue(input.value); + editor_blockly.entryType = type; + editor_blockly.parse(); + editor_blockly.show(); + var _offsetIndex = [editor_blockly.entryType, editor.pos.x, editor.pos.y, editor.currentFloorId].join(":"); + editor_blockly.workspace.scroll(0, _lastOpenPosition[_offsetIndex] || 0) + return true; + } + + var blocklyWidgetDiv = document.getElementsByClassName('blocklyWidgetDiv'); + editor_blockly.show = function () { + if (typeof (selectBox) !== typeof (undefined)) selectBox.isSelected(false); + document.getElementById('left6').style = ''; + for (var ii = 0, node; node = blocklyWidgetDiv[ii]; ii++) { + node.style.zIndex = 201; + node.style.opacity = ''; + } + } + editor_blockly.hide = function () { + document.getElementById('left6').style = 'z-index:-1;opacity: 0;'; + for (var ii = 0, node; node = blocklyWidgetDiv[ii]; ii++) { + node.style.zIndex = -1; + node.style.opacity = 0; + } + } + + var blocklyParseBtn = document.getElementById('blocklyParse'); + editor_blockly.highlightParse = function (shouldHighLight) { + if (shouldNotifyParse == shouldHighLight) return; + shouldNotifyParse = shouldHighLight; + if (shouldHighLight) blocklyParseBtn.classList.add('highlight'); + else blocklyParseBtn.classList.remove('highlight'); + } + + editor_blockly.cancel = function () { + var _offsetIndex = [editor_blockly.entryType, editor.pos.x, editor.pos.y, editor.currentFloorId].join(":"); + _lastOpenPosition[_offsetIndex] = editor_blockly.workspace.scrollY; + + editor_blockly.id = ''; + editor_blockly.hide(); + } + + editor_blockly.confirm = function (keep) { + if (!editor_blockly.id) { + editor_blockly.id = ''; + return; + } + if (shouldNotifyParse) { + alert('你尚未解析修改后的内容,请进行解析或放弃操作'); + return; + } + if (editor_blockly.workspace.topBlocks_.length >= 2) { + editor_blockly.setValue('入口方块只能有一个'); + return; + } + var eventType = editor_blockly.entryType; + if (editor_blockly.workspace.topBlocks_.length == 1) { + var blockType = editor_blockly.workspace.topBlocks_[0].type; + if (blockType !== eventType + '_m' && !(editor_blockly.isCommonEntry() && blockType == 'common_m')) { + editor_blockly.setValue('入口方块类型错误'); + return; + } + } + var setvalue = function (value) { + var thisTr = document.getElementById(editor_blockly.id); + var input = thisTr.children[2].children[0].children[0]; + input.value = value; + if (!keep) { + editor_blockly.id = ''; + editor_blockly.hide(); + } + else alert('保存成功!'); + input.onchange(); + } + if (codeAreaHL.getValue() === '') { + eventType === 'shop' ? setvalue('[]') : setvalue('null'); + return; + } + var code = Blockly.JavaScript.workspaceToCode(editor_blockly.workspace); + code = code.replace(/\\(i|c|d|e|g|z)/g, '\\\\$1'); + eval('var obj=' + code); + if (this.checkAsync(obj) && confirm("警告!存在不等待执行完毕的事件但却没有用【等待所有异步事件处理完毕】来等待" + + "它们执行完毕,这样可能会导致录像检测系统出问题。\n你要返回修改么?")) return; + + var _offsetIndex = [editor_blockly.entryType, editor.pos.x, editor.pos.y, editor.currentFloorId].join(":"); + _lastOpenPosition[_offsetIndex] = editor_blockly.workspace.scrollY; + setvalue(JSON.stringify(obj)); + } + + // 检查"不等待处理完毕" + editor_blockly.checkAsync = function (obj) { + if (!(obj instanceof Array)) return false; + var hasAsync = false; + for (var i = 0; i < obj.length; ++i) { + var one = obj[i]; + if (one.type == 'if' && (this.checkAsync(one['true']) || this.checkAsync(one['false']))) + return true; + if ((one.type == 'while' || one.type == 'dowhile') && this.checkAsync(one.data)) + return true; + if (one.type == 'confirm' && (this.checkAsync(one.yes) || this.checkAsync(one.no))) + return true; + if (one.type == 'choices') { + var list = one.choices; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'switch') { + var list = one.caseList; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'wait') { + var list = one.data; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'previewUI' && this.checkAsync(one.action)) return true; + if (one.async && one.type != 'animate' && one.type != 'function' && one.type != 'text') hasAsync = true; + if (one.type == 'waitAsync' || one.type == 'stopAsync') hasAsync = false; + } + return hasAsync; + } + + var _isTextAttributeSet = false; + + editor_blockly.previewBlock = function (b, args) { + + var previewTextDrawing = function (content) { + var arr = []; + content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { + var list = str.split(","); + if (list.length == 3 || list.length == 5 || list.length >= 9) { + var name = list[0]; + var obj = { "type": "drawImage" }; + if (name.endsWith(":o") || name.endsWith(":x") || name.endsWith(":y")) { + obj.reverse = name.substring(name.length - 2); + name = name.substring(0, name.length - 2); + } + obj.image = name; + obj.x = parseFloat(list[1]); + obj.y = parseFloat(list[2]); + if (list.length >= 5) { + obj.w = parseFloat(list[3]); + obj.h = parseFloat(list[4]); + } + if (list.length >= 9) { + obj.x1 = parseFloat(list[5]); + obj.y1 = parseFloat(list[6]); + obj.w1 = parseFloat(list[7]); + obj.h1 = parseFloat(list[8]); + } + if (list.length >= 10) { + arr.push({ "type": "setAttribute", "alpha": parseFloat(list[9]) }); + } + if (list.length >= 11) { + obj.angle = parseFloat(list[10]); + } + arr.push(obj); + } + return ""; + }); + editor.uievent.previewUI(arr); + return true; + } + + try { + // 特殊处理立绘 + if (b.type == 'textDrawing') { + previewTextDrawing(Blockly.JavaScript.blockToCode(b)); + return true; + } + + var code = "[" + Blockly.JavaScript.blockToCode(b).replace(/\\(i|c|d|e|g|z)/g, '\\\\$1') + "]"; + eval("var obj=" + code); + if (obj.length == 0) return true; + obj = obj[0]; + switch (b.type) { + case 'text_0_s': + case 'text_1_s': + case 'text_2_s': + case 'choices_s': + case 'confirm_s': + if (!_isTextAttributeSet) { + alert('警告!你尚未设置用于预览的剧情文本的属性,将采用默认属性进行预览。\n你可以双击“设置剧情文本的属性”事件来设置用于预览的属性。'); + core.status.textAttribute = core.clone(core.initStatus.textAttribute); + _isTextAttributeSet = true; + } + editor.uievent.previewUI([obj]); + break; + case 'setText_s': // 设置剧情文本的属性 + _isTextAttributeSet = true; + core.status.textAttribute = core.clone(core.initStatus.textAttribute); + core.setTextAttribute(obj); + alert('已成功设置此属性为显示文章的预览属性!') + break; + case 'waitContext_2': // 等待用户操作坐标预览 + editor.uievent.previewUI([{ + "type": "fillRect", "x": obj.px[0], "y": obj.py[0], + "width": "(" + obj.px[1] + ")-(" + obj.px[0] + ")", "height": "(" + obj.py[1] + ")-(" + obj.py[0] + ")", + "style": "rgba(255,0,0,0.5)" + }]); + break; + case 'showImage_s': // 显示图片 + case 'showImage_1_s': + if (obj.sloc) { + editor.uievent.previewUI([ + { type: "setAttribute", alpha: obj.opacity }, + { + type: "drawImage", image: obj.image, x: obj.sloc[0], y: obj.sloc[1], w: obj.sloc[2], h: obj.sloc[3], + x1: obj.loc[0], y1: obj.loc[1], w1: obj.loc[2], h1: obj.loc[3], reverse: obj.reverse + } + ]); + } else { + editor.uievent.previewUI([ + { type: "setAttribute", alpha: obj.opacity }, + { type: "drawImage", image: obj.image, x: obj.loc[0], y: obj.loc[1], w: obj.loc[2], h: obj.loc[3], reverse: obj.reverse } + ]); + } + break; + case 'showGif_s': // 显示动图 + if (obj.name && obj.loc) { + editor.uievent.previewUI([{ type: "drawImage", image: obj.name, x: obj.loc[0], y: obj.loc[1] }]); + } + break; + case 'setCurtain_0_s': // 更改色调 + if (obj.color) { + editor.uievent.previewUI([{ type: "fillRect", x: 0, y: 0, width: core.__PIXELS__, height: core.__PIXELS__, style: obj.color }]); + } + break; + case 'floorOneImage': // 楼层贴图 + obj.w = obj.w / (obj.frame || 1); + editor.uievent.previewUI([ + { + type: "drawImage", image: obj.name, x: obj.sx || 0, y: obj.sy || 0, w: obj.w, h: obj.h, + x1: obj.x, y1: obj.y, w1: obj.w, h1: obj.h, reverse: obj.reverse + } + ]); + break; + case 'previewUI_s': // 预览 + editor.uievent.previewUI(obj.action); + break; + default: + if (b.type.startsWith(obj.type)) { + editor.uievent.previewUI([obj]); + } + } + } catch (ee) { console.error(ee) } + + } + + editor_blockly.selectMaterial = function (b, material) { + var value = b.getFieldValue(material[1]); + value = main.nameMap[value] || value; + editor.uievent.selectMaterial([value], '请选择素材', material[0], function (one) { + if (b.type == 'animate_s' || b.type == 'animate_1_s' || b.type == 'nameMapAnimate') { + return /^[-A-Za-z0-9_.]+\.animate$/.test(one) ? one.substring(0, one.length - 8) : null; + } + return /^[-A-Za-z0-9_.]+$/.test(one) ? one : null; + }, function (value) { + if (value instanceof Array && value.length > 0) { + value = value[0]; + // 检测是否别名替换 + for (var name in main.nameMap) { + if (main.nameMap[name] == value) { + if (confirm("检测到该文件存在别名:" + name + "\n是否使用别名进行替换?")) { + b.setFieldValue(name, material[1]); + return; + } else { + break; + } + } + } + b.setFieldValue(value, material[1]); + } + }); + } + + editor_blockly.doubleclicktext = function (b, f) { + var value = b.getFieldValue(f); + //多行编辑 + editor_multi.multiLineEdit(value, b, f, { 'lint': f === 'RawEvalString_0' }, function (newvalue, b, f) { + if (!f.startsWith('EvalString_Multi')) { + newvalue = newvalue.split('\n').join('\\n'); + } + b.setFieldValue(newvalue, f); + }); + } + + editor_blockly.doubleClickBlock = function (blockId) { + var b = editor_blockly.workspace.getBlockById(blockId); + + if (b && MotaActionBlocks[b.type].previewBlock) { + editor_blockly.previewBlock(b, MotaActionBlocks[b.type].previewBlock) + return; + } + + if (b && MotaActionBlocks[b.type].selectPoint) { // selectPoint + editor_blockly.selectPoint(b, eval(MotaActionBlocks[b.type].selectPoint)); + return; + } + + if (b && MotaActionBlocks[b.type].material) { + editor_blockly.selectMaterial(b, JSON.parse(MotaActionBlocks[b.type].material)); + return; + } + + if (b && MotaActionBlocks[b.type].doubleclicktext) { //多行编辑 + editor_blockly.doubleclicktext(b, MotaActionBlocks[b.type].doubleclicktext); + return; + } + } + + editor_blockly.selectPointFromButton = function () { + var b = Blockly.selected; + if (b && MotaActionBlocks[b.type].selectPoint) { + editor_blockly.selectPoint(b, eval(MotaActionBlocks[b.type].selectPoint)); + return; + } else { + editor.uievent.selectPoint(); + } + } + + editor_blockly.showKeyCodes = function () { + alert('键值查询表:\nA65 B66 C67 D68 E69 F70 G71 H72 I73 J74 K75 L76 M77\n' + + 'N78 O79 P80 Q81 R82 S83 T84 U85 V86 W87 X88 Y89 Z90\n0:48 1:49 2:50 3:51 4:52 5:53 6:54 7:55 8:56 9:57\n' + + '空格:13 回车:32 ESC:27 后退:8 Tab:9 Shift:16 Ctrl:17 Alt:18\nPgUp:33 PgDn:34 左:37 上:38 右:39 下:40\n更多键值请自行百度查表') + } + + editor_blockly.lastUsedType = [ + 'text_0_s', + 'comment_s', + 'show_s', + 'hide_s', + 'setValue_s', + 'if_s', + 'while_s', + 'battle_s', + 'openDoor_s', + 'choices_s', + 'setText_s', + 'exit_s', + 'sleep_s', + 'setBlock_s', + 'insert_1_s' + ]; // 最常用的15个事件 + editor_blockly.lastUsedTypeNum = 15; + + editor_blockly.addIntoLastUsedType = function (blockId) { + var b = editor_blockly.workspace.getBlockById(blockId); + if (!b) return; + var blockType = b.type; + if (!blockType || blockType.indexOf("_s") !== blockType.length - 2 || blockType === 'pass_s') return; + editor_blockly.lastUsedType = editor_blockly.lastUsedType.filter(function (v) { return v !== blockType; }); + if (editor_blockly.lastUsedType.length >= editor_blockly.lastUsedTypeNum) + editor_blockly.lastUsedType.pop(); + editor_blockly.lastUsedType.unshift(blockType); + + document.getElementById("searchBlock").value = ''; + } + + // Index from 1 - 9 + editor_blockly.openToolbox = function (index) { + if (index < 0) index += editor_blockly.workspace.toolbox_.tree_.children_.length; + editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index]); + } + editor_blockly.reopenToolbox = function (index) { + if (index < 0) index += editor_blockly.workspace.toolbox_.tree_.children_.length; + editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index]); + editor_blockly.workspace.getFlyout().show(editor_blockly.workspace.toolbox_.tree_.children_[index].blocks); + } + + editor_blockly.closeToolbox = function () { + editor_blockly.workspace.toolbox_.clearSelection(); + } + + var searchInput = document.getElementById("searchBlock"); + searchInput.onfocus = function () { + editor_blockly.reopenToolbox(-1); + } + + searchInput.oninput = function () { + editor_blockly.reopenToolbox(-1); + } + + editor_blockly.searchBlock = function (value) { + if (value == null) value = searchInput.value; + value = value.toLowerCase(); + if (value == '') return editor_blockly.lastUsedType; + var results = []; + for (var name in MotaActionBlocks) { + if (typeof name !== 'string' || name.indexOf("_s") !== name.length - 2) continue; + var block = MotaActionBlocks[name]; + if (block && block.json) { + if ((block.json.type || "").toLowerCase().indexOf(value) >= 0 + || (block.json.message0 || "").toLowerCase().indexOf(value) >= 0 + || (block.json.tooltip || "").toLowerCase().indexOf(value) >= 0) { + results.push(name); + if (results.length >= editor_blockly.lastUsedTypeNum) + break; + } + } + } + + return results.length == 0 ? editor_blockly.lastUsedType : results; + } + + // ------ select point ------ + + editor_blockly.selectPoint = function (block, arr) { + + var floorId = editor.currentFloorId, pos = editor.pos, x = pos.x, y = pos.y; + + var xv = block.getFieldValue(arr[0]), yv = block.getFieldValue(arr[1]); + if (xv != null) x = xv; + if (yv != null) y = yv; + if (arr[2] != null) floorId = block.getFieldValue(arr[2]) || floorId; + + editor.uievent.selectPoint(floorId, x, y, false, function (fv, xv, yv) { + if (!arr) return; + if (arr[2] != null) { + if (fv != editor.currentFloorId || editor_blockly.entryType == 'commonEvent') block.setFieldValue(fv, arr[2]); + else block.setFieldValue(arr[3] ? fv : "", arr[2]); + } + block.setFieldValue(xv + "", arr[0]); + block.setFieldValue(yv + "", arr[1]); + if (block.type == 'changeFloor_m' || block.type == 'changeFloor_s') { + block.setFieldValue("floorId", "Floor_List_0"); + block.setFieldValue("loc", "Stair_List_0"); + } + }); + } + + editor_blockly.getAutoCompletions = function (content, type, name, pb) { + // --- content为当前框中输入内容;将返回一个列表,为后续所有可补全内容 + + // console.log(type, name); + + // 检查 status:xxx,item:xxx和flag:xxx + var index = Math.max(content.lastIndexOf(":"), content.lastIndexOf(":")); + if (index >= 0) { + var ch = content.charAt(index); + var before = content.substring(0, index), token = content.substring(index + 1); + if (/^[a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]*$/.test(token)) { + if (before.endsWith("状态") || (ch == ':' && before.endsWith("status"))) { + var list = Object.keys(core.status.hero); + if (before.endsWith("状态") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceStatusList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + else if (before.endsWith("物品") || (ch == ':' && before.endsWith("item"))) { + var list = Object.keys(core.material.items); + if (before.endsWith("物品") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceItemList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + else if (before.endsWith("变量") || (ch == ':' && before.endsWith("flag"))) { + return Object.keys(editor.used_flags || {}).filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } else if (before.endsWith("怪物") || (ch == ':' && before.endsWith("enemy"))) { + var list = Object.keys(core.material.enemys); + if (before.endsWith("怪物") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceEnemyList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }) + } else { + var index2 = Math.max(content.lastIndexOf(":", index - 1), content.lastIndexOf(":", index - 1)); + var ch2 = content.charAt(index2); + if (index2 >= 0) { + before = content.substring(0, index2); + if (before.endsWith("怪物") || (ch == ':' && ch2 == ':' && before.endsWith("enemy"))) { + var list = MotaActionBlocks['EnemyId_List'].options.map(function (v) { return v[1] }); + if (before.endsWith("怪物") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceEnemyValueList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }) + } + } + + } + } + } + + // 提供 core.xxx 的补全 + index = content.lastIndexOf("core."); + if (index >= 0) { + var s = content.substring(index + 5); + if (/^[\w.]*$/.test(s)) { + var tokens = s.split("."); + var now = core, prefix = tokens[tokens.length - 1]; + for (var i = 0; i < tokens.length - 1; ++i) { + now = now[tokens[i]]; + if (now == null) break; + } + if (now != null) { + var candidates = []; + for (var i in now) { + candidates.push(i); + } + return candidates.filter(function (one) { + return one != prefix && one.startsWith(prefix); + }).sort(); + } + } + } + + // 提供 flags.xxx 补全 + index = content.lastIndexOf("flags."); + if (index >= 0) { + var token = content.substring(index + 6); + return Object.keys(editor.used_flags || {}).filter(function (one) { + return one != token && one.startsWith(token) + && /^[a-zA-Z_]\w*$/.test(one); + }).sort(); + } + + // 提供 hero.xxx 补全 + index = content.lastIndexOf("hero."); + if (index >= 0) { + var token = content.substring(index + 6); + return Object.keys(core.status.hero).filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + + // 提供 IdText_0 的补全 + if (type == 'idIdList_e' && name == 'IdText_0') { + var list = []; + switch (pb.getFieldValue('Id_List_0')) { + case 'status': + list = Object.keys(core.status.hero); + if (MotaActionFunctions && replaceCheckbox.checked) { + list = MotaActionFunctions.pattern.replaceStatusList.map(function (v) { + return v[1]; + }).concat(list); + } + break; + case 'item': + list = Object.keys(core.material.items); + if (MotaActionFunctions && replaceCheckbox.checked) { + list = MotaActionFunctions.pattern.replaceItemList.map(function (v) { + return v[1]; + }).concat(list); + } + break; + case 'flag': + list = Object.keys(editor.used_flags || {}); + break; + } + return list.filter(function (one) { + return one != content && one.startsWith(content); + }).sort(); + } + + var namesObj = {}; + + namesObj.allIds = ["this"].concat(core.getAllIconIds()); + namesObj.allIconIds = namesObj.allIds.concat(Object.keys(core.statusBar.icons).filter(function (x) { + return core.statusBar.icons[x] instanceof Image; + })); + namesObj.allImages = Object.keys(core.material.images.images) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.images.images[main.nameMap[one]]; })); + namesObj.allEnemys = Object.keys(core.material.enemys); + if (MotaActionFunctions && !MotaActionFunctions.disableReplace) { + namesObj.allEnemys = namesObj.allEnemys.concat(MotaActionFunctions.pattern.replaceEnemyList.map(function (x) { + return x[1]; + })) + } + namesObj.allItems = Object.keys(core.material.items); + namesObj.allEquips = namesObj.allItems.filter(function (one) { return core.material.items[one].cls == 'equips' }); + if (MotaActionFunctions && !MotaActionFunctions.disableReplace) { + namesObj.allItems = namesObj.allItems.concat(MotaActionFunctions.pattern.replaceItemList.map(function (x) { + return x[1]; + })); + namesObj.allEquips = namesObj.allEquips.concat(MotaActionFunctions.pattern.replaceItemList.filter(function (x) { + return namesObj.allEquips.includes(x[0]); + }).map(function (x) { return x[1]; })); + } + namesObj.allAnimates = Object.keys(core.material.animates) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.animates[main.nameMap[one]]; })); + namesObj.allBgms = Object.keys(core.material.bgms) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.bgms[main.nameMap[one]]; })); + namesObj.allSounds = Object.keys(core.material.sounds) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.sounds[main.nameMap[one]]; }));; + namesObj.allShops = Object.keys(core.status.shops); + namesObj.allFloorIds = core.floorIds; + namesObj.allColors = ["aqua(青色)", "black(黑色)", "blue(蓝色)", "fuchsia(品红色)", "gray(灰色)", "green(深绿色)", "lime(绿色)", + "maroon(深红色)", "navy(深蓝色)", "gold(金色)", "olive(黄褐色)", "orange(橙色)", "purple(品红色)", + "red(红色)", "silver(淡灰色)", "teal(深青色)", "white(白色)", "yellow(黄色)"]; + namesObj.allFonts = [main.styles.font].concat(main.fonts); + namesObj.allDoors = ["this"].concat(Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e) + .map(function (key) { return maps_90f36752_8815_4be8_b32b_d7fad1d0542e[key]; }) + .filter(function (one) { return one.doorInfo != null; }) + .map(function (one) { return one.id; })); + namesObj.allEvents = Object.keys(core.events.commonEvent); + var filter = function (list, content) { + return list.filter(function (one) { + return one != content && one.startsWith(content); + }).sort(); + } + + // 对任意图块提供补全 + // 对怪物ID提供补全 + // 对道具ID进行补全 + // 对图片名进行补全 + // 对动画进行补全 + // 对音乐进行补全 + // 对音效进行补全 + // 对全局商店进行补全 + // 对楼层名进行补全 + for (var ii = 0, names; names = ['allIds', 'allEnemys', 'allItems', 'allEquips', 'allImages', 'allAnimates', 'allBgms', 'allSounds', 'allShops', 'allFloorIds', 'allDoors', 'allEvents'][ii]; ii++) { + if (MotaActionBlocks[type][names] && eval(MotaActionBlocks[type][names]).indexOf(name) !== -1) { + return filter(namesObj[names], content); + } + } + + // 对\f进行自动补全 + index = Math.max(content.lastIndexOf("\f["), content.lastIndexOf("\\f[")); + if (index >= 0) { + if (content.charAt(index) == '\\') index++; + var after = content.substring(index + 2); + if (after.indexOf(",") < 0 && after.indexOf("]") < 0) { + return filter(namesObj.allImages, after); + } + } + + // 对\\i进行补全 + index = content.lastIndexOf("\\i["); + if (index >= 0) { + var after = content.substring(index + 3); + if (after.indexOf("]") < 0) { + return filter(namesObj.allIconIds, after); + } + } + + // 对\r进行补全 + index = Math.max(content.lastIndexOf("\r["), content.lastIndexOf("\\r[")); + if (index >= 0) { + if (content.charAt(index) == '\\') index++; + var after = content.substring(index + 2); + if (after.indexOf("]") < 0) { + return filter(namesObj.allColors, after); + } + } + + // 对\g进行补全 + index = content.lastIndexOf("\\g["); + if (index >= 0) { + var after = content.substring(index + 3); + if (after.indexOf("]") < 0) { + return filter(namesObj.allFonts, after); + } + } + + // 对\进行补全! + if (content.charAt(content.length - 1) == '\\') { + return ["n(换行)", "f(立绘)", "r(变色)", "i(图标)", "z(暂停打字)", "t(标题图标)", "b(对话框)", "c(字体大小)", "d(粗体)", "e(斜体)", "g(字体)"]; + } + + return []; + } + + editor_blockly.completeItems = []; + + editor_blockly.onTextFieldCreate = function (self, htmlInput) { + var pb = self.sourceBlock_ + var args = MotaActionBlocks[pb.type].args + var targetf = args[args.indexOf(self.name) + 1] + + // ------ colour + + if (targetf && targetf.slice(0, 7) === 'Colour_') { + var inputDom = htmlInput; + // var getValue=function(){ // 获得自己的字符串 + // return pb.getFieldValue(self.name); + // } + var setValue = function (newValue) { // 设置右边颜色块的css颜色 + pb.setFieldValue(newValue, targetf) + } + // 给inputDom绑事件 + inputDom.oninput = function () { + var value = inputDom.value + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(value)) { + setValue('rgba(' + value + ')') + } + } + } + else { + + htmlInput.onkeydown = function (e) { + if (e.keyCode == 13 && awesomplete.opened && awesomplete.selected) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + awesomplete.select(); + return false; + } + } + + // --- awesomplete + var awesomplete = new Awesomplete(htmlInput, { + minChars: 1, + maxItems: 12, + autoFirst: true, + replace: function (text) { + text = text.toString(); + var index = text.indexOf("("); + if (index >= 0) text = text.substring(0, index); + var value = this.input.value, index = this.input.selectionEnd; + if (index == null) index = value.length; + if (index < awesomplete.prefix.length) index = awesomplete.prefix.length; + var str = value.substring(0, index - awesomplete.prefix.length) + text + value.substring(index); + this.input.value = str; + pb.setFieldValue(str, self.name); + self.forceRerender(); + self.resizeEditor_(); + index += text.length - awesomplete.prefix.length; + this.input.setSelectionRange(index, index); + + editor_blockly.completeItems = editor_blockly.completeItems.filter(function (x) { + return x != text; + }); + editor_blockly.completeItems.unshift(text); + }, + filter: function () { return true; }, + item: function (text, input) { + var id = text.label, info = core.getBlockInfo(id); + var li = document.createElement("li"); + li.setAttribute("role", "option"); + li.setAttribute("aria-selected", "false"); + input = awesomplete.prefix.trim(); + if (input != "") text = text.replace(new RegExp("^" + input, "i"), "$&"); + li.innerHTML = text; + if (info) { + var height = (info.height || 32), width = 32; + var scale = 75; + height *= scale / 100; + width *= scale / 100; + var ctx = core.createCanvas('list_' + id, 0, 0, width, height), + canvas = ctx.canvas; + canvas.style.display = 'inline'; + canvas.style.marginRight = '8px'; + core.drawIcon(ctx, id, 0, 0, width, height); + canvas.style.position = ''; + li.insertBefore(canvas, li.children[0]); + } + return li; + }, + sort: function (a, b) { + a = a.toString(); b = b.toString(); + var ia = editor_blockly.completeItems.indexOf(a), ib = editor_blockly.completeItems.indexOf(b); + if (ia < 0) ia = editor_blockly.completeItems.length; + if (ib < 0) ib = editor_blockly.completeItems.length; + if (ia != ib) return ia - ib; + if (a.length != b.length) return a.length - b.length; + return a < b ? -1 : 1; + } + }); + + htmlInput.oninput = function () { + var value = htmlInput.value, index = htmlInput.selectionEnd; + if (index == null) index = value.length; + value = value.substring(0, index); + // cal prefix + awesomplete.prefix = value; + for (var i = index - 1; i >= 0; i--) { + var c = value.charAt(i); + if (!/^[a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]$/.test(c)) { + awesomplete.prefix = value.substring(i + 1); + break; + } + } + + var list = editor_blockly.getAutoCompletions(value, pb.type, self.name, pb); + + awesomplete.list = list; + var caretPosition = getCaretCoordinates(htmlInput, htmlInput.selectionStart); + awesomplete.ul.style.marginLeft = caretPosition.left - htmlInput.scrollLeft - 20 + "px"; + var totalHeight = parseFloat(Blockly.WidgetDiv.DIV.style.height.replace('px', '')); + awesomplete.ul.style.marginTop = caretPosition.top + caretPosition.height - totalHeight + 10 + 'px'; + awesomplete.evaluate(); + } + + awesomplete.container.style.width = "100%"; + + window.awesomplete = awesomplete; + } + } + + editor_blockly.isBlockCollapsedSupported = function (block) { + var supportedDisabledBlocks = [ + 'text_0_s', 'text_1_s', 'text_2_s', 'if_s', 'if_1_s', 'confirm_s', 'switch_s', 'choices_s', + 'for_s', 'forEach_s', 'while_s', 'dowhile_s', 'wait_s', 'previewUI_s', + 'waitContext_1', 'waitContext_2', 'waitContext_3', 'switchCase', 'choicesContext' + ]; + return supportedDisabledBlocks.indexOf(block.type || "") >= 0; + } + + return editor_blockly; +} + +// --- modify Blockly + +Blockly.FieldColour.prototype.showEditor_ = function () { + Blockly.WidgetDiv.hide(); + + // console.log('here') + var self = this; + var pb = self.sourceBlock_ + var args = MotaActionBlocks[pb.type].args + var targetf = args[args.indexOf(self.name) - 1] + + var getValue = function () { + // return self.getValue() // css颜色 + var f = pb.getFieldValue(targetf); + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(f)) { + return f; + } + return ""; + // 也可以用 pb.getFieldValue(targetf) 获得颜色块左边的域的内容 + } + + var setValue = function (newValue) { // css颜色 + self.setValue(newValue) + pb.setFieldValue(newValue.replace("rgba(", "").replace(")", ""), targetf) // 放在颜色块左边的域中 + } + + setTimeout(function () { + document.getElementById("colorPicker").value = getValue(); + // 设置位置 + var scaledBBox = self.getScaledBBox(); + openColorPicker(scaledBBox.left, scaledBBox.bottom, setValue); + }); + + return document.createElement('table'); +}; + +Blockly.FieldColour.prototype.setValue = function (colour) { + this.doValueUpdate_(colour); +} + +Blockly.FieldColour.prototype.initView = function () { + this.size_ = new Blockly.utils.Size( + this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH, + this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT); + if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) { + this.createBorderRect_(); + this.borderRect_.style['fillOpacity'] = '1'; + this.borderRect_.classList.add('blocklyColourFieldRect'); + } else { + this.clickTarget_ = this.sourceBlock_.getSvgRoot(); + } +}; + +Blockly.FieldTextInput.prototype.showInlineEditor_ = function (quietInput) { + Blockly.WidgetDiv.show( + this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); + this.htmlInput_ = this.widgetCreate_(); + this.isBeingEdited_ = true; + + editor_blockly.onTextFieldCreate(this, this.htmlInput_); + + if (!quietInput) { + this.htmlInput_.focus({ preventScroll: true }); + this.htmlInput_.select(); + } +}; + +Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function (e) { + if (e.keyCode == Blockly.utils.KeyCodes.ENTER && !(window.awesomplete && window.awesomplete.opened)) { + Blockly.WidgetDiv.hide(); + Blockly.DropDownDiv.hideWithoutAnimation(); + } else if (e.keyCode == Blockly.utils.KeyCodes.ESC) { + this.htmlInput_.value = this.htmlInput_.defaultValue; + Blockly.WidgetDiv.hide(); + Blockly.DropDownDiv.hideWithoutAnimation(); + } else if (e.keyCode == Blockly.utils.KeyCodes.TAB) { + Blockly.WidgetDiv.hide(); + Blockly.DropDownDiv.hideWithoutAnimation(); + this.sourceBlock_.tab(this, !e.shiftKey); + e.preventDefault(); + } +}; + +Blockly.FieldMultilineInput.prototype.showInlineEditor_ = function (quietInput) { + Blockly.FieldMultilineInput.superClass_.showInlineEditor_.call(this, quietInput); + // force to resize the input + this.htmlInput_.style.height = Blockly.WidgetDiv.DIV.style.height; +}; + +Blockly.FieldMultilineInput.prototype.onHtmlInputChange_ = function (e) { + Blockly.FieldMultilineInput.superClass_.onHtmlInputChange_.call(this, e); + // force to resize the input + this.htmlInput_.style.height = Blockly.WidgetDiv.DIV.style.height; +}; + +Blockly.copy_ = function (toCopy) { + if (toCopy.isComment) { + var xml = toCopy.toXmlWithXY(); + } else { + var xml = Blockly.Xml.blockToDom(toCopy, true); + // Copy only the selected block and internal blocks. + Blockly.Xml.deleteNext(xml); + // Encode start position in XML. + var xy = toCopy.getRelativeToSurfaceXY(); + xml.setAttribute('x', toCopy.RTL ? -xy.x : xy.x); + xml.setAttribute('oy', xy.y); + xml.setAttribute('sy', toCopy.workspace.scrollY); + } + Blockly.clipboardXml_ = xml; + Blockly.clipboardSource_ = toCopy.workspace; + Blockly.clipboardTypeCounts_ = toCopy.isComment ? null : + Blockly.utils.getBlockTypeCounts(toCopy, true); +}; + +/** + * Paste the provided block onto the workspace. + * @param {!Element} xmlBlock XML block element. + */ +Blockly.WorkspaceSvg.prototype.paste = function (xmlBlock) { + if (!this.rendered || xmlBlock.getElementsByTagName('block').length >= + this.remainingCapacity()) { + return; + } + if (this.currentGesture_) { + this.currentGesture_.cancel(); // Dragging while pasting? No. + } + if (xmlBlock.tagName.toLowerCase() == 'comment') { + this.pasteWorkspaceComment_(xmlBlock); + } else { + if (xmlBlock.hasAttribute('oy') && xmlBlock.hasAttribute('sy')) { + xmlBlock.setAttribute('y', parseFloat(xmlBlock.getAttribute('oy')) + parseFloat(xmlBlock.getAttribute('sy')) - this.scrollY); + } + this.pasteBlock_(xmlBlock); + } +}; + +// -- Support showing disabled blocks + +Blockly.Generator.prototype.blockToCode = function (block, opt_thisOnly) { + if (this.isInitialized === false) { + console.warn( + 'Generator init was not called before blockToCode was called.'); + } + if (!block) { + return ''; + } + if (!block.isEnabled() && !editor_blockly.isBlockCollapsedSupported(block)) { + // Skip past this block if it is disabled. + return opt_thisOnly ? '' : this.blockToCode(block.getNextBlock()); + } + if (block.isInsertionMarker()) { + // Skip past insertion markers. + return opt_thisOnly ? '' : this.blockToCode(block.getChildren(false)[0]); + } + + var func = this[block.type]; + if (typeof func != 'function') { + throw Error('Language "' + this.name_ + '" does not know how to generate ' + + 'code for block type "' + block.type + '".'); + } + // First argument to func.call is the value of 'this' in the generator. + // Prior to 24 September 2013 'this' was the only way to access the block. + // The current preferred method of accessing the block is through the second + // argument to func.call, which becomes the first parameter to the generator. + var code = func.call(block, block); + if (Array.isArray(code)) { + // Value blocks return tuples of code and operator order. + if (!block.outputConnection) { + throw TypeError('Expecting string from statement block: ' + block.type); + } + return [this.scrub_(block, code[0], opt_thisOnly), code[1]]; + } else if (typeof code == 'string') { + if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) { + code = this.injectId(this.STATEMENT_PREFIX, block) + code; + } + if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) { + code = code + this.injectId(this.STATEMENT_SUFFIX, block); + } + return this.scrub_(block, code, opt_thisOnly); + } else if (code === null) { + // Block has handled code generation itself. + return ''; + } + throw SyntaxError('Invalid code generated: ' + code); +}; + +Blockly.BlockSvg.prototype.generateContextMenu = function () { + if (this.workspace.options.readOnly || !this.contextMenu) { + return null; + } + // Save the current block in a variable for use in closures. + var block = this; + var menuOptions = []; + + if (!this.isInFlyout) { + // 删除 + if (this.isDeletable() && this.isMovable()) { + menuOptions.push(Blockly.ContextMenu.blockDuplicateOption(block)); + } + + if (editor_blockly.isBlockCollapsedSupported(this)) { + menuOptions.push({ + text: this.isCollapsed() ? Blockly.Msg['EXPAND_BLOCK'] : Blockly.Msg['COLLAPSE_BLOCK'], + enabled: true, + callback: function () { block.setCollapsed(!block.collapsed_); } + }); + + menuOptions.push({ + text: this.isEnabled() ? Blockly.Msg['DISABLE_BLOCK'] : Blockly.Msg['ENABLE_BLOCK'], + enabled: !this.getInheritedDisabled(), + callback: function () { + var group = Blockly.Events.getGroup(); + if (!group) { + Blockly.Events.setGroup(true); + } + block.setEnabled(!block.isEnabled()); + if (!group) { + Blockly.Events.setGroup(false); + } + } + }); + } + if (this.isDeletable()) { + menuOptions.push(Blockly.ContextMenu.blockDeleteOption(block)); + } + } + + menuOptions.push(Blockly.ContextMenu.blockHelpOption(block)); + if (this.customContextMenu) this.customContextMenu(menuOptions); + return menuOptions; +}; + +Blockly.FieldDropdown.prototype.doClassValidation_ = function (opt_newValue) { + return opt_newValue; +} + +Blockly.FieldDropdown.prototype.doValueUpdate_ = function (newValue) { + Blockly.FieldDropdown.superClass_.doValueUpdate_.call(this, newValue); + var options = this.getOptions(true); + for (var i = 0, option; (option = options[i]); i++) { + if (option[1] == this.value_) { + this.selectedOption_ = option; + } + } + if (this.selectedOption_[1] != this.value_) { + options.push([this.value_, this.value_]); + this.selectedOption_ = options[options.length - 1]; + } +}; + +Blockly.FieldMultilineInput.prototype.getDisplayText_ = function () { + var value = this.value_; + if (!value) return Blockly.Field.NBSP; + var curr = '', text = ''; + for (var i = 0; i < value.length; ++i) { + if (value[i] == '\n' || curr.length == this.maxDisplayLength) { + text += curr.replace(/\s/g, Blockly.Field.NBSP) + '\n'; + curr = value[i] == '\n' ? '' : value[i]; + } else curr += value[i]; + } + return text + curr; +}; diff --git a/editor_blocklyconfig.js b/editor_blocklyconfig.js new file mode 100644 index 0000000..be72015 --- /dev/null +++ b/editor_blocklyconfig.js @@ -0,0 +1,705 @@ +editor_blocklyconfig=(function(){ +// start mark sfergsvae + + + +(function(){ + var getCategory = function(name,custom){ + for(var node of document.getElementById('toolbox').children) { + if(node.getAttribute('name')==name) return node; + } + var node = document.createElement('category'); + node.setAttribute('name',name); + if(custom)node.setAttribute('custom',custom); + document.getElementById('toolbox').appendChild(node); + return node; + } + + var toolboxObj = { + '入口方块':[ + '', + MotaActionFunctions.actionParser.parse([ + "欢迎使用事件编辑器", + "本事件触发一次后会消失", + {"type": "hide", "time": 500}, + ],'event'), + MotaActionFunctions.actionParser.parse({ + "condition": "flag:__door__===2", + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + {"type": "openDoor", "loc": [10,5]} + ], + },'autoEvent'), + MotaActionBlocks['changeFloor_m'].xmlText(), + MotaActionFunctions.actionParser.parse([{ + "id": "shop1", + "text": "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", + "textInList": "1F金币商店", + "choices": [ + {"text": "生命+800", "need": "status:money>=20+2*flag:shop1", "action": [ + {"type": "comment", "text": "新版商店中需要手动扣减金币和增加访问次数"}, + {"type": "setValue", "name": "status:money", "operator": "-=", "value": "20+2*flag:shop1"}, + {"type": "setValue", "name": "flag:shop1", "operator": "+=", "value": "1"}, + {"type": "setValue", "name": "status:hp", "operator": "+=", "value": "800"} + ]} + ] + },{ + "id": "itemShop", + "item": true, + "textInList": "道具商店", + "choices": [ + {"id": "yellowKey", "number": 10, "money": 10} + ] + },{ + "id": "keyShop1", + "textInList": "回收钥匙商店", + "commonEvent": "回收钥匙商店", + "args": "" + }],'shop'), + MotaActionBlocks['common_m'].xmlText(), + MotaActionBlocks['beforeBattle_m'].xmlText(), + MotaActionBlocks['afterBattle_m'].xmlText(), + MotaActionBlocks['afterGetItem_m'].xmlText(), + MotaActionBlocks['afterOpenDoor_m'].xmlText(), + MotaActionBlocks['firstArrive_m'].xmlText(), + MotaActionBlocks['eachArrive_m'].xmlText(), + MotaActionBlocks['level_m'].xmlText(), + MotaActionFunctions.actionParser.parse([ + ['MTx', ''] + ], 'floorPartition'), + MotaActionBlocks['commonEvent_m'].xmlText(), + MotaActionBlocks['item_m'].xmlText(), + MotaActionFunctions.actionParser.parse([ + {"title":"简单", "name": "Easy", "hard": 1, "action": [ + {"type": "comment", "text": "在这里写该难度需执行的事件"} + ]} + ], 'levelChoose'), + MotaActionFunctions.actionParser.parse({ + "type": 0, "value": {"atk": 10}, "percentage": {"speed": 10}, + }, 'equip'), + MotaActionFunctions.actionParser.parse([{ + "name": "bg.jpg", "x": 0, "y": 0, "canvas": "bg" + }], 'floorImage'), + MotaActionFunctions.actionParser.parse({ + "time": 160, "openSound": "door.mp3", "closeSound": "door.mp3", "keys": {"yellowKey": 1, "orangeKey": 1} + }, 'doorInfo'), + MotaActionBlocks['faceIds_m'].xmlText(), + MotaActionBlocks['mainStyle_m'].xmlText(), + MotaActionFunctions.actionParser.parse({ + "背景音乐": "bgm.mp3", "确定": "confirm.mp3", "攻击": "attack.mp3", "背景图": "bg.jpg", "领域": "zone", "文件名": "file.jpg" + }, 'nameMap'), + MotaActionFunctions.actionParser.parse([ + {"name": "hero.png", "width": 32, "height": 32, "prefix": "hero_"}, + ], 'splitImages'), + ], + '显示文字':[ + MotaActionBlocks['text_0_s'].xmlText(), + MotaActionBlocks['text_1_s'].xmlText(), + MotaActionFunctions.actionParser.parseList("\t[小妖精,fairy]\f[fairy.png,0,0]欢迎使用事件编辑器(双击方块可直接预览)"), + MotaActionBlocks['moveTextBox_s'].xmlText(), + MotaActionBlocks['clearTextBox_s'].xmlText(), + MotaActionBlocks['comment_s'].xmlText(), + MotaActionBlocks['autoText_s'].xmlText(), + MotaActionBlocks['scrollText_s'].xmlText(), + MotaActionBlocks['setText_s'].xmlText(), + MotaActionBlocks['tip_s'].xmlText(), + MotaActionBlocks['confirm_s'].xmlText(), + MotaActionBlocks['choices_s'].xmlText([ + '选择剑或者盾','流浪者','man',0,'',MotaActionBlocks['choicesContext'].xmlText([ + '剑','','',null,'','',MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), + ]) + ]), + MotaActionBlocks['win_s'].xmlText(), + MotaActionBlocks['lose_s'].xmlText(), + MotaActionBlocks['restart_s'].xmlText(), + ], + '数据相关':[ + MotaActionBlocks['setValue_s'].xmlText([ + MotaActionBlocks['idIdList_e'].xmlText(['status','生命']), '=', '', false + ]), + MotaActionBlocks['setEnemy_s'].xmlText(), + MotaActionBlocks['setEnemyOnPoint_s'].xmlText(), + MotaActionBlocks['resetEnemyOnPoint_s'].xmlText(), + MotaActionBlocks['moveEnemyOnPoint_s'].xmlText(), + MotaActionBlocks['moveEnemyOnPoint_1_s'].xmlText(), + MotaActionBlocks['setEquip_s'].xmlText(), + MotaActionBlocks['setFloor_s'].xmlText(), + MotaActionBlocks['setGlobalAttribute_s'].xmlText(), + MotaActionBlocks['setGlobalValue_s'].xmlText(), + MotaActionBlocks['setGlobalFlag_s'].xmlText(), + MotaActionBlocks['setNameMap_s'].xmlText(), + MotaActionBlocks['input_s'].xmlText(), + MotaActionBlocks['input2_s'].xmlText(), + MotaActionBlocks['update_s'].xmlText(), + MotaActionBlocks['moveAction_s'].xmlText(), + MotaActionBlocks['changeFloor_s'].xmlText(), + MotaActionBlocks['changePos_s'].xmlText(), + MotaActionBlocks['battle_s'].xmlText(), + MotaActionBlocks['useItem_s'].xmlText(), + MotaActionBlocks['loadEquip_s'].xmlText(), + MotaActionBlocks['unloadEquip_s'].xmlText(), + MotaActionBlocks['openShop_s'].xmlText(), + MotaActionBlocks['disableShop_s'].xmlText(), + MotaActionBlocks['setHeroIcon_s'].xmlText(), + MotaActionBlocks['follow_s'].xmlText(), + MotaActionBlocks['unfollow_s'].xmlText(), + ], + '地图处理':[ + MotaActionBlocks['battle_1_s'].xmlText(), + MotaActionBlocks['openDoor_s'].xmlText(), + MotaActionBlocks['closeDoor_s'].xmlText(), + MotaActionBlocks['show_s'].xmlText(), + MotaActionBlocks['hide_s'].xmlText(), + MotaActionBlocks['setBlock_s'].xmlText(), + MotaActionBlocks['setBlockOpacity_s'].xmlText(), + MotaActionBlocks['setBlockFilter_s'].xmlText(), + MotaActionBlocks['turnBlock_s'].xmlText(), + MotaActionBlocks['moveHero_s'].xmlText(), + MotaActionBlocks['move_s'].xmlText(), + MotaActionBlocks['jumpHero_s'].xmlText(), + MotaActionBlocks['jumpHero_1_s'].xmlText(), + MotaActionBlocks['jump_s'].xmlText(), + MotaActionBlocks['jump_1_s'].xmlText(), + MotaActionBlocks['showBgFgMap_s'].xmlText(), + MotaActionBlocks['hideBgFgMap_s'].xmlText(), + MotaActionBlocks['setBgFgBlock_s'].xmlText(), + MotaActionBlocks['showFloorImg_s'].xmlText(), + MotaActionBlocks['hideFloorImg_s'].xmlText(), + ], + '事件控制':[ + MotaActionBlocks['if_1_s'].xmlText(), + MotaActionBlocks['if_s'].xmlText(), + MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [ + {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, + {"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]}, + ]}), + MotaActionFunctions.actionParser.parseList({"type": "for", "name": "temp:A", "from": "0", "to": "12", "step": "1", "data": []}), + MotaActionFunctions.actionParser.parseList({"type": "forEach", "name": "temp:A", "list": ["status:atk","status:def"], "data": []}), + MotaActionBlocks['while_s'].xmlText(), + MotaActionBlocks['dowhile_s'].xmlText(), + MotaActionBlocks['break_s'].xmlText(), + MotaActionBlocks['continue_s'].xmlText(), + MotaActionBlocks['exit_s'].xmlText(), + MotaActionBlocks['trigger_s'].xmlText(), + MotaActionBlocks['insert_1_s'].xmlText(), + MotaActionBlocks['insert_2_s'].xmlText(), + ], + '特效表现':[ + MotaActionBlocks['sleep_s'].xmlText(), + MotaActionFunctions.actionParser.parseList({"type": "wait", "timeout": 0, "data": [ + {"case": "keyboard", "keycode": "13,32", "action": [{"type": "comment", "text": "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout"}]}, + {"case": "mouse", "px": [0,32], "py": [0,32], "action": [{"type": "comment", "text": "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout"}]}, + {"case": "condition", "condition": "flag:type==0\n&&flag:keycode==13", "action": [{"type": "comment", "text": "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout"}]}, + {"case": "timeout", "action": [{"type": "comment", "text": "当超时未操作时执行此事件"}]}, + ]}), + MotaActionBlocks['waitAsync_s'].xmlText(), + MotaActionBlocks['stopAsync_s'].xmlText(), + MotaActionBlocks['vibrate_s'].xmlText(), + MotaActionBlocks['animate_s'].xmlText(), + MotaActionBlocks['animate_1_s'].xmlText(), + MotaActionBlocks['stopAnimate_s'].xmlText(), + MotaActionBlocks['setViewport_s'].xmlText(), + MotaActionBlocks['setViewport_1_s'].xmlText(), + MotaActionBlocks['lockViewport_s'].xmlText(), + MotaActionBlocks['showStatusBar_s'].xmlText(), + MotaActionBlocks['hideStatusBar_s'].xmlText(), + MotaActionBlocks['setHeroOpacity_s'].xmlText(), + MotaActionBlocks['setCurtain_0_s'].xmlText(), + MotaActionBlocks['setCurtain_1_s'].xmlText(), + MotaActionBlocks['screenFlash_s'].xmlText(), + MotaActionBlocks['setWeather_s'].xmlText(), + MotaActionBlocks['callBook_s'].xmlText(), + MotaActionBlocks['callSave_s'].xmlText(), + MotaActionBlocks['autoSave_s'].xmlText(), + MotaActionBlocks['forbidSave_s'].xmlText(), + MotaActionBlocks['callLoad_s'].xmlText(), + ], + '音像处理':[ + MotaActionBlocks['showImage_s'].xmlText(), + MotaActionBlocks['showImage_1_s'].xmlText(), + MotaActionBlocks['hideImage_s'].xmlText(), + MotaActionBlocks['showTextImage_s'].xmlText(), + MotaActionBlocks['moveImage_s'].xmlText(), + MotaActionBlocks['rotateImage_s'].xmlText(), + MotaActionBlocks['scaleImage_s'].xmlText(), + MotaActionBlocks['showGif_s'].xmlText(), + MotaActionBlocks['playBgm_s'].xmlText(), + MotaActionBlocks['pauseBgm_s'].xmlText(), + MotaActionBlocks['resumeBgm_s'].xmlText(), + MotaActionBlocks['loadBgm_s'].xmlText(), + MotaActionBlocks['freeBgm_s'].xmlText(), + MotaActionBlocks['playSound_s'].xmlText(), + MotaActionBlocks['playSound_1_s'].xmlText(), + MotaActionBlocks['stopSound_s'].xmlText(), + MotaActionBlocks['setVolume_s'].xmlText(), + MotaActionBlocks['setBgmSpeed_s'].xmlText(), + ], + 'UI绘制':[ + MotaActionBlocks['previewUI_s'].xmlText(), + MotaActionBlocks['clearMap_s'].xmlText(), + MotaActionBlocks['setAttribute_s'].xmlText(), + MotaActionBlocks['setFilter_s'].xmlText(), + MotaActionBlocks['fillText_s'].xmlText(), + MotaActionBlocks['fillBoldText_s'].xmlText(), + MotaActionBlocks['drawTextContent_s'].xmlText(), + MotaActionBlocks['fillRect_s'].xmlText(), + MotaActionBlocks['strokeRect_s'].xmlText(), + MotaActionBlocks['drawLine_s'].xmlText(), + MotaActionBlocks['drawArrow_s'].xmlText(), + MotaActionBlocks['fillPolygon_s'].xmlText(), + MotaActionBlocks['strokePolygon_s'].xmlText(), + MotaActionBlocks['fillEllipse_s'].xmlText(), + MotaActionBlocks['strokeEllipse_s'].xmlText(), + MotaActionBlocks['fillArc_s'].xmlText(), + MotaActionBlocks['strokeArc_s'].xmlText(), + MotaActionBlocks['drawImage_s'].xmlText(), + MotaActionBlocks['drawImage_1_s'].xmlText(), + MotaActionBlocks['drawIcon_s'].xmlText(), + MotaActionBlocks['drawBackground_s'].xmlText(), + MotaActionBlocks['drawSelector_s'].xmlText(), + MotaActionBlocks['drawSelector_1_s'].xmlText(), + ], + '原生脚本':[ + MotaActionBlocks['function_s'].xmlText(), + MotaActionBlocks['unknown_s'].xmlText(), + ], + '值块':[ + MotaActionBlocks['setValue_s'].xmlText([ + MotaActionBlocks['idIdList_e'].xmlText(['status','生命']), '=', '', false + ]), + MotaActionBlocks['expression_arithmetic_0'].xmlText(), + MotaActionBlocks['idFlag_e'].xmlText(), + MotaActionBlocks['idTemp_e'].xmlText(), + MotaActionBlocks['negate_e'].xmlText(), + MotaActionBlocks['unaryOperation_e'].xmlText(), + MotaActionBlocks['bool_e'].xmlText(), + MotaActionBlocks['idString_e'].xmlText(), + MotaActionBlocks['idIdList_e'].xmlText(), + MotaActionBlocks['idFixedList_e'].xmlText(), + MotaActionBlocks['enemyattr_e'].xmlText(), + MotaActionBlocks['blockId_e'].xmlText(), + MotaActionBlocks['blockNumber_e'].xmlText(), + MotaActionBlocks['blockCls_e'].xmlText(), + MotaActionBlocks['hasEquip_e'].xmlText(), + MotaActionBlocks['equip_e'].xmlText(), + MotaActionBlocks['nextXY_e'].xmlText(), + MotaActionBlocks['isReplaying_e'].xmlText(), + MotaActionBlocks['hasVisitedFloor_e'].xmlText(), + MotaActionBlocks['isShopVisited_e'].xmlText(), + MotaActionBlocks['canBattle_e'].xmlText(), + MotaActionBlocks['damage_e'].xmlText(), + MotaActionBlocks['damage_1_e'].xmlText(), + MotaActionBlocks['rand_e'].xmlText(), + MotaActionBlocks['evalString_e'].xmlText(), + ], + '常见事件模板':[ + '', + MotaActionFunctions.actionParser.parseList({"type": "if", "condition": "!core.musicStatus.bgmStatus", + "true": [ + "\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳" + ], + "false": [] + }), + '', + MotaActionFunctions.actionParser.parse([ + { + "type": "if", + "condition": "switch:A", + "true": [ + "\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。", + { + "type": "comment", + "text": "下一条指令可视情况使用或不使用" + }, + { + "type": "hide", + "remove": true, + "time": 250 + } + ], + "false": [ + { + "type": "confirm", + "text": "我有3把黄钥匙,\n你出50金币就卖给你。", + "yes": [ + { + "type": "if", + "condition": "status:money>=50", + "true": [ + { + "type": "setValue", + "name": "status:money", + "operator": "-=", + "value": "50" + }, + { + "type": "setValue", + "name": "item:yellowKey", + "operator": "+=", + "value": "3" + }, + { + "type": "playSound", + "name": "确定", + "stop": true + }, + { + "type": "setValue", + "name": "switch:A", + "value": "true" + } + ], + "false": [ + { + "type": "playSound", + "name": "操作失败" + }, + "\t[行商,trader]\b[this]你的金币不足!" + ] + } + ], + "no": [] + } + ] + } + ], 'event'), + '', + MotaActionFunctions.actionParser.parse([ + { + "type": "comment", + "text": "全地图选中一个点,需要用鼠标或触屏操作" + }, + { + "type": "setValue", + "name": "temp:X", + "value": "status:x" + }, + { + "type": "setValue", + "name": "temp:Y", + "value": "status:y" + }, + { + "type": "tip", + "text": "再次点击闪烁位置确认" + }, + { + "type": "while", + "condition": "true", + "data": [ + { + "type": "drawSelector", + "image": "winskin.png", + "code": 1, + "x": "32*temp:X", + "y": "32*temp:Y", + "width": 32, + "height": 32 + }, + { + "type": "wait" + }, + { + "type": "if", + "condition": "(flag:type === 1)", + "true": [ + { + "type": "if", + "condition": "((temp:X===flag:x)&&(temp:Y===flag:y))", + "true": [ + { + "type": "break", + "n": 1 + } + ] + }, + { + "type": "setValue", + "name": "temp:X", + "value": "flag:x" + }, + { + "type": "setValue", + "name": "temp:Y", + "value": "flag:y" + } + ] + } + ] + }, + { + "type": "drawSelector", + "code": 1 + }, + { + "type": "comment", + "text": "流程进行到这里可以对[X,Y]点进行处理,比如" + }, + { + "type": "closeDoor", + "id": "yellowDoor", + "loc": [ + "temp:X", + "temp:Y" + ] + } + ],'event'), + '', + MotaActionFunctions.actionParser.parse([ + { + "type": "comment", + "text": "多阶段boss,请直接作为战后事件使用" + }, + { + "type": "setValue", + "name": "switch:A", + "operator": "+=", + "value": "1" + }, + { + "type": "switch", + "condition": "switch:A", + "caseList": [ + { + "case": "1", + "action": [ + { + "type": "setBlock", + "number": "redSlime" + }, + "\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗?没听说过史莱姆有九条命吗?" + ] + }, + { + "case": "2", + "action": [ + { + "type": "setBlock", + "number": "blackSlime" + }, + "\t[3阶段boss,blackSlime]\b[this]不能消灭我的,只会让我更强大!" + ] + }, + { + "case": "3", + "action": [ + { + "type": "setBlock", + "number": "slimelord" + }, + "\t[4阶段boss,slimelord]\b[this]我还能打!" + ] + }, + { + "case": "4", + "action": [ + "\t[4阶段boss,slimelord]我一定会回来的!" + ] + } + ] + } + ],'afterBattle'), + ], + '最近使用事件':[ + '', + ] + } + var toolboxgap = '' + //xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event') + //MotaActionBlocks['idString_e'].xmlText() + + for (var name in toolboxObj){ + var custom = null; + if(name=='最近使用事件')custom='searchBlockCategory'; + if(name=='入口方块')custom='entranceCategory'; + getCategory(name,custom).innerHTML = toolboxObj[name].join(toolboxgap); + } + +var blocklyArea = document.getElementById('blocklyArea'); +var blocklyDiv = document.getElementById('blocklyDiv'); +var workspace = Blockly.inject(blocklyDiv,{ + media: '_server/blockly/media/', + toolbox: document.getElementById('toolbox'), + zoom:{ + controls: true, + wheel: false,//滚轮改为上下(shift:左右)翻滚 + startScale: 1.0, + maxScale: 3, + minScale: 0.3, + scaleSpeed: 1.08 + }, + trashcan: false, +}); + +editor_blockly.isCommonEntry = function () { + var commonEntries = ['beforeBattle', 'afterBattle', 'afterOpenDoor', 'firstArrive', 'eachArrive', 'commonEvent', 'item']; + return commonEntries.indexOf(editor_blockly.entryType) >= 0; +} + +editor_blockly.entranceCategoryCallback = function(workspace) { + var list=toolboxObj['入口方块'] + var xmlList = []; + var eventType = (editor_blockly.isCommonEntry() ? 'common' : editor_blockly.entryType)+'_m'; + for(var ii=0,blockText;blockText=list[ii];ii++){ + if(new RegExp('').exec(blockText)){ + var block = Blockly.Xml.textToDom(''+blockText+'').firstChild; + block.setAttribute("gap", 5); + xmlList.push(block); + } + } + return xmlList; +} + +workspace.registerToolboxCategoryCallback( + 'entranceCategory', editor_blockly.entranceCategoryCallback); + +editor_blockly.searchBlockCategoryCallback = function(workspace) { + var xmlList = []; + var labels = editor_blockly.searchBlock(); + for (var i = 0; i < labels.length; i++) { + var blockText = '' + + MotaActionBlocks[labels[i]].xmlText() + + ''; + var block = Blockly.Xml.textToDom(blockText).firstChild; + block.setAttribute("gap", 5); + xmlList.push(block); + } + return xmlList; +}; + +workspace.registerToolboxCategoryCallback( + 'searchBlockCategory', editor_blockly.searchBlockCategoryCallback); + +var onresize = function(e) { + blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; + blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; + Blockly.svgResize(workspace); +}; +if(typeof editor !== "undefined" && !editor.isMobile)window.addEventListener('resize', onresize, false); +onresize(); +//Blockly.svgResize(workspace); + +//Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){}); +document.getElementById('blocklyDiv').onmousewheel = function(e){ + //console.log(e); + e.preventDefault(); + var hvScroll = e.shiftKey?'hScroll':'vScroll'; + var mousewheelOffsetValue=20/380*workspace.scrollbar[hvScroll].handleLength_*3; + workspace.scrollbar[hvScroll].handlePosition_+=( ((e.deltaY||0)+(e.detail||0)) >0?mousewheelOffsetValue:-mousewheelOffsetValue); + workspace.scrollbar[hvScroll].onScroll_(); + // workspace.setScale(workspace.scale); +} + +var doubleClickCheck=[[0,'abc']]; +function omitedcheckUpdateFunction(event) { + if(event.type==='create'){ + editor_blockly.addIntoLastUsedType(event.blockId); + } + if(event.type==='ui' && event.element == 'click'){ + var newClick = [new Date().getTime(),event.blockId]; + var lastClick = doubleClickCheck.shift(); + doubleClickCheck.push(newClick); + if(newClick[0]-lastClick[0]<500){ + if(newClick[1]===lastClick[1]){ + editor_blockly.doubleClickBlock(newClick[1]); + } + } + } + // Only handle these events + if (["create", "move", "change", "delete"].indexOf(event.type) < 0) return; + if(editor_blockly.workspace.topBlocks_.length>=2){ + editor_blockly.setValue('入口方块只能有一个'); + return; + } + var eventType = editor_blockly.entryType; + if(editor_blockly.workspace.topBlocks_.length==1){ + var blockType = editor_blockly.workspace.topBlocks_[0].type; + if(blockType!==eventType+'_m' && !(editor_blockly.isCommonEntry() && blockType == 'common_m')){ + editor_blockly.setValue('入口方块类型错误'); + return; + } + } + try { + var code = Blockly.JavaScript.workspaceToCode(workspace).replace(/\\(i|c|d|e|g|z)/g, '\\\\$1'); + editor_blockly.setValue(code); + } catch (error) { + editor_blockly.setValue(String(error)); + if (error instanceof OmitedError){ + var blockName = error.blockName; + var varName = error.varName; + var block = error.block; + } + // console.log(error); + } + } + + workspace.addChangeListener(omitedcheckUpdateFunction); + + workspace.addChangeListener(Blockly.Events.disableOrphans); + + editor_blockly.workspace = workspace; + + MotaActionFunctions.workspace = function(){ + return editor_blockly.workspace; + } + + // 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现<等 + MotaActionFunctions.xmlText = function (ruleName,inputs,isShadow,comment,collapsed,disabled) { + var rule = MotaActionBlocks[ruleName]; + var blocktext = isShadow?'shadow':'block'; + var xmlText = []; + xmlText.push('<'+blocktext+' type="'+ruleName+'"'+(collapsed ? ' collapsed="true"' : '')+(disabled ? ' disabled="true"' : '')+'>'); + if(!inputs)inputs=[]; + for (var ii=0,inputType;inputType=rule.argsType[ii];ii++) { + var input = inputs[ii]; + var _input = ''; + var noinput = (input===null || input===undefined); + if(noinput && inputType==='field' && MotaActionBlocks[rule.argsGrammarName[ii]].type!=='field_dropdown') continue; + if(noinput && inputType==='field') { + noinput = false; + input = rule.fieldDefault(rule.args[ii]) + } + if(noinput) input = ''; + if(inputType==='field' && MotaActionBlocks[rule.argsGrammarName[ii]].type==='field_checkbox')input=input?'TRUE':'FALSE'; + if(inputType!=='field') { + var subList = false; + var subrulename = rule.argsGrammarName[ii]; + var subrule = MotaActionBlocks[subrulename]; + if (subrule instanceof Array) { + subrulename=subrule[subrule.length-1]; + subrule = MotaActionBlocks[subrulename]; + subList = true; + } + _input = subrule.xmlText([],true); + if(noinput && !subList && !isShadow) { + //无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块 + input = subrule.xmlText(); + } + } + xmlText.push('<'+inputType+' name="'+rule.args[ii]+'">'); + xmlText.push(_input+input); + xmlText.push(''); + } + if(comment){ + xmlText.push(''); + xmlText.push(comment); + xmlText.push(''); + } + var next = inputs[rule.args.length]; + if (next) {//next + xmlText.push(''); + xmlText.push(next); + xmlText.push(''); + } + xmlText.push(''); + return xmlText.join(''); + } +})(); + + + +// end mark sfergsvae +}).toString().split('// start mark sfergsvae')[1].split('// end mark sfergsvae')[0] diff --git a/editor_config.js b/editor_config.js new file mode 100644 index 0000000..6a8a9e9 --- /dev/null +++ b/editor_config.js @@ -0,0 +1,52 @@ +function editor_config() { + this.address = "_server/config.json"; + this._isWriting = false; +} + +editor_config.prototype.load = function(callback) { + var _this = this; + fs.readFile(this.address, "utf-8", function(e, d) { + if (e) { + console.error("无法读取配置文件, 已重新生成"); + _this.config = {}; + _this.save(callback); + } else { + try { + _this.config = JSON.parse(d); + if (callback) callback(); + } catch (e) { + console.error(e); + _this.config = {}; + _this.save(callback); + } + } + }); +} + +editor_config.prototype.get = function(key, defaultValue) { + value = this.config[key]; + return value != null ? value : defaultValue; +} + +editor_config.prototype.set = function(key, value, callback) { + this.config[key] = value; + if (callback !== false) this.save(callback); +} + +editor_config.prototype.save = function(callback) { + // 读写锁防止写文件冲突 + if (this._isWriting) return; + try { + this._isWriting = true; + var _this = this; + fs.writeFile(this.address, JSON.stringify(this.config) ,'utf-8', function(e) { + _this._isWriting = false; + if (e) console.error("写入配置文件失败"); + if (callback instanceof Function) callback(); + }) + } catch (e) { + this._isWriting = false; + console.error(e); + if (callback instanceof Function) callback(); + } +} diff --git a/editor_datapanel.js b/editor_datapanel.js new file mode 100644 index 0000000..075a067 --- /dev/null +++ b/editor_datapanel.js @@ -0,0 +1,1228 @@ +editor_datapanel_wrapper = function (editor) { + + // 此文件内的内容仅做了分类, 未仔细整理函数 + + /////////////////////////////////////////////////////////////////////// + //////////////////// 地图编辑 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + var pout = document.getElementById('pout'); + var exportMap = document.getElementById('exportMap'); + var importMap = document.getElementById('importMap'); + var clearMapButton=document.getElementById('clearMapButton') + var deleteMap=document.getElementById('deleteMap') + + var formatArr= function () { + var formatArrStr = ''; + + var si=editor.map.length,sk=editor.map[0].length; + if (pout.value.split(/\D+/).join(' ').trim().split(' ').length != si*sk) return false; + var arr = pout.value.replace(/\s+/g, '').split('],['); + + if (arr.length != si) return; + for (var i = 0; i < si; i++) { + var a = []; + formatArrStr += '['; + if (i == 0 || i == si-1) a = arr[i].split(/\D+/).join(' ').trim().split(' '); + else a = arr[i].split(/\D+/); + if (a.length != sk) { + formatArrStr = ''; + return; + } + + for (var k = 0; k < sk; k++) { + var num = parseInt(a[k]); + formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk-1 ? '' : ','); + } + formatArrStr += ']' + (i == si-1 ? '' : ',\n'); + } + return formatArrStr; + } + + exportMap.onclick=function(){ + editor.updateMap(); + var sx=editor.map.length-1,sy=editor.map[0].length-1; + + var filestr = ''; + for (var yy = 0; yy <= sy; yy++) { + filestr += '[' + for (var xx = 0; xx <= sx; xx++) { + var mapxy = editor.map[yy][xx]; + if (typeof(mapxy) == typeof({})) { + if ('idnum' in mapxy) mapxy = mapxy.idnum; + else { + printe("生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!"); + return; + } + } else if (typeof(mapxy) == 'undefined') { + printe("生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!"); + return; + } + mapxy = String(mapxy); + mapxy = Array(Math.max(4 - mapxy.length, 0)).join(' ') + mapxy; + filestr += mapxy + (xx == sx ? '' : ',') + } + + filestr += ']' + (yy == sy ? '' : ',\n'); + } + pout.value = filestr; + if (formatArr()) { + pout.focus(); + pout.setSelectionRange(0, pout.value.length); + document.execCommand("Copy"); + printf("导出并复制成功!"); + } else { + printe("无法导出并复制此地图,可能有不合法块。") + } + } + importMap.onclick= function () { + var sy=editor.map.length,sx=editor.map[0].length; + var mapArray = null; + var value = pout.value.trim(); + // 去除可能末尾的 ',' + if (value.endsWith(',')) value = value.substring(0, value.length - 1); + try { mapArray = JSON.parse(value); } catch (e) {console.log(e)} + try { mapArray = mapArray || JSON.parse('[' + value + ']'); } catch (e) {console.log(e)} + if (mapArray == null || mapArray.length != sy || mapArray[0].length != sx) { + printe('格式错误!请使用正确格式(请使用地图生成器进行生成,且需要和本地图宽高完全一致)'); + return; + } + var hasError = false; + for (var y = 0; y < sy; y++) + for (var x = 0; x < sx; x++) { + var num = mapArray[y][x]; + if (num == 0) + editor.map[y][x] = 0; + else if (editor.indexs[num]==null || editor.indexs[num][0]==null) { + printe('当前有未定义ID(在地图区域显示红块),请用有效的图块进行覆盖!') + hasError = true; + editor.map[y][x] = {}; + } else editor.map[y][x] = editor.ids[[editor.indexs[num][0]]]; + } + editor.updateMap(); + if (!hasError) printf('地图导入成功!'); + } + + clearMapButton.onclick=function () { + if (!confirm('你确定要清除地图上所有内容么?此过程不可逆!')) return; + editor.mapInit(); + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('地图清除成功'); + }); + editor.updateMap(); + } + + deleteMap.onclick=function () { + if (!confirm('你确定要删除此地图么?此过程不可逆!')) return; + editor_mode.onmode(''); + var index = core.floorIds.indexOf(editor.currentFloorId); + if (index>=0) { + core.floorIds.splice(index,1); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw(objs_.slice(-1)[0]) + } + ;printe('删除成功,请F5刷新编辑器生效'); + }); + } + else printe('删除成功,请F5刷新编辑器生效'); + } + + editor.uifunctions.newMap_func = function () { + + var newMap = document.getElementById('newMap'); + var newFileName = document.getElementById('newFileName'); + newMap.onclick = function () { + if (!newFileName.value) return; + var findFunc = function (id) { + var re = new RegExp(newFileName.value, 'i'); + return re.test(id); + } + if (core.floorIds.find(findFunc) != null) { + printe("同名楼层已存在!(不区分大小写)"); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(newFileName.value)) { + printe("楼层名不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + var width = parseInt(document.getElementById('newMapWidth').value); + var height = parseInt(document.getElementById('newMapHeight').value); + if (!core.isset(width) || !core.isset(height) || width > 128 || height > 128) { + printe("新建地图的宽高都不得大于128"); + return; + } + + editor_mode.onmode(''); + editor.file.saveNewFile(newFileName.value, function (err) { + if (err) { + printe(err); + throw (err) + } + core.floorIds.push(newFileName.value); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('新建成功,请F5刷新编辑器生效'); + }); + }); + } + + } + + + editor.uifunctions.createNewMaps_func = function () { + + document.getElementById('newMapWidth').value = core.__SIZE__; + document.getElementById('newMapHeight').value = core.__SIZE__; + document.getElementById('newMapsWidth').value = core.__SIZE__; + document.getElementById('newMapsHeight').value = core.__SIZE__; + + var newMaps = document.getElementById('newMaps'); + var newFloors = document.getElementById('newFloors'); + newMaps.onclick = function () { + if (newFloors.style.display == 'none') newFloors.style.display = 'block'; + else newFloors.style.display = 'none'; + } + + var createNewMaps = document.getElementById('createNewMaps'); + createNewMaps.onclick = function () { + var floorIds = document.getElementById('newFloorIds').value; + if (!floorIds) return; + var from = parseInt(document.getElementById('newMapsFrom').value), + to = parseInt(document.getElementById('newMapsTo').value); + if (!core.isset(from) || !core.isset(to) || from > to) { + printe("请输入有效的起始和终止楼层"); + return; + } + if (to - from >= 100) { + printe("一次最多创建99个楼层"); + return; + } + var floorIdList = []; + for (var i = from; i <= to; i++) { + var floorId = floorIds.replace(/\${(.*?)}/g, function (word, value) { + return eval(value); + }); + var findFunc = function (id) { + var re = new RegExp(floorId, 'i'); + return re.test(id); + } + if (core.floorIds.find(findFunc) != null) { + printe("同名楼层已存在!(不区分大小写)"); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { + printe("楼层名 " + floorId + " 不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + if (floorIdList.indexOf(floorId) >= 0) { + printe("尝试重复创建楼层 " + floorId + " !"); + return; + } + floorIdList.push(floorId); + } + + var width = parseInt(document.getElementById('newMapsWidth').value); + var height = parseInt(document.getElementById('newMapsHeight').value); + if (!core.isset(width) || !core.isset(height) || width > 128 || height > 128) { + printe("新建地图的宽高都不得大于128"); + return; + } + editor_mode.onmode(''); + + editor.file.saveNewFiles(floorIdList, from, to, function (err) { + if (err) { + printe(err); + throw (err) + } + core.floorIds = core.floorIds.concat(floorIdList); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('批量创建 ' + floorIdList[0] + '~' + floorIdList[floorIdList.length - 1] + ' 成功,请F5刷新编辑器生效'); + }); + }); + } + + } + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 地图选点 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + // 添加自动事件页,无需双击 + editor.uifunctions.addAutoEvent = function () { + if (editor_mode.mode != 'loc') return false; + var newid = '2'; + var ae = editor.currentFloorData.autoEvent[editor_mode.pos.x + ',' + editor_mode.pos.y]; + if (ae != null) { + var testid; + for (testid = 2; Object.hasOwnProperty.call(ae, testid); testid++); + newid = testid + ''; + } + editor_mode.addAction(['add', "['autoEvent']['" + newid + "']", null]); + editor_mode.onmode('save'); + } + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 图块属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + editor.uifunctions.newIdIdnum_func = function () { + var newIdIdnum = document.getElementById('newIdIdnum'); + newIdIdnum.children[2].onclick = function () { + if (newIdIdnum.children[0].value && newIdIdnum.children[1].value) { + var id = newIdIdnum.children[0].value; + var idnum = parseInt(newIdIdnum.children[1].value); + if (!core.isset(idnum)) { + printe('不合法的idnum'); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id)) { + printe('不合法的id,请使用字母、数字或下划线,且不能以数字开头'); + return; + } + if (id == 'hero' || id == 'this' || id == 'none' || id == 'airwall') { + printe('不得使用保留关键字作为id!'); + return; + } + if (core.statusBar.icons[id] != null) { + alert('警告!此ID在状态栏图标中被注册;仍然允许使用,但是\\i[]等绘制可能出现冲突。'); + } + editor.file.changeIdAndIdnum(id, idnum, editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err) + } + printe('添加id和idnum成功,请F5刷新编辑器'); + }); + } else { + printe('请输入id和idnum'); + } + } + newIdIdnum.children[4].onclick = function () { + editor.file.autoRegister(editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err) + } + printe('该列所有剩余项全部自动注册成功,请F5刷新编辑器'); + }) + } + newIdIdnum.children[5].onclick = function () { + if (!confirm("警告!你确定要删除此素材吗?此过程不可逆!")) return; + editor.file.removeMaterial(editor_mode.info, function (err) { + if (err) { + printe(err); + throw err; + } + alert('删除此素材成功!'); + window.location.reload(); + }); + } + newIdIdnum.children[6].onclick = function () { + editor.uifunctions.appendMaterialByInfo(editor_mode.info); + } + } + + editor.uifunctions.changeId_func = function () { + var changeId = document.getElementById('changeId'); + changeId.children[1].onclick = function () { + var id = changeId.children[0].value; + if (id) { + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id)) { + printe('不合法的id,请使用字母、数字或下划线,且不能以数字开头') + return; + } + if (id == 'hero' || id == 'this' || id == 'none' || id == 'airwall') { + printe('不得使用保留关键字作为id!'); + return; + } + if (editor_mode.info.images == 'autotile') { + printe('自动元件不可修改id!'); + return; + } + if (editor_mode.info.idnum >= 10000) { + printe('额外素材不可修改id!'); + return; + } + if (core.statusBar.icons[id] != null) { + alert('警告!此ID在状态栏图标中被注册;仍然允许使用,但是\\i[]等绘制可能出现冲突。'); + } + editor.file.changeIdAndIdnum(id, null, editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err); + } + printe('修改id成功,请F5刷新编辑器'); + }); + } else { + printe('请输入要修改到的ID'); + } + } + changeId.children[2].onclick = function () { + if (editor_mode.info.isTile) { + printe("额外素材不可删除!"); + return; + } + if (!confirm("警告!你确定要删除此素材吗?此过程不可逆!\n请务必首先进行备份操作,并保证此素材没有在地图的任何位置使用,否则可能会出现不可知的后果!")) return; + editor.file.removeMaterial(editor_mode.info, function (err) { + if (err) { + printe(err); + return; + } + alert('删除此素材成功!'); + window.location.reload(); + }); + } + changeId.children[3].onclick = function () { + editor.uifunctions.appendMaterialByInfo(editor_mode.info); + } + } + + editor.uifunctions.copyPasteEnemyItem_func = function () { + var copyEnemyItem = document.getElementById('copyEnemyItem'); + var pasteEnemyItem = document.getElementById('pasteEnemyItem'); + var clearEnemyItem = document.getElementById('clearEnemyItem'); + var clearAllEnemyItem = document.getElementById('clearAllEnemyItem'); + + copyEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || (cls != 'enemys' && cls != 'enemy48' && cls != 'items')) return; + editor.uivalues.copyEnemyItem.type = cls; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + editor.uivalues.copyEnemyItem.data = core.clone(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id]); + printf("怪物属性复制成功"); + } else if (cls == 'items') { + editor.uivalues.copyEnemyItem.data = core.clone(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id]); + printf("道具属性复制成功"); + } + } + + pasteEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls || cls != editor.uivalues.copyEnemyItem.type) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要覆盖此怪物的全部属性么?这是个不可逆操作!")) { + var name = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].name; + var displayIdInBook = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].displayIdInBook; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id] = core.clone(editor.uivalues.copyEnemyItem.data); + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].id = id; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].name = name; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].displayIdInBook = displayIdInBook; + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("怪物属性粘贴成功\n请再重新选中该怪物方可查看更新后的表格。"); + }) + } + } else if (cls == 'items') { + if (confirm("你确定要覆盖此道具的全部属性么?这是个不可逆操作!")) { + var name = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id].name; + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id] = core.clone(editor.uivalues.copyEnemyItem.data); + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id].id = id; + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id].name = name; + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("道具属性粘贴成功\n请再重新选中该道具方可查看更新后的表格。"); + }) + } + } + } + + var _clearEnemy = function (id) { + var info = core.clone(comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.enemys_template); + info.id = id; + info.name = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].name; + info.displayIdInBook = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].displayIdInBook; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id] = info; + } + + var _clearItem = function (id) { + for (var x in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id]) { + if (x != 'id' && x!='cls' && x != 'name') delete items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id][x]; + } + } + + clearEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要清空本怪物的全部属性么?这是个不可逆操作!")) { + _clearEnemy(id); + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("怪物属性清空成功\n请再重新选中该怪物方可查看更新后的表格。"); + }) + } + } else if (cls == 'items') { + if (confirm("你确定要清空本道具的全部属性么?这是个不可逆操作!")) { + _clearItem(id); + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("道具属性清空成功\n请再重新选中该道具方可查看更新后的表格。"); + }) + } + } + } + + clearAllEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要批量清空【全塔怪物】的全部属性么?这是个不可逆操作!")) { + for (var id in enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80) + _clearEnemy(id); + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("全塔全部怪物属性清空成功!"); + }) + } + } else if (cls == 'items') { + if (confirm("你确定要批量清空【全塔所有自动注册且未修改ID的道具】的全部属性么?这是个不可逆操作!")) { + for (var id in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + if (/^I\d+$/.test(id)) { + _clearItem(id); + } + } + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("全塔全部道具属性清空成功!"); + }) + } + } + } + + + + + + } + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 楼层属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + editor.uifunctions.changeFloorId_func = function () { + + editor.dom.changeFloorId.children[1].onclick = function () { + var floorId = editor.dom.changeFloorId.children[0].value; + if (floorId) { + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { + printe("楼层名 " + floorId + " 不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + if (main.floorIds.indexOf(floorId) >= 0) { + printe("楼层名 " + floorId + " 已存在!"); + return; + } + var currentFloorId = editor.currentFloorId; + editor.currentFloorId = floorId; + editor.currentFloorData.floorId = floorId; + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err); + } + core.floorIds[core.floorIds.indexOf(currentFloorId)] = floorId; + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + alert("修改floorId成功,需要刷新编辑器生效。\n请注意,原始的楼层文件没有删除,请根据需要手动删除。"); + window.location.reload(); + }); + }); + } else { + printe('请输入要修改到的floorId'); + } + } + } + + editor.uifunctions.changeFloorSize_func = function () { + var children = editor.dom.changeFloorSize.children; + children[4].onclick = function () { + var width = parseInt(children[0].value); + var height = parseInt(children[1].value); + var x = parseInt(children[2].value); + var y = parseInt(children[3].value); + if (!(width <= 128 && height <= 128 && x >= 0 && y >= 0)) { + printe("参数错误!宽高不得大于128,偏移量不得小于0"); + return; + } + var currentFloorData = editor.currentFloorData; + var currWidth = currentFloorData.width; + var currHeight = currentFloorData.height; + if (width < currWidth) x = -x; + if (height < currHeight) y = -y; + // Step 1:创建一个新的地图 + var newFloorData = core.clone(currentFloorData); + newFloorData.width = width; + newFloorData.height = height; + + // Step 2:更新map, bgmap和fgmap + editor.dom.maps.forEach(function (name) { + newFloorData[name] = []; + if (currentFloorData[name] && currentFloorData[name].length > 0) { + for (var j = 0; j < height; ++j) { + newFloorData[name][j] = []; + for (var i = 0; i < width; ++i) { + var oi = i - x; + var oj = j - y; + if (oi >= 0 && oi < currWidth && oj >= 0 && oj < currHeight) { + newFloorData[name][j].push(currentFloorData[name][oj][oi]); + } else { + newFloorData[name][j].push(0); + } + } + } + } + }); + + // Step 3:更新所有坐标 + ["events", "beforeBattle", "afterBattle", "afterGetItem", "afterOpenDoor", "changeFloor", "autoEvent", "cannotMove"].forEach(function (name) { + newFloorData[name] = {}; + if (!currentFloorData[name]) return; + for (var loc in currentFloorData[name]) { + var oxy = loc.split(','), ox = parseInt(oxy[0]), oy = parseInt(oxy[1]); + var nx = ox + x, ny = oy + y; + if (nx >= 0 && nx < width && ny >= 0 && ny < height) { + newFloorData[name][nx+","+ny] = core.clone(currentFloorData[name][loc]); + } + } + }); + + // Step 4:上楼点&下楼点 + ["upFloor", "downFloor"].forEach(function (name) { + if (newFloorData[name] && newFloorData[name].length == 2) { + newFloorData[name][0]+=x; + newFloorData[name][1]+=y; + } + }); + + editor.file.saveFloor(newFloorData, function (err) { + if (err) { + printe(err); + throw(err) + } + ;alert('地图更改大小成功,即将刷新地图...\n请检查所有点的事件是否存在问题。'); + window.location.reload(); + }); + } + } + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 全塔属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 脚本编辑 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 追加素材 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + editor.uifunctions.appendPic_func = function () { + // --- fix ctx + [editor.dom.appendSourceCtx, editor.dom.appendSpriteCtx].forEach(function (ctx) { + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + }) + + // --- selectAppend + var selectAppend_str = []; + ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"].forEach(function (image) { + selectAppend_str.push(["\n'].join('')); + }); + editor.dom.selectAppend.innerHTML = selectAppend_str.join(''); + editor.dom.selectAppend.onchange = function () { + + var value = editor.dom.selectAppend.value; + + if (value == 'autotile') { + editor_mode.appendPic.imageName = 'autotile'; + for (var jj = 0; jj < 4; jj++) editor.dom.appendPicSelection.children[jj].style = 'display:none'; + if (editor_mode.appendPic.img) { + editor.dom.appendSprite.style.width = (editor.dom.appendSprite.width = editor_mode.appendPic.img.width) / editor.uivalues.ratio + 'px'; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = editor_mode.appendPic.img.height) / editor.uivalues.ratio + 'px'; + editor.dom.appendSpriteCtx.clearRect(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + editor.dom.appendSpriteCtx.drawImage(editor_mode.appendPic.img, 0, 0); + } + return; + } + + var ysize = editor.dom.selectAppend.value.endsWith('48') ? 48 : 32; + editor_mode.appendPic.imageName = value; + var img = core.material.images[value]; + editor_mode.appendPic.toImg = img; + var num = ~~img.width / 32; + editor_mode.appendPic.num = num; + editor_mode.appendPic.index = 0; + var selectStr = ''; + for (var ii = 0; ii < num; ii++) { + editor.dom.appendPicSelection.children[ii].style = 'left:0;top:0;height:' + (ysize - 6) + 'px'; + selectStr += '{"x":0,"y":0},' + } + editor_mode.appendPic.selectPos = eval('[' + selectStr + ']'); + for (var jj = num; jj < 4; jj++) { + editor.dom.appendPicSelection.children[jj].style = 'display:none'; + } + editor.dom.appendSprite.style.width = (editor.dom.appendSprite.width = img.width) / editor.uivalues.ratio + 'px'; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = img.height + ysize) / editor.uivalues.ratio + 'px'; + editor.dom.appendSpriteCtx.drawImage(img, 0, 0); + } + editor.dom.selectAppend.onchange(); + + // --- selectFileBtn + var autoAdjust = function (image, callback) { + var changed = false; + + // Step 1: 检测白底 + var tempCanvas = document.createElement('canvas').getContext('2d'); + tempCanvas.canvas.width = image.width; + tempCanvas.canvas.height = image.height; + tempCanvas.mozImageSmoothingEnabled = false; + tempCanvas.webkitImageSmoothingEnabled = false; + tempCanvas.msImageSmoothingEnabled = false; + tempCanvas.imageSmoothingEnabled = false; + tempCanvas.drawImage(image, 0, 0); + var imgData = tempCanvas.getImageData(0, 0, image.width, image.height); + var trans = 0, white = 0, black = 0; + for (var i = 0; i < image.width; i++) { + for (var j = 0; j < image.height; j++) { + var pixel = editor.util.getPixel(imgData, i, j); + if (pixel[3] == 0) trans++; + if (pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255 && pixel[3] == 255) white++; + // if (pixel[0]==0 && pixel[1]==0 && pixel[2]==0 && pixel[3]==255) black++; + } + } + if (white > black && white > trans * 10 && confirm("看起来这张图片是以纯白为底色,是否自动调整为透明底色?")) { + for (var i = 0; i < image.width; i++) { + for (var j = 0; j < image.height; j++) { + var pixel = editor.util.getPixel(imgData, i, j); + if (pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255 && pixel[3] == 255) { + editor.util.setPixel(imgData, i, j, [0, 0, 0, 0]); + } + } + } + tempCanvas.clearRect(0, 0, image.width, image.height); + tempCanvas.putImageData(imgData, 0, 0); + changed = true; + } + /* + if (black>white && black>trans*10 && confirm("看起来这张图片是以纯黑为底色,是否自动调整为透明底色?")) { + for (var i=0;i= num) editor_mode.appendPic.index = ii + 1 - num; + else editor_mode.appendPic.index++; + editor_mode.appendPic.selectPos[ii] = pos; + editor.dom.appendPicSelection.children[ii].style = [ + 'left:', pos.x * 32, 'px;', + 'top:', pos.y * pos.ysize, 'px;', + 'height:', pos.ysize - 6, 'px;' + ].join(''); + } + + // appendConfirm + var appendRegister = document.getElementById('appendRegister'); + var appendConfirm = document.getElementById('appendConfirm'); + appendConfirm.onclick = function () { + + var confirmAutotile = function () { + var image = editor_mode.appendPic.img; + if (image.width % 96 != 0 || image.height != 128) { + printe("不合法的Autotile图片!"); + return; + } + var imgData = editor.dom.appendSourceCtx.getImageData(0, 0, image.width, image.height); + editor.dom.appendSpriteCtx.putImageData(imgData, 0, 0); + var imgbase64 = editor.dom.appendSprite.toDataURL().split(',')[1]; + + // Step 1: List文件名 + fs.readdir('./project/autotiles', function (err, data) { + if (err) { + printe(err); + throw (err); + } + + // Step 2: 选择Autotile文件名 + var filename; + for (var i = 1; ; ++i) { + filename = 'autotile' + i; + if (data.indexOf(filename + ".png") == -1) break; + } + + // Step 3: 写入文件 + fs.writeFile('./project/autotiles/' + filename + ".png", imgbase64, 'base64', function (err, data) { + if (err) { + printe(err); + throw (err); + } + // Step 4: 自动注册 + editor.file.registerAutotile(filename, function (err) { + if (err) { + printe(err); + throw (err); + } + printe('自动元件' + filename + '注册成功,请F5刷新编辑器'); + }) + + }) + + }) + + } + + if (editor.dom.selectAppend.value == 'autotile') { + confirmAutotile(); + return; + } + + var ysize = editor.dom.selectAppend.value.endsWith('48') ? 48 : 32; + for (var ii = 0, v; v = editor_mode.appendPic.selectPos[ii]; ii++) { + // var imgData = editor.dom.appendSourceCtx.getImageData(v.x * 32, v.y * ysize, 32, ysize); + // editor.dom.appendSpriteCtx.putImageData(imgData, ii * 32, editor.dom.appendSprite.height - ysize); + // editor.dom.appendSpriteCtx.drawImage(editor_mode.appendPic.img, v.x * 32, v.y * ysize, 32, ysize, ii * 32, height, 32, ysize) + + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, v.x * 32, v.y * ysize, 32, ysize, 32 * ii, editor.dom.appendSprite.height - ysize, 32, ysize); + } + var dt = editor.dom.appendSpriteCtx.getImageData(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + var imgbase64 = editor.dom.appendSprite.toDataURL('image/png'); + var imgName = editor_mode.appendPic.imageName; + fs.writeFile('./project/materials/' + imgName + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) { + printe(err); + throw (err) + } + var currHeight = editor.dom.appendSprite.height; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = (currHeight + ysize)) + "px"; + editor.dom.appendSpriteCtx.putImageData(dt, 0, 0); + core.material.images[imgName].src = imgbase64; + editor.widthsX[imgName][3] = currHeight; + if (appendRegister && appendRegister.checked) { + editor.file.autoRegister({images: imgName}, function (e) { + if (e) { + printe(e); + throw e; + } + printf('追加素材并自动注册成功!你可以继续追加其他素材,最后再刷新以使用。'); + }); + } else { + printf('追加素材成功!你可以继续追加其他素材,最后再刷新以使用。'); + } + }); + } + + var quickAppendConfirm = document.getElementById('quickAppendConfirm'); + quickAppendConfirm.onclick = function () { + var value = editor.dom.selectAppend.value; + if (value != 'items' && value != 'enemys' && value != 'enemy48' && value != 'npcs' && value != 'npc48') + return printe("只有怪物或NPC才能快速导入!"); + var ysize = value.endsWith('48') ? 48 : 32; + var sw = editor.dom.appendSourceCtx.canvas.width, sh = editor.dom.appendSourceCtx.canvas.height; + if (value == 'items') { + if (sw % 32 || sh % 32) { + return printe("只有长宽都是32的倍数的道具图才可以快速导入!"); + } + } else { + if ((sw != 128 && sw != 96) || sh != 4 * ysize) { + return printe("只有 3*4 或 4*4 的素材图片才可以快速导入!"); + } + } + sw = sw / 32; + sh = sh / ysize; + + var dt = editor.dom.appendSpriteCtx.getImageData(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + var appendSize = value == 'items' ? (sw * sh - 1) : 3; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = (editor.dom.appendSprite.height + appendSize * ysize)) + "px"; + editor.dom.appendSpriteCtx.putImageData(dt, 0, 0); + if (editor.dom.appendSprite.width == 32) { // 1帧:道具 + for (var i = 0; i < sw * sh; ++i) { + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 32 * (i % sw), 32 * parseInt(i / sw), 32, 32, 0, editor.dom.appendSprite.height - (sw * sh - i) * ysize, 32, 32); + } + } else if (editor.dom.appendSprite.width == 64) { // 两帧 + if (sw == 3) { + // 3*4的规格使用13帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 0, 0, 32, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 32, 4 * ysize); + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 64, 0, 32, 4 * ysize, 32, editor.dom.appendSprite.height - 4 * ysize, 32, 4 * ysize); + } else { + // 4*4的规格使用23帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 32, 0, 64, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 64, 4 * ysize); + } + } else { // 四帧 + if (sw == 3) { + // 3*4的规格使用2123帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 32, 0, 32, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 32, 4 * ysize); + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 0, 0, 96, 4 * ysize, 32, editor.dom.appendSprite.height - 4 * ysize, 96, 4 * ysize); + } else { + // 4*4的规格使用1234帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 0, 0, 128, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 128, 4 * ysize); + } + } + + dt = editor.dom.appendSpriteCtx.getImageData(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + var imgbase64 = editor.dom.appendSprite.toDataURL('image/png'); + var imgName = editor_mode.appendPic.imageName; + fs.writeFile('./project/materials/' + imgName + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) { + printe(err); + throw (err) + } + var currHeight = editor.dom.appendSprite.height; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = (currHeight + ysize)) + "px"; + editor.dom.appendSpriteCtx.putImageData(dt, 0, 0); + core.material.images[imgName].src = imgbase64; + editor.widthsX[imgName][3] = currHeight; + if (appendRegister && appendRegister.checked) { + editor.file.autoRegister({images: imgName}, function (e) { + if (e) { + printe(e); + throw e; + } + printf('快速追加素材并自动注册成功!你可以继续追加其他素材,最后再刷新以使用。'); + }) + } else { + printf('快速追加素材成功!你可以继续追加其他素材,最后再刷新以使用。'); + } + }); + + } + + editor.uifunctions.dragImageToAppend = function (file, cls) { + editor.mode.change('appendpic'); + editor.dom.selectAppend.value = cls; + editor.dom.selectAppend.onchange(); + + var reader = new FileReader(); + reader.onload = function () { + afterReadFile(reader.result, function() { + if (cls == 'terrains') return; + if (confirm('你确定要快速追加么?')) { + if (cls == 'autotile') { + appendConfirm.onclick(); + } else { + quickAppendConfirm.onclick(); + } + } + }); + } + reader.readAsDataURL(file); + } + + editor.uifunctions.appendMaterialByInfo = function (info) { + if (info.isTile) { + printe('额外素材不支持此功能!'); + return; + } + var img = null; + var cls = info.images; + var height = cls == 'enemy48' || cls == 'npc48' ? 48 : 32; + + if (cls == 'autotile') { + img = core.material.images.autotile[info.id]; + } else { + var image = core.material.images[cls]; + var width = image.width; + img = document.createElement('canvas'); + img.width = width; + img.height = height; + img.getContext('2d').drawImage(image, 0, info.y * height, width, height, 0, 0, width, height); + } + + editor.mode.change('appendpic'); + editor.dom.selectAppend.value = cls; + editor.dom.selectAppend.onchange(); + + afterReadFile(img, function () { + changeColorInput.value = 0; + if (cls == 'autotile') return; + + editor_mode.appendPic.index = 0; + for (var ii = 0; ii < editor_mode.appendPic.num; ++ii) { + editor_mode.appendPic.selectPos[ii] = {x: ii, y: 0, ysize: height}; + editor.dom.appendPicSelection.children[ii].style = [ + 'left:', ii * 32, 'px;', + 'top:', 0, 'px;', + 'height:', height - 6, 'px;' + ].join(''); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////// + //////////////////// 公共事件 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 插件编写 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + +} diff --git a/editor_file.js b/editor_file.js new file mode 100644 index 0000000..1b4a200 --- /dev/null +++ b/editor_file.js @@ -0,0 +1,1118 @@ +editor_file_wrapper = function (editor) { + editor_file_proto = function () { + /** + * 以 + * { + * "floor.MT1":, + * "plugins": + * } + * 的形式记录所有更改过的文件,save时写入 + * 的内容暂时还没想好 + * + * Mark相关的思路搁置 + */ + this.fileMark = {} + } + + // 这个函数之后挪到editor.table? + editor_file_proto.prototype.loadCommentjs = function (callback) { + var commentjs = { + 'comment': 'comment', + 'data.comment': 'dataComment', + 'functions.comment': 'functionsComment', + 'events.comment': 'eventsComment', + 'plugins.comment': 'pluginsComment', + } + for (var key in commentjs) { + (function (key) { + var value = commentjs[key]; + var script = document.createElement('script'); + if (window.location.href.indexOf('_server') !== -1) + script.src = key + '.js'; + else + script.src = '_server/table/' + key + '.js'; + document.body.appendChild(script); + script.onload = function () { + editor.file[value] = eval(key.replace('.', '_') + '_c456ea59_6018_45ef_8bcc_211a24c627dc'); + var loaded = Boolean(callback); + for (var key_ in commentjs) { + loaded = loaded && editor.file[commentjs[key_]] + } + if (loaded) callback(); + } + })(key); + } + } + + editor_file_proto.prototype.alertWhenCompress = function () { + if (editor.useCompress === true) { + editor.useCompress = 'alerted'; + setTimeout("alert('当前游戏使用的是压缩文件,修改完成后请使用启动服务.exe->Js代码压缩工具重新压缩,或者把main.js的useCompress改成false来使用原始文件')", 1000) + } + } + + editor_file_proto.prototype.formatMap = function (mapArr, trySimplify) { + if (!mapArr || JSON.stringify(mapArr) == JSON.stringify([])) return ''; + if (trySimplify) { + //检查是否是全0二维数组 + var jsoncheck = JSON.stringify(mapArr).replace(/\D/g, ''); + if (jsoncheck == Array(jsoncheck.length + 1).join('0')) return ''; + } + //把二维数组格式化 + var formatArrStr = ''; + var arr = JSON.stringify(mapArr).replace(/\s+/g, '').split('],['); + var si = mapArr.length - 1, sk = mapArr[0].length - 1; + for (var i = 0; i <= si; i++) { + var a = []; + formatArrStr += ' ['; + if (i == 0 || i == si) a = arr[i].split(/\D+/).join(' ').trim().split(' '); + else a = arr[i].split(/\D+/); + for (var k = 0; k <= sk; k++) { + var num = parseInt(a[k]); + formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk ? '' : ','); + } + formatArrStr += ']' + (i == si ? '' : ',\n'); + } + return formatArrStr; + } + + editor_file_proto.prototype.saveFloor = function (floorData, callback) { + //callback(err:String) + var floorId = floorData.floorId; + var filename = 'project/floors/' + floorId + '.js'; + var datastr = ['main.floors.', floorId, '=\n']; + + var tempJsonObj = Object.assign({}, floorData); + var tempMap = editor.dom.maps.map(function (one) { + return [one, editor.util.guid()] + }); + tempMap.forEach(function (v) { + v[2] = tempJsonObj[v[0]]; + tempJsonObj[v[0]] = v[1]; + }); + var tempJson = JSON.stringify(tempJsonObj, editor.game.replacerForSaving, 4); + tempMap.forEach(function (v) { + tempJson = tempJson.replace('"' + v[1] + '"', '[\n' + editor.file.formatMap(v[2], v[0] != 'map') + '\n]') + }); + datastr = datastr.concat([tempJson]); + datastr = datastr.join(''); + editor.file.alertWhenCompress(); + editor.fs.writeFile(filename, editor.util.encode64(datastr), 'base64', function (err, data) { + editor.addUsedFlags(datastr); + callback(err); + }); + } + + editor_file_proto.prototype.saveScript = function (name, varName, dataObj, callback) { + // 此处格式化以及写入 project/xxx.js 形式的文件 + editor.file.alertWhenCompress(); + + if (['maps', 'enemys'].indexOf(name) === -1) { + // 全部用\t展开 + var content = JSON.stringify(dataObj, editor.game.replacerForSaving, '\t'); + } else { + // 只用\t展开第一层 + var emap = {}; + var estr = JSON.stringify(dataObj, function (_k, v) { + if (v.id != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v, editor.game.replacerForSaving); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]); + } + var content = estr; + } + + var strToWrite = `var ${varName} = \n${content}`; + editor.fs.writeFile(`project/${name}.js`, editor.util.encode64(strToWrite), 'base64', function (err, data) { + editor.addUsedFlags(content); + callback(err); + }); + } + + editor_file_proto.prototype.saveCommentJs = function () { + // 无需格式化的写入, 把multi的那部分略微修改 + } + + editor_file_proto.prototype.saveImage = function () { + // 给追加素材使用 + } + + // Mark相关的思路搁置 + editor_file_proto.prototype.addMark = function (name) { + // 把name对应的文件在editor.file.fileMark添加标记 + } + + editor_file_proto.prototype.save = function (callback) { + // 根据 editor.file.fileMark 把游戏对象格式化写入文件 + } +} + +///////////////////////////////////////////////////////////////////////// + + +editor_file = function (editor, callback) { + + var editor_file = new editor_file_proto(); + editor.file=editor_file; + + editor.file.loadCommentjs(callback); + + editor.file.saveFloorFile = function (callback) { + //callback(err:String) + checkCallback(callback); + /* if (!isset(editor.currentFloorId) || !isset(editor.currentFloorData)) { + callback('未选中文件或无数据'); + } */ + if (core.floorIds.indexOf(editor.currentFloorId) >= 0) { + for(var ii=0,name;name=editor.dom.maps[ii];ii++){ + var mapArray=editor[name].map(function (v) { + return v.map(function (v) { + return v.idnum || v || 0 + }) + }); + editor.currentFloorData[name]=mapArray; + } + } + editor.file.saveFloor(editor.currentFloorData, callback) + } + + /////////////////////////////////////////////////////////////////////////// + + editor.file.saveNewFile = function (saveFilename, callback) { + //saveAsFilename不含'/'不含'.js' + checkCallback(callback); + var currData=editor.currentFloorData; + var saveStatus = document.getElementById('newMapStatus').checked; + + var title = saveStatus?currData.title:"新建楼层"; + var name = saveStatus?currData.name:"0"; + if (/^mt\d+$/i.test(saveFilename)) { + name = saveFilename.substring(2); + title = "主塔 "+name+" 层"; + } + + var width = parseInt(document.getElementById('newMapWidth').value); + var height = parseInt(document.getElementById('newMapHeight').value); + var row = [], map = []; + for (var i=0;i= 4) { + var i = faceIds.length - 4; + var down = faceIds[i], left = faceIds[i+1], right = faceIds[i+2], up = faceIds[i+3]; + var obj = {down: down.id, left: left.id, right: right.id, up: up.id}; + if (image.indexOf('enemy')==0) { + templateActions.push(["add", "['" + down.id + "']['faceIds']", obj]); + templateActions.push(["add", "['" + left.id + "']['faceIds']", obj]); + templateActions.push(["add", "['" + right.id + "']['faceIds']", obj]); + templateActions.push(["add", "['" + up.id + "']['faceIds']", obj]); + } else { + mapActions.push(["add", "['" + down.idnum + "']['faceIds']", obj]); + mapActions.push(["add", "['" + left.idnum + "']['faceIds']", obj]); + mapActions.push(["add", "['" + right.idnum + "']['faceIds']", obj]); + mapActions.push(["add", "['" + up.idnum + "']['faceIds']", obj]); + } + } + } + + if (mapActions.length==0) { + callback("没有要注册的项!"); + return; + } + + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 3) { + if (templist[0] != null || templist[1] != null || templist[2] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '') + '\n' + (templist[2] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + if (iconActions.length>0) + saveSetting('icons', iconActions, tempcallback); + else tempcallback(null); + + saveSetting('maps', mapActions, tempcallback); + + if (image=='items') + saveSetting('items', templateActions, tempcallback); + else if (image.indexOf('enemy')==0) + saveSetting('enemys', templateActions, tempcallback); + else tempcallback(null); + } + + editor.file.registerAutotile = function (filename, callback) { + var idnum = 140; + while (editor.core.maps.blocksInfo[idnum]) idnum++; + + var iconActions = []; + var mapActions = []; + + iconActions.push(["add", "['autotile']['" + filename + "']", 0]); + mapActions.push(["add", "['" + idnum + "']", {'cls': 'autotile', 'id': filename}]); + + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 2) { + if (templist[0] != null || templist[1] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + + saveSetting('icons', iconActions, tempcallback); + saveSetting('maps', mapActions, tempcallback); + } + + editor.file.changeIdAndIdnum = function (id, idnum, info, callback) { + checkCallback(callback); + + var changeOrNew=core.isset(editor_mode.info.id)?'change':'new' + if(changeOrNew=='new'){ + //检查maps中是否有重复的idnum或id + for (var ii in editor.core.maps.blocksInfo) { + if (ii == idnum) { + callback('idnum重复了'); + return; + } + if (editor.core.maps.blocksInfo[ii].id == id) { + callback('id重复了'); + return; + } + } + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 2) { + if (templist[0] != null || templist[1] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + saveSetting('maps', [["add", "['" + idnum + "']", {'cls': info.images, 'id': id}]], tempcallback); + saveSetting('icons', [["add", "['" + info.images + "']['" + id + "']", info.y]], tempcallback); + if (info.images === 'items') { + saveSetting('items', [["add", "['" + id + "']", editor.file.comment._data.items_template]], function (err) { + if (err) { + printe(err); + throw(err) + } + }); + } + if (info.images === 'enemys' || info.images === 'enemy48') { + saveSetting('enemys', [["add", "['" + id + "']", editor.file.comment._data.enemys_template]], function (err) { + if (err) { + printe(err); + throw(err) + } + }); + } + + callback(null); + + }else{ + //检查maps中是否有重复的idnum或id + for (var ii in editor.core.maps.blocksInfo) { + if (editor.core.maps.blocksInfo[ii].id == id) { + callback('id重复了'); + return; + } + } + idnum = info.idnum; + + maps_90f36752_8815_4be8_b32b_d7fad1d0542e[idnum].id = id; + + var arr=[icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1,{items: items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a},{enemys: enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80}] + arr.forEach(function (obj) { + for(var jj in obj){ + var ii=obj[jj] + if (ii.hasOwnProperty(info.id)){ + ii[id]=ii[info.id]; + delete(ii[info.id]); + } + } + }); + + editor.file.save_icons_maps_items_enemys(callback) + + } + } + + editor.file.removeMaterial = function (info, callback) { + console.log(info); + + // Step 1: 尝试删除图片 + var _deleteMaterialImage = function (cb) { + if (info.images == 'autotile') return cb(); + var img = core.material.images[info.images]; + if (img == null) return callback('该素材不存在!'); + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + var width = img.width, height = img.height, per_height = info.images.endsWith('48') ? 48 : 32 + if (height == per_height) return callback('该素材图片只有一个素材,无法删除'); + canvas.width = width; + canvas.height = height - per_height; + ctx.drawImage(img, 0, 0, width, info.y * per_height, 0, 0, width, info.y * per_height); + ctx.drawImage(img, 0, (info.y + 1) * per_height, width, height - (info.y + 1) * per_height, 0, info.y * per_height, width, height - (info.y + 1) * per_height); + var imgbase64 = canvas.toDataURL('image/png'); + fs.writeFile('./project/materials/' + info.images + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) return callback(err); + cb(); + }); + } + + _deleteMaterialImage(function () { + // Step 2: 删除图块信息 + if (info.id) { + delete icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1[info.images][info.id]; + delete maps_90f36752_8815_4be8_b32b_d7fad1d0542e[info.idnum]; + if (info.images == 'items') { + delete items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[info.id]; + } + if (info.images == 'enemys' || info.images == 'enemy48') { + delete enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[info.id]; + } + } + + // Step 3: 将所有素材向下移动一格 + if (info.images != 'autotile') { + var value = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1[info.images]; + Object.keys(value).forEach(function (one) { + if (value[one] > info.y) value[one]--; + }); + } + + // Step 4: 保存并删除成功! + editor.file.save_icons_maps_items_enemys(callback); + }); + } + + //callback(err:String) + editor.file.editItem = function (id, actionList, callback) { + /*actionList:[ + ["change","['name']","红宝石的新名字"], + ["add","['新的和name同级的属性']",123], + ["change","['itemEffectTip']","',攻击力+'+editor.core.values.redGem"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['" + id + "']" + value[1]; + }); + saveSetting('items', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.core.items.items[id]); + Object.keys(editor.file.comment._data.items._data).forEach(function (v) { + if (!isset((editor.core.items.items[id]||{})[v])) + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.items, + null]); + } + //只有items.cls是items的才有itemEffect和itemEffectTip,keys和constants和tools只有items + } + //callback([obj,commentObj,err:String]) + editor.file.editEnemy = function (id, actionList, callback) { + /*actionList:[ + ["change","['name']","初级巫师的新名字"], + ["add","['新的和name同级的属性']",123], + ["change","['bomb']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['" + id + "']" + value[1]; + }); + saveSetting('enemys', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.core.enemys.enemys[id]); + Object.keys(editor.file.comment._data.enemys._data).forEach(function (v) { + if (!isset((editor.core.enemys.enemys[id]||{})[v])) + /* locObj[v]=editor.core.enemys.enemys[id][v]; + else */ + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.enemys, + null]); + } + } + //callback([obj,commentObj,err:String]) + + editor.file.editMapBlocksInfo = function (idnum, actionList, callback) { + /*actionList:[ + ["change","['events']",["\t[老人,wizard]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], + ["change","['afterBattle']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + var tempmap=[]; + for(var ii=0;ii=editor.core.icons.tilesetStartOffset && !isset(editor.core.maps.blocksInfo[idnum]) && tempmap.indexOf(idnum)===-1){ + actionList.splice(ii,0,["add","['" + idnum + "']",{"cls": "tileset", "id": "X"+idnum}]); + tempmap.push(idnum); + ii++; + } + value[1] = "['" + idnum + "']" + value[1]; + } + saveSetting('maps', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var sourceobj=editor.core.maps.blocksInfo[idnum]; + if(!isset(sourceobj) && idnum>=editor.core.icons.tilesetStartOffset)sourceobj={"cls": "tileset", "id": "X"+idnum} + var locObj = Object.assign({}, sourceobj); + Object.keys(editor.file.comment._data.maps._data).forEach(function (v) { + if (!isset(sourceobj[v])) + locObj[v] = null; + }); + locObj.idnum = idnum; + return locObj; + })(), + editor.file.comment._data.maps, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editLoc = function (x, y, actionList, callback) { + /*actionList:[ + ["change","['events']",["\t[老人,wizard]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], + ["change","['afterBattle']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + if(/\['autoEvent'\]\['\d+'\]$/.test(value[1]))value[1]=value[1].replace(/\['\d+'\]$/,function(v){return "['" + x + "," + y + "']"+v}) + else value[1] = value[1] + "['" + x + "," + y + "']"; + }); + saveSetting('floorloc', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = {}; + Object.keys(editor.file.comment._data.floors._data.loc._data).forEach(function (v) { + if (isset(editor.currentFloorData[v][x + ',' + y])) + locObj[v] = editor.currentFloorData[v][x + ',' + y]; + else + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.floors._data.loc, + null]); + } + + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editFloor = function (actionList, callback) { + /*actionList:[ + ["change","['title']",'样板 3 层'], + ["change","['color']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('floors', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.currentFloorData); + Object.keys(editor.file.comment._data.floors._data.floor._data).forEach(function (v) { + if (!isset(editor.currentFloorData[v])) + /* locObj[v]=editor.currentFloorData[v]; + else */ + locObj[v] = null; + }); + Object.keys(editor.file.comment._data.floors._data.loc._data).forEach(function (v) { + delete(locObj[v]); + }); + editor.dom.maps.forEach(function (one) { delete locObj[one]; }); + return locObj; + })(), + editor.file.comment._data.floors._data.floor, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editTower = function (actionList, callback) { + /*actionList:[ + ["change","['firstData']['version']",'Ver 1.0.1 (Beta)'], + ["change","['values']['lavaDamage']",200], + ] + 为[]时只查询不修改 + */ + var data_obj = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('data', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + //var locObj=Object.assign({'main':{}},editor.core.data); + var locObj = Object.assign({}, data_obj, {'main': {}}); + Object.keys(editor.file.dataComment._data.main._data).forEach(function (v) { + if (isset(editor.main[v])) + locObj.main[v] = data_obj.main[v]; + else + locObj.main[v] = null; + }); + return locObj; + })(), + editor.file.dataComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var fmap = {}; + var fjson = JSON.stringify(functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + fmap[id_] = v.toString(); + return id_; + } else return v + }, 4); + var fobj = JSON.parse(fjson); + editor.file.functionsMap = fmap; + editor.file.functionsJSON = fjson; + var buildlocobj = function (locObj) { + for (var key in locObj) { + if (typeof(locObj[key]) !== typeof('')) buildlocobj(locObj[key]); + else locObj[key] = fmap[locObj[key]]; + } + }; + + editor.file.editFunctions = function (actionList, callback) { + /*actionList:[ + ["change","['events']['afterChangeLight']","function(x,y){console.log(x,y)}"], + ["change","['ui']['drawAbout']","function(){...}"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('functions', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = JSON.parse(fjson); + buildlocobj(locObj); + return locObj; + })(), + editor.file.functionsComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editCommonEvent = function (actionList, callback) { + /*actionList:[ + ["change","['test']",['123']], + ] + 为[]时只查询不修改 + */ + var data_obj = events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent; + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['commonEvent']" + value[1]; + }); + saveSetting('events', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + Object.assign({},data_obj), + editor.file.eventsComment._data.commonEvent, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var plmap = {}; + var pljson = JSON.stringify(plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + plmap[id_] = v.toString(); + return id_; + } else if(v===null){ + var id_ = editor.util.guid(); + plmap[id_] = null; + return id_; + } return v + }, 4); + var plobj = JSON.parse(pljson); + editor.file.pluginsMap = plmap; + editor.file.pluginsObj = plobj; + var buildpllocobj = function (locObj) { + for (var key in locObj) { + if (typeof(locObj[key]) !== typeof('')) buildpllocobj(locObj[key]); + else locObj[key] = plmap[locObj[key]]; + } + }; + + editor.file.editPlugins = function (actionList, callback) { + /*actionList:[ + ["change","['test']","function(x,y){console.log(x,y)}"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('plugins', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = JSON.parse(JSON.stringify(plobj)); + buildpllocobj(locObj); + return locObj; + })(), + editor.file.pluginsComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var isset = function (val) { + if (val == undefined || val == null) { + return false; + } + return true + } + + var checkCallback=function(callback){ + if (!isset(callback)) { + printe('未设置callback'); + throw('未设置callback') + } + } + + var encode = editor.util.encode64; + + editor.file.save_icons_maps_items_enemys=function(callback){ + var check=[] + saveSetting('icons',[],function(err){ + if(err){callback(err);return;} + check.push('icons') + if(check.length==4)callback(null); + }) + saveSetting('maps',[],function(err){ + if(err){callback(err);return;} + check.push('maps') + if(check.length==4)callback(null); + }) + saveSetting('items',[],function(err){ + if(err){callback(err);return;} + check.push('items') + if(check.length==4)callback(null); + }) + saveSetting('enemys',[],function(err){ + if(err){callback(err);return;} + check.push('enemys') + if(check.length==4)callback(null); + }) + } + + var saveSetting = function (file, actionList, callback) { + var _update = function (name, value) { + if (value[2] === undefined) { + eval("delete " + name + value[1]); + } else { + eval(name + value[1] + "=" + JSON.stringify(value[2])); + } + } + + //console.log(file); + //console.log(actionList); + editor.file.alertWhenCompress(); + + if (file == 'icons') { + actionList.forEach(function (value) { + _update("icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1", value); + }); + var datastr = 'var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = \n'; + datastr += JSON.stringify(icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1, null, '\t'); + fs.writeFile('project/icons.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'maps') { + actionList.forEach(function (value) { + _update("maps_90f36752_8815_4be8_b32b_d7fad1d0542e", value); + }); + var datastr = 'var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = \n'; + //datastr+=JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e,null,4); + + var emap = {}; + var estr = JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e, function (k, v) { + if (v.id != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + + fs.writeFile('project/maps.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'items') { + actionList.forEach(function (value) { + _update("items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a", value); + }); + var datastr = 'var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = \n'; + var items = core.clone(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a); + for (var id in items) delete items[id].id; + datastr += JSON.stringify(items, null, '\t'); + fs.writeFile('project/items.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'enemys') { + actionList.forEach(function (value) { + _update("enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80", value); + }); + var datastr = 'var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = \n'; + var emap = {}; + var enemys = core.clone(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80); + for (var id in enemys) delete enemys[id].id; + var estr = JSON.stringify(enemys, function (k, v) { + if (v && v.hp != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + fs.writeFile('project/enemys.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'data') { + actionList.forEach(function (value) { + _update("data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d", value); + }); + if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId) < 0) + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds[0]; + var datastr = 'var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = \n'; + datastr += JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, null, '\t'); + fs.writeFile('project/data.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'functions') { + actionList.forEach(function (value) { + eval("fmap[fobj" + value[1] + ']=' + JSON.stringify(value[2])); + }); + var fraw = fjson; + for (var id_ in fmap) { + fraw = fraw.replace('"' + id_ + '"', fmap[id_]) + } + var datastr = 'var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = \n'; + datastr += fraw; + fs.writeFile('project/functions.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'floorloc') { + actionList.forEach(function (value) { + // 检测null/undefined + if(/\['autoEvent'\]\['\d+,\d+'\]\['\d+'\]$/.test(value[1])){ + var tempvalue=value[1].replace(/\['\d+'\]$/,'') + tempvalue="editor.currentFloorData" +tempvalue + tempvalue=tempvalue+'='+tempvalue+'||{}' + eval(tempvalue) + } + if (value[2]==null && value[0]!=='add') + eval("delete editor.currentFloorData" + value[1]); + else + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + }); + editor.file.saveFloorFile(callback); + return; + } + if (file == 'floors') { + actionList.forEach(function (value) { + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + }); + editor.file.saveFloorFile(callback); + return; + } + if (file == 'events') { + actionList.forEach(function (value) { + _update("events_c12a15a8_c380_4b28_8144_256cba95f760", value); + }); + var datastr = 'var events_c12a15a8_c380_4b28_8144_256cba95f760 = \n'; + datastr += JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760, null, '\t'); + fs.writeFile('project/events.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'plugins') { + actionList.forEach(function (value) { + if(value[0]==='add'){ + eval("plobj" + value[1] + '=' + JSON.stringify(value[2])); + } else { + eval("plmap[plobj" + value[1] + ']=' + JSON.stringify(value[2])); + } + }); + var plraw = JSON.stringify(plobj,null,4); + for (var id_ in plmap) { + plraw = plraw.replace('"' + id_ + '"', plmap[id_]) + } + var datastr = 'var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = \n'; + datastr += plraw; + fs.writeFile('project/plugins.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + callback('出错了,要设置的文件名不识别'); + } + + editor.file.saveSetting = saveSetting; + + return editor_file; +} +//editor_file = editor_file(editor); \ No newline at end of file diff --git a/editor_game.js b/editor_game.js new file mode 100644 index 0000000..a8b3f65 --- /dev/null +++ b/editor_game.js @@ -0,0 +1,199 @@ +editor_game_wrapper = function (editor, main, core) { + // 原则上重构后只有此文件允许`\s(main|core)`形式的调用, 以及其初始化 editor_game_wrapper(editor, main, core) + + editor_game = function () { + this.replacerRecord = {} + } + + + //////////////////// 修改数据相关 //////////////////// + // 三个 replacer 和 replacerRecord 暂时放在此处 + + editor_game.prototype.replacerForLoading = function (_key, value) { + var rmap = editor.game.replacerRecord; + if (value instanceof Function) { + var guid_ = editor.util.guid() + rmap[guid_] = value.toString() + return guid_ + } else if (value === null) { + // 为了包含plugins的新建 + var guid_ = editor.util.guid() + rmap[guid_] = null + return guid_ + } + return value + } + + editor_game.prototype.replacerForSaving = function (_key, value) { + var rmap = editor.game.replacerRecord; + if (rmap.hasOwnProperty(value)) { + return rmap[value] + } + return value + } + + editor_game.prototype.getValue = function (field) { + var rmap = editor.game.replacerRecord; + var value = eval(field) + if (rmap.hasOwnProperty(oldval)) { + return rmap[value] + } else { + return value + } + } + + editor_game.prototype.setValue = function (field, value) { + var rmap = editor.game.replacerRecord; + var oldval = eval(field) + if (rmap.hasOwnProperty(oldval)) { + rmap[value] = eval(value) + } else { + eval(field + '=' + value) + } + } + + editor_game.prototype.replacerWithoutRecord = function (_key, value) { + if (value instanceof Function) { + return value.toString() + } else return value + } + + editor_game.prototype.fixFunctionInGameData = function () { + var rf = editor.game.replacerWithoutRecord + core.floors = JSON.parse(JSON.stringify(core.floors, rf)); + core.data = JSON.parse(JSON.stringify(core.data, rf)); + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = JSON.parse(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, rf)); + } + + //////////////////// 加载游戏数据相关 //////////////////// + + // 初始化数字与地图图块的对应 + editor_game.prototype.idsInit = function (maps, icons) { + editor.ids = [0]; + editor.indexs = []; + var MAX_NUM = 0; + var keys = Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); + for (var ii = 0; ii < keys.length; ii++) { + var v = ~~keys[ii]; + if (v > MAX_NUM && v < core.icons.tilesetStartOffset) MAX_NUM = v; + } + editor.MAX_NUM = MAX_NUM; + var getInfoById = function (id) { + var block = maps.initBlock(0, 0, id); + if (Object.prototype.hasOwnProperty.call(block, 'event')) { + return block; + } + } + var point = 0; + for (var i = 0; i <= MAX_NUM; i++) { + var indexBlock = getInfoById(i); + editor.indexs[i] = []; + if (indexBlock) { + var id = indexBlock.event.id; + var indexId = indexBlock.id; + var allCls = Object.keys(icons); + if (i == 17) { + editor.ids.push({ 'idnum': 17, 'id': id, 'images': 'terrains' }); + point++; + editor.indexs[i].push(point); + continue; + } + for (var j = 0; j < allCls.length; j++) { + if (id in icons[allCls[j]]) { + editor.ids.push({ 'idnum': indexId, 'id': id, 'images': allCls[j], 'y': icons[allCls[j]][id] }); + point++; + editor.indexs[i].push(point); + } + } + } + } + editor.indexs[0] = [0]; + + var startOffset = core.icons.tilesetStartOffset; + for (var i in core.tilesets) { + var imgName = core.tilesets[i]; + var img = core.material.images.tilesets[imgName]; + var width = Math.floor(img.width / 32), height = Math.floor(img.height / 32); + if (img.width % 32 || img.height % 32) { + // alert(imgName + '的长或宽不是32的整数倍, 请修改后刷新页面'); + console.warn(imgName + '的长或宽不是32的整数倍, 请修改后刷新页面'); + } + if (img.width * img.height > 32 * 32 * 3000) { + // alert(imgName + '上的图块数量超过了3000,请修改后刷新页面'); + console.warn(imgName + '上的图块数量超过了3000,请修改后刷新页面'); + } + for (var id = startOffset; id < startOffset + width * height; id++) { + var x = (id - startOffset) % width, y = parseInt((id - startOffset) / width); + var indexBlock = getInfoById(id); + editor.ids.push({ 'idnum': id, 'id': indexBlock.event.id, 'images': imgName, "x": x, "y": y, isTile: true }); + point++; + editor.indexs[id] = [point]; + } + startOffset += core.icons.tilesetStartOffset; + } + } + + // 获取当前地图 + editor_game.prototype.fetchMapFromCore = function () { + var mapArray = core.getMapArray(core.status.floorId); + editor.map = mapArray.map(function (v) { + return v.map(function (v) { + var x = parseInt(v), y = editor.indexs[x]; + if (y == null) { + printe("素材数字" + x + "未定义。是不是忘了注册,或者接档时没有覆盖icons.js和maps.js?"); + y = [0]; + } + return editor.ids[y[0]] + }) + }); + editor.currentFloorId = core.status.floorId; + editor.currentFloorData = core.floors[core.status.floorId]; + // 补出缺省的数据 + editor.currentFloorData.autoEvent = editor.currentFloorData.autoEvent || {}; + // + for (var ii = 0, name; name = editor.dom.canvas[ii]; ii++) { + name += 'map'; + var mapArray = editor.currentFloorData[name]; + if (!mapArray || JSON.stringify(mapArray) == JSON.stringify([])) {//未设置或空数组 + //与editor.map同形的全0 + mapArray = eval('[' + Array(editor.map.length + 1).join('[' + Array(editor.map[0].length + 1).join('0,') + '],') + ']'); + } + mapArray = core.maps._processInvalidMap(mapArray, editor.map[0].length, editor.map.length); + editor[name] = mapArray.map(function (v) { + return v.map(function (v) { + var x = parseInt(v), y = editor.indexs[x]; + if (y == null) { + printe("素材数字" + x + "未定义。是不是忘了注册,或者接档时没有覆盖icons.js和maps.js?"); + y = [0]; + } + return editor.ids[y[0]] + }) + }); + } + } + + // 获取地图列表 + editor_game.prototype.getFloorFileList = function (callback) { + // callback([Array,err:String]) + editor.util.checkCallback(callback); + /* editor.fs.readdir('project/floors',function(err, data){ + callback([data,err]); + }); */ + callback([editor.core.floorIds, null]); + } + + editor_game.prototype.doCoreFunc = function (funcname) { + return core[funcname].apply(core, Array.prototype.slice.call(arguments, 1)); + } + + editor_game.prototype.getEnemy = function (id) { + return core.material.enemys[id]; + } + + editor_game.prototype.getFirstData = function () { + return core.firstData; + } + + editor.constructor.prototype.game = new editor_game(); +} +//editor_game_wrapper(editor); \ No newline at end of file diff --git a/editor_listen.js b/editor_listen.js new file mode 100644 index 0000000..0be858c --- /dev/null +++ b/editor_listen.js @@ -0,0 +1,193 @@ +editor_listen_wrapper = function (editor) { + + editor.constructor.prototype.listen = function () { + + editor.dom.body.onmousedown = editor.uifunctions.body_click; + + editor.dom.eui.oncontextmenu = function (e) { e.preventDefault() } // 自定义了右键菜单, 阻止默认行为 + editor.dom.midMenu.oncontextmenu = function (e) { e.preventDefault() } + + editor.dom.eui.ondblclick = editor.uifunctions.map_doubleClick + + editor.dom.eui.onmousedown = editor.uifunctions.map_ondown + editor.dom.eui.onmousemove = editor.uifunctions.map_onmove + editor.dom.eui.onmouseup = editor.uifunctions.map_onup + editor.dom.eui.onmouseout = editor.uifunctions.map_onmoveout + + editor.dom.mid.onmousewheel = editor.uifunctions.map_mousewheel + + editor.uivalues.shortcut = editor.config.get('shortcut', { 48: 0, 49: 0, 50: 0, 51: 0, 52: 0, 53: 0, 54: 0, 55: 0, 56: 0, 57: 0 }); + editor.dom.body.onkeydown = editor.uifunctions.body_shortcut + + editor.uivalues.scrollBarHeight = editor.uifunctions.getScrollBarHeight(); + editor.dom.iconExpandBtn.style.display = 'block'; + editor.dom.iconExpandBtn.innerText = editor.uivalues.folded ? "展开素材区" : "折叠素材区"; + editor.dom.iconExpandBtn.onclick = editor.uifunctions.fold_material_click + + editor.dom.iconLib.onmousedown = editor.uifunctions.material_ondown + editor.dom.iconLib.onmousemove = editor.uifunctions.material_onmove + editor.dom.iconLib.onmouseup = editor.uifunctions.material_onup + editor.dom.iconLib.oncontextmenu = function (e) { e.preventDefault() } + + editor.dom.extraEvent.onmouseup = editor.uifunctions.extraEvent_click + editor.dom.chooseThis.onmouseup = editor.uifunctions.chooseThis_click + editor.dom.chooseInRight.onmouseup = editor.uifunctions.chooseInRight_click + editor.dom.copyLoc.onmouseup = editor.uifunctions.copyLoc_click + editor.dom.pasteLoc.onmouseup = editor.uifunctions.pasteLoc_click + editor.dom.clearEvent.onmouseup = editor.uifunctions.clearEvent_click + editor.dom.clearLoc.onmouseup = editor.uifunctions.clearLoc_click + editor.dom.undoFloor.onclick = editor.uifunctions.undoFloor_click + editor.dom.selectFloorBtn.onclick = editor.uifunctions.selectFloorBtn_click + editor.dom.editorTheme.onchange = editor.uifunctions.editorTheme_onchange + + editor.dom.lastUsed.onmouseup = editor.uifunctions.lastUsed_click; + editor.dom.lastUsed.oncontextmenu = function (e) { e.preventDefault(); } + editor.dom.clearLastUsedBtn.onclick = editor.uifunctions.clearLastUsedBtn_click; + editor.dom.showMovable.onchange = editor.uifunctions.showMovable_onchange; + + editor.dom.brushMod.onchange = editor.uifunctions.brushMod_onchange + if (editor.dom.brushMod2) editor.dom.brushMod2.onchange = editor.uifunctions.brushMod2_onchange; + if (editor.dom.brushMod3) editor.dom.brushMod3.onchange = editor.uifunctions.brushMod3_onchange; + if (editor.dom.brushMod4) editor.dom.brushMod4.onchange = editor.uifunctions.brushMod4_onchange; + + editor.dom.layerMod.onchange = editor.uifunctions.layerMod_onchange + if (editor.dom.layerMod2) editor.dom.layerMod2.onchange = editor.uifunctions.layerMod2_onchange; + if (editor.dom.layerMod3) editor.dom.layerMod3.onchange = editor.uifunctions.layerMod3_onchange; + + editor.uifunctions.viewportButtons_func() + + window.onbeforeunload = function () { + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return '你尚未保存地图,确定退出么?'; + } + return null; + } + } + + editor.constructor.prototype.mobile_listen = function () { + if (!editor.isMobile) return; + + var mobileview = document.getElementById('mobileview'); + var mid = document.getElementById('mid'); + var right = document.getElementById('right'); + // var mobileeditdata = document.getElementById('mobileeditdata'); + + + editor.showdataarea = function (callShowMode) { + mid.style = 'z-index:-1;opacity: 0;'; + right.style = 'z-index:-1;opacity: 0;'; + // mobileeditdata.style = ''; + if (callShowMode) editor.mode.showMode(editor.dom.editModeSelect.value); + editor.uifunctions.hideMidMenu(); + } + mobileview.children[0].onclick = function () { + editor.showdataarea(true) + } + mobileview.children[1].onclick = function () { + mid.style = 'z-index:110'; + right.style = 'z-index:-1;opacity: 0;'; + // mobileeditdata.style = 'z-index:-1;opacity: 0;'; + editor.lastClickId = ''; + } + mobileview.children[3].onclick = function () { + mid.style = 'z-index:-1;opacity: 0;'; + right.style = 'z-index:110'; + // mobileeditdata.style = 'z-index:-1;opacity: 0;'; + editor.lastClickId = ''; + } + + /* + var gettrbyid = function () { + if (!editor.lastClickId) return false; + thisTr = document.getElementById(editor.lastClickId); + input = thisTr.children[2].children[0].children[0]; + field = thisTr.children[0].getAttribute('title'); + cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); + return [thisTr, input, field, cobj]; + } + mobileeditdata.children[0].onclick = function () { + var info = gettrbyid() + if (!info) return; + info[1].ondblclick() + } + mobileeditdata.children[1].onclick = function () { + var info = gettrbyid() + if (!info) return; + printf(info[2]) + } + mobileeditdata.children[2].onclick = function () { + var info = gettrbyid() + if (!info) return; + printf(info[0].children[1].getAttribute('title')) + } + */ + + //===== + + document.body.ontouchstart = document.body.onmousedown; + document.body.onmousedown = null; + + editor.dom.eui.ontouchstart = editor.dom.eui.onmousedown + editor.dom.eui.onmousedown = null + editor.dom.eui.ontouchmove = editor.dom.eui.onmousemove + editor.dom.eui.onmousemove = null + editor.dom.eui.ontouchend = editor.dom.eui.onmouseup + editor.dom.eui.onmouseup = null + + + editor.dom.chooseThis.ontouchstart = editor.dom.chooseThis.onmousedown + editor.dom.chooseThis.onmousedown = null + editor.dom.chooseInRight.ontouchstart = editor.dom.chooseInRight.onmousedown + editor.dom.chooseInRight.onmousedown = null + editor.dom.copyLoc.ontouchstart = editor.dom.copyLoc.onmousedown + editor.dom.copyLoc.onmousedown = null + editor.dom.pasteLoc.ontouchstart = editor.dom.pasteLoc.onmousedown + editor.dom.pasteLoc.onmousedown = null + editor.dom.clearLoc.ontouchstart = editor.dom.clearLoc.onmousedown + editor.dom.clearLoc.onmousedown = null + + // 不使用以下6语句, 会使得素材区手机无法拖动, 手机的框选素材只能放弃, 要通过弹框实现框选 + // editor.dom.iconLib.ontouchstart = editor.dom.iconLib.onmousedown + // editor.dom.iconLib.onmousedown = null + // editor.dom.iconLib.ontouchmove = editor.dom.iconLib.onmousemove + // editor.dom.iconLib.onmousemove = null + // editor.dom.iconLib.ontouchend = editor.dom.iconLib.onmouseup + // editor.dom.iconLib.onmouseup = null + } + + editor.constructor.prototype.mode_listen = function (callback) { + + // 这里的函数还没有写jsdoc, 通过_func()的方式先完成分类 + + editor.uifunctions.newIdIdnum_func() + editor.uifunctions.changeId_func() + editor.uifunctions.copyPasteEnemyItem_func(); + + editor.uifunctions.selectFloor_func() + editor.uifunctions.saveFloor_func() + editor.uifunctions.openDoc_func(); + + editor.uifunctions.newMap_func() + + editor.uifunctions.createNewMaps_func() + + editor.uifunctions.changeFloorId_func() + editor.uifunctions.changeFloorSize_func() + + // editor.uifunctions.fixCtx_func() + editor.uifunctions.appendPic_func(); + /* + editor.uifunctions.selectAppend_func() + editor.uifunctions.selectFileBtn_func() + editor.uifunctions.changeColorInput_func() + editor.uifunctions.picClick_func() + editor.uifunctions.appendConfirm_func() + */ + + editor.dom.editModeSelect.onchange = editor.mode.editModeSelect_onchange + + if (Boolean(callback)) callback(); + } + +} \ No newline at end of file diff --git a/editor_mappanel.js b/editor_mappanel.js new file mode 100644 index 0000000..a65a760 --- /dev/null +++ b/editor_mappanel.js @@ -0,0 +1,1190 @@ +editor_mappanel_wrapper = function (editor) { + + // 暂时先 注释+分类 内部函数未完成重构 + + /** + * 在绘图区格子内画一个随机色块 + */ + editor.uifunctions.fillPos = function (pos) { + editor.dom.euiCtx.fillStyle = '#' + ~~(Math.random() * 8) + ~~(Math.random() * 8) + ~~(Math.random() * 8); + var grid = _getGridByPos({ x: pos.x, y: pos.y }); + editor.dom.euiCtx.fillRect(grid.x + grid.size * 3 / 8, grid.y + grid.size * 3 / 8, grid.size / 4, grid.size / 4); + } + + /** + * 从鼠标点击返回可用的组件内坐标 + */ + editor.uifunctions.eToLoc = function (e) { + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop + var xx = e.clientX, yy = e.clientY + if (editor.isMobile) { xx = e.touches[0].clientX, yy = e.touches[0].clientY } + editor.loc = { + 'x': scrollLeft + xx - editor.dom.mid.offsetLeft - editor.dom.mapEdit.offsetLeft, + 'y': scrollTop + yy - editor.dom.mid.offsetTop - editor.dom.mapEdit.offsetTop, + 'size': editor.isMobile ? (32 * innerWidth * 0.96 / core.__PIXELS__) : 32 + }; + return editor.loc; + } + + /** + * 组件内坐标转地图位置 + * @param {Boolean} addViewportOffset 是否加上大地图的偏置 + */ + editor.uifunctions.locToPos = function (loc, addViewportOffset) { + if (editor.uivalues.bigmap) { + var info = editor.uivalues.bigmapInfo; + var size = loc.size / 32 * info.size; + return { + x: core.clamp(Math.floor((loc.x - info.left) / size), 0, editor.currentFloorData.width - 1), + y: core.clamp(Math.floor((loc.y - info.top) / size), 0, editor.currentFloorData.height - 1), + } + } + + var offsetX = 0, offsetY = 0; + if (addViewportOffset) { + offsetX = core.bigmap.offsetX / 32; + offsetY = core.bigmap.offsetY / 32; + } + return { 'x': ~~(loc.x / loc.size) + offsetX, 'y': ~~(loc.y / loc.size) + offsetY } + } + + /** + * editor.dom.eui.ondblclick + * 双击地图可以选中素材 + */ + editor.uifunctions.map_doubleClick = function (e) { + if (editor.uivalues.bindSpecialDoor.loc != null) return; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + editor.setSelectBoxFromInfo(editor[editor.layerMod][pos.y][pos.x], true); + return; + } + + /** + * 用于鼠标移出map后清除状态 + */ + editor.uifunctions.clearMapStepStatus = function () { + if (editor.uivalues.mouseOutCheck > 1) { + editor.uivalues.mouseOutCheck--; + setTimeout(editor.uifunctions.clearMapStepStatus, 1000); + return; + } + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + } + + /** + * editor.dom.eui.onmousedown + * + 绑定机关门事件的选择怪物 + * + 右键进入菜单 + * + 非绘图时选中 + * + 绘图时画个矩形在那个位置 + */ + editor.uifunctions.map_ondown = function (e) { + editor.uivalues.selectedArea = null; + editor.uivalues.lastMoveE = e; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + editor.pos = pos; + + if (editor.uivalues.bindSpecialDoor.loc != null) { + var x = pos.x, y = pos.y, id = (editor.map[y][x] || {}).id; + // 检测是否是怪物 + if (id && editor.game.getEnemy(id)) { + var locstr = x + "," + y, index = editor.uivalues.bindSpecialDoor.enemys.indexOf(locstr); + if (index >= 0) editor.uivalues.bindSpecialDoor.enemys.splice(index, 1); + else editor.uivalues.bindSpecialDoor.enemys.push(locstr); + editor.drawEventBlock(); + editor.uifunctions._extraEvent_bindSpecialDoor_doAction(); + } + return false; + } + // if (e.buttons == 2) { // 挪到onup + // editor.uifunctions.showMidMenu(e.clientX, e.clientY); + // return false; + // } + if (!selectBox.isSelected()) { + editor_mode.onmode('nextChange'); + editor_mode.onmode('loc'); + //editor_mode.loc(); + editor.uifunctions.showTips(6); + editor.uivalues.startPos = pos; + editor.dom.euiCtx.strokeStyle = '#FF0000'; + editor.dom.euiCtx.lineWidth = 2; + if (editor.isMobile) editor.uifunctions.showMidMenu(e.clientX, e.clientY); + return false; + } + + editor.uivalues.holdingPath = 1; + editor.uivalues.mouseOutCheck = 2; + setTimeout(editor.uifunctions.clearMapStepStatus); + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.stepPostfix = []; + editor.uivalues.stepPostfix.push(pos); + if (editor.brushMod == 'line') editor.uifunctions.fillPos(pos); + return false; + } + + var _getGridByPos = function (pos) { + if (editor.uivalues.bigmap) { + var info = editor.uivalues.bigmapInfo; + return { x: info.left + info.size * pos.x, y: info.top + info.size * pos.y, size: info.size }; + } else { + return { x: 32 * pos.x - core.bigmap.offsetX, y: 32 * pos.y - core.bigmap.offsetY, size: 32 }; + } + } + + var _setMarksHightlight = function (marks, index) { + for (var i = 0; i < marks.length; ++i) { + if (marks[i].classList.contains('highlight')) { + if (i == index) index = null; + else marks[i].classList.remove('highlight'); + } + } + if (index != null) { + marks[index].classList.add('highlight'); + } + } + + /** + * editor.dom.eui.onmousemove + * + 非绘图模式时维护起止位置并画箭头 + * + 绘图模式时找到与队列尾相邻的鼠标方向的点画个矩形 + */ + editor.uifunctions.map_onmove = function (e) { + editor.uivalues.lastMoveE = e; + if (!editor.uivalues.bigmap && !editor.isMobile && editor.dom.midMenu.style.display == 'none') { + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc); + _setMarksHightlight(Array.from(editor.dom.mapColMark.children[0].rows[0].cells), pos.x); + _setMarksHightlight(Array.from(editor.dom.mapRowMark.children[0].rows).map(function (tr) { return tr.cells[0]; }), pos.y); + } + + if (!selectBox.isSelected()) { + if (editor.uivalues.startPos == null) return; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + if (editor.uivalues.endPos != null && editor.uivalues.endPos.x == pos.x && editor.uivalues.endPos.y == pos.y) return; + var startGrid = _getGridByPos(editor.uivalues.startPos), endGrid; + if (editor.uivalues.endPos != null) { + endGrid = _getGridByPos(editor.uivalues.endPos); + editor.dom.euiCtx.clearRect(Math.min(startGrid.x, endGrid.x), Math.min(startGrid.y, endGrid.y), + (Math.abs(editor.uivalues.startPos.x - editor.uivalues.endPos.x) + 1) * startGrid.size, + (Math.abs(editor.uivalues.startPos.y - editor.uivalues.endPos.y) + 1) * startGrid.size); + } + editor.uivalues.endPos = pos; + endGrid = _getGridByPos(editor.uivalues.endPos); + if (editor.uivalues.startPos != null) { + if (editor.uivalues.startPos.x != editor.uivalues.endPos.x || editor.uivalues.startPos.y != editor.uivalues.endPos.y) { + if (e.buttons == 2) { + // 右键拖拽: 画选的区域 + var x0 = editor.uivalues.startPos.x; + var y0 = editor.uivalues.startPos.y; + var x1 = editor.uivalues.endPos.x; + var y1 = editor.uivalues.endPos.y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + // draw rect + editor.dom.euiCtx.clearRect(0, 0, editor.dom.euiCtx.canvas.width, editor.dom.euiCtx.canvas.height); + editor.dom.euiCtx.fillStyle = 'rgba(0, 127, 255, 0.4)'; + var grid = _getGridByPos({ x: x0, y: y0 }); + editor.dom.euiCtx.fillRect(grid.x, grid.y, grid.size * (x1 - x0 + 1), grid.size * (y1 - y0 + 1)); + } else { + // 左键拖拽: 画箭头 + core.drawArrow('eui', startGrid.x + startGrid.size / 2, startGrid.y + startGrid.size / 2, endGrid.x + endGrid.size / 2, endGrid.y + endGrid.size / 2); + } + } + } + // editor_mode.onmode('nextChange'); + // editor_mode.onmode('loc'); + //editor_mode.loc(); + return false; + } + + if (editor.uivalues.holdingPath == 0) { + return false; + } + editor.uivalues.mouseOutCheck = 2; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + var pos0 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1] + var directionDistance = [pos.y - pos0.y, pos0.x - pos.x, pos0.y - pos.y, pos.x - pos0.x] + var max = 0, index = 4; + for (var i = 0; i < 4; i++) { + if (directionDistance[i] > max) { + index = i; + max = directionDistance[i]; + } + } + var pos = [{ 'x': 0, 'y': 1 }, { 'x': -1, 'y': 0 }, { 'x': 0, 'y': -1 }, { 'x': 1, 'y': 0 }, false][index] + if (pos) { + pos.x += pos0.x; + pos.y += pos0.y; + if (editor.brushMod == 'line') editor.uifunctions.fillPos(pos); + else { + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var x1 = pos.x; + var y1 = pos.y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + // draw rect + editor.dom.euiCtx.clearRect(0, 0, editor.dom.euiCtx.canvas.width, editor.dom.euiCtx.canvas.height); + editor.dom.euiCtx.fillStyle = 'rgba(0, 127, 255, 0.4)'; + var grid = _getGridByPos({ x: x0, y: y0 }); + editor.dom.euiCtx.fillRect(grid.x, grid.y, + grid.size * (x1 - x0 + 1), grid.size * (y1 - y0 + 1)); + } + editor.uivalues.stepPostfix.push(pos); + } + return false; + } + + editor.uifunctions.map_onmoveout = function () { + if (!editor.uivalues.bigmap && !editor.isMobile) { + _setMarksHightlight(Array.from(editor.dom.mapColMark.children[0].rows[0].cells)); + _setMarksHightlight(Array.from(editor.dom.mapRowMark.children[0].rows).map(function (tr) { return tr.cells[0]; })); + } + } + + /** + * editor.dom.eui.onmouseup + * + 非绘图模式时, 交换首末点的内容 + * + 绘图模式时, 根据画线/画矩形/画tileset 做对应的绘制 + */ + editor.uifunctions.map_onup = function (ee) { + editor.uivalues.selectedArea = null; + ee.preventDefault(); + ee.stopPropagation(); + var e = editor.uivalues.lastMoveE; + if (e.buttons == 2 && (editor.uivalues.endPos == null || (editor.uivalues.startPos.x == editor.uivalues.endPos.x && editor.uivalues.startPos.y == editor.uivalues.endPos.y))) { + editor.uifunctions.showMidMenu(e.clientX, e.clientY); + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + return false; + } + if (!selectBox.isSelected()) { + if (e.buttons == 2) { + // 右键拖拽: 选中区域 + printf('已经选中该区域') + editor.uivalues.selectedArea = Object.assign({}, editor.uivalues.startPos, { x1: editor.uivalues.endPos.x, y1: editor.uivalues.endPos.y }); + // 后续的处理 + } else { + // 左键拖拽: 交换 + editor.savePreMap(); + // editor.movePos(editor.uivalues.startPos, editor.uivalues.endPos); + editor.exchangePos(editor.uivalues.startPos, editor.uivalues.endPos); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + } + editor.uivalues.startPos = editor.uivalues.endPos = null; + return false; + } + + editor.uivalues.holdingPath = 0; + if (editor.uivalues.stepPostfix && editor.uivalues.stepPostfix.length) { + editor.savePreMap(); + if (editor.brushMod !== 'line') { + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var x1 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1].x; + var y1 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1].y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + editor.uivalues.stepPostfix = []; + for (var jj = y0; jj <= y1; jj++) { + for (var ii = x0; ii <= x1; ii++) { + editor.uivalues.stepPostfix.push({ x: ii, y: jj }) + } + } + } + var useBrushMode = editor.brushMod == 'tileset'; + if (editor.uivalues.stepPostfix.length == 1 && (editor.uivalues.tileSize[0] > 1 || editor.uivalues.tileSize[1] > 1)) { + useBrushMode = true; + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + editor.uivalues.stepPostfix = []; + for (var jj = y0; jj < y0 + editor.uivalues.tileSize[1]; ++jj) { + for (var ii = x0; ii < x0 + editor.uivalues.tileSize[0]; ++ii) { + if (jj >= editor[editor.layerMod].length || ii >= editor[editor.layerMod][0].length) continue; + editor.uivalues.stepPostfix.push({ x: ii, y: jj }); + } + } + } + if (useBrushMode && core.tilesets.indexOf(editor.info.images) !== -1) { + var imgWidth = ~~(core.material.images.tilesets[editor.info.images].width / 32); + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var idnum = editor.info.idnum; + var pmod = function (a, b) { return (a % b + b) % b; } + for (var ii = 0; ii < editor.uivalues.stepPostfix.length; ii++) { + var dx = pmod(editor.uivalues.stepPostfix[ii].x - x0, editor.uivalues.tileSize[0]); + var dy = pmod(editor.uivalues.stepPostfix[ii].y - y0, editor.uivalues.tileSize[1]); + editor[editor.layerMod][editor.uivalues.stepPostfix[ii].y][editor.uivalues.stepPostfix[ii].x] = editor.ids[editor.indexs[idnum + dx + dy * imgWidth]]; + } + } else { + // 检测是否是填充模式 + if (editor.uivalues.stepPostfix.length == 1 && editor.brushMod == 'fill') { + editor.uivalues.stepPostfix = editor.uifunctions._fillMode_bfs(editor[editor.layerMod], editor.uivalues.stepPostfix[0].x, editor.uivalues.stepPostfix[0].y, + editor[editor.layerMod][0].length, editor[editor.layerMod].length); + } + for (var ii = 0; ii < editor.uivalues.stepPostfix.length; ii++) { + var currx = editor.uivalues.stepPostfix[ii].x, curry = editor.uivalues.stepPostfix[ii].y; + editor[editor.layerMod][curry][currx] = editor.info; + // 检查上下楼梯绑定 + if (editor.layerMod == 'map' && editor.info && editor.info.id == 'upFloor') { + editor.currentFloorData.changeFloor[currx + "," + curry] = { "floorId": ":next", "stair": "downFloor" }; + editor.drawEventBlock(); + } + if (editor.layerMod == 'map' && editor.info && editor.info.id == 'downFloor') { + editor.currentFloorData.changeFloor[currx + "," + curry] = { "floorId": ":before", "stair": "upFloor" }; + editor.drawEventBlock(); + } + } + + } + // console.log(editor.map); + if (editor.info.y != null) { + var found = false; + editor.uivalues.lastUsed.forEach(function (one) { + if (one.id == editor.info.id) { + found = true; + one.recent = new Date().getTime(); + one.frequent = (one.frequent || 0) + 1; + } + }) + if (!found) { + editor.uivalues.lastUsed.push(Object.assign({}, editor.info, { recent: new Date().getTime(), frequent: 1 })); + } + editor.config.set("lastUsed", editor.uivalues.lastUsed); + } + editor.updateMap(); + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uifunctions.highlightSaveFloorButton(); + } + return false; + } + + /** + * bfs找寻和某点相连的全部相同图块坐标 + */ + editor.uifunctions._fillMode_bfs = function (array, x, y, maxWidth, maxHeight) { + var _getNumber = function (x, y) { + if (x < 0 || y < 0 || x >= maxWidth || y >= maxHeight) return null; + return array[y][x].idnum || array[y][x] || 0; + } + var number = _getNumber(x, y) || 0; + var visited = {}, result = []; + var list = [{ x: x, y: y }]; + while (list.length != 0) { + var next = list.shift(), key = next.x + "," + next.y; + if (visited[key]) continue; + visited[key] = true; + result.push(next); + [[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(function (dir) { + var nx = next.x + dir[0], ny = next.y + dir[1]; + if (_getNumber(nx, ny) == number) { + list.push({ x: nx, y: ny }); + } + }); + } + return result; + } + + /** + * editor.dom.mid.onmousewheel + * 在地图编辑区域滚轮切换楼层 + */ + editor.uifunctions.map_mousewheel = function (e) { + var wheel = function (direct) { + var index = editor.core.floorIds.indexOf(editor.currentFloorId); + var toId = editor.currentFloorId; + + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return; + } + + if (direct > 0 && index < editor.core.floorIds.length - 1) + toId = editor.core.floorIds[index + 1]; + else if (direct < 0 && index > 0) + toId = editor.core.floorIds[index - 1]; + else return; + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = toId; + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(toId); + } + + try { + if (e.wheelDelta) + wheel(Math.sign(e.wheelDelta)) + else if (e.detail) + wheel(Math.sign(e.detail)); + } + catch (e) { + console.error(e); + } + return false; + } + + editor.uifunctions.undoFloor_click = function () { + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return; + } + + var toId = editor.uivalues.recentFloors.pop(); + if (toId == null || toId == editor.currentFloorId) return; + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = toId; + editor.changeFloor(toId); + } + + editor.uifunctions.selectFloorBtn_click = function () { + editor.uievent.selectFloor(null, '选择楼层', function (floorId) { + if (!floorId || floorId == editor.currentFloorId) return; + + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + printe('请先保存地图!'); + return; + } + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = floorId; + editor.changeFloor(floorId); + }) + } + + editor.uifunctions.editorTheme_onchange = function () { + var theme = editor.dom.editorTheme.value; + editor.config.set('theme', theme); + document.getElementById('color_css').href = '_server/css/' + theme + '.css'; + } + + /** + * 显示右键菜单 + */ + editor.uifunctions.showMidMenu = function (x, y) { + // --- copy + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + + // 检测是否是上下楼 + var thisevent = editor.map[editor.pos.y][editor.pos.x]; + var extraEvent = editor.dom.extraEvent, parent = extraEvent.parentElement; + if (thisevent == 0) { + parent.removeChild(extraEvent); + parent.appendChild(extraEvent); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定出生点为此点'; + } else if (editor.currentFloorData.changeFloor[editor.pos.x + "," + editor.pos.y]) { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '跳转到目标传送点'; + } else if (thisevent.id == 'upFloor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定上楼事件'; + } + else if (thisevent.id == 'downFloor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定下楼事件'; + } + else if (['leftPortal', 'rightPortal', 'downPortal', 'upPortal'].indexOf(thisevent.id) >= 0) { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定楼传事件'; + } + else if (thisevent.id == 'specialDoor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定机关门事件'; + } + else editor.dom.extraEvent.style.display = 'none'; + + editor.dom.chooseThis.children[0].innerHTML = '选中此点' + '(' + editor.pos.x + ',' + editor.pos.y + ')' + editor.dom.copyLoc.children[0].innerHTML = '复制此事件'; + editor.dom.pasteLoc.children[0].innerHTML = '粘贴到此事件'; + editor.dom.midMenu.style = 'top:' + (y + scrollTop) + 'px;left:' + (x + scrollLeft) + 'px;'; + } + + /** + * 隐藏右键菜单 + */ + editor.uifunctions.hideMidMenu = function () { + editor.uivalues.lastMoveE = { buttons: 0, clientX: 0, clientY: 0 }; + editor.dom.midMenu.style = 'display:none'; + } + + /** + * editor.dom.extraEvent.onmousedown + * 菜单 附加点操作 + */ + editor.uifunctions.extraEvent_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + + var thisevent = editor.map[editor.pos.y][editor.pos.x]; + return editor.uifunctions._extraEvent_bindStartPoint(thisevent) + || editor.uifunctions._extraEvent_changeFloor() + || editor.uifunctions._extraEvent_bindStair(thisevent) + || editor.uifunctions._extraEvent_bindSpecialDoor(thisevent); + } + + /** + * 绑定该空地点为起始点 + */ + editor.uifunctions._extraEvent_bindStartPoint = function (thisevent) { + if (thisevent != 0) return false; + if (!confirm('再次确认,你想绑定此点为出生点吗?')) return false; + editor.mode.onmode('tower'); + editor.mode.addAction(["change", "['firstData']['floorId']", editor.currentFloorId]); + editor.mode.addAction(["change", "['firstData']['hero']['loc']['x']", editor.pos.x]); + editor.mode.addAction(["change", "['firstData']['hero']['loc']['y']", editor.pos.y]); + editor.mode.onmode('save', function () { + core.firstData.floorId = editor.currentFloorId; + core.firstData.hero.loc.x = editor.pos.x; + core.firstData.hero.loc.y = editor.pos.y; + editor.drawPosSelection(); + editor.drawEventBlock(); + editor.mode.tower(); + printf('绑定初始点成功'); + }); + } + + editor.uifunctions._extraEvent_changeFloor = function () { + var changeFloor = editor.currentFloorData.changeFloor[editor.pos.x + "," + editor.pos.y]; + if (!changeFloor) return false; + core.status.hero.loc = { x: editor.pos.x, y: editor.pos.y, direction: "up" }; + var targetLoc = changeFloor.loc ? { x: changeFloor.loc[0], y: changeFloor.loc[1] } : null; + var info = core.events._changeFloor_getInfo(changeFloor.floorId, changeFloor.stair, targetLoc); + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = info.floorId; + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(info.floorId, function () { + editor.pos.x = info.heroLoc.x; + editor.pos.y = info.heroLoc.y; + editor.setViewport(32 * (editor.pos.x - core.__HALF_SIZE__), 32 * (editor.pos.y - core.__HALF_SIZE__)); + editor.drawPosSelection(); + }); + return true; + } + + /** + * 绑定该楼梯的楼传事件 + */ + editor.uifunctions._extraEvent_bindStair = function (thisevent) { + if (['upFloor', 'downFloor', 'leftPortal', 'rightPortal', 'upPortal', 'downPortal'].indexOf(thisevent.id) < 0) + return false; + var loc = editor.pos.x + "," + editor.pos.y; + if (thisevent.id == 'upFloor') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": "downFloor" }; + } + else if (thisevent.id == 'downFloor') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":before", "stair": "upFloor" }; + } + else if (thisevent.id == 'leftPortal' || thisevent.id == 'rightPortal') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": ":symmetry_x" } + } + else if (thisevent.id == 'upPortal' || thisevent.id == 'downPortal') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": ":symmetry_y" } + } + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + editor.drawPosSelection(); + editor.drawEventBlock(); + editor_mode.showMode('loc'); + printf('添加楼梯事件成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + }); + return true; + } + + /** + * 绑定该机关门的事件 + */ + editor.uifunctions._extraEvent_bindSpecialDoor = function (thisevent) { + if (thisevent.id != 'specialDoor') return false; + var number = parseInt(prompt("请输入该机关门的怪物数量", "0")) || 0; + if (number <= 0) return true; + editor.uivalues.bindSpecialDoor.n = number; + editor.uivalues.bindSpecialDoor.loc = editor.pos.x + ',' + editor.pos.y; + editor.uivalues.bindSpecialDoor.enemys = []; + printf("请点击选择" + number + "个怪物;切换楼层或刷新页面取消操作。"); + } + + /** + * 确定绑定该机关门的事件 + * cancel:是否取消此模式 + */ + editor.uifunctions._extraEvent_bindSpecialDoor_doAction = function (cancel) { + var bindSpecialDoor = editor.uivalues.bindSpecialDoor; + if (cancel) { + bindSpecialDoor.loc = null; + bindSpecialDoor.enemys = []; + bindSpecialDoor.n = 0; + editor.drawEventBlock(); + printf(""); + return; + } + if (bindSpecialDoor.loc == null || bindSpecialDoor.enemys.length != bindSpecialDoor.n) return; + // 添加机关门自动事件 + var doorFlag = "flag:door_" + editor.currentFloorId + "_" + bindSpecialDoor.loc.replace(',', '_'); + editor.currentFloorData.autoEvent[bindSpecialDoor.loc] = { + '0': { + "condition": doorFlag + "==" + bindSpecialDoor.n, + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + { "type": "openDoor" }, + { "type": "setValue", "name": doorFlag, "operator": "=", "value": "null" }, + ] + } + }; + bindSpecialDoor.enemys.forEach(function (loc) { + if (!editor.currentFloorData.afterBattle[loc]) + editor.currentFloorData.afterBattle[loc] = []; + editor.currentFloorData.afterBattle[loc].push({ "type": "setValue", "name": doorFlag, "operator": "+=", "value": "1" }); + }); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + editor.drawEventBlock(); + editor.drawPosSelection(); + editor_mode.showMode('loc'); + printf('绑定机关门事件成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + }); + bindSpecialDoor.loc = null; + bindSpecialDoor.enemys = []; + bindSpecialDoor.n = 0; + } + + /** + * editor.dom.chooseThis.onmousedown + * 菜单 选中此点 + */ + editor.uifunctions.chooseThis_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + selectBox.isSelected(false); + + editor_mode.onmode('nextChange'); + editor_mode.onmode('loc'); + //editor_mode.loc(); + if (editor.isMobile) editor.showdataarea(false); + return false; + } + + /** + * editor.dom.chooseInRight.onmousedown + * 菜单 在素材区选中此图块 + */ + editor.uifunctions.chooseInRight_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + var thisevent = editor[editor.layerMod][editor.pos.y][editor.pos.x]; + editor.setSelectBoxFromInfo(thisevent, true); + return false; + } + + /** + * editor.dom.copyLoc.onmousedown + * 菜单 复制此事件 + */ + editor.uifunctions.copyLoc_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + editor_mode.onmode(''); + editor.uivalues.copyedInfo = editor.copyFromPos(); + printf('该点事件已复制'); + return false; + } + + /** + * editor.dom.pasteLoc.onmousedown + * 菜单 移动此事件 + */ + editor.uifunctions.pasteLoc_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + if (!editor.uivalues.copyedInfo) { + printe("没有复制的事件"); + return false; + } + editor.savePreMap(); + editor_mode.onmode(''); + editor.pasteToPos(editor.uivalues.copyedInfo); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('粘贴到事件成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.drawPosSelection(); + }); + return false; + } + + /** + * editor.dom.clearEvent.onmousedown + * 菜单 仅清空此点事件 + */ + editor.uifunctions.clearEvent_click = function (e) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + editor.clearPos(false); + editor.uifunctions.unhighlightSaveFloorButton(); + return false; + } + + /** + * editor.dom.clearLoc.onmousedown + * 菜单 清空此点及事件 + */ + editor.uifunctions.clearLoc_click = function (e) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + editor.clearPos(true); + editor.uifunctions.unhighlightSaveFloorButton(); + return false; + } + + /** + * editor.dom.showMovable.onchange + * 点击【】 + */ + editor.uifunctions.showMovable_onchange = function () { + printf('此模式下将显示每个点的不可通行状态。
      请注意,修改了图块属性的不可出入方向后需要刷新才会正确显示在地图上。'); + editor.uivalues.showMovable = editor.dom.showMovable.checked; + editor.drawEventBlock(); + } + + /** + * editor.dom.brushMod.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod_onchange = function () { + editor.brushMod = editor.dom.brushMod.value; + if (editor.brushMod == 'fill') { + printf('填充模式下,将会用选中的素材替换所有和目标点联通的相同素材'); + } + } + + /** + * editor.dom.brushMod2.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod2_onchange = function () { + editor.brushMod = editor.dom.brushMod2.value; + } + + /** + * editor.dom.brushMod3.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod3_onchange = function () { + if (!editor.config.get('alertTileModeV2.7') && + !confirm("从V2.7开始,请直接素材区拖框进行绘制区域。\n\n点取消将不再显示此提示。")) { + editor.config.set('alertTileModeV2.7', true); + } + printf('tileset平铺模式下可以按选中tileset素材,并在地图上拖动来一次绘制一个区域'); + editor.brushMod = editor.dom.brushMod3.value; + } + + editor.uifunctions.brushMod4_onchange = function () { + printf('填充模式下,将会用选中的素材替换所有和目标点联通的相同素材'); + editor.brushMod = editor.dom.brushMod4.value; + } + + editor.uifunctions.setLayerMod = function (layer) { + editor.layerMod = layer; + var canvas = ['ev', 'ev2'].concat(editor.dom.canvas); + canvas.forEach(function (one) { + editor.dom[one + 'c'].style.opacity = 1; + }); + if (layer != 'map') { + canvas.filter(function (one) { + return one + 'map' != editor.layerMod + }).forEach(function (one) { + editor.dom[one + 'c'].style.opacity = 0.3; + }); + } + } + + /** + * editor.dom.layerMod.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod_onchange = function () { + editor.uifunctions.setLayerMod(editor.dom.layerMod.value); + } + + /** + * editor.dom.layerMod2.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod2_onchange = function () { + editor.uifunctions.setLayerMod('bgmap'); + } + + /** + * editor.dom.layerMod3.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod3_onchange = function () { + editor.uifunctions.setLayerMod('fgmap'); + } + + editor.uifunctions.triggerBigmap = function () { + editor.uivalues.bigmap = !editor.uivalues.bigmap; + if (editor.uivalues.bigmap) { + editor.dom.bigmapBtn.classList.add('highlight'); + printf("已进入大地图模式"); + } else { + editor.dom.bigmapBtn.classList.remove('highlight'); + editor.setViewport(32 * (editor.pos.x - core.__HALF_SIZE__), 32 * (editor.pos.y - core.__HALF_SIZE__)); + printf("已退出大地图模式"); + } + editor.dom.ebmCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + editor.updateMap(); + editor.drawPosSelection(); + } + + /** + * 移动大地图可视窗口的绑定 + */ + editor.uifunctions.viewportButtons_func = function () { + var pressTimer = null; + for (var ii = 0, node; node = editor.dom.viewportButtons.children[ii]; ii++) { + if (ii == 4) { + // 大地图 + node.onclick = function () { + editor.uifunctions.triggerBigmap(); + } + continue; + } + (function (x, y) { + var move = function () { + editor.moveViewport(x, y); + } + node.onmousedown = function () { + clearTimeout(pressTimer); + pressTimer = setTimeout(function () { + pressTimer = -1; + var f = function () { + if (pressTimer != null) { + move(); + setTimeout(f, 150); + } + } + f(); + }, 500); + }; + node.onmouseup = function () { + if (pressTimer > 0) { + clearTimeout(pressTimer); + move(); + } + pressTimer = null; + } + })([-1, 0, 0, 1][ii], [0, -1, 1, 0][ii]); + } + } + + editor.uifunctions.selectFloor_func = function () { + var selectFloor = document.getElementById('selectFloor'); + editor.game.getFloorFileList(function (floors) { + var outstr = []; + floors[0].forEach(function (floor) { + outstr.push(["\n'].join('')); + }); + selectFloor.innerHTML = outstr.join(''); + selectFloor.value = core.status.floorId; + selectFloor.onchange = function () { + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(selectFloor.value); + } + }); + } + + editor.uifunctions.highlightSaveFloorButton = function () { + var saveFloor = document.getElementById('saveFloor'); + saveFloor.classList.add('highlight'); + } + + editor.uifunctions.unhighlightSaveFloorButton = function () { + var saveFloor = document.getElementById('saveFloor'); + saveFloor.classList.remove('highlight'); + } + + editor.uifunctions.saveFloor_func = function () { + var saveFloor = document.getElementById('saveFloor'); + editor_mode.saveFloor = function () { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('保存成功'); + editor.uifunctions.unhighlightSaveFloorButton() + }); + } + saveFloor.onclick = editor_mode.saveFloor; + } + + editor.uifunctions.openDoc_func = function () { + var openDoc = document.getElementById('openDoc'); + openDoc.onclick = function () { + if (editor.isMobile) { + if (!confirm('你确定要打开帮助文档吗?')) return; + window.location = '/_docs/'; + } else { + window.open('/_docs/', '_blank'); + } + } + } + + editor.uifunctions.lastUsed_click = function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + e.stopPropagation(); + if (editor.isMobile) return false; + editor.uivalues.tileSize = [1, 1]; + + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop + var px = scrollLeft + e.clientX - editor.dom.mid2.offsetLeft - editor.dom.lastUsedDiv.offsetLeft + editor.dom.lastUsedDiv.scrollLeft, + py = scrollTop + e.clientY - editor.dom.mid2.offsetTop - editor.dom.lastUsedDiv.offsetTop + editor.dom.lastUsedDiv.scrollTop; + var x = parseInt(px / 32), y = parseInt(py / 32); + if (x == core.__SIZE__ - 1) return false; + var index = x + (core.__SIZE__ - 1) * y; + if (index >= editor.uivalues.lastUsed.length) return; + var lastUsed = editor.uivalues.lastUsed.sort(function (a, b) { + if ((a.istop || 0) != (b.istop || 0)) return (b.istop || 0) - (a.istop || 0); + return (b[editor.uivalues.lastUsedType] || 0) - (a[editor.uivalues.lastUsedType] || 0); + }); + + if (e.button == 2) { + lastUsed[index].istop = lastUsed[index].istop ? 0 : 1; + printf("已" + (lastUsed[index].istop ? '置顶' : '取消置顶') + "该图块"); + editor.config.set('lastUsed', editor.uivalues.lastUsed); + editor.updateLastUsedMap(); + return false; + } + var one = Object.assign({}, lastUsed[index]); + delete one['recent']; + delete one['frequent']; + delete one['istop']; + editor.setSelectBoxFromInfo(one); + return false; + } + + editor.uifunctions.clearLastUsedBtn_click = function () { + if (editor.isMobile) return; + + if (confirm("你确定要清理全部最近使用图块么?\n所有最近使用和最常使用图块(含置顶图块)都将被清除;此过程不可逆!")) { + editor.uivalues.lastUsed = []; + editor.config.set('lastUsed', []); + editor.updateLastUsedMap(); + editor.dom.lastUsedDiv.scroll(0, 0); + } + } + + ///////////////////////////////////////////////////////////////////////////// + + + editor.constructor.prototype.copyFromPos = function (pos) { + editor.uivalues.tileSize = [1, 1]; + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var x0 = pos.x, y0 = pos.y, x1 = pos.x1, y1 = pos.y1; + if (x1 == null) x1 = x0; + if (y1 == null) y1 = y0; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + var result = { w: x1 - x0 + 1, h: y1 - y0 + 1, layer: editor.layerMod, data: [] }; + for (var i = x0; i <= x1; ++i) { + for (var j = y0; j <= y1; ++j) { + var map = core.clone(editor[editor.layerMod][j][i]); + var events = {}; + fields.forEach(function (v) { + events[v] = core.clone(editor.currentFloorData[v][i + ',' + j]); + }) + result.data.push({ map: map, events: events }); + } + } + return result; + } + + editor.constructor.prototype.pasteToPos = function (info, pos) { + editor.uivalues.tileSize = [1, 1]; + if (info == null) return; + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var w = info.w || 1, h = info.h || 1, layer = info.layer || 'map'; + var data = core.clone(info.data || []); + for (var i = pos.x; i < pos.x + w; ++i) { + for (var j = pos.y; j < pos.y + h; ++j) { + var one = data.shift(); + if (j >= editor[editor.layerMod].length || i >= editor[editor.layerMod][0].length) continue; + editor[editor.layerMod][j][i] = core.clone(one.map); + if (layer == 'map' && editor.layerMod == 'map') { + fields.forEach(function (v) { + if (one.events[v] == null) delete editor.currentFloorData[v][i + "," + j]; + else editor.currentFloorData[v][i + "," + j] = core.clone(one.events[v]); + }); + } + } + } + } + + editor.constructor.prototype.movePos = function (startPos, endPos, callback) { + editor.uivalues.tileSize = [1, 1]; + if (!startPos || !endPos) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var copyed = editor.copyFromPos(startPos); + editor.pasteToPos({ w: 1, h: 1, layer: 'map', data: [{ map: 0, events: {} }] }, startPos); + editor.pasteToPos(copyed, endPos); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('移动事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.exchangePos = function (startPos, endPos, callback) { + editor.uivalues.tileSize = [1, 1]; + if (!startPos || !endPos) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var startInfo = editor.copyFromPos(startPos); + var endInfo = editor.copyFromPos(endPos); + editor.pasteToPos(startInfo, endPos); + editor.pasteToPos(endInfo, startPos); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('交换事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.savePreMap = function () { + if (editor.map.length * editor.map[0].length >= 4096) return; + var dt = {}; + editor.dom.maps.forEach(function (one) { + dt[one] = editor[one]; + }); + if (editor.uivalues.preMapData.length == 0 + || !core.same(editor.uivalues.preMapData[editor.uivalues.preMapData.length - 1], dt)) { + editor.uivalues.preMapData.push(core.clone(dt)); + if (editor.uivalues.preMapData.length > editor.uivalues.preMapMax) { + editor.uivalues.preMapData.shift(); + } + } + } + + editor.constructor.prototype.clearPos = function (clearPos, pos, callback) { + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var x0 = pos.x, y0 = pos.y, x1 = pos.x1, y1 = pos.y1; + if (x1 == null) x1 = x0; + if (y1 == null) y1 = y0; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + editor.uifunctions.hideMidMenu(); + editor.savePreMap(); + editor.info = 0; + editor_mode.onmode(''); + for (var i = x0; i <= x1; ++i) { + for (var j = y0; j <= y1; ++j) { + if (j >= editor[editor.layerMod].length || i >= editor[editor.layerMod][0].length) continue; + if (clearPos) + editor[editor.layerMod][j][i] = 0; + if (editor.layerMod == 'map') { + fields.forEach(function (v) { + delete editor.currentFloorData[v][i + "," + j]; + }); + } + } + } + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf(clearPos ? '清空该点和事件成功' : '只清空该点事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/editor_materialpanel.js b/editor_materialpanel.js new file mode 100644 index 0000000..3ff0f5d --- /dev/null +++ b/editor_materialpanel.js @@ -0,0 +1,243 @@ +editor_materialpanel_wrapper = function (editor) { + + // 由于历史遗留原因, 以下变量作为全局变量使用 + // selectBox + + window.selectBox=document.getElementById('selectBox') + selectBox._isSelected=false + selectBox.isSelected=function(value){ + if(value!=null){ + selectBox._isSelected=value; + editor.dom.dataSelection.style.display=value?'':'none' + } + return selectBox._isSelected + } + + var locToPos = function (loc) { + return { 'x': ~~(loc.x / loc.size), 'y': ~~(loc.y / loc.size) }; + } + + editor.uifunctions.getScrollBarHeight = function () { + var outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.width = "100px"; + outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps + + document.body.appendChild(outer); + + var widthNoScroll = outer.offsetWidth; + // force scrollbars + outer.style.overflow = "scroll"; + + // add innerdiv + var inner = document.createElement("div"); + inner.style.width = "100%"; + outer.appendChild(inner); + + var widthWithScroll = inner.offsetWidth; + + // remove divs + outer.parentNode.removeChild(outer); + + return widthNoScroll - widthWithScroll; + } + + /** + * editor.dom.iconExpandBtn.onclick + */ + editor.uifunctions.fold_material_click = function () { + if (editor.uivalues.folded) { + if (confirm("你想要展开素材吗?\n展开模式下将显示全素材内容。")) { + editor.config.set('folded', false, function() { + window.location.reload(); + }); + } + } else { + var perCol = parseInt(prompt("请输入折叠素材模式下每列的个数:", "50")) || 0; + if (perCol > 0) { + editor.config.set('foldPerCol', perCol, false); + editor.config.set('folded', true, function() { + window.location.reload(); + }); + } + } + } + + /** + * editor.dom.iconLib.onmousedown + * 素材区的单击/拖拽事件 + */ + editor.uifunctions.material_ondown = function (e) { + e.stopPropagation(); + e.preventDefault(); + editor.uivalues.lastMoveMaterE=e; + if (!editor.isMobile && e.clientY >= editor.dom.iconLib.offsetHeight - editor.uivalues.scrollBarHeight) return; + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + editor.uivalues.startLoc={ + 'x': scrollLeft + e.clientX + editor.dom.iconLib.scrollLeft - right.offsetLeft - editor.dom.iconLib.offsetLeft, + 'y': scrollTop + e.clientY + editor.dom.iconLib.scrollTop - right.offsetTop - editor.dom.iconLib.offsetTop, + 'px': e.clientX, + 'py': e.clientY, + 'size': 32 + }; + } + + /** + * editor.dom.iconLib.onmousemove + * 素材区的单击/拖拽事件 + */ + editor.uifunctions.material_onmove = function (e) { + e.stopPropagation(); + e.preventDefault(); + editor.uivalues.lastMoveMaterE=e; + if (!editor.uivalues.startLoc) return; + var pos0 = locToPos(editor.uivalues.startLoc); + + editor.dom.dataSelection.style.left = 32 * pos0.x + 'px'; + editor.dom.dataSelection.style.top = 32 * pos0.y + 'px'; + editor.dom.dataSelection.style.width = e.clientX - editor.uivalues.startLoc.px + 'px'; + editor.dom.dataSelection.style.height = e.clientY - editor.uivalues.startLoc.py + 'px'; + editor.dom.dataSelection.style.display = 'block'; + } + + /** + * editor.dom.iconLib.onmouseup + * 素材区的单击/拖拽事件 + */ + editor.uifunctions.material_onup = function (ee) { + var startLoc = editor.uivalues.startLoc; + editor.uivalues.startLoc = null; + + var e=editor.uivalues.lastMoveMaterE; + if (!editor.isMobile && e.clientY >= editor.dom.iconLib.offsetHeight - editor.uivalues.scrollBarHeight) return; + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + var loc = { + 'x': scrollLeft + e.clientX + editor.dom.iconLib.scrollLeft - right.offsetLeft - editor.dom.iconLib.offsetLeft, + 'y': scrollTop + e.clientY + editor.dom.iconLib.scrollTop - right.offsetTop - editor.dom.iconLib.offsetTop, + 'size': 32 + }; + editor.loc = loc; + editor.uivalues.tileSize = [1,1]; + var pos0 = locToPos(startLoc); + var pos = locToPos(loc); + for (var spriter in editor.widthsX) { + if (pos.x >= editor.widthsX[spriter][1] && pos.x < editor.widthsX[spriter][2]) { + var ysize = spriter.endsWith('48') ? 48 : 32; + loc.ysize = ysize; + pos.images = editor.widthsX[spriter][0]; + pos.y = ~~(loc.y / loc.ysize); + if (!editor.uivalues.folded && core.tilesets.indexOf(pos.images) == -1) pos.x = editor.widthsX[spriter][1]; + var autotiles = core.material.images['autotile']; + if (pos.images == 'autotile') { + var imNames = Object.keys(autotiles); + if (editor.uivalues.folded) { + pos.y = Math.min(pos.y, imNames.length - 1); + pos.images = imNames[pos.y]; + } else { + if ((pos.y + 1) * ysize > editor.widthsX[spriter][3]) + pos.y = ~~(editor.widthsX[spriter][3] / ysize) - 4; + else { + for (var i = 0; i < imNames.length; i++) { + if (pos.y >= 4 * i && pos.y < 4 * (i + 1)) { + pos.images = imNames[i]; + pos.y = 4 * i; + } + } + } + } + } + else { + var height = editor.widthsX[spriter][3], col = height / ysize; + if (spriter == 'terrains') col += 2; + if (editor.uivalues.folded && core.tilesets.indexOf(pos.images) == -1) { + col = (pos.x == editor.widthsX[spriter][2] - 1) ? ((col - 1) % editor.uivalues.foldPerCol + 1) : editor.uivalues.foldPerCol; + } + pos.y = Math.min(pos.y, col - 1); + } + + selectBox.isSelected(true); + // console.log(pos,core.material.images[pos.images].height) + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize - 6 + 'px'; + editor.dom.dataSelection.style.width = 32 - 6 + 'px'; + + if (pos.x == 0 && pos.y == 0) { + // editor.info={idnum:0, id:'empty','images':'清除块', 'y':0}; + editor.info = 0; + } else if (pos.x == 0 && pos.y == 1) { + editor.info = editor.ids[editor.indexs[17]]; + } else { + if (autotiles[pos.images]) editor.info = { 'images': pos.images, 'y': 0 }; + else if (core.tilesets.indexOf(pos.images) != -1) editor.info = { 'images': pos.images, 'y': pos.y, 'x': pos.x - editor.widthsX[spriter][1] }; + else { + var y = pos.y; + if (editor.uivalues.folded) { + y += editor.uivalues.foldPerCol * (pos.x - editor.widthsX[spriter][1]); + } + if (pos.images == 'terrains') y -= 2; + editor.info = { 'images': pos.images, 'y': y } + } + + for (var idindex = 0; idindex < editor.ids.length; idindex++) { + if ((core.tilesets.indexOf(pos.images) != -1 && editor.info.images == editor.ids[idindex].images + && editor.info.y == editor.ids[idindex].y && editor.info.x == editor.ids[idindex].x) + || (Object.prototype.hasOwnProperty.call(autotiles, pos.images) && editor.info.images == editor.ids[idindex].id + && editor.info.y == editor.ids[idindex].y) + || (core.tilesets.indexOf(pos.images) == -1 && editor.info.images == editor.ids[idindex].images + && editor.info.y == editor.ids[idindex].y) + ) { + + editor.info = editor.ids[idindex]; + break; + } + } + + if (editor.info.isTile && (editor.isMobile || e.button == 2)) { //这段改一改之类的应该能给手机用,就不删了 + var v = prompt("请输入该额外素材区域绑定宽高,以逗号分隔", "1,1"); + if (v != null && /^\d+,\d+$/.test(v)) { + v = v.split(","); + var x = parseInt(v[0]), y = parseInt(v[1]); + var widthX = editor.widthsX[editor.info.images]; + if (x <= 0 || y <= 0 || editor.info.x + x > widthX[2] - widthX[1] || 32*(editor.info.y + y) > widthX[3]) { + alert("不合法的输入范围,已经越界"); + } else { + editor.uivalues.tileSize = [x, y]; + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize*y - 6 + 'px'; + editor.dom.dataSelection.style.width = 32*x - 6 + 'px'; + } + } + } + if (editor.info.isTile && !editor.isMobile && e.button != 2) { //左键拖拽框选 + + var x = pos.x-pos0.x+1, y = pos.y-pos0.y+1; + var widthX = editor.widthsX[editor.info.images]; + // 懒得仔细处理了, 只允许左上往右下拉 + if (x <= 0 || y <= 0 || pos0.x < widthX[1]){ + + } else { + editor.info = editor.ids[idindex-(x-1)-(y-1)*(widthX[2]-widthX[1])]; + editor.uivalues.tileSize = [x, y]; + editor.dom.dataSelection.style.left = pos0.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos0.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize*y - 6 + 'px'; + editor.dom.dataSelection.style.width = 32*x - 6 + 'px'; + } + + } + + } + editor.uifunctions.showBlockInfo(JSON.parse(JSON.stringify(editor.info))); + editor_mode.onmode('nextChange'); + editor_mode.onmode('enemyitem'); + editor.updateLastUsedMap(); + //editor_mode.enemyitem(); + } + } + } + +} \ No newline at end of file diff --git a/editor_mode.js b/editor_mode.js new file mode 100644 index 0000000..151a3b8 --- /dev/null +++ b/editor_mode.js @@ -0,0 +1,371 @@ +editor_mode = function (editor) { + var core = editor.core; + + function editor_mode() { + this.ids = { + 'loc': 'left2', + 'enemyitem': 'left3', + 'floor': 'left4', + 'tower': 'left5', + 'functions': 'left8', + + 'map': 'left', + 'appendpic': 'left1', + + 'commonevent': 'left9', + 'plugins': 'left10', + } + this._ids = {} + this.dom = {} + this.actionList = []; + this.mode = 'tower'; // 初始默认显示全塔属性 + this.info = {}; + this.appendPic = {}; + this.doubleClickMode = 'change'; + } + + editor_mode.prototype.init = function (callback) { + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.init_dom_ids = function (callback) { + + Object.keys(editor_mode.ids).forEach(function (v) { + editor_mode.dom[v] = document.getElementById(editor_mode.ids[v]); + editor_mode._ids[editor_mode.ids[v]] = v; + }); + + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.indent = function (field) { + var num = '\t'; + if (field.indexOf("['main']") === 0) return 0; + if (field === "['special']") return 0; + return num; + } + + editor_mode.prototype.addAction = function (action) { + editor_mode.actionList.push(action); + } + + editor_mode.prototype.doActionList = function (mode, actionList, callback) { + if (actionList.length == 0) return; + printf('修改中...'); + var cb = function (objs_) { + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; + var str = '修改成功!'; + if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.name == 'template') + str += '
      请注意:全塔属性的name尚未修改,请及时予以设置。'; + if (mode == 'enemyitem') { + if (editor.info && editor.info.idnum) { + var block = editor.core.maps.blocksInfo[editor.info.idnum]; + if (block.doorInfo != null && block.doorInfo.keys != null && Object.keys(block.doorInfo.keys).length > 0 + && block.trigger != 'openDoor') { + str += "
      你修改了门信息,但触发器未改成openDoor,请修改否则无法撞击开门。" + } + } + if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { + if (core.getFaceDownId(editor_mode.info.id) != editor_mode.info.id) { + str += "
      绑定行走图朝向后只需要对应设置朝下怪物的属性,会自动同步而无需修改其他朝向的属性。" + } + } + } + printf(str); + if (callback) callback(); + } + switch (mode) { + case 'loc': + editor.file.editLoc(editor_mode.pos.x, editor_mode.pos.y, actionList, function (objs_) { + cb(objs_); + editor.drawPosSelection(); + }); + break; + case 'enemyitem': + if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { + editor.file.editEnemy(editor_mode.info.id, actionList, cb); + } else if (editor_mode.info.images == 'items') { + editor.file.editItem(editor_mode.info.id, actionList, cb); + } else { + editor.file.editMapBlocksInfo(editor_mode.info.idnum, actionList, cb); + } + break; + case 'floor': + editor.file.editFloor(actionList, cb); + break; + case 'tower': + editor.file.editTower(actionList, cb); + break; + case 'functions': + editor.file.editFunctions(actionList, cb); + break; + case 'commonevent': + editor.file.editCommonEvent(actionList, cb); + break; + case 'plugins': + editor.file.editPlugins(actionList, cb); + break; + default: + break; + } + } + + editor_mode.prototype.onmode = function (mode, callback) { + if (editor_mode.mode != mode) { + if (mode === 'save') editor_mode.doActionList(editor_mode.mode, editor_mode.actionList, callback); + if (editor_mode.mode === 'nextChange' && mode) editor_mode.showMode(mode); + if (mode !== 'save') editor_mode.mode = mode; + editor_mode.actionList = []; + } + } + + editor_mode.prototype.showMode = function (mode) { + for (var name in this.dom) { + editor_mode.dom[name].style = 'z-index:-1;opacity: 0;'; + } + editor_mode.dom[mode].style = ''; + editor_mode.doubleClickMode = 'change'; + // clear + editor.drawEventBlock(); + if (editor_mode[mode]) editor_mode[mode](); + editor.dom.editModeSelect.value = mode; + if (!selectBox.isSelected()) editor.uifunctions.showTips(); + } + + editor_mode.prototype.change = function (value) { + editor_mode.onmode('nextChange'); + editor_mode.onmode(value); + if (editor.isMobile) editor.showdataarea(false); + } + + + editor_mode.prototype.checkUnique = function (thiseval) { + if (!(thiseval instanceof Array)) return false; + var map = {}; + for (var i = 0; i < thiseval.length; ++i) { + if (map[thiseval[i]]) { + alert("警告:存在重复定义!"); + return false; + } + map[thiseval[i]] = true; + } + return true; + } + + editor_mode.prototype.checkFloorIds = function (thiseval) { + if (!editor_mode.checkUnique(thiseval)) return false; + var oldvalue = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds; + fs.readdir('project/floors', function (err, data) { + if (err) { + printe(err); + throw Error(err); + } + var newfiles = thiseval.map(function (v) { return v + '.js' }); + var notExist = ''; + for (var name, ii = 0; name = newfiles[ii]; ii++) { + if (data.indexOf(name) === -1) notExist = name; + } + if (notExist) { + var discard = confirm('文件' + notExist + '不存在, 保存会导致工程无法打开, 是否放弃更改'); + if (discard) { + editor.file.editTower([['change', "['main']['floorIds']", oldvalue]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('已放弃floorIds的修改,请F5进行刷新'); + }); + } + } + }); + return true + } + + editor_mode.prototype.checkImages = function (thiseval, directory) { + if (!directory) return true; + if (!editor_mode.checkUnique(thiseval)) return false; + fs.readdir(directory, function (err, data) { + if (err) { + printe(err); + throw Error(err); + } + var notExist = null; + thiseval.map(function (v) { + var name = v.indexOf('.') < 0 ? (v+'.png') : v; + if (data.indexOf(name) < 0) notExist = name; + return name; + }); + if (notExist) { + alert('警告!图片' + notExist + '不存在!保存可能导致工程无法打开,请及时修改!'); + } + }); + return true; + } + + editor_mode.prototype.changeDoubleClickModeByButton = function (mode) { + ({ + delete: function () { + printf('下一次双击表格的项删除,切换下拉菜单可取消;编辑后需刷新浏览器生效。'); + editor_mode.doubleClickMode = mode; + }, + add: function () { + printf('下一次双击表格的项则在同级添加新项,切换下拉菜单可取消;编辑后需刷新浏览器生效。'); + editor_mode.doubleClickMode = mode; + } + }[mode])(); + } + + ///////////////////////////////////////////////////////////////////////////// + + editor_mode.prototype.loc = function (callback) { + //editor.pos={x: 0, y: 0}; + if (!core.isset(editor.pos)) return; + editor_mode.pos = editor.pos; + document.getElementById('pos_a6771a78_a099_417c_828f_0a24851ebfce').innerText = editor_mode.pos.x + ',' + editor_mode.pos.y; + + var objs = []; + editor.file.editLoc(editor_mode.pos.x, editor_mode.pos.y, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_3d846fc4_7644_44d1_aa04_433d266a73df').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + editor.drawPosSelection(); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.enemyitem = function (callback) { + //editor.info=editor.ids[editor.indexs[201]]; + if (!core.isset(editor.info)) return; + + if (Object.keys(editor.info).length !== 0 && editor.info.idnum != 17) editor_mode.info = editor.info;//避免editor.info被清空导致无法获得是物品还是怪物 + + if (!core.isset(editor_mode.info.id)) { + // document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML = ''; + document.getElementById('newIdIdnum').style.display = 'block'; + document.getElementById('enemyItemTable').style.display = 'none'; + document.getElementById('changeId').style.display = 'none'; + return; + } + + document.getElementById('newIdIdnum').style.display = 'none'; + document.getElementById('enemyItemTable').style.display = 'block'; + document.getElementById('changeId').style.display = 'block'; + + var objs = []; + if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { + editor.file.editEnemy(editor_mode.info.id, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + } else if (editor_mode.info.images == 'items') { + editor.file.editItem(editor_mode.info.id, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + } else { + /* document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML=''; + return; */ + editor.file.editMapBlocksInfo(editor_mode.info.idnum, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + } + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.floor = function (callback) { + var objs = []; + editor.file.editFloor([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_4a3b1b09_b2fb_4bdf_b9ab_9f4cdac14c74').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.tower = function (callback) { + var objs = []; + editor.file.editTower([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_b6a03e4c_5968_4633_ac40_0dfdd2c9cde5').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.functions = function (callback) { + var objs = []; + editor.file.editFunctions([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_e260a2be_5690_476a_b04e_dacddede78b3').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.commonevent = function (callback) { + var objs = []; + editor.file.editCommonEvent([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_b7bf0124_99fd_4af8_ae2f_0017f04a7c7d').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.plugins = function (callback) { + var objs = []; + editor.file.editPlugins([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_e2c034ec_47c6_48ae_8db8_4f8f32fea2d6').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + ///////////////////////////////////////////////////////////////////////////// + + /** + * editor.dom.editModeSelect.onchange + */ + editor_mode.prototype.editModeSelect_onchange = function () { + editor_mode.change(editor.dom.editModeSelect.value); + } + + editor_mode.prototype.listen = function (callback) { + // 移动至 editor_listen.js -> editor.constructor.prototype.mode_listen + } + + var editor_mode = new editor_mode(); + editor_mode.init_dom_ids(); + + return editor_mode; +} +//editor_mode = editor_mode(editor); \ No newline at end of file diff --git a/editor_multi.js b/editor_multi.js new file mode 100644 index 0000000..30d8250 --- /dev/null +++ b/editor_multi.js @@ -0,0 +1,515 @@ + + +editor_multi = function () { + + var editor_multi = {}; + + var extraKeys = { + "Ctrl-/": function (cm) { cm.toggleComment(); }, + "Ctrl-B": function (cm) { ternServer.jumpToDef(cm); }, + "Ctrl-Q": function (cm) { ternServer.rename(cm); }, + "Cmd-F": CodeMirror.commands.findPersistent, + "Ctrl-F": CodeMirror.commands.findPersistent, + "Ctrl-R": CodeMirror.commands.replaceAll, + "Ctrl-D": function (cm) { cm.foldCode(cm.getCursor()); }, + "Ctrl-O": function () { editor_multi.openUrl('/_docs/#/api'); }, + "Ctrl-P": function () { editor_multi.openUrl('https://h5mota.com/plugins/'); } + }; + + var codeEditor = CodeMirror.fromTextArea(document.getElementById("multiLineCode"), { + lineNumbers: true, + matchBrackets: true, + indentUnit: 4, + tabSize: 4, + indentWithTabs: true, + smartIndent: true, + mode: { name: "javascript", globalVars: true, localVars: true }, + lineWrapping: true, + continueComments: "Enter", + gutters: ["CodeMirror-lint-markers", "CodeMirror-linenumbers", "CodeMirror-foldgutter"], + lint: true, + autocomplete: true, + autoCloseBrackets: true, + styleActiveLine: true, + extraKeys: extraKeys, + foldGutter: true, + inputStyle: "textarea", + highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true } + }); + + var commandsName = { + 'Ctrl-/': '注释当前选中行(Ctrl+/)', + 'Ctrl-B': '跳转到定义(Ctrl+B)', + 'Ctrl-Q': '重命名变量(Ctrl+Q)', + 'Ctrl-F': '查找(Ctrl+F)', + 'Ctrl-R': '全部替换(Ctrl+R)', + 'Ctrl-D': '折叠或展开块(Ctrl+D)', + 'Ctrl-O': '打开API列表(Ctrl+O)', + 'Ctrl-P': '打开在线插件列表(Ctrl+P)' + }; + + document.getElementById('codemirrorCommands').innerHTML = + "" + + Object.keys(commandsName).map(function (name) { + return "" + }).join(''); + + var coredef = terndefs_f6783a0a_522d_417e_8407_94c67b692e50[2]; + Object.keys(core.material.enemys).forEach(function (name) { + coredef.core.material.enemys[name] = { + "!type": "enemy", + "!doc": core.material.enemys[name].name || "怪物" + } + }); + Object.keys(core.material.bgms).forEach(function (name) { + coredef.core.material.bgms[name] = { + "!type": "audio", + "!doc": "背景音乐" + } + }); + Object.keys(core.material.sounds).forEach(function (name) { + coredef.core.material.sounds[name] = { + "!type": "audio", + "!doc": "音效" + } + }); + Object.keys(core.material.animates).forEach(function (name) { + coredef.core.material.animates[name] = { + "!type": "animate", + "!doc": "动画" + } + }); + Object.keys(core.material.images).forEach(function (name) { + if (core.material.images[name] instanceof Image) { + coredef.core.material.images[name] = { + "!type": "image", + "!doc": "系统图片" + } + } else { + coredef.core.material.images[name] = { + "!doc": name == 'autotile' ? '自动元件' : name == 'tilesets' ? '额外素材' : name == 'images' ? '自定义图片' : '系统图片' + } + for (var v in core.material.images[name]) { + coredef.core.material.images[name][v] = { + "!type": "image", + } + } + } + + }) + Object.keys(core.material.items).forEach(function (name) { + coredef.core.material.items[name] = { + "!type": "item", + "!doc": core.material.items[name].name || "道具" + } + }); + functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys.getSpecials().forEach(function (one) { + var name = one[1]; + if (name instanceof Function) name = name({}); + coredef.core.enemys.hasSpecial["!doc"] += name + "(" + one[0] + "); "; + }); + Object.keys(core.canvas).forEach(function (name) { + coredef.core.canvas[name] = { + "!type": "CanvasRenderingContext2D", + "!doc": "系统画布" + } + }); + Object.keys(core.status.maps).forEach(function (name) { + coredef.core.status.maps[name] = { + "!type": "floor", + "!doc": core.status.maps[name].title || '' + } + coredef.core.status.bgmaps[name] = { + "!type": "[[number]]", + "!doc": core.status.maps[name].title || '' + } + coredef.core.status.fgmaps[name] = { + "!type": "[[number]]", + "!doc": core.status.maps[name].title || '' + } + }); + Object.keys(core.status.shops).forEach(function (id) { + coredef.core.status.shops[id] = { + "!doc": core.status.shops[id].textInList || "全局商店" + } + }); + Object.keys(core.status.textAttribute).forEach(function (id) { + coredef.core.status.textAttribute[id] = {}; + }); + // --- 转发函数 + for (var name in coredef.core) { + if (typeof coredef.core[name] === 'object') { + for (var funcname in coredef.core[name]) { + var one = coredef.core[name][funcname] || {}; + var type = one["!type"] || ""; + if (type.startsWith("fn(")) { + var forwardname = (functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a[name] || {})[funcname] ? '脚本编辑' : name; + coredef.core[funcname] = { + "!type": one["!type"], + "!doc": one["!doc"] + "
      (转发到" + forwardname + "中)" + }; + if (one["!url"]) coredef.core[funcname]["!url"] = one["!url"]; + } + } + for (var funcname in core[name]) { + if (!(core[name][funcname] instanceof Function) || funcname.charAt(0) == '_' || coredef.core[name][funcname]) continue; + var parameterInfo = /^\s*function\s*[\w_$]*\(([\w_,$\s]*)\)\s*\{/.exec(core[name][funcname].toString()); + var parameters = (parameterInfo == null ? "" : parameterInfo[1]) + .replace(/\s*/g, '').replace(/,/g, ', ').split(', ') + .filter(function (one) { return one.trim() != ''; }) + .map(function (one) { return one.trim() + ': ?'; }).join(', '); + coredef.core[funcname] = coredef.core[name][funcname] = { + "!type": "fn(" + parameters + ")" + } + } + } + } + + Object.keys(core.values).forEach(function (id) { + var one = data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.values._data[id]; + if (!one) return; + coredef.core.values[id] = { + "!type": "number", + "!doc": one._data, + } + }); + Object.keys(core.flags).forEach(function (id) { + var one = data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.flags._data[id]; + if (!one) return; + coredef.core.flags[id] = { + "!type": id == 'statusBarItems' ? '[string]' : 'bool', + "!doc": one._data, + } + }); + + var ternServer = new CodeMirror.TernServer({ + defs: terndefs_f6783a0a_522d_417e_8407_94c67b692e50, + plugins: { + doc_comment: true, + complete_strings: true, + }, + useWorker: false + }); + + editor_multi.ternServer = ternServer; + editor_multi.codeEditor = codeEditor; + + codeEditor.on("cursorActivity", function (cm) { + var cursor = cm.getCursor(); + if (codeEditor.getOption("autocomplete") && !(cursor.line == 0 && cursor.ch == 0)) { + ternServer.updateArgHints(cm); + ternServer.showDocs(cm); + } + }); + + var ctrlRelease = new Date(); + codeEditor.on("keyup", function (cm, event) { + var date = new Date(); + if (event.keyCode == 17 || event.keyCode == 91) { // ctrl, cmd + ctrlRelease = date; + } + else if (codeEditor.getOption("autocomplete") && !event.ctrlKey && date - ctrlRelease >= 1000 && ( + (event.keyCode >= 65 && event.keyCode <= 90) || + (!event.shiftKey && event.keyCode == 190) || (event.shiftKey && event.keyCode == 189))) { + try { + ternServer.complete(cm); + } catch (e) { + } + } + }); + + editor_multi.id = ''; + editor_multi.isString = false; + editor_multi.lintAutocomplete = false; + + var lastOffset = {}; + + editor_multi.show = function () { + if (typeof (selectBox) !== typeof (undefined)) selectBox.isSelected(false); + var valueNow = codeEditor.getValue(); + //try{eval('function _asdygakufyg_() { return '+valueNow+'\n}');editor_multi.lintAutocomplete=true;}catch(ee){} + if (valueNow.slice(0, 8) === 'function') editor_multi.lintAutocomplete = true; + editor_multi.setLint(); + document.getElementById('left7').style = ''; + } + editor_multi.hide = function () { + document.getElementById('left7').style = 'z-index:-1;opacity: 0;'; + } + editor_multi.setLint = function () { + if (editor_multi.lintAutocomplete) { + codeEditor.setOption("lint", { + options: { + esversion: 2021 + } + }); + } else { + codeEditor.setOption("lint", false); + } + codeEditor.setOption("autocomplete", editor_multi.lintAutocomplete); + document.getElementById("lintCheckbox").checked = editor_multi.lintAutocomplete; + } + editor_multi.toggerLint = function () { + editor_multi.lintAutocomplete = document.getElementById("lintCheckbox").checked; + editor_multi.setLint(); + } + + editor_multi.indent = function (field) { + if (typeof (editor) !== typeof (undefined) && editor && editor.mode && editor.mode.indent) return editor.mode.indent(field); + return '\t'; + } + + var _format = function () { + if (!editor_multi.lintAutocomplete) return; + var offset = (codeEditor.getScrollInfo() || {}).top || 0; + _setValue(beautifier.js(codeEditor.getValue(), { + brace_style: "collapse-preserve-inline", + indent_with_tabs: true, + jslint_happy: true + })); + codeEditor.scrollTo(0, offset); + } + + var _setValue = function (val) { + codeEditor.setValue(val || ''); + ternServer.delDoc('doc'); + ternServer.addDoc('doc', new CodeMirror.Doc(val || '', 'javascript')); + } + + editor_multi.format = function () { + if (!editor_multi.lintAutocomplete) { + alert("只有代码才能进行格式化操作!"); + return; + } + _format(); + } + + editor_multi.hasError = function () { + if (!editor_multi.lintAutocomplete) return false; + return JSHINT.errors.filter(function (e) { + return e.code.startsWith("E") + }).length > 0; + } + + var _previewButton = document.getElementById('editor_multi_preview'); + + _previewButton.onclick = function () { + if (!editor_multi.preview) return; + _format(); + if (editor_multi.hasError()) { + alert("当前好像存在严重的语法错误,请处理后再预览。"); + return; + } + editor.uievent.previewEditorMulti(editor_multi.preview, codeEditor.getValue()); + } + + editor_multi.import = function (id_, args) { + var thisTr = document.getElementById(id_); + if (!thisTr) return false; + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var comment = thisTr.children[1].getAttribute('title'); + if (!input.type || input.type !== 'textarea') return false; + editor_multi.id = id_; + editor_multi.isString = false; + editor_multi.lintAutocomplete = false; + editor_multi.preview = args.preview; + _previewButton.style.display = editor_multi.preview ? 'inline' : 'none'; + if (args.lint === true) editor_multi.lintAutocomplete = true; + if ((!input.value || input.value == 'null') && args.template) + input.value = '"' + args.template + '"'; + if ((!input.value || input.value == 'null') && editor_mode.mode == 'plugins') + input.value = '"function () {\\n\\t// 在此增加新插件\\n\\t\\n}"'; + // if ((!input.value || input.value == 'null') && args) + if (input.value.slice(0, 1) === '"' || args.string) { + editor_multi.isString = true; + _setValue(JSON.parse(input.value) || ''); + } else { + var num = editor_multi.indent(field); + eval('var tobj=' + (input.value || 'null')); + var tmap = {}; + var tstr = JSON.stringify(tobj, function (k, v) { + if (typeof (v) === typeof ('') && v.slice(0, 8) === 'function') { + var id_ = editor.util.guid(); + tmap[id_] = v.toString(); + return id_; + } else return v + }, num); + for (var id_ in tmap) { + tstr = tstr.replace('"' + id_ + '"', tmap[id_]) + } + _setValue(tstr || ''); + } + editor_multi.show(); + codeEditor.scrollTo(0, lastOffset[editor_multi.id] || 0); + return true; + } + + editor_multi.cancel = function () { + if (editor_multi.id && editor_multi.id != 'callFromBlockly' && editor_multi.id != 'importFile') { + lastOffset[editor_multi.id] = (codeEditor.getScrollInfo() || {}).top; + } + editor_multi.hide(); + editor_multi.id = ''; + multiLineArgs = [null, null, null]; + } + + editor_multi.confirm = function (keep) { + if (editor_multi.hasError()) { + alert("当前好像存在严重的语法错误,请处理后再保存。\n严重的语法错误可能会导致整个编辑器的崩溃。"); + return; + } + + if (!editor_multi.id) { + editor_multi.id = ''; + return; + } + + if (editor_multi.id === 'callFromBlockly') { + // ----- 自动格式化 + _format(); + editor_multi.multiLineDone(keep); + return; + } + + if (editor_multi.id === 'importFile') { + _format(); + editor_multi.writeFileDone(keep); + return; + } + + var setvalue = function (value) { + var thisTr = document.getElementById(editor_multi.id); + var input = thisTr.children[2].children[0].children[0]; + if (editor_multi.isString) { + input.value = JSON.stringify(value); + } else { + eval('var tobj=' + (value || 'null')); + var tmap = {}; + var tstr = JSON.stringify(tobj, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + tmap[id_] = v.toString(); + return id_; + } else return v + }, 4); + for (var id_ in tmap) { + tstr = tstr.replace('"' + id_ + '"', JSON.stringify(tmap[id_])) + } + input.value = tstr; + } + if (!keep) { + editor_multi.id = ''; + editor_multi.hide(); + } else { + alert('写入成功!'); + } + input.onchange(); + } + lastOffset[editor_multi.id] = (codeEditor.getScrollInfo() || {}).top; + // ----- 自动格式化 + _format(); + setvalue(codeEditor.getValue() || ''); + } + + editor_multi.doCommand = function (select) { + var value = select.value; + select.selectedIndex = 0; + if (extraKeys[value]) { + extraKeys[value](codeEditor); + } + } + + editor_multi.openUrl = function (url) { + if (editor.isMobile && !confirm('你确定要离开本页面么?')) return; + window.open(url, '_blank'); + } + + var multiLineArgs = [null, null, null]; + editor_multi.multiLineEdit = function (value, b, f, args, callback) { + editor_multi.id = 'callFromBlockly'; + _setValue(value.split('\\n').join('\n') || ''); + multiLineArgs[0] = b; + multiLineArgs[1] = f; + multiLineArgs[2] = callback; + editor_multi.lintAutocomplete = Boolean(args.lint); + editor_multi.show(); + } + editor_multi.multiLineDone = function (keep) { + if (!multiLineArgs[0] || !multiLineArgs[1] || !multiLineArgs[2]) return; + var newvalue = codeEditor.getValue() || ''; + multiLineArgs[2](newvalue, multiLineArgs[0], multiLineArgs[1]) + if (!keep) { + editor_multi.id = ''; + editor_multi.hide(); + } else { + alert('写入成功!'); + } + } + + var _fileValues = [''] + editor_multi.importFile = function (filename) { + editor_multi.id = 'importFile' + _fileValues[0] = filename + _setValue('loading') + editor_multi.show(); + fs.readFile(filename, 'base64', function (e, d) { + if (e) { + _setValue('加载文件失败:\n' + e) + editor_multi.id = '' + return; + } + var str = editor.util.decode64(d) + _setValue(str) + _fileValues[1] = str + }) + } + + editor_multi.writeFileDone = function (keep) { + fs.writeFile(_fileValues[0], editor.util.encode64(codeEditor.getValue() || ''), 'base64', function (err, data) { + if (err) printe('文件写入失败,请手动粘贴至' + _fileValues[0] + '\n' + err); + else { + if (!keep) { + editor_multi.id = ''; + editor_multi.hide(); + } else { + alert('写入成功!'); + } + printf(_fileValues[0] + " 写入成功,F5刷新后生效"); + } + }); + } + + editor_multi.editCommentJs = function (mod) { + var dict = { + loc: '_server/table/comment.js', + enemyitem: '_server/table/comment.js', + floor: '_server/table/comment.js', + tower: '_server/table/data.comment.js', + functions: '_server/table/functions.comment.js', + commonevent: '_server/table/events.comment.js', + plugins: '_server/table/plugins.comment.js', + } + editor_multi.lintAutocomplete = true + editor_multi.setLint() + editor_multi.importFile(dict[mod]) + } + + // 字体大小 + { + const CONFIG_KEY = "editor_multi.fontSize"; + let fontsize = editor.config.get(CONFIG_KEY, 14); + const input = document.getElementById("editor_multi_fontsize"); + const check = document.getElementById("editor_multi_fontweight") + input.value = fontsize; + editor_multi.setFontSize = function () { + const value = Number(input.value); + editor.config.set(CONFIG_KEY, value); + const ele = codeEditor.getWrapperElement() + ele.style.fontSize = `${value}px`; + ele.style.fontWeight = `${check.checked ? 'bold' : 'normal'}` + } + } + + return editor_multi; +} +//editor_multi=editor_multi(); \ No newline at end of file diff --git a/editor_table.js b/editor_table.js new file mode 100644 index 0000000..1e7fcf3 --- /dev/null +++ b/editor_table.js @@ -0,0 +1,602 @@ +editor_table_wrapper = function (editor) { + + editor_table = function () { + + } + + ///////////////////////////////////////////////////////////////////////////// + // HTML模板 + + editor_table.prototype.select = function (value, values) { + if (values.indexOf(value) < 0) values = [value].concat(values); + var content = values.map(function (v) { + return editor.table.option(v, v == value) + }).join('') + return /* html */`\n` + } + editor_table.prototype.option = function (value, selected) { + return /* html */`\n` + } + editor_table.prototype.text = function (value) { + return /* html */`\n` + } + editor_table.prototype.checkbox = function (value) { + return /* html */`\n` + } + editor_table.prototype.textarea = function (value, indent, disable) { + return /* html */`\n` + } + editor_table.prototype.checkboxSet = function (value, keys, prefixStrings) { + if (value == null) value = []; + if (!(value instanceof Array)) { + if (value == 0) value = []; + else value = [value]; + } + keys=Array.from(keys) + prefixStrings=Array.from(prefixStrings) + for (var index = 0; index < value.length; index++) { + if (keys.indexOf(value[index])==-1) { + keys.push(value[index]) + prefixStrings.push('
      '+value[index]+': ') + } + } + var content=[] + for (var index = 0; index < keys.length; index++) { + content.push(editor.table.checkboxSetMember(value.indexOf(keys[index])!=-1,keys[index],prefixStrings[index])) + } + return /* html */`
      ${content.join('')}
      \n`; + } + editor_table.prototype.checkboxSetMember = function (value,key,prefixString) { + return /* html */`${prefixString}\n`; + } + editor_table.prototype.editGrid = function (showComment, type) { + var list = []; + if (showComment) list.push(""); + if (type != 'select' && type != 'checkbox' && type != 'checkboxSet' && type != 'popCheckboxSet' && type != 'disable') + list.push(""); + if (type == 'popCheckboxSet') + list.push(""); + if (type == 'disable') list.push(""); + return list.join(' '); + } + + editor_table.prototype.title = function () { + return /* html */`\n条目注释值操作\n` + } + + editor_table.prototype.gap = function (field) { + var tokenlist = field.slice(2, -2).split("']['"); + var rule = tokenlist.join("-"); + tokenlist.pop(); + var self = tokenlist.join("-"); + var status = !!tokenPool[rule]; + return /* html */` + ---- + ---- + ${field} + + \n` + } + + editor_table.prototype.tr = function (guid, field, shortField, commentHTMLescape, cobjstr, shortComment, tdstr, type) { + return /* html */` + ${shortField} + ${shortComment || commentHTMLescape} +
      ${tdstr}
      + ${editor.table.editGrid(shortComment, type)} + \n` + } + + + /** + * checkboxset中checkbox的onchange + * 这个函数本质是模板editor_table.prototype.checkboxSetMember的一部分 + * 故放在HTML模板分类下 + */ + editor_table.prototype.checkboxSetMemberOnchange = function (onemember) { + var thisset=onemember.parentNode + var inputs=thisset.children + var value=[] + for (var i in inputs) { + if (inputs[i].nodeName == 'INPUT') { + if (inputs[i].checked) { + var one = inputs[i].getAttribute('key'); + if (inputs[i].getAttribute('ctype') == 'number') one = parseFloat(one); + value.push(one); + } + } + } + thiseval = value; + // if (value.length == 0) thiseval = null; + thisset.value=JSON.stringify(thiseval) + thisset.onchange() + } + + + ///////////////////////////////////////////////////////////////////////////// + // 表格生成的控制 + + /** + * 注释对象的默认值 + */ + editor_table.prototype.defaultcobj = { + // 默认是文本域 + _type: 'textarea', + _data: '', + _string: function (args) {//object~[field,cfield,vobj,cobj] + var thiseval = args.vobj; + return (typeof (thiseval) === typeof ('')) && thiseval[0] === '"'; + }, + // 默认情况下 非对象和数组的视为叶节点 + _leaf: function (args) {//object~[field,cfield,vobj,cobj] + var thiseval = args.vobj; + if (thiseval == null || thiseval == undefined) return true;//null,undefined + if (typeof (thiseval) === typeof ('')) return true;//字符串 + if (Object.keys(thiseval).length === 0) return true;//数字,true,false,空数组,空对象 + return false; + }, + } + + /** + * 把来自数据文件的obj和来自*comment.js的commentObj组装成表格 + * commentObj在无视['_data']的意义下与obj同形 + * 即: commentObj['_data']['a']['_data']['b'] 与 obj['a']['b'] 是对应的 + * 在此意义下, 两者的结构是一致的 + * 在commentObj没有被定义的obj的分支, 会取defaultcobj作为默认值 + * 因此在深度优先遍历时,维护 + * field="['a']['b']" + * cfield="['_data']['a']['_data']['b']" + * vobj=obj['a']['b'] + * cobj=commentObj['_data']['a']['_data']['b'] + * cobj + * cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]) + * 每一项若未定义,就从defaultcobj中取 + * 当其是函数不是具体值时,把args = {field: field, cfield: cfield, vobj: vobj, cobj: cobj}代入算出该值 + * 得到的叶节点的结构如下 + * tr>td[title=field] + * >td[title=comment,cobj=cobj:json] + * >td>div>input[value=thiseval] + * 返回结果 + * 返回一个对象, 假设被命名为tableinfo + * 在把一个 table 的 innerHTML 赋值为 tableinfo.HTML 后 + * 再调 tableinfo.listen(tableinfo.guids) 进行绑定事件 + * @param {Object} obj + * @param {Object} commentObj + * @returns {{"HTML":String,"guids":String[],"listen":Function}} + */ + editor_table.prototype.objToTable = function (obj, commentObj) { + // 表格抬头 + var outstr = [editor.table.title()]; + var guids = []; + var defaultcobj = this.defaultcobj + /** + * 深度优先遍历, p*即为父节点的四个属性 + * @param {String} pfield + * @param {String} pcfield + * @param {Object} pvobj + * @param {Object} pcobj + */ + var recursionParse = function (pfield, pcfield, pvobj, pcobj) { + var keysForTableOrder = {}; + var voidMark = {}; + // 1. 按照pcobj排序生成 + if (pcobj && pcobj['_data']) { + for (var ii in pcobj['_data']) keysForTableOrder[ii] = voidMark; + } + // 2. 对每个pvobj且不在pcobj的,再添加到最后 + keysForTableOrder = Object.assign(keysForTableOrder, pvobj) + for (var ii in keysForTableOrder) { + // 3. 对于pcobj有但是pvobj中没有的, 弹出提示, (正常情况下editor_file会补全成null) + // 事实上能执行到这一步工程没崩掉打不开,就继续吧.. + if (keysForTableOrder[ii] === voidMark) { + if (typeof id_815975ad_ee6f_4684_aac7_397b7e392702 === "undefined") { + // alert('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + console.error('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + id_815975ad_ee6f_4684_aac7_397b7e392702 = 1; + } + pvobj[ii] = null; + } + var field = pfield + "['" + ii + "']"; + var cfield = pcfield + "['_data']['" + ii + "']"; + var vobj = pvobj[ii]; + var cobj = null; + if (pcobj && pcobj['_data'] && pcobj['_data'][ii]) { + // cobj存在时直接取 + cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]); + } else { + // 当其函数时代入参数算出cobj, 不存在时只取defaultcobj + if (pcobj && (pcobj['_data'] instanceof Function)) cobj = Object.assign({}, defaultcobj, pcobj['_data'](ii)); + else cobj = Object.assign({}, defaultcobj); + } + var args = { field: field, cfield: cfield, vobj: vobj, cobj: cobj } + // 当cobj的参数为函数时,代入args算出值 + for (var key in cobj) { + if (key === '_data') continue; + if (cobj[key] instanceof Function) cobj[key] = cobj[key](args); + } + pvobj[ii] = vobj = args.vobj; + // 标记为_hide的属性不展示 + if (cobj._hide) continue; + if (!cobj._leaf) { + // 不是叶节点时, 插入展开的标记并继续遍历, 此处可以改成按钮用来添加新项或折叠等 + outstr.push(editor.table.gap(field)); + recursionParse(field, cfield, vobj, cobj); + } else { + // 是叶节点时, 调objToTr_渲染 + var leafnode = editor.table.objToTr(obj, commentObj, field, cfield, vobj, cobj); + outstr.push(leafnode[0]); + guids.push(leafnode[1]); + } + } + } + // 开始遍历 + recursionParse("", "", obj, commentObj); + + var listen = function (guids) { + // 每个叶节点的事件绑定 + var tableid = editor.util.guid(); + editor.mode.currentTable=tableid; + guids.forEach(function (guid) { + editor.table.guidListen(guid, tableid, obj, commentObj) + }); + } + return { "HTML": outstr.join(''), "guids": guids, "listen": listen }; + } + + /** + * 返回叶节点形如 + * tr>td[title=field] + * >td[title=comment,cobj=cobj:json] + * >td>div>input[value=thiseval] + * 参数意义在 objToTable 中已解释 + * @param {Object} obj + * @param {Object} commentObj + * @param {String} field + * @param {String} cfield + * @param {Object} vobj + * @param {Object} cobj + */ + editor_table.prototype.objToTr = function (obj, commentObj, field, cfield, vobj, cobj) { + var guid = editor.util.guid(); + var thiseval = vobj; + var comment = String(cobj._data); + + // var charlength = 15; + // "['a']['b']" => "b" + var shortField = field.split("']").slice(-2)[0].split("['").slice(-1)[0]; + // 把长度超过 charlength 的字符改成 固定长度+...的形式 + // shortField = (shortField.length < charlength ? shortField : shortField.slice(0, charlength) + '...'); + + // 完整的内容转义后供悬停查看 + var commentHTMLescape = editor.util.HTMLescape(comment); + // 把长度超过 charlength 的字符改成 固定长度+...的形式 + // var shortCommentHTMLescape = (comment.length < charlength ? commentHTMLescape : editor.util.HTMLescape(comment.slice(0, charlength)) + '...'); + + var cobjstr = Object.assign({}, cobj); + delete cobjstr._data; + // 把cobj塞到第二个td的[cobj]中, 方便绑定事件时取 + cobjstr = editor.util.HTMLescape(JSON.stringify(cobjstr)); + + var tdstr = editor.table.objToTd(obj, commentObj, field, cfield, vobj, cobj) + var outstr = editor.table.tr(guid, field, shortField, commentHTMLescape, cobjstr, cobj._docs, tdstr, cobj._type) + return [outstr, guid]; + } + + editor_table.prototype.objToTd = function (obj, commentObj, field, cfield, vobj, cobj) { + var thiseval = vobj; + switch (cobj._type) { + case 'select': + return editor.table.select(thiseval, cobj._select.values); + case 'checkbox': + return editor.table.checkbox(thiseval); + case 'checkboxSet': + return editor.table.checkboxSet(thiseval, cobj._checkboxSet.key, cobj._checkboxSet.prefix); + default: + return editor.table.textarea(thiseval, cobj.indent || 0, cobj._type == 'disable'); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // 表格的用户交互 + + /** + * 检查一个值是否允许被设置为当前输入 + * @param {Object} cobj + * @param {*} thiseval + */ + editor_table.prototype.checkRange = function (cobj, thiseval) { + if (cobj._range) { + return eval(cobj._range); + } + if (cobj._select) { + return cobj._select.values.indexOf(thiseval) !== -1; + } + if (cobj._bool) { + return [true, false].indexOf(thiseval) !== -1; + } + return true; + } + + /** + * 监听一个guid对应的表格项 + * @param {String} guid + */ + editor_table.prototype.guidListen = function (guid, tableid, obj, commentObj) { + // tr>td[title=field] + // >td[title=comment,cobj=cobj:json] + // >td>div>input[value=thiseval] + var thisTr = document.getElementById(guid); + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); + var modeNode = thisTr.parentNode; + thisTr.setAttribute('tableid',tableid) + while (!editor_mode._ids.hasOwnProperty(modeNode.getAttribute('id'))) { + modeNode = modeNode.parentNode; + } + input.onchange = function () { + editor.table.onchange(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + // 用检测两次单击的方式来实现双击(以支持手机端的双击) + var doubleClickCheck = [0]; + thisTr.onclick = function () { + var newClick = new Date().getTime(); + var lastClick = doubleClickCheck.shift(); + doubleClickCheck.push(newClick); + if (newClick - lastClick < 500) { + editor.table.dblclickfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + } + } + + /** + * 表格的值变化时 + */ + editor_table.prototype.onchange = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + if (editor.mode.currentTable!=thisTr.getAttribute('tableid')) return; + var thiseval = null; + if (input.checked != null) input.value = input.checked; + try { + if (input.value == '') input.value = 'null'; + thiseval = JSON.parse(input.value); + } catch (ee) { + printe(field + ' : ' + ee); + throw ee; + } + if (editor.table.checkRange(cobj, thiseval)) { + editor_mode.addAction(['change', field, thiseval]); + editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 + } else { + printe(field + ' : 输入的值不合要求,请鼠标放置在注释上查看说明'); + } + } + + var tokenPool = {}; + var tokenstyle = document.createElement("style"); + document.body.appendChild(tokenstyle); + + var tokenPoolRender = function() { + var content = ""; + Object.keys(tokenPool).forEach(function(k) { + content += /* CSS */`[data-field|=${k}]{ display: none }`; + }) + tokenstyle.innerHTML = content; + } + + /** + * 当"折叠"被按下时 + */ + editor_table.prototype.onFoldBtnClick = function (button) { + var tr = button.parentNode.parentNode; + if (button.dataset.fold == "true") { + delete tokenPool[tr.dataset.gap]; + tokenPoolRender(); + button.dataset.fold = "false"; + button.innerText = "折叠"; + } else { + tokenPool[tr.dataset.gap] = true; + tokenPoolRender(); + button.dataset.fold = "true"; + button.innerText = "展开"; + } + } + + /** + * 当"显示完整注释"被按下时 + */ + editor_table.prototype.onCommentBtnClick = function (button) { + var tr = button.parentNode.parentNode; + printf(tr.children[1].getAttribute('title')); + } + + /** + * 当"编辑表格内容"被按下时 + */ + editor_table.prototype.onEditBtnClick = function (button) { + var tr = button.parentNode.parentNode; + var guid = tr.getAttribute('id'); + var cobj = JSON.parse(tr.children[1].getAttribute('cobj')); + var input = tr.children[2].children[0].children[0]; + if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); + if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string, template: cobj._template, preview: cobj._preview }); + if (cobj._type === 'material') editor.table.selectMaterial(input, cobj); + if (cobj._type === 'color') editor.table.selectColor(input); + if (cobj._type === 'point') editor.table.selectPoint(input); + if (cobj._type === 'popCheckboxSet') editor.table.popCheckboxSet(input, cobj); + } + + editor_table.prototype.onCopyBtnClick = function (button) { + var tr = button.parentNode.parentNode; + var input = tr.children[2].children[0].children[0]; + var value = JSON.parse(input.value); + if (value == null) { + printe('没有赋值的内容'); + return; + } + if (core.copy(value.toString())) { + printf('复制成功!'); + } else { + printe('无法复制此内容,请手动选择复制'); + } + } + + /** + * 双击表格时 + * 正常编辑: 尝试用事件编辑器或多行文本编辑器打开 + * 添加: 在该项的同一级创建一个内容为null新的项, 刷新后生效并可以继续编辑 + * 删除: 删除该项, 刷新后生效 + * 在点击按钮 添加/删除 后,下一次双击将被视为 添加/删除 + */ + editor_table.prototype.dblclickfunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + if (editor_mode.doubleClickMode === 'change') { + if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); + if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string, template: cobj._template, preview: cobj._preview }); + if (cobj._type === 'material') editor.table.selectMaterial(input, cobj); + if (cobj._type === 'color') editor.table.selectColor(input); + if (cobj._type === 'point') editor.table.selectPoint(input); + if (cobj._type === 'popCheckboxSet') editor.table.popCheckboxSet(input, cobj); + } else if (editor_mode.doubleClickMode === 'add') { + editor_mode.doubleClickMode = 'change'; + editor.table.addfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } else if (editor_mode.doubleClickMode === 'delete') { + editor_mode.doubleClickMode = 'change'; + editor.table.deletefunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + } + + editor_table.prototype.selectMaterial = function (input, cobj) { + editor.uievent.selectMaterial(input.value, cobj._docs || cobj._data || '请选择素材', cobj._directory, function (one) { + if (!/^[-A-Za-z0-9_.]+$/.test(one)) return null; + if (cobj._transform) return eval("("+cobj._transform+")(one)"); + return one; + }, function (data) { + input.value = JSON.stringify(cobj._onconfirm ? eval("("+cobj._onconfirm+")(JSON.parse(input.value), data)") : data); + input.onchange(); + }) + } + + editor_table.prototype.selectColor = function (input) { + if (input.value != null) { + var str = input.value.toString().replace(/[^\d.,]/g, ''); + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(str)) { + document.getElementById('colorPicker').value = str; + } + } + var boundingBox = input.getBoundingClientRect(); + openColorPicker(boundingBox.x, boundingBox.y + boundingBox.height, function (value) { + value = value.replace(/[^\d.,]/g, ''); + input.value = '[' + value +']'; + input.onchange(); + }) + } + + editor_table.prototype.selectPoint = function (input) { + var x = 0, y = 0, value = input.value; + if (value != null) { + try { + var loc = JSON.parse(value); + if (loc instanceof Array && loc.length == 2) { + x = loc[0]; + y = loc[1]; + } + } catch (e) {} + } + editor.uievent.selectPoint(editor.currentFloorId, x, y, false, function (floorId, x, y) { + input.value = '['+x+','+y+']'; + input.onchange(); + }) + } + + editor_table.prototype.popCheckboxSet = function (input, cobj) { + editor.uievent.popCheckboxSet(JSON.parse(input.value), cobj._checkboxSet, cobj._docs || cobj._data || '请选择多选项', function (value) { + input.value = JSON.stringify(value); + input.onchange(); + }) + } + + /** + * 删除表格项 + */ + editor_table.prototype.deletefunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + if (editor.table.checkRange(cobj, null)) { + editor_mode.addAction(['delete', field, undefined]); + editor_mode.onmode('save', function () { + printf('删除成功,刷新后生效。') + }); + } else { + printe(field + ' : 该值不允许为null,无法删除'); + } + } + + /** + * 添加表格项 + */ + editor_table.prototype.addfunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + if (modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + } + + var mode = editor.dom.editModeSelect.value; + var supportText = mode === 'commonevent' || mode === 'plugins'; + + if (obj == null) { + if (mode === 'commonevent') obj = events_c12a15a8_c380_4b28_8144_256cba95f760; + else if (mode === 'plugins') obj = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; + else return; + } + + // 1.输入id + var newid = '2'; + if (mode == 'loc') { + var ae = editor.currentFloorData.autoEvent[editor_mode.pos.x + ',' + editor_mode.pos.y]; + if (ae != null) { + var testid; + for (testid = 2; Object.hasOwnProperty.call(ae, testid); testid++); // 从3开始是因为comment中设置了始终显示012 + newid = testid + ''; + } + } else { + newid = prompt(supportText ? '请输入新项的ID(支持中文)' : '请输入新项的ID(数字字母下划线)'); + if (newid == null || newid.length == 0) { + return; + } + } + + // 2.检查id是否符合规范或与已有id重复 + if (!supportText) { + if (!/^[a-zA-Z0-9_]+$/.test(newid)) { + printe('id不符合规范, 请使用大小写字母数字下划线来构成'); + return; + } + } + + var conflict = true; + var basefield = (field || "").replace(/\[[^\[]*\]$/, ''); + if (basefield === "['main']") { + printe("全塔属性 ~ ['main'] 不允许添加新值"); + return; + } + try { + var baseobj = eval('obj' + basefield); + conflict = newid in baseobj; + } catch (ee) { + // 理论上这里不会发生错误 + printe(ee); + throw ee; + } + if (conflict) { + printe('id已存在, 请直接修改该项的值'); + return; + } + // 3.添加 + editor_mode.addAction(['add', basefield + "['" + newid + "']", null]); + editor_mode.onmode('save', function () { + printf('添加成功,刷新后生效;也可以继续新增其他项目。') + });//自动保存 删掉此行的话点保存按钮才会保存 + } + + ///////////////////////////////////////////////////////////////////////////// + editor.constructor.prototype.table = new editor_table(); +} +//editor_table_wrapper(editor); \ No newline at end of file diff --git a/editor_ui.js b/editor_ui.js new file mode 100644 index 0000000..f3431eb --- /dev/null +++ b/editor_ui.js @@ -0,0 +1,339 @@ +editor_ui_wrapper = function (editor) { + + var tip=document.getElementById('tip'); + var print = function (msg, cls) { + if (msg == '') { + tip.innerHTML = ''; + return; + } + tip.innerHTML = '

      ' + msg + "

      "; + } + + window.printf = function (msg) { + selectBox.isSelected(false); + print(msg, 'successText'); + } + window.printe = function (msg) { + selectBox.isSelected(false); + print(msg, 'warnText'); + } + window.printi = function (msg) { + print(msg, 'infoText'); + } + + editor.uifunctions.showBlockInfo = function (value) { + if (value == 0) { + printi("当前选择为清除块,可擦除地图上块"); + return; + } + var hasId = 'id' in value; + if (hasId && value.idnum == 17) { + printi("当前选择为空气墙, 在编辑器中可视, 在游戏中隐藏的墙, 用来配合前景/背景的贴图"); + return; + } + var isAutotile = hasId && value.images == "autotile"; + tip.innerHTML = (hasId?`

      图块编号:${ value['idnum'] }

      +

      图块ID:${ value['id'] }

      `:` +

      该图块无对应的数字或ID存在,请先前往icons.js和maps.js中进行定义!

      `)+` +

      图块所在素材:${ value['images'] + (isAutotile ? '( '+value['id']+' )' : '') } +

      +

      图块索引:${ value['y'] }

      `; + } + + editor.uifunctions.showTips = function (value) { + var tips = [ + '表格的文本域可以双击进行编辑', + '双击地图可以选中素材,右键可以弹出菜单', + '双击事件编辑器的图块可以进行长文本编辑/脚本编辑/地图选点/UI绘制预览等操作', + 'ESC或点击空白处可以自动保存当前修改', + 'H键可以打开操作帮助哦', + 'tileset平铺模式可以在地图上拖动来平铺框选的图形', + '可以拖动地图上的图块和事件;或按Ctrl+C, Ctrl+X和Ctrl+V进行复制,剪切和粘贴,Delete删除;右键也可以拉框选择区域', + 'Alt+数字键保存图块,数字键读取保存的图块', + ]; + if (value == null) value = Math.floor(Math.random() * tips.length); + printf('tips: ' + tips[value]) + } + + /** + * 根据鼠标点击, 得到从元素向上到body的所有id + */ + editor.uifunctions.getClickpath = function (e) { + //console.log(e); + var clickpath = []; + var getpath = function (e) { + var path = []; + var currentElem = e.target; + while (currentElem) { + path.push(currentElem); + currentElem = currentElem.parentElement; + } + if (path.indexOf(window) === -1 && path.indexOf(document) === -1) + path.push(document); + if (path.indexOf(window) === -1) + path.push(window); + return path; + } + getpath(e).forEach(function (node) { + if (!node.getAttribute) return; + var id_ = node.getAttribute('id'); + if (id_) { + if (['left', 'left1', 'left2', 'left3', 'left4', 'left5', 'left8', 'mobileview'].indexOf(id_) !== -1) clickpath.push('edit'); + clickpath.push(id_); + } + }); + return clickpath; + } + + /** + * editor.dom.body.onmousedown + * 检测鼠标点击, + * + 如果选中了绘图区域之外, 就保存地图 + * + 维护绘图区的菜单的隐藏 + * + 记录最后一次点击的id(主要为了数据区服务) + */ + editor.uifunctions.body_click = function (e) { + var clickpath = editor.uifunctions.getClickpath(e); + + var unselect = true; + for (var ii = 0, thisId; thisId = ['edit', 'tip', 'brushMod', 'brushMod2', 'brushMod3', 'brushMode4', 'layerMod', 'layerMod2', 'layerMod3', 'viewportButtons'][ii]; ii++) { + if (clickpath.indexOf(thisId) !== -1) { + unselect = false; + break; + } + } + if (unselect && !editor.uivalues.lockMode) { + if (clickpath.indexOf('eui') === -1 && clickpath.indexOf('lastUsed') === -1) { + if (selectBox.isSelected()) { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('地图保存成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + }); + } + selectBox.isSelected(false); + editor.info = {}; + } + } + //editor.mode.onmode(''); + if (e.button != 2 && !editor.isMobile && clickpath.indexOf('midMenu') === -1) { + editor.uifunctions.hideMidMenu(); + } + if (clickpath.indexOf('down') !== -1 && clickpath.indexOf('midMenu') === -1 && editor.isMobile) { + editor.uifunctions.hideMidMenu(); + } + if (clickpath.length >= 2 && clickpath[0].indexOf('id_') === 0) { editor.lastClickId = clickpath[0] } + } + + /** + * editor.dom.body.onkeydown + * 绑定快捷键 + */ + editor.uifunctions.body_shortcut = function (e) { + editor.uivalues.tileSize = [1,1]; + + // UI预览 & 地图选点 + if (editor.uievent && editor.uievent.isOpen) { + editor.uievent.onKeyDown(e); + return; + } + + // 监听Ctrl+S保存 + if (e.ctrlKey && e.keyCode == 83) { + e.preventDefault(); + if (editor_multi.id != "") { + editor_multi.confirm(); // 保存脚本编辑器 + } + else if (editor_blockly.id != "") { + editor_blockly.confirm(); // 保存事件编辑器 + } + else { + editor_mode.saveFloor(); + } + return; + } + + // 如果是开启事件/脚本编辑器状态,则忽略 + if (editor_multi.id != "" || editor_blockly.id != "") + return; + + // PGUP和PGDOWN切换楼层 + if (e.keyCode == 33 || e.keyCode == 34) { + e.preventDefault(); + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return; + } + + var index = editor.core.floorIds.indexOf(editor.currentFloorId); + var nextIndex = index + (e.keyCode == 33 ? 1 : -1); + if (nextIndex >= 0 && nextIndex < editor.core.floorIds.length) { + var toId = editor.core.floorIds[nextIndex]; + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + document.getElementById('selectFloor').value = toId; + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(toId); + } + return; + } + + var focusElement = document.activeElement; + if (!focusElement || focusElement.tagName.toLowerCase() == 'body' + || focusElement.id == 'selectFloor' || focusElement.id == 'bigmapBtn' + || focusElement.id.startsWith('layerMod')) { + + //Ctrl+z 撤销上一步undo + if (e.keyCode == 90 && e.ctrlKey) { + e.preventDefault(); + if (editor.uivalues.preMapData.length > 0) { + var data = editor.uivalues.preMapData.pop(); + editor.dom.maps.forEach(function (one) { + editor[one] = JSON.parse(JSON.stringify(data[one])); + }); + editor.updateMap(); + editor.uivalues.postMapData.push(data); + editor.uifunctions.highlightSaveFloorButton(); + printf("已撤销此操作,你可能需要重新保存地图。"); + } + return; + } + //Ctrl+y 重做一步redo + if (e.keyCode == 89 && e.ctrlKey) { + e.preventDefault(); + if (editor.uivalues.postMapData.length > 0) { + var data = editor.uivalues.postMapData.pop(); + editor.dom.maps.forEach(function (one) { + editor[one] = JSON.parse(JSON.stringify(data[one])); + }); + editor.updateMap(); + editor.uivalues.preMapData.push(data); + editor.uifunctions.highlightSaveFloorButton(); + printf("已重做此操作,你可能需要重新保存地图。"); + } + return; + } + + // Ctrl+C, Ctrl+X, Ctrl+V + if (e.ctrlKey && e.keyCode == 67 && !selectBox.isSelected()) { + e.preventDefault(); + editor.uivalues.copyedInfo = editor.copyFromPos(editor.uivalues.selectedArea); + printf('该点事件已复制;请注意右键地图拉框可以复制一个区域;若有时复制失灵请多点几下空白处'); + return; + } + if (e.ctrlKey && e.keyCode == 88 && !selectBox.isSelected()) { + e.preventDefault(); + editor.savePreMap(); + editor.uivalues.copyedInfo = editor.copyFromPos(editor.uivalues.selectedArea); + editor.clearPos(true, editor.uivalues.selectedArea, function () { + printf('该点事件已剪切;请注意右键地图拉框可以剪切一个区域;若有时剪切失灵请多点几下空白处'); + editor.uifunctions.unhighlightSaveFloorButton(); + }) + return; + } + if (e.ctrlKey && e.keyCode == 86 && !selectBox.isSelected()) { + e.preventDefault(); + if (!editor.uivalues.copyedInfo) { + printe("没有复制的事件"); + return; + } + editor.savePreMap(); + editor.pasteToPos(editor.uivalues.copyedInfo); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('粘贴事件成功;若有时粘贴失灵请多点几下空白处'); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.drawPosSelection(); + }); + return; + } + // DELETE + if (e.keyCode == 46 && !selectBox.isSelected()) { + editor.savePreMap(); + editor.clearPos(true, editor.uivalues.selectedArea, function () { + printf('该点事件已删除;请注意右键地图拉框可以删除一个区域;;若有时删除失灵请多点几下空白处'); + editor.uifunctions.unhighlightSaveFloorButton(); + }) + return; + } + // ESC + if (e.keyCode == 27) { + if (selectBox.isSelected()) { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('地图保存成功'); + }); + } + selectBox.isSelected(false); + editor.info = {}; + return; + } + //alt + 0~9 改变快捷图块 + if (e.altKey && [48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) { + var infoToSave = JSON.stringify(editor.info || 0); + if (infoToSave == JSON.stringify({})) return; + editor.uivalues.shortcut[e.keyCode] = JSON.parse(infoToSave); + printf('已保存该快捷图块, 数字键 ' + (e.keyCode - 48) + ' 使用.') + editor.config.set('shortcut', editor.uivalues.shortcut); + return; + } + //ctrl + 0~9 切换到快捷图块 + if ([48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) { + editor.setSelectBoxFromInfo(JSON.parse(JSON.stringify(editor.uivalues.shortcut[e.keyCode] || 0))); + return; + } + switch (e.keyCode) { + // WASD + case 87: editor.moveViewport(0, -1); break; + case 65: editor.moveViewport(-1, 0); break; + case 83: editor.moveViewport(0, 1); break; + case 68: editor.moveViewport(1, 0); break; + // F + case 70: editor.uifunctions.triggerBigmap(); break; + // Z~. + case 90: editor_mode.change('map'); break; // Z + case 88: editor_mode.change('loc'); break; // X + case 67: editor_mode.change('enemyitem'); break; // C + case 86: editor_mode.change('floor'); break; // V + case 66: editor_mode.change('tower'); break; // B + case 78: editor_mode.change('functions'); break; // N + case 77: editor_mode.change('appendpic'); break; // M + case 188: editor_mode.change('commonevent'); break; // , + case 190: editor_mode.change('plugins'); break; // . + // H + case 72: editor.uifunctions.showHelp(); break; + } + return; + } + } + + editor.uifunctions.showHelp = function () { + alert( + "快捷操作帮助:\n" + + "ESC / 点击空白处:自动保存当前修改\n" + + "F:切换大地图\n" + + "WASD / 长按箭头:平移大地图\n" + + "PgUp, PgDn / 鼠标滚轮:上下切换楼层\n" + + "Z~.(键盘的第三排):快捷切换标签\n" + + "双击地图:选中对应点的素材\n" + + "右键地图:弹出菜单栏\n" + + "Alt+0~9:保存当前使用的图块\n" + + "0~9:选中保存的图块\n" + + "Ctrl+Z / Ctrl+Y:撤销/重做上次绘制\n" + + "Ctrl+S:事件与脚本编辑器的保存并退出\n" + + "双击事件编辑器:长文本编辑/脚本编辑/地图选点/UI绘制预览" + ); + } +} \ No newline at end of file diff --git a/editor_uievent.js b/editor_uievent.js new file mode 100644 index 0000000..6f064fb --- /dev/null +++ b/editor_uievent.js @@ -0,0 +1,1068 @@ +editor_uievent_wrapper = function (editor) { + + // ------ UI预览 & 地图选点相关 ------ // + + var uievent = { + elements: {}, + values: {}, + isOpen: false, + mode: "" + }; + + uievent.elements.div = document.getElementById('uieventDiv'); + uievent.elements.title = document.getElementById('uieventTitle'); + uievent.elements.yes = document.getElementById('uieventYes'); + uievent.elements.no = document.getElementById('uieventNo'); + uievent.elements.select = document.getElementById('uieventSelect'); + uievent.elements.selectPoint = document.getElementById('selectPoint'); + uievent.elements.selectFloor = document.getElementById('selectPointFloor'); + uievent.elements.selectPointBox = document.getElementById('selectPointBox'); + uievent.elements.body = document.getElementById('uieventBody'); + uievent.elements.selectPointButtons = document.getElementById('selectPointButtons'); + uievent.elements.canvas = document.getElementById('uievent'); + uievent.elements.extraBody = document.getElementById('uieventExtraBody'); + + uievent.close = function () { + uievent.isOpen = false; + uievent.elements.div.style.display = 'none'; + if (uievent.values.interval) { + clearTimeout(uievent.values.interval); + clearInterval(uievent.values.interval); + } + uievent.values = {}; + } + uievent.elements.no.onclick = uievent.close; + + uievent.drawPreviewUI = function () { + core.setAlpha('uievent', 1); + core.clearMap('uievent'); + core.setFilter('uievent', null); + + // 绘制UI + var background = uievent.elements.select.value; + if (background == 'thumbnail') { + core.drawThumbnail(editor.currentFloorId, null, { ctx: 'uievent' }); + } + else { + core.fillRect('uievent', 0, 0, core.__PIXELS__, core.__PIXELS__, background); + } + + if (uievent.values.list instanceof Array) { + uievent.values.list.forEach(function (data) { + if (typeof data == 'string') data = { "type": "text", "text": data }; + var type = data.type; + if (type == "text") { + data.ctx = 'uievent'; + core.saveCanvas('uievent'); + core.drawTextBox(data.text, data); + core.loadCanvas('uievent'); + return; + } + else if (type == "choices") { + for (var i = 0; i < data.choices.length; i++) { + if (typeof data.choices[i] === 'string') + data.choices[i] = { "text": data.choices[i] }; + data.choices[i].text = core.replaceText(data.choices[i].text); + } + core.saveCanvas('uievent'); + core.status.event.selection = data.selected || 0; + core.drawChoices(core.replaceText(data.text), data.choices, data.width, 'uievent'); + core.status.event.selection = null; + core.loadCanvas('uievent'); + return; + } else if (type == "confirm") { + core.saveCanvas('uievent'); + core.drawConfirmBox(data.text, null, null, 'uievent'); + core.loadCanvas('uievent'); + } else if (core.ui["_uievent_" + type]) + core.ui["_uievent_" + type](data); + }) + } + } + + uievent.previewUI = function (list) { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'previewUI'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = 'UI绘制预览'; + uievent.elements.select.style.display = 'inline'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'block'; + uievent.elements.extraBody.style.display = 'none'; + uievent.elements.body.style.overflow = "hidden"; + + uievent.elements.select.innerHTML = + '' + + '' + + ''; + uievent.elements.select.onchange = function () { + uievent.drawPreviewUI(); + } + + uievent.values.list = list; + uievent.drawPreviewUI(); + } + + uievent.selectPoint = function (floorId, x, y, bigmap, callback) { + uievent.values.bigmap = bigmap; + uievent.values.size = editor.isMobile ? window.innerWidth / core.__SIZE__ : 32 * 540 / core.__PIXELS__; + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectPoint'; + uievent.elements.selectPoint.style.display = 'block'; + uievent.elements.yes.style.display = 'inline'; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'inline'; + uievent.elements.selectPointBox.style.display = 'block'; + uievent.elements.canvas.style.display = 'block'; + uievent.elements.extraBody.style.display = 'none'; + uievent.elements.body.style.overflow = "hidden"; + uievent.elements.yes.onclick = function () { + var floorId = uievent.values.floorId, x = uievent.values.x, y = uievent.values.y; + var multipoints = uievent.values.multipoints || []; + uievent.close(); + if (callback) { + if (multipoints.length > 0) { + callback(floorId, multipoints.map(function (one) { return one.split(',')[0] }).join(','), + multipoints.map(function (one) { return one.split(',')[1] }).join(',')); + } else { + callback(floorId, x, y); + } + } + } + + // Append children + var floors = ""; + core.floorIds.forEach(function (f) { + floors += ""; + }) + uievent.elements.selectFloor.innerHTML = floors; + // 检查多选点 + if (/^\d+(,\d+)+$/.test(x) && /^\d+(,\d+)+$/.test(y)) { + var xx = x.split(','), yy = y.split(','); + uievent.values.multipoints = []; + for (var i = 0; i < xx.length; ++i) { + uievent.values.multipoints.push(xx[i] + "," + yy[i]); + } + x = xx[xx.length - 1]; + y = yy[yy.length - 1]; + } + this.setPoint(floorId || editor.currentFloorId, core.calValue(x) || 0, core.calValue(y) || 0); + } + + uievent.updateSelectPoint = function (redraw) { + uievent.elements.title.innerText = '地图选点【右键多选】 (' + uievent.values.x + "," + uievent.values.y + ')'; + // 计算size + uievent.values.boxSize = uievent.values.size * + (uievent.values.bigmap ? (core.__SIZE__ / Math.max(uievent.values.width, uievent.values.height)) : 1); + uievent.values.boxLeft = uievent.values.bigmap ? + (core.__PIXELS__ * Math.max(0, (1 - uievent.values.width / uievent.values.height) / 2)) : 0; + uievent.values.boxTop = uievent.values.bigmap ? + (core.__PIXELS__ * Math.max(0, (1 - uievent.values.height / uievent.values.width) / 2)) : 0; + + if (uievent.values.bigmap) { + uievent.elements.selectPointBox.style.left = uievent.values.boxSize * uievent.values.x + uievent.values.boxLeft + "px"; + uievent.elements.selectPointBox.style.top = uievent.values.boxSize * uievent.values.y + uievent.values.boxTop + "px"; + } else { + uievent.elements.selectPointBox.style.left = uievent.values.boxSize * (uievent.values.x - uievent.values.left) + "px"; + uievent.elements.selectPointBox.style.top = uievent.values.boxSize * (uievent.values.y - uievent.values.top) + "px"; + } + uievent.elements.selectPointBox.style.width = uievent.values.boxSize - 6 + "px"; + uievent.elements.selectPointBox.style.height = uievent.values.boxSize - 6 + "px"; + + if (redraw) { + core.setAlpha('uievent', 1); + core.clearMap('uievent'); + core.drawThumbnail(uievent.values.floorId, null, { + ctx: 'uievent', centerX: uievent.values.left + core.__HALF_SIZE__, + centerY: uievent.values.top + core.__HALF_SIZE__, all: uievent.values.bigmap + }); + uievent.values.multipoints = uievent.values.multipoints || []; + core.setTextAlign('uievent', 'right'); + for (var i = 0; i < uievent.values.multipoints.length; ++i) { + var xy = uievent.values.multipoints[i].split(","), x = parseInt(xy[0]), y = parseInt(xy[1]); + core.fillBoldText('uievent', i + 1, + 32 * (x - uievent.values.left) + 28, 32 * (y - uievent.values.top) + 26, '#FF7F00', null, '14px Verdana'); + } + core.setTextAlign('uievent', 'left'); + } + } + + uievent.setPoint = function (floorId, x, y) { + if (core.floorIds.indexOf(floorId) == -1) floorId = editor.currentFloorId; + uievent.values.floorId = floorId; + uievent.elements.selectFloor.value = floorId; + uievent.values.x = x != null ? x : (uievent.values.x || 0); + uievent.values.y = y != null ? y : (uievent.values.y || 0); + uievent.values.width = core.floors[uievent.values.floorId].width || core.__SIZE__; + uievent.values.height = core.floors[uievent.values.floorId].height || core.__SIZE__; + uievent.values.left = core.clamp(uievent.values.x - core.__HALF_SIZE__, 0, uievent.values.width - core.__SIZE__); + uievent.values.top = core.clamp(uievent.values.y - core.__HALF_SIZE__, 0, uievent.values.height - core.__SIZE__); + uievent.updateSelectPoint(true); + } + + uievent.elements.selectFloor.onchange = function () { + uievent.values.multipoints = []; + uievent.setPoint(uievent.elements.selectFloor.value); + } + + uievent.elements.selectPointBox.onclick = function (e) { + e.preventDefault(); + e.stopPropagation(); + return false; + } + + uievent.elements.body.onclick = function (e) { + if (uievent.mode != 'selectPoint') return; + if (uievent.values.bigmap) { + uievent.values.x = core.clamp(Math.floor((e.offsetX - uievent.values.boxLeft) / uievent.values.boxSize), 0, uievent.values.width - 1); + uievent.values.y = core.clamp(Math.floor((e.offsetY - uievent.values.boxTop) / uievent.values.boxSize), 0, uievent.values.height - 1); + } else { + uievent.values.x = uievent.values.left + Math.floor(e.offsetX / uievent.values.size); + uievent.values.y = uievent.values.top + Math.floor(e.offsetY / uievent.values.size); + } + uievent.updateSelectPoint(false); + } + + uievent.elements.body.oncontextmenu = function (e) { + e.preventDefault(); + e.stopPropagation(); + if (uievent.mode != 'selectPoint' || uievent.values.bigmap) return; + var x = uievent.values.left + Math.floor(e.offsetX / uievent.values.size); + var y = uievent.values.top + Math.floor(e.offsetY / uievent.values.size); + uievent.values.multipoints = uievent.values.multipoints || []; + if (uievent.values.multipoints.indexOf(x + "," + y) >= 0) { + uievent.values.multipoints = uievent.values.multipoints.filter(function (o) { return o != x + "," + y; }) + } else { + uievent.values.multipoints.push(x + "," + y); + } + uievent.values.x = x; + uievent.values.y = y; + uievent.updateSelectPoint(true); + return false; + } + + uievent.move = function (dx, dy) { + if (uievent.mode != 'selectPoint') return; + if (uievent.values.bigmap) return; + uievent.values.left = core.clamp(uievent.values.left + dx, 0, uievent.values.width - core.__SIZE__); + uievent.values.top = core.clamp(uievent.values.top + dy, 0, uievent.values.height - core.__SIZE__); + this.updateSelectPoint(true); + }; + + uievent.triggerBigmap = function () { + if (uievent.mode != 'selectPoint') return; + uievent.values.bigmap = !uievent.values.bigmap; + uievent.values.multipoints = []; + uievent.setPoint(uievent.values.floorId); + }; + + (function () { + + var viewportButtons = uievent.elements.selectPointButtons; + var pressTimer = null; + for (var ii = 0, node; node = viewportButtons.children[ii]; ii++) { + if (ii == 4) { + node.onclick = uievent.triggerBigmap; + continue; + } + if (ii == 5) { + node.onclick = function () { + alert(core.copy(uievent.values.floorId) ? ('楼层ID ' + uievent.values.floorId + ' 已成功复制到剪切板') : '无法复制楼层ID'); + } + } + (function (x, y) { + var move = function () { + uievent.move(x, y); + } + node.onmousedown = function () { + clearTimeout(pressTimer); + pressTimer = setTimeout(function () { + pressTimer = -1; + var f = function () { + if (pressTimer != null) { + move(); + setTimeout(f, 150); + } + } + f(); + }, 500); + }; + node.onmouseup = function () { + if (pressTimer > 0) { + clearTimeout(pressTimer); + move(); + } + pressTimer = null; + } + })([-1, 0, 0, 1][ii], [0, -1, 1, 0][ii]); + } + })(); + + uievent.elements.div.onmousewheel = function (e) { + if (uievent.mode != 'selectPoint') return; + var index = core.floorIds.indexOf(uievent.values.floorId); + try { + if (e.wheelDelta) + index += Math.sign(e.wheelDelta); + else if (e.detail) + index += Math.sign(e.detail); + } catch (ee) { console.error(ee) } + index = core.clamp(index, 0, core.floorIds.length - 1); + uievent.values.multipoints = []; + uievent.setPoint(core.floorIds[index]); + } + + uievent.onKeyDown = function (e) { + if (e.keyCode == 27) editor.uievent.close(); + if (uievent.mode == 'selectPoint') { + if (e.keyCode == 87) editor.uievent.move(0, -1) + if (e.keyCode == 65) editor.uievent.move(-1, 0) + if (e.keyCode == 83) editor.uievent.move(0, 1); + if (e.keyCode == 68) editor.uievent.move(1, 0); + } + } + + // ------ 搜索变量出现的位置,也放在uievent好了 ------ // + + uievent.searchUsedFlags = function () { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'searchUsedFlags'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = '搜索变量'; + uievent.elements.select.style.display = 'inline'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + // build flags + var html = ""; + Object.keys(editor.used_flags).sort().forEach(function (v) { + v = "flag:" + v; + html += ""; + }); + uievent.elements.select.innerHTML = html; + uievent.elements.select.onchange = uievent.doSearchUsedFlags; + + uievent.doSearchUsedFlags(); + } + + uievent.doSearchUsedFlags = function () { + var flag = uievent.elements.select.value; + + var html = "

      该变量出现的所有位置如下:

        "; + var list = uievent._searchUsedFlags(flag); + list.forEach(function (x) { + html += "
      • " + x + "
      • "; + }); + html += "
      "; + uievent.elements.extraBody.innerHTML = html; + } + + var hasUsedFlags = function (obj, flag) { + if (obj == null) return false; + if (typeof obj != 'string') return hasUsedFlags(JSON.stringify(obj), flag); + + var index = -1, length = flag.length; + while (true) { + index = obj.indexOf(flag, index + 1); + if (index < 0) return false; + if (!/^[a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]$/.test(obj.charAt(index + length))) return true; + } + } + + uievent._searchUsedFlags = function (flag) { + var list = []; + // 每个点的事件 + var events = ["events", "autoEvent", "changeFloor", "beforeBattle", "afterBattle", "afterGetItem", "afterOpenDoor"] + for (var floorId in core.floors) { + var floor = core.floors[floorId]; + if (hasUsedFlags(floor.firstArrive, flag)) list.push([floorId, "firstArrive"]); + if (hasUsedFlags(floor.eachArrive, flag)) list.push([floorId, "eachArrive"]); + events.forEach(function (e) { + if (floor[e]) { + for (var loc in floor[e]) { + if (hasUsedFlags(floor[e][loc], flag)) { + list.push(floorId + " 层 " + e + " 的 (" + loc + ") 点"); + } + } + } + }); + } + // 公共事件 + for (var name in events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent) { + if (hasUsedFlags(events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent[name], flag)) + list.push("公共事件 " + name); + } + // 道具 & 装备属性 + for (var id in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + var item = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id]; + // 装备属性 + if (hasUsedFlags(item.equip, flag)) { + list.push("道具 " + (item.name || id) + " 的装备属性"); + } + // 使用事件 + if (hasUsedFlags(item.useItemEvent, flag)) { + list.push("道具 " + (item.name || id) + " 的使用事件"); + } + } + // 怪物战前 & 战后 + for (var id in enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80) { + var enemy = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id]; + if (hasUsedFlags(enemy.beforeBattle, flag)) { + list.push("怪物 " + (enemy.name || id) + " 的战前事件"); + } + if (hasUsedFlags(enemy.afterBattle, flag)) { + list.push("怪物 " + (enemy.name || id) + " 的战后事件"); + } + } + // 图块的碰触 & 门信息 + for (var id in maps_90f36752_8815_4be8_b32b_d7fad1d0542e) { + var mapInfo = maps_90f36752_8815_4be8_b32b_d7fad1d0542e[id]; + if (hasUsedFlags(mapInfo.doorInfo, flag)) + list.push("图块 " + (mapInfo.name || mapInfo.id) + " 的门信息"); + if (hasUsedFlags(mapInfo.event, flag)) + list.push("图块 " + (mapInfo.name || mapInfo.id) + " 碰触事件"); + } + // 难度 & 标题事件 & 开场剧情 & 等级提升 + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.levelChoose, flag)) + list.push("难度分歧"); + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.startCanvas, flag)) + list.push("标题事件"); + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.startText, flag)) + list.push("开场剧情"); + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.levelUp, flag)) + list.push("等级提升"); + // 全局商店 + (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.shops || []).forEach(function (shop) { + if (hasUsedFlags(shop, flag)) list.push("商店 " + shop.id); + }); + + return list; + } + + // ------ 选择楼层 ------ // + uievent.selectFloor = function (floorId, title, callback) { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectFloor'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var floorId = uievent.values.floorId; + uievent.close(); + if (callback) callback(floorId); + } + + if (floorId instanceof Array) floorId = floorId[0]; + if (!floorId) floorId = editor.currentFloorId; + uievent.values.floorId = floorId; + + var html = "

      "; + html += "搜索楼层:"; + html += "" + + one + '(' + floor.title + ')' + ""; + html += ""; + html += ""; + html += '
      '; + }); + floorList.innerHTML = html; + } + + uievent._selectFloor_preview = function (button) { + var span = button.nextElementSibling; + while (span.firstChild) span.removeChild(span.lastChild); + var floorId = span.getAttribute('key'); + + if (span.style.display == 'none') { + button.innerText = '收起'; + span.style.display = 'inline'; + if (!uievent.values.dom) { + var canvas = document.createElement('canvas'); + canvas.style.position = 'relative'; + canvas.style.marginLeft = "-10px"; + canvas.style.marginTop = '5px'; + canvas.width = canvas.height = core.__PIXELS__; + uievent.values.dom = canvas; + uievent.values.ctx = canvas.getContext('2d'); + } + span.appendChild(uievent.values.dom); + core.clearMap(uievent.values.ctx); + core.drawThumbnail(floorId, null, { ctx: uievent.values.ctx, all: true }); + } else { + button.innerText = '预览'; + span.style.display = 'none'; + } + } + + // ------ 素材选择框 ------ // + uievent.selectMaterial = function (value, title, directory, transform, callback) { + var one = directory.split(':'); + if (one.length > 1) directory = one[0]; + var appendedImages = one[1] == 'images' ? core.material.images.images : {}; + + fs.readdir(directory, function (err, data) { + if (err) { + printe(directory + '不存在!'); + throw (directory + '不存在!'); + } + if (!(data instanceof Array)) { + printe('没有可显示的内容') + return; + } + value = value || []; + data = (transform ? data.map(transform) : data).filter(function (one) { return one; }).sort(); + var data2 = Object.keys(appendedImages); + data2 = (transform ? data2.map(transform) : data2).filter(function (one) { + return one && data.indexOf(one) < 0; + }).sort(); + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectMaterial'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var list = Array.from(document.getElementsByClassName('materialCheckbox')).filter(function (one) { + return one.checked; + }).map(function (one) { return one.getAttribute('key'); }); + uievent.close(); + if (callback) callback(list); + } + + var _isTileset = directory.indexOf('project/tilesets') >= 0; + + // 显示每一项内容 + var html = "

      "; + html += "" + + "
      "; + if (_isTileset) { + html += "警告!额外素材一旦注册成功将不可删除,否则可能会导致素材错位风险!如果你不再想用某个额外素材," + + "但又不想让它出现在素材区,可以考虑使用空气墙同名替换该额外素材文件。
      " + } + data.forEach(function (one) { + var checked = value.indexOf(one) >= 0 ? 'checked' : ''; + var disabled = _isTileset && value.indexOf(one) >= 0 ? 'disabled' : '' + html += ` ${one}`; + // 预览图片 + if (one.endsWith('.png') || one.endsWith('.jpg') || one.endsWith('.jpeg') || one.endsWith('.gif')) { + html += ""; + html += '
      '; + } + // 试听音频 + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) { + html += "" + html += " 音调:"; + html += `0:00 / 0:00
      + + `; + } + // 预览动画 + if (directory.indexOf('animates') >= 0) { + html += ""; + html += ""; + } + html += '
      '; + }); + data2.forEach(function (one) { + var checked = value.indexOf(one) >= 0 ? 'checked' : ''; + var disabled = _isTileset && value.indexOf(one) >= 0 ? 'disabled' : ''; + html += ` ${one}`; + // 预览图片 + if (one.endsWith('.png') || one.endsWith('.jpg') || one.endsWith('.jpeg') || one.endsWith('.gif')) { + html += ""; + html += '

      '; + } + }) + html += "

      "; + html += "

      如果文件未在此列表显示,请检查文件名是否合法(只能由数字字母下划线横线和点组成),后缀名是否正确。

      "; + uievent.elements.extraBody.innerHTML = html; + }); + } + + uievent._selectAllMaterial = function (checked) { + Array.from(document.getElementsByClassName('materialCheckbox')).forEach(function (one) { + if (!one.disabled) one.checked = checked; + }) + } + + uievent._previewMaterialImage = function (button) { + var br = button.nextElementSibling; + var img = br.nextElementSibling; + if (br.style.display == 'none') { + button.innerText = '折叠'; + br.style.display = 'block'; + img.style.display = 'block'; + img.src = img.getAttribute('key'); + } else { + button.innerText = '预览'; + br.style.display = 'none'; + img.style.display = 'none'; + } + } + + uievent._previewMaterialImage2 = function (button) { + var br = button.nextElementSibling; + if (br.style.display == 'none') { + button.innerText = '折叠'; + br.style.display = 'block'; + br.parentElement.insertBefore(core.material.images.images[br.getAttribute('key')], br.nextElementSibling); + } else { + button.innerText = '预览'; + br.style.display = 'none'; + br.parentElement.removeChild(core.material.images.images[br.getAttribute('key')]); + } + } + + uievent._previewMaterialAudio = function (button) { + var span = button.nextElementSibling.nextElementSibling; + var br = span.nextElementSibling; + var audio = br.nextElementSibling; + var progress = audio.nextElementSibling; + if (br.style.display == 'none') { + button.innerText = '暂停'; + br.style.display = 'block'; + progress.style.display = 'block'; + span.style.display = 'inline'; + audio.play(); + } else { + button.innerText = '播放'; + br.style.display = 'none'; + progress.style.display = 'none'; + span.style.display = 'none'; + audio.pause(); + } + } + + uievent._previewMaterialAudio_onPitchChange = function (input) { + var audio = input.parentElement.nextElementSibling.nextElementSibling.nextElementSibling; + audio.preservesPitch = false; + audio.playbackRate = core.clamp((parseInt(input.value) || 100) / 100, 0.3, 3.0); + } + + uievent._previewMaterialAudio_onTimeUpdate = function (audio) { + var _format = function (time) { return parseInt(time / 60) + ":" + core.setTwoDigits(parseInt(time) % 60); } + if (audio.duration > 0) { + audio.previousElementSibling.previousElementSibling.innerText = _format(audio.currentTime) + " / " + _format(audio.duration); + audio.nextElementSibling.setAttribute('value', audio.currentTime / audio.duration); + } + } + + uievent._previewMaterialAudio_seek = function (element, event) { + var audio = element.previousElementSibling; + var value = event.offsetX * element.max / element.offsetWidth; + element.setAttribute("value", value); + audio.currentTime = audio.duration * value; + if (audio.paused) audio.play(); + } + + var _previewMaterialAnimate = function (span, content) { + _previewMaterialAnimate_buildSounds(span, content); + + // 创建dom + if (!uievent.values.dom) { + var dom = document.createElement('span'); + dom.style.position = "relative"; + dom.style.marginLeft = "-10px"; + var canvas = document.createElement('canvas'); + canvas.width = canvas.height = core.__PIXELS__; + canvas.style.position = 'absolute'; + core.drawThumbnail(editor.currentFloorId, null, { ctx: canvas.getContext('2d') }); + dom.appendChild(canvas); + var canvas2 = document.createElement('canvas'); + canvas2.style.position = 'absolute'; + canvas2.width = canvas2.height = core.__PIXELS__; + uievent.values.ctx = canvas2.getContext('2d'); + dom.appendChild(canvas2); + var canvas3 = document.createElement('canvas'); + canvas3.width = canvas3.height = core.__PIXELS__; + dom.appendChild(canvas3); + uievent.values.dom = dom; + } + + span.appendChild(uievent.values.dom); + clearInterval(uievent.values.interval); + var frame = 0; + uievent.values.interval = setInterval(function () { + if (span.style.display == 'none') { + clearInterval(uievent.values.interval); + uievent.values.interval = null; + return; + } + core.clearMap(uievent.values.ctx); + core.maps._drawAnimateFrame(uievent.values.ctx, content, core.__PIXELS__ / 2, core.__PIXELS__ / 2, frame++); + }, 50); + } + + var _previewMaterialAnimate_buildSounds = function (span, content) { + var sounds = content.se || {}; + if (typeof sounds == 'string') sounds = { 1: sounds }; + var pitch = content.pitch || {}; + + span.appendChild(document.createElement('br')); + var dom = document.createElement('span'); + dom.setAttribute('frames', content.frame); + var html = ""; + Object.keys(sounds).forEach(function (frame) { + html += "" + _previewMaterialAnimate_buildSoundRow(frame, sounds[frame], content.frame, pitch[frame]) + ""; + }); + html += ''; + html += ''; + html += "

      "; + dom.innerHTML = html; + span.appendChild(dom); + _previewMaterialAnimate_awesomplete(span); + } + + var _previewMaterialAnimate_buildSoundRow = function (index, se, frames, pitch) { + var audios = Object.keys(core.material.sounds).sort().join(","); + var html = ""; + html += "第 帧:"; + html += ''; + html += ''; + html += " 音调:"; + html += ''; + html += '
      '; + return html; + } + + var _previewMaterialAnimate_awesomplete = function (span) { + var inputs = span.getElementsByClassName("_audio"); + for (var i = 0; i < inputs.length; ++i) { + var input = inputs[i]; + if (!input.hasAttribute('awesomplete')) { + input.setAttribute('awesomplete', '1'); + new Awesomplete(input); + } + } + } + + uievent._previewMaterialAnimate = function (button) { + var span = button.nextElementSibling; + while (span.firstChild) span.removeChild(span.lastChild); + var filename = span.getAttribute("key"); + uievent.values.animates = uievent.values.animates || {}; + if (span.style.display == 'none') { + button.innerText = '收起'; + span.style.display = 'inline'; + if (uievent.values.animates[filename]) { + _previewMaterialAnimate(span, uievent.values.animates[filename]); + } else { + fs.readFile(filename, 'utf-8', function (e, d) { + if (e) { + alert('无法打开动画文件!' + e); return; + } + uievent.values.animates[filename] = core.loader._loadAnimate(d); + if (uievent.values.animates[filename]) { + uievent.values.animates[filename + ':raw'] = JSON.parse(d); + _previewMaterialAnimate(span, uievent.values.animates[filename]); + } + }) + } + } else { + button.innerText = '预览'; + span.style.display = 'none'; + } + } + + uievent._previewMaterialAnimate_previewSound = function (button) { + var input = button.previousElementSibling; + if (input.tagName == 'DIV') input = input.firstChild; + if (!input.value) return; + if (!uievent.values.audio) + uievent.values.audio = new Audio(); + uievent.values.audio.src = './project/sounds/' + input.value; + uievent.values.audio.preservesPitch = false; + uievent.values.audio.playbackRate = core.clamp((parseInt(button.nextElementSibling.children[0].value) || 100) / 100, 0.3, 3.0); + uievent.values.audio.play(); + } + + uievent._previewMaterialAnimate_addSound = function (button) { + var parent = button.parentElement; + var span = document.createElement("span"); + span.innerHTML = _previewMaterialAnimate_buildSoundRow(1, "", parseInt(parent.getAttribute("frames"))); + parent.insertBefore(span, button); + _previewMaterialAnimate_awesomplete(parent); + } + + uievent._previewMaterialAnimate_deleteSound = function (button) { + var element = button.parentElement; + element.parentElement.removeChild(element); + } + + uievent._previewMaterialAnimate_saveSound = function (button) { + var span = button.parentElement; + var filename = span.parentElement.getAttribute("key"); + if (!filename || !uievent.values.animates[filename]) return; + var se = {}; + var pitch = {}; + + var audios = span.getElementsByClassName("_audio"); + for (var i = 0; i < audios.length; ++i) { + var audio = audios[i]; + var select = audio.parentElement.previousElementSibling; + if (audio.value && select.tagName == 'SELECT') { + se[select.value] = audio.value; + var p = audio.parentElement.nextElementSibling.nextElementSibling.children[0]; + pitch[select.value] = core.clamp(parseInt(p.value) || 100, 30, 300); + } + } + uievent.values.animates[filename].se = se; + uievent.values.animates[filename + ':raw'].se = se; + uievent.values.animates[filename].pitch = pitch; + uievent.values.animates[filename + ':raw'].pitch = pitch; + fs.writeFile(filename, JSON.stringify(uievent.values.animates[filename + ':raw']), 'utf-8', function (e, d) { + if (e) alert('无法修改音效文件!' + e); + else { + alert('动画音效修改成功!别忘了在全塔属性中注册音效哦!'); + } + }) + } + + // ------ 多选框 ------ // + uievent.popCheckboxSet = function (value, comments, title, callback) { + if (value == null) value = []; + if (!(value instanceof Array)) { + if (value == 0) value = []; + else value = [value]; + } + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'popCheckboxSet'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var list = Array.from(document.getElementsByClassName('uieventCheckboxSet')).filter(function (one) { + return one.checked; + }).map(function (one) { + var value = one.getAttribute('key'); + if (one.getAttribute('_type') == 'number') value = parseFloat(value); + return value; + }); + uievent.close(); + if (callback) callback(list); + } + + var keys = Array.from(comments.key) + var prefixStrings = Array.from(comments.prefix) + for (var index = 0; index < value.length; index++) { + if (keys.indexOf(value[index]) == -1) { + prefixStrings.push(value[index] + ': ') + keys.push(value[index]) + } + } + var table = ''; + + for (var index = 0; index < keys.length; index++) { + var one = keys[index]; + if (index % 3 == 0) { + table += ''; + } + table += ``; + if (index % 3 == 2) { + table += ''; + } + } + if (keys.length % 3 != 0) table += ''; + table += '
      ${prefixStrings[index]}= 0 ? 'checked' : ''}/>
      '; + + uievent.elements.extraBody.innerHTML = "

      " + table + "

      "; + } + + uievent.previewEditorMulti = function (mode, code) { + if (mode == 'statusBar') return uievent.previewStatusBar(code); + } + + // ------ 状态栏预览 ------ // + uievent.previewStatusBar = function (code) { + if (!/^function\s*\(\)\s*{/.test(code)) return; + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'previewStatusBar'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = '状态栏自绘预览'; + uievent.elements.select.style.display = 'inline'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.select.innerHTML = '' + uievent.elements.select.onchange = uievent._previewStatusBar; + + // 计算在自绘状态栏中使用到的所有flag + var flags = {}; + code.replace(/flag:([a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]+)/g, function (s0, s1) { + flags[s1] = 0; return s0; + }); + code.replace(/(core\.)?flags.([a-zA-Z0-9_]+)/g, function (s0, s1, s2) { + if (!s1) flags[s2] = 0; return s0; + }); + code.replace(/core\.(has|get|set|add|remove)Flag\('(.*?)'/g, function (s0, s1, s2) { + flags[s2] = 0; return s0; + }); + code.replace(/core\.(has|get|set|add|remove)Flag\("(.*?)"/g, function (s0, s1, s2) { + flags[s2] = 0; return s0; + }); + + var html = ''; + html += "

      注:此处预览效果与实际游戏内效果会有所出入,仅供参考,请以游戏内实际效果为准。

      "; + html += "

      "; + html += ""; + html += "属性设置:
      " + html += "名称: 生命: 上限: 攻击: 防御: 护盾: "; + html += "魔力: 上限: 金币: 经验: 等级: "; + html += "
      当前道具ID(以逗号分隔):
      "; + html += "
      当前装备ID(以逗号分隔):
      "; + html += "
      当前变量值(JSON格式):
      "; + html += "

      " + uievent.elements.extraBody.innerHTML = html; + + var inputs = document.querySelectorAll('#_previewStatusBarP input'); + for (var i = 0; i < inputs.length; ++i) inputs[i].style.width = '50px'; + + uievent.values.code = code; + uievent._previewStatusBar(); + } + + uievent._previewStatusBar = function () { + var domStyle = core.clone(core.domStyle); + var hero = core.clone(core.status.hero); + core.status.hero.flags.__statistics__ = true; + + var statusCanvasCtx = core.dom.statusCanvasCtx; + var enable = core.flags.statusCanvas; + + core.domStyle.showStatusBar = true; + core.flags.statusCanvas = true; + core.domStyle.isVertical = uievent.elements.select.value == 'vertical'; + + var canvas = document.getElementById('_previewStatusBarCanvas'); + var canvas2 = document.createElement('canvas'); + + document.getElementById('_previewStatusBarP').style.flexWrap = core.domStyle.isVertical ? 'wrap' : 'nowrap'; + + var values = Array.from(document.getElementById('_previewStatusBarValue').children).filter(function (one) { + return one.tagName == 'INPUT' || one.tagName == 'TEXTAREA'; + }).map(function (one) { return one.value; }); + core.status.hero.name = values[0]; + core.status.hero.hp = parseFloat(values[1]); + core.status.hero.hpmax = parseFloat(values[2]); + core.status.hero.atk = parseFloat(values[3]); + core.status.hero.def = parseFloat(values[4]); + core.status.hero.mdef = parseFloat(values[5]); + core.status.hero.mana = parseFloat(values[6]); + core.status.hero.manamax = parseFloat(values[7]); + core.status.hero.money = parseFloat(values[8]); + core.status.hero.exp = parseFloat(values[9]); + core.status.hero.lv = parseFloat(values[10]); + + values[11].split(',').forEach(function (itemId) { + if (!core.material.items[itemId]) return; + var itemCls = core.material.items[itemId].cls; + if (itemCls == 'items') return; + core.status.hero.items[itemCls][itemId] = (core.status.hero.items[itemCls][itemId] || 0) + 1; + }); + core.status.hero.equipment = values[12].split(','); + try { + var flags = JSON.parse(values[13]); + for (var flag in flags) { + core.status.hero.flags[flag] = flags[flag]; + } + } catch (e) { } + + var ctx = canvas2.getContext('2d'); + + if (core.domStyle.isVertical) { + canvas.width = canvas2.width = core.__PIXELS__; + canvas.height = canvas2.height = 32 * (core.values.statusCanvasRowsOnMobile || 3) + 9; + } else if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.flags.extendToolbar) { + canvas.width = canvas2.width = Math.round(core.__PIXELS__ * 0.31); + canvas.height = canvas2.height = core.__PIXELS__ + 3 + 38; + } else { + canvas.width = canvas2.width = Math.round(core.__PIXELS__ * 0.31); + canvas.height = canvas2.height = core.__PIXELS__; + } + + core.dom.statusCanvasCtx = ctx; + + try { + eval('(' + uievent.values.code + ')()'); + } catch (e) { + console.error(e); + } + + var toCtx = canvas.getContext('2d'); + core.fillRect(toCtx, 0, 0, canvas.width, canvas.height, 'black'); + core.drawImage(toCtx, canvas2, 0, 0); + + core.dom.statusCanvasCtx = statusCanvasCtx; + core.domStyle = domStyle; + core.flags.statusCanvas = enable; + core.status.hero = hero; + window.hero = hero; + window.flags = core.status.hero.flags; + } + + editor.constructor.prototype.uievent = uievent; +} \ No newline at end of file diff --git a/editor_util.js b/editor_util.js new file mode 100644 index 0000000..e8b35de --- /dev/null +++ b/editor_util.js @@ -0,0 +1,173 @@ +editor_util_wrapper = function (editor) { + + editor_util = function () { + + } + + editor_util.prototype.guid = function () { + return 'id_' + 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + editor_util.prototype.HTMLescape = function (str_) { + return String(str_).split('').map(function (v) { + return '&#' + v.charCodeAt(0) + ';' + }).join(''); + } + + editor_util.prototype.getPixel = function (imgData, x, y) { + var offset = (x + y * imgData.width) * 4; + var r = imgData.data[offset + 0]; + var g = imgData.data[offset + 1]; + var b = imgData.data[offset + 2]; + var a = imgData.data[offset + 3]; + return [r, g, b, a]; + } + + editor_util.prototype.setPixel = function (imgData, x, y, rgba) { + var offset = (x + y * imgData.width) * 4; + imgData.data[offset + 0] = rgba[0]; + imgData.data[offset + 1] = rgba[1]; + imgData.data[offset + 2] = rgba[2]; + imgData.data[offset + 3] = rgba[3]; + } + + // rgbToHsl hue2rgb hslToRgb from https://github.com/carloscabo/colz.git + //-------------------------------------------- + // The MIT License (MIT) + // + // Copyright (c) 2014 Carlos Cabo + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in all + // copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + // SOFTWARE. + //-------------------------------------------- + // https://github.com/carloscabo/colz/blob/master/public/js/colz.class.js + var round = Math.round; + var rgbToHsl = function (rgba) { + var arg, r, g, b, h, s, l, d, max, min; + + arg = rgba; + + if (typeof arg[0] === 'number') { + r = arg[0]; + g = arg[1]; + b = arg[2]; + } else { + r = arg[0][0]; + g = arg[0][1]; + b = arg[0][2]; + } + + r /= 255; + g /= 255; + b /= 255; + + max = Math.max(r, g, b); + min = Math.min(r, g, b); + l = (max + min) / 2; + + if (max === min) { + h = s = 0; // achromatic + } else { + d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + //CARLOS + h = round(h * 360); + s = round(s * 100); + l = round(l * 100); + + return [h, s, l]; + } + // + var hue2rgb = function (p, q, t) { + if (t < 0) { t += 1; } + if (t > 1) { t -= 1; } + if (t < 1 / 6) { return p + (q - p) * 6 * t; } + if (t < 1 / 2) { return q; } + if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } + return p; + } + var hslToRgb = function (hsl) { + var arg, r, g, b, h, s, l, q, p; + + arg = hsl; + + if (typeof arg[0] === 'number') { + h = arg[0] / 360; + s = arg[1] / 100; + l = arg[2] / 100; + } else { + h = arg[0][0] / 360; + s = arg[0][1] / 100; + l = arg[0][2] / 100; + } + + if (s === 0) { + r = g = b = l; // achromatic + } else { + + q = l < 0.5 ? l * (1 + s) : l + s - l * s; + p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + return [round(r * 255), round(g * 255), round(b * 255)]; + } + editor_util.prototype.rgbToHsl = rgbToHsl + editor_util.prototype.hue2rgb = hue2rgb + editor_util.prototype.hslToRgb = hslToRgb + + editor_util.prototype.encode64 = function (str) { + return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { + return String.fromCharCode(parseInt(p1, 16)) + })) + } + + editor_util.prototype.decode64 = function (str) { + return decodeURIComponent(atob(str.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')).split('').map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + }).join('')) + } + + editor_util.prototype.isset = function (val) { + return val != null && !(typeof val == 'number' && isNaN(val)); + } + + editor_util.prototype.checkCallback = function (callback) { + if (!editor.util.isset(callback)) { + editor.printe('未设置callback'); + throw ('未设置callback') + } + } + + editor.constructor.prototype.util = new editor_util(); +} +//editor_util_wrapper(editor); \ No newline at end of file diff --git a/element.md b/element.md new file mode 100644 index 0000000..449223a --- /dev/null +++ b/element.md @@ -0,0 +1,467 @@ +# 元件说明 + +?> 在这个部分,将详细讲解编辑器的每个部件的用法。 + +## 素材区 + +素材区在展开状态下,从左到右分为若干列: + +1. `project\materials\terrains.png`:位于素材区第一列(最上面两个图块不在这张图片里,它们分别是擦除和空气墙),其中从楼梯开始往下有系统含义,请勿随意修改其图块属性。 +2. `project\materials\animates.png`:位于素材区第二列,共4帧。主要为星空、岩浆、三色墙、六色门、四向出入口箭头、四种路障。 +3. `project\materials\enemys.png`:32×32px(像素,下同)的怪物,您可以随意修改它们的任何属性。如果嫌两帧太少,还可以作为32×48px怪物画在靠下2/3部分。 +4. `project\materials\enemy48.png`:32×48px的怪物,自带只有四只。您可以随意修改它们的任何属性。 +5. `project\materials\npcs.png`:32×32px的NPC,如老人、商人、小偷、公主、仙子、木牌、魔龙和章鱼的其他8块,您可以随意修改它们的任何属性。 +6. `project\materials\npc48.png`:32×48的NPC,自带只有样板0层的小姐姐,但您也可以用它来制作32×48px的门。 +7. `project\autotiles`:自动元件,会随着在地图上的连续摆放而自动采取适当的绘制方式。 +8. `project\tilesets`:额外素材,用来突破其他素材合计不得超过10000个的限制。您可以在这个区域拖动来批量框选,再在地图区单击成片绘制或拖动平铺。 + +V2.7.3中,`terrains.png`追加了薄墙图块(红线),V2.8中它们被挪到了最下方,可供利用。 + +V2.8中,自动元件在素材区折叠模式下会只显示左上角一格,与RPG Maker一致。 + +## 地图编辑(快捷键Z) + +![image](img/editor.jpg) + +如图所示,您可以在此对地图进行清空或删除操作,也可以新建或批量新建任意宽高的空白地图。 + +其中“导出并复制地图”是指显示出左侧的矩阵并复制(一般用来跨塔复制地图),您也可以直接改动其中的数字(例如将绿色史莱姆批量替换为红色史莱姆),再点击“从框中导入地图”就能将改动的结果同步到地图上。 + +下面的“楼层ID、中文名、状态栏名”分别对应楼层属性(快捷键V)的floorId、title和name,其中floorId也作为文件名(不能使用中文,注意大小写问题),title会显示在楼传界面和楼层切换黑屏,name也允许使用中文但请注意控制字数。 + +## 图块属性(快捷键C) + +![image](img/mapsc.jpg) + +如上图,除怪物和道具外,所有素材的图块属性都定义在`project\maps.js`中。道具和怪物属性也支持清空和批量复制,下面逐一讲解各条目的含义和用法: + +1. **图块ID:**图块的唯一标识符`core.getBlockId(x, y, floorId, showDisable)`,不允许使用中文和纯数字。请注意,额外素材`tileset`的图块ID由素材图片的顺序和图块在图片上的位置确定,无法更改,也请勿随意调换图片的顺序。样板已注册的图块中建议只修改怪物和NPC的图块ID,修改方法为上图最下方的**修改图块id为**。 +2. **图块数字:**见前面的描述,额外素材的数字由ID去掉字母X得到。 +3. **图块类别:**图块素材的类型。 +4. **图块名称:**怪物在手册中、道具在道具栏中、其他图块在剧情对话中的默认名称,可以随意修改。但原则上不推荐不同的怪物和道具有重复的名称,否则会影响事件编辑器的中文替换功能。 + +V2.8中,图块名称在有罗马数字、希腊字母、日文假名的情况下也支持事件编辑器中的中文替换了,这大大方便了一些科幻或魔法题材的作品。 + +V2.8中,道具名称支持使用${表达式计算}语法,但这样做会使其在事件编辑器中的中文替换失效。 + +你可以随时使用 `core.getBlockId(x, y, floorId, showDisable)` 获得地图上任何一个点的图块ID;`core.getBlockCls(x, y, floorId, showDisable)` 或的地图上任何一个点的图块类别;详见[API列表](api)。 + +在讲解其他属性之前,这里简单介绍一下素材的注册机制: + +* 除自动元件和额外素材外,其余图块只有在注册后才拥有上面所说的ID和数字。 +* 未注册的图块则只有“索引”,索引为n表示该图块在图片的第(n+1)行, +* ID和索引的对应关系定义在`project\icons.js`中。 +* 尝试用未注册的图块(如利用便捷PS工具新追加的图块)在地图上绘制,就会出现红色的问号方框。 +* 此时请在数据区手动注册此图块,只需填写一个新ID和数字(10000以内)即可。 +* 也可以点击“自动注册”按钮批量注册该图片的所有未注册素材,自动注册出的ID无任何语义(一般是一个表示图块类别的大写字母加几个数字),建议手动修改成有语义的内容(如史莱姆以Slime结尾)。 + +自动元件的注册与此不同,除了替换样板现有的几个外,如果还需要追加新的,请在地图区下方的下拉框中切换到“追加素材”(快捷键M),然后导入文件到画板autotile,再点击“追加”按钮即可。非Windows系统追加其他素材也主要依靠这种方式,具体用法请自行探索。 + +V2.7起,电脑端支持将文件直接拖动到素材区对应的列,进行追加。具体规则如下: +1. 道具的图片宽高必须为32的倍数,每格会被追加为一个新道具 +2. 其他类别的多帧图块(不包括自动元件和tileset),规格必须为128×128、128×192、96×128、96×192之一,分别对应32×32和32×48的四行四列或四行三列(后者为RPG Maker VX Ace格式)。 +3. 向两帧的enemys.png或npcs.png追加时,四列的图片取中间两列,三列的图片取两边两列。 +4. 向四帧的animates.png、enemy48.png、npc48.png追加时,三列的图片按2123排布。 + +V2.8起,图块支持“(注销并)删除”操作,执行后将(对已注册的)删除该图块在maps.js和icons.js的信息(如果是怪物和道具则还会删除对应的信息),然后将该图块从图片上删除,图片上更靠下的图块会被统一上移一格。 + +### 非怪物非道具属性 + +1. **触发器:**当碰触到地图上此图块时触发的系统事件,详见[事件](event)。 + * **battle**: (未列出)战斗;当撞上一个怪物且没有覆盖触发器时(参见[事件](event))将自动调用此触发器产生战斗,并触发战前和战后事件。 + * **getItem**: (未列出)拾获道具;当撞上/轻拾一个道具且没有覆盖触发器时(参见[事件](event))将自动调用此触发器获得它,并触发(拾获)道具后事件。 + * **changeFloor**: (未列出)楼层切换;对于地图上绑定的绿点(常见于楼梯或彩色箭头)将自动调用此触发器产生楼层切换事件。 + * **openDoor**: 用于制作门效果,当撞上此图块时将尝试开门(仅对`animates`和`npc48`生效),并触发开门后事件;具体开门动画参见下面的门信息。 + * **pushBox**: 推箱子;请勿对非箱子使用此触发器。 + * **ski**: 滑冰;拥有此触发器的图块放置在背景层时,走上去将触发滑冰效果。 + * **custom**: 自定义系统触发器;你可以使用 `core.registerSystemEvent` 来自己定义一个系统触发器,参见[API列表](api)。 +2. **可通行性:**勾选后勇士才可以踏入此图块,否则只能撞击此图块。(怪物被锁定为不可通行,道具被锁定为可通行,如有需要可以修改点上的不可通行性) +3. **碰触脚本/碰触事件:**勇士踏入或撞击此图块时执行的脚本,该项会被eval,相当于一种自定义的触发器,您可以参考踩灯和四种路障去填写它。V2.8起,新增了“碰触事件”(会覆盖该点的普通事件),使用起来更方便也更安全。 +4. **不可出入方向:**对三个图层的图块都有效。不可出方向指的是勇士站在这种图块上不能向哪个方向走(包括撞击),不可入方向指的是勇士不能从哪个方向走向这种图块(包括撞击)。例如,不可入方向勾选了“上”则不能“从上方、向下”走向这个图块。请参考素材区第一列的四个灰色箭头(样板1层右下角也有)。 + * V2.8.1起,编辑器地图区下方提供了“通行度”勾选框,您可以随时勾选它来查看“不可出入方向”(包括图块的和点的)在地图上的实际效果。 +5. **可破震:**勾选后,此图块将成为破墙镐(pickaxe)和地震卷轴(earthquake)这两个道具的目标。 +6. **动画帧数:**您可以修改此帧数来让本来有4帧的图块只用前2或3帧循环播放,另外制作门时请务必将此帧数改为1,表示门在打开前静止在第1帧。 +7. **门信息:**只对`animates`和`npc48`有效,您可以点击“编辑按钮”来填写此图块作为门的开关耗时、开关音效以及需要哪些钥匙各多少把(可以填写任何消耗类道具,也可以选择某些道具只需持有一定数量而不消耗)。修改此信息后,您需要将上面的“动画帧数”改为1,并可能需要将“触发器”改为openDoor(不改的话将无法通过撞击来开门,但可以像三色墙一样用来制作暗墙)。 + * 该项在V2.7首次提供,只支持“同时需要多种钥匙各多少把、消耗其中哪几种”的“且运算”条件。 + * 如果您需要指定“或运算”条件(如优先消耗一种钥匙,没有再消耗另一种),或“消耗量”少于“需要持有量”,或者需要判断的条件不是道具而是其他条件(比如勇士状态或flag),请使用“碰触事件”,但这样将无法自动存档。 + * V2.8新增了(批量)开门后事件,它和(单点)开门后事件的执行先后顺序,由“脚本编辑——开门后脚本”指定,默认先执行单点的。 + * 因此,复杂的开门条件也可以换个角度思考,把开门条件设为什么都不需要(先斩后奏,这一思想在道具使用条件中也很常见),开门后事件中再进行判断,如果开门成功就按照条件扣除钥匙,开门失败就再把门关上就好了,这样就保留了自动存档功能。 + * V2.8.1起,npc48支持绑定大型贴图,您可以制作大型门了,开关门的动画会更为震撼! +8. **行走图朝向:**设置后,当勇士撞击该图块时,图块会尝试转身面向勇士(对话事件结束前请使用“事件转向”指令将其转回去)。走动时也会尝试自动转向,请参考样板0层使用的小姐姐。 + * V2.8起,“行走图朝向”可以用于怪物,绑定后只需手动设置脸朝下的怪物属性(包括下面的绑定贴图)而不用管其他的,手册显示、实际战斗、阻激夹域(尤其是夹击)、漏怪检测(`hasEnemyLeft`)等都会强制读取脸朝下的怪物,游戏中动态修改任何一个朝向的怪物属性都会立即强制同步到四个怪物身上。 + * npc在设置行走图朝向以后,其他属性不具有自同步机制,包括下面的绑定贴图,请自行注意。 + * 该项的四个方向甚至可以写不同的图块类别,后果未知。 +9. **绑定贴图:**V2.8.1新增,可以用于NPC和怪物。这很大程度上弥补了H5魔塔比起RPG Maker不能使用大型行走图的缺憾。 + * 点击此项时,将会直接弹窗请求选择文件,文件首先会判断总高度和总宽度的比例是否大于0.5。 + * 【1×4】如果这个比例不大于0.5(这个临界值可以通过复写`core.maps._getBigImageInfo`来修改),那么整张图视为1行4列(每列的高度至多为宽度的2倍),即一个图块的单向4帧行走图。方向默认视为朝下,如需修改,可以在其“行走图朝向”只绑定一个需要的方向(id写自己)。 + * 【4×4】如果这个比例大于0.5,那么整张图视为4行4列共16块,即4个朝向图块各自的4帧行走图(从上到下的朝向分别为“下、左、右、上”),因此您需要注册四个(最好是同类别的)图块并绑定好行走图朝向,贴图则都绑定这一张。 + * 该图块放在地图的事件层以后本体会透明化,并且根据朝向(默认向下)选择行走图的绘制偏移量。 + * 例如,朝下的图块,行走图在绘制时会让本体位于图片下缘中央,其他方向同理。 + * 而在编辑器中,1:1显示模式下大贴图会被缩小到1格,如需预览原始尺寸的效果,请点击地图区下方的“大地图”按钮。 + * 游戏中该图块会全程使用绑定的贴图(选择项的子选项图标和文本的`\\i[]`转义序列仍然使用本体)进行显示,包括在怪物手册和显示文章的`\t[]`效果(这两种都会被缩小到一定尺寸)。而显示文章的`\b[]`效果则还是根据图块类别使用原始高度,不过V2.8.1中您可以手动去掉尖角然后指定对话框的左上角坐标和限宽。 + * 对该图块执行“显隐事件、转变图块、开关门(npc48)、不透明度设置和渐变、移动跳跃”都会直接操作大贴图,这比起2.7.x的楼层贴图要好用许多! + * 但是,如果您有一张图片希望分成16小格,但是每行对应完全独立的四个图块而不具有行走图朝向关系,该怎么做呢? + * “插件编写”的第一项init中,提供了“资源加载后的操作”,您可以在里面用`splitImage`将该图的四行提前裁剪成四张图片并重新命名,就可以使用啦! + * 手动使用脚本切分出的新图片无法通过弹窗选取,必须手动填写,敬请谅解。 + * 如果希望能够弹窗选取,可以使用全塔属性中的“图片切分”功能。 +``` js + this._afterLoadResources = function () { + // 这是一个将4by4.png(假设为384*384)按行拆分成四个图片并保存的样例, + // 可以用来在没有条件ps(如原图尺寸不支持便捷ps或者手机造塔时)的条件下, + // 预处理图片来得到独立朝向的大型行走图(1of4.png到4of4.png)。 + var arr = core.splitImage('4by4.png', 384, 96); // 以宽386高96进行切分图片 + for (var i = 1; i <= arr.length; ++i) + core.material.images.images[i + 'of4.png'] = arr[i - 1]; + } +``` +### 道具属性 + +样板自带的道具都在样板0层摆好了,您可以直接进入游戏捡起它们,就会看到该道具的注意事项,这里不再赘述。 + +1. **道具类别:**虽然和图块类别的英文缩写都是cls,但有本质区别,请注意区分。道具的图块类别都是items,而道具类别分为以下几种: + * items:是的你没看错,又是`items`这个词,请注意和图块类别的`items`相区分。它表示即捡即用类不进背包的道具,如四种血瓶、三种宝石等。这类道具需要用到的其他属性有“即捡即用效果”、“即捡即用提示”、“碰触或使用事件”。 + * tools:进背包的消耗类道具,如钥匙和解药瓶、便携式血瓶蓝瓶(生命魔杖)、破震炸飞和跳跃靴等。这类道具需要用到的其他属性有“道具描述”、“不显示在道具栏”、“回放不绘制道具栏”、“碰触或使用事件”、“使用效果”、“能否使用或装备”。 + * constants:进背包的永久道具(每件在背包的数量要么为1要么为0),如手册、楼传、幸运金币、十字架、护符、二倍斩等,这类道具需要用到的其他属性和tools一致。 + * equips:装备,它需要用到的其他属性有“道具描述”、“道具的装备属性”、“能否使用或装备”。 + * 例如:如果您想把四种血瓶和三种宝石改为便携式,只需把其道具类别改为tools(当然,楼层属性中的ratio一项也就失效了)。 + * 如果想把大黄门钥匙变为钥匙盒(红黄蓝钥匙各一把),只需把其道具类别从tools改为items + * 如果想把剑盾变成装备,只需把其道具类别改为`equips`; + * 如果想修改破墙镐/炸弹/冰冻徽章的目标个数(V2.8支持改为八方向,使用`core.utils.scan2`即可)或让炸弹能够获得金经/触发战后事件,请修改它们的使用效果。 +2. **道具描述:**对除即捡即用类外的道具都有效。一个字符串,为道具在道具栏里的描述,也作为首次捡到时的提示信息的一部分(如果全塔属性中开启了这一提示功能的话)。支持使用`${表达式计算}`语法(如四种血瓶和三种宝石那样,但不支持中文替换),此语法的详细规则见“显示文章正文的转义序列”,和“值块和冒号缩写量”。 +3. **不显示在道具栏:**对tools和constants有效,勾选此项后,该道具在背包中将不显示出来。常用于不能主动使用或已有专门的使用按钮的道具来节省显示篇幅,如手册、楼传、幸运金币、十字架、护符和钥匙等。该属性的详细原理,见“脚本编辑(N键)”最下面的“道具栏显示项”。 +4. **回放不绘制道具栏:**勾选此项后,录像回放中使用此道具将不显示黑黑的道具栏。常用于频繁使用的道具,如楼传、技能和冰冻徽章等。 +5. **即捡即用效果:**如题,该项会被eval,一般为一行下述的代码: `core.status.hero.xxx += yyy * core.status.thisMap.ratio` + * 其中xxx为勇士的某种状态(如生命hp、生命上限hpmax、魔力mana、魔力上限manamax、护盾mdef、攻防、金经) + * yyy为此道具的基础效果(如四种血瓶和三种宝石的基础效果定义在了全塔属性中) + * `core.status.thisMap.ratio`则是指该道具所在楼层的“楼层属性”最下面的“宝石血瓶效果”。 + * 此效果会在“B键数据统计”被模拟执行(使用道具所在楼层的ratio)然后计算执行前后勇士状态差异,因此请尽量只在里面用脚本直接操作`core.status.hero`,而不要使用tip或特效。 + * 可以在此项中使用`core.insertAction([...])`或`core.insertCommonEvent()`插入事件或公共事件,但不会计入数据统计。 +6. **即捡即用提示:**实际显示时会被接在“获得xxx”后面,所以该项总是一个以逗号开头的字符串,同样支持`${表达式计算}`语法。 +7. **碰触或使用事件:**对除equips外都有效。该项用于代替“即捡即用效果”(但会使勇士停下脚步,且会晚于地图上的afterGetItem事件被执行,且不计入B键数据统计)和“使用效果”,如样板中的黄宝石和生命魔杖。如果您的js语法基础薄弱,那么它将是您的不二之选。 +8. **使用效果:**对tools和constants有效。该项会被eval,一般为一个js函数,较为简单的使用效果(如解药瓶)也可能是一行代码,破炸冰的目标个数请直接在该项中修改。总的来说因为事件比起脚本更容易实现异步特效且录像安全性更好,所以如非必要,不建议用此项。 +9. **能否使用或装备:**对tools、constants、equips有效。该项也会被eval,一般为一个js函数,较为简单的使用条件(如解药瓶)也可能是一行形如`"core.hasFlag('xxx')"`的代码。 + * 如果该道具在任何情况下都不能主动使用,请留`null`(并最好勾选“不显示在道具栏”以节约显示篇幅)。如果该道具在任何情况下都可以主动使用,请填`"true"`。 + * 如果使用条件较为复杂,也推荐直接填`"true"`先斩后奏,在使用效果中再行判定,并在使用失败的场合使用`core.addItem('xxx')`静默返还一件该道具,如样板中的破墙镐和炸弹。 + * 如果用于装备,那么`null`表示任何情况下都可以装备。但是请注意装上以后,即使条件变得不满足也不会自动脱下。 +10. **道具的装备属性:**在介绍此项之前,请先留意一下“全塔属性”中的“装备孔”一项。该项为一个字符串数组,最多允许6项(13×13样板)或8项(15×15样板)。每一项为装备的类型名称,建议是两个汉字(如“武器”、“防具”)。类型允许重复,如可以让勇士最多同时装备两块盾牌。 + * 装备类型:一个自然数,和前面的“装备孔”对应,如0表示武器,1表示防具。如果装备孔有重复的名称则这里也直接写名称(不用加引号),穿戴时会自动尝试寻找第一个同类型的空闲装备位(如果同类型的装备位唯一则会不管空闲与否直接替换),没有空闲的话会提示玩家先卸下一件。 + * 普攻动画:`project\animates`文件夹中任何一个文件的名称(不带后缀,但需要在全塔属性中注册过,支持别名),只对第一个装备孔有效。普攻动画会播放在和勇士战斗的怪物位置处,如果是强制战斗的天降怪物,则会播放在勇士身上并跟随,请自行注意。详见“文件注册”使用动画。 + * 数值提升项:若干个键值对,表示该装备增加属性的常数值(支持负数)。7个常用属性可以通过下拉框选取,自定义的新属性也可以手动输入。 + * 百分比提升项:若干个键值对,表示该装备增加属性的百分比(支持负数,如填-10就表示减少10%),修改方法同上。 + * 请注意,两种提升项只能写常数,请不要认为写个flag变量就能自动变化了。 + * V2.8中,两种提升项可以在游戏过程中通过事件指令来修改(包括已经穿在身上的装备,但是修改后的值仍然视为常数),同时每件装备新增了“穿脱时事件”属性,该事件以自动事件方式实现,在换好装备后关闭装备栏的瞬间触发。而且下述各种buff可以使用冒号缩写量来读写了,详见[事件](event)。 + * “穿脱时事件”指的是身上某件装备“从无到有”和“从有到无”,不包括“从多到少”和“从少到多”(在装备孔有重复名称的情况下)。 + * “穿脱时事件”的自动事件定义在`libs\core.js`,如需修改(例如改为检测“多少”而不是“有无”),请在插件“init”中直接修改`core.initStatus.autoEvents`。 + * 这是一个一维数组,数组的每项为一个自动事件对象,对象的`symbol`属性含有`"equipEvent_"`时就表明这是一个穿脱时事件。您可以修改其触发条件以及执行内容的第一项(flag变化)。 + * 装备对属性的影响原理:在穿脱装备时,会根据数值提升项和百分比提升项,分别调用`core.status.hero.xxx += yyy`和`core.addBuff('xxx', yyy)`这两个API(衰弱的附加和解除同理),而状态栏的显示值和战斗中的使用值则是`core.getStatus('xxx')`和buff值相乘再向下取整所得。 + * 可以按Alt+0~9快速更换套装(在装备界面按这个组合键则是保存套装),手机端的Alt键在V2.8被提供为粘滞键,点击状态栏右下角的难度标签就能看到。 + +道具相关API请阅读[API列表](api)。 + +### 怪物属性 + +1. **手册ID:**【已弃用】,原本是V2.8以前用来不完全实现多朝向怪物和大型怪物缩略图的手段,现在建议全部用新增的“行走图朝向”属性和“单点图块不透明度设置”代替。 + * 该项设置后怪物将在手册中不显示为原怪物而显示为对应的怪物ID(比“行走图朝向”更优先),如果对应ID的怪物在本楼层恰好也存在,那么看上去就像原怪物在手册中消失了(即RMXP魔塔的“伪装”属性),可以适当利用这一点。 + * 漏怪检测(`hasEnemyLeft`)和阻激夹域(尤其是夹击)依然只会使用原怪物ID,如有需求,请使用“行走图朝向”属性。 +2. **生命、攻防、金经:**如题,注意金经必须在“全塔属性”(快捷键B)中的“状态栏显示项”中也勾选才能真正被启用。持有幸运金币时打怪获得的金币翻倍,附加诅咒状态时打怪不获得金经。 +3. **加点:**若全塔属性勾选了“加点”,则此项为正数时将作为与该怪物每场战斗胜利后传递给“公共事件——加点事件”的参数(即那里的`flag:arg1`,默认表示加点的倍率),您可以自行修改该事件。 +4. **不可炸:**勾选后该怪物不会成为炸弹的目标,有阻击怪在场的情况下请务必给有(单点)战后事件的怪物(如机关门守卫和boss)勾选此项,否则玩家可能会偷梁换柱地炸掉该怪物并把阻击怪推过去打死来触发战后事件。不过V2.8.1起,炸弹的使用效果中提供了炸单点怪物触发战后事件的示例,可供利用。 +5. **(批量)战前/战后事件:**V2.8新增,用于解决阻击怪等会移动的怪物不能使用地图上的战前/战后事件的问题。 + * 两种战前事件都会在“撞击怪物、判定可以战胜、自动存档”后触发,这比起曾经的“覆盖触发器+天降强制战斗”对玩家更为友好。 + * 批量战前事件默认晚于单点战前事件(修改需要复写函数),批量战后事件与单点战后事件的执行顺序可以直接在“脚本编辑——战后脚本”中调整,默认也是单点先执行。 + * 两种战前事件在强制战斗时都不会触发,并且都早于支援怪的跳跃动画,因此捕捉怪的战前事件也不会触发。 + * 两种战前事件中如果使用“立刻结束当前事件”就会取消战斗,必要时您可以利用这一点制作回合制战斗。 + * 批量战前事件可以方便地制作“怪物先打一下勇士,再被勇士打死”的效果,同时延迟了怪物的消失,使得怪物根据“行走图朝向”转身后的效果能够来得及被看到。 + * 批量战后事件可以方便地制作“被打败后立即变身为另一种怪物/路障,或掉落一种道具”的效果。 + * 您新增加的类似先攻、破甲、净化、吸血、仇恨、固伤的属性可以使用战前/战后事件更便捷地实现并无视护盾,同时也避免了下面提到的各项value冲突的问题,但这样做的后果是不计入显伤,详见下面的解释。如果一定要这样实现,建议通过额外的说明提醒玩家。 +6. **特殊属性:**一个由正整数组成的一维数组,您可以点击“多选框编辑”按钮来修改它。所有特殊属性都定义在“脚本编辑——怪物特殊属性”,您可以在那里追加新的。它们大体分为四类: + 1. 手册中属性值的修正:(按照结算顺序)模仿、坚固、光环,修正后的属性也将被用于战斗,详见“脚本编辑——怪物真实属性”。 + 2. 战损的修正:这类最多,先攻、魔攻、连击(次数为n)、破甲(比例为defValue)、反击(比例为atkValue,回合数为勇士的攻击回合数)、净化(倍数为n,1表示单纯无视护盾)、吸血(比例为value,是否加到自身为add)、仇恨(每场战斗的仇恨增值由全塔属性指定)、无敌、固伤(数值为damage)、支援。其中又以仇恨和固伤不能被护盾直接抵消而和无敌较为特殊,详见“脚本编辑——战斗伤害信息”。 + 3. 战后的影响:中毒、衰弱、诅咒、仇恨(的累加和减半)、自爆、退化(扣减值分别为atkValue和defValue)、重生,详见“脚本编辑——战后脚本”和“脚本编辑——毒衰咒处理”。由于上面的“批量战前/战后事件”的存在,这种技能不再推荐由脚本实现。 + 4. 阻激夹域捕捉:即对主角行走的妨害,详见“脚本编辑——阻激夹域伤害”,该函数也负责了血网(图块ID为lavaNet,请勿修改)的伤害。 + +您会发现一个可怕的事情,那就是上述1和2会影响显伤,而3(例如自爆)和战前/战后事件不会(且后者对勇士生命的影响默认是无视护盾的)。这对于魔塔来说可能是致命的,毕竟没有玩家想看到“一个明明显伤不是红色的怪物打了却会死”等情况。当然,也有一种办法是像flash版新新魔塔1和2那样“战斗过程带有随机性或QTE,手册只显示预估伤害”,这种设计模式在rm和h5魔塔界并不常用,请谨慎尝试。 + +支援怪在进行支援时(护盾默认只计算一次)不具有坐标(类似天降强制战斗),因此不支持V2.8的“定点设置怪物属性”,同时基于坐标的一些判定也会失效。 + +发生支援时,金经(以及加点塔的加点值)都会累加,但加点事件只执行一次,也就是这次所有点数必须加在同一项属性,如需修改,请修改公共事件。 + +阻激域的伤害都为value且在夹击之前结算,领域的形状和半径与光环一致。如果需要更复杂的形状(如米字形激光),请自行研究该函数。 + +V2.8起,阻击和捕捉支持九宫格形状,由zoneSquare属性指定。如需实现九宫格夹击,请仿照它们。阻击默认可以推到已隐藏的事件处,如需禁止,请修改所在的函数。 + +您甚至可以给同一种怪物设置阻击和捕捉,那么它会先后退然后在原位置留下一个残影和勇士战斗,从而起到刷金经的作用。 + +样板的光环是作用于怪物的,如果想制作作用于勇士的光环,需要注意缓存问题以免卡顿。 + +可以看到,怪物属性中有很多值是彼此互相冲突的。请自行注意,比如设计新属性时分散给各项而不要都吊死在三个value上。最后介绍一些和怪物相关的API: +``` js + core.status.hero.flags.no_repulse = true; // 禁用阻击,包括伤害和后退效果 + core.status.hero.flags.no_laser = true; // 禁用激光 + core.status.hero.flags.no_betweenAttack = true; // 禁用夹击 + core.status.hero.flags.no_zone = true; // 禁用领域 + core.status.hero.flags.no_ambush = true; // 禁用捕捉 + core.getItem('amulet'); // 禁用血网等路障 + core.setEnemy('greenSlime', 'atk', 100); // 设置怪物属性,并计入存档 + core.getDamageString(enemy, x, y, floorId); // 获取某只怪的地图显伤字符串和颜色 + core.getCurrentEnemys(floorId); // 获取某层楼的(映射后)怪物列表,按战损递增排列 + core.hasEnemyLeft(enemyId, floorId); // 漏怪检测,两个参数都允许使用一维数组 + core.hasSpecial(special, test); // 检测special是否有test这一个特殊属性 +``` + +如果您想在数据区的表格中追加新的属性项,或修改已有项的格式、范围和长短注释,请点击数据区顶部的“配置表格”按钮,并参照已有的项去追加和修改,具体可查阅[修改编辑器](editor)。 + +至此您会发现,每种怪物不管出现在地图上的什么地方,其属性都是一样的(除非受到局部光环的影响)。所幸的是,V2.8提供了读写/移动单点怪物属性的API和事件,这种属性会在“脚本编辑——怪物真实属性”中最早生效,战后脚本中重置。此外,因受此属性或光环影响而导致同层多个同种怪物在手册中的数值不一致时,玩家可以选择作为多种怪物分别显示(包括具体坐标),详见游戏中的Esc菜单。 + +## 楼层属性(快捷键V) + +![image](img/floor.jpg) + +V2.7.1起,楼层支持使用最大128×128的超大地图,您可以在地图区下方点击“大地图”按钮切换到全景进行绘制,或在1:1模式下使用wsad进行视角移动。 + +V2.8.1新增了怪物的大贴图绑定,编辑器1:1模式下大贴图会被缩小到一格中,您可以点击“大地图”按钮让大贴图按原始尺寸绘制。 + +1. **楼层ID:**`project/floors`中的文件名,不允许使用中文也不能直接修改。修改方法见上图底部,修改后必须立即刷新浏览器页面,该项需要注意大小写问题。另外,作品发布时会统计楼层总数,如果有一些剧情层或连载塔半成品层不希望被统计进去,请使用“sample”+纯数字楼层ID即可,就像三个样板层一样。 +2. **楼层名:**楼层在楼传、上下楼黑屏和浏览地图界面的名称。 +3. **状态栏显示:**勇士在此楼层时状态栏左上角“上楼梯”图标右边的文字,允许使用中文,但请注意控制字数。 +4. **地图宽度和高度:**可在表格最下方修改。 + * 如果地图被加宽或加高,则“偏移”表示右移或下移的格子数(左边缘或上边缘用空格子填补)。 + * 如果地图被减窄或减矮,则“偏移”表示左移或上移的格子数(被移出左边缘或上边缘的图块将丢失)。 + * 在大地图中,您可能需要对地图中的绝对坐标和视野中的相对坐标进行互相转换。这需要用到`core.bigmap`的以下几个属性: + * `core.bigmap.width`和`core.bigmap.height`表示大地图的宽高(单位为格子)。 + * `core.bigmap.offsetX`和`core.bigmap.offsetY`表示视野左上角在大地图中的绝对像素坐标,再除以32就是格子坐标。 + * 因此,对于任意绝对坐标(x,y),它在视野中的相对坐标就是(x-offsetX/32,y-offsetY/32)。 + * 在大地图的非边缘区域,`hero.loc.x-core.bigmap.offsetX/32`和`hero.loc.y-core.bigmap.offsetY/32`总是都为6或7(样板尺寸的一半)。 + * 反之,已知相对坐标求绝对坐标就用加法,请举一反三。 +5. **几个勾选框:** + * 可楼传飞到:如果不勾选,则此楼层禁止成为楼传的目标楼层。该项的具体原理见“脚本编辑——flyTo楼层飞行”。 + * 可楼传飞出:如果不勾选,则勇士在此楼层禁止使用楼传。 + * 快捷商店:如果不勾选,则勇士在此楼层禁止快捷使用全局商店。事件中的“启用全局商店同时打开”不受影响,详见“插件编写——全局商店”。 + * 不可浏览:如果勾选,则此楼层无法通过滚轮/PageUp/PageDown键浏览,也不会计入全塔B键数据统计。 + * 不可瞬移:如果勾选,则勇士在此楼层无法用E键和点击瞬移,常用于用自动事件去监听勇士坐标时,或者需要严格的计步属性时(如每走10步失去一把黄钥匙)。 + * 是否是地下层:如果勾选,则楼传非平面模式下(或平面模式下该层还没有锚点时)勇士在此楼层原地使用楼传会传送到上楼点,详见“脚本编辑——楼层飞行”。 +6. **首次到达事件、每次到达事件:**如题,详见“脚本编辑——切换楼层后”。每层的这两个事件是共用独立开关的,常见用法有“进入新区时清空已到达的楼层列表,只保留当前楼层”从而实现勇士不能再飞回从前区域的效果。 +7. **并行处理脚本:**一个字符串,为勇士在此楼层时浏览器每帧都会执行一次(eval)的脚本,最快每秒60次。一般用来制作一些定时特效如bgs、bgv,详见“脚本编辑——并行脚本”。 +8. **上下楼点:**两个自然数构成的一维数组,将作为“楼层转换”事件(在地图上以图块左下角出现绿色小方块作为标记)和“楼层切换”指令中“上下楼梯”以及非平面楼传的目标坐标。 + * 如果不设置,则在传送时会尝试从地图中搜索上下楼梯图块。因此当某个楼层没有楼梯或有多个楼梯时(如《[新新魔塔](http://h5mota.com/games/xinxin/editor.html)》),请务必设置这个属性。点击“编辑”按钮从地图选点即可。 +9. **楼传落点:**格式和设置方法同上。如果设置了此项,则楼传在此层的落点将强制变为该点,无视平面模式下的离开点和上面的上下楼点以及该层实际的楼梯位置。 +10. **地面图块:**可以填写任何一个图块ID(需要加引号)或数字(如1是墙6是冰块),此项也会作为手册和剧情对话中的帧动画背景。填写tileset时可直接填写数字,不需要带字母X和引号。 +11. **色调:**一行四列的数组,前三项为小于256的自然数(分别表示红、绿、蓝),最后一项为0到1的浮点数表示不透明度,可以点击“编辑”按钮用调色器调色。 + * 值得一提的是,很多事件也以颜色作为参数,这些都是可以使用调色器调色的。 +12. **天气:**一行两列的数组,第一项为字符串“rain”、“snow”、“fog”、“cloud”、“sun”,第二项为不大于10的正整数,分别表示1—10级的雨天(雨丝数量和等级正相关)、雪天(由大小不一的白色实心圆组成,详见样板1层,雪花的数量和横飘速度和等级正相关)、雾天、多云、晴天(左上角洒下金色的阳光)。 + * 色调层在天气层上方、UI层下方(如不透明色调会遮盖天气,浏览地图看不到色调),关于图层的详细说明,参见“个性化” +13. **背景音乐:**如题,当在游戏中触发楼层切换时(包括读档),如果`flag:__color__、flag:__weather__、flag:__bgm__`没有值,游戏当时的画面色调、天气、背景音乐就会变为楼层属性中的这三个设置项,详见“脚本编辑——切换楼层中”。 + * V2.8起,该项允许多选,会随机播放其中一个。 +14. **宝石血瓶效果:**如题,必须填写且必须为正数。此项的用法为`core.status.thisMap.ratio`,请参考四种血瓶和三种宝石的捡拾效果。 + * V2.8起,手册中的“1防”改为“加防”,表示加ratio点防御能减伤多少。 + * 您还可以将其用于其他各种场合作为系数,如血网的伤害、中毒后每步的损血等。 +15. **楼层贴图:**【V2.8.1起】,怪物和npc可以直接绑定4帧大贴图了,因此楼层贴图主要用于布景。 + * 由于样板提供的图块只有32×32px和32×48px两种尺寸,且后者只能画在事件层,每个图块最多只能有4帧,因此对于一些多帧大图块十分不便,如npcs.png中被大卸八块的魔龙和章鱼。 + * 你可以使用“楼层贴图”,该项允许您使用任何尺寸、任何帧数的素材,唯一的缺点是不支持移动跳跃和淡入淡出效果。 + * 点击“编辑”按钮进入事件编辑器,每张图片的写法为(可从入口方块拖出,然后双击预览第一帧的效果): + 1. 图片名(name):如题,图片需要放在`project/images`文件夹并注册。 + 2. 翻转(:x/:y/:o):您可以对贴图的每帧进行三种翻转,当然,帧顺序在原图中依然是从左到右的。 + 3. 图层(bg/fg/auto):此项决定贴图绘制在哪个图层,您可以全部画在背景层或前景层。也可以选择“自适配”让贴图的上半部分画在前景层,下半部分画在背景层,比如树木等。如果选择了自适配,最好让下面的绘制坐标和宽高都是32的倍数。 + 4. 绘制坐标(x,y):贴图在地图中的左上角像素坐标,譬如x和y都填32则表示贴图左上角和“地图左上角格子的右下角”重合。 + 5. 初始禁用(Y/N):如果勾选了此项,则此贴图初始时不显示,您可以在事件中再将其显示出来。 + 6. 裁剪起点坐标(x,y)和宽高(w,h):此项规定了贴图在按帧切分前从原图中取哪一部分,x和y为所取部分在原图中的左上角坐标(不填视为两个0),w和h为所取部分的宽高(不填表示一直取到右下角)。 + 7. 帧数(frame):不填视为1,如果填写了大于1的整数,就会把上述裁剪得到的结果再从左到右等分为若干份,并在实际绘制时从左到右逐帧(可能还带有翻转)循环绘制,每帧的持续时间和其他图块一致。 + * 贴图本身只具有观赏性,您仍然需要使用空气墙等手段去控制其绘制区域各个点的通行性。 + * 在使用贴图来表现魔龙和章鱼这类大型怪物时,需要预先准备一种32×32(2帧)或32×48(4帧)的行走图,并注册为怪物,放在地图上时指定该点的不透明度为0,最后在该点的战后事件中隐藏贴图即可。 + * 你可以在插件中复写`drawBg`和`drawFg`函数以控制贴图和图块的绘制顺序(默认先绘制图块),详见[脚本](script)。 + ``` js + ////// 绘制背景层 ////// + core.maps.drawBg = function (floorId, ctx) { + var onMap = ctx == null; + if (onMap) { + ctx = core.canvas.bg; + core.clearMap(ctx); + } + this._drawBg_drawBackground(floorId, ctx); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块 + // 后绘制的覆盖先绘制的。 + this._drawFloorImages(floorId, ctx, 'bg'); + this._drawBgFgMap(floorId, ctx, 'bg', onMap); + } + ``` + +## 全塔属性(快捷键B) + +全塔属性共分为四部分:文件注册、初始勇士、全局数值、系统开关,您可以随时折叠其中任何一个部分。 + +![image](img/firstdatab.jpg) + +### 文件注册 + +这部分基本上都是经由多选框半自动完成的,下面逐一讲解: +1. **楼层列表:**`project/floors`文件夹中的文件名(不含后缀,但请务必注意大小写问题),此数组的顺序决定了楼传和上下楼器(fly、upFly、downFly)的顺序。 + * 如果您不慎将勇士的出生楼层注销了或不慎删除了某些楼层的js文件,导致编辑器页面打开后一片白屏,请手动打开`project/data.js`去小心地修改floorIds以和实际的文件名相匹配,并将出生楼层改为一个存在的楼层。 + * 其他更复杂的白屏请在控制台根据报错信息(安卓手机则使用ES文件浏览器查看日志)去小心地修改文件(如某个楼层文件有问题则可以注销它),如果难以独立解决,欢迎加QQ群959329661寻求帮助。 +2. **分区指定:**一行两列的数组,可以使用事件编辑器编辑,每行表示塔的一个区域,该行的两列分别表示该区域的第一层和最后一层(后者不填表示到塔顶),不在任何区域的楼层组成公区。 + * 游戏中,除了公区和勇士当前所在区以外的地图都将处于冻结状态,无法被浏览、无法被飞到、无法触发其自动事件、地图中的图块无法被读写。冻结状态的地图不会存入存档,从而节约了存档大小并加快了存读档速度,上百层的高层塔必备! +3. **使用图片、图片切分:**`project/images`文件夹中的文件名(需要后缀,必须全英数),单击“编辑”按钮,编辑器会自动扫描文件系统中格式合适的图片(如jpg、png和gif)。 + * 您可以预览并将需要的图片勾选。请注意,勇士的初始行走图必须在这里注册。另外,`winskin.png`只许替换为相同规格的图片而不要注销,否则道具商店等插件无法正常绘制。 + * V2.8.1起,新增了“图片切分”功能,用于代替插件init中的“资源加载后操作”函数。 + * 该项最核心的用法就是将4行4列共16块的大型行走图切成4行,绑定给没有朝向关系的独立图块。 + * 具体用法是,指定一张宽高分别为mw和nh的图片(限png格式),按照w和h的单位宽高裁剪,就会得到mn个小图。 + * 每个小图会被重命名为您指定的前缀+数字(从0起),按正常的Z字形文本顺序逐行从左到右编号。 +4. **额外素材:**`project/tilesets`中的文件名(需要后缀,只支持png)。 + * 注册方法同上,最大的区别在于这个数组的顺序必须保持好。如果随意调换其中的顺序,或注销不在数组末尾的图片,就会导致地图上最终呈现的素材发生错位。因此,新勾选的图片总会被自动追加到数组的末尾。 + * 比起常规素材,额外素材最大的好处有几点: + 1. 图片没有数量限制。常规素材的总数量最多只允许不到10000个,而额外素材每张图片上的图块数量最多允许3000个。 + 2. 查看和绘制更为方便。常规素材每个图块独占一行,每列为一帧,导致不方便观察,且用多个图块拼图必须逐图块绘制。额外素材都是静止的,所以每个图块只占一格,多个图块可以在准备素材时就直接以相邻的样子绘制在同一张图片上,绘制地图时直接从素材区拖动来批量框选,再在地图区单击成片绘制或拖动平铺。 + 3. 批量替换也更为方便。譬如您如果想制作形如“一场大战/天灾过后/多年以后,村庄/城镇化为了一片废墟”的效果,可以预先准备两张甚至更多张相同规格的额外素材图片,然后在适当的时候遍历某个/某些楼层的图块ID,将以“X1”开头的图块统一替换为“X2”开头等。发布单机版游戏时,您也可以提供多张相同规格的额外素材图片供玩家直接替换皮肤风格。 +5. **使用动画:**`project/animates`文件夹中的文件名(不含后缀,请注意与`animates.png`相区分)。 + * 要使用动画,您可以使用“RM动画导出”工具从RPG Maker XP 1.03及其制作的游戏中导出动画,也可以用动画编辑器修改已有的动画或用图片新建。但这些办法都只适用于Windows系统,非Windows系统建议直接从我们的官网下载他人的作品取用其中的动画。 + * 每个动画将其用到的各张图片直接以base64硬编码进同一个animate文件,每个动画为一个animate文件。这样做的缺点是如果多个动画使用了相同的图片那么图片会被重复存储,优点则是跨作品迁移动画更加方便。 + * animate文件为json格式的文本文件,文件末尾记录了动画的帧信息,文件开头则记录了动画的伸缩比和所有音效(第几帧用什么音效,音调是多高)。 + * 在导出动画时,会出现一个输入框并提示动画的第一音效文件名。不管该文件名是什么语言,请直接点击下一步。音效文件会被尝试自动复制,随后您只需手动注册该动画并在编辑器中预览,然后重新按帧绑定需要的音效。 + * 可以使用如下动画相关的脚本对动画进行播放,或在事件中使用「播放动画」事件。 + ``` js + core.drawAnimate(name, x, y, alignWindow, callback); + // 播放一个动画,name为不带后缀的动画文件名,x和y为播放的格子坐标。 + // alignWindow表示在大地图中该坐标是绝对坐标还是视野中的相对坐标,填true表示相对坐标。 + // 相对坐标模式下,坐标应填写为小于13或15的自然数,譬如对13×13样板来说, + // 填两个6就会强制播放在视野中心。 + // callback为动画播放完毕后(和音效无关)的回调函数,因为动画播放本身是异步的。 + core.drawHeroAnimate(name, callback); // 和上面类似,但该动画会跟随勇士移动。 + // 每场战斗后,都会根据怪物坐标尝试用前者播放普攻动画。若坐标不存在, + // (即天降强制战斗),则会尝试用后者播放。看上去就像勇士在打自己,请自行注意。 + core.stopAnimate(id, doCallback); // 停止一段动画,id为上面两个函数的返回值, + // 第二个参数表示本次停止后是否依然执行前两个函数的回调 + + // 您可以用前两个函数回调自己,从而做到一个动画反复循环, + // 但务必要将每次的id记录在flags中,以便于第三个函数去停止它。 + ``` +6. **使用音乐:**`project/bgms`文件夹中的文件名(需要后缀,默认只支持wav、mp3、ogg、m4a、flac)。 + * 如果玩家使用的是手机且没有连接WiFi(iOS和部分浏览器无法获知网络状态,将始终视为流量党),那么背景音乐默认不会开启,可以在标题画面点击右下角的圆形按钮来开启。 + * V2.8起,这个圆形的音乐开关旁边还增加了一个放大镜按钮,您可以在标题画面连续点击它来获得一个最佳的屏幕缩放倍率。 + * 发布到官网的作品还可以从远程加载背景音乐,您可以点击此链接试听和下载其他作品的背景音乐。 + * 是否启用远程加载、以及启用时远程加载的根目录,由main.js指定。因此从官网下载其他作品的离线版本后请先关闭远程加载功能,才能正常加载本地注册的音乐。 + * 使用`core.material.bgms`可以查看所有的背景音乐,如果需要(不存档的情况下)同时播放多个背景音乐并独立控制时刻、音量、速度、音调,请直接对它们使用`play()`和`pause()`方法并按照下图操作。 + * `duration`表示总时长(秒,音效也适用),`currentTime`表示当前进度(秒,可读写) + * `volume`表示音量(0到1),`playbackRate`表示播放的倍速 + * `preservesPitch`表示变速播放时是否不变调,`false`表示变调 + * V2.8起,背景音乐“变速不变调”和“变速且变调”、音效“变速且变调”正式作为样板API提供,同时也有对应的事件指令,这是样板在追逐RPG Maker上迈出的一大步,且行且珍惜! + * V2.8.1起,动画的音效支持“变速且变调”,同时编辑器中试听音频也支持了(但bgm试听不能只变速不变调)。 + ![image](img/bgm.jpg) +7. **使用音效:**`project/sounds`文件夹中的文件名(格式要求和写法同上)。 + * V2.8起,音效的播放和动画一样会返回一个ID并且有回调。音效不能像bgm一样只变速不变调,而是必须一起变。 + * 可以指定ID来提前停止某个音效(不指定ID则全部停止),但不能像停止动画时一样取消回调。 + * 对玩家来说音效的音量和bgm是绑定的,无法分开调节。此外,样板没有背景音效(bgs、bgv)的默认实现。如有需求,请使用并行脚本或自我回调处理。 + * 音乐和音效在使用多选框注册时都支持试听(但此时不支持变速变调),您可以看到它们的总时长和已播时长(精确到秒),从而指定音乐的开播秒数或配合使用“等待n毫秒”事件或并行脚本处理。 +8. **使用字体:**project\fonts文件夹中的文件名(只支持ttf格式,不写后缀)。不建议给在线游戏版本添加中文字体,因为文件真的很大... +9. **文件别名:**如前所述,样板所有需要加载的文件名都必须全部是英文或数字。这一项允许你给文件取别名,别名可以使用任何语言的文字。 + * V2.8起,此项改由事件编辑器编辑,同时样板新增了大量系统音效,可供灵活配置,务必善用! +10. **装备孔:**见“道具的装备属性”。 +11. **标题音乐:**如题,请注意部分浏览器不会在刚打开某个页面时就播放音频,必须用户操作一下才行。 + * V2.8起背景音乐支持“变速不变调”或“变速且变调”,但无法直接用于标题音乐。如有需求,请复写`core.control.showStartAnimate`函数。 +12. **主样式:**一些css设置项,单击“编辑”按钮可以看到具体含义和用法,这里不再赘述(横竖屏标题画面背景支持gif动图)。 +13. **游戏名:**标题画面和网页选项卡上显示的名字,可以和官网别的作品重名。 +14. **唯一英文标识符:** **必须修改,且不得和官网别的作品重名**。只能使用字母数字下划线,如果您不确定一个标识符是否已被别的作品使用,请输入`https://h5mota.com/games/xxx`,如出现404就说明此名称未被使用。 +15. **游戏版本:**当您的游戏发生版本更迭后,旧版本的存档直接读取可能会出现bug.因此届时您可以修改此项来让旧存档无法直接读取,只能回放其录像。 +16. **难度分歧:**单击“编辑”按钮进入事件编辑器,每个难度分为以下几项。 + * 名称:标题界面单击“开始游戏”后出现的二级菜单中的文字。一般为该难度的最简要的介绍,如减伤比例、初始福利等。 + * 简写:横屏状态栏左下角(竖屏右下角,也作为数字键切换按钮)和存读档界面缩略图上的文字,也会出现在在线游戏排行榜和旧版官网的作品卡片上。允许使用中文但请注意控制字数,用`core.status.hard`表示。 + * 变量hard值:即`core.status.hero.flags.hard`的值,建议填自然数。若同一结局有多个难度有人通关,则站点排行榜只统计其中此值最高的难度。请务必保证有一个难度的此值为0,理由见下。 + * 颜色:上述“简写”的颜色,用`core.status.hero.flags.__hardColor__`表示,默认为红色。详见“脚本编辑——更新状态栏”。 + * 事件:此事件比下述的“开场剧情”更早执行,一般用来设置初始福利。 + * 如果将难度分歧数组留空,那么标题界面单击“开始游戏”就会直接进入开场剧情。 + * 请注意,游戏中的难度分歧最好只通过`core.status.hero.flags.hard`进行,这是因为除非开启了标题界面事件化,否则`core.status.hard`是可以由玩家在标题画面任意指定的,而未定义过的难度的“变量:hard”值都会为0,这也是为什么上面要求务必要有一个难度指定0值的原因。 + * 关于如何在标题画面任意指定难度和随机种子,参见[事件](event)一章的最底部。 + +### 初始勇士 + +1. **初始楼层、朝向和坐标:**如题,请注意初始楼层必须在上述的“楼层列表”中。初始坐标一般通过右击地图上的空地快速绑定,但您也可以手动在这里填写负数或超出初始楼层宽高的值。然后使用“无视地形移动勇士”或“跳跃勇士”等改变勇士位置的事件指令,做出“勇士从地图外进入”的演出效果。 + * 如需根据难度分歧或用户选项等条件来改变它们,请在“开场剧情”中修改`core.firstData.floorId`和`core.firstData.hero.loc` +2. **行走图:**如题,必须在“使用图片”中注册过。宽高必须为4的倍数,宽度至少为128px(即每帧32px)。高度不限,剧情对话中和状态栏中会尝试保持比例压缩到每帧32px宽。 + * 在游戏中,勇士当前的行走图文件名用`core.status.hero.image`表示(只读) + * 游戏中如需更改行走图请使用事件指令,但请注意它并不会计入站点录像验证,而且更换新行走图后如果立即重绘勇士就会重置视角。 +3. **帧动画:**勾选此项后,勇士在剧情对话中(朝上视为朝下)和原地不动时会循环播放四帧踏步动画,一般用于长翅膀的勇士。但是勾选后会导致视角随时锁定在勇士身上,也就是说大地图中操作视角的指令都会失效! +4. **勇士名:**如题,也会作为剧情对话中`\t[hero]`的默认标题。 +5. **初始等级:**如果开启了自动进阶功能,请不要修改此项。 +6. **生命上限、魔力上限、初始生命/魔力/攻防/护盾/金经:**如题。注意生命上限和金经需要在系统开关中勾选后才会启用,魔力上限填负数代表没有上限。 +7. **初始装备、游戏变量:**建议留空(事件中的变量初始时都会视为0,脚本中也有办法做到)。 +8. **永久道具、消耗道具、初始拥有装备个数:**点击“注释”按钮,按照提示进行修改。 +9. **标题事件:**需要配合系统开关中勾选“标题开启事件化”来使用,可以在“开始游戏”、“读取存档”之余添加一些额外的功能。如成就系统、音像和回想鉴赏,但这并不是唯一的方法,请自行研究。 + * 使用此项的后果主要是开局的若干操作也会计入录像,观感较差。 + * 使用此项后,难度分歧会通过用户操作(如显示选择项)实现,因而成为了录像的一部分。 + * 使用此项后,会导致因为无法像默认标题一样做成长方形,只能采用“黑色边框+状态栏黑底+隐藏状态栏”的妥协方法得到一个偏右的正方形画面。 + * V2.8提供了一个插件,可以将这个正方形画面暂时居中,这是没有办法的办法。 +10. **开场剧情:**会在难度分歧事件之后执行,可以在这里设置各种变量的初始值、穿上初始拥有的装备、隐藏勇士和一些初始不希望显示的图层块、追加跟随者等。 + * 需要注意的是,此时`core.status.floorId`还没有值,因此无法进行相关操作,包括存档! +11. **全局商店:**详见“QTE与全局商店”。 +12. **等级提升:**需要配合系统开关中勾选“等级”、“经验”和“升级”来使用,每个等级分为以下几项: + * 需求:刷新状态栏时,如果勇士当前等级是此等级的前一级,且经验值大于等于此需求,就会触发升级。 + * 称号:状态栏显示的等级默认是个正整数,会尝试替换为这里的称号(调用core.getLvName()函数),请注意控制字数。 + * 是否扣除经验:如果勾选了此项,则触发升级时经验值会扣除需求值,这一项主要是为了应对一次升多级的情况。 + * 事件:触发升级时执行的事件,如全面提升属性。事件中甚至可以降低等级,从而反复触发升同一级。 + +### 全局数值 + +![image](img/values_flagsb.jpg) + +这个类型的数值会保存在core.values中,可以直接在游戏中修改。 + +1. **血网伤害和中毒伤害:**如题,如果不想用常数,请修改“脚本编辑”的“阻激夹域伤害”和“每步后操作”。 +2. **衰弱效果:**填小于1的正数代表扣减的比例,否则为扣减的常数。扣减和恢复的原理和装备相同,详见“脚本编辑——毒衰咒处理”。 +3. **三种宝石和四种血瓶的值:**如题,此值为基础值。实际效果还需乘以楼层属性最下面的“宝石血瓶效果”(限即捡即用类,详见这七种道具的属性)。 +4. **反击、破甲、净化比例:**如果反击、破甲、净化怪物没有指定atkValue、defValue和n,就会用这三个值。请注意反击的总回合数是勇士的攻击回合数,净化比例填1表示单纯无视护盾。 +5. **仇恨增值:**每进行一场战斗,core.status.hero.flags.hatred的增加量。如果不想用常数,请修改“脚本编辑——战后脚本”。 +6. **全局帧动画时间:**即怪物和NPC的振动速度,建议改为300毫秒。 +7. **竖状态栏自绘行数:**需要配合系统开关“开启自绘状态栏”使用,建议改为4. +8. **楼层切换时间:**此项可被玩家更改,单位为毫秒,必须为0.1秒的0到10倍。 + +如有需求,可以把勇士移速(可被玩家更改)通过“配置表格”追加到这里,并修改`libs\core.js`对上述数值的初始化行为。 + +### 系统开关 + +这个类型的开关会保存在core.flags中(只读),请注意和core.status.hero.flags相区分。如需在游戏中修改,请使用“设置系统开关”事件。 + +1. **状态栏显示项:**如题,总个数请控制在12个以内,否则竖屏可能塞不下。 + * 这些项的图标都在`project\materials\icons.png`中。该文件夹下也提供了一个`icons_old.png`可供替换。 + * “血限”、“金币”和“经验”必须勾选才会启用(指会处理生命溢出、金经会显示在手册、打怪也会掉落), + * 必须勾选“升级”才会启用自动进阶,“升级扣除模式”如果不勾选就会同时显示下一级的需求(NEXT)和当前经验(EXP,如果勾选了的话),否则会只显示两者的差(依然用NEXT图标),“扣除”这个词用的不太好。 + * 如果同时勾选了“钥匙”和“绿钥”,则每种钥匙的数量都会被缩小尺寸显示,因此如非必要请不要勾选“绿钥”。 +2. **楼传需在楼梯边:**如果勾选了此项,则只有勇士在楼梯旁边(包括六种portal旁边)时才允许使用楼传。 + * 请注意,此项是在楼传道具使用条件之外额外进行的判定,目的是给出不同的提示信息。 + * 因此如果您要修改楼传的使用条件(指总的使用条件,具体能否飞到某层的条件则在“脚本编辑——楼层飞行”),则可能需要一并取消此勾选。 + * V2.8起,浏览地图界面按下G键或点击楼传图标则会尝试飞到当前正在浏览的楼层,这大大方便了玩家。 +3. **楼传开平面模式:**如果勾选了此项,则勇士在使用楼传飞往某层时会落在上次离开该层的位置(锚点)。 + * 此项和上一项建议要么同时勾选要么同时不勾选,同时勾选以后建议在“脚本编辑——flyTo楼层飞行”中禁止同层传送,否则可能有意想不到的后果。 + * 楼层切换过程中可以根据局部变量`fromLoad`和`isFlying`进行不同的处理(例如播放不同的音效),前者表示是否为读档,后者表示是否为楼传,建议由这两者导致的楼层切换一律不更新锚点。 +4. **铁门不消耗钥匙:**如果勾选了此项,则铁门无需钥匙也能撞开。【不建议使用!】因为此项会导致撞铁门时不再自动存档,因此建议修改铁门的`doorInfo`属性将钥匙栏留空即可。 +5. **首次道具进行提示:**勾选后,首次捡到非即捡即用类道具都会弹窗提示(晚于`afterGetItem`事件被执行)。 +6. **状态栏装备按钮:**勾选后,状态栏的楼传按钮会变为装备栏按钮,但玩家仍然可以双击道具栏来呼出装备栏。 +7. **加点:**勾选后怪物的加点值会在“脚本编辑——战后脚本”中作为参数`core.status.hero.flags.arg1`被传递给“公共事件——加点事件”。 +8. **负伤:**勾选后,战斗结束时如果勇士的护盾没有完全被打破,则剩余的护盾值会加到其生命上。所以勾选此项后,护盾可以这样“间接”抵消掉仇恨伤害和固伤。 +9. **夹击不超伤害值:**勾选此项后,夹击伤害将封顶至夹击怪的战损。同时被四只怪夹击时,取两个战损中较小的。 +10. **二分临界:**我们知道,打败怪物所需的回合数,取决于勇士的攻击减去怪物的防御。这个值并非每增大1都能使回合数减少,因而有了“临界”的说法,即“再至少增加多少攻击力,才能减少回合数”。然而,当您修改“脚本编辑——战斗伤害信息”函数后,攻击力的增加可能反而导致回合数也增加,于是临界值计算出错。您可以勾选此开关来修复,代价是游戏可能较卡,请自行权衡。 + * 目前样板的临界只有回合数法和二分法被真正采用,而循环法则只是保留了代码。如需启用,请修改`main.js`中“循环临界的分界”。 + * V2.8.1起,未破防怪的临界表中,第一项将用负数表示刚刚破防时的伤害,后面的项正常表示破防后再加攻击的减伤。 +11. **标题开启事件化:**勾选此项后,标题画面将改为执行前述的“标题事件”,请自行研究,V2.8起建议配合“标题事件居中”插件。 +12. **开启自绘状态栏:**勾选此项后,状态栏将改用“脚本编辑——自绘状态栏”来绘制,同时“脚本编辑——点击状态栏”也将启用,请自行研究。 +13. **四个显伤:**略,玩家依然可以在设置菜单中开关之,其中“定点怪显”指的是手册中把受到额外影响而数值不同的同种怪物分开显示。 +14. **允许轻按:**勾选此项后,玩家可以按下空格/大键盘数字7/双击勇士来拾取相邻的道具(优先面前)。 +15. **允许穿透楼梯:**在狭窄的区域拦路放置一个可通行的“楼层转换”事件时(图块左下角出现绿色标记),玩家可能希望勇士能直接走过去。您可以逐个去设置其能否被这样走过,或者让其依据本勾选项。 + * 值得注意的是,当玩家从允许穿透的楼梯向一个不可走的方向(如旁边是墙,或不勾选下一个开关时的致命领域)手动寻路时,可以停在楼梯上(进而再轻按拾取周围物品等)。不建议您利用这类极端情况去迫使玩家进行非常规操作,毕竟穿透楼梯和不能踏入致命领域的本意是方便玩家,不是么? + * 确切地说,即使旁边没有障碍物,录像意义下勇士也一定能停在任何可穿透的楼梯上。因此笔者再次强调,非常不建议利用这样的盲点戏弄玩家。 +16. **允许将死领域:**“脚本编辑——阻激夹域伤害”会将地图中每个点的阻激夹域和血网伤害加总,如果不勾选此开关,则当勇士生命小于等于相邻空格子的总伤害(没有伤害则没关系)时,勇士无法走向该格子。 + * 值得注意的是,这种判定方式并没有考虑“走这一步后、结算该点伤害前”可能的加血或该点伤害变化,因此如有必要可根据“脚本编辑——每步后操作”去修改core.canMoveHero()函数。 + * 另外,此项的保护作用只会在“勇士可以自由行动”(`core.status.lockControl`为`false`)时生效,因此事件中(滑冰等场合)踩到致命领域仍然会死亡。 +17. **允许瞬移:**若不勾选此开关,将全程禁用瞬移功能。一般只建议在需要的楼层逐层勾选禁止瞬移,或通过`flags.cannotMoveDirectly`来控制。 +18. **录像折叠:**勾选此项后,将开启录像折叠功能。录像折叠将尽可能优化掉在一个地方无意义的行走,从而减少录像长度并提升播放观感。 + * 当经过一段时间的行走、转向和瞬移后,若勇士的坐标、朝向和状态(步数除外)和之前某个时刻完全相同,那么将会直接删除这中间的录像记录。 + * 当中毒状态、触发任何系统或自定义事件、图块脚本、楼层切换、受到阻激夹域伤害等等时,将清除录像折叠信息。 + * 请注意:录像折叠将会优化步数,所以如果游戏和步数有直接关系(比如步数算分)请关闭录像折叠功能。另外,如果你的塔存在楼层并行脚本且对游戏数据有直接影响,也请关闭录像折叠功能。 +19. **伤害禁用商店:**勾选此项后,每当勇士踩到阻激夹域和血网并受到伤害时,所有全局商店都将被禁用,需要重新去启用(譬如勇士去撞击该商店的实体NPC)。 +20. **虚化前景层:**前景层会遮挡事件层,这对魔塔来说有时可能不太友好。勾选此项后,事件层有东西(如道具)时将虚化该格子的前景层,使得该东西以半透明状态可见。 + * 出于布景需要,某点事件层为自动元件或tileset时,必须有“碰触脚本/碰触事件”,该点前景层才会虚化。 + +上面就是整个样板中的各个元件说明。通过这种方式,你就已经可以做出一部没有任何事件的塔了。 + +尝试着做一个两到三层的塔吧! + +========================================================================================== + +[继续阅读下一章:事件](event) diff --git a/enemy.d.ts b/enemy.d.ts new file mode 100644 index 0000000..95945f5 --- /dev/null +++ b/enemy.d.ts @@ -0,0 +1,258 @@ +type PartialNumbericEnemyProperty = + | 'value' + | 'zone' + | 'repulse' + | 'laser' + | 'breakArmor' + | 'counterAttack' + | 'vampire' + | 'hpBuff' + | 'atkBuff' + | 'defBuff' + | 'range' + | 'haloRange' + | 'n' + | 'purify' + | 'atkValue' + | 'defValue' + | 'damage'; + +type BooleanEnemyProperty = + | 'zoneSquare' + | 'haloSquare' + | 'notBomb' + | 'add' + | 'haloAdd' + | 'specialAdd'; + +type DetailedEnemy = { + specialText: string[]; + toShowSpecial: string[]; + toShowColor: Color[]; + specialColor: Color[]; + damageColor: Color; + criticalDamage: number; + critical: number; + defDamage: number; +} & Enemy; + +type Enemy = { + /** + * 怪物id + */ + id: I; + + /** + * 怪物名称 + */ + name: string; + + /** + * 怪物说明 + */ + description: string; + + /** + * 在怪物手册中映射到的怪物ID。如果此项不为null,则在怪物手册中,将用目标ID来替换该怪物原本的ID。 + * 常被运用在同一个怪物的多朝向上 + */ + displayIdInBook: EnemyIds; + + /** + * 行走图朝向。在勇士撞上图块时,或图块在移动时,会自动选择最合适的朝向图块(如果存在定义)来进行绘制。 + */ + faceIds: Record; + + /** + * 战前事件 + */ + beforeBattle: MotaEvent; + + /** + * 战后事件 + */ + afterBattle: MotaEvent; +} & { + [P in PartialNumbericEnemyProperty]?: number; +} & { + [P in BooleanEnemyProperty]: boolean; +} & EnemyInfoBase; + +/** + * 怪物的特殊属性定义 + */ +type EnemySpecialDeclaration = [ + id: number, + name: string | ((enemy: EnemySpecialBase) => string), + desc: string | ((enemy: EnemySpecialBase) => string), + color: Color, + extra?: number +]; + +interface DamageString { + /** + * 伤害字符串 + */ + damage: string; + + /** + * 伤害颜色 + */ + color: Color; +} + +interface EnemySpecialBase { + /** + * 怪物特殊属性 + */ + special: number[]; + + /** + * 特殊属性光环 + */ + specialHalo?: number[]; +} + +interface EnemyInfoBase extends EnemySpecialBase { + /** + * 生命值 + */ + hp: number; + + /** + * 攻击力 + */ + atk: number; + + /** + * 防御力 + */ + def: number; + + /** + * 金币 + */ + money: number; + + /** + * 经验 + */ + exp: number; + + /** + * 加点量 + */ + point: number; +} + +interface EnemyInfo extends EnemyInfoBase { + /** + * 支援信息 + */ + guards: [x: number, y: number, id: EnemyIds]; +} + +interface DamageInfo { + /** + * 怪物生命值 + */ + mon_hp: number; + + /** + * 怪物攻击力 + */ + mon_atk: number; + + /** + * 怪物防御力 + */ + mon_def: number; + + /** + * 先攻伤害 + */ + init_damage: number; + + /** + * 怪物的每回合伤害 + */ + per_damage: number; + + /** + * 勇士的每回合伤害 + */ + hero_per_damage: number; + + /** + * 战斗的回合数 + */ + turn: number; + + /** + * 勇士损失的生命值 + */ + damage: number; +} + +interface BookEnemyInfo extends Enemy, EnemyInfo { + /** + * 怪物的坐标列表 + */ + locs?: [x: number, y: number][]; + + /** + * 怪物的中文名 + */ + name: string; + + /** + * 特殊属性名称列表 + */ + specialText: string[]; + + /** + * 特殊属性的颜色列表 + */ + specialColor: Color[]; + + /** + * 怪物的伤害 + */ + damage: number; + + /** + * 第一个临界的加攻的值 + */ + critical: number; + + /** + * 临界的减伤值 + */ + criticalDamage: number; + + /** + * ratio防减伤 + */ + defDamage: number; +} + +/** + * 怪物模块 + */ +interface Enemys { + /** + * 所有的怪物信息 + */ + readonly enemys: { + [P in EnemyIds]: Enemy

      ; + }; + + /** + * 获得所有怪物原始数据的一个副本 + */ + getEnemys(): { + [P in EnemyIds]: Enemy

      ; + }; +} + +declare const enemys: new () => Enemys; diff --git a/enemys.js b/enemys.js new file mode 100644 index 0000000..597a24a --- /dev/null +++ b/enemys.js @@ -0,0 +1,232 @@ +var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = +{ + "greenSlime": {"name":"史莱姆","hp":4,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "redSlime": {"name":"红头怪","hp":4,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"value":10,"faceIds":{"down":"E434","left":"redSlime","right":"blackSlime","up":"slimelord"}}, + "blackSlime": {"name":"青头怪","hp":30,"atk":2,"def":1,"money":0,"exp":0,"point":0,"special":0,"faceIds":{"down":"E434","left":"redSlime","right":"blackSlime","up":"slimelord"}}, + "slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[],"faceIds":{"down":"E434","left":"redSlime","right":"blackSlime","up":"slimelord"}}, + "bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":0}, + "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":0,"n":8}, + "skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "rock": {"name":"石头人","hp":50,"atk":50,"def":0,"money":3,"exp":0,"point":0,"special":3}, + "bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[9]}, + "redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":15,"value":100,"range":2}, + "redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":15,"value":200,"zoneSquare":true}, + "swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"special":4}, + "soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "redKnight": {"name":"红骑士","hp":500,"atk":200,"def":50,"money":0,"exp":0,"point":0,"special":[7]}, + "darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"special":8}, + "goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "poisonSkeleton": {"name":"紫骷髅","hp":50,"atk":60,"def":70,"money":80,"exp":0,"point":0,"special":13}, + "poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"special":13}, + "skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"special":18,"value":20}, + "skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "evilHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"special":14}, + "whiteHornSlime": {"name":"尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "redSwordsman": {"name":"剑王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"special":[12]}, + "octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"bigImage":"dragon_1.png"}, + "princessEnemy": {"name":"假公主","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "skeletonWarrior": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "whiteSlimeman": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "slimeman": {"name":"影子战士","hp":100,"atk":0,"def":0,"money":11,"exp":0,"point":0,"special":[9],"atkValue":2,"defValue":3}, + "yellowGateKeeper": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":0}, + "blueGateKeeper": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "redGateKeeper": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "magicMaster": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"special":11,"value":0.3333333333333333,"add":true,"notBomb":true}, + "devilWarrior": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "fairyEnemy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "dragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"bigImage":"dragon_0.png"}, + "skeletonKnight": {"name":"骷髅武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "skeletonPresbyter": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "ironRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "grayRock": {"name":"灰色石头人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "yellowPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "evilPrincess": {"name":"痛苦魔女","hp":1000,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[10]}, + "blademaster": {"name":"剑圣","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "evilFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "bowman": {"name":"初级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "watcherSlime": {"name":"邪眼怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "devilKnight": {"name":"恶灵骑士","hp":150,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":[1,5,7,8]}, + "grayPriest": {"name":"混沌法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "greenGateKeeper": {"name":"卫兵队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "ghostSoldier": {"name":"冥队长","hp":200,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":0}, + "frostBat": {"name":"寒蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"special":0,"notBomb":true}, + "yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "blueKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":16}, + "keiskeiFairy": {"name":"铃兰花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "tulipFairy": {"name":"郁金香花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "purpleBowman": {"name":"高级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "E434": {"name":"史莱姆","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E434","left":"redSlime","right":"blackSlime","up":"slimelord"}}, + "E435": {"name":"红史莱姆","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E436": {"name":"僵尸","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E437": {"name":"尸壳","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E438": {"name":"溺尸","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1}, + "E439": {"name":"骷髅","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E440": {"name":"苦力怕","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E441": {"name":"闪电苦力怕","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E442": {"name":"蜘蛛","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E443": {"name":"末影人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E444": {"name":"狼","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E445": {"name":"守卫者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E446": {"name":"远古守卫者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E447": {"name":"幻翼","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E448": {"name":"掠夺者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E449": {"name":"唤魔者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E450": {"name":"恼鬼","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E451": {"name":"卫道士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E452": {"name":"劫掠兽","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E453": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E454": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E455": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E456": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E457": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E458": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E459": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E460": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E461": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E462": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "E987": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E988": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E989": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E990": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E991": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E992": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E993": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E994": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E995": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E996": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E997": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E998": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E999": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1000": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1001": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1002": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1003": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1004": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1005": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1006": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1007": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1008": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1009": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1010": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1011": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1012": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1013": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1014": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1015": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1016": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1017": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1018": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1019": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1020": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1021": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1022": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1023": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1024": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1025": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1026": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1027": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1028": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1029": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1030": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1031": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1032": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1033": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1034": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1035": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1036": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1037": {"name":"怪","hp":1,"atk":1,"def":0,"as":1000,"ms":1000,"ar":1,"dr":3,"money":0,"exp":0,"point":0,"special":[],"type":0}, + "E1": {"name":"史莱姆","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"faceIds":{"down":"E1","left":"E2","right":"E3","up":"E4"},"type":0}, + "E2": {"name":"史莱姆","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"faceIds":{"down":"E1","left":"E2","right":"E3","up":"E4"},"type":0}, + "E3": {"name":"铁守卫","hp":50,"atk":50,"def":50,"money":0,"exp":0,"point":0,"special":[18],"value":20,"faceIds":{"down":"E1","left":"E2","right":"E3","up":"E4"},"type":0}, + "E4": {"name":"邪恶蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"faceIds":{"down":"E1","left":"E2","right":"E3","up":"E4"},"type":0}, + "E5": {"name":"红史莱姆","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"E5","left":"E6","right":"E7","up":"E8"},"bigImage":null,"type":0}, + "E6": {"name":"僵尸","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"E5","left":"E6","right":"E7","up":"E8"},"type":0}, + "E7": {"name":"僵尸","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"E5","left":"E6","right":"E7","up":"E8"},"type":0}, + "E8": {"name":"僵尸","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"E5","left":"E6","right":"E7","up":"E8"},"type":0}, + "E9": {"name":"僵尸","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E9","left":"E10","right":"E11","up":"E12"}}, + "E10": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E9","left":"E10","right":"E11","up":"E12"}}, + "E11": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E9","left":"E10","right":"E11","up":"E12"}}, + "E12": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E9","left":"E10","right":"E11","up":"E12"}}, + "E13": {"name":"尸壳","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E13","left":"E14","right":"E15","up":"E16"}}, + "E14": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E13","left":"E14","right":"E15","up":"E16"}}, + "E15": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E13","left":"E14","right":"E15","up":"E16"}}, + "E16": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E13","left":"E14","right":"E15","up":"E16"}}, + "E17": {"name":"溺尸","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E17","left":"E18","right":"E19","up":"E20"}}, + "E18": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E17","left":"E18","right":"E19","up":"E20"}}, + "E19": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E17","left":"E18","right":"E19","up":"E20"}}, + "E20": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E17","left":"E18","right":"E19","up":"E20"}}, + "E21": {"name":"骷髅","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E21","left":"E22","right":"E23","up":"E24"}}, + "E22": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E21","left":"E22","right":"E23","up":"E24"}}, + "E23": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E21","left":"E22","right":"E23","up":"E24"}}, + "E24": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E21","left":"E22","right":"E23","up":"E24"}}, + "E25": {"name":"苦力怕","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E25","left":"E26","right":"E27","up":"E28"}}, + "E26": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E25","left":"E26","right":"E27","up":"E28"}}, + "E27": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E25","left":"E26","right":"E27","up":"E28"}}, + "E28": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E25","left":"E26","right":"E27","up":"E28"}}, + "E29": {"name":"闪电苦力怕","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E29","left":"E30","right":"E31","up":"E32"}}, + "E30": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E29","left":"E30","right":"E31","up":"E32"}}, + "E31": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E29","left":"E30","right":"E31","up":"E32"}}, + "E32": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E29","left":"E30","right":"E31","up":"E32"}}, + "E33": {"name":"蜘蛛","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E33","left":"E34","right":"E35","up":"E36"}}, + "E34": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E33","left":"E34","right":"E35","up":"E36"}}, + "E35": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E33","left":"E34","right":"E35","up":"E36"}}, + "E36": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E33","left":"E34","right":"E35","up":"E36"}}, + "E37": {"name":"末影人","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E37","left":"E38","right":"E39","up":"E40"}}, + "E38": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E37","left":"E38","right":"E39","up":"E40"}}, + "E39": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E37","left":"E38","right":"E39","up":"E40"}}, + "E40": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E37","left":"E38","right":"E39","up":"E40"}}, + "E41": {"name":"恶狼","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E41","left":"E42","right":"E43","up":"E44"}}, + "E42": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E41","left":"E42","right":"E43","up":"E44"}}, + "E43": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E41","left":"E42","right":"E43","up":"E44"}}, + "E44": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E41","left":"E42","right":"E43","up":"E44"}}, + "E45": {"name":"守卫者","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E45","left":"E46","right":"E47","up":"E48"}}, + "E46": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E45","left":"E46","right":"E47","up":"E48"}}, + "E47": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E45","left":"E46","right":"E47","up":"E48"}}, + "E48": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E45","left":"E46","right":"E47","up":"E48"}}, + "E49": {"name":"远古守卫者","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E49","left":"E50","right":"E51","up":"E52"}}, + "E50": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E49","left":"E50","right":"E51","up":"E52"}}, + "E51": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E49","left":"E50","right":"E51","up":"E52"}}, + "E52": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":1,"faceIds":{"down":"E49","left":"E50","right":"E51","up":"E52"}}, + "E53": {"name":"幻翼","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E53","left":"E54","right":"E55","up":"E56"}}, + "E54": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E53","left":"E54","right":"E55","up":"E56"}}, + "E55": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E53","left":"E54","right":"E55","up":"E56"}}, + "E56": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E53","left":"E54","right":"E55","up":"E56"}}, + "E57": {"name":"掠夺者","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E57","left":"E58","right":"E59","up":"E60"}}, + "E58": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E57","left":"E58","right":"E59","up":"E60"}}, + "E59": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E57","left":"E58","right":"E59","up":"E60"}}, + "E60": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E57","left":"E58","right":"E59","up":"E60"}}, + "E61": {"name":"唤魔者","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E61","left":"E62","right":"E63","up":"E64"}}, + "E62": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E61","left":"E62","right":"E63","up":"E64"}}, + "E63": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E61","left":"E62","right":"E63","up":"E64"}}, + "E64": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E61","left":"E62","right":"E63","up":"E64"}}, + "E65": {"name":"恼鬼","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E65","left":"E66","right":"E67","up":"E68"}}, + "E66": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E65","left":"E66","right":"E67","up":"E68"}}, + "E67": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E65","left":"E66","right":"E67","up":"E68"}}, + "E68": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":2,"faceIds":{"down":"E65","left":"E66","right":"E67","up":"E68"}}, + "E69": {"name":"卫道士","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E69","left":"E70","right":"E71","up":"E72"}}, + "E70": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E69","left":"E70","right":"E71","up":"E72"}}, + "E71": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E69","left":"E70","right":"E71","up":"E72"}}, + "E72": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E69","left":"E70","right":"E71","up":"E72"}}, + "E73": {"name":"劫掠兽","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E73","left":"E74","right":"E75","up":"E76"}}, + "E74": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E73","left":"E74","right":"E75","up":"E76"}}, + "E75": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E73","left":"E74","right":"E75","up":"E76"}}, + "E76": {"name":"怪","hp":1,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[],"type":0,"faceIds":{"down":"E73","left":"E74","right":"E75","up":"E76"}} +} \ No newline at end of file diff --git a/event.d.ts b/event.d.ts new file mode 100644 index 0000000..4e440ce --- /dev/null +++ b/event.d.ts @@ -0,0 +1,762 @@ +/** + * 注册的系统事件函数 + */ +type SystemEventFunc = (data: any, callback: (...params: any[]) => any) => void; + +/** + * 注册的事件函数 + */ +type EventFunc = (data: any, x?: number, y?: number, prefix?: string) => void; + +/** + * 处理所有和事件相关的操作 + */ +interface Events extends EventData { + /** + * 脚本编辑中的事件相关内容 + */ + eventdata: EventData; + + /** + * 公共事件信息 + */ + commonEvent: Record; + + /** + * 所有的系统事件 + */ + systemEvent: Record; + + /** + * 注册的自定义事件 + */ + actions: Record; + + /** + * 开始新游戏 + * @example core.startGame('咸鱼乱撞', 0, ''); // 开始一局咸鱼乱撞难度的新游戏,随机种子为0 + * @param hard 难度名,会显示在左下角(横屏)或右下角(竖屏) + * @param seed 随机种子,相同的种子保证了录像的可重复性 + * @param route 经由base64压缩后的录像,用于从头开始的录像回放 + * @param callback 回调函数 + */ + startGame( + hard: string, + seed?: number, + route?: string, + callback?: () => void + ): void; + + /** + * 游戏结束 + * @example core.gameOver(); // 游戏失败 + * @param ending 结局名,省略表示失败 + * @param fromReplay true表示在播放录像 + * @param norank true表示不计入榜单 + */ + gameOver(ending?: string, fromReplay?: boolean, norank?: boolean): void; + + /** + * 重新开始游戏;此函数将回到标题页面 + */ + restart(): void; + + /** + * 询问是否需要重新开始 + */ + confirmRestart(): void; + + /** + * 注册一个系统事件 + * @param type 事件名 + * @param func 为事件的处理函数,可接受(data,callback)参数 + */ + registerSystemEvent(type: string, func: SystemEventFunc): void; + + /** + * 注销一个系统事件 + * @param type 事件名 + */ + unregisterSystemEvent(type: string): void; + + /** + * 执行一个系统事件 + * @param type 执行的事件名 + * @param data 数据信息 + * @param callback 传入事件处理函数的回调函数 + */ + doSystemEvent( + type: string, + data: any, + callback?: (...params: any[]) => any + ): void; + + /** + * 触发(x,y)点的系统事件;会执行该点图块的script属性,同时支持战斗(会触发战后)、道具(会触发道具后)、楼层切换等等 + * @param x 横坐标 + * @param y 纵坐标 + * @param callback 回调函数 + */ + trigger(x: number, y: number, callback?: () => void): void; + + /** + * 开门(包括三种基础墙) + * @example core.openDoor(0, 0, true, core.jumpHero); // 打开左上角的门,需要钥匙,然后主角原地跳跃半秒 + * @param x 门的横坐标 + * @param y 门的纵坐标 + * @param needKey true表示需要钥匙,会导致机关门打不开 + * @param callback 门完全打开后或打不开时的回调函数 + */ + openDoor( + x: number, + y: number, + needKey?: boolean, + callback?: () => void + ): void; + + /** + * 获得道具并提示,如果填写了坐标就会删除该点的该道具 + * @example core.getItem('book'); // 获得敌人手册并提示 + * @param id 道具id,必填 + * @param num 获得的数量,不填视为1,填了就别填坐标了 + * @param x 道具的横坐标 + * @param y 道具的纵坐标 + * @param callback 回调函数 + */ + getItem( + id: AllIdsOf<'items'>, + num?: number, + x?: number, + y?: number, + callback?: () => void + ): void; + + /** + * 轻按获得面前的物品或周围唯一物品 + * @param noRoute 若为true则不计入录像 + */ + getNextItem(noRoute?: boolean): void; + + /** + * 场景切换 + * @example core.changeFloor('MT0'); // 传送到主塔0层,主角坐标和朝向不变,黑屏时间取用户设置值 + * @param floorId 传送的目标地图id,可以填':before'和':after'分别表示楼下或楼上 + * @param stair 传送的位置,可以填':now'(保持不变,可省略),':symmetry'(中心对称),':symmetry_x'(左右对称),':symmetry_y'(上下对称)或图块id(该图块最好在目标层唯一,一般为'downFloor'或'upFloor') + * @param heroLoc 传送的坐标(如果填写了,就会覆盖上述的粗略目标位置)和传送后主角的朝向(不填表示保持不变) + * @param time 传送的黑屏时间,单位为毫秒。不填为用户设置值 + * @param callback 黑屏结束后的回调函数 + */ + changeFloor( + floorId: FloorIds, + stair?: FloorChangeStair | AllIds, + heroLoc?: Partial, + time?: number, + callback?: () => void + ): void; + + /** + * 是否到达过某个楼层 + * @param floorId 楼层id + */ + hasVisitedFloor(floorId?: FloorIds): boolean; + + /** + * 到达某楼层 + * @param floorId 楼层id + */ + visitFloor(floorId?: FloorIds): void; + + /** + * 推箱子 + * @param data 图块信息 + */ + pushBox(data?: Block): void; + + /** + * 当前是否在冰上 + */ + onSki(number?: number): boolean; + + /** + * 注册一个自定义事件 + * @param type 事件类型 + * @param func 事件的处理函数,可接受(data, x, y, prefix)参数 + * data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀 + */ + registerEvent(type: string, func: EventFunc): void; + + /** + * 注销一个自定义事件 + * @param type 事件类型 + */ + unregisterEvent(type: string): void; + + /** + * 执行一个自定义事件 + * @param data 事件信息 + * @param x 事件横坐标 + * @param y 事件纵坐标 + * @param prefix 当前点前缀 + */ + doEvent(data: any, x?: number, y?: number, prefix?: string): void; + + /** + * 直接设置事件列表 + * @param list 事件信息 + * @param x 横坐标 + * @param y 纵坐标 + * @param callback 事件执行完毕后的回调函数 + */ + setEvents( + list: MotaEvent, + x?: number, + y?: number, + callback?: () => void + ): void; + + /** + * 开始执行一系列自定义事件 + * @param list 事件信息 + * @param x 横坐标 + * @param y 纵坐标 + * @param callback 事件执行完毕后的回调函数 + */ + startEvents( + list?: MotaEvent, + x?: number, + y?: number, + callback?: () => void + ): void; + + /** + * 执行下一个事件指令,常作为回调 + * @example + * // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果 + * core.setCurtain([0,0,0,1], undefined, null, core.doAction); + */ + doAction(): void; + + /** + * 插入一段事件;此项不可插入公共事件,请用 core.insertCommonEvent + * @example core.insertAction('一段文字'); // 插入一个显示文章 + * @param action 单个事件指令,或事件指令数组 + * @param x 新的当前点横坐标 + * @param y 新的当前点纵坐标 + * @param callback 新的回调函数 + * @param addToLast 插入的位置,true表示插入到末尾,否则插入到开头 + */ + insertAction( + action: MotaEvent | MotaAction, + x?: number, + y?: number, + callback?: () => void, + addToLast?: boolean + ): void; + + /** + * 插入一个公共事件 + * @example core.insertCommonEvent('加点事件', [3]); + * @param name 公共事件名;如果公共事件不存在则直接忽略 + * @param args 参数列表,为一个数组,将依次赋值给 flag:arg1, flag:arg2, ... + * @param x 新的当前点横坐标 + * @param y 新的当前点纵坐标 + * @param callback 新的回调函数 + * @param addToLast 插入的位置,true表示插入到末尾,否则插入到开头 + */ + insertCommonEvent( + name: EventDeclaration, + args?: any[], + x?: number, + y?: number, + callback?: () => void, + addToLast?: boolean + ): void; + + /** + * 获得一个公共事件 + * @param name 公共事件名称 + */ + getCommonEvent(name: EventDeclaration): any; + + /** + * 恢复一个事件 + * @param data 事件信息 + */ + recoverEvents(data?: any): boolean; + + /** + * 检测自动事件 + */ + checkAutoEvents(): void; + + /** + * 当前是否在执行某个自动事件 + * @param symbol 自动事件的标识符 + * @param value 不太清楚有什么用 + */ + autoEventExecuting(symbol?: string, value?: any): boolean; + + /** + * 当前是否执行过某个自动事件 + * @param symbol 自动事件的标识符 + * @param value 不太清楚有什么用 + */ + autoEventExecuted(symbol?: string, value?: any): boolean; + + /** + * 将当前点坐标入栈 + */ + pushEventLoc(x: number, y: number, floorId?: FloorIds): void; + + /** + * 弹出事件坐标点 + */ + popEventLoc(): void; + + /** + * 预编辑事件 + * @param data 事件信息 + */ + precompile(data?: any): any; + + /** + * 点击怪物手册时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + openBook(fromUserAction?: boolean): void; + + /** + * 点击楼层传送器时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + useFly(fromUserAction?: boolean): void; + + /** 点击装备栏时的打开操作 */ + openEquipbox(fromUserAction?: boolean): void; + + /** + * 点击工具栏时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + openToolbox(fromUserAction?: boolean): void; + + /** + * 点击快捷商店按钮时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + openQuickShop(fromUserAction?: boolean): void; + + /** + * 点击虚拟键盘时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + openKeyBoard(fromUserAction?: boolean): void; + + /** + * 点击存档按钮时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + save(fromUserAction?: boolean): void; + + /** + * 点击读档按钮时的打开操作 + * @param fromUserAction 是否是用户开启的 + */ + load(fromUserAction?: boolean): void; + + /** + * 点击设置按钮时的操作 + * @param fromUserAction 是否是用户开启的 + */ + openSettings(fromUserAction?: boolean): void; + + /** + * 当前是否有未处理完毕的异步事件(不包含动画和音效) + */ + hasAsync(): boolean; + + /** + * 立刻停止所有异步事件 + */ + stopAsync(): void; + + /** + * 是否有异步动画 + */ + hasAsyncAnimate(): boolean; + + /** + * 跟随 + * @param name 要跟随的一个合法的4x4的行走图名称,需要在全塔属性注册 + */ + follow(name: ImageIds | NameMapIn): void; + + /** + * 取消跟随 + * @param name 取消跟随的行走图,不填则取消全部跟随者 + */ + unfollow(name?: ImageIds | NameMapIn): void; + + /** + * 数值操作 + * @param name 操作的数值的名称 + * @param operator 操作运算符 + * @param value 值 + * @param prefix 独立开关前缀 + */ + setValue( + name: `${EventValuePreffix}:${string}`, + operator: MotaOperator, + value: number, + prefix?: string + ): void; + + /** + * @deprecated 已失效(大概 + * 设置一项敌人属性并计入存档 + * @example core.setEnemy('greenSlime', 'def', 0); // 把绿头怪的防御设为0 + * @param id 敌人id + * @param name 属性的英文缩写 + * @param value 属性的新值 + * @param operator 操作符 + * @param prefix 独立开关前缀,一般不需要 + * @param norefresh 是否不刷新状态栏 + */ + setEnemy( + id: AllIdsOf<'enemys' | 'enemy48'>, + name: K, + value: Enemy[K], + operator?: MotaOperator, + prefix?: string, + norefresh?: boolean + ): void; + + /** + * @deprecated 已失效(大概 + * 设置某个点的敌人属性 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param name 属性的英文缩写 + * @param value 属性的新值 + * @param operator 操作符 + * @param prefix 独立开关前缀,一般不需要 + * @param norefresh 是否不刷新状态栏 + */ + setEnemyOnPoint( + x: number, + y: number, + floorId: FloorIds, + name: K, + value: Enemy[K], + operator?: MotaOperator, + prefix?: string, + norefresh?: boolean + ): void; + + /** + * @deprecated 已失效(大概 + * 重置某个点的敌人属性 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param norefresh 是否不刷新状态栏 + */ + resetEnemyOnPoint( + x: number, + y: number, + floorId?: FloorIds, + norefresh?: boolean + ): void; + + /** + * @deprecated 已失效(大概 + * 将某个点已经设置的敌人属性移动到其他点 + * @param fromX 起始横坐标 + * @param fromY 起始纵坐标 + * @param toX 目标横坐标 + * @param toY 目标纵坐标 + * @param floorId 楼层id + * @param norefresh 是否不刷新状态栏 + */ + moveEnemyOnPoint( + fromX: number, + fromY: number, + toX: number, + toY: number, + floorId?: FloorIds, + norefresh?: boolean + ): void; + + /** + * 设置一项楼层属性并刷新状态栏 + * @example core.setFloorInfo('ratio', 2, 'MT0'); // 把主塔0层的血瓶和宝石变为双倍效果 + * @param name 要求改的属性名 + * @param values 属性的新值 + * @param floorId 楼层id,不填视为当前层 + * @param prefix 独立开关前缀,一般不需要,下同 + */ + setFloorInfo( + name: K, + values?: Floor[K], + floorId?: FloorIds, + prefix?: string + ): void; + + /** + * 设置全塔属性 + * @param name 属性名 + * @param value 属性值 + */ + setGlobalAttribute( + name: K, + value: GlobalAttribute[K] + ): void; + + /** + * 设置一个系统开关 + * @example core.setGlobalFlag('steelDoorWithoutKey', true); // 使全塔的所有铁门都不再需要钥匙就能打开 + * @param name 系统开关的英文名 + * @param value 开关的新值,您可以用!core.flags[name]简单地表示将此开关反转 + */ + setGlobalFlag( + name: K, + value: CoreFlags[K] + ): void; + + /** + * 设置文件别名 + * @param name 别名 + * @param value 别名对应的文件名 + */ + setNameMap(name: string, value?: SourceIds): void; + + /** + * 设置剧情文本的属性 + */ + setTextAttribute(data: Partial): void; + + /** + * 移动对话框 + * @param code 对话框的代码 + * @param loc 目标位置 + * @param relative 是否是相对模式 + * @param moveMode 缓动模式 + * @param time 动画时间 + * @param callback 移动完毕的回调函数 + */ + moveTextBox( + code: number, + loc: LocArr, + relative?: boolean, + moveMode?: EaseMode, + time?: number, + callback?: () => void + ): void; + + /** + * 清除对话框 + * @param code 对话框的代码 + * @param callback 回调函数 + */ + clearTextBox(code: number, callback: () => void): void; + + /** + * 关门,目标点必须为空地 + * @example core.closeDoor(0, 0, 'yellowWall', core.jumpHero); // 在左上角关掉一堵黄墙,然后主角原地跳跃半秒 + * @param x 横坐标 + * @param y 纵坐标 + * @param id 门的id,也可以用三种基础墙 + * @param callback 门完全关上后的回调函数 + */ + closeDoor( + x: number, + y: number, + id: AllIdsOf>, + callback?: () => void + ): void; + + /** + * 显示一张图片 + * @example + * // 裁剪winskin.png的最左边128×128px,放大到铺满整个视野,1秒内淡入到50%透明,编号为1 + * core.showImage(1, core.material.images.images['winskin.png'], [0,0,128,128], [0,0,416,416], 0.5, 1000); + * @param code 图片编号,为不大于50的正整数,加上100后就是对应画布层的z值,较大的会遮罩较小的,注意色调层的z值为125,UI层为140 + * @param image 图片文件名(可以是全塔属性中映射前的中文名)或图片对象(见上面的例子) + * @param sloc 一行且至多四列的数组,表示从原图裁剪的左上角坐标和宽高 + * @param loc 一行且至多四列的数组,表示图片在视野中的左上角坐标和宽高 + * @param opacityVal 不透明度,为小于1的正数。不填视为1 + * @param time 淡入时间,单位为毫秒。不填视为0 + * @param callback 图片完全显示出来后的回调函数 + */ + showImage( + code: number, + image: ImageIds | NameMapIn | ImageSource, + sloc?: [number, number, number, number], + loc?: [number, number, number?, number?], + opacityVal?: number, + time?: number, + callback?: () => void + ): void; + + /** + * 隐藏一张图片 + * @example core.hideImage(1, 1000, core.jumpHero); // 1秒内淡出1号图片,然后主角原地跳跃半秒 + * @param code 图片编号 + * @param time 淡出时间,单位为毫秒 + * @param callback 图片完全消失后的回调函数 + */ + hideImage(code: number, time?: number, callback?: () => void): void; + + /** + * 移动一张图片并/或改变其透明度 + * @example core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明 + * @param code 图片编号 + * @param to 新的左上角坐标,省略表示原地改变透明度 + * @param opacityVal 新的透明度,省略表示不变 + * @param moveMode 移动模式 + * @param time 移动用时,单位为毫秒。不填视为1秒 + * @param callback 图片移动完毕后的回调函数 + */ + moveImage( + code: number, + to?: LocArr, + opacityVal?: number, + moveMode?: string, + time?: number, + callback?: () => void + ): void; + + /** + * 旋转一张图片 + * @param code 图片编号 + * @param center 旋转中心像素(以屏幕为基准);不填视为图片本身中心 + * @param angle 旋转角度;正数为顺时针,负数为逆时针 + * @param moveMode 旋转模式 + * @param time 移动用时,单位为毫秒。不填视为1秒 + * @param callback 图片移动完毕后的回调函数 + */ + rotateImage( + code: number, + center?: LocArr, + angle?: number, + moveMode?: EaseMode, + time?: number, + callback?: () => void + ): void; + + /** + * 放缩一张图片 + * @param code 图片编号 + * @param center 旋转中心像素(以屏幕为基准);不填视为图片本身中心 + * @param scale 放缩至的比例 + * @param moveMode 旋转模式 + * @param time 移动用时,单位为毫秒。不填视为1秒 + * @param callback 图片移动完毕后的回调函数 + */ + scaleImage( + code: number, + center?: LocArr, + scale?: number, + moveMode?: string, + time?: number, + callback?: () => void + ): void; + + /** + * 绘制一张动图或擦除所有动图 + * @example core.showGif(); // 擦除所有动图 + * @param name 动图文件名,可以是全塔属性中映射前的中文名 + * @param x 动图在视野中的左上角横坐标 + * @param y 动图在视野中的左上角纵坐标 + */ + showGif( + name?: + | Extract> + | NameMapIn>>, + x?: number, + y?: number + ): void; + + /** + * 调节bgm的音量 + * @example core.setVolume(0, 100, core.jumpHero); // 0.1秒内淡出bgm,然后主角原地跳跃半秒 + * @param value 新的音量,为0或不大于1的正数。注意系统设置中是这个值的平方根的十倍 + * @param time 渐变用时,单位为毫秒。不填或小于100毫秒都视为0 + * @param callback 渐变完成后的回调函数 + */ + setVolume(value: number, time?: number, callback?: () => void): void; + + /** + * 视野抖动 + * @example core.vibrate(); // 视野左右抖动1秒 + * @param direction 抖动方向 + * @param time 抖动时长,单位为毫秒 + * @param speed 抖动速度 + * @param power 抖动幅度 + * @param callback 抖动平息后的回调函数 + */ + vibrate( + direction?: string, + time?: number, + speed?: number, + power?: number, + callback?: () => void + ): void; + + /** + * 强制移动主角(包括后退),这个函数的作者已经看不懂这个函数了 + * @example core.eventMoveHero(['forward'], 125, core.jumpHero); // 主角强制前进一步,用时1/8秒,然后主角原地跳跃半秒 + * @param steps 步伐数组,注意后退时跟随者的行为会很难看 + * @param time 每步的用时,单位为毫秒。0或不填则取主角的移速,如果后者也不存在就取0.1秒 + * @param callback 移动完毕后的回调函数 + */ + eventMoveHero(steps: Step[], time?: number, callback?: () => void): void; + + /** + * 主角跳跃,跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。 + * @example core.jumpHero(); // 主角原地跳跃半秒 + * @param ex 跳跃后的横坐标 + * @param ey 跳跃后的纵坐标 + * @param time 跳跃时长,单位为毫秒。不填视为半秒 + * @param callback 跳跃完毕后的回调函数 + */ + jumpHero( + ex?: number, + ey?: number, + time?: number, + callback?: () => void + ): void; + + /** + * 更改主角行走图 + * @example core.setHeroIcon('npc48.png', true); // 把主角从阳光变成样板0层左下角的小姐姐,但不立即刷新 + * @param name 新的行走图文件名,可以是全塔属性中映射前的中文名。映射后会被存入core.status.hero.image + * @param noDraw true表示不立即刷新(刷新会导致大地图下视野重置到以主角为中心) + */ + setHeroIcon(name: string, noDraw?: boolean): void; + + /** 检查升级事件 */ + checkLvUp(): void; + + /** + * 尝试使用一个道具 + * @example core.tryUseItem('pickaxe'); // 尝试使用破墙镐 + * @param itemId 道具id,其中敌人手册、传送器和飞行器会被特殊处理 + */ + tryUseItem( + itemId: ItemIdOf<'tools' | 'constants'>, + noRoute?: boolean, + callback?: () => void + ): void; + + _sys_battle(data: Block, callback?: () => void): void; + + _action_battle(data: any, x?: number, y?: number, prefix?: any): void; + + __action_getLoc(data: any, x?: number, y?: number, prefix?: any): any; + + _changeFloor_beforeChange(info: any, callback: () => void): void; +} + +declare const events: new () => Events; diff --git a/event.md b/event.md new file mode 100644 index 0000000..e033e0a --- /dev/null +++ b/event.md @@ -0,0 +1,329 @@ +# 事件 + +?> 在这一节中,让我们来了解事件系统的基本原理 + +## 事件编辑(地图选点,快捷键X) + +![image](img/events.jpg) + +样板所有的事件都是依靠“触发器”完成的。例如,勇士踩到(绑定好的)楼梯可以触发changeFloor,碰到门可以触发openDoor,碰到怪物可以触发battle,踩到/轻拾道具可以触发getItem,推到箱子可以触发pushBox,走上冰面(背景层)可以触发ski. + +这些触发器都是系统自带的(称为系统事件),已经存在处理机制,不需要我们操心。我们真正所需要关心的,其实只是自定义的事件(如NPC)。 + +地图选点(快捷键X)一共有八项,在地图中以图块左下角用八色小方块标记。 + +* **红色:**普通事件,也包括该点的一些特效。 +* **橙色:**自动事件,可以设置为“仅执行一次”、“仅在本层检测”。 +* **暗绿色:**战前事件,强制战斗时不触发。 +* **黄色:**战后事件。 +* **亮绿色:**楼层转换,可以设置为“可穿透”。 +* **青色:**获得道具后事件,可以设置为“轻按时不触发”。 +* **靛色:**不是事件,为勇士站在该点不能朝哪些方向走。 +* **紫(粉)色:**开门后事件。 + +此外还有一些事件不在地图上,如“首次/每次到达”(这两个的触发原理,详见“脚本编辑——切换楼层后”)、“道具效果”、“碰触事件”、“(批量)战前/战后事件”、“公共事件”和全塔属性中那五个事件。 + +### 事件的机制 + +地图上的所有事件都存在两种状态:启用和禁用。 + +* 启用状态下,该事件才处于可见状态,可被触发、交互与处理。 +* 禁用状态下,该事件几乎相当于不存在(默认可以被阻击怪和箱子推上去),不可见、不可被触发、不可交互,只能通过“插入事件”指令远程调用。 + +所有事件默认情况下初始都是启用的,只有普通事件可以通过不勾选“启用”来初始隐藏。 + +在事件列表中使用“显示事件”和“隐藏事件”可以将一个禁用事件给启用,或将一个启用事件给禁用,甚至直接从地图中删除。 + +**【重要警告】:** +1. 打怪开门捡道具后,如果有对应的坐标,则该点的事件会被从地图中删除(重生怪除外,它只会被暂时隐藏)。然后会尝试执行该点的战后事件、开门后事件或(拾获)道具后事件(您可以勾选“轻按时不触发”)。如果执行,则“当前点坐标”(`core.status.event.data`的坐标)也会变为该点。 + * 所以,这三个XX后事件,其实是有可能被多次触发或意外触发的(如打死或炸掉某个有战后事件的怪,推了个阻击怪过去打死),请自行注意。 +2. “移动事件”(如阻击和捕捉)和“跳跃事件”(如支援)会将起点处的事件从地图中删除,如果不勾选“不消失”(如阻击),则会在终点处“转变图块”并“显示事件”。但事件的内容不会被跟着移动到终点,推箱子同理,但在终点只转变图块而不予显示。 + +### 楼梯、传送门事件 + +![image](img/changefloor.jpg) + +当您在地图上绘制楼梯、或绘制四种三帧箭头并右击绑定后,就创建了一个“楼层转换”事件,您可以在事件编辑器右侧看到一行代码(json),请注意对照。 + +此事件与`core.changeFloor(floorId, stair, heroLoc, time, callback)`函数相对应,只是多了一项“穿透性”。 + +每个这样的事件都是上图的写法,下面逐一讲解其各项的含义和用法: + +1. **目标楼层(floorId):**如题,如果选择了“楼层ID”就需要在第二个框中输入目标楼层的ID(会自动提示补全,也可以双击整个紫色块从地图选点)。如果选择了其他三种,则json代码中`:before`、`:next`、`:now`分别表示“前一楼”、“后一楼”(顺序由“全塔属性——楼层列表”指定,上下楼器同理)和“当前楼”,函数中也是一样的写法。 +2. **目标位置(stair):**如果选择了“坐标”则需要在后面两个框里输入(同样可以地图选点,在函数中则将`stair`填`null`并填写`heroLoc`),也可以选择下拉框中的其他几项(在函数中则将`heroLoc`填`null`),如保持不变(`:now`)、中心对称(`:symmetry`)、左右对称(`:symmetry_x`)、上下对称(`:symmetry_y`)或任何一个图块ID. + * 填写三种对称位置时请注意,坐标计算以【当前楼层的勇士】位置(对不可踏入的楼梯来说,这个位置与楼梯相邻而不重合)而不是目标楼层或该楼梯为准,注意防止勇士出界。 + * 填写图块ID时请注意,“上下楼梯”和“楼传落点”提供在了下拉框中,实际传送时会优先尝试取用目标层的“楼层属性——上下楼点”。其次尝试像其他ID(必须写在右侧的json区再点击解析)一样从目标楼层搜索,因此请确保该图块在目标楼层中存在且唯一。 +3. **朝向:**有八种写法,可以直接填写改变后的朝向(`up, down, left, right`),也可以填写转身的角度(`:left, :right, :back`)或不变。 +4. **动画时间:**指黑屏的毫秒数,可以填0或不小于100的整数,不填则使用玩家设定值。 + * V2.8起,楼层切换的音效被延迟到了“屏幕完全变黑”的瞬间(脚本编辑——切换楼层中)才播放,以方便作者根据条件设置不同音效,因此上下楼时间建议尽量缩短。 +5. **穿透性:**见[系统开关](start)允许穿透楼梯。 + +值得注意的是,绑定了楼层转换事件的门、怪物、道具、箱子,其系统行为(开门、战斗、拾取、推行)将不再发生(阻激夹域等地图属性不受影响),而是被覆盖为上下楼。 + +## 普通事件(红) + +普通事件在启用后可以被勇士碰触而触发,它们都有着这样的开头: +``` +覆盖触发器(Y/N) 启用(Y/N) 通行状态(Y/N?) 显伤(Y/N) +V2.8新增:不透明度(0~1) 虚化 色相(0~359) 灰度(0~1) 反色(Y/N) 阴影 +``` +1. **覆盖触发器:**如前所述,门、怪物、道具、箱子都已经被绑定了系统触发器。如果您需要让地图上的个别这类图块在被勇士碰触时不发生系统行为(开门、战斗、拾取、推行),而是执行您在下面自定义的事件(比如需要制作一个“怪物中的叛徒”,它虽然有显伤但其实是个npc),请勾选此项。(阻激夹域捕捉支援等地图属性不受影响) +2. **启用:**如果不勾选此项,则此事件初始时是隐藏的。 + * 非常不推荐仅仅为了初始隐藏一些图块(如战利品、陷阱门墙、埋伏怪等)就去绑定一堆没有指令的普通事件。 + * 更安全的做法是“从0转变图块”(支持淡入效果)和“关门”(这样还有音效,多香)。 + * 事实上,除“覆盖触发器”外,其余所有参数本不该作为“普通事件”的一部分而应该移出去放在表格中,这是样板的历史遗留问题。 + * 如今V2.8加入了六项特效,可能导致无指令的普通事件越来越不可避免,请自行注意。 +3. **通行状态:**除怪物和道具外,所有图块本身都具有“可通行性”属性。 + * 您可以设置此项去覆盖地图中这一点的可通行性,譬如强行让一格空地不可通行,勇士撞击时出现一堵墙(51层魔塔第23层的效果)。 +4. **显伤:**在对怪物使用覆盖触发器后,表面上看不出它和一般怪物的不同。 + * 如有必要(比如触碰这怪物根本不战斗),可不勾选显伤以示区别。 + * 如数量较多,可注册一个相同素材的NPC,比如样板中的仙子。 + * 如需在一段剧情中关闭所有显伤,可以简单地暂时移除怪物手册 +5. **不透明度:**如题,0表示完全不透明,1表示完全隐身。可以设为1用来配合楼层贴图实现大型怪物,或者设为半透明来表示灵魂等状态的角色。本属性可以在事件中用指令动态修改,支持渐变。 +6. **虚化:**必须为自然数,设定后图块会变模糊,可以用来配合快速移动/跳跃来表示幻影状态的角色。不建议对多帧图块长时间使用,因为会随着不断的抖动把周围越描越黑。 +7. **色相:**必须为0~359的整数,180表示互补色。常用于npc以避免过于单调,也可以在游戏的教学环节用此项高亮强调部分图块。 +8. **灰度:**如题,1表示完全变灰,必要时可以全图变灰来表示回忆/梦境/濒死等场合。 +9. **反色:**勾选后此图块将反色,必要时可以全图瞬间反色来表示惊吓。 +10. **阴影:**必须为自然数,设定后图块周围将出现阴影描边,在草地上效果更佳哦。 + +可以使用`core.setBlockFilter`更改任何一点的最后五项特效。 + +## 自动事件(橙) + +在“右键绑定机关门”中,我们已经初见了自动事件的端倪。 + +在游戏中,我们经常需要监听大量乱七八糟的状态,根据状态的变化去做出及时的处理,比如勇士生命小于等于0会触发游戏失败。比如51层魔塔第39层监听9扇黄门的状态来触发中心对称飞行器的出现、第49层监听8名魔法警卫的状态来封印假魔王。 + +如果给9扇黄门都绑定类似的开门后事件、给8名警卫都绑定类似的战后事件,就非常繁琐。 + +而自动事件则不然,它不需要您去主动考虑所有“改变状态”的原因,而是简单地在每次刷新状态栏时,检查是否满足触发条件,满足的话就执行。 + +每个自动事件具有以下几个属性: + +1. **触发条件:**自动事件最重要的属性,为支持“冒号缩写量”的逻辑表达式。 + * 譬如上述两个例子中,触发条件就是“某两个点没有图块而某7个点图块为黄门”和“某四个点没有图块而某四个点图块为魔法警卫”。 + * 关于冒号缩写量,详见下面的“值块和冒号缩写量”。 + * V2.8起,自动事件的触发条件和“值块”支持多行编辑(eval时会忽略\n)。 + * 因此高级作者可以将此条件写成无名函数,这样就可以在里面使用var进行复杂的多步计算了。 +2. **优先级:**一般不需要改动。当多个自动事件的条件同时满足时,优先执行此级别较大的。 + * 此级别也相等时,允许跨层触发则优先执行楼层较矮的。 + * 同一楼层优先执行横坐标较小的,横坐标相等时优先执行纵坐标较小的,同一点处优先执行页码较小的。 +3. **仅在本层检测:**如题,一般不需要改动。除非您要制作一些全局的效果,如“勇士生命低于上限的1/3时改变背景音乐”。 + * V2.8起,即使不勾选此项,也不会在分区砍层模式下触发冻结状态楼层的自动事件! +4. **事件流中延迟执行:**勾选此项后,在执行其他事件时触发此自动事件则会将其内容追加到待执行事件的末尾,否则插队到待执行事件的开头。 +5. **允许多次执行:**如果不勾选,则此事件最多只会被触发一次。即使勾选了,每次执行的过程中也会暂时禁止自己被触发。勾选后,请记得在此事件的指令中将条件变得不满足。 + +每个点初始提供了两个自动事件页,您还可以再在数据区最上面单击“添加自动事件页”。 + +## 公共事件(逗号键) + +有几个事件,如战后加点、回收钥匙商店,会在战后脚本或全局商店等处频繁用到。于是它们被整理成了公共事件,您也可以追加自己的。 + +公共事件在内容上和其他事件并无不同,只是在插入公共事件时(如`core.insertCommonEvent("回收钥匙商店",[...])`)可以提供一个`参数列表`。 + +参数列表是一个一维数组,其每项会被依次代入`core.status.hero.flags.arg1、arg2、……`,而`arg0`则会记录公共事件的中文名。 + +在整个事件流结束后(即勇士恢复行动,请注意不是公共事件结束后),这些以arg加纯数字命名的变量、以及以@temp@开头的临时变量都会被清空。 + +## 滑冰事件 + +滑冰(ski)是一个非常特殊的触发器,使用此触发器的图块需要画在背景层。 + +冰上可以画门、怪物、道具等任何图块,您还可以在前景层画上四种单向通行箭头(样板1层右下方那些)或红线薄墙(素材区第一列最下方那些),把三个图层的游戏性都发挥出来。 + +勇士走上冰面后,将一直“前进一格或撞击”。直到滑出冰面或无法再前进,如撞到墙或事件。 + +比如撞到怪物会强制战斗,打不过直接游戏失败。走到道具上会捡起来,撞到门则要看有没有钥匙,有的话会把门打开。V2.7.3起,踩到致命领域会死亡而不是停在前一格。 + +默认情况下,触发事件后勇士会停下来。如果要继续滑冰,可以在这些点的战后事件、开门后事件、(拾获)道具后事件(这里最好勾选“轻按时不触发”)中判断勇士还在不在冰上(`core.onSki()`函数),在冰上的话就命令“勇士前进一格或撞击”。 + +## 事件编辑器 + +![image](img/blockly.jpg) + +当您从数据区单击任何一个事件类属性的“编辑”按钮时,就会呼出事件编辑器,上图给出了事件编辑器的全貌。 + +图中左下角为指令类别,可以点击它们呼出各类指令菜单,如左上方的值块菜单。 + +中央为指令区,被一个标识事件类型的紫色块包覆(即左下角的“入口方块”,图中为开门后事件),所有的指令都塞在里面。 + +右上角为json区,会随着指令区的编辑而自动变化。`core.insertAction()`函数的自变量就是它们,所以学有余力的话也建议您经常左右对照去了解每个指令对应的json写法。 + +右下角为调色器,当您单击指令块中的颜色按钮时(如图中“画面闪烁”指令的白块)就会自动呼出,可以进行诸如十进制和十六进制颜色格式互转并预览等操作。 + +左上角为功能区,各功能的含义和用法如下: + +1. **确认(Ctrl+S):**将指令区的指令组翻译为json写回表格和文件,并关闭事件编辑器。如果手动修改了json区却没有点击变黄的解析按钮,或指令区存在异步事件没有用“等待所有异步事件执行完毕”(图中第二条褐色指令)去等待,则单击确认按钮会弹窗警告。此时如果想放弃对json区的修改,请随便点一下指令区的一个块即可。 +2. **解析:**将手动修改后的json区翻译回指令组同步到指令区,一般用于跨作品复制事件,或长距离批量挪动和复制指令。此外,由于下拉框不支持任意输入(如新增的怪物属性、楼层切换的任意目标图块ID),所以也必须这样做。 +3. **取消:**放弃本次编辑,关闭事件编辑器,表格和文件会停留在打开事件编辑器前的状态。 +4. **搜索事件块:**当您想不起来需要的指令在哪个类别时,请使用此项,如输入“勇”字就会列出所有带有“勇士”一词的指令。 +5. **地图选点:**在编辑事件尤其是大篇幅事件的过程中,如果需要频繁保存并退出事件编辑器去浏览地图尤其是浏览别的楼层就很不方便。单击此按钮,您可以浏览所有楼层及其全景。通过点击地图去确认一个点的坐标,并复制任何一个楼层ID以填入需要的地方。需要填坐标的指令也可以直接双击其指令块从地图选点填入坐标和楼层,从而避免了肉眼数格子和手敲的麻烦和出错的隐患。 +6. **变量出现位置搜索:**您可以搜索所有以“变量:xxx”形式出现在事件中的变量的出现位置,形式必须是冒号缩写量,位置必须是事件而不是脚本。也就是说,`flags.xxx`和`core.insertAction([...])`中的变量都是搜索不到的。 +7. **开启中文名替换:**勾选此项后,“flag、status、item、switch、temp、global、enemy”等冒号缩写量的前缀以及怪物和道具的ID才会允许用中文在指令块中书写,如“变量、状态、物品、独立开关、临时变量、全局存储、怪物”。当然,json中还是只能用英文的。此外,如果您需要使用名称相同的道具或怪物,或在`${}`以外的文本中将上述几个词和冒号连用,则也可能需要关闭此功能以避免blockly和json互译出错。 +8. **展开值块逻辑运算:**在值块菜单中我们可以看到第一个就是二元运算块,但是它并不完整,比如没有位运算。因此如果解析的优先级与js的优先级不一致,请在表达式中适当的位置添加圆括弧,或取消勾选此项。 + +## 值块和冒号缩写量 +![image](img/values.jpg) +值块并不能单独构成指令,但它们可以被嵌入数值操作、设置怪物属性和很多事件控制类指令。而冒号缩写量的用法就更多了,比如用于`${表达式计算}`和其他任何支持填表达式的地方。 + +值块大体上分为三种:勾选框与运算块、只读块、可读写块,其中后两类对应着冒号缩写量。 + +### 勾选框与运算块(附js比较运算大图鉴) + +包括勾选框块(`true`和`false`两个逻辑常量)、否定运算块和二元运算块。 + +否定运算块(一个“非”字,脚本中写作一个叹号)会把`false、0、null、undefined、NaN`和空字符串”(以下简称广义`false`)变为`true`,而把别的量都变为`false`. + +二元运算块包括四则运算、取余、乘方、比较运算和三种逻辑运算。 + +四则运算中,两个整数相除会得到小数,两个0的乘方会得到1(数学上无意义)。 + +八种比较运算中,四种比大小的运算如果两项中有一项是数字,就会把另一项也转换成数字去比,所以会出现这些: + +`'2' < 10; 10 <= '10'; '10' < 2; null >= 0; null <= 0; 1/0 > 1/-0` + +![image](img/compare.jpg) + +弱(不)等于(==和!=)非常难用,建议放弃,统一使用(不)等于(===和!==)。 + +`NaN`一般是`0/0`或字符串瞎做减乘除得到的,它跟谁都没有可比性,包括自己。 + +纯数字字符串(包括十六进制)和对应的数字,都是弱相等关系。如`'0x10' == 16` + +数组和对象,跟自己都是既大于等于又小于等于的关系(因为不是同一个)。 + +单个数字(或什么都没有)用方括号还是引号括起来,对外的比较行为都一致。 + +`undefined`倒是不用担心,它除了和`null`弱相等外,和其他值都没有可比性。 + +三种逻辑运算的原则是,“且”的优先级高于“或”,它俩都不满足交换律。 + +若干个由“且”(&&)连起来的量中,取第一个广义`false`(没有则取最后一个量), + +若干个由“或”(||)连起来的量中,取第一个不是广义`false`的量(没有则同上), + +若干个由“异或”(^)连起来的`true`或`false`,总结果取决于其中`true`的个数的奇偶,奇数个`true`则总结果为1,偶数个`true`则总结果为0. + +所以样板中充斥着形如`x=x||y`和`z=x||y`的代码,y算是x的默认值。 + +这种做法并不安全,如果您的作品不打算发布到我们的`h5mota.com`,则更建议使用`x??y`,它只会在“x为`null`或`undefined`时”才用y代替x。 + +双问号运算在早期浏览器并不被支持,因此如需在我们的站点发布则需要写`if(x==null){x=y;}`。 + +再次强调,广义`false`不一定和`false`弱相等(比如`null`),而和`false`弱相等的也不一定是广义`false`(如`[]==![]`)。 + +V2.7.3起,一元运算块追加了一些数学函数,如四种取整、开平方、绝对值。还追加了“变量类型”(typeof),只有两个类型相同的变量才有可能满足“相等”(===)。 + +V2.8起,二元运算块追加了一些js函数,如“取较大/较小”(`Math.max`和`Math.min`)、“开始于/结束于”(字符串的`startsWith`和`endsWith`)、“包含”(数组和字符串的`includes`),进阶作者可以学习使用。 +### 只读块(怪物属性、图块ID、图块类别、装备孔) + +尽管这几个值块是只读的,但您仍然可以使用“设置怪物属性”、“穿脱装备”和“转变图块”等指令去改变它们。 + +1. **怪物属性:**冒号缩写量写作`怪物:绿头怪:生命`,json代码中写作`enemy:greenSlime:hp`。怪物名称有重复的话可以在事件编辑器顶部关闭中文替换功能,道具名称同理。 + * 常见的怪物属性都提供在了下拉框中,如果下拉框里没有(如通过配置表格新增的属性),可以修改`_server\MotaAction.g4`文件最后的那些表来追加其中文简称,也可以放弃解析回中文块而是就用缩写量。 + * V2.8起,怪物支持“行走图朝向”绑定,此值块将始终获取脸朝下的怪物属性。 +2. **图块ID:**冒号缩写量写作`图块ID:x,y`,json代码中写作`blockId:x,y`。请注意逗号始终要用英文的,此值块实际直接调用的API为`core.getBlockId(x,y)`,即本值块和对应的冒号缩写量只支持本层,可以获知本层某个点的图块ID. +3. **图块数字:**冒号缩写量写作`图块数字:x,y`,json代码中写作`blockNumber:x,y`。同上,实际调用`core.getBlockNumber(x,y)`,可以获知本层某个点的图块数字。 +4. **图块类别:**冒号缩写量写作`图块类别:x,y`,json代码中写作`blockCls:x,y`。同上,实际调用`core.getBlockCls(x,y)`,可以获知本层某个点的图块类别。 + * V2.8起,这三个值块中的坐标支持填写表达式,会在json区直接翻译为API调用,但这样填写也意味着无法再解析回值块,而且本质上这已经不是冒号缩写量了,不能直接用于${表达式计算}等场合。 +5. **第n格装备孔:**冒号缩写量写作`装备孔:n`,json代码中写作`equip:n`。实际调用`core.getEquip(n)`,可以获知勇士第n个装备孔中的装备ID,n从0开始! + +### 可读写块(status、item、flag、switch、temp、global、buff) + +比起只读块,可读写块允许作为“数值操作”指令的左块: +1. **状态:**冒号缩写量为`状态:生命`等,json代码中写作`status:hp`等。 + * 作为左块时会调用`core.setStatus()`,其他时候会调用`core.getStatus()` +2. **物品:**冒号缩写量为`物品:炸弹`等,json代码中写作`item:bomb`等 + * 作为左块时会调用`core.getItem()`或`core.setItem()`,其他时候会调用`core.itemCount()`来统计背包中的道具数量。 + * 此外,最常用的一些勇士状态(包括坐标和朝向)、三色钥匙、三围增益都提供在了下拉框中。您可以修改`_server/MotaAction.g4`文件最后的`FixedId_List`来追加,向“楼层转换”、“门信息”和“装备属性”的下拉框追加新的楼梯ID、钥匙ID和勇士状态名也是一样的道理,当然不追加也不影响手写(如果指令只提供了下拉框版本,那就必须写在json区再解析)。 +3. **变量:**冒号缩写量为`变量:hatred`等,json代码中写作`flag:hatred`等。 + * 作为左块时会调用`core.setFlag()`,其他时候会调用`core.getFlag(’xxx’, 0)` + * 请注意:变量类型支持中文,如`变量:开门个数` + * 这两个API实际操作的就是前文多次提到的`core.status.hero.flags`,只不过在事件中未定义的变量都视为0,更为安全。 + * 如果您忘记了自己在哪些事件用过哪些变量,请善用事件编辑器顶部的“搜索变量出现位置”。 +4. **独立开关:**如果您有一大批NPC都具有“首次对话不同于之后各次对话”之类的特点,那么为他们设置一大批不重名的变量就很繁琐。独立开关(叫独立变量更准确)就是为这种场合而生的,它对每层楼(首次到达和每次到达中)和每个坐标都是独立的。 + * 冒号缩写量写作`独立开关:A—Z`,json代码中写作`switch:A—Z`。 + * 每个坐标处的独立开关有26个,用大写拉丁字母A—Z表示。MTn层(x,y)点的独立开关A,本质上是一个flag变量——`flag:MTn@x@y@A`,如果需要在其他点访问,就需要用这样的写法。 + * 首次到达/每次到达楼层的事件中,上述x和y会保留小写字母而不代入任何数字。 + * 标题事件/开场剧情中,前缀的“楼层ID”视为“`:f`”。 + * 可以在事件编辑器左侧菜单的“常用事件模板”中看到一个仿51层/新新魔塔的一次性商人的例子。 +5. **临时变量:**冒号缩写量写作“临时变量:A—Z”,json代码中写作`temp:A-Z`, + * 临时变量泛指所有以`@temp@`开头的flag变量,一共也有26个。 + * 临时变量一般用于计数循环和数组迭代,每当事件流结束(勇士恢复行动)时,临时变量和参数变量(以arg+纯数字命名)都会被清空。 + * 全局商店状态下,临时变量`@temp@shop`会被启用,从而实现长按连续购买等操作。 + * 上面提到的“一次性商人”模板中多次出现出售数量和价格常数,如果使用临时变量就很方便,不需要修改多个地方了。 +6. **全局存储:**冒号缩写量写作`全局存储:xxx`,json代码中写作`global:xxx`, + * 和变量一样,冒号右侧本身支持中文。全局存储一般用来保存一些跨存档的信息,如成就系统、回想和音像鉴赏的收集进度、多周目数据等。 + * 用于左块时会调用`core.setGlobal()`,其他时候会调用`core.getGlobal()`。 + * `core.setGlobal()`在录像中会被忽略,`core.getGlobal()`在正常游戏中会将读取到的值写进录像,而在录像播放中直接读取这个写进去的值,从而复原了那次游戏时的样子。 + * 然而,由于`core.getGlobal()`的调用频率在正常游戏和录像播放中可能不一致,因而可能会导致录像中的记录次数过多、过少或位置不合适。 + * V2.8对这个问题进行了一定的修复,请放心使用。 +7. **增益:**冒号缩写量写作`增益:生命上限`等,json代码中写作`buff:hpmax`等,冒号右侧支持自定义的勇士新属性英文名。 + * 用于左块时会调用`core.setBuff()`,其他时候会调用`core.getBuff()`。 + * 增益这个概念在前面讲解装备属性时提到过,所有的百分比装备和百分比衰弱都是靠它发挥作用。 + * `buff:xxx`本质上就是`flag:__buff_xxx__`,但前者更安全(默认值为1)。 + +## 随机数(扩展内容,了解即可) + +此外,V2.8还新增了几个样板API的值块,包括“当前是否身穿某装备”、“当前是否在录像播放中”、“是否到达过某楼层”、“是否开启了某全局商店”、“能否打败某怪物”、“勇士面前第n格的坐标(负数表示身后)”、“随机数”。 + +样板包括两种随机数,一种是上面说的值块提供的`core.rand(n)`,会得到小于正整数n的随机自然数,n不填则会得到小于1的随机正小数。 + +一种则是`core.rand2(n)`,同样会得到小于n的随机自然数,n必须填正整数,不要省略! + +rand本质上是`flag:__rand__`不断一步步推进的结果,初始值为`flag:__seed__`,初始值(又叫随机种子)和游戏难度可以在标题画面通过`core.startGame(hard,seed)`函数指定(如果开启了标题界面事件化,则该函数还必须作为游戏重启函数`core.hideStartAnimate()`的回调来使用,但这样只能指定种子),种子必须为正整数。 + +rand2则是通过js自带的`Math.random()`函数得到的真随机小数经过整数化处理的结果,会被直接写入录像(`random:n`),因此多次调用会导致录像(和存档)变得很庞大。而且正因为是写入录像,所以玩家也可以直接编辑录像文件或`core.status.route`来控制随机的结果。 + +直观地说,rand的结果只和它的调用次数有关,因此玩家不能通过简单的SL操作来优化结果,必须调整路线或干脆重开游戏。 + +而rand2的结果可以通过SL来改变,如果需要连续大量使用“可被SL改变的随机数”(期间不能存档)但又不希望录像变得太庞大,可以先在存档后使用一次`n=core.rand2(114514)`,再使用n次`core.rand()`来推进,效果就能让人满意了。(新新魔塔1就是这样的思路,每次撞怪时先自动存档然后生成n值,战斗中只使用rand)。 + +然而,如果rand和rand2的使用场合不当,那么其调用频率在正常游戏中和录像回放时可能不一致。rand会因为推进步数不一致而导致游戏流程发生变化,rand2则会导致录像中记录的随机数出现多余或缺失。 + +V2.8对rand2(以及“全局存储”、“弹窗输入”、“选择项”、“确认框”)的此问题进行了一定的修复,如果录像中出现了多余的随机数记录,就会在播放时跳过并在控制台警告,如果需要rand2但未记录或记录越界则会现场重新随机生成并补录。在V2.8之前这两种情况都会直接报错,而在V2.8中会导致首次播放结果的二次记录不一致,请不要惊慌,按R键从头再次播放一遍即可。 + +总之,不计入录像的纯观赏性的随机效果(如捡到某道具随机播放一个音效甚至随机指定音调高低,以及样板雨雪效果中的随机)建议直接用`Math.random()`实现(例如V2.8每次上下楼后会在楼层属性的背景音乐中随机播放一个)。 + +## 录像回放(扩展内容,了解即可) + +魔塔具有时空离散性和完全可重复性,因此可以像棋类运动一样记录类似棋谱的东西,我们称之为【录像】。 + +正常游戏中,已录制的内容被保存在一维数组`core.status.route`中并不断从尾部延长,录像回放时,即将播放的内容会保存在一维数组`core.status.replay.toReplay`中并不断从头部缩减。 + +V2.8.1起,录像中勇士死亡将会报错并询问是否回到上一个节点,比起只能读取自动存档更为友好。 + +您可以在正常游戏中自由行动时随时按下R键进行回放,上述数组具体的内容含义如下: + +``` +up down left right 勇士向某个方向行走一步 +item:ID 打开背包使用某件道具,如item:bomb表示使用炸弹 +unEquip:n 卸掉身上第n件装备(n从0开始),如unEquip:1默认表示卸掉盾牌 +equip:ID 打开背包穿上一件装备,如equip:sword1表示装上铁剑 +saveEquip:n 将身上的当前套装保存到第n套快捷套装(n从0开始) +loadEquip:n 快捷换上之前保存好的第n套套装 +fly:ID 使用楼传飞到某一层,如fly:MT10表示飞到主塔10层 +choices:none 确认框/选择项界面超时(作者未设置超时时间则此项视为缺失) +choices:n 确认框/选择项界面选择第n项(选择项中n从0开始,确认框界面0表示确定,1表示取消),此项越界将报错。此项缺失的话,确认框将选择作者指定的默认项(初始光标位置),选择项将弹窗请求补选(后台录像验证中则选默认项)。 +shop:ID 打开某个全局商店,如shop:itemShop表示打开道具商店。因此连载塔千万不要中途修改商店ID! +turn 单击勇士(Z键)转身 +turn:dir 勇士转向某个方向,dir可以为up down left right,注意此项在正常游戏中无法随意触发。 +getNext 轻按获得身边道具,优先获得面前的,身边如果没有道具则此项会被跳过。 +input:none “等待用户操作事件”中超时(作者未设置超时时间则此项会导致报错) +input:xxx 可能表示“等待用户操作事件”的一个操作,也可能表示一个“接受用户输入数字”的输入,后者的情况下xxx为输入的数字。此项缺失的话前者将直接报错,后者将用0代替 +input2:xxx 可能表示“读取全局存储(core.getGlobal)”读取到的值,也可能表示一个“接受用户输入文本”的输入,两种情况下xxx都为base64编码。此项缺失的话前者将重新现场读取,后者将用空字符串代替 +no 走到可穿透的楼梯上不触发楼层切换事件,注意正常游戏中勇士无法随意停在旁边没有障碍物的楼梯上。 +move:x:y 尝试瞬移到[x,y]点,注意正常游戏中勇士无法随意瞬移到相邻格,而回放时连续的瞬移操作将被合并。 +key:n 按下键值为n的键,如key:49表示按下大键盘数字键1,默认会触发使用破墙镐 +click:n:px:py 点击自绘状态栏,n为0表示横屏1表示竖屏,[px,py]为点击的像素坐标 +random:n 生成了随机数n,即core.rand2(num)的返回结果,n必须在[0,num-1]范围,num必须为正整数。此项缺失或越界将导致现场重新随机生成数值,可能导致回放结果不一致! +作者自定义的新项(一般为js对象,可以先JSON.stringify()再core.encodeBase64()得到纯英文数字的内容)需要用(半角圆括弧)括起来。 +``` + +[在线插件库](https://h5mota.com/plugins/)中提供了“录像自助精修”插件,手机也适用,可供研究。 + +开门打怪前会先转身再自动存档,因此该存档被读取后,已录制内容的最后一步将变成转身,导致录像变长,请自行注意。 + +========================================================================================== + +[继续阅读下一章:事件指令](instruction) diff --git a/eventDec.d.ts b/eventDec.d.ts new file mode 100644 index 0000000..a624f85 --- /dev/null +++ b/eventDec.d.ts @@ -0,0 +1,313 @@ +type MotaAction = any; +type MotaEvent = any[]; + +/** + * 某种类型的商店 + */ +type ShopEventOf = ShopEventMap[T]; + +interface ShopEventMap { + /** + * 普通商店 + */ + common: CommonShopEvent; + + /** + * 道具商店 + */ + item: ItemShopEvent; + + /** + * 公共事件商店 + */ + event: CommonEventShopEvent; +} + +interface ShopEventBase { + /** + * 商店的id + */ + id: string; + + /** + * 商店快捷名称 + */ + textInList: string; + + /** + * 是否在未开启状态下快捷商店不显示该商店 + */ + mustEnable: boolean; + + /** + * 是否不可预览 + */ + disablePreview: boolean; +} + +/** + * 普通商店的一个商店选项 + */ +interface CommonShopChoice { + /** + * 选项文字 + */ + text: string; + + /** + * 选项需求,需要是一个表达式 + */ + need: string; + + /** + * 图标 + */ + icon: AllIds; + + /** + * 文字的颜色 + */ + color: Color; + + /** + * 该选项被选中时执行的事件 + */ + action: MotaEvent; +} + +/** + * 普通商店 + */ +interface CommonShopEvent extends ShopEventBase { + /** + * 商店中显示的文字 + */ + text: string; + + /** + * 普通商店的选项 + */ + choices: CommonShopChoice[]; +} + +/** + * 道具商店的一个选项 + */ +interface ItemShopChoice { + /** + * 该选项的道具id + */ + id: AllIdsOf<'items'>; + + /** + * 道具存量 + */ + number: number; + + /** + * 购买时消耗的资源数量,是字符串大概是因为这玩意可以用${} + */ + money: string; + + /** + * 卖出时获得的资源数量 + */ + sell: string; + + /** + * 出现条件 + */ + condition: string; +} + +/** + * 道具商店 + */ +interface ItemShopEvent extends ShopEventBase { + /** + * 道具商店标识 + */ + item: true; + + /** + * 购买消耗什么东西,金币还是经验 + */ + use: 'money' | 'exp'; + + /** + * 每个选项 + */ + choices: ItemShopChoice[]; +} + +interface CommonEventShopEvent { + /** + * 使用的公共事件 + */ + commonEvent: EventDeclaration; +} + +interface AutoEventBase { + /** + * 自动事件的触发条件 + */ + condition: string; + + /** + * 是否只在当前层检测 + */ + currentFloor: boolean; + + /** + * 优先级,优先级越高越优先执行 + */ + priority: number; + + /** + * 是否在事件流中延迟执行 + */ + delayExecute: boolean; + + /** + * 是否允许多次执行 + */ + multiExecute: boolean; + + /** + * 当条件满足时执行的事件 + */ + data: MotaEvent; +} + +interface AutoEvent extends AutoEventBase { + /** + * 当前的楼层id + */ + floorId: FloorIds; + + /** + * 自动事件的索引 + */ + index: string; + + /** + * 事件所在的横坐标 + */ + x: number; + + /** + * 事件所在的纵坐标 + */ + y: number; + + /** + * 事件的唯一标识符 + */ + symbol: string; +} + +interface LevelChooseEvent { + /** + * 难度名称 + */ + title: string; + + /** + * 难度简称 + */ + name: string; + + /** + * 难度的hard值 + */ + hard: number; + + /** + * 难度的颜色 + */ + color: RGBArray; + + /** + * 选择该难度时执行的事件 + */ + action: MotaEvent; +} + +interface LevelUpEvent { + /** + * 升级所需经验 + */ + need: number; + + /** + * 这个等级的等级名 + */ + title: string; + + /** + * 升级时执行的事件 + */ + action: MotaEvent; +} + +/** + * 门信息 + */ +interface DoorInfo { + /** + * 开门时间 + */ + time: number; + + /** + * 开门音效 + */ + openSound: SoundIds; + + /** + * 关门音效 + */ + closeSound: SoundIds; + + /** + * 需要的钥匙 + */ + keys: Partial | `${ItemIdOf<'tools'>}:o`, number>>; + + /** + * 开门后事件 + */ + afterOpenDoor?: MotaEvent; +} + +interface ChangeFloorEvent { + /** + * 到达的楼层 + */ + floorId: ':before' | ':after' | ':now' | FloorIds; + + /** + * 到达的坐标,填了的话stair就无效了 + */ + loc?: LocArr; + + /** + * 到达的坐标 + */ + stair?: FloorChangeStair; + + /** + * 勇士朝向 + */ + direction?: HeroTurnDir; + + /** + * 楼层转换时间 + */ + time?: number; + + /** + * 是否不可穿透 + */ + ignoreChangeFloor?: boolean; +} diff --git a/eventStatus.d.ts b/eventStatus.d.ts new file mode 100644 index 0000000..59ddc52 --- /dev/null +++ b/eventStatus.d.ts @@ -0,0 +1,456 @@ +interface EventStatusDataMap { + /** + * 执行事件中 + */ + action: ActionStatusData; + + /** + * 怪物手册的信息,是当前选择了哪个怪物 + */ + book: number; + + /** + * 楼层传送器中当前楼层索引 + */ + fly: number; + + /** + * 浏览地图时的信息 + */ + viewMaps: ViewMapStatusData; + + /** + * 装备栏的信息 + */ + equipbox: EquipboxStatusData; + + /** + * 道具栏的信息 + */ + toolbox: ToolboxStatusData; + + /** + * 存档界面的信息 + */ + save: SaveStatusData; + load: SaveStatusData; + replayLoad: SaveStatusData; + replayRemain: SaveStatusData; + replaySince: SaveStatusData; + + /** + * 文本框界面的信息 + */ + text: TextStatusData; + + /** + * 确认框界面的信息 + */ + confirmBox: ConfirmStatusData; + + /** + * 关于界面,帮助界面,怪物手册详细信息界面,虚拟键盘界面,系统设置界面,系统选项栏界面, + * 快捷商店界面,存档笔记界面,同步存档界面,光标界面,录像回放界面,游戏信息界面,没有东西 + */ + about: null; + help: null; + 'book-detail': null; + keyBoard: null; + switchs: null; + 'switchs-sounds': null; + 'switchs-display': null; + 'switchs-action': null; + settings: null; + selectShop: null; + notes: null; + syncSave: null; + syncSelect: null; + localSaveSelect: null; + storageRemove: null; + cursor: null; + replay: null; + gameInfo: null; +} + +interface _EventStatusSelectionMap { + /** + * 执行事件中,一般是选择框的当前选中项 + */ + action: number; + + /** + * 装备栏中当前选中了哪个装备 + */ + equipbox: number; + + /** + * 道具栏中当前选中了哪个道具 + */ + toolbox: number; + + /** + * 当前是否是删除模式 + */ + save: boolean; + load: boolean; + + /** + * 当前选择了确认(0)还是取消(1) + */ + confirmBox: 0 | 1; + + /** + * 系统设置界面,存档笔记界面,同步存档界面,录像回放界面,游戏信息界面,当前的选择项 + */ + switchs: number; + 'switchs-sounds': number; + 'switchs-display': number; + 'switchs-action': number; + settings: number; + notes: number; + syncSave: number; + syncSelect: number; + localSaveSelect: number; + storageRemove: number; + replay: number; + gameInfo: number; +} + +interface _EventStatusUiMap { + /** + * 执行事件中,一般是与选择框有关的 + */ + action: ActionStatusUi; + + /** + * 如果是从浏览地图界面呼出的怪物手册,该项是当前正在浏览的地图的索引(注意不是id) + */ + book: number; + + /** + * 确认框中显示的文字 + */ + confirmBox: string; + + /** + * 显示设置的选择信息 + */ + 'switchs-display': SwitchsStatusData; + + /** + * 系统选项栏的选择信息 + */ + settings: SwitchsStatusData; + + /** + * 快捷商店界面,存档笔记界面,同步存档界面,录像回放界面,游戏信息界面的绘制信息 + */ + selectShop: SelectShopStatusUi; + notes: SelectShopStatusUi; + syncSave: SelectShopStatusUi; + syncSelect: SelectShopStatusUi; + localSaveSelect: SelectShopStatusUi; + storageRemove: SelectShopStatusUi; + gameInfo: SelectShopStatusUi; +} + +interface _EventStatusIntervalMap { + /** + * 执行事件中,一般用于呼出某个界面时暂存当前事件信息(? + */ + action: ActionStatusData; + + /** + * 如果是从事件中呼出的,用于存储当前事件信息,当退出怪物手册时恢复事件 + */ + book: ActionStatusData; + + /** + * 如果是从事件中呼出的,用于存储当前事件信息,当退出存档节目时恢复事件 + */ + save: ActionStatusData; + load: ActionStatusData; +} + +interface _EventStatusTimeoutMap { + /** + * 执行事件中,一般是等待用户操作事件等事件中的超时时间的判定 + */ + action: number; +} + +interface _EventStatusAnimateUiMap { + /** + * 执行事件中,一般是对话框事件的动画定时器 + */ + action: number; +} + +type EventStatus = keyof EventStatusDataMap; + +type _FillEventStatus = { + [P in EventStatus]: P extends keyof T ? T[P] : null; +}; + +type EventStatusSelectionMap = _FillEventStatus<_EventStatusSelectionMap>; +type EventStatusUiMap = _FillEventStatus<_EventStatusUiMap>; +type EventStatusIntervalMap = _FillEventStatus<_EventStatusIntervalMap>; +type EventStatusTimeoutMap = _FillEventStatus<_EventStatusTimeoutMap>; +type EventStatusAnimateUiMap = _FillEventStatus<_EventStatusAnimateUiMap>; + +/** + * 某个事件状态下的信息 + */ +interface EventStatusOf { + /** + * 当前事件状态的类型 + */ + id: T; + + /** + * 当前事件状态的信息 + */ + data: EventStatusDataMap[T]; + + /** + * 当前事件状态的选择信息 + */ + selection: EventStatusSelectionMap[T]; + + /** + * 当前事件状态的ui信息 + */ + ui: EventStatusUiMap[T]; + + /** + * 当前事件状态的定时器信息 + */ + interval: EventStatusIntervalMap[T]; + + /** + * 当前事件状态的计时器信息 + */ + timeout: EventStatusTimeoutMap[T]; + + /** + * 当前事件状态的动画信息 + */ + animateUi: EventStatusAnimateUiMap[T]; +} + +interface ActionStatusEventList { + /** + * 要执行的事件列表 + */ + todo: MotaEvent; + + /** + * 全部的事件列表 + */ + total: MotaEvent; + + /** + * 执行条件 + */ + contidion: string; +} + +interface ActionLocStackInfo { + /** + * 横坐标 + */ + x: number; + + /** + * 纵坐标 + */ + y: number; + + /** + * 楼层id + */ + floorId: FloorIds; +} + +/** + * 执行事件中 + */ +interface ActionStatusData { + /** + * 当前的事件列表 + */ + list: DeepReadonly; + + /** + * 事件执行的横坐标,或者对话框的横坐标 + */ + x?: number; + + /** + * 事件执行的纵坐标,或者对话框的纵坐标 + */ + y?: number; + + /** + * 事件执行完毕的回调函数 + */ + callback?: () => void; + + /** + * 不太清楚用处,可能是与自动事件有关的 + */ + appendingEvents: MotaEvent[]; + + /** + * 事件的坐标栈 + */ + locStack: any[]; + + /** + * 当前的事件类型 + */ + type: string; + + /** + * 当前事件 + */ + current: MotaAction; +} + +interface ActionStatusUi { + /** + * 显示文字事件的文字,包括确认框等 + */ + text: string; + + /** + * 确认框中确定时执行的事件 + */ + yes?: MotaEvent; + + /** + * 确认框中取消时执行的事件 + */ + no?: MotaEvent; + + /** + * 当前是选择事件时所有的选项 + */ + choices?: string[]; + + /** + * 当前是选择事件时选项框的宽度 + */ + width?: number; +} + +interface ViewMapStatusData { + /** + * 当前浏览的楼层索引 + */ + index: number; + + /** + * 是否显示伤害 + */ + damage: boolean; + + /** + * 大地图是否显示全部地图 + */ + all: boolean; + + /** + * 大地图不显示全部地图时当前的横坐标,单位格子 + */ + x: number; + + /** + * 大地图不显示全部地图时当前的纵坐标,单位格子 + */ + y: number; +} + +interface EquipboxStatusData { + /** + * 拥有装备的当前页数 + */ + page: number; + + /** + * 当前选中的装备 + */ + selectId: ItemIdOf<'equips'>; +} + +interface ToolboxStatusData { + /** + * 消耗道具的当前页码数 + */ + toolsPage: number; + + /** + * 永久道具的当前页码数 + */ + constantsPage: number; + + /** + * 当前选中的道具id + */ + selectId: ItemIdOf<'constants' | 'tools'>; +} + +interface SaveStatusData { + /** + * 当前存读档界面的页码数 + */ + page: number; + + /** + * 选择的框的偏移量,在不同状态下意义不同 + */ + offset: number; + + /** + * 当前存读档界面的模式,fav表示收藏,all表示所有存档 + */ + mode: 'fav' | 'all'; +} + +interface TextStatusData { + /** + * 文本框要显示的文字列表 + */ + list: string[]; + + /** + * 文字显示完毕后的回调函数 + */ + callback: () => void; +} + +interface ConfirmStatusData { + /** + * 点击确认时 + */ + yes: () => void; + + /** + * 点击取消时 + */ + no: () => void; +} + +interface SwitchsStatusData { + /** + * 选择项 + */ + choices: string[]; +} + +interface SelectShopStatusUi { + /** + * 选择框的偏移量 + */ + offset: number; +} diff --git a/events.js b/events.js new file mode 100644 index 0000000..a78e0e5 --- /dev/null +++ b/events.js @@ -0,0 +1,147 @@ +var events_c12a15a8_c380_4b28_8144_256cba95f760 = +{ + "commonEvent": { + "加点事件": [ + { + "type": "comment", + "text": "通过传参,flag:arg1 表示当前应该的加点数值" + }, + { + "type": "choices", + "choices": [ + { + "text": "攻击+${1*flag:arg1}", + "action": [ + { + "type": "setValue", + "name": "status:atk", + "operator": "+=", + "value": "1*flag:arg1" + } + ] + }, + { + "text": "防御+${2*flag:arg1}", + "action": [ + { + "type": "setValue", + "name": "status:def", + "operator": "+=", + "value": "2*flag:arg1" + } + ] + }, + { + "text": "生命+${200*flag:arg1}", + "action": [ + { + "type": "setValue", + "name": "status:hp", + "operator": "+=", + "value": "200*flag:arg1" + } + ] + } + ] + } + ], + "回收钥匙商店": [ + { + "type": "comment", + "text": "此事件在全局商店中被引用了(全局商店keyShop)" + }, + { + "type": "comment", + "text": "解除引用前勿删除此事件" + }, + { + "type": "comment", + "text": "玩家在快捷列表(V键)中可以使用本公共事件" + }, + { + "type": "while", + "condition": "1", + "data": [ + { + "type": "choices", + "text": "\t[商人,trader]你有多余的钥匙想要出售吗?", + "choices": [ + { + "text": "黄钥匙(10金币)", + "color": [ + 255, + 255, + 0, + 1 + ], + "action": [ + { + "type": "if", + "condition": "item:yellowKey >= 1", + "true": [ + { + "type": "setValue", + "name": "item:yellowKey", + "operator": "-=", + "value": "1" + }, + { + "type": "setValue", + "name": "status:money", + "operator": "+=", + "value": "10" + } + ], + "false": [ + "\t[商人,trader]你没有黄钥匙!" + ] + } + ] + }, + { + "text": "蓝钥匙(50金币)", + "color": [ + 0, + 0, + 255, + 1 + ], + "action": [ + { + "type": "if", + "condition": "item:blueKey >= 1", + "true": [ + { + "type": "setValue", + "name": "item:blueKey", + "operator": "-=", + "value": "1" + }, + { + "type": "setValue", + "name": "status:money", + "operator": "+=", + "value": "50" + } + ], + "false": [ + "\t[商人,trader]你没有蓝钥匙!" + ] + } + ] + }, + { + "text": "离开", + "action": [ + { + "type": "exit" + } + ] + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/extensions.js b/extensions.js new file mode 100644 index 0000000..90e688a --- /dev/null +++ b/extensions.js @@ -0,0 +1,51 @@ + +/* +extensions.js:负责拓展插件 + */ + +"use strict"; + +function extensions () { + +} + +extensions.prototype._load = function (callback) { + if (main.replayChecking) return callback(); + if (!window.fs) { + this._loadJs('_server/fs.js', function () { + core.extensions._listExtensions(callback); + }, callback); + } else this._listExtensions(callback); +} + +extensions.prototype._loadJs = function (file, callback, onerror) { + var script = document.createElement('script'); + script.src = file + '?v=' + main.version; + script.onload = callback; + script.onerror = onerror; + main.dom.body.appendChild(script); +} + +extensions.prototype._listExtensions = function (callback) { + if (!window.fs) return callback(); + fs.readdir('extensions', function (error, data) { + if (error || !(data instanceof Array)) return callback(); + var list = []; + data.forEach(function (name) { + if (/^[\w.-]+\.js$/.test(name)) { + list.push(name); + } + }); + list.sort(); + core.extensions._loadExtensions(list, callback); + }); +} + +extensions.prototype._loadExtensions = function (list, callback) { + var i = 0; + var load = function () { + if (i == list.length) return callback(); + core.extensions._loadJs('extensions/' + list[i++], load, load); + } + load(); +} diff --git a/fs.js b/fs.js new file mode 100644 index 0000000..3e741cd --- /dev/null +++ b/fs.js @@ -0,0 +1,186 @@ +(function () { + window.fs = {}; + + + var _isset = function (val) { + if (val == undefined || val == null || (typeof val == 'number' && isNaN(val))) { + return false; + } + return true + } + + var _http = function (type, url, formData, success, error, mimeType, responseType) { + var xhr = new XMLHttpRequest(); + xhr.open(type, url, true); + if (_isset(mimeType)) + xhr.overrideMimeType(mimeType); + if (_isset(responseType)) + xhr.responseType = responseType; + xhr.onload = function (e) { + if (xhr.status == 200) { + if (_isset(success)) { + success(xhr.response); + } + } + else { + if (_isset(error)) + error("HTTP " + xhr.status); + } + }; + xhr.onabort = function () { + if (_isset(error)) + error("Abort"); + } + xhr.ontimeout = function () { + if (_isset(error)) + error("Timeout"); + } + xhr.onerror = function () { + if (_isset(error)) + error("Error on Connection"); + } + if (_isset(formData)) + xhr.send(formData); + else xhr.send(); + } + + + var postsomething = function (data, _ip, callback) { + if (typeof (data) == typeof ([][0]) || data == null) data = JSON.stringify({ 1: 2 }); + + _http("POST", _ip, data, function (data) { + if (data.slice(0, 6) == 'error:') { + callback(data, null); + } + else { + callback(null, data); + } + }, function (e) { + if (window.main != null) console.log(e); + else console.log(e); + callback(e + ":请检查启动服务是否处于正常运行状态。"); + }, "text/plain; charset=x-user-defined"); + } + + fs.readFile = function (filename, encoding, callback) { + if (typeof (filename) != typeof ('')) + throw 'Type Error in fs.readFile'; + if (encoding == 'utf-8') { + //读文本文件 + //filename:支持"/"做分隔符 + //callback:function(err, data) + //data:字符串 + var data = ''; + data += 'type=utf8&'; + data += 'name=' + filename; + postsomething(data, '/readFile', callback); + return; + } + if (encoding == 'base64') { + //读二进制文件 + //filename:支持"/"做分隔符 + //callback:function(err, data) + //data:base64字符串 + var data = ''; + data += 'type=base64&'; + data += 'name=' + filename; + postsomething(data, '/readFile', callback); + return; + } + throw 'Type Error in fs.readFile'; + } + + fs.writeFile = function (filename, datastr, encoding, callback) { + if (typeof (filename) != typeof ('') || typeof (datastr) != typeof ('')) + throw 'Type Error in fs.writeFile'; + if (encoding == 'utf-8') { + //写文本文件 + //filename:支持"/"做分隔符 + //callback:function(err) + //datastr:字符串 + var data = ''; + data += 'type=utf8&'; + data += 'name=' + filename; + data += '&value=' + datastr; + postsomething(data, '/writeFile', callback); + return; + } + if (encoding == 'base64') { + //写二进制文件 + //filename:支持"/"做分隔符 + //callback:function(err) + //datastr:base64字符串 + var data = ''; + data += 'type=base64&'; + data += 'name=' + filename; + data += '&value=' + datastr; + postsomething(data, '/writeFile', callback); + return; + } + throw 'Type Error in fs.writeFile'; + } + + fs.writeMultiFiles = function (filenames, datastrs, callback) { + postsomething('name=' + filenames.join(';') + '&value=' + datastrs.join(';'), '/writeMultiFiles', callback); + } + + fs.readdir = function (path, callback) { + //callback:function(err, data) + //path:支持"/"做分隔符,不以"/"结尾 + //data:[filename1,filename2,..] filename是字符串,只包含文件不包含目录 + if (typeof (path) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'name=' + path; + postsomething(data, '/listFile', function (err, data) { + try { + data = JSON.parse(data); + } catch (e) { + err = "Invalid /listFile"; + data = null; + } + callback(err, data); + }); + return; + } + + /** + * @param {string} path 支持"/"做分隔符 + * @param {() => {err: string, data}} callback + */ + fs.mkdir = function (path, callback) { + //callback:function(err, data) + if (typeof (path) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'name=' + path; + postsomething(data, '/makeDir', callback); + return; + } + + /** + * @param {string} path 支持"/"做分隔符, 不以"/"结尾 + * @param {() => {err: string, data}} callback + */ + fs.moveFile = function (src, dest, callback) { + if (typeof (src) != typeof ('') || typeof (dest) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'src=' + src + "&dest=" + dest; + postsomething(data, '/moveFile', callback); + return; + } + + /** + * @param {string} path 支持"/"做分隔符, 不以"/"结尾 + * @param {() => {err: string, data}} callback + */ + fs.deleteFile = function (path, callback) { + if (typeof (path) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'name=' + path; + postsomething(data, '/deleteFile', callback); + return; + } +})(); \ No newline at end of file diff --git a/fsTest_cs.html b/fsTest_cs.html new file mode 100644 index 0000000..acfcd52 --- /dev/null +++ b/fsTest_cs.html @@ -0,0 +1,70 @@ + + + + + + + + + + \ No newline at end of file diff --git a/function.d.ts b/function.d.ts new file mode 100644 index 0000000..95c2c9f --- /dev/null +++ b/function.d.ts @@ -0,0 +1,162 @@ +interface ActionData { + /** + * @deprecated + * 当按键弹起时 + * @param keyCode 按键的keyCode + * @param altKey 当前是否按下了alt键 + */ + onKeyUp(keyCode: number, altKey: boolean): boolean; +} + +interface ControlData { + /** + * 获取保存信息 + */ + saveData(): Save; + + /** + * 读取一个存档 + * @param data 存档信息 + * @param callback 读取完毕后的回调函数 + */ + loadData(data: Save, callback?: () => void): void; + + /** + * 立即仅更新状态栏 + */ + updateStatusBar(): void; + + /** + * 每步移动后执行的函数 + * @param callback 回调函数(好像没什么必要吧 + */ + moveOneStep(callback?: () => void): void; + + /** + * 瞬移到某一点 + * @param x 瞬移至的横坐标 + * @param y 瞬移至的纵坐标 + * @param ignoreSteps 忽略的步数,不填则会自动计算 + */ + moveDirectly(x: number, y: number, ignoreSteps?: number): boolean; +} + +interface UiData { + /** + * 数据统计界面统计的道具数量 + */ + drawStatistics(): AllIdsOf<'items'>[]; +} + +interface EventData { + /** + * 重置游戏 + * @param hero 勇士信息 + * @param hard 难度信息 + * @param floorId 勇士所在楼层 + * @param maps 所有的地图信息 + * @param values 全局数值信息 + */ + resetGame( + hero: HeroStatus, + hard: string, + floorId: FloorIds, + maps: GameStatus['maps'], + values: Partial + ): void; + + /** + * 游戏获胜 + * @param reason 胜利原因 + * @param norank 是否不计榜 + * @param noexit 是否不退出 + */ + win(reason: string, norank?: boolean, noexit?: boolean): void; + + /** + * 游戏失败 + * @param reason 失败原因 + */ + lose(reason?: string): void; + + /** + * 切换楼层中,即屏幕完全变黑的那一刻 + * @param floorId 目标楼层 + * @param heroLoc 勇士到达的位置 + */ + changingFloor(floorId: FloorIds, heroLoc: Loc): void; + + /** + * 切换楼层后 + * @param floorId 目标楼层 + */ + afterChangeFloor(floorId: FloorIds): void; + + /** + * 飞往某个楼层 + * @param toId 目标楼层 + * @param callback 飞到后的回调函数 + */ + flyTo(toId: FloorIds, callback?: () => void): boolean; + + /** + * 与怪物战斗后 + * @param enemyId 打败的怪物 + * @param x 怪物横坐标 + * @param y 怪物纵坐标 + */ + afterBattle(enemyId: any, x?: number, y?: number): void; + + /** + * 开门后 + * @param doorId 门的id + * @param x 门的横坐标 + * @param y 门的纵坐标 + */ + afterOpenDoor( + doorId: AllIdsOf>, + x: number, + y: number + ): void; + + /** + * 获得道具后 + * @param itemId 道具id + * @param x 道具横坐标 + * @param y 道具纵坐标 + * @param isGentleClick 是否是轻按 + */ + afterGetItem( + itemId: AllIdsOf<'items'>, + x: number, + y: number, + isGentleClick?: boolean + ): void; + + /** + * 推箱子后 + */ + afterPushBox(): void; +} + +interface FunctionsData { + /** + * 交互信息 + */ + actions: ActionData; + + /** + * 游戏的逻辑信息 + */ + control: ControlData; + + /** + * ui信息 + */ + ui: UiData; + + /** + * 事件信息 + */ + events: EventData; +} diff --git a/functions.js b/functions.js new file mode 100644 index 0000000..5d83786 --- /dev/null +++ b/functions.js @@ -0,0 +1,2165 @@ +var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = +{ + "events": { + "resetGame": function (hero, hard, floorId, maps, values) { + // 重置整个游戏;此函数将在游戏开始时,或者每次读档时最先被调用 + // hero:勇士信息;hard:难度;floorId:当前楼层ID;maps:地图信息;values:全局数值信息 + + // 清除游戏数据 + // 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布 + core.clearStatus(); + // 初始化status + core.status = core.clone(core.initStatus, function (name) { + return name != 'hero' && name != 'maps'; + }); + core.control._bindRoutePush(); + core.status.played = true; + // 初始化人物,图标,统计信息 + core.status.hero = core.clone(hero); + window.hero = core.status.hero; + window.flags = core.status.hero.flags; + core.events.setHeroIcon(core.status.hero.image, true); + core.control._initStatistics(core.animateFrame.totalTime); + core.status.hero.statistics.totalTime = core.animateFrame.totalTime = + Math.max(core.status.hero.statistics.totalTime, core.animateFrame.totalTime); + core.status.hero.statistics.start = null; + // 初始难度 + core.status.hard = hard || ""; + // 初始化地图 + core.status.floorId = floorId; + core.status.maps = maps; + core.maps._resetFloorImages(); + // 初始化怪物和道具 + core.material.enemys = core.enemys.getEnemys(); + core.material.items = core.items.getItems(); + // 初始化全局数值和全局开关 + core.values = core.clone(core.data.values); + for (var key in values || {}) + core.values[key] = values[key]; + core.flags = core.clone(core.data.flags); + var globalFlags = core.getFlag("globalFlags", {}); + for (var key in globalFlags) + core.flags[key] = globalFlags[key]; + core._init_sys_flags(); + // 初始化界面,状态栏等 + core.resize(); + // 状态栏是否显示 + if (core.hasFlag('hideStatusBar')) + core.hideStatusBar(core.hasFlag('showToolbox')); + else + core.showStatusBar(); + // 隐藏右下角的音乐按钮 + core.dom.musicBtn.style.display = 'none'; + + if (document.getElementById("title")) document.getElementById("title").style.display = 'none'; + + + + + }, + "win": function (reason, norank, noexit) { + // 游戏获胜事件 + // 请注意,成绩统计时是按照hp进行上传并排名 + // 可以先在这里对最终分数进行计算,比如将2倍攻击和5倍黄钥匙数量加到分数上 + // core.status.hero.hp += 2 * core.getRealStatus('atk') + 5 * core.itemCount('yellowKey'); + + // 如果不退出,则临时存储数据 + if (noexit) { + core.status.extraEvent = core.clone(core.status.event); + } + + // 游戏获胜事件 + core.ui.closePanel(); + var replaying = core.isReplaying(); + if (replaying) core.stopReplay(); + core.waitHeroToStop(function () { + if (!noexit) { + core.clearMap('all'); // 清空全地图 + core.deleteAllCanvas(); // 删除所有创建的画布 + core.dom.gif2.innerHTML = ""; + } + reason = core.replaceText(reason); + core.drawText([ + "\t[" + (reason || "恭喜通关") + "]你的分数是${status:hp}。" + ], function () { + core.events.gameOver(reason || '', replaying, norank); + }) + }); + }, + "lose": function (reason) { + // 游戏失败事件 + core.ui.closePanel(); + var replaying = core.isReplaying(); + core.stopReplay(); + core.waitHeroToStop(function () { + core.drawText([ + "\t[" + (reason || "结局1") + "]你死了。\n如题。" + ], function () { + core.events.gameOver(null, replaying); + }); + }) + }, + "changingFloor": function (floorId, heroLoc) { + // 正在切换楼层过程中执行的操作;此函数的执行时间是“屏幕完全变黑“的那一刻 + // floorId为要切换到的楼层ID;heroLoc表示勇士切换到的位置 + + // ---------- 此时还没有进行切换,当前floorId还是原来的 ---------- // + var currentId = core.status.floorId || null; // 获得当前的floorId,可能为null + var fromLoad = core.hasFlag('__fromLoad__'); // 是否是读档造成的切换 + var isFlying = core.hasFlag('__isFlying__'); // 是否是楼传造成的切换 + if (!fromLoad && !(isFlying && currentId == floorId)) { + if (!core.hasFlag("__leaveLoc__")) core.setFlag("__leaveLoc__", {}); + if (currentId != null) core.getFlag("__leaveLoc__")[currentId] = core.clone(core.status.hero.loc); + } + + // 可以对currentId进行判定,比如删除某些自定义图层等 + // if (currentId == 'MT0') { + // core.deleteAllCanvas(); + // } + + // 根据分区信息自动砍层与恢复 + if (core.autoRemoveMaps) core.autoRemoveMaps(floorId); + + // 重置画布尺寸 + core.maps.resizeMap(floorId); + // 设置勇士的位置 + heroLoc.direction = core.turnDirection(heroLoc.direction); + core.status.hero.loc = heroLoc; + // 检查重生怪并重置 + if (!fromLoad) { + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + if (block.disable && core.enemys.hasSpecial(block.event.id, 23)) { + block.disable = false; + core.setMapBlockDisabled(floorId, block.x, block.y, false); + core.maps._updateMapArray(floorId, block.x, block.y); + } + }); + core.control.gatherFollowers(); + } + + // ---------- 重绘新地图;这一步将会设置core.status.floorId ---------- // + core.drawMap(floorId); + + // 切换楼层BGM + if (core.status.maps[floorId].bgm) { + var bgm = core.status.maps[floorId].bgm; + if (bgm instanceof Array) bgm = bgm[Math.floor(Math.random() * bgm.length)]; // 多个bgm则随机播放一个 + if (!core.hasFlag("__bgm__")) core.playBgm(bgm); + } else if (fromLoad && !core.hasFlag("__bgm__")) { + core.pauseBgm(); + } + // 更改画面色调 + var color = core.getFlag('__color__', null); + if (!color && core.status.maps[floorId].color) + color = core.status.maps[floorId].color; + core.clearMap('curtain'); + core.status.curtainColor = color; + if (color) core.fillRect('curtain', 0, 0, core._PX_ || core.__PIXELS__, core._PY_ || core.__PIXELS__, core.arrayToRGBA(color)); + // 更改天气 + var weather = core.getFlag('__weather__', null); + if (!weather && core.status.maps[floorId].weather) + weather = core.status.maps[floorId].weather; + if (weather) + core.setWeather(weather[0], weather[1]); + else core.setWeather(); + // ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等 + core.eventMoveHero(['forward'], 300); + + + }, + "afterChangeFloor": function (floorId) { + // 转换楼层结束的事件;此函数会在整个楼层切换完全结束后再执行 + // floorId是切换到的楼层 + + + // 如果是读档,则进行检查(是否需要恢复事件) + if (core.hasFlag('__fromLoad__')) { + core.events.recoverEvents(core.getFlag("__events__")); + core.removeFlag("__events__"); + + for (var e in flags.enemylist) { + core.plugin.enemystate(e); + } + for (var c in flags.crops) { + core.plugin.plantgrow(c); + } + core.plugin.ronglujiance(); + core.plugin.siwangjiance(); + } else { + // 每次抵达楼层执行的事件 + core.insertAction(core.floors[floorId].eachArrive); + core.insertAction(core.floors[floorId].firstArrive); + // 首次抵达楼层时执行的事件(后插入,先执行) + if (!core.hasVisitedFloor(floorId)) { + core.insertAction(core.floors[floorId].firstArrive); + core.visitFloor(floorId); + if (floorId.slice(0, 3) == "zhu") { + if (floorId in flags.mapicon["zhu"]) { } + else { + flags.mapicon["zhu"][floorId] = 0; + } + } else if (floorId.slice(0, 3) == "don") { + if (floorId in flags.mapicon["dong"]) { } + else { + flags.mapicon["dong"][floorId] = 0; + } + } + } + } + + // 背景设置 + if (flags.dimension[0] == "主世界" && flags.dimension[1] == "地表" && + core.status.globalAttribute.statusLeftBackground != 'url("project/materials/wood1.png") repeat') { + core.dom.statusBar.style.background = 'url("project/materials/wood1.png") repeat'; + core.status.globalAttribute.statusLeftBackground = 'url("project/materials/wood1.png") repeat'; + extend.canvas.style.background = 'url("project/materials/wood1.png") repeat'; + //core.plugin.drawLight('fg1', 0, [[208, 208, 200]]); + } else if (flags.dimension[0] == "主世界" && flags.dimension[1] == "洞穴" && + core.status.globalAttribute.statusLeftBackground != 'url("project/materials/stone1.png") repeat') { + core.dom.statusBar.style.background = 'url("project/materials/stone1.png") repeat'; + core.status.globalAttribute.statusLeftBackground = 'url("project/materials/stone1.png") repeat'; + extend.canvas.style.background = 'url("project/materials/stone1.png") repeat'; + //core.plugin.drawLight('fg1', 1, [[208, 208, 200]]); + } + + //core.eventMoveHero(['forward'], 300); + + + + }, + "flyTo": function (toId, callback) { + // 楼层传送器的使用,从当前楼层飞往toId + // 如果不能飞行请返回false + + var fromId = core.status.floorId; + + // 检查能否飞行 + if (!core.status.maps[fromId].canFlyFrom || !core.status.maps[toId].canFlyTo || !core.hasVisitedFloor(toId)) { + core.playSound('操作失败'); + core.drawTip("无法飞往" + core.status.maps[toId].title + "!", 'fly'); + return false; + } + + // 平面塔模式 + var stair = null, + loc = null; + if (core.flags.flyRecordPosition) { + loc = core.getFlag("__leaveLoc__", {})[toId] || null; + } + if (core.status.maps[toId].flyPoint != null && core.status.maps[toId].flyPoint.length == 2) { + stair = 'flyPoint'; + } + if (stair == null && loc == null) { + // 获得两个楼层的索引,以决定是上楼梯还是下楼梯 + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + var stair = fromIndex <= toIndex ? "downFloor" : "upFloor"; + // 地下层:同层传送至上楼梯 + if (fromIndex == toIndex && core.status.maps[fromId].underGround) stair = "upFloor"; + } + + // 记录录像 + core.status.route.push("fly:" + toId); + // 传送 + core.ui.closePanel(); + core.setFlag('__isFlying__', true); + core.changeFloor(toId, stair, loc, null, function () { + core.removeFlag("__isFlying__"); + if (callback) callback(); + }); + + return true; + }, + "beforeBattle": function (enemyId, x, y) { + // 战斗前触发的事件,可以加上一些战前特效(详见下面支援的例子) + // 此函数在“检测能否战斗和自动存档”【之后】执行。如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 + // 返回true则将继续战斗,返回false将不再战斗。 + // + // ------ 支援技能 ------ // + if (x != null && y != null) { + var index = x + "," + y, + cache = core.status.checkBlock.cache[index] || {}, + guards = cache.guards || []; + // 如果存在支援怪 + if (guards.length > 0) { + // 记录flag,当前要参与支援的怪物 + core.setFlag("__guards__" + x + "_" + y, guards); + var actions = [{ "type": "playSound", "name": "跳跃" }]; + // 增加支援的特效动画(图块跳跃) + guards.forEach(function (g) { + core.push(actions, { "type": "jump", "from": [g[0], g[1]], "to": [x, y], "time": 300, "keep": false, "async": true }); + }); + core.push(actions, [ + { "type": "waitAsync" }, // 等待所有异步事件执行完毕 + { + "type": "setBlock", + "number": enemyId, + "loc": [ + [x, y] + ] + }, // 重新设置怪物自身 + { "type": "battle", "loc": [x, y] } // 重要!重新触发本次战斗 + ]); + core.insertAction(actions); + return false; + } + } + + return true; + }, + "afterBattle": function (enemyId, x, y) { + // 战斗结束后触发的事件 + + var enemy = core.material.enemys[enemyId]; + var special = enemy.special; + + // 播放战斗音效和动画 + // 默认播放的动画;你也可以使用 + var animate = 'hand'; // 默认动画 + // 检查当前装备是否存在攻击动画 + var equipId = core.getEquip(0); + if (equipId && (core.material.items[equipId].equip || {}).animate) + animate = core.material.items[equipId].equip.animate; + // 你也可以在这里根据自己的需要,比如enemyId或special或flag来修改播放的动画效果 + // if (enemyId == '...') animate = '...'; + + // 检查该动画是否存在SE,如果不存在则使用默认音效 + if (!(core.material.animates[animate] || {}).se) + core.playSound('attack.mp3'); + + // 播放动画;如果不存在坐标(强制战斗)则播放到勇士自身 + if (x != null && y != null) + core.drawAnimate(animate, x, y); + else + core.drawHeroAnimate(animate); + + // 获得战斗伤害信息 + var damageInfo = core.getDamageInfo(enemyId, null, x, y) || {}; + // 战斗伤害 + var damage = damageInfo.damage; + // 当前战斗回合数,可用于战后所需的判定 + var turn = damageInfo.turn; + // 判定是否致死 + if (damage == null || damage >= core.status.hero.hp) { + core.status.hero.hp = 0; + core.updateStatusBar(false, true); + core.events.lose('战斗失败'); + return; + } + + // 扣减体力值并记录统计数据 + core.status.hero.hp -= damage; + core.status.hero.statistics.battleDamage += damage; + core.status.hero.statistics.battle++; + + // 计算当前怪物的支援怪物 + var guards = []; + if (x != null && y != null) { + guards = core.getFlag("__guards__" + x + "_" + y, []); + core.removeFlag("__guards__" + x + "_" + y); + } + + // 获得金币 + var money = guards.reduce(function (curr, g) { + return curr + core.material.enemys[g[2]].money; + }, core.getEnemyValue(enemy, "money", x, y)); + if (core.hasItem('coin')) money *= 2; // 幸运金币:双倍 + if (core.hasFlag('curse')) money = 0; // 诅咒效果 + core.status.hero.money += money; + core.status.hero.statistics.money += money; + + // 获得经验 + var exp = guards.reduce(function (curr, g) { + return curr + core.material.enemys[g[2]].exp; + }, core.getEnemyValue(enemy, "exp", x, y)); + if (core.hasFlag('curse')) exp = 0; + core.status.hero.exp += exp; + core.status.hero.statistics.exp += exp; + + var hint = "打败 " + core.getEnemyValue(enemy, "name", x, y); + if (core.flags.statusBarItems.indexOf('enableMoney') >= 0) + hint += ',' + core.getStatusLabel('money') + '+' + money; // hint += ",金币+" + money; + if (core.flags.statusBarItems.indexOf('enableExp') >= 0) + hint += ',' + core.getStatusLabel('exp') + '+' + exp; // hint += ",经验+" + exp; + core.drawTip(hint, enemy.id); + + // 中毒 + if (core.enemys.hasSpecial(special, 12)) { + core.triggerDebuff('get', 'poison'); + } + // 衰弱 + if (core.enemys.hasSpecial(special, 13)) { + core.triggerDebuff('get', 'weak'); + } + // 诅咒 + if (core.enemys.hasSpecial(special, 14)) { + core.triggerDebuff('get', 'curse'); + } + // 仇恨怪物将仇恨值减半 + if (core.enemys.hasSpecial(special, 17)) { + core.setFlag('hatred', Math.floor(core.getFlag('hatred', 0) / 2)); + } + // 自爆 + if (core.enemys.hasSpecial(special, 19)) { + core.status.hero.statistics.battleDamage += core.status.hero.hp - 1; + core.status.hero.hp = 1; + } + // 退化 + if (core.enemys.hasSpecial(special, 21)) { + core.status.hero.atk -= (enemy.atkValue || 0); + core.status.hero.def -= (enemy.defValue || 0); + if (core.status.hero.atk < 0) core.status.hero.atk = 0; + if (core.status.hero.def < 0) core.status.hero.def = 0; + } + // 增加仇恨值 + core.setFlag('hatred', core.getFlag('hatred', 0) + core.values.hatred); + + // 战后的技能处理,比如扣除魔力值 + if (core.flags.statusBarItems.indexOf('enableSkill') >= 0) { + // 检测当前开启的技能类型 + var skill = core.getFlag('skill', 0); + if (skill == 1) { // 技能1:二倍斩 + core.status.hero.mana -= 5; // 扣除5点魔力值 + } + // 关闭技能 + core.setFlag('skill', 0); + core.setFlag('skillName', '无'); + } + + + // 事件的处理 + var todo = []; + + // 加点事件 + var point = guards.reduce(function (curr, g) { + return curr + core.material.enemys[g[2]].point; + }, core.getEnemyValue(enemy, "point", x, y)) || 0; + if (core.flags.enableAddPoint && point > 0) { + core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]); + } + + // 战后事件 + if (core.status.floorId != null) { + core.push(todo, core.floors[core.status.floorId].afterBattle[x + "," + y]); + } + core.push(todo, enemy.afterBattle); + + // 在这里增加其他的自定义事件需求 + /* + if (enemyId=='xxx') { + core.push(todo, [ + {"type": "...", ...}, + ]); + } + */ + + // 如果事件不为空,将其插入 + if (todo.length > 0) core.insertAction(todo, x, y); + + // 删除该点设置的怪物信息 + delete ((flags.enemyOnPoint || {})[core.status.floorId] || {})[x + "," + y]; + + // 因为removeBlock和hideBlock都会刷新状态栏,因此将删除部分移动到这里并保证刷新只执行一次,以提升效率 + if (core.getBlock(x, y) != null) { + // 检查是否是重生怪物;如果是则仅隐藏不删除 + if (core.hasSpecial(enemy.special, 23)) { + core.hideBlock(x, y); + } else { + core.removeBlock(x, y); + } + } else { + core.updateStatusBar(); + } + + // 如果已有事件正在处理中 + if (core.status.event.id == null) + core.continueAutomaticRoute(); + else + core.clearContinueAutomaticRoute(); + + }, + "afterOpenDoor": function (doorId, x, y) { + // 开一个门后触发的事件 + + var todo = []; + // 检查该点的开门后事件 + if (core.status.floorId) { + core.push(todo, core.floors[core.status.floorId].afterOpenDoor[x + "," + y]); + } + // 检查批量开门事件 + var door = core.getBlockById(doorId); + if (door && door.event.doorInfo) { + core.push(todo, door.event.doorInfo.afterOpenDoor); + } + + if (todo.length > 0) core.insertAction(todo, x, y); + + if (core.status.event.id == null) + core.continueAutomaticRoute(); + else + core.clearContinueAutomaticRoute(); + }, + "afterGetItem": function (itemId, x, y, isGentleClick) { + // 获得一个道具后触发的事件 + // itemId:获得的道具ID;x和y是该道具所在的坐标 + // isGentleClick:是否是轻按触发的 + if (itemId.endsWith('Potion') && core.material.items[itemId].cls == 'items') + core.playSound('回血'); + else if (itemId.endsWith('Gem') && core.material.items[itemId].cls == 'items') + core.playSound('宝石') + else + core.playSound('获得道具'); + + var todo = []; + // 检查该点的获得道具后事件。 + if (core.status.floorId == null) return; + var event = core.floors[core.status.floorId].afterGetItem[x + "," + y]; + if (event && (event instanceof Array || !isGentleClick || !event.disableOnGentleClick)) { + if (event.data) event = event.data; + core.unshift(todo, event); + } + + if (todo.length > 0) core.insertAction(todo, x, y); + + core.plugin.mcjiemian(); + core.plugin.beibaocunchu(); + }, + "afterPushBox": function () { + // 推箱子后的事件 + if (core.searchBlock('box').length == 0) { + // 可以通过if语句来进行开门操作 + /* + if (core.status.floorId=='xxx') { // 在某个楼层 + core.insertAction([ // 插入一条事件 + {"type": "openDoor", "loc": [x,y]} // 开门 + ]) + } + */ + } + } + }, + "enemys": { + "getSpecials": function () { + // 获得怪物的特殊属性,每一行定义一个特殊属性。 + // 分为五项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 + // 第四项为该特殊属性的颜色,可以写十六进制 #RRGGBB 或者 [r,g,b,a] 四元数组 + // 第五项为该特殊属性的标记;目前 1 代表是地图类技能(需要进行遍历全图) + // 名字和描述可以直接写字符串,也可以写个function将怪物传进去 + return [ + [1, "先攻", "怪物首先攻击", "#ffcc33"], + [2, "魔攻", "怪物无视角色的防御", "#bbb0ff"], + [3, "坚固", "怪物防御不小于角色攻击-1", "#c0b088"], + [4, "2连击", "怪物每回合攻击2次", "#ffee77"], + [5, "3连击", "怪物每回合攻击3次", "#ffee77"], + [6, function (enemy) { return (enemy.n || '') + "连击"; }, function (enemy) { return "怪物每回合攻击" + (enemy.n || 4) + "次"; }, "#ffee77"], + [7, "破甲", function (enemy) { return "战斗前,怪物附加角色防御的" + Math.floor(100 * (enemy.breakArmor || core.values.breakArmor || 0)) + "%作为伤害"; }, "#88c0ff"], + [8, "反击", function (enemy) { return "战斗时,怪物每回合附加角色攻击的" + Math.floor(100 * (enemy.counterAttack || core.values.counterAttack || 0)) + "%作为伤害,无视角色防御"; }, "#ffaa44"], + [9, "净化", function (enemy) { return "战斗前,怪物附加角色护盾的" + (enemy.purify || core.values.purify) + "倍作为伤害"; }, "#80eed6"], + [10, "模仿", "怪物的攻防和角色攻防相等", "#b0c0dd"], + [11, "吸血", function (enemy) { return "战斗前,怪物首先吸取角色的" + Math.floor(100 * enemy.vampire || 0) + "%生命(约" + Math.floor((enemy.vampire || 0) * core.getStatus('hp')) + "点)作为伤害" + (enemy.add ? ",并把伤害数值加到自身生命上" : ""); }, "#dd4448"], + [12, "中毒", "战斗后,角色陷入中毒状态,每一步损失生命" + core.values.poisonDamage + "点", "#99ee88"], + [13, "衰弱", "战斗后,角色陷入衰弱状态,攻防暂时下降" + (core.values.weakValue >= 1 ? core.values.weakValue + "点" : parseInt(core.values.weakValue * 100) + "%"), "#f0bbcc"], + [14, "诅咒", "战斗后,角色陷入诅咒状态,战斗无法获得金币和经验", "#bbeef0"], + [15, "领域", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "范围内" + (enemy.range || 1) + "格时自动减生命" + (enemy.zone || 0) + "点"; }, "#c677dd"], + [16, "夹击", "经过两只相同的怪物中间,角色生命值变成一半", "#bb99ee"], + [17, "仇恨", "战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得" + (core.values.hatred || 0) + "点仇恨值)", "#b0b666"], + [18, "阻击", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "时自动减生命" + (enemy.repulse || 0) + "点,同时怪物后退一格"; }, "#8888e6"], + [19, "自爆", "战斗后角色的生命值变成1", "#ff6666"], + [20, "无敌", "角色无法打败怪物,除非拥有十字架", "#aaaaaa"], + [21, "退化", function (enemy) { return "战斗后角色永久下降" + (enemy.atkValue || 0) + "点攻击和" + (enemy.defValue || 0) + "点防御"; }], + [22, "固伤", function (enemy) { return "战斗前,怪物对角色造成" + (enemy.damage || 0) + "点固定伤害,未开启负伤时无视角色护盾。"; }, "#ff9977"], + [23, "重生", "怪物被击败后,角色转换楼层则怪物将再次出现", "#a0e0ff"], + [24, "激光", function (enemy) { return "经过怪物同行或同列时自动减生命" + (enemy.laser || 0) + "点"; }, "#dda0dd"], + [25, "光环", function (enemy) { return (enemy.range != null ? ((enemy.haloSquare ? "该怪物九宫格" : "该怪物十字") + enemy.haloRange + "格范围内") : "同楼层所有") + "怪物生命提升" + (enemy.hpBuff || 0) + "%,攻击提升" + (enemy.atkBuff || 0) + "%,防御提升" + (enemy.defBuff || 0) + "%," + (enemy.haloAdd ? "可叠加" : "不可叠加"); }, "#e6e099", 1], + [26, "支援", "当周围一圈的怪物受到攻击时将上前支援,并组成小队战斗。", "#77c0b6", 1], + [27, "捕捉", function (enemy) { return "当走到怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "时会强制进行战斗。"; }, "#c0ddbb"] + ]; + }, + "getEnemyInfo": function (enemy, hero, x, y, floorId) { + // 获得某个怪物变化后的数据;该函数将被伤害计算和怪物手册使用 + // 例如:坚固、模仿、仿攻等等 + // + // 参数说明: + // enemy:该怪物信息 + // hero_hp,hero_atk,hero_def,hero_mdef:勇士的生命攻防护盾数据 + // 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'); + + var mon_hp = core.getEnemyValue(enemy, 'hp', x, y, floorId), + mon_atk = core.getEnemyValue(enemy, 'atk', x, y, floorId), + mon_def = core.getEnemyValue(enemy, 'def', x, y, floorId), + mon_special = core.getEnemyValue(enemy, 'special', x, y, floorId); + var mon_money = core.getEnemyValue(enemy, 'money', x, y, floorId), + mon_exp = core.getEnemyValue(enemy, 'exp', x, y, floorId), + mon_point = core.getEnemyValue(enemy, 'point', x, y, floorId); + // 模仿 + if (core.hasSpecial(mon_special, 10)) { + mon_atk = hero_atk; + mon_def = hero_def; + } + // 坚固 + if (core.hasSpecial(mon_special, 3) && mon_def < hero_atk - 1) { + mon_def = hero_atk - 1; + } + + var guards = []; + + // 光环和支援检查 + if (!core.status.checkBlock) core.status.checkBlock = {}; + + if (core.status.checkBlock.needCache) { + // 从V2.5.4开始,对光环效果增加缓存,以解决多次重复计算的问题,从而大幅提升运行效率。 + var hp_buff = 0, + atk_buff = 0, + def_buff = 0; + // 已经计算过的光环怪ID列表,用于判定叠加 + var usedEnemyIds = {}; + // 检查光环和支援的缓存 + var index = x != null && y != null ? (x + "," + y) : floorId; + if (!core.status.checkBlock.cache) core.status.checkBlock.cache = {}; + var cache = core.status.checkBlock.cache[index]; + if (!cache) { + // 没有该点的缓存,则遍历每个图块 + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + if (!block.disable) { + // 获得该图块的ID + var id = block.event.id, + enemy = core.material.enemys[id]; + // 检查【光环】技能,数字25 + if (enemy && core.hasSpecial(enemy.special, 25)) { + // 检查是否是范围光环 + var inRange = enemy.haloRange == null; + if (enemy.haloRange != null && x != null && y != null) { + var dx = Math.abs(block.x - x), + dy = Math.abs(block.y - y); + // 检查十字和九宫格光环 + if (dx + dy <= enemy.haloRange) inRange = true; + if (enemy.haloSquare && dx <= enemy.haloRange && dy <= enemy.haloRange) inRange = true; + } + // 检查是否可叠加 + if (inRange && (enemy.haloAdd || !usedEnemyIds[enemy.id])) { + hp_buff += enemy.hpBuff || 0; + atk_buff += enemy.atkBuff || 0; + def_buff += enemy.defBuff || 0; + usedEnemyIds[enemy.id] = true; + } + } + // 检查【支援】技能,数字26 + if (enemy && core.hasSpecial(enemy.special, 26) && + // 检查支援条件,坐标存在,距离为1,且不能是自己 + // 其他类型的支援怪,比如十字之类的话.... 看着做是一样的 + x != null && y != null && Math.abs(block.x - x) <= 1 && Math.abs(block.y - y) <= 1 && !(x == block.x && y == block.y)) { + // 记录怪物的x,y,ID + guards.push([block.x, block.y, id]); + } + + // 如果有其他类型光环怪物在这里仿照添加检查 + // 注:新增新的类光环属性(需要遍历全图的)需要在特殊属性定义那里的第五项写1,参见光环和支援的特殊属性定义。 + } + }); + + core.status.checkBlock.cache[index] = { "hp_buff": hp_buff, "atk_buff": atk_buff, "def_buff": def_buff, "guards": guards }; + } else { + // 直接使用缓存数据 + hp_buff = cache.hp_buff; + atk_buff = cache.atk_buff; + def_buff = cache.def_buff; + guards = cache.guards; + } + + // 增加比例;如果要增加数值可以直接在这里修改 + mon_hp *= (1 + hp_buff / 100); + mon_atk *= (1 + atk_buff / 100); + mon_def *= (1 + def_buff / 100); + } + + // 可以在这里新增其他的怪物数据变化 + // 比如仿攻(怪物攻击不低于勇士攻击): + // if (core.hasSpecial(mon_special, 27) && mon_atk < hero_atk) { + // mon_atk = hero_atk; + // } + // 也可以按需增加各种自定义内容 + + return { + "hp": Math.floor(mon_hp), + "atk": Math.floor(mon_atk), + "def": Math.floor(mon_def), + "money": Math.floor(mon_money), + "exp": Math.floor(mon_exp), + "point": Math.floor(mon_point), + "special": mon_special, + "guards": guards, // 返回支援情况 + }; + }, + "getDamageInfo": function (enemy, hero, x, y, 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'), + origin_hero_hp = core.getStatusOrDefault(hero, 'hp'), + origin_hero_atk = core.getStatusOrDefault(hero, 'atk'), + origin_hero_def = core.getStatusOrDefault(hero, 'def'); + + // 勇士的负属性都按0计算 + hero_hp = Math.max(0, hero_hp); + hero_atk = Math.max(0, hero_atk); + hero_def = Math.max(0, hero_def); + hero_mdef = Math.max(0, hero_mdef); + + // 怪物的各项数据 + // 对坚固模仿等处理扔到了脚本编辑-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_special = enemyInfo.special; + + // 技能的处理 + if (core.getFlag('skill', 0) == 1) { // 开启了技能1:二倍斩 + hero_atk *= 2; // 计算时攻击力翻倍 + } + + // 如果是无敌属性,且勇士未持有十字架 + if (core.hasSpecial(mon_special, 20) && !core.hasItem("cross")) + return null; // 不可战斗 + + // 战前造成的额外伤害(可被护盾抵消) + var init_damage = 0; + + // 吸血 + if (core.hasSpecial(mon_special, 11)) { + var vampire_damage = hero_hp * enemy.vampire; + + // 如果有神圣盾免疫吸血等可以在这里写 + // 也可以用hasItem和hasEquip来判定装备 + // if (core.hasFlag('shield5')) vampire_damage = 0; + + vampire_damage = Math.floor(vampire_damage) || 0; + // 加到自身 + if (enemy.add) // 如果加到自身 + mon_hp += vampire_damage; + + init_damage += vampire_damage; + } + + // 每回合怪物对勇士造成的战斗伤害 + var per_damage = mon_atk - hero_def; + // 魔攻:战斗伤害就是怪物攻击力 + if (core.hasSpecial(mon_special, 2)) per_damage = mon_atk; + // 战斗伤害不能为负值 + if (per_damage < 0) per_damage = 0; + + // 2连击 & 3连击 & N连击 + if (core.hasSpecial(mon_special, 4)) per_damage *= 2; + if (core.hasSpecial(mon_special, 5)) per_damage *= 3; + if (core.hasSpecial(mon_special, 6)) per_damage *= (enemy.n || 4); + + // 每回合的反击伤害;反击是按照勇士的攻击次数来计算回合 + var 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, 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); + + // 勇士每回合对怪物造成的伤害 + var hero_per_damage = Math.max(hero_atk - mon_def, 0); + + // 如果没有破防,则不可战斗 + if (hero_per_damage <= 0) return null; + + // 勇士的攻击回合数;为怪物生命除以每回合伤害向上取整 + var turn = Math.ceil(mon_hp / hero_per_damage); + + // ------ 支援 ----- // + // 这个递归最好想明白为什么,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) { + if (!guard_before_current_enemy) { // --- 先打当前怪物,记录当前回合数 + core.setFlag("__extraTurn__", turn); + } + // 获得那些怪物组成小队战斗 + for (var i = 0; i < guards.length; i++) { + var gx = guards[i][0], + gy = guards[i][1], + gid = guards[i][2]; + // 递归计算支援怪伤害信息,这里不传x,y保证不会重复调用 + // 这里的mdef传0,因为护盾应该只会被计算一次 + var info = core.enemys.getDamageInfo(core.material.enemys[gid], { hp: origin_hero_hp, atk: origin_hero_atk, def: origin_hero_def, mdef: 0 }); + if (info == null) { // 小队中任何一个怪物不可战斗,直接返回null + core.removeFlag("__extraTurn__"); + return null; + } + // 已经进行的回合数 + core.setFlag("__extraTurn__", info.turn); + init_damage += info.damage; + } + if (guard_before_current_enemy) { // --- 先打支援怪物,增加当前回合数 + turn += core.getFlag("__extraTurn__", 0); + } + } + core.removeFlag("__extraTurn__"); + // ------ 支援END ------ // + + // 最终伤害:初始伤害 + 怪物对勇士造成的伤害 + 反击伤害 + var damage = init_damage + (turn - 1) * per_damage + turn * counterDamage; + // 再扣去护盾 + damage -= hero_mdef; + + // 检查是否允许负伤 + 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; + } + + return { + "mon_hp": Math.floor(mon_hp), + "mon_atk": Math.floor(mon_atk), + "mon_def": Math.floor(mon_def), + "init_damage": Math.floor(init_damage), + "per_damage": Math.floor(per_damage), + "hero_per_damage": Math.floor(hero_per_damage), + "turn": Math.floor(turn), + "damage": Math.floor(damage) + }; + } + }, + "actions": { + "onKeyUp": function (keyCode, altKey) { + /* + // 键盘按键处理,可以在这里自定义快捷键列表 + // keyCode:当前按键的keyCode(每个键的keyCode自行百度) + // altKey:Alt键是否被按下,为true代表同时按下了Alt键 + // 可以在这里任意增加或编辑每个按键的行为 + + // 如果处于正在行走状态,则不处理 + if (core.isMoving()) return; + + // 商店长按时忽略 + if (core.status.onShopLongDown) return core.status.onShopLongDown = false; + + // Alt+0~9,快捷换上套装 + if (altKey && keyCode >= 48 && keyCode <= 57) { + core.items.quickLoadEquip(keyCode - 48); + return; + } + + // 根据keyCode值来执行对应操作 + switch (keyCode) { + case 27: // ESC:打开菜单栏 + core.openSettings(true); + break; + case 88: // X:使用怪物手册 + core.openBook(true); + break; + case 71: // G:使用楼传器 + core.useFly(true); + break; + case 65: // A:读取自动存档(回退) + core.doSL("autoSave", "load"); + break; + case 87: // W:撤销回退 + core.doSL("autoSave", "reload"); + break; + case 83: // S:存档 + core.save(true); + break; + case 68: // D:读档 + core.load(true); + break; + case 69: // E:打开光标 + core.ui._drawCursor(); + break; + case 84: // T:打开道具栏 + core.openToolbox(true); + break; + case 81: // Q:打开装备栏 + core.openEquipbox(true); + break; + case 90: // Z:转向 + core.turnHero(); + break; + case 86: // V:打开快捷商店列表 + core.openQuickShop(true); + break; + case 32: // SPACE:轻按 + core.getNextItem(); + break; + case 82: // R:回放录像 + core.ui._drawReplay(); + break; + case 33: + case 34: // PgUp/PgDn:浏览地图 + core.ui._drawViewMaps(); + break; + case 66: // B:打开数据统计 + core.ui._drawStatistics(); + break; + case 72: // H:打开帮助页面 + core.ui._drawHelp(); + break; + case 77: // M:打开存档笔记 + core.actions._clickNotes_show(); + break; + case 78: // N:重新开始 + core.confirmRestart(); + break; + case 79: // O:查看工程 + core.actions._clickGameInfo_openProject(); + break; + case 80: // P:游戏主页 + core.actions._clickGameInfo_openComments(); + break; + case 49: // 快捷键1: 破 + if (core.hasItem('pickaxe')) { + core.status.route.push("key:49"); // 将按键记在录像中 + core.useItem('pickaxe', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + } + break; + case 50: // 快捷键2: 炸 + if (core.hasItem('bomb')) { + core.status.route.push("key:50"); // 将按键记在录像中 + core.useItem('bomb', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + } + break; + case 51: // 快捷键3: 飞 + if (core.hasItem('centerFly')) { + core.ui._drawCenterFly(); + } + break; + case 52: // 快捷键4:破冰/冰冻/地震/上下楼器/... 其他道具依次判断 + { + var list = ["icePickaxe", "freezeBadge", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"]; + for (var i = 0; i < list.length; i++) { + var itemId = list[i]; + if (core.canUseItem(itemId)) { + core.status.route.push("key:52"); + core.useItem(itemId, true); + break; + } + } + } + break; + case 53: // 5:读取自动存档(回退),方便手机版操作 + core.doSL("autoSave", "load"); + break; + case 54: // 6:撤销回退,方便手机版操作 + core.doSL("autoSave", "reload"); + break; + case 55: // 快捷键7:绑定为轻按,方便手机版操作 + core.getNextItem(); + break; + case 118: // F7:开启debug模式 + case 119: // F8:由于F7与部分浏览器有冲突,故新增F8 + core.debug(); + break; + case 70: // F:开启技能“二倍斩” + // 检测是否拥有“二倍斩”这个技能道具 + if (core.hasItem('skill1')) { + core.status.route.push("key:70"); + core.useItem('skill1', true); + } + break; + }*/ + + }, + "onStatusBarClick": function (px, py, vertical) { + // 点击状态栏时触发的事件,仅在自绘状态栏开启时生效 + // px和py为点击的像素坐标 + // vertical为录像播放过程中的横竖屏信息 + // 横屏模式下状态栏的画布大小是 129*416 (开启拓展装备栏后是 129*457) + // 竖屏模式下状态栏的画布大小是 416*(32*rows+9) 其中rows为状态栏行数,即全塔属性中statusCanvasRowsOnMobile值 + // 可以使用 _isVertical() 来判定当前是否是竖屏模式 + + // 判定当前是否是竖屏模式。录像播放过程中可能会记录当时的横竖屏信息以覆盖。 + var _isVertical = function () { + if (core.isReplaying() && vertical != null) return vertical; + return core.domStyle.isVertical; + } + // 如果正在执行事件,则忽略 + //if (core.status.lockControl) return; + // 如果当前正在行走,则忽略;也可以使用 core.waitHeroToStop(callback) 来停止行走再回调执行脚本 + if (core.isMoving()) return; + //如果生命小于零,则忽略 + if (core.status.hero.hp <= 0) return; + + if (_isVertical()) { + // 竖屏模式下 + } else {// 横屏模式下 + //console.log(px, py) + if (px >= 1 && px <= 128 && py >= 1 && py <= 25) { //世界等级 + var caidan = 0; + if (px >= 1 && px <= 30 && py >= 1 && py <= 25) { //世界等级 + if (flags.worldlevel == 3) { + if (core.rand2(100) == 0) { + flags.tip[0] = "你感受到世界在微微的震动..."; + caidan = 1; + } + } else if (flags.worldlevel == 4) { + if (core.rand2(100) == 0) { + flags.tip[0] = "新的世界,新的冒险!"; + caidan = 1; + } + if (core.rand2(200) == 0) { + flags.tip[0] = "世界重获新生,你也如此!"; + caidan = 1; + } + } else if (flags.worldlevel == 7) { + if (core.rand2(200) == 0) { + flags.tip[0] = "遁入虚空...杀死入侵者"; + caidan = 1; + } + } else if (flags.worldlevel == 10) { + if (core.rand2(50) == 0) { + flags.tip[0] = "啊...你已经走了这么远了吗...真是一场...了不起的旅行啊"; + caidan = 1; + } + } + } + if (caidan == 0) { + var tpoint = 0; + switch (flags.worldlevel) { + case 1: + tpoint = 1 - flags.tpointsum; + break; + case 2: + tpoint = 6 - flags.tpointsum; + break; + case 3: + tpoint = 19 - flags.tpointsum; + break; + case 4: + tpoint = 47 - flags.tpointsum; + break; + case 5: + tpoint = 95 - flags.tpointsum; + break; + case 6: + tpoint = 170 - flags.tpointsum; + break; + case 7: + tpoint = 263 - flags.tpointsum; + break; + case 8: + tpoint = 374 - flags.tpointsum; + break; + case 9: + tpoint = 505 - flags.tpointsum; + break; + case 10: + tpoint = 0; + break; + } + flags.tip[0] = "欢迎来到\r[#61fff3]" + flags.worldlevel + "级\r世界,再消耗\r[#61fff3]" + tpoint + "个\r科技点提升世界等级"; + } + } else if (px >= 1 && px <= 128 && py >= 26 && py <= 50) { //时间 + flags.tip[0] = "显示当前时段和时间信息"; + } else if (px >= 1 && px <= 128 && py >= 51 && py <= 75) { //生物数 + flags.tip[0] = "显示角色附近生物的数量"; + } else if (px >= 1 && px <= 128 && py >= 76 && py <= 100) { //攻击防御 + flags.tip[0] = "显示主角攻击和防御(注意,每次伤害最低为\r[#61fff3]1点\r)"; + } else if (px >= 1 && px <= 128 && py >= 101 && py <= 125) { //钱币 + flags.tip[0] = "显示拥有的钱币数:\r[#61fff3]" + flags.coppernum + "\r"; + } else if (px >= 5 && px <= 5 + 28 && py >= 130 && py <= 130 + 40) { //角色属性 + core.playSound("确定"); + if (flags.attrui == 0) { + flags.tip[0] = "查看角色属性信息"; + flags.attrui = 1; + flags.CustomKey = 0; + flags.tech = 0; + flags.taskui = 0; + flags.achiui = 0; + core.plugin.attrshow(); + } else { + flags.tip[0] = ""; + flags.attrui = 0; + flags.interface = "物品栏"; + core.plugin.mcjiemian(); + } + } else if (px >= 35 && px <= 35 + 28 && py >= 130 && py <= 130 + 40) { //显示攻击范围 + core.playSound("确定"); + flags.playerAr = !flags.playerAr; + if (flags.playerAr) { + flags.tip[0] = "正在显示攻击范围"; + } else { + flags.tip[0] = "已关闭攻击范围显示"; + } + core.plugin.atkRange(); + } else if (px >= 65 && px <= 65 + 28 && py >= 130 && py <= 130 + 40) { //自定义按键 + core.playSound("确定"); + if (flags.CustomKey == 0) { + flags.tip[0] = "自定义按键(仅PC端)"; + flags.attrui = 0; + flags.CustomKey = 1; + flags.tech = 0; + flags.taskui = 0; + flags.achiui = 0; + core.plugin.zidingyianjian(); + } else { + flags.tip[0] = ""; + flags.CustomKey = 0; + flags.interface = "物品栏"; + core.plugin.mcjiemian(); + } + } else if (px >= 95 && px <= 95 + 28 && py >= 130 && py <= 130 + 40) { //科技 + core.playSound("确定"); + if (flags.tech == 0) { + flags.tip[0] = "升级你的科技,升级你的世界等级!"; + flags.attrui = 0; + flags.CustomKey = 0; + flags.tech = 1; + flags.taskui = 0; + flags.achiui = 0; + core.plugin.techUI(); + } else { + flags.tip[0] = ""; + flags.tech = 0; + flags.interface = "物品栏"; + core.plugin.mcjiemian(); + } + } else if (px >= 5 && px <= 5 + 28 && py >= 172 && py <= 172 + 40) { //任务 + core.playSound("确定"); + if (flags.taskui == 0) { + flags.tip[0] = "来看看村民的委托吧,有报酬的~"; + flags.attrui = 0; + flags.CustomKey = 0; + flags.tech = 0; + flags.taskui = 1; + flags.achiui = 0; + core.plugin.taskshow(); + } else { + flags.tip[0] = ""; + flags.taskui = 0; + flags.interface = "物品栏"; + core.plugin.mcjiemian(); + } + } else if (px >= 35 && px <= 35 + 28 && py >= 172 && py <= 172 + 40) { //成就 + core.playSound("确定"); + if (flags.achiui == 0) { + flags.tip[0] = "成就感拉满了!"; + flags.attrui = 0; + flags.CustomKey = 0; + flags.tech = 0; + flags.taskui = 0; + flags.achiui = 1; + core.plugin.achishow(); + } else { + flags.tip[0] = ""; + flags.achiui = 0; + flags.interface = "物品栏"; + core.plugin.mcjiemian(); + } + } else if (px >= 65 && px <= 65 + 28 && py >= 172 && py <= 172 + 40) { //百科 + flags.tip[0] = "百科全苏(敬请期待)"; + core.playSound("确定"); + + } else if (px >= 95 && px <= 95 + 28 && py >= 172 && py <= 172 + 40) { //设置 + if (core.status.event.id == 'settings') { + flags.tip[0] = ""; + flags.interface = "物品栏"; + core.closePanel(); + core.playSound('取消'); + } else if (flags.interface == "物品栏" && flags.tech == 0 && flags.CustomKey == 0 && core.status.event.id == null) { + flags.tip[0] = "打开设置"; + flags.interface = "锁定"; + core.clearUI(); + core.openSettings(); + core.lockControl(); + } + } else { + flags.tip[0] = ""; + } + } + core.updateStatusBar(); + // 样例一:点击某个区域后使用一个道具 + /* + if (core.hasItem("pickaxe")) { + if (_isVertical()) { + // 竖屏模式下 + if (px >= 200 && px <= 250 && py >= 50 && py <= 100) { + core.useItem("pickaxe"); + } + } else { + // 横屏模式下 + if (px >= 50 && px <= 100 && py >= 200 && py <= 250) { + core.useItem("pickaxe"); + } + } + } + */ + // 样例二:点击某个区域后执行一段公共事件或脚本 + /* + if (core.hasFlag("xxx")) { + if (_isVertical()) { + // 竖屏模式下 + if (px >= 200 && px <= 250 && py >= 50 && py <= 100) { + // 记录点击坐标。这里的1代表此时是竖屏! + core.status.route.push("click:1:" + px + ":" + py); + // 可以插入公共事件 / 普通事件 / 执行一段脚本(如打开自绘UI或增减flag) + core.insertCommonEvent("道具商店"); + // core.insertAction(["一段事件"]); + // core.openItemShop("shop1"); + } + } else { + // 横屏模式下 + if (px >= 50 && px <= 100 && py >= 200 && py <= 250) { + // 记录点击坐标。这里的0代表此时是横屏! + core.status.route.push("click:0:" + px + ":" + py); + // 可以插入公共事件 / 普通事件 / 执行一段脚本(如打开自绘UI或增减flag) + core.insertCommonEvent("道具商店"); + // core.insertAction(["一段事件"]); + // core.openItemShop("shop1"); + } + } + } + */ + } + }, + "control": { + "saveData": function () { + // 存档操作,此函数应该返回“具体要存档的内容” + + // 差异化存储values + var values = {}; + for (var key in core.values) { + if (!core.same(core.values[key], core.data.values[key])) + values[key] = core.clone(core.values[key]); + } + + // 要存档的内容 + var data = { + 'floorId': core.status.floorId, + 'hero': core.clone(core.status.hero), + 'hard': core.status.hard, + 'maps': core.clone(core.maps.saveMap()), + /*'route': core.encodeRoute(core.status.route),*/ + 'values': values, + 'version': core.firstData.version, + 'guid': core.getGuid(), + "time": new Date().getTime() + }; + + return data; + }, + "loadData": function (data, callback) { + // 读档操作;从存储中读取了内容后的行为 + // 重置游戏和路线 + core.resetGame(data.hero, data.hard, data.floorId, core.maps.loadMap(data.maps, null, data.hero.flags), data.values); + /*core.status.route = core.decodeRoute(data.route);*/ + core.control._bindRoutePush(); + // 文字属性,全局属性 + core.status.textAttribute = core.getFlag('textAttribute', core.status.textAttribute); + var toAttribute = core.getFlag('globalAttribute', core.status.globalAttribute); + if (!core.same(toAttribute, core.status.globalAttribute)) { + core.status.globalAttribute = toAttribute; + core.resize(); + } + // 重置音量 + core.events.setVolume(core.getFlag("__volume__", 1), 0); + // 加载勇士图标 + var icon = core.status.hero.image; + icon = core.getMappedName(icon); + if (core.material.images.images[icon]) { + core.material.images.hero = core.material.images.images[icon]; + core.material.icons.hero.width = core.material.images.images[icon].width / 4; + core.material.icons.hero.height = core.material.images.images[icon].height / 4; + } + core.setFlag('__fromLoad__', true); + // 增加自己的一些读档处理 + core.plugin.initVariable(); + // 切换到对应的楼层 + core.changeFloor(data.floorId, null, data.hero.loc, 0, function () { + // 可以在这里设置读档后播放BGM + if (core.hasFlag("__bgm__")) { // 持续播放 + core.playBgm(core.getFlag("__bgm__")); + } + + core.removeFlag('__fromLoad__'); + if (callback) callback(); + }); + }, + "getStatusLabel": function (name) { + // 返回某个状态英文名的对应中文标签,如atk -> 攻击,def -> 防御等。 + // 请注意此项仅影响 libs/ 下的内容(如绘制怪物手册、数据统计等) + // 自行定义的(比如获得道具效果)中用到的“攻击+3”等需要自己去对应地方修改 + + return { + name: "名称", + lv: "等级", + hpmax: "生命上限", + hp: "生命", + manamax: "魔力上限", + mana: "魔力", + atk: "攻击", + def: "防御", + mdef: "护盾", + money: "金币", + exp: "经验", + point: "加点", + steps: "步数", + }[name] || name; + }, + "triggerDebuff": function (action, type) { + // 毒衰咒效果的获得与解除 + // action:获得还是解除;'get'表示获得,'remove'表示解除 + // type:一个数组表示获得了哪些毒衰咒效果;poison, weak,curse + if (!(type instanceof Array)) type = [type]; + + if (action == 'get') { + if (core.inArray(type, 'poison') && !core.hasFlag("poison")) { + // 获得毒效果 + core.setFlag('poison', true); + } + if (core.inArray(type, 'weak') && !core.hasFlag('weak')) { + // 获得衰效果 + core.setFlag('weak', true); + if (core.values.weakValue >= 1) { + // >=1,直接扣数值 + core.addStatus('atk', -core.values.weakValue); + core.addStatus('def', -core.values.weakValue); + } else { + // <1,扣比例 + core.addBuff('atk', -core.values.weakValue); + core.addBuff('def', -core.values.weakValue); + } + } + if (core.inArray(type, 'curse') && !core.hasFlag('curse')) { + // 获得咒效果 + core.setFlag('curse', true); + } + } else if (action == 'remove') { + var success = false; + if (core.inArray(type, "poison") && core.hasFlag("poison")) { + success = true; + // 移除毒效果 + core.setFlag("poison", false); + } + if (core.inArray(type, "weak") && core.hasFlag("weak")) { + success = true; + // 移除衰效果 + core.setFlag("weak", false); + if (core.values.weakValue >= 1) { + // >=1,直接扣数值 + core.addStatus('atk', core.values.weakValue); + core.addStatus('def', core.values.weakValue); + } else { + // <1,扣比例 + core.addBuff('atk', core.values.weakValue); + core.addBuff('def', core.values.weakValue); + } + } + if (core.inArray(type, "curse") && core.hasFlag("curse")) { + success = true; + // 移除咒效果 + core.setFlag("curse", false); + } + if (success) core.playSound('回血'); + } + }, + "updateStatusBar": function () { + // 自定义状态栏绘制 + core.ui.drawStatusBar(); + // 更新状态栏 + /* + // 检查等级 + core.events.checkLvUp(); + + // 检查HP上限 + if (core.flags.statusBarItems.indexOf('enableHPMax') >= 0) { + core.setStatus('hp', Math.min(core.getRealStatus('hpmax'), core.getStatus('hp'))); + } + + // 设置楼层名 + if (core.status.floorId) { + core.setStatusBarInnerHTML('floor', core.status.maps[core.status.floorId].name); + } + + // 设置勇士名字和图标 + core.setStatusBarInnerHTML('name', core.getStatus('name')); + // 设置等级名称 + core.setStatusBarInnerHTML('lv', "1"); + + // 设置生命上限、生命值、攻防护盾金币和经验值 + var statusList = ['hpmax', 'hp', 'mana', 'atk', 'def', 'mdef', 'money', 'exp']; + statusList.forEach(function (item) { + // 向下取整 + core.status.hero[item] = Math.floor(core.status.hero[item]); + // 大数据格式化 + core.setStatusBarInnerHTML(item, core.getRealStatus(item)); + }); + + // 设置魔力值; status:manamax 只有在非负时才生效。 + if (core.status.hero.manamax != null && core.getRealStatus('manamax') >= 0) { + core.status.hero.mana = Math.min(core.status.hero.mana, core.getRealStatus('manamax')); + core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.getRealStatus('manamax')); + } else { + core.setStatusBarInnerHTML("mana", core.status.hero.mana); + } + // 设置技能栏 + // 可以用flag:skill表示当前开启的技能类型,flag:skillName显示技能名;详见文档-个性化-技能塔的支持 + core.setStatusBarInnerHTML('skill', core.getFlag('skillName', '无')); + + // 可以在这里添加自己额外的状态栏信息,比如想攻击显示 +0.5 可以这么写: + // if (core.hasFlag('halfAtk')) core.setStatusBarInnerHTML('atk', core.statusBar.atk.innerText + "+0.5"); + + // 如果是自定义添加的状态栏,也需要在这里进行设置显示的数值 + + // 进阶 + if (core.flags.statusBarItems.indexOf('enableLevelUp') >= 0) { + core.setStatusBarInnerHTML('up', core.formatBigNumber(core.getNextLvUpNeed()) || ""); + } else core.setStatusBarInnerHTML('up', ""); + + // 钥匙 + var keys = ['yellowKey', 'blueKey', 'redKey', 'greenKey']; + keys.forEach(function (key) { + core.setStatusBarInnerHTML(key, core.setTwoDigits(core.itemCount(key))); + }); + // 毒衰咒 + core.setStatusBarInnerHTML('poison', core.hasFlag('poison') ? "毒" : ""); + core.setStatusBarInnerHTML('weak', core.hasFlag('weak') ? "衰" : ""); + core.setStatusBarInnerHTML('curse', core.hasFlag('curse') ? "咒" : ""); + // 破炸飞 + core.setStatusBarInnerHTML('pickaxe', "破" + core.itemCount('pickaxe')); + core.setStatusBarInnerHTML('bomb', "炸" + core.itemCount('bomb')); + core.setStatusBarInnerHTML('fly', "飞" + core.itemCount('centerFly')); + + // 难度 + if (core.statusBar.hard.innerText != core.status.hard) { + core.statusBar.hard.innerText = core.status.hard; + } + var hardColor = core.getFlag('__hardColor__'); + if (hardColor == null) core.statusBar.hard.innerText = ''; + if (core.statusBar.hard.getAttribute('_style') != hardColor) { + core.statusBar.hard.style.color = hardColor; + core.statusBar.hard.setAttribute('_style', hardColor); + }*/ + + // 更新阻激夹域的伤害值 + //core.updateCheckBlock(); + // updateDamage只能在此处执行!!更新全地图显伤 + //core.updateDamage(); + }, + "updateCheckBlock": function (floorId) { + // 领域、夹击、阻击等的伤害值计算 + floorId = floorId || core.status.floorId; + if (!floorId || !core.status.maps) return; + + var width = core.floors[floorId].width, + height = core.floors[floorId].height; + var blocks = core.getMapBlocksObj(floorId); + + var damage = {}, // 每个点的伤害值 + type = {}, // 每个点的伤害类型 + repulse = {}, // 每个点的阻击怪信息 + ambush = {}; // 每个点的捕捉信息 + var betweenAttackLocs = {}; // 所有可能的夹击点 + var needCache = false; + var canGoDeadZone = core.flags.canGoDeadZone; + core.flags.canGoDeadZone = true; + + // 计算血网和领域、阻击、激光的伤害,计算捕捉信息 + for (var loc in blocks) { + var block = blocks[loc], + x = block.x, + y = block.y, + id = block.event.id, + enemy = core.material.enemys[id]; + if (block.disable) continue; + + type[loc] = type[loc] || {}; + + // 血网 + // 如需调用当前楼层的ratio可使用 core.status.maps[floorId].ratio + if (id == 'lavaNet' && !core.hasItem('amulet')) { + damage[loc] = (damage[loc] || 0) + core.values.lavaDamage; + type[loc][(block.event.name || "血网") + "伤害"] = true; + } + + // 领域 + // 如果要防止领域伤害,可以直接简单的将 flag:no_zone 设为true + if (enemy && core.hasSpecial(enemy.special, 15) && !core.hasFlag('no_zone')) { + // 领域范围,默认为1 + var range = enemy.range || 1; + // 是否是九宫格领域 + var zoneSquare = false; + if (enemy.zoneSquare != null) zoneSquare = enemy.zoneSquare; + // 在范围内进行搜索,增加领域伤害值 + for (var dx = -range; dx <= range; dx++) { + for (var dy = -range; dy <= range; dy++) { + if (dx == 0 && dy == 0) continue; + var nx = x + dx, + ny = y + dy, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + // 如果是十字领域,则还需要满足 |dx|+|dy|<=range + if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; + damage[currloc] = (damage[currloc] || 0) + (enemy.zone || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["领域伤害"] = true; + } + } + } + + // 阻击 + // 如果要防止阻击伤害,可以直接简单的将 flag:no_repulse 设为true + if (enemy && core.hasSpecial(enemy.special, 18) && !core.hasFlag('no_repulse')) { + var scan = enemy.zoneSquare ? core.utils.scan2 : core.utils.scan; + for (var dir in scan) { + var nx = x + scan[dir].x, + ny = y + scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + damage[currloc] = (damage[currloc] || 0) + (enemy.repulse || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["阻击伤害"] = true; + + var rdir = core.turnDirection(":back", dir); + // 检查下一个点是否存在事件(从而判定是否移动) + var rnx = x + scan[rdir].x, + rny = y + scan[rdir].y; + if (rnx < 0 || rnx >= width || rny < 0 || rny >= height) continue; + // 如需禁止阻击被推到已隐藏的事件处(如重生怪处),可将这一句的false改为true + if (core.getBlock(rnx, rny, floorId, false) != null) continue; + if (core.utils.scan[rdir] && !core.canMoveHero(x, y, rdir, floorId)) continue; + repulse[currloc] = (repulse[currloc] || []).concat([ + [x, y, id, rdir] + ]); + } + } + + // 激光 + // 如果要防止激光伤害,可以直接简单的将 flag:no_laser 设为true + if (enemy && core.hasSpecial(enemy.special, 24) && !core.hasFlag("no_laser")) { + for (var nx = 0; nx < width; nx++) { + var currloc = nx + "," + y; + if (nx != x) { + damage[currloc] = (damage[currloc] || 0) + (enemy.laser || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["激光伤害"] = true; + } + } + for (var ny = 0; ny < height; ny++) { + var currloc = x + "," + ny; + if (ny != y) { + damage[currloc] = (damage[currloc] || 0) + (enemy.laser || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["激光伤害"] = true; + } + } + } + + // 捕捉 + // 如果要防止捕捉效果,可以直接简单的将 flag:no_ambush 设为true + if (enemy && core.enemys.hasSpecial(enemy.special, 27) && !core.hasFlag("no_ambush")) { + var scan = enemy.zoneSquare ? core.utils.scan2 : core.utils.scan; + // 给周围格子加上【捕捉】记号 + for (var dir in scan) { + var nx = x + scan[dir].x, + ny = y + scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height || (core.utils.scan[dir] && !core.canMoveHero(x, y, dir, floorId))) continue; + ambush[currloc] = (ambush[currloc] || []).concat([ + [x, y, id, dir] + ]); + } + } + + // 夹击;在这里提前计算所有可能的夹击点,具体计算逻辑在下面 + // 如果要防止夹击伤害,可以简单的将 flag:no_betweenAttack 设为true + if (enemy && core.enemys.hasSpecial(enemy.special, 16) && !core.hasFlag('no_betweenAttack')) { + for (var dir in core.utils.scan) { + var nx = x + core.utils.scan[dir].x, + ny = y + core.utils.scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + betweenAttackLocs[currloc] = true; + } + } + + // 检查地图范围类技能 + var specialFlag = core.getSpecialFlag(enemy); + if (specialFlag & 1) needCache = true; + if (core.status.event.id == 'viewMaps') needCache = true; + if ((core.status.event.id == 'book' || core.status.event.id == 'bool-detail') && core.status.event.ui) needCache = true; + } + + // 对每个可能的夹击点计算夹击伤害 + for (var loc in betweenAttackLocs) { + var xy = loc.split(","), + x = parseInt(xy[0]), + y = parseInt(xy[1]); + // 夹击怪物的ID + var enemyId1 = null, + enemyId2 = null; + // 检查左右夹击 + var leftBlock = blocks[(x - 1) + "," + y], + rightBlock = blocks[(x + 1) + "," + y]; + var leftId = core.getFaceDownId(leftBlock), + rightId = core.getFaceDownId(rightBlock); + if (leftBlock && !leftBlock.disable && rightBlock && !rightBlock.disable && leftId == rightId) { + if (core.hasSpecial(leftId, 16)) + enemyId1 = leftId; + } + // 检查上下夹击 + var topBlock = blocks[x + "," + (y - 1)], + bottomBlock = blocks[x + "," + (y + 1)]; + var topId = core.getFaceDownId(topBlock), + bottomId = core.getFaceDownId(bottomBlock); + if (topBlock && !topBlock.disable && bottomBlock && !bottomBlock.disable && topId == bottomId) { + if (core.hasSpecial(topId, 16)) + enemyId2 = topId; + } + + if (enemyId1 != null || enemyId2 != null) { + var leftHp = core.status.hero.hp - (damage[loc] || 0); + if (leftHp > 1) { + // 夹击伤害值 + var value = Math.floor(leftHp / 2); + // 是否不超过怪物伤害值 + if (core.flags.betweenAttackMax) { + var enemyDamage1 = core.getDamage(enemyId1, x, y, floorId); + if (enemyDamage1 != null && enemyDamage1 < value) + value = enemyDamage1; + var enemyDamage2 = core.getDamage(enemyId2, x, y, floorId); + if (enemyDamage2 != null && enemyDamage2 < value) + value = enemyDamage2; + } + if (value > 0) { + damage[loc] = (damage[loc] || 0) + value; + type[loc] = type[loc] || {}; + type[loc]["夹击伤害"] = true; + } + } + } + } + + // 取消注释下面这一段可以让护盾抵御阻激夹域伤害 + /* + for (var loc in damage) { + damage[loc] = Math.max(0, damage[loc] - core.getRealStatus('mdef')); + } + */ + + core.flags.canGoDeadZone = canGoDeadZone; + core.status.checkBlock = { + damage: damage, + type: type, + repulse: repulse, + ambush: ambush, + needCache: needCache, + cache: {} // clear cache + }; + }, + "moveOneStep": function (callback) { + // 勇士每走一步后执行的操作。callback为行走完毕后的回调 + // 这个函数执行在“刚走完”的时候,即还没有检查该点的事件和领域伤害等。 + // 请注意:瞬间移动不会执行该函数。如果要控制能否瞬间移动有三种方法: + // 1. 将全塔属性中的cannotMoveDirectly这个开关勾上,即可在全塔中全程禁止使用瞬移。 + // 2, 将楼层属性中的cannotMoveDirectly这个开关勾上,即禁止在该层楼使用瞬移。 + // 3. 将flag:cannotMoveDirectly置为true,即可使用flag控制在某段剧情范围内禁止瞬移。 + + // 增加步数 + core.status.hero.steps++; + + // 更新跟随者状态,并绘制 + //core.updateFollowers(); + core.drawHero(); + + extend.update(); + + core.plugin.step(); + + + var x = core.status.hero.loc.x; + var y = core.status.hero.loc.y; + var floor = core.status.floorId; + if (x == 31 && y == 0) { //上 + if (floor.slice(0, 3) == "zhu") { + var n = Number(floor.slice(3)); + if (n >= 6) { + floor = "zhu" + (n - 5); + } + } else if (floor.slice(0, 3) == "don") { + var n = Number(floor.slice(4)); + if (n >= 4) { + floor = "dong" + (n - 3); + } + } + core.changeFloor(floor, ':symmetry'); + } else if (x == 31 && y == 62) { //下 + if (floor.slice(0, 3) == "zhu") { + var n = Number(floor.slice(3)); + if (n < 21) { + floor = "zhu" + (n + 5); + } + } else if (floor.slice(0, 3) == "don") { + var n = Number(floor.slice(4)); + if (n < 7) { + floor = "dong" + (n + 3); + } + } + core.changeFloor(floor, ':symmetry'); + } else if (x == 0 && y == 31) { //左 + if (floor.slice(0, 3) == "zhu") { + var n = Number(floor.slice(3)); + if (n != 1 && n != 6 && n != 11 && n != 16 && n != 21) { + floor = "zhu" + (n - 1); + } + } else if (floor.slice(0, 3) == "don") { + var n = Number(floor.slice(4)); + if (n != 1 && n != 4 && n != 7) { + floor = "dong" + (n - 1); + } + } + core.changeFloor(floor, ':symmetry'); + } else if (x == 62 && y == 31) { //右 + if (floor.slice(0, 3) == "zhu") { + var n = Number(floor.slice(3)); + if (n % 5 != 0) { + floor = "zhu" + (n + 1); + } + } else if (floor.slice(0, 3) == "don") { + var n = Number(floor.slice(4)); + if (n % 3 != 0) { + floor = "dong" + (n + 1); + } + } + core.changeFloor(floor, ':symmetry'); + } + + + if (core.getBgNumber(core.nextX(0), core.nextY(0)) == 910) { + if (flags.lavaocc == 0) { + flags.lavaocc = 1; + var lava = setInterval(function () { + if (core.getBgNumber(core.nextX(0), core.nextY(0)) != 910) { + flags.lavaocc = 0; + clearInterval(lava); + } + core.status.hero.hp -= 1; + var rand1 = core.rand2(3); + if (rand1 == 0) { + core.playSound("hit1.ogg"); + } else if (rand1 == 1) { + core.playSound("hit2.ogg"); + } else if (rand1 == 2) { + core.playSound("hit3.ogg"); + } + core.plugin.hpBar(2); + if (core.status.hero.hp <= 0) { + flags.lavaocc = 0; + clearInterval(lava); + core.plugin.siwangjiance(); + } + }, 100); + } + } else if (core.getBgNumber(core.nextX(0), core.nextY(0)) == 345 || + core.getBgNumber(core.nextX(0), core.nextY(0)) == 348) { + if (flags.breocc == 0) { + flags.breocc = 1; + var bre = setInterval(function () { + if (core.getBgNumber(core.nextX(0), core.nextY(0)) != 345 && + core.getBgNumber(core.nextX(0), core.nextY(0)) != 348) { + flags.breocc = 0; + flags.breathe[0] = flags.breathe[1]; + core.plugin.breBar(); + clearInterval(bre); + return; + } + flags.breathe[0] -= 1; + core.plugin.breBar(); + if (flags.breathe[0] < 0) { + core.status.hero.hp -= 5; + var rand1 = core.rand2(3); + if (rand1 == 0) { + core.playSound("hit1.ogg"); + } else if (rand1 == 1) { + core.playSound("hit2.ogg"); + } else if (rand1 == 2) { + core.playSound("hit3.ogg"); + } + core.plugin.hpBar(2); + if (core.status.hero.hp <= 0) { + flags.breocc = 0; + flags.breathe[0] = flags.breathe[1]; + clearInterval(bre); + core.plugin.siwangjiance(); + } + } + }, 1000); + } + } else { + flags.lavaocc = 0; + flags.breocc = 0; + } + + // 检查中毒状态的扣血和死亡 + // if (core.hasFlag('poison')) { + // core.status.hero.statistics.poisonDamage += core.values.poisonDamage; + // core.status.hero.hp -= core.values.poisonDamage; + // if (core.status.hero.hp <= 0) { + // core.status.hero.hp = 0; + // core.updateStatusBar(false, true); + // core.events.lose(); + // return; + // } else { + // core.updateStatusBar(false, true); + // } + // } + + // 从v2.7开始,每一步行走不会再刷新状态栏。 + // 如果有特殊要求(如每走一步都加buff之类),可手动取消注释下面这一句: + // core.updateStatusBar(true, true); + + // 检查自动事件 + //core.checkAutoEvents(); + + // ------ 检查目标点事件 ------ // + // 无事件的道具(如血瓶)需要优先于阻激夹域判定 + var nowx = core.getHeroLoc('x'), + nowy = core.getHeroLoc('y'); + var block = core.getBlock(nowx, nowy); + var hasTrigger = false; + if (block != null && block.event.trigger == 'getItem' && + !core.floors[core.status.floorId].afterGetItem[nowx + "," + nowy]) { + hasTrigger = true; + core.trigger(nowx, nowy, callback); + } + // 执行目标点的阻激夹域事件 + //core.checkBlock(); + + // 执行目标点的script和事件 + if (!hasTrigger) core.trigger(nowx, nowy, callback); + + // 检查该点是否是滑冰 + // if (core.onSki()) { + // // 延迟到事件最后执行,因为这之前可能有阻激夹域动画 + // core.insertAction({ "type": "moveAction" }, null, null, null, true); + // } + + //地图重绘 + //core.drawMap(); + + // ------ 检查目标点事件 END ------ // + + // 如需强行终止行走可以在这里条件判定: + // core.stopAutomaticRoute(); + }, + "moveDirectly": function (x, y, ignoreSteps) { + // 瞬间移动;x,y为要瞬间移动的点;ignoreSteps为减少的步数,可能之前已经被计算过 + // 返回true代表成功瞬移,false代表没有成功瞬移 + + // 判定能否瞬移到该点 + if (ignoreSteps == null) ignoreSteps = core.canMoveDirectly(x, y); + if (ignoreSteps >= 0) { + // 中毒也允许瞬移 + if (core.hasFlag('poison')) { + var damage = ignoreSteps * core.values.poisonDamage; + if (damage >= core.status.hero.hp) return false; + core.status.hero.statistics.poisonDamage += damage; + core.status.hero.hp -= damage; + } + + core.clearMap('hero'); + // 获得勇士最后的朝向 + var lastDirection = core.status.route[core.status.route.length - 1]; + if (['left', 'right', 'up', 'down'].indexOf(lastDirection) >= 0) + core.setHeroLoc('direction', lastDirection); + // 设置坐标,并绘制 + core.control._moveDirectyFollowers(x, y); + core.status.hero.loc.x = x; + core.status.hero.loc.y = y; + core.drawHero(); + // 记录录像 + core.status.route.push("move:" + x + ":" + y); + // 统计信息 + core.status.hero.statistics.moveDirectly++; + core.status.hero.statistics.ignoreSteps += ignoreSteps; + if (core.hasFlag('poison')) { + core.updateStatusBar(false, true); + } + core.ui.drawStatusBar(); + core.checkRouteFolding(); + return true; + } + return false; + }, + "parallelDo": function (timestamp) { + // 并行事件处理,可以在这里写任何需要并行处理的脚本或事件,该函数将被系统反复执行,每次执行间隔视浏览器或设备性能而定,一般约为16.6ms一次,参数timestamp为“从游戏资源加载完毕到当前函数执行时”的时间差,以毫秒为单位 + // 检查当前是否处于游戏开始状态 + if (!core.isPlaying()) return; + // 执行当前楼层的并行事件处理 + if (core.status.floorId) { + try { + eval(core.floors[core.status.floorId].parallelDo); + } catch (e) { + console.error(e); + } + } + + + + } + }, + "ui": { + "getToolboxItems": function (cls) { + // 获得道具栏中当前某类型道具的显示项和显示顺序 + // cls为道具类型,只可能是 tools, constants 和 equips + // 返回一个数组,代表当前某类型道具的显示内容和顺序 + // 默认按id升序排列,您可以取消下面的注释改为按名称排列 + + return Object.keys(core.status.hero.items[cls] || {}) + .filter(function (id) { return !core.material.items[id].hideInToolbox; }) + .sort( /*function (id1, id2) { return core.material.items[id1].name <= core.material.items[id2].name ? -1 : 1 }*/); + }, + "drawStatusBar": function () { + var ctx, fill = function (text, x, y, style, strokeStyle, font, maxWidth) { + core.ui.setFont(ctx, (/\w+/.test(text) ? 'italic ' : '') + 'bold 12px Verdana'); + core.ui.fillBoldText(ctx, text, x, y, style, strokeStyle, font, maxWidth); + }; + + if (core.flags.statusCanvas) { // 系统开关「自绘状态栏」开启 129*416 + core.ui.clearMap(ctx = core.dom.statusCanvasCtx); // 清空状态栏 + core.ui.setFillStyle(ctx, core.status.globalAttribute.statusBarColor); + if (core.domStyle.isVertical) { // 竖屏 + core.drawImage(ctx, core.statusBar.icons.floor, 6, 6, 25, 25); + fill((core.status.thisMap || {}).name || "Loading", 42, 26); + core.drawImage(ctx, core.statusBar.icons.hp, 137, 6, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('hp')), 173, 26); + core.drawImage(ctx, core.statusBar.icons.atk, 268, 6, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('atk')), 304, 26); + core.drawImage(ctx, core.statusBar.icons.def, 6, 38, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('def')), 42, 58); + core.drawImage(ctx, core.statusBar.icons.mdef, 137, 38, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('mdef')), 173, 58); + core.drawImage(ctx, core.statusBar.icons.money, 268, 38, 25, 25); + fill(core.formatBigNumber(core.status.hero.money), 304, 58); + core.drawImage(ctx, core.statusBar.icons.exp, 6, 70, 25, 25); + fill(core.formatBigNumber(core.status.hero.exp), 42, 90); + } + // 横屏且未隐藏状态栏 + else if (!core.flags.hideLeftStatusBar) { + //世界等级 + core.setAlpha(ctx, 0.6); + core.fillRoundRect(ctx, 4, 4, 122, 70, 3, "#dbdbdb"); + core.setAlpha(ctx, 1); + core.drawIcon(ctx, 310, 8, 6, 20, 20); + fill("世界等级:" + flags.worldlevel, 32, 20, "#fffcb3", "", ui.prototype._buildFont(12, false), 100); + //时段、时间 + core.drawIcon(ctx, 307, 8, 28, 20, 20); + //fill(" ***", 32, 42, "white", "", ui.prototype._buildFont(12, false), 100); + var time = Math.floor(flags.time % 60); + switch (Math.floor(flags.time % 60)) { + case 0: + time = "00"; + break; + case 1: + time = "01"; + break; + case 2: + time = "02"; + break; + case 3: + time = "03"; + break; + case 4: + time = "04"; + break; + case 5: + time = "05"; + break; + case 6: + time = "06"; + break; + case 7: + time = "07"; + break; + case 8: + time = "08"; + break; + case 9: + time = "09"; + break; + } + fill(flags.timePeriod + " " + Math.floor(flags.time / 60) + " : " + time, 32, 42, "white", "", ui.prototype._buildFont(12, false), 100); + //周围生物数 + core.drawIcon(ctx, 309, 8, 50, 20, 20); + if (!flags.enemylist) flags.enemylist = {}; + fill("周围生物:" + Object.keys(flags.enemylist).length || 0, 32, 64, "white", "", ui.prototype._buildFont(12, false), 100); + //攻击防御属性 + core.setAlpha(ctx, 0.6); + core.fillRoundRect(ctx, 4, 78, 122, 44, 3, "#dbdbdb"); + core.setAlpha(ctx, 1); + core.drawIcon(ctx, 312, 8, 80, 20, 20); + fill(core.status.hero.atk, 36, 94, "white", "", ui.prototype._buildFont(12, false), 100); + core.drawIcon(ctx, 314, 70, 80, 20, 20); + fill(core.status.hero.def, 98, 94, "white", "", ui.prototype._buildFont(12, false), 100); + //钱币 + core.drawIcon(ctx, 315, 8, 106, 12, 12); + fill(Math.floor(flags.coppernum / 1000), 22, 116, "", "", ui.prototype._buildFont(10, false), 100); + core.drawIcon(ctx, 316, 38, 106, 12, 12); + fill(Math.floor(flags.coppernum % 1000 / 100), 52, 116, "", "", ui.prototype._buildFont(10, false), 100); + core.drawIcon(ctx, 317, 68, 106, 12, 12); + fill(Math.floor(flags.coppernum % 1000 % 100 / 10), 82, 116, "", "", ui.prototype._buildFont(10, false), 100); + core.drawIcon(ctx, 318, 98, 106, 12, 12); + fill(Math.floor(flags.coppernum % 1000 % 100 % 10), 112, 116, "", "", ui.prototype._buildFont(10, false), 100); + + core.setAlpha(ctx, 0.8); + core.fillRoundRect(ctx, 4, 128, 122, 86, 3, "#dbdbdb"); + core.setAlpha(ctx, 1); + //高级属性标志 + core.drawIcon(ctx, 417, 5, 130, 28, 28); + fill("属性", 10, 166, "white", "", ui.prototype._buildFont(9, false), 100); + //显示攻击范围 + core.drawIcon(ctx, 337, 35, 130, 28, 28); + fill("攻距", 41, 166, "white", "", ui.prototype._buildFont(9, false), 100); + //自定义按键标志 + core.drawIcon(ctx, 332, 65, 130, 28, 28); + fill("键位", 71, 166, "white", "", ui.prototype._buildFont(9, false), 100); + //科技标志 + core.drawIcon(ctx, 333, 95, 130, 28, 28); + fill("科技", 101, 166, "white", "", ui.prototype._buildFont(9, false), 100); + //委托标志 + core.drawIcon(ctx, "I542", 5, 172, 28, 28); + fill("委托", 10, 207, "white", "", ui.prototype._buildFont(9, false), 100); + //成就标志 + core.drawIcon(ctx, "I541", 35, 172, 28, 28); + fill("成就", 41, 207, "white", "", ui.prototype._buildFont(9, false), 100); + //百科标志 + core.drawIcon(ctx, 336, 65, 172, 28, 28); + fill("wiki", 71, 207, "white", "", ui.prototype._buildFont(9, false), 100); + //设置标志 + core.drawIcon(ctx, 334, 95, 172, 28, 28); + fill("设置", 101, 207, "white", "", ui.prototype._buildFont(9, false), 100); + + //信息框 + core.setAlpha(ctx, 0.8); + core.fillRoundRect(ctx, 4, 220, 122, 80, 3, "#8a8a8a"); + core.setAlpha(ctx, 1); + //信息 + if (!flags.tip) flags.tip = [""]; + if (flags.tip[0].length <= 8) { + core.drawTextContent( + ctx, flags.tip[0], + { left: 6, top: 235, color: "#fdff61", fontSize: 15, maxWidth: 120 } + ); + } else { + core.drawTextContent( + ctx, flags.tip[0], + { left: 6, top: 220, color: "#fdff61", fontSize: 15, maxWidth: 120 } + ); + } + + + } + + + } else if (core.flags.hideLeftStatusBar && !core.domStyle.isVertical) { // 横屏且隐藏状态栏 + if (!core.dymCanvas['status']) + core.ui.createCanvas('status', 0, 0, core._PX_, core._PY_, 66); // 刚好盖过显伤层 + core.ui.clearMap(ctx = core.dymCanvas['status']); + core.ui.setFillStyle(ctx, core.status.globalAttribute.statusBarColor); + var offset = core.status.hero.loc.x - core.bigmap.offsetX / 32 >= core._HEIGHT_ ? 0 : core._PY_; + core.ui.setAlpha(ctx, 0.75); + core.ui.drawWindowSkin('winskin.png', ctx, offset, 0, core._PX_ - core._PY_, core._PY_); + core.ui.setAlpha(ctx, 1); + core.drawImage(ctx, core.statusBar.icons.floor, 6 + offset, 9, 25, 25); + fill((core.status.thisMap || {}).name || "Loading", 42 + offset, 29); + core.drawImage(ctx, core.statusBar.icons.hp, 6 + offset, 43, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('hp')), 42 + offset, 63); + core.drawImage(ctx, core.statusBar.icons.atk, 6 + offset, 77, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('atk')), 42 + offset, 97); + core.drawImage(ctx, core.statusBar.icons.def, 6 + offset, 111, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('def')), 42 + offset, 131); + core.drawImage(ctx, core.statusBar.icons.mdef, 6 + offset, 145, 25, 25); + fill(core.formatBigNumber(core.getRealStatus('mdef')), 42 + offset, 165); + core.drawImage(ctx, core.statusBar.icons.money, 6 + offset, 179, 25, 25); + fill(core.formatBigNumber(core.status.hero.money), 42 + offset, 199); + core.drawImage(ctx, core.statusBar.icons.exp, 6 + offset, 213, 25, 25); + fill(core.formatBigNumber(core.status.hero.exp), 42 + offset, 233); + fill(core.setTwoDigits(core.itemCount('yellowKey')), 11 + offset, 267, '#FFCCAA'); + fill(core.setTwoDigits(core.itemCount('blueKey')), 46 + offset, 267, '#AAAADD'); + fill(core.setTwoDigits(core.itemCount('redKey')), 81 + offset, 267, '#FF8888'); + } + }, + "drawStatistics": function () { + // 浏览地图时参与的统计项目 + + return [ + 'yellowDoor', 'blueDoor', 'redDoor', 'greenDoor', 'steelDoor', + 'yellowKey', 'blueKey', 'redKey', 'greenKey', 'steelKey', + 'redGem', 'blueGem', 'greenGem', 'yellowGem', + 'redPotion', 'bluePotion', 'greenPotion', 'yellowPotion', 'superPotion', + 'pickaxe', 'bomb', 'centerFly', 'icePickaxe', 'freezeBadge', + 'earthquake', 'upFly', 'downFly', 'jumpShoes', 'lifeWand', + 'poisonWine', 'weakWine', 'curseWine', 'superWine', + 'sword1', 'sword2', 'sword3', 'sword4', 'sword5', + 'shield1', 'shield2', 'shield3', 'shield4', 'shield5', + // 在这里可以增加新的ID来进行统计个数,只能增加道具ID + ]; + }, + "drawAbout": function () { + // 绘制“关于”界面 + core.ui.closePanel(); + core.lockControl(); + core.status.event.id = 'about'; + + var left = 48, + top = 36, + right = (core._PX_ || core.__PIXELS__) - 2 * left, + bottom = (core._PY_ || core.__PIXELS__) - 2 * top; + + core.setAlpha('ui', 0.85); + core.fillRect('ui', left, top, right, bottom, '#000000'); + core.setAlpha('ui', 1); + core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); + + var text_start = left + 24; + + // 名称 + core.setTextAlign('ui', 'left'); + var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; + core.fillText('ui', "HTML5 魔塔样板", text_start, top + 35, globalAttribute.selectColor, "bold 22px " + globalAttribute.font); + core.fillText('ui', "版本: " + main.__VERSION__, text_start, top + 80, "#FFFFFF", "bold 17px " + globalAttribute.font); + core.fillText('ui', "作者: 天堑无涯", text_start, top + 112); + core.fillText('ui', 'HTML5魔塔技术交流群:539113091', text_start, top + 112 + 32); + core.fillText('ui', '沙盒魔塔交流群:892801510', text_start, top + 112 + 32 + 32); + // 写自己的“关于”页面,每次增加32像素即可 + core.playSound('打开界面'); + } + } +} \ No newline at end of file diff --git a/icon.d.ts b/icon.d.ts new file mode 100644 index 0000000..043f781 --- /dev/null +++ b/icon.d.ts @@ -0,0 +1,77 @@ +type IconIds = + | keyof MaterialIcon['animates'] + | keyof MaterialIcon['autotile'] + | keyof MaterialIcon['enemy48'] + | keyof MaterialIcon['enemys'] + | keyof MaterialIcon['hero'] + | keyof MaterialIcon['items'] + | keyof MaterialIcon['items'] + | keyof MaterialIcon['npc48'] + | keyof MaterialIcon['npcs'] + | keyof MaterialIcon['terrains']; + +interface IconOffsetInfo { + /** + * 图块所在额外素材的id + */ + image: string; + + /** + * 图块所在图片位于额外素材的横坐标 + */ + x: number; + + /** + * 图块所在图片位于额外素材的纵坐标 + */ + y: number; +} + +/** + * 和图标相关的内容 + */ +interface Icons { + /** + * 图标信息 + */ + readonly icons: MaterialIcon; + + /** + * 额外素材偏移起点 + */ + readonly tilesetStartOffset: 10000; + + /** + * 图标的id + */ + readonly allIconIds: IconIds; + + /** + * 获得所有图标类型 + */ + getIcons(): MaterialIcon; + + /** + * 根据ID获得图块类型 + */ + getClsFromId(id: T): ClsOf; + + /** + * 获得所有图标的ID + */ + getAllIconIds(): IconIds; + + /** + * 根据图块数字或ID获得所在的tileset和坐标信息 + * @param id 图块数字或id + */ + getTilesetOffset(id: string | number): IconOffsetInfo | null; + + /** + * 获取动画帧数 + * @param cls 类型 + */ + getAnimateFrames(cls: T): FrameOf; +} + +declare const icons: new () => Icons; diff --git a/icons.js b/icons.js new file mode 100644 index 0000000..59ca79d --- /dev/null +++ b/icons.js @@ -0,0 +1,1046 @@ +var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = +{ + "hero": { + "down": { + "loc": 0, + "stop": 0, + "leftFoot": 1, + "rightFoot": 3 + }, + "left": { + "loc": 1, + "stop": 0, + "leftFoot": 1, + "rightFoot": 3 + }, + "right": { + "loc": 2, + "stop": 0, + "leftFoot": 1, + "rightFoot": 3 + }, + "up": { + "loc": 3, + "stop": 0, + "leftFoot": 1, + "rightFoot": 3 + }, + "width": 32, + "height": 48 + }, + "terrains": { + "ground": 0, + "grass": 1, + "grass2": 2, + "ground2": 3, + "ground3": 4, + "downFloor": 5, + "upFloor": 6, + "blueShopLeft": 7, + "pinkShopLeft": 8, + "blueShopRight": 9, + "pinkShopRight": 10, + "arrowUp": 11, + "arrowDown": 12, + "arrowLeft": 13, + "arrowRight": 14, + "light": 15, + "darkLight": 16, + "ski": 17, + "flower": 18, + "box": 19, + "boxed": 20, + "sWallT": 21, + "T331": 35, + "T332": 36, + "T333": 37, + "T334": 38, + "T335": 39, + "T336": 40, + "T337": 41, + "T338": 42, + "T339": 43, + "T340": 44, + "T341": 45, + "T342": 46, + "T343": 47, + "T344": 48, + "T345": 49, + "T346": 50, + "T347": 51, + "T348": 52, + "T349": 53, + "T350": 54, + "T351": 55, + "T352": 56, + "T353": 57, + "T354": 58, + "T355": 59, + "T356": 60, + "T357": 61, + "T358": 62, + "T359": 63, + "T360": 64, + "T361": 65, + "T362": 66, + "T363": 67, + "T364": 68, + "T365": 69, + "T366": 70, + "T367": 71, + "T368": 72, + "T369": 73, + "T370": 74, + "T371": 75, + "T372": 76, + "T373": 77, + "T374": 78, + "T375": 79, + "T376": 80, + "T377": 81, + "T378": 82, + "T379": 83, + "T380": 84, + "T381": 85, + "T382": 86, + "T383": 87, + "T384": 88, + "T385": 89, + "T386": 90, + "T387": 91, + "T388": 92, + "T389": 93, + "T390": 94, + "T391": 95, + "T392": 96, + "T393": 97, + "T394": 98, + "T395": 99, + "T396": 100, + "T397": 101, + "T398": 102, + "T399": 103, + "T400": 104, + "T401": 105, + "T402": 106, + "T403": 107, + "T404": 108, + "T405": 109, + "T406": 110, + "T407": 111, + "T408": 112, + "T409": 113, + "T410": 114, + "T411": 115, + "T412": 116, + "T413": 117, + "T414": 118, + "T415": 119, + "T416": 120, + "T417": 121, + "T418": 122, + "T419": 123, + "T420": 124, + "T421": 125, + "T422": 126, + "T423": 127, + "T424": 128, + "T425": 129, + "T426": 130, + "T427": 131, + "T428": 132, + "T429": 133, + "T430": 134, + "T431": 135, + "T432": 136, + "T433": 137, + "T302": 22, + "T303": 23, + "T304": 24, + "T306": 25, + "T307": 26, + "T309": 27, + "T310": 28, + "T312": 29, + "T314": 30, + "T315": 31, + "T316": 32, + "T317": 33, + "T318": 34, + "T897": 139, + "T898": 140, + "T899": 141, + "T900": 142, + "T901": 143, + "T902": 144, + "T903": 145, + "T904": 146, + "T905": 147, + "T906": 148, + "T907": 149, + "T908": 150, + "T909": 151, + "T910": 152, + "T911": 153, + "T912": 154, + "T913": 155, + "T914": 156, + "T915": 157, + "T916": 158, + "T917": 159, + "T918": 160, + "T919": 161, + "T920": 162, + "T921": 163, + "T922": 164, + "T896": 138, + "T923": 165, + "T924": 166, + "T925": 167, + "T926": 168, + "T927": 169, + "T928": 170, + "T929": 171, + "T930": 172, + "T931": 173, + "T932": 174, + "T933": 175, + "T934": 176, + "T935": 177, + "T936": 178, + "T937": 179, + "T938": 180, + "T939": 181, + "T940": 182, + "T941": 183, + "T942": 184, + "T943": 185, + "T944": 186, + "T945": 187, + "T946": 188, + "T947": 189, + "T948": 190, + "T949": 191, + "T950": 192, + "T951": 193, + "T952": 194, + "T953": 195, + "T954": 196, + "T955": 197, + "T956": 198, + "T957": 199, + "T958": 200, + "T959": 201, + "T960": 202, + "T961": 203, + "T962": 204, + "T963": 205, + "T964": 206, + "T965": 207, + "T966": 208, + "T967": 209, + "T968": 210, + "T969": 211, + "T970": 212, + "T971": 213, + "T972": 214, + "T973": 215, + "T974": 216, + "T975": 217, + "T976": 218, + "T977": 219, + "T978": 220, + "T979": 221, + "T980": 222, + "T981": 223, + "T982": 224, + "T983": 225, + "T984": 226, + "T985": 227, + "T986": 228 + }, + "animates": { + "star": 0, + "lava": 1, + "blueLava": 2, + "water": 3, + "yellowDoor": 4, + "blueDoor": 5, + "redDoor": 6, + "greenDoor": 7, + "specialDoor": 8, + "steelDoor": 9, + "yellowWall": 10, + "whiteWall": 11, + "blueWall": 12, + "crystalUp": 13, + "crystalBottom": 14, + "starPortal": 15, + "fire": 16, + "portal": 17, + "switch": 18, + "lavaNet": 19, + "poisonNet": 20, + "weakNet": 21, + "curseNet": 22, + "downPortal": 23, + "leftPortal": 24, + "rightPortal": 25, + "upPortal": 26, + "ice": 27, + "magentaWall": 28, + "A463": 29, + "A464": 30, + "A465": 31 + }, + "npcs": { + "man": 0, + "trader": 1, + "thief": 2, + "fairy": 3, + "wizard": 4, + "recluse": 5, + "king": 6, + "youngMan": 7, + "sign": 8, + "expShop": 9, + "moneyShop": 10, + "princess": 11, + "greenMan": 12, + "blueTrader": 13, + "N878": 14, + "N879": 15, + "N880": 16, + "N881": 17, + "N882": 18, + "N883": 19, + "N884": 20, + "N885": 21, + "N886": 22, + "N887": 23, + "N888": 24, + "N889": 25, + "N890": 26, + "N891": 27, + "N892": 28, + "N893": 29, + "N894": 30, + "N895": 31 + }, + "npc48": { + "npc0": 0, + "npc1": 1, + "npc2": 2, + "npc3": 3, + "tallYellowDoor": 4, + "tallBlueDoor": 5, + "tallRedDoor": 6, + "tallGreenDoor": 7, + "tallSpecialDoor": 8, + "tallSteelDoor": 9 + }, + "enemys": { + "greenSlime": 0, + "redSlime": 1, + "blackSlime": 2, + "slimelord": 3, + "bat": 4, + "bigBat": 5, + "redBat": 6, + "vampire": 7, + "skeleton": 8, + "skeletonCaptain": 10, + "zombie": 12, + "zombieKnight": 13, + "rock": 14, + "bluePriest": 16, + "redPriest": 17, + "brownWizard": 18, + "redWizard": 19, + "swordsman": 23, + "soldier": 24, + "yellowKnight": 25, + "redKnight": 26, + "darkKnight": 27, + "blueKnight": 31, + "goldSlime": 32, + "poisonSkeleton": 33, + "poisonBat": 34, + "skeletonPriest": 36, + "skeletonKing": 37, + "demonPriest": 42, + "goldHornSlime": 43, + "silverSlime": 47, + "whiteHornSlime": 49, + "redSwordsman": 53, + "poisonZombie": 55, + "octopus": 57, + "princessEnemy": 59, + "skeletonWarrior": 9, + "silverSlimelord": 60, + "goldSlimelord": 61, + "whiteSlimeman": 54, + "slimeman": 15, + "yellowGateKeeper": 20, + "blueGateKeeper": 21, + "redGateKeeper": 22, + "magicMaster": 46, + "devilWarrior": 41, + "fairyEnemy": 58, + "dragon": 56, + "skeletonKnight": 39, + "skeletonPresbyter": 38, + "ironRock": 35, + "grayRock": 62, + "yellowPriest": 52, + "evilPrincess": 50, + "blademaster": 48, + "evilFairy": 51, + "greenKnight": 63, + "bowman": 64, + "watcherSlime": 66, + "frostBat": 67, + "devilKnight": 68, + "grayPriest": 69, + "greenGateKeeper": 70, + "ghostSoldier": 11, + "evilHero": 40, + "blackKing": 28, + "yellowKing": 29, + "greenKing": 30, + "redKing": 44, + "blueKing": 45, + "keiskeiFairy": 71, + "tulipFairy": 72, + "purpleBowman": 65, + "E434": 73, + "E435": 74, + "E436": 75, + "E437": 76, + "E438": 77, + "E439": 78, + "E440": 79, + "E441": 80, + "E442": 81, + "E443": 82, + "E444": 83, + "E445": 84, + "E446": 85, + "E447": 86, + "E448": 87, + "E449": 88, + "E450": 89, + "E451": 90, + "E452": 91, + "E453": 92, + "E454": 93, + "E455": 94, + "E456": 95, + "E457": 96, + "E458": 97, + "E459": 98, + "E460": 99, + "E461": 100, + "E462": 101, + "E987": 102, + "E988": 103, + "E989": 104, + "E990": 105, + "E991": 106, + "E992": 107, + "E993": 108, + "E994": 109, + "E995": 110, + "E996": 111, + "E997": 112, + "E998": 113, + "E999": 114, + "E1000": 115, + "E1001": 116, + "E1002": 117, + "E1003": 118, + "E1004": 119, + "E1005": 120, + "E1006": 121, + "E1007": 122, + "E1008": 123, + "E1009": 124, + "E1010": 125, + "E1011": 126, + "E1012": 127, + "E1013": 128, + "E1014": 129, + "E1015": 130, + "E1016": 131, + "E1017": 132, + "E1018": 133, + "E1019": 134, + "E1020": 135, + "E1021": 136, + "E1022": 137, + "E1023": 138, + "E1024": 139, + "E1025": 140, + "E1026": 141, + "E1027": 142, + "E1028": 143, + "E1029": 144, + "E1030": 145, + "E1031": 146, + "E1032": 147, + "E1033": 148, + "E1034": 149, + "E1035": 150, + "E1036": 151, + "E1037": 152 + }, + "enemy48": { + "E1": 0, + "E2": 1, + "E3": 2, + "E4": 3, + "E5": 4, + "E6": 5, + "E7": 6, + "E8": 7, + "E9": 8, + "E10": 9, + "E11": 10, + "E12": 11, + "E13": 12, + "E14": 13, + "E15": 14, + "E16": 15, + "E17": 16, + "E18": 17, + "E19": 18, + "E20": 19, + "E21": 20, + "E22": 21, + "E23": 22, + "E24": 23, + "E25": 24, + "E26": 25, + "E27": 26, + "E28": 27, + "E29": 28, + "E30": 29, + "E31": 30, + "E32": 31, + "E33": 32, + "E34": 33, + "E35": 34, + "E36": 35, + "E37": 36, + "E38": 37, + "E39": 38, + "E40": 39, + "E41": 40, + "E42": 41, + "E43": 42, + "E44": 43, + "E45": 44, + "E46": 45, + "E47": 46, + "E48": 47, + "E49": 48, + "E50": 49, + "E51": 50, + "E52": 51, + "E53": 52, + "E54": 53, + "E55": 54, + "E56": 55, + "E57": 56, + "E58": 57, + "E59": 58, + "E60": 59, + "E61": 60, + "E62": 61, + "E63": 62, + "E64": 63, + "E65": 64, + "E66": 65, + "E67": 66, + "E68": 67, + "E69": 68, + "E70": 69, + "E71": 70, + "E72": 71, + "E73": 72, + "E74": 73, + "E75": 74, + "E76": 75 + }, + "items": { + "yellowKey": 0, + "blueKey": 1, + "redKey": 2, + "greenKey": 3, + "steelKey": 4, + "bigKey": 6, + "redGem": 16, + "blueGem": 17, + "greenGem": 18, + "yellowGem": 19, + "redPotion": 20, + "bluePotion": 21, + "greenPotion": 22, + "yellowPotion": 23, + "lifeWand": 33, + "sword0": 60, + "sword1": 50, + "sword2": 51, + "sword3": 52, + "sword4": 53, + "sword5": 54, + "shield0": 61, + "shield1": 55, + "shield2": 56, + "shield3": 57, + "shield4": 58, + "shield5": 59, + "book": 9, + "fly": 12, + "pickaxe": 45, + "icePickaxe": 44, + "bomb": 43, + "centerFly": 13, + "upFly": 15, + "downFly": 14, + "coin": 11, + "freezeBadge": 41, + "cross": 40, + "superPotion": 29, + "earthquake": 8, + "poisonWine": 24, + "weakWine": 25, + "curseWine": 27, + "superWine": 28, + "dagger": 42, + "amulet": 47, + "hammer": 48, + "jumpShoes": 49, + "skill1": 30, + "wand": 10, + "pack": 46, + "I466": 5, + "I467": 7, + "I468": 26, + "I469": 31, + "I470": 32, + "I471": 34, + "I472": 35, + "I473": 36, + "I474": 37, + "I475": 38, + "I476": 39, + "I477": 62, + "I478": 63, + "I479": 64, + "I480": 65, + "I481": 66, + "I482": 67, + "I483": 68, + "I484": 69, + "I485": 70, + "I486": 71, + "I487": 72, + "I488": 73, + "I489": 74, + "I490": 75, + "I491": 76, + "I492": 77, + "I493": 78, + "I494": 79, + "I495": 80, + "I496": 81, + "I497": 82, + "I498": 83, + "I499": 84, + "I500": 85, + "I501": 86, + "I502": 87, + "I503": 88, + "I504": 89, + "I505": 90, + "I506": 91, + "I507": 92, + "I508": 93, + "I509": 94, + "I510": 95, + "I511": 96, + "I512": 97, + "I513": 98, + "I514": 99, + "I515": 100, + "I516": 101, + "I517": 102, + "I518": 103, + "I519": 104, + "I520": 105, + "I521": 106, + "I522": 107, + "I523": 108, + "I524": 109, + "I525": 110, + "I526": 111, + "I527": 112, + "I528": 113, + "I529": 114, + "I530": 115, + "I531": 116, + "I532": 117, + "I533": 118, + "I534": 119, + "I535": 120, + "I536": 121, + "I537": 122, + "I538": 123, + "I539": 124, + "I540": 125, + "I541": 126, + "I542": 127, + "I543": 128, + "I544": 129, + "I545": 130, + "I546": 131, + "I547": 132, + "I548": 133, + "I549": 134, + "I550": 135, + "I551": 136, + "I552": 137, + "I553": 138, + "I554": 139, + "I555": 140, + "I556": 141, + "I557": 142, + "I558": 143, + "I559": 144, + "I560": 145, + "I561": 146, + "I562": 147, + "I563": 148, + "I564": 149, + "I565": 150, + "I566": 151, + "I567": 152, + "I568": 153, + "I569": 154, + "I570": 155, + "I571": 156, + "I572": 157, + "I573": 158, + "I574": 159, + "I575": 160, + "I576": 161, + "I577": 162, + "I578": 163, + "I579": 164, + "I580": 165, + "I581": 166, + "I582": 167, + "I583": 168, + "I584": 169, + "I585": 170, + "I586": 171, + "I587": 172, + "I588": 173, + "I589": 174, + "I590": 175, + "I591": 176, + "I592": 177, + "I593": 178, + "I594": 179, + "I595": 180, + "I596": 181, + "I597": 182, + "I598": 183, + "I599": 184, + "I600": 185, + "I601": 186, + "I602": 187, + "I603": 188, + "I604": 189, + "I605": 190, + "I606": 191, + "I607": 192, + "I608": 193, + "I609": 194, + "I610": 195, + "I611": 196, + "I612": 197, + "I613": 198, + "I614": 199, + "I615": 200, + "I616": 201, + "I617": 202, + "I618": 203, + "I619": 204, + "I620": 205, + "I621": 206, + "I622": 207, + "I623": 208, + "I624": 209, + "I625": 210, + "I626": 211, + "I627": 212, + "I628": 213, + "I629": 214, + "I630": 215, + "I631": 216, + "I632": 217, + "I633": 218, + "I634": 219, + "I635": 220, + "I636": 221, + "I637": 222, + "I638": 223, + "I639": 224, + "I640": 225, + "I641": 226, + "I642": 227, + "I643": 228, + "I644": 229, + "I645": 230, + "I646": 231, + "I647": 232, + "I648": 233, + "I649": 234, + "I650": 235, + "I651": 236, + "I652": 237, + "I653": 238, + "I654": 239, + "I655": 240, + "I656": 241, + "I657": 242, + "I658": 243, + "I659": 244, + "I660": 245, + "I661": 246, + "I662": 247, + "I663": 248, + "I664": 249, + "I665": 250, + "I666": 251, + "I667": 252, + "I668": 253, + "I669": 254, + "I670": 255, + "I671": 256, + "I672": 257, + "I673": 258, + "I674": 259, + "I675": 260, + "I676": 261, + "I677": 262, + "I678": 263, + "I679": 264, + "I680": 265, + "I681": 266, + "I682": 267, + "I683": 268, + "I684": 269, + "I685": 270, + "I686": 271, + "I687": 272, + "I688": 273, + "I689": 274, + "I690": 275, + "I691": 276, + "I692": 277, + "I693": 278, + "I694": 279, + "I695": 280, + "I696": 281, + "I697": 282, + "I698": 283, + "I699": 284, + "I700": 285, + "I701": 286, + "I702": 287, + "I703": 288, + "I704": 289, + "I705": 290, + "I706": 291, + "I707": 292, + "I708": 293, + "I709": 294, + "I710": 295, + "I711": 296, + "I712": 297, + "I713": 298, + "I714": 299, + "I715": 300, + "I716": 301, + "I717": 302, + "I718": 303, + "I719": 304, + "I720": 305, + "I721": 306, + "I722": 307, + "I723": 308, + "I724": 309, + "I725": 310, + "I726": 311, + "I727": 312, + "I728": 313, + "I729": 314, + "I730": 315, + "I731": 316, + "I732": 317, + "I733": 318, + "I734": 319, + "I735": 320, + "I736": 321, + "I737": 322, + "I738": 323, + "I739": 324, + "I740": 325, + "I741": 326, + "I742": 327, + "I743": 328, + "I744": 329, + "I745": 330, + "I746": 331, + "I747": 332, + "I748": 333, + "I749": 334, + "I750": 335, + "I751": 336, + "I752": 337, + "I753": 338, + "I754": 339, + "I755": 340, + "I756": 341, + "I757": 342, + "I758": 343, + "I759": 344, + "I760": 345, + "I761": 346, + "I762": 347, + "I763": 348, + "I764": 349, + "I765": 350, + "I766": 351, + "I767": 352, + "I768": 353, + "I769": 354, + "I770": 355, + "I771": 356, + "I772": 357, + "I773": 358, + "I774": 359, + "I775": 360, + "I776": 361, + "I777": 362, + "I778": 363, + "I779": 364, + "I780": 365, + "I781": 366, + "I782": 367, + "I783": 368, + "I784": 369, + "I785": 370, + "I786": 371, + "I787": 372, + "I788": 373, + "I789": 374, + "I790": 375, + "I791": 376, + "I792": 377, + "I793": 378, + "I794": 379, + "I795": 380, + "I796": 381, + "I797": 382, + "I798": 383, + "I799": 384, + "I800": 385, + "I801": 386, + "I802": 387, + "I803": 388, + "I804": 389, + "I805": 390, + "I806": 391, + "I807": 392, + "I808": 393, + "I809": 394, + "I810": 395, + "I811": 396, + "I812": 397, + "I813": 398, + "I814": 399, + "I815": 400, + "I816": 401, + "I817": 402, + "I818": 403, + "I819": 404, + "I820": 405, + "I821": 406, + "I822": 407, + "I823": 408, + "I824": 409, + "I825": 410, + "I826": 411, + "I827": 412, + "I828": 413, + "I829": 414, + "I830": 415, + "I831": 416, + "I832": 417, + "I833": 418, + "I834": 419, + "I835": 420, + "I836": 421, + "I837": 422, + "I838": 423, + "I839": 424, + "I840": 425, + "I841": 426, + "I842": 427, + "I843": 428, + "I844": 429, + "I845": 430, + "I846": 431, + "I847": 432, + "I848": 433, + "I849": 434, + "I850": 435, + "I851": 436, + "I852": 437, + "I853": 438, + "I854": 439, + "I855": 440, + "I856": 441, + "I857": 442, + "I858": 443, + "I859": 444, + "I860": 445, + "I861": 446, + "I862": 447, + "I863": 448, + "I864": 449, + "I865": 450, + "I866": 451, + "I867": 452, + "I868": 453, + "I869": 454, + "I870": 455, + "I871": 456, + "I872": 457, + "I873": 458, + "I874": 459, + "I875": 460, + "I876": 461, + "I877": 462 + }, + "autotile": { + "autotile": 0, + "autotile1": 0, + "autotile2": 0, + "autotile3": 0 + } +} \ No newline at end of file diff --git a/index.html b/index.html index 538efc2..2711059 100644 --- a/index.html +++ b/index.html @@ -1,208 +1,197 @@ - - + - - - - - HTML5魔塔 - - - - - - - - + + HTML5魔塔样板 + + + + + + + + + - - -

      -
      - -
      - - -
      -

      请稍候...

      - -
      -
      -
      -
      -
      -

      资源即将开始加载

      -

      HTML5魔塔游戏平台,享受更多魔塔游戏:
      https://h5mota.com/

      -
      - - -
      -
      - 开始游戏 - 载入游戏 - 录像回放 -
      -
      -
      -
      -
      -

      -

      -

      -
      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - -

      -
      -
      - - - - -
      -
      - - - -
      -
      - - - -
      + node.innerHTML=str + }) + } + */ + ] + } + + + + + - -
      -
      - - - - - - - - - - - - - - - - -

      -
      -
      -
      -
      - - - - - - - - - - 此浏览器不支持HTML5 - -
      -
      -
      -
      -

      请输入文字...

      - - - -
      -
      -
      - - - - - - - - + var script = document.createElement('script'); + script.innerHTML = converter.mainFile[5] + converter.mainFile[6]; + window.core={material:{items:[],enemys:[]}} + document.body.appendChild(script); + MotaActionFunctions.disableReplace = true; + MotaActionFunctions.disableExpandCompare = true; + + script = document.createElement('script'); + script.src='https://cdn.bootcss.com/docsify/4.5.5/docsify.min.js' + document.body.appendChild(script); + script = document.createElement('script'); + script.src='https://cdn.bootcss.com/docsify/4.5.5/plugins/search.min.js' + document.body.appendChild(script); + } + xhr.open('GET', '../_server/MotaAction.g4', true); + xhr.send(null); + --> - - \ No newline at end of file + diff --git a/index.md b/index.md new file mode 100644 index 0000000..30af938 --- /dev/null +++ b/index.md @@ -0,0 +1,25 @@ +# HTML5 魔塔样板说明文档 + +当您打开这份帮助文档的瞬间,相信您一定是抱着幼年时的游戏开发梦想前来的。众所周知,即时游戏的开发要比非即时游戏难上许多,像素级游戏的开发又要比网格地图游戏难上许多。 + +在非即时网格地图游戏(譬如策略战棋)中,有一类叫做“固定数值RPG”,简称“魔塔”。这是一种基于运筹学的数学优化建模游戏,虽然小众,却不失有自己的圈子。 + +在当下,魔塔的趋势是向移动端发展,网络上也常常能见到“求手机魔塔”的提问。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于安卓和苹果还必须开发不同的版本,非常麻烦。 + +但是,现在我们有了HTML5。 +HTML5的画布(canvas)以及它被Android/iOS内置浏览器所支持的特性,可以让我们做出真正意义上的全平台覆盖的魔塔。 + +然而,一般而言使用非RPG +Maker制作魔塔往往需要一定的编程技术,HTML5魔塔自然也不例外。但是,为了能让大家更加注重于“做塔”本身,而不用考虑做塔以外的各种脚本问题,@艾之葵(GitHub +ckcz123)特意制作了这样一部HTML5魔塔样板。 + +这个魔塔样板,可以让你在完全不懂任何编程语言的情况下,做出自己的H5魔塔。不会代码?没关系!只要你想做,就能做出来! + +继续查看文档的详细介绍,让你学会如何使用这一个样板来制作属于自己的HTML5魔塔,或者……任何非即时的网格地图游戏。 + +* [新版视频教程](https://www.bilibili.com/video/BV1SB4y1p7bg?share_source=copy_web) +* [脚本教程](https://www.bilibili.com/video/BV1uL411J7yZ?share_source=copy_web) + +========================================================================================== + +[继续阅读下一章:现在就做出自己的第一部H5魔塔!](start) diff --git a/instruction.md b/instruction.md new file mode 100644 index 0000000..8d364ec --- /dev/null +++ b/instruction.md @@ -0,0 +1,656 @@ +# 事件指令 + +?> 在这一节中,让我们来了解每一类事件的具体介绍 + +本样板之所以敢宣称“零编程基础的您也能大展身手”,就是因为它搭载了强大的图形化json编辑器(blockly)。 + +熟悉Antlr语法的读者可以根据[修改编辑器](editor)去自行修改`_server\MotaAction.g4`等文件去扩展其功能。 + +json代码本身则可以作为`core.insertAction()`函数的自变量,去插入一段事件执行。 + +下述提到的“当前点”坐标均指`core.status.event.data.x`和`core.status.event.data.y`。 + +## 指令的分类(注意区分块的颜色和地图的七彩点) + +尽管事件指令和`core.events._action_xxx()`函数是一一对应的,但它们进一步调用的底层函数却分布在libs文件夹的不同文件中,大致上: + +* 显示文字类(严格来说需要玩家操作的事件还涉及到actions.js)和UI绘制类在ui.js +* 数据相关类(这类也有不少道具相关在items.js)和特效声音类在control.js +* 地图处理类在maps.js +* 事件控制类(或许应该叫流程控制类)则在events.js,请注意区分libs和project的同名文件。 + +另一种分类方法则是按照同步和异步,分成以下几类: +1. **瞬间就能执行完的:**如UI绘制、设置XX属性、显隐和转变图层块等。 +2. **阻塞直到玩家操作的:**如显示文章/选择项/确认框、接受用户输入、等待用户操作、呼出xxx等。 +3. **阻塞一段固定时间的:**如开关门、显示动画(观赏性)、移动跳跃、淡入淡出等。 +4. **耗时但不阻塞的:**如播放音效(V2.8支持阻塞)、显示提示等,一般为纯观赏性指令,会和后面的指令同时执行。 + +上述第3类指令都可以勾选“不等待执行完毕”(即前面提到的异步事件)变为第4类,从而实现诸如同步开关多个门的效果。 + +在json区,每条指令的格式为:`{"type": "xxx", "param1": ..., "param2": ..., ......}`, +实际执行的函数为`core.events._action_xxx(data, x, y, prefix)` + +data为整个指令对象,x和y为当前点坐标,prefix为独立开关的楼层前缀。 + +您可以自行使用`core.registerEvent`注册一个新的事件。如果需要把新指令做成像已有的指令一样有类别、名称、取色器、勾选框、下拉框、输入框等部件,请查阅[修改编辑器](editor)。 + +V2.8.1起,下拉框中没有的项都可以通过在json区输入并点击“解析”按钮来临时追加(刷新网页后失效),如需永久追加请查阅[修改编辑器](editor)。 + +与此同时,显示文章、显示选择项、显示确认框都支持双击预览,预览前请先摆一个“设置剧情文本的属性”设置您预览时需要的属性然后双击它。 + +另外,原本“显示文字类”的图片相关指令,和“特效声音类”的音频相关指令,在V2.8.1被移出来合并到了一个新类“音像处理类”,请知悉。 + +## 显示文字类(黄色) + +![image](img/images_texty.jpg) + +这个类别的指令会负责UI层图文的处理,如图片的移动和淡入淡出,游戏的胜败和重启等。 + +### 显示文章 + +最基本的就是最灵活的。本指令的讲解将占用大量篇幅,请做好准备。 + +上述函数中,第一个自变量为字符串数组或单个字符串,每个字符串为一段需要玩家按下确认键或点击屏幕才会消失的剧情文本,第二个自变量(可选)为全部文本消失后的回调函数。 + +每条显示文章分为五部分:标题、图像、对话框效果、正文、立绘。 + +写成一个字符串就是`\t[标题,图像]\b[对话框效果]\f[立绘]正文`。 + +1. **标题:**可选,一般填说话人的名字。如果不填,则尝试根据图像取中文名(道具除外,道具不会说话所以只填图像不填标题就会没有标题)。如图像填hero但不填标题则以勇士名字作为标题,标题还可以填`null`强制不显示标题(如`\t[null,hero]`只显示勇士头像)。 +2. **图像:**可选,可以填hero(勇士行走图,如果勇士开了帧动画则会取当前朝向,但朝上会视为朝下)或任何图块ID,或者填this来尝试取当前点图块。 + * 也可以填一个png格式的图片文件名(需要后缀),图像甚至还可以填null来避免以图块ID为标题被解释成图像(如`\t[bomb,null]`会以英文单词bomb为标题而没有图像,但单独的`\t[bomb]`则会没有标题但以炸弹道具为图像)。 + * 只填写图像而不填写标题时,会被优先解析到标题框中,请不要惊慌,这并不影响效果。 +3. **对话框效果:**可选,填法非常灵活,如下(坐标在大地图中均为绝对坐标,省略则取当前点)。 + 1. `up,x,y`:对话框显示在点(x,y)上方,尖角朝下指着这个点(具体指的高度取决于图像。没有图像则判断该点是32×32px还是32×48px的图块,但是不管有无图像,若该点没有图块则都没有尖角)。 + 2. `down,x,y`:对话框显示在点(x,y)下方,尖角朝上指着这个点。比起上面,这个没有高度问题,不过该点没有图块的话则还是没有尖角。 + * 上述两种写法中,如果把坐标换成hero则显示在勇士上方或下方(尖角朝下的话,高度取决于勇士的行走图)。 + 3. `this,x,y`:在大地图中,点(x,y)可能位于视野上半部分也可能位于下半部分,写this就能让对话框自动适配上下来防止越界。 + 4. `hero`:在大地图上下边缘或小地图,勇士可能位于视野上半部分也可能位于下半部分,只写hero也能自动适配。 + 5. `hero,n`:n为正整数,尖角指向勇士的第n名跟随者,自动适配上下,将hero改为up或down则手动指定上下。 + 6. `up,null`:显示在屏幕最上方,同理up换成down则为最下方。 + 7. `center`:强制显示在屏幕中央,宽度为视野宽度。 + 8. 除最后两种外,其余写法都会给对话框进行宽度自适配: + * 如果正文没有自动换行,则会先尝试取一个让文本总行数最接近“几行半”的宽度,可能会再适当加宽来防止标题出界。 + * 如果正文有自动换行,则会尝试连同标题在内取最长的一行作为宽度。 + * V2.7起,文本绘制一定程度上支持了排版标点禁则,成对符号的左半部分不会出现在行尾,右半部分和其他某些标点不会出现在行首。 + 9. 最终绘制时会尽量让尖角居中,除非尖角所指的点实在太靠左或太靠右。 + 10. 值得注意的是,使用`project/images/winskin.png`或类似的图片作为文章背景时,尖角的绘制用的是用图片右下角64×32px的两格进行的,所以往往需要您自己准备好。 + * 技术群`959329661`的群文件“常用素材”提供了一些已经制作好的这样的图片,敬请取用。 +4. **正文:**双击指令块,进入多行编辑。正文中允许使用很多转义序列,当您键入一个\字符时就会提示补全,后面逐一介绍。 +5. **立绘:**显示文章的同时可以绘制一张或多张立绘图,请双击预览各张图的效果或右击整个指令预览所有立绘。每张立绘由一大堆参数组成:`\f[name(:x/:y/:o,sx,sy,sw,sh,)x,y(,w,h,alpha,angle)]` + 1. **文件名:**需要放在project\images文件夹中并注册,这里需要带后缀。 + 2. **翻转:**和楼层贴图一样,支持三种翻转,在json代码中以文件的后缀名之后追加“:x、:y、:o”来表示。 + 3. **绘制坐标:**立绘在视野中的左上角像素坐标,后面的参数一旦省略其中一个则必须省略其后所有。 + 4. **绘制的宽高:**立绘被伸缩后在视野中实际显示的宽高,必须同时填写,不填则不伸缩。 + 5. **裁剪坐标和宽高:**必须同时填写,为从原图中裁剪区域的左上角坐标和区域宽高,不填则取全图。 + 6. **不透明度和旋转角度:**可选,前者填一个不大于1的正数,请自行双击预览。 +6. **左上角坐标、限宽:**V2.8.1新增,该项不能和上面的`\b[对话框效果]`同时使用。 + * 使用此项后,本指令将变为正常的json格式而不是字符串格式。 + * “左上角坐标”指视野中的相对像素坐标,“限宽”必须和左上角坐标一起指定,不能单独指定。 + * “限宽”的下限,在没有图像的情况下大约为64px,在有图像的情况下大约为128px,可以双击预览来确认到底能否正常显示。 + * 一种推荐的用法是,在指定下面的非0编号后使用,然后“显示选择项/确认框”,这样就和RPG Maker的行为一致了。 + * 而且还能充分利用两侧的空间讲清楚每个子选项,这是在“选择项的提示文字无法随光标位置而变化”的现状下的一种妥协做法。 +7. **编号:**V2.8.1新增,可以同时显示多个不同编号的对话框,常用来表现多名角色的嘈杂对话。 + * 和上一项一样,此项填写非0值后,指令将变为正常的json格式而不是字符串格式。 + * 非0编号的对话框将不会自动消失(甚至勇士恢复自由行动后也是),可以被同编号的对话框覆盖或手动清除。 + * 自由行动时,该项常用来做一些常驻提示,如技能的开关状态、毒衰咒状态、当前剧情任务进度等。 + * 您甚至可以像图片一样去移动对话框(如和一个npc一起移动),支持四种变速效果,立绘也会被一起移动。 + +立绘是画在UI层的,下一个指令执行前就会擦除。如需持续显示请使用“显示图片”指令,另外立绘会被“图像”遮挡。 + +### 显示文章正文的转义序列 + +1. **表达式计算:**使用`${}`可以计算(eval)一个js表达式,式子中允许使用所有的冒号缩写量和API,详见`core.calValue()`函数。 + * 此语法也可以用于“道具名称”、“道具描述”和“即捡即用提示”,只不过那里就不支持中文替换了。 + * 如`勇士当前的攻防相乘是\${状态:攻击\*状态:防御}`(中文替换),`持有三色钥匙共\${item:yellowKey+item:blueKey+item:redKey}把。`(json) + * V2.8和更早的版本中,样板对右花括弧的处理是(非贪心)正则匹配,因此`${内部}`不能再出现右花括弧,这也意味着您无法使用对象、复杂的流程控制语句和函数定义,只能使用简单的运算符(包括三元运算)和函数调用。 + * V2.8.1中,匹配方式改为了堆栈匹配,上述问题得到解决,您可以放心使用匿名函数了,这对道具名称/道具描述这种场合是个福音。 +2. **局部文字变色:**使用`\r[颜色英文名]`或`\r[\#RrGgBb]`(十六进制)来将这之后的文本变为另一种颜色。 + * 最常用的17种颜色提供了自动补全,十六进制颜色可以随便找个有颜色参数的指令,呼出调色器去自己调配。只使用\r不带方括号则变回默认颜色。 +3. **局部字号调节:**使用`\\c[正整数]`改变这之后文本的字号,只使用`\\c`不加方括号则恢复默认字号。 +4. **手动换行、局部加粗和斜体:**退出多行编辑后,手动换行写作`\n`,另外可以使用`\\d`将局部文本加粗或取消加粗,使用`\\e`将局部文本变为斜体或取消斜体。 +5. **32×32px图标的绘制:**使用`\\i[图标ID]`绘制一个32×32px的图块的第一帧或系统图标,您可以使用`core.statusBar.icons`查看所有的系统图标。 + * 出于历史遗留问题,图块ID可以和系统图标ID重复,此时优先使用图块ID. +6. **打字速度调节:**开启打字机效果后,文本的打字速度总是匀速的。所以样板提供了名为“时间占位符”的转义序列,使用`\\z[正整数]`可以暂停相当于打这么多个字的时间。 + +除`\n,\t,\b,\r,\f`外,其余转义序列的反斜杠在json中必须写两个! + +### 其他文字类指令 + +1. **自动剧情文本:**和上面的显示文章基本相同,只不过不是由玩家按下确认键或点击屏幕,而是一定毫秒后自动消失,录像回放中则忽略。 + * 比起那个,这个不能通过长按Ctrl键或长按屏幕快进,大量使用时一般用来搭配语音。否则对魔塔这种快餐游戏来说可能会非常不友好,建议统一塞进“显示确认框”指令的场合之一。 +2. **滚动剧情文本:**将一段文字从屏幕最下方滚动到最上方,不支持自动换行,常用于op和ed. + * 该指令对应`core.drawScrollText(content, time, lineHeight, callback)`。 +3. **显示提示:**即诸如打败怪物、捡到道具、打不开门时左上角的提示,只支持`${表达式计算}`和`\r[变色]`。 + * 可以填写一个图标ID显示在提示文本的左侧(支持32×48px但只画靠上的2/3,也可以使用系统图标)。 + * 此指令对应`core.drawTip(text, icon, frame)`函数。自然数`frame`表示绘制第几帧,默认为0表示第一帧(但没有在事件中提供)。 + * 此指令看似显示出的提示会过一会才消失,但此指令本身依然是瞬间完成的,不属于异步事件! + * V2.6.4短暂提供了“同时显示多个tip”的功能,这在事件中连续获得多个道具时很友好, + * 但是玩家纷纷表示自由行动时此功能遮挡了太多的视野,因而在V2.6.5中就取消了。 + * 如果需要此功能(包括自定义底色),请在本项目的[github](https://github.com/ckcz123/mota-js/archive/refs/tags/v2.6.4-release.zip)站点自行下载V2.6.4的发布版本并对照研究。 +4. **游戏胜败和重启:**游戏胜败分别对应“脚本编辑”(快捷键N)的`win`和`lose`函数,在线游戏排行榜中每个结局的每个难度都有一张榜。 + * 但同一结局只有最高难度有效,您可以勾选“不计入榜单”来让这个本来有效的结局也无效。还可以勾选“不结束游戏”来先停止录像的录制,再演出ed. + * win和lose函数最终都会调用`core.gameOver(ending)`函数,区别在于lose不填写ending. 但是,事件中使用gameOver这一异步函数需要额外的技巧,详见“原生脚本”。 + * 重启游戏对应的函数为`core.showStartAnimate()` +5. **设置剧情文本的属性:**可用`core.status.textAttribute`获取当前的剧情文本属性,各项含义: + 1. **位置:**“显示文章”不使用`\b`对话框效果时文本的位置,默认为屏幕中央。如果您有大量集中的剧情文本都欲使用`up,null`的对话框效果,则可以直接将此项设置为“顶部”,而将剩余的个别剧情文本使用`center`或`down,null`的对话框效果,反之亦然。 + 2. **偏移像素:**上面的“位置”选择“顶部”或“底部”时和屏幕上下边缘的距离,也作为滚动剧情文本和左边缘的距离。 + 3. **对齐:**默认为左对齐,可以修改此项来让显示文章的标题和正文都居中或都右对齐。 + 4. **标题色:**准确地说是“标题和图像边框色”,格式和楼层画面色调一致,可以点击调色器按钮呼出调色器调色。 + 5. **正文色:**如题,修改方法同上,从而避免频繁使用\r进行局部文本变色。 + 6. **背景色:**如题,修改方法同上。但比起前两个,这个也允许填写一个类似`winskin.png`的图片文件名。 + 7. **标题和正文字号:**如题,正文字号推荐设置为偶数。 + 8. **行距和字符间距:**如题,单位都是像素,行距推荐为正文字号的一倍半。 + 9. **粗体(Y/N):**文本是否默认加粗,推荐在大量粗体文本中穿插少量细体文本时使用,以免频繁的`\\d`切换。 + 10. **打字间隔:**0表示不启用打字机效果而是一次显示完,正整数表示每打一个字的毫秒数,也作为`\\z`暂停的单位时间。 + 11. **淡入淡出时间:**V2.8新增,指定此项后,每个“显示文章”指令都会增加淡入淡出效果,建议用于大段的剧情层。 + +可以使用`core.clone(core.status.textAttribute)`将文本属性备份到`hero.flags`中,从而做到临时使用另一套文本属性绘制部分内容。 + +### 图片类指令 + +1. **显示图片:**和立绘的语法基本类似,只不过多了编号(1—50)和淡入时间。 + * 可以双击预览效果,还可以勾选“不等待执行完毕”来和后面的指令同时执行,比如同时淡入两张图片,或者淡入一张同时淡出/移动另一张。 + * 编号较大的图片会遮盖较小的,1—24号图片会被色调层遮盖,25—40号图片会遮盖色调层但被UI层遮盖,41—50号图片会遮盖UI层。 + * 此指令对应`core.showImage()`函数,编号真正的意义,详见[个性化](personalization) +2. **清除图片:**如题,需要指定要清除的图片编号和淡出时间(显隐图片的时间都可以填0表示瞬间完成)。 + * 此指令对应`core.hideImage(code, time, callback)` +3. **图片移动:**其实还包括了透明度渐变,“终点像素位置”指移动结束后的图片在视野中的左上角像素坐标(不填则表示单纯的透明度渐变),“不透明度”指渐变结束后的新的不透明度(不填表示单纯的移动)。对应`core.moveImage(code, to, opacityVal, moveMode, time, callback)` + * V2.8起,图片和视野的移动支持加速度,分为“匀速、加速、减速、先加速再减速”四种,请任意选用。 +4. **图片旋转:**V2.8新增,同样支持加速度,旋转中心坐标不填则取图片中心。 + * 此指令对应`core.rotateImage(code, center, angle, moveMode, time, callback)`函数。 + * 比起移动,旋转本身不支持同时透明度渐变,您可以先写一个不指定终点的移动指令且“不等待执行完毕”来实现单纯的淡入淡出,然后再写一个耗时相同或略长的旋转指令,这样两个指令就会一起执行了。 + * 当不指定旋转中心时,本指令可以和移动指令同时使用,从而得到“图片的中心做直线运动、同时图片还在绕着中心自转”的效果。 +5. **图片放缩:**V2.8.1新增,同样支持加速度,放缩中心坐标不填则取图片中心。 + * 此指令对应`core.scaleImage(code, center, scale, moveMode, time, callback)`函数。 + * 可以和“图片移动/旋转”一起使用,做出PowerPoint中常见的动画效果。 +5. **显示或清除动图:**需要填写动图的文件名(带.gif后缀),“起点像素位置”含义如前且必须填写,可以双击指令块来预览第一帧的效果。 + * 动图不支持淡入淡出和伸缩移动,如果不填任何参数则清除所有动图(只支持全部清除)。 + * 该指令对应`core.showGif(name, x, y)`函数。 +6. **显示图片化文本:**这是您唯一显示镜像文字的机会。 + * 显示出来后就会视为一张通常的图片,可以被清除、移动、淡入淡出、旋转。 + +### 显示确认框,选择项,QTE与全局商店 + +QTE,即快速反应事件。一般表现为需要玩家在收到某信号后尽快做出正确操作,如新新魔塔2中面对白银史莱姆王的猜拳战斗就需要根据其出拳的颜色尽快按下相克的数字键。 + +样板同样支持这类事件,一共有三种,这里先介绍两种。 + +一是**显示确认框**,它会显示一段支持`${表达式求值}`但不支持自动换行、淡入淡出和其他转义序列的文字。然后要求玩家在一定毫秒数内选择“确定”或“取消”之一,如果超时就视为哪个都没选,直接继续执行后面的事件。 + +您可以指定闪烁光标的初始停留位置是确定还是取消,还可以指定超时毫秒数为0表示不限时间但玩家必须做出二选一。 + +当指定了超时时间并且及时做出选择时,剩余时间会被写入“变量:timeout”,可以用来做一些处理(音游?)。 + +此指令对应`core.drawConfirmBox(text, yesCallback, noCallback)`函数,其中两个Callback分别为选择确定和取消后的回调函数。 + +V2.8起,显示确认框在录像回放时如果录像中没有记录该选哪一项(或者明明此事件未设置超时时间但录像中记录了超时),就会选择默认项(也就是光标的默认位置),请注意。 + +二是**显示选择项**,和RPG Maker不同,我们的选择项不会和它之前的“显示文章”同时出现,可以直接配上除对话框、打字机、淡入淡出外的所有文字效果。 + +此指令对应`core.drawChoices(content, choices)`函数,其中content为提示文字,choices为各子选项文字组成的字符串数组。是的,比起上面的函数,这个不直接支持回调。 + +在没有提示文字的情况下,一次能同时显示的子选项最多为13或15个。和确认框一样,选择项的超时值填0表示不限时间但玩家必须从中选择一个。大于0的话超时视为什么都没选,直接继续执行后面的事件。 + +每个子选项的文字只支持`${表达式求值}`和整行变色,请注意控制字数。文字左侧也支持追加一个图标(多帧图块取第一帧),支持系统图标。 + +每个子选项还可以指定“出现条件”(不指定则一定出现),条件的写法和自动事件的触发条件一样,从而做出形如“怒气值满才显示大招选项”的效果。 + +如果实际执行时所有子选项都不满足出现条件,则直接跳过。但是如果出现的项都不满足下面的“启用条件”就会导致所有项都不可选,然后游戏卡死,请务必注意这个问题。 + +V2.7.2起,每个子选项还可以指定“启用条件”,当出现条件和启用条件都满足时才能真正选中这一项。 + +如果只满足前者但不满足后者,该项会变灰,尝试选择时会播放“操作失败”系统音效并显示提示(超时倒计时不会重置)。 + +当指定了超时时间并且及时做出有效选择时,剩余时间同样会被写入“变量:timeout”,可以用来做一些处理。 + +您或许会疑惑提示文字为什么不做成随光标位置而变化(这在很多电脑/手柄游戏中很常见),这是因为触屏设备无法改变光标位置,如有需要,请自行在此界面提供虚拟方向键(不打算发布触屏版游戏的则无所谓),然后在提示文字中使用${}对`core.status.event.selection`进行判定,从而改变提示文字。 + +V2.8起,显示选择项在录像回放时如果录像中没有记录该选哪一项(或者明明此事件未设置超时时间但录像中记录了超时),就会弹窗询问玩家是否补选一项,此时玩家可以输入某项的序号(从0起)来修复录像,当然也可以在弹窗时点取消来放弃修复。 + +提交成绩后,站点后端的录像验证时如果发生了同样的问题,则因为无法询问玩家,会选择默认项(即初始光标位置,下面会提到)。 + +如果录像回放中尝试选择不满足“启用条件”的灰项,V2.8起会直接报错。这种处理方式比上面的“弹窗请求补选”更为严厉,如您在造塔测试时遇到了这种问题,可以先“回退到上一个节点”,然后在控制台输入`core.status.replay.toReplay`查看接下来该播放的内容(可以按N键单步播放),并在这个一维数组中删除掉那个非法的选项值。 + +而如果录像中记录了多余的确认框或选择项(`choices:n`),V2.8起就会在播放时跳过并警告。 + +V2.8.1起,“显示选择项”支持限宽和手动指定默认项了(注意最好指定一个100%出现且启用的项),配合“带编号的显示文章”效果更佳哦! + +![image](img/quickshops.jpg) + +在“全塔属性——全局商店”中可以编辑各个商店,商店一共有三种: + +1. **公共事件商店:**最简单的一种商店,或者应该叫做给玩家准备的快捷功能更合适,因为它的内容完全不一定非得是个做买卖做交易的“商店”,也可以是诸如“开启或关闭主动技能”、“快速换上最强套装”之类的便捷功能。 + * 多说一句,鉴于全局商店列表最多只能同时显示12或14项(还有一项是关闭列表),因此您也可以准备一些永久道具,设置适当的使用条件,并在使用效果事件中去实现这些给玩家的快捷功能。当然全局商店的使用条件更加统一,请自行权衡。 + * 公共事件商店在用法上和一般的“插入公共事件”并无二致,同样可以提供一个参数列表。 +2. **道具商店:**这种商店也很简单,由第三种QTE指令实现但没有限时。 + * 您可以在其中随意填写买卖的道具ID、存量、买卖价和出现条件。 + * 存量不填视为无限,买入价不填视为只能卖(回收),卖出价不填视为只能买,出现条件的含义和选择项一致。 + * 如果需要在游戏中对买卖价和对存量进行读写,请读写`core.status.shops` + * 请注意,使用道具商店的话务必保留`project/images/winskin.png`及其注册信息,可以换成相同规格的同名图片。 +3. **新版商店:**用法非常灵活的一种商店,其外形酷似“显示选择项”但有一些不同。 + * 首先和其他两种商店一样,它多出了“商店id、快捷名称、未开启不显示”。 + * 商店id只能使用全英数,且必须两两不同。 + * “快捷名称”为显示在V键快捷菜单的名称,请注意控制字数,最好也两两不同以免玩家混淆。 + * 勾选“未开启不显示”则此商店在开启前或禁用后不会出现在V键菜单中,当商店总个数超过12或14个且随着游戏流程进度依次开新的关旧的时,这个勾选项就很有必要了。 + * 其次,和其他两种商店不同,您可以允许玩家预览它(前提是V键菜单中显示了),这对魔塔这种倡导完全信息的游戏来说非常有意义。 + * 最后,比起常规的“显示选择项”,它不能指定超时毫秒数,但是(V2.8起)允许长按连续购买。 + * 实际执行中在所有子选项的最后会自动追加一个“离开”选项,选择其他子选项并执行后商店并不会立即关闭而是停在那个界面。就像胖老鼠和两部新新魔塔一样。 + * “出现条件”和“使用条件”相搭配,让您能够很轻松地做出形如“消耗金币和各种材料的装备合成路线”这样的设定。 + * 在预览模式下除“离开”外的子选项、以及交易模式下不满足“使用条件”的子选项,都会显示为灰色,尝试选择时会播放“操作失败”系统音效并提示失败原因。 + * 子选项的执行内容中需要手动处理扣费等问题,此外,在制作像两部新新魔塔一样会涨价的商店时,您也需要自己准备变量(变量名不必与商店id相同)去记录已购次数或者直接记录价格,并手动处理涨价问题。 + +有关全局商店的详细实现,请参见“插件编写”(句号键,`project/plugin.js`)。 + +其中还提供了一个`core.canUseQuickShop(id)`函数来控制勇士什么时候可以通过V键菜单快捷使用哪些商店,自变量id为商店id. + +本质上,新版商店是套在一个死循环里的。您可以在子选项的执行内容中使用“跳出当前循环”指令来打断该子选项剩余的未执行内容而强制离开商店, + +或使用“提前结束本轮循环”来打断未执行内容而强制重启商店。 + +同理,公共事件(包括公共事件商店)和自动事件本质上是“一次性”的条件为`false`的后置条件循环,因此使用这两个指令都能跳出它们。 + +另外,全局商店在录像中的记录方式是`"shop:id"`紧接着显示选择项的记录,也就是说“离开”项的序号可能会不固定(尤其是在连载塔中),请稍加留心。 + +## 数据相关类(绿色) + +![image](img/control_itemsg.jpg) + +这类的指令会设置各种数据(如怪物属性、楼层属性、全塔属性、七大可读写块),处理弹窗输入和开关全局商店,以及控制玩家最最关心的勇士的各种行为。 + +### 设置各种数据的指令 + +1. **数值操作:**最简单的就是最灵活的,本指令能够修改七大可读写块(状态、物品、变量、独立开关、临时变量、全局存储、增益)的值。 + * 修改的运算符有十种,“设为”会将右块的值代入左块,“增加、减少、乘以、除以”则是对左块的值进行增减和扩大缩小。 + * 除法如想只保留整数商(向零靠近)则改用“除以并取商”,如想要余数(例如想取勇士生命值的后两位)则使用“除以并取余”。 + * “乘方”指的是将若干个(不一定是正整数)左块连乘起来的积代入左块。 + * “设为不大于”和“设为不小于”是指在左块大于/小于右块的时候将右块代入左块,也就是“封顶”和“保底”的作用。 + * 指令的右块为一表达式,可以使用任何值块和运算块,甚至直接使用API. + * 如果需要连续使用本指令,建议除最后一个外都勾选“不刷新状态栏”,以降低刷新状态栏的性能损耗,并且避免意外触发自动事件、生命和魔力溢出甚至死亡(生命小于等于0)。 +2. **设置怪物属性:**可以设置怪物的任何一项属性并计入存档。 + * 怪物ID在blockly块中也可以填中文(要求没有重复,有的话请在事件编辑器顶部关闭“中文替换”功能),需要设置的属性项在下拉框中选取。通过配置表格自行新增的属性在下拉框里没有,但可以写在json区再单击变黄的“解析”按钮,或修改`_server\MotaAction.g4`文件最后的部分去追加。 + * 本指令对应`core.setEnemy(id, name, value, operator, prefix)`函数,完全等价。注意value会被eval,因此字符串需要额外套一层引号! + * 最后的“值”和“数值操作”的右块写法一致,注意设置怪物名称需要加引号,设置逻辑值(是否)需要填写`true`或`false`,设置“特殊属性”需要填数组且只支持结果值。 + * V2.8起,怪物支持“行走图朝向”功能,您在四个朝向的怪物中任意设置一个的属性都会强制同步到其他三个。 +3. **定点设置/移动或重置怪物属性:**V2.8新增,该指令主要是为了实现一些诸如“发动技能后指定某个点,该点怪物被削弱/增强,并计入存档”的功能。 + * 该指令支持设置的属性有“名称、生命、攻击、防御、金币、经验、加点”,设置后,该属性会在“脚本编辑——怪物真实属性”中最先被使用,然后可以被光环等影响。 + * 使用时,需要指定楼层和坐标,楼层不写则取当前层,坐标不填则取当前点,支持双击从地图选点,(除移动外)支持选多个点。 + * “移动某点怪物属性”支持写增量(dx、dy),如写[4,-2]就会让某个点的怪物属性移动到向右4格、向上2格的位置。 + * 发生战斗后,该点会被自动重置定点属性。怪物移动跳跃时(如阻击)定点属性会自动一起被移动,您无需手动移动。 + * 该组指令实际调用的API为: + ``` + core.setEnemyOnPoint(x, y, floorId, ...) + core.moveEnemyOnPoint(fromX, fromY, toX, toY, floorId) + core.resetEnemyOnPoint(x, y, floorId) + core.enemys.getEnemyValue(enemy, name, x, y, floorId) // 读取 + ``` +4. **设置装备属性:**V2.8新增,该项可以制作一些“随剧情推进而强化装备”的效果并计入存档。 + * 使用时,需要指定装备ID、要修改的是常数值还是增益、要修改的属性英文名等。 +5. **设置楼层属性:**除了贴图和两个到达事件,其他属性都可以方便地修改。 + * 楼层ID不填则视为当前楼层,可以去“地图选点”浏览各楼层并复制ID. + * 注意修改“楼层中文名”、“状态栏中名称”、“默认地面ID”、“背景音乐”(需要后缀名)这些字符串类型都需要加引号(V2.8起背景音乐支持一维数组),几个“能否/是否”只支持修改为`true`或`false`,三个坐标和天气、色调这些数组类型都需要加方括弧。本指令对应`core.events.setFloorInfo(name, value, floorId, prefix)` + * 修改当前层的天气/色调/背景音乐后不会立即生效,如需生效请补一个对应的特效指令(如“恢复画面色调”)并且不要勾选“持续”。 +6. **设置全局属性:**即全塔属性中的“主样式”(无需再加引号)和装备孔列表。 + * 修改装备孔列表时请注意,如果装备的道具属性中填写的装备类型是自然数,则可以【在列表结尾】随着游戏流程的推进解锁新的装备孔或移除装备孔(请先将身上的此类装备脱下)。 + * 而如果装备的道具属性中填写的装备类型是装备孔名称,则可以随着游戏流程的推进修改装备孔的类型组成,如本来是两把剑一块盾改成一把剑两块盾(同样需要注意已经穿上的装备问题)。 + * 本指令对应`core.events.setGlobalAttribute(name, value)`函数。 +7. **设置全局数值:**如题,可以修改四种宝石和三种血瓶的基础值等,必要时也可以修改图块的每帧时间以及上下楼时间以得到一些演出效果。 + * 如需使用脚本,请直接修改`core.values`,完全等价。但是竖屏状态栏的自绘行数如果动态修改有可能会出问题,请注意。 +8. **设置系统开关:**如题,可以用来随着游戏流程的推进解锁/移除状态栏的显示项或改动其他开关。 + * 比如中途开关生命上限、加点和负伤,中途显隐魔力、技能、绿钥匙和破炸飞毒衰咒。 + * 在游戏胜利时会将生命值作为分数上传到在线游戏排行榜,因此请在胜利前关闭生命上限再修改生命,比如根据钥匙等道具的持有情况进行加分。 + * 请注意,即使您在游戏中途才将楼传变为平面塔模式,访问过的楼层依然已经记录了最后离开的位置。 + * 本指令对应`core.setGlobalFlag(name, value)`函数,实际会修改`core.flags`(但请不要用脚本直接修改它) +9. **设置文件别名:**V2.8新增,您可以修改一个中文名实际指向的英文文件名,从而做到随剧情推进采用不同的系统音效(如上下楼)等效果,如果英文文件名不填则表示恢复到全塔属性中的默认值。 + +### 导致勇士位置变化的指令 + +这类指令都支持填写负坐标、超出地图宽高的坐标或小数坐标(大地图中请慎用小数坐标), + +当勇士在这些异常坐标时【除第一个指令外】都可以正常执行。 + +可以用于一些特殊演出,但请记得在事件结束(玩家恢复行动)前改回正常。 + +1. **勇士前进一格或撞击:**如题,会让勇士像自由行动时一样尝试前进一格。 + * 如果可以前进但前方不可被踏入(如门、怪物、箱子、NPC)则会撞击并触发事件,走到道具、踩灯或路障或用普通事件制作的陷阱等也会触发。 + * 本指令可以正常触发跑毒和阻激夹域捕捉(可以致死),滑冰事件就是在冰上执行了它。 + * 本指令对应`core.moveAction(callback)`函数,但请勿直接调用它。 +2. **无视地形移动勇士:**“动画时间”为每步的时间,不填则取玩家设定值,该值从V2.8起允许在移动过程中修改(最少为16)。 + * 可以勾选“不等待执行完毕”来和后面的指令同时执行,比如让勇士和一个NPC肩并肩走。 + * 本指令不会触发跑毒和阻激夹域捕捉,且会无视地形可进出性、可通行性。 + * 移动过程中不会触发任何事件,就像开启调试模式时按住Ctrl键移动一样(与之不同的是,也可以移动出地图外) + * 勇士后退时,跟随者们会照常前进,数不清楚格子时记得善用地图选点功能浏览地图。 + * V2.8起,支持斜向移动,支持移动过程中单纯转向(步数填0)。 + * 斜向移动时行走图以左右为准,但“后退”依然以勇士朝向为准而不考虑上一步的行走方向(这点和图块的移动不同,勇士不可能斜向后退但图块可能)。 + * 本指令对应`core.eventMoveHero(steps, time, callback)`函数,请注意不是`core.moveHero()` + * 多说一句,您可能会发现勇士在移动时会在行走图的2、4两帧之间反复切换(尤其是在大地图中心时很明显),这和图块以及RPG Maker的行为很不一致而且观感较差,如需改成1234帧循环,请启用“勇士四帧行走动画”插件(V2.8该插件有更新,从V2.7.x接档的用户需要重新抄写一下)。 +3. **跳跃勇士:**可以填写目标点坐标(支持双击从地图选点),坐标允许使用带有冒号缩写量甚至API的表达式。 + * 比如`["core.nextX(2)", "core.nextY(2)"]`(json)表示勇士越过面前一格,即道具“跳跃靴”的效果。 + * V2.7.3起,跳跃的目标坐标支持写增量(dx、dy),如写[4,-2]就会让勇士跳到向右4格向上2格的位置。 + * 跳跃高度和距离有关,原地跳跃的高度只有半格(可在下述函数中修改)。跳跃过程中跟随者消失,跳跃结束时跟随者聚集。 + * 跳跃也支持异步效果(如和NPC一起跳),对应`core.jumpHero(ex, ey, time, callback)`函数,其中`callback`为异步跳跃完毕的回调函数。 + * 跳跃默认没有音效,您可以自行像支援怪和道具“跳跃靴”一样配上音效(具体方法在“插件复写”一节有讲)。 + * 和“无视地形移动勇士”一样,勇士跳跃也会无视目标点的地形和阻激夹域捕捉,不会触发目标点的任何事件。 + * “无视地形移动勇士”和“跳跃勇士”因为经常和图块的这两个行为一起使用进行演出且都没有碰撞效果,因此V2.7.3起移动到了“地图处理类”,请注意。 +4. **楼层切换:**和前面的“楼梯、传送门”绿点事件用法完全一样,但不可穿透。 + * 此指令同样支持双击从地图选点(坐标支持表达式)和在json区填写传送的目标点图块ID(在目标层唯一)再点击变黄的“解析”按钮。 + * 另外,正如本小节开头提到的,本指令比起“楼梯、传送门”事件更多地用于演出,因此您可以填写异常坐标。 + * 楼层ID只能填写常量,如需使用变量,请使用“原生脚本”插入事件。 +5. **位置朝向切换:**“跳跃勇士”不会改变勇士朝向,“楼层切换”又会导致重生怪复活。且这两个都会导致跟随者聚集,所以同楼层内改变勇士位置可以使用本指令(坐标和跳跃一样支持双击从地图选点以及表达式)。 + * 本指令还可以用来让勇士原地转身(不填坐标,这样也不会聚集跟随者),支持4种绝对朝向和4种相对转向。 + +### “数据相关”类的其他杂牌指令 + +以下杂牌指令负责弹窗输入、显伤战斗、道具装备、全局商店、行走图和队伍: + +1. **接受用户输入:**弹窗请求用户输入一个自然数或字符串,提示文字允许使用`${表达式计算}`。 + * 请求输入自然数(支持十六进制)时,负整数会被取绝对值。小数会被向0取整,其他非法输入会变为0,“输入自然数”在录像中会记录为`input:n`。 + * 请求输入字符串时,玩家点击取消则视为输入了空字符串。 + * 输入的结果会保存在值块“`变量:input`(`flag:input`)”中,可供后续处理。 + * 比如样板的生命魔杖就是一个例子,它允许玩家一次使用多个同种道具。 + * 读取“全局存储”这一行为,在录像中和“输入字符串”的记录方式一致(`input2:base64`)。 + * V2.8起,录像回放中如果出现了多余的`input:`或`input2:`,都会警告并跳过。反之,“接受用户输入”事件在录像中缺失了值则会使用0或空字符串并补录。 +2. **更新状态栏和地图显伤:**如题,可以勾选“不检查自动事件”来不检查。 + * 本指令实际执行“脚本编辑——更新状态栏”,即`core.updateStatusBar(doNotCheckAutoEvents);` +3. **强制战斗(点名):**和天降怪物(指定ID,中文替换只支持不重复的中文名)强制战斗。 + * 此指令战后不会从地图删除图块也不会触发各点的战后事件(黄点),但可以触发战后脚本和怪物属性中的(批量)战后事件。 + * 此指令的战斗是强制的,打不过直接死亡(V2.8可以用值块预先判定能否打过)。 + * 此指令一般用于boss战(通过走到某个点或开启某个门来触发),可以制作战前剧情,然后强制战斗。 + * 战后boss不立即消失(从而避免基于漏怪检测的自动事件被误触发),可以继续进行一些演出,如51层魔塔四区骑士队长的逃跑效果。 + * 因为是天降怪物(没有坐标),所以对这只怪物在属性修正以及战损计算等处涉及到怪物坐标的代码一律不起作用。 + * 比如它不会受局部光环的加成,也不会被任何怪支援,也无法被V2.8的“定点设置怪物属性”影响。 + * 另一种强制战斗指令在“地图处理类”,指定的是坐标而不是怪物ID. + * V2.8新增了两种战前事件,两种强制战斗指令都不会触发它们,如需触发,请使用“触发系统事件”指令。 + * 由于V2.8提供了战前事件,因此不再建议使用曾经的“覆盖触发器+天降强制战斗”方式实现战前剧情,因为这样做不会自动存档,对玩家不太友好。 +4. **尝试使用道具和穿脱装备:**使用道具和穿戴装备需要指定ID(中文替换规则和强制战斗一样)。 + * 不能使用怪物手册(请使用“特效声音类”的“呼出怪物手册”指令)和楼层传送器(如果“覆盖楼传事件”则没有关系),使用中心对称飞行器则会跳过确认画面。实际对应`core.useItem(itemId)`函数。 + * 穿脱装备对应`core.loadEquip(equipId)`和`core.unloadEquip(type)`函数。脱下装备需要指定类型,这里只能写自然数不能写名称。 + * 道具使用失败或穿不上装备(比如没有或不满足条件)时会播放音效并提示。 +5. **开关全局商店:**本指令可以设置一个全局商店的启用和禁用状态,设为启用时也支持立即打开。 + * 一般用于商店的实体NPC处,再配合独立开关可以让勇士首次接触时先进行一些对话,然后启用(并打开)全局商店。 + * V2.8新增了值块可以用来判定一个全局商店是否是开启状态。 +6. **更改角色行走图:**如题,文件名必须填写(支持双击选文件)。 + * 文件必须放在`project/images`文件夹并注册,且规格必须符合要求(4帧总宽度至少128px,高度不限。宽高必须为4的倍数)。 + * 如果勾选“不重绘”就不会立即刷新,从而避免大地图视角重置到以勇士为中心。本指令对应`core.setHeroIcon(image, noDraw)`函数。 +7. **跟随者入队和离队:**您可以用这一对指令来让跟随者入队和离队,同样支持双击选文件。本指令对应`core.follow()`和`core.unfollow()`函数。 + * 行走图和勇士的规格要求(尺寸不需要一样)、文件位置和注册方法相同。 + * 离队可以不填文件名表示解散队伍只留勇士,如果填写文件名则尝试踢出队伍中第一个以此为行走图的跟随者。 + * 入队成功后、以及尝试离队后队伍都会聚拢,大地图视角也会重置。 + +## 地图处理类(浅蓝) + +![image](img/maps_waits_raw.jpg) + +这个类型的指令会影响三层地图矩阵的阵元,如果您觉得三层还不够用,“插件编写”(句号键)五图层插件欢迎您。 + +开始介绍前,首先明确一点:改变地图数据不会立即影响事件的进程(自动事件除外)。 + +比如因为踩灯、路障和阻激夹域捕捉怪的分布变化导致勇士行走被妨害的区域发生变化,但不会立即触发妨害效果,而是要等到勇士下次行走。 + +在勇士所在点转变成(显示)一个门/怪物/道具/箱子/楼梯什么的(包括在脚下转变成/显示冰面)都不会立即触发事件,把这些图块移动/跳跃到勇士身上也是。 + +反之,“隐藏事件”(转变图块为0)也不会立即中止当前的事件处理,只是下次不会触发。 + +1. **强制战斗(定点):**这是另一种强制战斗,它指定坐标而不是怪物ID. + * 可以双击从地图选点(只能选当前楼层的,不填则取当前点),也可以用表达式指定坐标,坐标一次只能写【一个点】。 + * 战斗后会自动从地图删除该点的怪物(重生怪则是隐藏),并尝试插入该点的战后事件(黄点)以及怪物属性的(批量)战后事件,成功插入前者时会改变当前点坐标到该点。 + * V2.8新增了两种战前事件,它们无法被“强制战斗”指令触发,如需触发,请使用“触发系统事件”指令。 +2. **开关门:**坐标写法同上(限1个点),同层开门时楼层ID可以略去不写。 + * 关门的位置必须是空地,“需要钥匙”只对同层开门有效。跨层开门请自己加判定,本指令对应`core.openDoor(x, y, needKey, callback)`函数。 + * 这对指令支持所有完整填写了“门信息”的四帧图块(自动元件除外),比如样板自带的三色墙和六色门。 + * 可以勾选“不等待执行完毕”来实现异步效果(如同时开关多个门,具体速度取决于门信息)。 + * 和上面的强制战斗一样,开门后将尝试插入该点的开门后事件(紫点),成功插入时会改变当前点坐标到该点。 +3. **显隐事件和图层块:**这两对指令可以令三层地图矩阵的某些阵元在0与非0之间切换。 + * 还以51层魔塔为例,二楼右下角的小偷在游戏开始时是不显示的,勇士进入四区后才出现。 + * 也就是说这个小偷是一个“普通事件”(红),内容是一些对话和打开35层魔龙处的暗道,只不过没有勾选“启用”。 + * 在适当的时候(这个例子中是和29楼小偷对话后),执行一个“显示MT2层(12,12)点处的事件”指令,就能显示出二楼的小偷。 + * 同理,勇士接触此小偷并处理事件,事件结束前执行一个“隐藏(同时删除)当前点事件,500毫秒”指令,小偷就会从画面中淡出,勇士可以任意在小偷存在过的位置走来走去而不会再触发什么。 + * 所以,一次性陷阱(走到某个地方关上墙/机关门、冒出埋伏怪)在触发后一定要及时隐藏。不然就会反复触发,样板1层有例子可供参考。 + * “显隐事件”都可以双击从地图选点,支持选多个点(只想要楼层ID的话可以点击“复制楼层ID”按钮)。在指令块中可以使用表达式作为坐标(但这样只能写一个点),多个点可以把横坐标依次填在x处而纵坐标对应填在y处(json中写作多行两列的二维数组,但只支持常数),从而同时显隐多个点。 + * 楼层ID省略则取当前楼层,“动画时间”用于同层显隐,从而表现出淡入淡出的效果。 + * “不等待执行完毕”的含义如前,您可以淡入一些点同时淡出另一些点。 + * 值得注意的是,“隐藏事件”还提供了一个“同时删除”勾选框,勾选后无法再用“显示事件”指令显示出来(例如永久移除一个重生怪)。 + * 请注意,隐藏或删除后不影响正在进行的事件流(不会立刻结束),您可以把该点安全地直接转变为别的图块或让别的图块移动/跳跃到此点,比如把箱子/阻击怪推过来(根据该点是否覆盖触发器,推过来以后的行为可能有变化)。 + * 其他两个图层的图块也支持显隐,对游戏性的影响主要体现在显隐背景层的滑冰图块以及两个图层的单向通行箭头/薄墙。坐标和楼层ID的填法同上,只不过这两个就没有淡入淡出效果了。因为其他两个图层的图块不支持什么初始隐藏,如有需要,可以在“开场剧情”中统一提前隐藏。 + * 显隐事件对应`core.showBlock(x, y, floorId)`和`core.hideBlock(x, y, floorId)`,同时删除对应`core.removeBlock(x, y, floorId)`函数;显隐图层块对应`core.maps._triggerFloorImage(type, loc, floorId, callback)` +4. **转变图块和图层块、事件转向:**这组指令可以修改三层地图矩阵的阵元。 + * 先说图层块吧(前景、背景),坐标和楼层的填法同上,不支持淡入淡出。转变图层块后,块的显隐状态不变,原来是显示/隐藏还是显示/隐藏。 + * 接着说事件层,坐标和楼层的填法同上。有几个注意事项: + 1. 新图块为0时“动画时间”全部用来淡出,用于没有普通事件和“楼梯、传送门”的点会视为删除。 + 2. 转变图块也不影响显隐状态,该点原来是显示/隐藏还是显示/隐藏。 + 3. 同层把一种非0图块转变为另一种非0图块(空气墙`airwall`算非0),“动画时间”的前一半用来淡出原图块,后一半用来淡入新图块。 + 4. 同层把0图块转变为非0图块,“动画时间”全部用来淡入。 + * 这对指令可以填写新图块的ID也可以填写数字(如17是空气墙,201起是怪物)。 + * 如需让绑定了“行走图朝向”的图块转向,也可以直接使用“事件转向”指令(和勇士一样支持7种转法),从而避免一个个手写行走图ID的麻烦。 + * 转变图块和图层块对应`core.setBlock(number, x, y, floorId)`和`core.maps._triggerBgFgMap(type, name, loc, floorId, callback)` +5. **设置图块不透明度和特效:**如题,V2.8新增。前者支持渐变效果,可以用来制作亡灵状态的角色或配合楼层贴图实现大型多帧怪物。 +6. **显隐贴图:**这个指令可以用来显隐之前在“楼层属性”中介绍的楼层贴图。 + * 显隐贴图不支持淡入淡出,坐标为贴图左上角的【像素坐标】因此不支持地图选点,楼层ID不填则取当前层。实际执行`core.maps._triggerFloorImage(type, loc, floorId, callback)` +7. **移动和跳跃事件:**这两个指令可以将地图一点的图块转移到另一点。 + * 首先明确一点,这两个指令转移的【仅仅是图块】。起点的七彩事件不会被一同转移(但不消失的情况下定点属性会一同转移),终点的七彩事件也不会被覆盖。 + * 从游戏性上讲,最终的效果是起点被“隐藏事件+同时删除”,勾选“不消失”时终点被“转变图块+显示事件”(终点原来的图块被覆盖)。 + * 比如,阻击怪是“移动后不消失”,捕捉怪是“移动后消失”,支援怪是“跳跃后消失”。 + * 这两个指令一次只能转移一个图块,双击从地图选点选择的是移动的起点和跳跃的终点(跳跃的起点请右击选取)。 + * 任何一个坐标不填都视为当前点,比如“跳跃事件”什么坐标都不填就会让当前点图块原地跳跃。 + * 和无视地形移动勇士一样,“移动事件”也没有碰撞效果,移动过程中会穿过勇士和一切地形。 + * “动画时间”为每步移动的时间或跳跃的用时,以及不勾选“不消失”时淡出的时间。 + * 和“跳跃勇士”一样,“跳跃事件”默认也没有音效,可以自己搭配。 + * V2.7.3起,跳跃的目标坐标支持写增量(dx、dy),如写[4,-2]就会让某个图块跳到其原位置向右4格向上2格的位置。 + * 移动和跳跃实际对应`core.moveBlock(x, y, steps, time, keep, callback)`和`core.jumpBlock(sx, sy, ex, ey, time, keep, callback)`函数。 + * V2.8起,这两个函数在“不消失”的情况下,会将起点处的定点怪物数据也一并移动到终点,这对阻击怪来说是个福音。 + * V2.8起,“移动事件”指令支持斜向移动(行走图仍取左右)、中途变速(速度不得小于16)、中途转向(步数填0),“后退”指令如果用在开头则必须是绑定了“行走图朝向”的图块,如果用在中途则会根据上一步移动/转向的方向后退(注意这一点和勇士不同,勇士是不可能斜向后退的)。 + * “不等待执行完毕”的用法如前,但几个图块再加上勇士以各异的速度和总步数移动时安排起来很麻烦,需要用到下述的“等待三姐妹”。 + +## 等待三姐妹、注释和原生js/json + +在讲解“事件控制”(流程控制)类指令之前,这里插播几个比较杂牌的指令。 + +1. **等待固定时间:**如题,可以用来实现复杂的集体移动、跳跃效果。 + * 比如说51层魔塔一区结尾的骷髅埋伏圈,就是九只骷髅和四扇机关门组成的复杂演出。 + * 每只骷髅开始移动时都“不等待执行完毕”,但又需要“等待一小段时间”再让下一只骷髅开始移动。 + * 本指令还提供了一个勾选项“不可被Ctrl跳过”,如果不勾选此项且当前【没有】正在执行的异步事件(动画、音效、气泡提示不算),则Ctrl可以跳过等待。 +2. **等待所有异步事件执行完毕:**让我们来想象这样一个情景。 + * 您使用了“移动事件”来移动一只怪物到勇士面前,并且“不等待执行完毕”。而下一条指令是“勇士前进一格或撞击”,以期触发战斗。然而因为怪物移动需要时间,两个指令同时执行,所以战斗没法触发。 + * 类似地,如果您在一个异步事件执行完毕之前就结束了整个事件流,让勇士恢复行动,那么可能这些异步事件还没来得及在游戏性方面生效,导致接下来会发生的事取决于玩家操作的时机和勇士的移速。 + * 考虑到录像系统,在录像回放时很多耗时的东西和所有需要用户响应的东西会被跳过,勇士的移速又可以很快(倍速播放),导致回放结果和原游戏不一致。 + * 总之,当您希望确保一些异步事件完全生效后再开始执行新的指令或结束事件流,“等待所有异步事件执行完毕”就是您的不二之选了,事件编辑器也会在发现缺少本指令时弹窗警告。 + * 动画默认会被等待,而音效不会被默认等待;V2.8.1提供了两个勾选框允许您对其进行控制。 + * 另外,您可以随时使用`core.getPlayAnimates()`和`core.getSounds()`获取当前未结束的所有动画和音效的id(支持自变量填名称来筛选)。 +3. **等待用户操作并获得键鼠/触屏信息:**前面提到三种QTE指令,这是最后一种。 + * 之前提到的“确认框”和“选择项”可以复现RPG Maker的回合制战斗,但没法做出更复杂的交互界面,比如技能/天赋树,以及样板的道具商店,这就需要用到本指令了。 + * 本指令会阻塞事件的执行,直到玩家按下键盘上的某个键(滚轮视为PageUp/PageDown键)、或点击【视野】中的某个点、或经过了超时毫秒数(不设置则不限时)。 + * 解除阻塞后,下列值块可能发生变化: + 1. `变量:type`(`flag:type`),解除的原因,0表示按键,1表示点击,-1表示超时。 + 2. `变量:keycode`(`flag:keycode`),按键情况下的键值,48—57为大键盘0—9,65—90为字母键A—Z,其他键请右击该子块查询(查不到的自己百度)。 + 3. `变量:timeout`(`flag:timeout`),进行有效操作后,距离超时的剩余倒计时,可以用来进行一些处理(音游?)。 + 4. `变量:x`和`变量:y`(`flag:x`和`flag:y`),点击情况下所点格子在视野中的相对坐标,一定在0—12或0—14范围。 + 5. `变量:px`和`变量:py`(`flag:px`和`flag:py`),点击情况下所点像素在视野中的相对坐标,一定在0—415或0—479范围。 + * 上述后两项如需转换为大地图中的绝对坐标,请查阅“楼层属性——修改楼层宽高”一节。 + * 您可以根据这些值块去做流程控制,较为简单的场合(如几个键执行同一段指令,或横平竖直的单个矩形点击区)也可直接使用图中的场合块。 + * 其中点击的场合块还支持双击预览判定区,用半透明的红色标出。 + * 默认情况下,一次操作同时满足多个场合块时会依次执行这几个块的内容,除非您使用了下面的“不进行剩余判定”。 + * V2.7.3起,场合块支持“不进行剩余判定”,当满足勾选了此项的场合块时,它下面的其他场合块会全部视为不满足。这允许您实现“点击地图上某个小区域做某些事,点击其他地方做另一些事”而不需要在后者的场合中专门排除前者。 + * V2.8起,提供了超时场合块和自定义条件的场合块,同时还支持“只检测子块”。 + * 在“只检测子块”模式下,如果玩家进行了不符合任何子块的操作,那么会继续阻塞而不是进入下一个指令,超时倒计时也不会重置。 + * 但是如果设置了超时时间,即使未提供超时场合,超时时依然会解除阻塞直接进入下面的指令。 + * 此指令获取到的玩家操作,在录像中会像“接受用户输入数字”一样记录为`input:...`。例如不带超时的键盘按键,就会记录为`input:keycode`。 + * 录像回放时,如果出现了多余的`input:...`,就会跳过并警告。如果遇到本指令但录像中未记录操作或记录了非法/无效操作,就会直接报错。 + * 非法操作指“本指令没有设置超时时间但录像中记录为超时”,无效操作指“本指令设置为只检测子块,但录像中记录的是一个不满足任何子块的操作”。 +4. **添加注释:**在上述的场合块里您还可以看到两个注释块。 + * 注释块在游戏中会被忽略,一般用来帮您记住写的指令是用来做什么的。 + * 极端情况下,样板某些场合会检测指令数组是否为空及其长度,此时您可能需要被迫用注释指令来占位。 +5. **自定义事件:**自带的指令毕竟有限,但事件可以与脚本任意联动。 + * 原生脚本分为两种,原生js和原生json.其中后者会实际执行您通过`core.registerEvent`注册的事件处理函数(即`{"type":"xxx",...}`对应`core.events._action_xxx()`),请注意这类函数在执行结束前务必调用`core.doAction()`函数去继续执行下一条指令。 + * 如果学有余力,还可根据[修改编辑器](editor)来代替原生json,就可以使用调色器、地图选点、选文件等功能啦。 +6. **原生JS脚本执行**:允许您执行任意JS脚本,例如造塔测试时发现事件流程不符合预期,此时可以使用`console.log()`语句在控制台输出日志信息进行检查。 + +原生js的用法就更广了,首先它可以做一些事件做不到的事,比如: + +如果用事件增加道具的话就会有提示和音效,而这有时不是我们需要的,尤其是在设计新道具时将“能否使用”填`"true"`并在使用效果事件中使用失败的场合返还道具时;因此我们可以直接调用脚本: +```js +core.addItem(itemId, n); // 静默增加n个道具,n可为负数,不填视为1 +``` + +其次,受制于Antlr—blockly的数据类型,很多指令的参数只能写常数,比如楼层ID。这时我们就需要在原生js中使用万能的`core.insertAction()`大法,来突破这些限制。 + +比如说我们有一座20层高的塔,楼层ID为MT0—MT19,某个事件中我们想让勇士随机传送到某个楼层,坐标不变。那么就可以使用下列原生js: +```js +core.insertAction({"type": "changeFloor", "floorId": "MT" + core.rand2(20)}) +``` + +连续使用多条json指令时,请先声明一个空的指令数组(`var todo = [];`)然后将需要的指令分批追加到其末尾(`core.push(todo, [...]);`),最后再一次性`core.insertAction(todo);`插入执行,可以选择插入到剩余事件的开头或结尾。 + +另外您可能会问:既然都用js了,为什么不直接用之前提到的`core.changeFloor()`函数呢? + +这是因为,原生js在不勾选“不自动执行下一个事件”的情况下,**只能使用瞬间完成的函数(或者drawTip、动画和音效这种虽然耗时但不影响游戏性的),不能使用任何异步函数(包括阻塞直到玩家操作的)**! + +系统常见可能会被造塔用到的API都在[API列表](api)中给出,一般而言异步API的最后一个自变量都叫`callback`(回调)。在勾选“不自动执行下一个事件”的情况下,原生js可以使用一个异步API,只需将其`callback`参数填`core.doAction`,请谨慎使用。 + +比如说,我们知道天降强制战斗没有坐标所以不受光环等影响也无法触发(单点)战后事件,那捕捉怪的战斗是怎么实现的呢?答案是在原生js中使用了异步的`core.battle(id, x, y, force, callback)`函数,这里`force`填`true`表示强制战斗,`callback`填`core.doAction`表示战斗结束后继续处理事件。 + +熟练运用后,还可以使用多个异步API,每个以下一个作为回调。 + +## 事件控制类(深蓝) + +![image](img/flowctrl.jpg) + +在三个QTE指令中,我们已经初见了流程控制的端倪。只不过,它们的流程走向是由玩家的选择直接左右的。能否通过对值块的比较等操作自动走向不同的流程分支呢?答案是肯定的。 + +1. **条件分歧:**和“显示确认框”很相似,只不过这个是自动的。 + * 和js的`if (...) {...;} else {...;}`完全等价,本指令需要内嵌一个值块(可以填逻辑表达式,常常是两个值块的比较,比如某道具是否足够)。 + * 当此值块的值不为`false、0、null、undefined、NaN和空字符串`(即之前提到的六大广义`false`)时,执行“如果:”和“否则:”之间的指令,当此值块的值为这六个值之一时,执行“否则:”后面的指令。 +2. **多重分歧:**本指令和js的`switch`语句有一定差别,即使您有js或其他编程语言基础,也请务必仔细阅读接下来的说明。 + * 事件执行到多重分歧指令时,会先计算一次“判别值”,结果记作`key`.然后准备一个空的指令数组,记作`list`; + * 接下来从上到下扫描每个场合块,如果一个场合块的“值”为`default`或计算结果和`key`相等,就把这个场合的指令组追加到`list`的末尾,即`core.push(list, [...]);`。 + * 每次成功追加后,如果被追加的此场合块未勾选“不跳出”,则无视下面的所有场合块直接结束扫描,否则继续扫描下一个场合块。 + * 扫描完全结束后,调用`core.insertAction(list);`付诸执行。 + * 所以各场合块的顺序一定要安排好,比如`default`(可以没有此场合)如果不勾选“不跳出”则一定要放在最后。 + * 多重分歧常见的用法是,判别值填未知量,所有场合块都不勾选“不跳出”且“值”填写为两两不同的常量(如果有相同的则只有第一个有效)。从而执行唯一的相等场合,没有的话还可以补一个`default`场合,类似js的`else if`语法。 + * 或者,判别值填常量,各场合块填未知量且都勾选“不跳出”。把所有相等的场合筛选出来依次执行,类似js的`filter`语法。 + * 第二种用法中,最常见的是判别值填`true`,各场合块填逻辑表达式。此时如果都勾选“不跳出”并且不提供`default`场合,就相当于一堆没有`else`的if语句。 + * 而如果在这个基础上改成都不勾选“不跳出”就相当于正常的`else if`语句(可以把`default`场合提供在最后或不提供),而且比起上面的“条件分歧”指令不需要嵌套了。 +3. **循环遍历(计数):**使用临时变量A—Z(事件流结束时会和arg+纯数字的变量一起被清空)可以进行循环遍历,它们都属于前置条件循环。 + * 循环遍历有两种,第一种是计数型。`从`和`到`之间的框填写临时变量的初始值,`到`和`步增`之间的框填写临时变量允许的边界值。 + * 每轮循环开始前会【重新计算】边界值和步增,如果临时变量位于界外(减去边界值后和步增同号)则跳出循环。 + * 每轮循环结束时,临时变量会加上步增值(可以为负数,请注意变号问题)。 +4. **循环遍历(迭代):**这是另一种循环遍历,可以迭代一个列表。 + * 每轮循环前,会检查列表是否已空。已空则跳出循环,否则将列表的第一项【移除并代入】临时变量。 + * 使用此项时请注意,列表中的字符串不会被自动解释为冒号缩写量。显示文章等处如有需求,请使用`${core.calValue(temp:A)}`来计算。 + * 分歧和循环都可以嵌套,嵌套循环遍历请给内外层使用不同的临时变量。 +5. **前/后置条件循环:** + * 前置条件循环和一个没有“否则”分支的条件分歧很相似,区别仅仅在于每次执行完分支内容后都会【跳转】回到条件检测之前,从而再次检测条件,还满足就再执行,如此往复。 + * 而后置条件循环比起前置,区别在于第一轮循环是无视条件强制执行的。 + * 它们对应的js语法分别为`while (...) {...;}`和`do {...;} while(...);` +6. **跳出当前循环:**如题,遇到此指令时,将检测当前是否在某个循环中(还包括自动事件、公共事件、全局商店),并跳出所在的【最内层】循环(V2.7.3起支持指定跳出层数)。如果不在任何循环中,则什么也不做。大致相当于js的`break;` +7. **提前结束本轮循环:**生效范围同上,不过是结束最内层的【本轮】循环。换言之: + * 对一般的前/后置条件循环会立刻跳转到下次检测条件前; + * 对循环遍历(计数)会立刻跳转到下次计算边界和步增并累加前; + * 对循环遍历(迭代)会立刻跳转到下次检查列表是否为空前。 + * 还可用来“重启新版商店或道具商店”。若不在任何循环中,则什么也不做。 + * 大致相当于js的`continue;`,V2.7.3起支持指定层数。 +8. **立刻结束当前事件:**此指令将清空临时变量(以`@temp@`开头)和参数变量(`arg+纯数字`),清空事件列表,中断一切事件处理,恢复勇士行动。 + * V2.8起,您可以在两种“战前事件”中使用它取消战斗。 +9. **触发系统事件:**模拟勇士撞击/踩踏本楼层的某个点(实际执行`core.trigger(x, y, callback)`),支持双击从地图选点。 + * 该指令把触发的事件(包括图块属性的“碰触脚本/碰触事件”,不包括阻激夹域捕捉和血网)插队到当前事件列表中。 + * 譬如该点是道具则会捡起来,是怪物则会触发战前事件然后战斗,是门则会尝试开门,还会连带触发对应的`afterXxx`事件。 + * 如果该点是普通事件(红)则会触发之,是楼梯事件(绿)则会直接触发传送(目标点是“保持不变”或三种对称点也以勇士位置而不是楼梯位置计算)。 + * 该点是路障则会触发对应的毒衰咒(血网除外),是踩灯则会把勇士脚下变成踩过的灯。滑冰在背景层,所以无法触发。至于推箱子,请自行探索。 + * V2.8起,您可以使用“强制战斗(定点)”指令跳过战前事件强制与地图上某个点的怪物战斗,也可以用“触发系统事件”指令指定某个有怪物的点然后先触发战前事件再战斗,请注意区分。 +10. **插入公共事件:**如题,“参数列表”为一数组,其各项会被依次代入值块`变量:arg1`、`变量:arg2`...而`变量:arg0`会记录公共事件的名称。 + * 实际执行`core.insertCommonEvent(name, args, x, y, callback, addToLast)` +11. **插入事件:**此指令可以无视目标点启用与否,跨楼层插入任何点的普通事件(不需要覆盖触发器)、战前事件、afterXxx事件执行。支持双击从地图选点。 + +## 特效声音类(褐色) + +![image](img/blockly.jpg) + +这个类别的指令会负责动画、视角、色调、天气、音频、存读档等其他一些细节。 + +1. **画面震动:**会让画面震动,可以选择四种振动方向,可以指定总时间、震动速度和振幅。 + * 实际执行`core.vibrate(direction, time, speed, power, callback)`函数。 +2. **显示动画:**如题,可以双击选文件并预览(预览的坐标锁定为视野中心)和试听/修改音效。 + * 如需从地图选点请右击指令块,也可用另一个指令让动画跟随勇士移动。 + * 坐标不写则取当前点,如果勾选“相对窗口坐标”则坐标应填写为0—12或0—14表示视野中的相对坐标(如13×13样板填两个6表示视野中心)。 + * 如果不勾选“不等待执行完毕”,则等待的实际时长只取决于动画,和音效无关。 + * 实际调用`core.drawAnimate(name, x, y, alignWindow, callback)`和`core.drawHeroAnimate(name, callback)` +3. **设置视角:**设置视角支持双击从地图选点,不填坐标则重置视角。动画时间指移动的总时间,支持四种变速移动。 + * 勇士的`status:animate`为`true`(原地抖动开启)时,禁止使用本指令! + * V2.7.3起,坐标支持写增量,如[4,-2]表示视角直线移动到向右4格、向上2格的位置。 + * 请注意,勇士重绘时(`core.drawHero()`函数)视角也会随之重置。所以视角变化后勇士的坐标、朝向、显隐、行走图(事件和API都提供了不重绘参数)和跟随者情况暂时都不要动。 + * 实际调用`core.setViewport(x, y)`和`core.moveViewport(x, y, moveMode, time, callback)`,其中前者的自变量为【像素坐标】且不必为32的倍数,必要时可作为原生js使用来实现特殊的演出。 + * V2.8.1起,支持暂时锁定视角,视角锁定期间,只有上下楼后才会将视角重置到勇士身上(但依然保持锁定)。 +4. **显隐状态栏:**如题,如果隐藏状态栏期间勇士需要恢复行动,则建议不隐藏竖屏工具栏以方便手机玩家。 + * 实际调用`core.showStatusBar()`和`core.hideStatusBar(showToolbox)` +5. **设置勇士不透明度:**如题,动画时间为淡入淡出时间,异步勾选框用法如前。 + * 实际调用`core.setHeroOpacity(opacity, moveMode, time, callback)` +6. **更改画面色调:**色调可以用调色器调配,“动画时间”为渐变的总时间。 + * 请注意渐变是在RGBA颜色空间中直线运动(V2.8.1支持加速度),因此效果可能不好,画面闪烁同理。 + * 如需在勇士自由行动时反复执行,请使用并行脚本或自我回调。 +7. **恢复画面色调:**指将更改后的色调恢复到楼层的默认色调,更改当前层的默认色调后您可以使用此指令刷新。 +8. **画面闪烁:**“单次时间”必须为3的倍数,前1/3时间用于将画面色调转变为目标色调,后2/3时间用于恢复当前色调,执行次数如题。 + * 实际调用`screenFlash(color, time, times, callback)` +9. **更改天气:**如题,可以选择“无、雨、雪、雾、晴”之一,强度需填小于等于10的正整数。 +10. **播放背景音乐:**如题,可以双击选文件并试听(支持别名),并指定开始播放的秒数。 + * 当在游戏中触发楼层切换时(包括读档),如果`flag:__color__、flag:__weather__、flag:__bgm__`这三个值块没有值,游戏当时的画面色调、天气、背景音乐就会变为楼层属性中的这三个设置项。 + * 以上几个指令都提供了“持续到下个本事件”勾选框,勾选后,本次设置的值将会计入这三个值块。它们的拦截行为在“脚本编辑——切换楼层中”。 + * 若不勾选,或恢复画面色调、设置天气为无(晴),就会清除对应的值块。您也可以随时对这三个值块进行手动干预。 + * V2.8.1起,支持由作者注册新的自定义天气,新天气在下拉框中默认未提供,可以手动填写在json区再点击“解析”按钮。 +11. **暂停和恢复背景音乐:**如题,暂停时会记录暂停在了第几秒,恢复时可以选择从这个秒数继续或从头重播。 +12. **预加载背景音乐和释放其缓存:**在线游戏使用,前者的原理是静音播放。 + * 最多同时缓存8首背景音乐(由`libs/core.js`控制),会自动释放最久未使用的,但您也可以手动释放。 +13. **播放音效和停止所有音效:**如题,开播一个音效的同时可以停止其他的。V2.8播放系统音效可以从下拉框选取,其他音效也支持使用别名。 + * V2.8起,播放音效支持“等待播放完毕”,支持同时“变调且变速”。100表示正常速度和音调,可以填30到300之间的值。 +14. **设置音量:**只对背景音乐有效,音量为小于等于100的自然数(玩家设置值其实被开了平方),渐变时间如题。 +15. **设置背景音乐播放速度和音调:**V2.8新增,需要填30到300之间的值。100表示正常速度和音调,可以只改变速度(RPG Maker做不到哦),也可以同时改变速度和音调。 + * 该项的效果不会进存档,必要时您可以配合使用“禁止存档”指令。 +16. **呼出怪物手册和SL界面:** + * 呼出手册只在勇士持有手册时生效,关闭手册后事件继续。 + * 呼出存档界面最多只能存一个档然后事件继续(读档后事件现场会恢复)。 + * 呼出读档界面如果不读档则事件继续,录像回放中这三条指令都被忽略。 +17. **自动存档:**读档后也会恢复事件现场,录像回放中会照常存档。 + * 如不勾选“不提示”则会`core.drawTip(“已自动存档”)`(即使在下面禁止的情况下!),此指令一般用于选择项/确认框之前。 +18. **是否禁止存档:**该项允许暂时禁止玩家存档(包括撞怪撞门的自动存档,不包括上面的呼出存档界面和事件中自动存档的指令),从而做出RPG中常见的“迷宫中只有特定位置可以存档,或者必须消耗道具进行存档”的效果。 + +## UI绘制类(瞬间) + +![image](img/uievent.jpg) + +这个类别的指令全部是瞬间完成的,有一一对应的API,请放心使用,除“绘制多行文本”外都可以逐个双击预览(多行文本需要右击预览)。游戏中,这些指令都是画在`uievent`层的。 + +1. **UI绘制并预览:**您可以把一堆UI绘制类指令塞进去然后双击黄框整体预览。 +2. **清除画布:**擦除`uievent`层的一部分,x和y为左上角坐标。 + * 四个参数都支持使用表达式,任何一个参数不填都会删除整个`uievent`层。 + * 实际调用`core.clearMap("uievent",x,y,width,height)`和`core.deleteCanvas("uievent")`,熟练后对其他画布使用也是可以的。 +3. **设置画布属性:** + 1. 字体:`italic和bold`表示斜体和加粗,可省略,字号和字体用于绘制文本。 + 2. 填充样式:绘制实心图形时的默认填色,可用调色器调配。 + 3. 边框样式:绘制空心图形时的默认边框颜色,可用调色器调配。 + 4. 线宽度:绘制线段、箭头和空心图形时的默认线宽,单位为像素。 + 5. 不透明度:不大于1的非负数,此项为“画笔”的不透明度。只影响接下来画的内容,已经画上去的不受影响。 + 6. 对齐:绘制单行文本的对齐方式,左对齐、左右居中、右对齐。 + 7. 基准线:绘制单行文本的基准线,有六种写法。绘制单行文本的坐标其实是文本的基准点, + 8. z值:初始为135,z值较大的画布将覆盖较小的,详见“个性化”。闪烁光标的z值总是比上述的值大1,即默认为136. +4. **绘制文本:** + * 这里仅绘制单行文本,坐标为基准点的像素坐标,需配合上述“对齐”和“基准线”使用。 + * 如果设置了最大宽度,那么在超出此宽度时就会保持比例压缩到这个宽度。 + * 正文只支持`${js表达式求值}`和`\r[变色]`,不支持其他一切转义序列,更不能手动换行。 + * 实际执行`core.fillText("uievent", text, x, y, style, font, maxWidth)` +5. **绘制描边文本:**同上,但不支持限宽,描边效果同状态栏数字的黑边。 + * 底层实现为`ctx.strokeText()`,描边颜色可以自己指定。 + * 本指令对应的js函数为`core.ui.fillBoldText('uievent', text, x, y, style, font)` +6. **绘制多行文本:**双击进入多行编辑,预览请右击。 + * 起点像素为左上角,只有设置了最大行宽才会对齐、居中和自动换行。 + * 如果不设置颜色和字号,就会采用“设置剧情文本的属性”中的正文设置。 + * 不设置行距就会采用字体大小的1.3倍,建议采用偶数字号和1.5倍行距。 + * 多行文本不支持字体样式的设置,使用的是全塔属性中的全局字体`Verdana`, + * 如有需要,请使用“设置全局属性”指令来设置字体样式。 +7. **绘制几何图形:**对应的js函数为`core.ui.fillXxx()`和`core.ui.strokeXxx()` +8. **绘制图片:**同“显示图片”指令但功能有出入,实际执行`core.drawImage('uievent',img,x,y,w,h,x1,y1,w1,h1)` +9. **绘制图标:**支持图块id和系统图标,支持伸缩和选择哪一帧。支持32×32和32×48两种尺寸,实际执行`core.drawIcon(name, id, x, y, w, h, frame)` +10. **绘制背景图:**背景色支持颜色数组,也支持类似`winskin.png`的图片名。使用图片时的不透明度以及纯色背景时的边框颜色,由“设置画布属性”指定。本指令对应的js函数为`core.ui.drawBackground(left, top, right, bottom, posInfo)` +11. **绘制和清除闪烁光标:**如题,光标的z值总是比`uievent`层大1. +12. **设置画布特效:**V2.8新增,允许给画布设置像图块一样的“虚化、色相、灰度、反色、阴影”属性。 + +========================================================================================== + +[继续阅读下一章:个性化](personalization) diff --git a/item.d.ts b/item.d.ts new file mode 100644 index 0000000..f044ffd --- /dev/null +++ b/item.d.ts @@ -0,0 +1,300 @@ +interface Item> { + /** + * 道具id + */ + id: I; + + /** + * 道具的类型 + */ + cls: ItemClsOf; + + /** + * 道具的名称 + */ + name: string; + + /** + * 道具的描述 + */ + text?: string; + + /** + * 是否在道具栏隐藏 + */ + hideInToolBox: boolean; + + /** + * 装备信息 + */ + equip: ItemClsOf extends 'equips' ? Equip : never; + + /** + * 回放使用时是否不先打开道具栏再使用 + */ + hideInReplay: boolean; + + /** + * 即捡即用效果 + */ + itemEffect?: string; + + /** + * 即捡即用道具捡过之后的提示 + */ + itemEffectTip?: string; + + /** + * 使用道具时执行的事件 + */ + useItemEvent?: MotaEvent; + + /** + * 使用道具时执行的代码 + */ + useItemEffect?: string; + + /** + * 能否使用道具 + */ + canUseItemEffect?: string | boolean; +} + +interface EquipBase { + /** + * 装备增加的数值 + */ + value: Record, number>; + + /** + * 装备增加的百分比 + */ + percentage: Record, number>; +} + +interface Equip extends EquipBase { + /** + * 可以装备到的装备孔 + */ + type: number | string; + + /** + * 动画信息 + */ + animate: AnimationIds; + + /** + * 穿上装备时执行的事件 + */ + equipEvent?: MotaEvent; + + /** + * 脱下装备时执行的事件 + */ + unequipEvent?: MotaEvent; +} + +/** + * 道具相关的内容 + */ +interface Items { + /** + * 获得所有道具 + */ + getItems(): { + [P in AllIdsOf<'items'>]: Item

      ; + }; + + /** + * 执行即捡即用类的道具获得时的效果 + * @example core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果 + * @param itemId 道具id + * @param itemNum 道具数量,默认为1 + */ + getItemEffect(itemId: AllIdsOf<'items'>, itemNum?: number): void; + + /** + * 即捡即用类的道具获得时的额外提示 + * @example core.getItemEffectTip(redPotion) // (获得 红血瓶)',生命+100' + * @param itemId 道具id + * @returns 图块属性itemEffectTip的内容 + */ + getItemEffectTip(itemId: AllIdsOf<'items'>): string; + + /** + * 使用一个道具 + * @example core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调 + * @param itemId 道具id + * @param noRoute 是否不计入录像,快捷键使用的请填true,否则可省略 + * @param callback 道具使用完毕或使用失败后的回调函数,好像没什么意义吧( + */ + useItem( + itemId: ItemIdOf<'tools' | 'constants'>, + noRoute?: boolean, + callback?: () => void + ): void; + + /** + * 检查能否使用某种道具 + * @example core.canUseItem('pickaxe') // 能否使用破墙镐 + * @param itemId 道具id + * @returns true表示可以使用 + */ + canUseItem(itemId: AllIdsOf<'items'>): boolean; + + /** + * 统计某种道具的持有量 + * @example core.itemCount('yellowKey') // 持有多少把黄钥匙 + * @param itemId 道具id + * @returns 该种道具的持有量,不包括已穿戴的装备 + */ + itemCount(itemId: AllIdsOf<'items'>): number; + + /** + * 检查主角是否持有某种道具(不包括已穿戴的装备) + * @example core.hasItem('yellowKey') // 主角是否持有黄钥匙 + * @param itemId 道具id + * @returns true表示持有 + */ + hasItem(itemId: AllIdsOf<'items'>): boolean; + + /** + * 检查主角是否穿戴着某件装备 + * @example core.hasEquip('sword5') // 主角是否装备了神圣剑 + * @param itemId 装备id + * @returns true表示已装备 + */ + hasEquip(itemId: ItemIdOf<'equips'>): boolean; + + /** + * 检查主角某种类型的装备目前是什么 + * @example core.getEquip(1) // 主角目前装备了什么盾牌 + * @param equipType 装备类型,自然数 + * @returns 装备id,null表示未穿戴 + */ + getEquip(equipType: number): ItemIdOf<'equips'> | null; + + /** + * 设置某种道具的持有量 + * @example core.setItem('yellowKey', 3) // 设置黄钥匙为3把 + * @param itemId 道具id + * @param itemNum 新的持有量,可选,自然数,默认为0 + */ + setItem(itemId: AllIdsOf<'items'>, itemNum?: number): void; + + /** + * 静默增减某种道具的持有量 不会更新游戏画面或是显示提示 + * @example core.addItem('yellowKey', -2) // 没收两把黄钥匙 + * @param itemId 道具id + * @param itemNum 增加量,负数表示减少 + */ + addItem(itemId: AllIdsOf<'items'>, itemNum?: number): void; + + /** + * @deprecated 使用addItem代替。 + * 删除某个物品一定的数量,相当于addItem(itemId, -n); + * @param itemId 道具id + * @param itemNum 减少量,负数表示增加 + */ + removeItem(itemId?: AllIdsOf<'items'>, itemNum?: number): void; + + /** + * 根据类型获得一个可用的装备孔 + * @param equipId 道具名称 + */ + getEquipTypeByName(name?: ItemIdOf<'equips'>): number; + + /** + * 判定某件装备的类型 + * @example core.getEquipTypeById('shield5') // 1(盾牌) + * @param equipId 装备id + * @returns 类型编号,自然数 + */ + getEquipTypeById(equipId: ItemIdOf<'equips'>): number; + + /** + * 检查能否穿上某件装备 + * @example core.canEquip('sword5', true) // 主角可以装备神圣剑吗,如果不能会有提示 + * @param equipId 装备id + * @param hint 无法穿上时是否提示(比如是因为未持有还是别的什么原因) + * @returns true表示可以穿上,false表示无法穿上 + */ + canEquip(equipId: ItemIdOf<'equips'>, hint?: boolean): boolean; + + /** + * 尝试穿上某件背包里的装备并提示 + * @example core.loadEquip('sword5') // 尝试装备上背包里的神圣剑,无回调 + * @param equipId 装备id + * @param callback 穿戴成功或失败后的回调函数 + */ + loadEquip(equipId: ItemIdOf<'equips'>, callback?: () => void): void; + + /** + * 脱下某个类型的装备 + * @example core.unloadEquip(1) // 卸下盾牌,无回调 + * @param equipType 装备类型编号,自然数 + * @param callback 卸下装备后的回调函数 + */ + unloadEquip(equipType: number, callback?: () => void): void; + + /** + * 比较两件(类型可不同)装备的优劣 + * @example core.compareEquipment('sword5', 'shield5') // 比较神圣剑和神圣盾的优劣 + * @param compareEquipId 装备甲的id + * @param beComparedEquipId 装备乙的id + * @returns 两装备的各属性差,甲减乙,0省略 + */ + compareEquipment>( + compareEquipId: F, + beComparedEquipId: Exclude, F> + ): EquipBase; + + /** + * 保存当前套装 + * @example core.quickSaveEquip(1) // 将当前套装保存为1号套装 + * @param index 套装编号,自然数 + */ + quickSaveEquip(index: number): void; + + /** + * 快速换装 + * @example core.quickLoadEquip(1) // 快速换上1号套装 + * @param index 套装编号,自然数 + */ + quickLoadEquip(index: number): void; + + /** + * 设置某个装备的属性并计入存档 + * @example core.setEquip('sword1', 'value', 'atk', 300, '+='); // 设置铁剑的攻击力数值再加300 + * @param equipId 装备id + * @param valueType 增幅类型,只能是value(数值)或percentage(百分比) + * @param name 要修改的属性名称,如atk + * @param value 要修改到的属性数值 + * @param operator 操作符,如+=表示在原始值上增加 + * @param prefix 独立开关前缀,一般不需要 + */ + setEquip( + equipId: ItemIdOf<'equips'>, + valueType: 'value' | 'percentage', + name: keyof SelectType, + value: number, + operator?: MotaOperator, + prefix?: string + ): void; + + /** + * 真正的穿脱装备 + * @param type 装备孔 + * @param loadId 装上的装备 + * @param unloadId 脱下的装备 + * @param callback 回调函数 + */ + _realLoadEquip( + type: number, + loadId?: ItemIdOf<'equips'>, + unloadId?: ItemIdOf<'equips'>, + callback?: () => void + ): void; +} + +declare const items: new () => Items; diff --git a/items.js b/items.js new file mode 100644 index 0000000..0c921e0 --- /dev/null +++ b/items.js @@ -0,0 +1,3385 @@ +var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = +{ + "yellowKey": { + "cls": "tools", + "name": "黄钥匙", + "text": "可以打开一扇黄门", + "hideInToolbox": true + }, + "blueKey": { + "cls": "tools", + "name": "蓝钥匙", + "text": "可以打开一扇蓝门", + "hideInToolbox": true + }, + "redKey": { + "cls": "tools", + "name": "红钥匙", + "text": "可以打开一扇红门", + "hideInToolbox": true + }, + "redGem": { + "cls": "items", + "name": "红宝石", + "text": "攻击+${core.values.redGem}", + "itemEffect": "core.status.hero.atk += core.values.redGem * core.status.thisMap.ratio", + "itemEffectTip": ",攻击+${core.values.redGem * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.atk += core.values.redGem", + "canUseItemEffect": "true" + }, + "blueGem": { + "cls": "items", + "name": "蓝宝石", + "text": ",防御+${core.values.blueGem}", + "itemEffect": "core.status.hero.def += core.values.blueGem * core.status.thisMap.ratio", + "itemEffectTip": ",防御+${core.values.blueGem * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.def += core.values.blueGem", + "canUseItemEffect": "true" + }, + "greenGem": { + "cls": "items", + "name": "绿宝石", + "text": ",护盾+${core.values.greenGem}", + "itemEffect": "core.status.hero.mdef += core.values.greenGem * core.status.thisMap.ratio", + "itemEffectTip": ",护盾+${core.values.greenGem * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.mdef += core.values.greenGem", + "canUseItemEffect": "true" + }, + "yellowGem": { + "cls": "items", + "name": "黄宝石", + "text": "可以进行加点", + "itemEffect": "core.status.hero.hp+=1000;core.status.hero.atk+=6;core.status.hero.def+=6;core.status.hero.mdef+=10;", + "itemEffectTip": ",全属性提升", + "useItemEvent": [ + { + "type": "choices", + "choices": [ + { + "text": "攻击+1", + "action": [ + { + "type": "setValue", + "name": "status:atk", + "operator": "+=", + "value": "1" + } + ] + }, + { + "text": "防御+2", + "action": [ + { + "type": "setValue", + "name": "status:def", + "operator": "+=", + "value": "2" + } + ] + }, + { + "text": "生命+200", + "action": [ + { + "type": "setValue", + "name": "status:hp", + "operator": "+=", + "value": "200" + } + ] + } + ] + } + ], + "canUseItemEffect": "true" + }, + "redPotion": { + "cls": "items", + "name": "红血瓶", + "text": ",生命+${core.values.redPotion}", + "itemEffect": "core.status.hero.hp += core.values.redPotion * core.status.thisMap.ratio", + "itemEffectTip": ",生命+${core.values.redPotion * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.hp += core.values.redPotion", + "canUseItemEffect": "true" + }, + "bluePotion": { + "cls": "items", + "name": "蓝血瓶", + "text": ",生命+${core.values.bluePotion}", + "itemEffect": "core.status.hero.hp += core.values.bluePotion * core.status.thisMap.ratio", + "itemEffectTip": ",生命+${core.values.bluePotion * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.hp += core.values.bluePotion", + "canUseItemEffect": "true" + }, + "yellowPotion": { + "cls": "items", + "name": "黄血瓶", + "text": ",生命+${core.values.yellowPotion}", + "itemEffect": "core.status.hero.hp += core.values.yellowPotion * core.status.thisMap.ratio", + "itemEffectTip": ",生命+${core.values.yellowPotion * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.hp += core.values.yellowPotion", + "canUseItemEffect": "true" + }, + "greenPotion": { + "cls": "items", + "name": "绿血瓶", + "text": ",生命+${core.values.greenPotion}", + "itemEffect": "core.status.hero.hp += core.values.greenPotion * core.status.thisMap.ratio", + "itemEffectTip": ",生命+${core.values.greenPotion * core.status.thisMap.ratio}", + "useItemEffect": "core.status.hero.hp += core.values.greenPotion", + "canUseItemEffect": "true" + }, + "sword0": { + "cls": "items", + "name": "破旧的剑", + "text": "一把已经生锈的剑", + "equip": { + "type": 0, + "animate": "sword", + "value": { + "atk": 0 + } + }, + "itemEffect": "core.status.hero.atk += 0", + "itemEffectTip": ",攻击+0" + }, + "sword1": { + "cls": "tools", + "name": "铁剑", + "text": "一把很普通的铁剑", + "equip": null, + "itemEffect": "core.status.hero.atk += 10", + "itemEffectTip": ",攻击+10", + "useItemEvent": [], + "useItemEffect": "core.status.hero.atk += 10;" + }, + "sword2": { + "cls": "items", + "name": "银剑", + "text": "一把很普通的银剑", + "equip": { + "type": 0, + "animate": "sword", + "value": { + "atk": 20 + } + }, + "itemEffect": "core.status.hero.atk += 20", + "itemEffectTip": ",攻击+20" + }, + "sword3": { + "cls": "items", + "name": "骑士剑", + "text": "一把很普通的骑士剑", + "equip": { + "type": 0, + "animate": "sword", + "value": { + "atk": 40 + } + }, + "itemEffect": "core.status.hero.atk += 40", + "itemEffectTip": ",攻击+40" + }, + "sword4": { + "cls": "items", + "name": "圣剑", + "text": "一把很普通的圣剑", + "equip": { + "type": 0, + "animate": "sword", + "value": { + "atk": 80 + } + }, + "itemEffect": "core.status.hero.atk += 80", + "itemEffectTip": ",攻击+80" + }, + "sword5": { + "cls": "items", + "name": "神圣剑", + "text": "一把很普通的神圣剑", + "equip": { + "type": 0, + "animate": "sword", + "value": { + "atk": 160 + } + }, + "itemEffect": "core.status.hero.atk += 100", + "itemEffectTip": ",攻击+100" + }, + "shield0": { + "cls": "items", + "name": "破旧的盾", + "text": "一个很破旧的铁盾", + "equip": { + "type": 1, + "value": { + "def": 0 + } + }, + "itemEffect": "core.status.hero.def += 0", + "itemEffectTip": ",防御+0" + }, + "shield1": { + "cls": "items", + "name": "铁盾", + "text": "一个很普通的铁盾", + "equip": { + "type": 1, + "value": { + "def": 10 + } + }, + "itemEffect": "core.status.hero.def += 10", + "itemEffectTip": ",防御+10" + }, + "shield2": { + "cls": "items", + "name": "银盾", + "text": "一个很普通的银盾", + "equip": { + "type": 1, + "value": { + "def": 20 + } + }, + "itemEffect": "core.status.hero.def += 20", + "itemEffectTip": ",防御+20" + }, + "shield3": { + "cls": "items", + "name": "骑士盾", + "text": "一个很普通的骑士盾", + "equip": { + "type": 1, + "value": { + "def": 40 + } + }, + "itemEffect": "core.status.hero.def += 40", + "itemEffectTip": ",防御+40" + }, + "shield4": { + "cls": "items", + "name": "圣盾", + "text": "一个很普通的圣盾", + "equip": { + "type": 1, + "value": { + "def": 80 + } + }, + "itemEffect": "core.status.hero.def += 80", + "itemEffectTip": ",防御+80" + }, + "shield5": { + "cls": "items", + "name": "神圣盾", + "text": "一个很普通的神圣盾", + "equip": { + "type": 1, + "value": { + "def": 100, + "mdef": 100 + } + }, + "itemEffect": "core.status.hero.def += 100;core.status.hero.mdef += 100", + "itemEffectTip": ",防御+100,护盾+100" + }, + "superPotion": { + "cls": "items", + "name": "圣水", + "itemEffect": "core.status.hero.hp *= 2", + "itemEffectTip": ",生命值翻倍", + "useItemEffect": "core.status.hero.hp *= 2;core.playSound('回血');", + "canUseItemEffect": "true", + "text": "生命值翻倍" + }, + "book": { + "cls": "constants", + "name": "怪物手册", + "text": "可以查看当前楼层各怪物属性", + "hideInToolbox": true, + "useItemEffect": "core.ui.drawBook(0);", + "canUseItemEffect": "true" + }, + "fly": { + "cls": "constants", + "name": "楼层传送器", + "text": "可以自由往来去过的楼层", + "hideInReplay": true, + "hideInToolbox": true, + "useItemEffect": "core.ui.drawFly(core.floorIds.indexOf(core.status.floorId));", + "canUseItemEffect": "(function () {\n\tif (core.flags.flyNearStair && !core.nearStair()) return false;\n\treturn core.status.maps[core.status.floorId].canFlyFrom;\n})();" + }, + "coin": { + "cls": "constants", + "name": "幸运金币", + "text": "持有时打败怪物可得双倍金币" + }, + "freezeBadge": { + "cls": "constants", + "name": "冰冻徽章", + "text": "可以将面前的熔岩变成平地", + "useItemEffect": "(function () {\n\tvar success = false;\n\n\tvar snowFourDirections = false; // 是否多方向雪花;如果是将其改成true\n\tif (snowFourDirections) {\n\t\t// 多方向雪花\n\t\tfor (var direction in core.utils.scan) { // 多方向雪花默认四方向,如需改为八方向请将这两个scan改为scan2\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (core.getBlockId(nx, ny) == 'lava') {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (core.getBlockId(core.nextX(), core.nextY()) == 'lava') {\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\tif (success) {\n\t\tcore.playSound('打开界面');\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\t} else {\n\t\tcore.playSound('操作失败');\n\t\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name, itemId);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n})();", + "canUseItemEffect": "true" + }, + "cross": { + "cls": "constants", + "name": "十字架", + "text": "持有后无视怪物的无敌属性" + }, + "dagger": { + "cls": "constants", + "name": "屠龙匕首", + "text": "该道具尚未被定义" + }, + "amulet": { + "cls": "constants", + "name": "护符", + "text": "持有时无视负面地形", + "useItemEvent": [], + "useItemEffect": "core.status.hero.hpmax += 10;" + }, + "bigKey": { + "cls": "tools", + "name": "大黄门钥匙", + "text": "可以开启当前层所有黄门", + "itemEffect": "core.addItem('yellowKey', 1);\ncore.addItem('blueKey', 1);\ncore.addItem('redKey', 1);", + "itemEffectTip": ",全钥匙+1", + "useItemEffect": "(function () {\n\tvar actions = core.searchBlock(\"yellowDoor\").map(function (block) {\n\t\treturn { \"type\": \"openDoor\", \"loc\": [block.x, block.y], \"async\": true };\n\t});\n\tactions.push({ \"type\": \"waitAsync\" });\n\tactions.push({ \"type\": \"tip\", \"text\": core.material.items[itemId].name + \"使用成功\" });\n\tcore.insertAction(actions);\n})();", + "canUseItemEffect": "(function () {\n\treturn core.searchBlock('yellowDoor').length > 0;\n})();" + }, + "greenKey": { + "cls": "tools", + "name": "绿钥匙", + "text": "可以打开一扇绿门" + }, + "steelKey": { + "cls": "tools", + "name": "铁门钥匙", + "text": "可以打开一扇铁门" + }, + "pickaxe": { + "cls": "tools", + "name": "破墙镐", + "text": "可以破坏勇士面前的墙", + "useItemEffect": "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable) return false;\n\t\treturn block.event.canBreak;\n\t};\n\n\tvar success = false;\n\tvar pickaxeFourDirections = false; // 是否多方向破;如果是将其改成true\n\tif (pickaxeFourDirections) {\n\t\t// 多方向破\n\t\tfor (var direction in core.utils.scan) { // 多方向破默认四方向,如需改成八方向请将这两个scan改为scan2\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (canBreak(nx, ny)) {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// 仅破当前\n\t\tif (canBreak(core.nextX(), core.nextY())) {\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\tif (success) {\n\t\tcore.playSound('破墙镐');\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\t} else {\n\t\t// 无法使用\n\t\tcore.playSound('操作失败');\n\t\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name, itemId);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n})();", + "canUseItemEffect": "true" + }, + "icePickaxe": { + "cls": "tools", + "name": "破冰镐", + "text": "可以破坏勇士面前的一堵冰墙", + "useItemEffect": "(function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\tcore.insertAction({ \"type\": \"openDoor\", \"loc\": [\"core.nextX()\", \"core.nextY()\"] });\n})();", + "canUseItemEffect": "(function () {\n\treturn core.getBlockId(core.nextX(), core.nextY()) == 'ice';\n})();" + }, + "bomb": { + "cls": "tools", + "name": "炸弹", + "text": "可以炸掉勇士面前的怪物", + "useItemEffect": "(function () {\n\tvar bombList = []; // 炸掉的怪物坐标列表\n\tvar todo = []; // 炸弹后事件\n\tvar money = 0,\n\t\texp = 0; // 炸弹获得的金币和经验\n\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable || block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tvar bomb = function (x, y) {\n\t\tif (!canBomb(x, y)) return;\n\t\tbombList.push([x, y]);\n\t\tvar id = core.getBlockId(x, y),\n\t\t\tenemy = core.material.enemys[id];\n\t\tmoney += core.getEnemyValue(enemy, 'money', x, y) || 0;\n\t\texp += core.getEnemyValue(enemy, 'exp', x, y) || 0;\n\t\tcore.push(todo, core.floors[core.status.floorId].afterBattle[x + \",\" + y]);\n\t\tcore.push(todo, enemy.afterBattle);\n\t\tcore.removeBlock(x, y);\n\t}\n\n\t// 如果要多方向可炸,把这里的false改成true\n\tif (false) {\n\t\tvar scan = core.utils.scan; // 多方向炸时默认四方向,如果要改成八方向炸可以改成 core.utils.scan2\n\t\tfor (var direction in scan) {\n\t\t\tvar delta = scan[direction];\n\t\t\tbomb(core.getHeroLoc('x') + delta.x, core.getHeroLoc('y') + delta.y);\n\t\t}\n\t} else {\n\t\t// 仅炸当前\n\t\tbomb(core.nextX(), core.nextY());\n\t}\n\n\tif (bombList.length == 0) {\n\t\tcore.playSound('操作失败');\n\t\tcore.drawTip('当前无法使用' + core.material.items[itemId].name, itemId);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n\n\tcore.playSound('炸弹');\n\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\n\t// 取消这里的注释可以炸弹后获得金币和经验\n\t// core.status.hero.money += money;\n\t// core.status.hero.exp += exp;\n\n\t// 取消这里的注释可以炸弹引发战后事件\n\t// if (todo.length > 0) core.insertAction(todo);\n\n})();", + "canUseItemEffect": "true" + }, + "centerFly": { + "cls": "tools", + "name": "中心对称飞行器", + "text": "可以飞向当前楼层中心对称的位置", + "useItemEffect": "core.playSound('centerFly.mp3');\ncore.clearMap('hero');\ncore.setHeroLoc('x', core.bigmap.width - 1 - core.getHeroLoc('x'));\ncore.setHeroLoc('y', core.bigmap.height - 1 - core.getHeroLoc('y'));\ncore.drawHero();\ncore.drawTip(core.material.items[itemId].name + '使用成功');", + "canUseItemEffect": "(function () {\n\tvar toX = core.bigmap.width - 1 - core.getHeroLoc('x'),\n\t\ttoY = core.bigmap.height - 1 - core.getHeroLoc('y');\n\tvar id = core.getBlockId(toX, toY);\n\treturn id == null;\n})();" + }, + "upFly": { + "cls": "tools", + "name": "上楼器", + "text": "可以飞往楼上的相同位置", + "useItemEffect": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) + 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();", + "canUseItemEffect": "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index < core.floorIds.length - 1) {\n\t\tvar toId = core.floorIds[index + 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();" + }, + "downFly": { + "cls": "tools", + "name": "下楼器", + "text": "可以飞往楼下的相同位置", + "useItemEffect": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) - 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();", + "canUseItemEffect": "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index > 0) {\n\t\tvar toId = core.floorIds[index - 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();" + }, + "earthquake": { + "cls": "tools", + "name": "地震卷轴", + "text": "可以破坏当前层的所有墙", + "useItemEffect": "(function () {\n\tvar indexes = [];\n\tfor (var index in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[index];\n\t\tif (!block.disable && block.event.canBreak) {\n\t\t\tindexes.push(index);\n\t\t}\n\t}\n\tcore.removeBlockByIndexes(indexes);\n\tcore.redrawMap();\n\tcore.playSound('炸弹');\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", + "canUseItemEffect": "(function () {\n\treturn core.status.thisMap.blocks.filter(function (block) {\n\t\treturn !block.disable && block.event.canBreak;\n\t}).length > 0;\n})();" + }, + "poisonWine": { + "cls": "tools", + "name": "解毒药水", + "text": "可以解除中毒状态", + "useItemEffect": "core.triggerDebuff('remove', 'poison');", + "canUseItemEffect": "core.hasFlag('poison');" + }, + "weakWine": { + "cls": "tools", + "name": "解衰药水", + "text": "可以解除衰弱状态", + "useItemEffect": "core.triggerDebuff('remove', 'weak');", + "canUseItemEffect": "core.hasFlag('weak');" + }, + "curseWine": { + "cls": "tools", + "name": "解咒药水", + "text": "可以解除诅咒状态", + "useItemEffect": "core.triggerDebuff('remove', 'curse');", + "canUseItemEffect": "core.hasFlag('curse');" + }, + "superWine": { + "cls": "tools", + "name": "万能药水", + "text": "可以解除所有不良状态", + "useItemEffect": "core.triggerDebuff('remove', ['poison', 'weak', 'curse']);", + "canUseItemEffect": "(function() {\n\treturn core.hasFlag('poison') || core.hasFlag('weak') || core.hasFlag('curse');\n})();" + }, + "hammer": { + "cls": "tools", + "name": "圣锤", + "text": "该道具尚未被定义" + }, + "lifeWand": { + "cls": "tools", + "name": "生命魔杖", + "text": "可以恢复100点生命值", + "useItemEvent": [ + { + "type": "comment", + "text": "先恢复一个魔杖(因为使用道具必须消耗一个)" + }, + { + "type": "function", + "function": "function(){\ncore.addItem('lifeWand', 1);\n}" + }, + { + "type": "playSound", + "name": "打开界面" + }, + { + "type": "input", + "text": "请输入生命魔杖使用次数:(0-${item:lifeWand})" + }, + { + "type": "comment", + "text": "【接受用户输入】弹窗输入的结果将会保存在“flag:input”中\n如果需要更多帮助,请查阅帮助文档" + }, + { + "type": "if", + "condition": "flag:input<=item:lifeWand", + "true": [ + { + "type": "setValue", + "name": "item:lifeWand", + "operator": "-=", + "value": "flag:input" + }, + { + "type": "setValue", + "name": "status:hp", + "operator": "+=", + "value": "flag:input*100" + }, + { + "type": "playSound", + "name": "回血" + }, + "成功使用${flag:input}次生命魔杖,恢复${flag:input*100}点生命。" + ], + "false": [ + { + "type": "playSound", + "name": "操作失败" + }, + "输入不合法!" + ] + } + ], + "canUseItemEffect": "true" + }, + "jumpShoes": { + "cls": "tools", + "name": "跳跃靴", + "text": "能跳跃到前方两格处", + "useItemEffect": "core.playSound(\"跳跃\");\ncore.insertAction({ \"type\": \"jumpHero\", \"loc\": [core.nextX(2), core.nextY(2)] });", + "canUseItemEffect": "(function () {\n\tvar nx = core.nextX(2),\n\t\tny = core.nextY(2);\n\treturn nx >= 0 && nx < core.bigmap.width && ny >= 0 && ny < core.bigmap.height && core.getBlockId(nx, ny) == null;\n})();" + }, + "skill1": { + "cls": "constants", + "name": "技能:二倍斩", + "text": "可以打开或关闭主动技能二倍斩", + "hideInReplay": true, + "useItemEffect": "(function () {\n\tvar skillValue = 1; // 技能的flag:skill值,可用于当前开启技能的判定;对于新技能可以依次改成2,3等等\n\tvar skillNeed = 5; // 技能的需求\n\tvar skillName = '二倍斩'; // 技能的名称\n\n\tif (core.getFlag('skill', 0) != skillValue) { // 判断当前是否已经开了技能\n\t\tif (core.getStatus('mana') >= skillNeed) { // 这里要写当前能否开技能的条件判断,比如魔力值至少要多少\n\t\t\tcore.playSound('打开界面');\n\t\t\tcore.setFlag('skill', skillValue); // 开技能1\n\t\t\tcore.setFlag('skillName', skillName); // 设置技能名\n\t\t} else {\n\t\t\tcore.playSound('操作失败');\n\t\t\tcore.drawTip('魔力不足,无法开启技能');\n\t\t}\n\t} else { // 关闭技能\n\t\tcore.setFlag('skill', 0); // 关闭技能状态\n\t\tcore.setFlag('skillName', '无');\n\t}\n})();", + "canUseItemEffect": "true" + }, + "wand": { + "cls": "items", + "name": "新物品" + }, + "pack": { + "cls": "items", + "name": "钱袋", + "itemEffect": "core.status.hero.money += 500", + "itemEffectTip": ",金币+500" + }, + "I466": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I467": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I468": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I469": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I470": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I471": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I472": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I473": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I474": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I475": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I476": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I477": { + "cls": "constants", + "name": "传送器", + "canUseItemEffect": "true", + "text": null, + "useItemEvent": [ + { + "type": "choices", + "text": "请选择你的目的", + "choices": [ + { + "text": "去后一楼", + "action": [ + { + "type": "changeFloor", + "floorId": ":next", + "loc": [ + 30, + 30 + ], + "time": 300 + } + ] + }, + { + "text": "加10速度", + "action": [ + { + "type": "function", + "function": "function(){\ncore.values.moveSpeed += 10;\ncore.drawTip(\"当前速度:${core.values.moveSpeed}\");\n}" + } + ] + }, + { + "text": "减10速度", + "action": [ + { + "type": "function", + "function": "function(){\ncore.values.moveSpeed -= 10;\ncore.drawTip(\"当前速度:${core.values.moveSpeed}\");\n}" + } + ] + }, + { + "text": "退出", + "action": [] + } + ] + } + ] + }, + "I478": { + "cls": "tools", + "name": "铁锭", + "canUseItemEffect": "true", + "text": "一块铁锭" + }, + "I479": { + "cls": "tools", + "name": "小麦", + "canUseItemEffect": "true", + "text": "一个小麦", + "useItemEffect": "" + }, + "I480": { + "cls": "tools", + "name": "面包", + "canUseItemEffect": "true", + "text": "一块面包", + "isfood": false + }, + "I481": { + "cls": "tools", + "name": "箱子", + "canUseItemEffect": "true", + "text": "一个箱子", + "useItemEffect": "if (!core.getBlockNumber(hero.loc.x, hero.loc.y)) {\n\tcore.setBlock(\"T314\", hero.loc.x, hero.loc.y)\n} else {\n\tcore.playSound('操作失败');\n\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name);\n\tcore.addItem(itemId, 1);\n}" + }, + "I482": { + "cls": "equips", + "name": "皮革帽子", + "canUseItemEffect": "true", + "text": "一件皮革帽子", + "equip": { + "type": 2, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I483": { + "cls": "equips", + "name": "皮革外套", + "canUseItemEffect": "true", + "text": "一件皮革外套", + "useItemEffect": null, + "equip": { + "type": 3, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I484": { + "cls": "equips", + "name": "皮革裤子", + "canUseItemEffect": "true", + "text": "一件皮革裤子", + "equip": { + "type": 4, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I485": { + "cls": "equips", + "name": "皮革靴子", + "canUseItemEffect": "true", + "equip": { + "type": 5, + "value": { + "def": 1 + }, + "percentage": {} + }, + "useItemEffect": "", + "text": "一双皮革靴子" + }, + "I486": { + "cls": "equips", + "name": "铁头盔", + "canUseItemEffect": "true", + "text": "一个铁头盔", + "equip": { + "type": 2, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I487": { + "cls": "equips", + "name": "铁胸甲", + "canUseItemEffect": "true", + "text": "一个铁胸甲", + "equip": { + "type": 3, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I488": { + "cls": "equips", + "name": "铁护腿", + "canUseItemEffect": "true", + "text": "一个铁护腿", + "equip": { + "type": 4, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I489": { + "cls": "equips", + "name": "铁靴子", + "canUseItemEffect": "true", + "text": "一双铁靴子", + "equip": { + "type": 5, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I490": { + "cls": "equips", + "name": "木盾", + "canUseItemEffect": "true", + "text": "一个木盾", + "equip": { + "type": 1, + "value": {}, + "percentage": {} + } + }, + "I491": { + "cls": "equips", + "name": "石锄", + "canUseItemEffect": "true", + "text": "一个石锄", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I492": { + "cls": "tools", + "name": "小麦种子", + "canUseItemEffect": "true", + "text": "一个小麦种子" + }, + "I493": { + "cls": "tools", + "name": "钻石", + "canUseItemEffect": "true", + "text": "一颗钻石", + "equip": null + }, + "I494": { + "cls": "tools", + "name": "原石", + "canUseItemEffect": "true", + "text": "一块原石" + }, + "I495": { + "cls": "equips", + "name": "木弓", + "canUseItemEffect": "true", + "text": "一把木弓", + "equip": { + "type": 0, + "value": { + "atk": 1 + }, + "percentage": {} + } + }, + "I496": { + "cls": "equips", + "name": "铁剑", + "canUseItemEffect": "true", + "text": "一把铁剑", + "equip": { + "type": 0, + "value": { + "atk": 1 + }, + "percentage": {} + } + }, + "I497": { + "cls": "equips", + "name": "钻石剑", + "canUseItemEffect": "true", + "text": "一把钻石剑", + "equip": { + "type": 0, + "value": { + "atk": 2 + }, + "percentage": {} + } + }, + "I498": { + "cls": "equips", + "name": "木镐", + "canUseItemEffect": "true", + "text": "一个木镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I499": { + "cls": "equips", + "name": "铁镐", + "canUseItemEffect": "true", + "text": "一个铁镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I500": { + "cls": "equips", + "name": "钻石镐", + "canUseItemEffect": "true", + "text": "一个钻石镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I501": { + "cls": "equips", + "name": "石斧", + "canUseItemEffect": "true", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + }, + "text": "一个石斧", + "useItemEvent": [] + }, + "I502": { + "cls": "equips", + "name": "铁斧", + "canUseItemEffect": "true", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + }, + "text": "一个铁斧" + }, + "I503": { + "cls": "equips", + "name": "钻石斧", + "canUseItemEffect": "true", + "useItemEffect": null, + "text": "一个钻石斧", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I504": { + "cls": "equips", + "name": "石锹", + "canUseItemEffect": "true", + "text": "一个石锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I505": { + "cls": "equips", + "name": "钻石锹", + "canUseItemEffect": "true", + "text": "一个钻石锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I506": { + "cls": "equips", + "name": "金锹", + "canUseItemEffect": "true", + "text": "一个金锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I507": { + "cls": "tools", + "name": "马铃薯", + "canUseItemEffect": "true", + "text": "一个马铃薯" + }, + "I508": { + "cls": "tools", + "name": "胡萝卜", + "canUseItemEffect": "true", + "text": "一个胡萝卜" + }, + "I509": { + "cls": "constants", + "name": "钟", + "canUseItemEffect": "true", + "useItemEffect": "core.drawTip(\"当前时间:\" + flags.timePeriod + Math.floor(flags.time / 60) + \":\" + (flags.time % 60).toString().padStart(2, '0'));", + "text": "一个钟" + }, + "I510": { + "cls": "tools", + "name": "", + "canUseItemEffect": "true", + "text": null + }, + "I511": { + "cls": "tools", + "name": "泥土", + "canUseItemEffect": "true", + "text": "放置后会根据环境变成对应的草皮" + }, + "I512": { + "cls": "tools", + "name": "雪块", + "canUseItemEffect": "true", + "text": "一个雪块" + }, + "I513": { + "cls": "tools", + "name": "沙子", + "canUseItemEffect": "true", + "text": "一个沙子" + }, + "I514": { + "cls": "tools", + "name": "红沙", + "canUseItemEffect": "true", + "text": "一个红沙" + }, + "I515": { + "cls": "tools", + "name": "圆石", + "canUseItemEffect": "true", + "text": "一个圆石" + }, + "I516": { + "cls": "tools", + "name": "石头", + "text": "一个石头" + }, + "I517": { + "cls": "tools", + "name": "菌丝", + "canUseItemEffect": "true", + "text": "可用于种蘑菇" + }, + "I518": { + "cls": "tools", + "name": "玻璃", + "canUseItemEffect": "true", + "text": "一个玻璃" + }, + "I519": { + "cls": "tools", + "name": "黑曜石", + "canUseItemEffect": "true", + "text": "一个黑曜石" + }, + "I520": { + "cls": "tools", + "name": "沙砾", + "canUseItemEffect": "true", + "text": "挖掉时有概率获得燧石" + }, + "I521": { + "cls": "tools", + "name": "砂岩", + "canUseItemEffect": "true", + "text": "一个砂岩" + }, + "I522": { + "cls": "tools", + "name": "羊毛", + "canUseItemEffect": "true", + "text": "一个羊毛" + }, + "I523": { + "cls": "tools", + "name": "煤炭", + "canUseItemEffect": "true", + "text": "用作燃料" + }, + "I524": { + "cls": "tools", + "name": "粗铁", + "canUseItemEffect": "true", + "text": "可烧成铁锭" + }, + "I525": { + "cls": "tools", + "name": "粗铜", + "canUseItemEffect": "true", + "text": "可烧成铜锭" + }, + "I526": { + "cls": "tools", + "name": "粗金", + "canUseItemEffect": "true", + "text": "可烧成金锭" + }, + "I527": { + "cls": "tools", + "name": "铁锭", + "canUseItemEffect": "true", + "text": "一个铁锭" + }, + "I528": { + "cls": "tools", + "name": "铜锭", + "canUseItemEffect": "true", + "text": "一个铜锭" + }, + "I529": { + "cls": "tools", + "name": "金锭", + "canUseItemEffect": "true", + "text": "一个金锭" + }, + "I530": { + "cls": "tools", + "name": "红石", + "canUseItemEffect": "true", + "text": "一个红石粉" + }, + "I531": { + "cls": "tools", + "name": "青金石", + "canUseItemEffect": "true", + "text": "可用于附魔" + }, + "I532": { + "cls": "tools", + "name": "钻石", + "canUseItemEffect": "true", + "text": "一个钻石" + }, + "I533": { + "cls": "tools", + "name": "绿宝石", + "canUseItemEffect": "true", + "text": "一个绿宝石" + }, + "I534": { + "cls": "tools", + "name": "紫水晶簇", + "canUseItemEffect": "true", + "text": "一个紫水晶簇" + }, + "I535": { + "cls": "tools", + "name": "煤炭块", + "canUseItemEffect": "true", + "text": "用作燃料" + }, + "I536": { + "cls": "tools", + "name": "铁块", + "canUseItemEffect": "true", + "text": "一个铁块" + }, + "I537": { + "cls": "tools", + "name": "铜块", + "canUseItemEffect": "true", + "text": "一个铜块" + }, + "I538": { + "cls": "tools", + "name": "金块", + "canUseItemEffect": "true", + "text": "一个金块" + }, + "I539": { + "cls": "tools", + "name": "红石块", + "canUseItemEffect": "true", + "text": "一个红石块" + }, + "I540": { + "cls": "tools", + "name": "青金石块", + "canUseItemEffect": "true", + "text": "一个青金石块" + }, + "I541": { + "cls": "tools", + "name": "钻石块", + "canUseItemEffect": "true", + "text": "一个钻石块" + }, + "I542": { + "cls": "tools", + "name": "绿宝石块", + "canUseItemEffect": "true", + "text": "一个绿宝石块" + }, + "I543": { + "cls": "tools", + "name": "紫水晶块", + "canUseItemEffect": "true", + "text": "一个紫水晶块" + }, + "I544": { + "cls": "tools", + "name": "橡树树苗", + "canUseItemEffect": "true", + "text": "可长成橡树" + }, + "I545": { + "cls": "tools", + "name": "白桦树苗", + "canUseItemEffect": "true", + "text": "可长成白桦树" + }, + "I546": { + "cls": "tools", + "name": "云杉树苗", + "canUseItemEffect": "true", + "text": "可长成云杉树" + }, + "I547": { + "cls": "tools", + "name": "丛林树苗", + "canUseItemEffect": "true", + "text": "可长成丛林树" + }, + "I548": { + "cls": "tools", + "name": "金合欢树苗", + "canUseItemEffect": "true", + "text": "可长成金合欢树" + }, + "I549": { + "cls": "tools", + "name": "橡木原木", + "canUseItemEffect": "true", + "text": "一个橡木原木" + }, + "I550": { + "cls": "tools", + "name": "白桦原木", + "canUseItemEffect": "true", + "text": "一个白桦原木" + }, + "I551": { + "cls": "tools", + "name": "云杉原木", + "canUseItemEffect": "true", + "text": "一个云杉原木" + }, + "I552": { + "cls": "tools", + "name": "丛林原木", + "canUseItemEffect": "true", + "text": "一个丛林原木" + }, + "I553": { + "cls": "tools", + "name": "金合欢原木", + "canUseItemEffect": "true", + "text": "一个金合欢原木" + }, + "I554": { + "cls": "tools", + "name": "橡木木板", + "canUseItemEffect": "true", + "text": "一个橡木木板" + }, + "I555": { + "cls": "tools", + "name": "白桦木板", + "canUseItemEffect": "true", + "text": "一个白桦木板" + }, + "I556": { + "cls": "tools", + "name": "云杉木板", + "canUseItemEffect": "true", + "text": "一个云杉木板" + }, + "I557": { + "cls": "tools", + "name": "丛林木板", + "canUseItemEffect": "true", + "text": "一个丛林木板" + }, + "I558": { + "cls": "tools", + "name": "金合欢木板", + "canUseItemEffect": "true", + "text": "一个金合欢木板" + }, + "I559": { + "cls": "tools", + "name": "箱子", + "canUseItemEffect": "true", + "text": "装有物品的箱子被破坏后将损失所有物品" + }, + "I560": { + "cls": "tools", + "name": "工作台", + "canUseItemEffect": "true", + "text": "用于制作更多物品" + }, + "I561": { + "cls": "tools", + "name": "熔炉", + "canUseItemEffect": "true", + "text": "用于烧制食物和矿物" + }, + "I562": { + "cls": "tools", + "name": "酿造台", + "canUseItemEffect": "true", + "text": "用于酿造药水" + }, + "I563": { + "cls": "tools", + "name": "附魔台", + "canUseItemEffect": "true", + "text": "用于附魔" + }, + "I564": { + "cls": "tools", + "name": "铁砧", + "canUseItemEffect": "true", + "text": "一个铁砧" + }, + "I565": { + "cls": "tools", + "name": "床", + "canUseItemEffect": "true", + "text": "用于设置重生点" + }, + "I566": { + "cls": "tools", + "name": "末影箱", + "canUseItemEffect": "true", + "text": "所有末影箱共用一个存储空间" + }, + "I567": { + "cls": "tools", + "name": "篝火", + "canUseItemEffect": "true", + "text": "提供大范围照明,天亮后熄灭" + }, + "I568": { + "cls": "tools", + "name": "垃圾桶", + "canUseItemEffect": "true", + "text": "不需要的东西可以丢在里面" + }, + "I569": { + "cls": "equips", + "name": "木剑", + "canUseItemEffect": "true", + "text": "一个木剑", + "equip": { + "type": 0, + "value": { + "atk": 1 + }, + "percentage": {} + } + }, + "I570": { + "cls": "equips", + "name": "石剑", + "canUseItemEffect": "true", + "text": "一个石剑", + "equip": { + "type": 0, + "value": { + "atk": 2 + }, + "percentage": {} + } + }, + "I571": { + "cls": "equips", + "name": "铁剑", + "canUseItemEffect": "true", + "text": "一个铁剑", + "equip": { + "type": 0, + "value": { + "atk": 5 + }, + "percentage": {} + } + }, + "I572": { + "cls": "equips", + "name": "金剑", + "canUseItemEffect": "true", + "text": "一个金剑", + "equip": { + "type": 0, + "value": { + "atk": 4 + }, + "percentage": {} + } + }, + "I573": { + "cls": "equips", + "name": "钻石剑", + "canUseItemEffect": "true", + "text": "一个钻石剑", + "equip": { + "type": 0, + "value": { + "atk": 10 + }, + "percentage": {} + } + }, + "I574": { + "cls": "equips", + "name": "木斧", + "canUseItemEffect": "true", + "text": "一个木斧", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I575": { + "cls": "equips", + "name": "石斧", + "canUseItemEffect": "true", + "text": "一个石斧", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I576": { + "cls": "equips", + "name": "铁斧", + "canUseItemEffect": "true", + "text": "一个铁斧", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I577": { + "cls": "equips", + "name": "金斧", + "canUseItemEffect": "true", + "text": "一个金斧", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I578": { + "cls": "equips", + "name": "钻石斧", + "canUseItemEffect": "true", + "text": "一个钻石斧", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I579": { + "cls": "equips", + "name": "木镐", + "canUseItemEffect": "true", + "text": "一个木镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I580": { + "cls": "equips", + "name": "石镐", + "canUseItemEffect": "true", + "text": "一个石镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I581": { + "cls": "equips", + "name": "铁镐", + "canUseItemEffect": "true", + "text": "一个铁镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I582": { + "cls": "equips", + "name": "金镐", + "canUseItemEffect": "true", + "text": "一个金镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I583": { + "cls": "equips", + "name": "钻石镐", + "canUseItemEffect": "true", + "text": "一个钻石镐", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I584": { + "cls": "equips", + "name": "木锹", + "canUseItemEffect": "true", + "text": "一个木锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I585": { + "cls": "equips", + "name": "石锹", + "canUseItemEffect": "true", + "text": "一个石锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I586": { + "cls": "equips", + "name": "铁锹", + "canUseItemEffect": "true", + "text": "一个铁锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I587": { + "cls": "equips", + "name": "金锹", + "canUseItemEffect": "true", + "text": "一个金锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I588": { + "cls": "equips", + "name": "钻石锹", + "canUseItemEffect": "true", + "text": "一个钻石锹", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I589": { + "cls": "equips", + "name": "木锄", + "canUseItemEffect": "true", + "text": "一个木锄", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I590": { + "cls": "equips", + "name": "石锄", + "canUseItemEffect": "true", + "text": "一个石锄", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I591": { + "cls": "equips", + "name": "铁锄", + "canUseItemEffect": "true", + "text": "一个铁锄", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I592": { + "cls": "equips", + "name": "金锄", + "canUseItemEffect": "true", + "text": "一个金锄", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I593": { + "cls": "equips", + "name": "钻石锄", + "canUseItemEffect": "true", + "text": "一个钻石锄", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I594": { + "cls": "equips", + "name": "皮革帽子", + "canUseItemEffect": "true", + "text": "一个皮革帽子", + "equip": { + "type": 2, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I595": { + "cls": "equips", + "name": "皮革外套", + "canUseItemEffect": "true", + "text": "一个皮革外套", + "equip": { + "type": 3, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I596": { + "cls": "equips", + "name": "皮革裤子", + "canUseItemEffect": "true", + "text": "一个皮革裤子", + "equip": { + "type": 4, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I597": { + "cls": "equips", + "name": "皮革靴子", + "canUseItemEffect": "true", + "text": "一个皮革靴子", + "equip": { + "type": 5, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I598": { + "cls": "equips", + "name": "铜头盔", + "canUseItemEffect": "true", + "text": "一个铜头盔", + "equip": { + "type": 2, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I599": { + "cls": "equips", + "name": "铜胸甲", + "canUseItemEffect": "true", + "text": "一个铜胸甲", + "equip": { + "type": 3, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I600": { + "cls": "equips", + "name": "铜护腿", + "canUseItemEffect": "true", + "text": "一个铜护腿", + "equip": { + "type": 4, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I601": { + "cls": "equips", + "name": "铜靴子", + "canUseItemEffect": "true", + "text": "一个铜靴子", + "equip": { + "type": 5, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I602": { + "cls": "equips", + "name": "铁头盔", + "canUseItemEffect": "true", + "text": "一个铁头盔", + "equip": { + "type": 2, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I603": { + "cls": "equips", + "name": "铁胸甲", + "canUseItemEffect": "true", + "text": "一个铁胸甲", + "equip": { + "type": 3, + "value": { + "def": 3 + }, + "percentage": {} + } + }, + "I604": { + "cls": "equips", + "name": "铁护腿", + "canUseItemEffect": "true", + "text": "一个铁护腿", + "equip": { + "type": 4, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I605": { + "cls": "equips", + "name": "铁靴子", + "canUseItemEffect": "true", + "text": "一个铁靴子", + "equip": { + "type": 5, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I606": { + "cls": "equips", + "name": "金头盔", + "canUseItemEffect": "true", + "text": "一个金头盔", + "equip": { + "type": 2, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I607": { + "cls": "equips", + "name": "金胸甲", + "canUseItemEffect": "true", + "text": "一个金胸甲", + "equip": { + "type": 3, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I608": { + "cls": "equips", + "name": "金护腿", + "canUseItemEffect": "true", + "text": "一个金护腿", + "equip": { + "type": 4, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I609": { + "cls": "equips", + "name": "金靴子", + "canUseItemEffect": "true", + "text": "一个金靴子", + "equip": { + "type": 5, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I610": { + "cls": "equips", + "name": "钻石头盔", + "canUseItemEffect": "true", + "text": "一个钻石头盔", + "equip": { + "type": 2, + "value": { + "def": 3 + }, + "percentage": {} + } + }, + "I611": { + "cls": "equips", + "name": "钻石胸甲", + "canUseItemEffect": "true", + "text": "一个钻石胸甲", + "equip": { + "type": 3, + "value": { + "def": 4 + }, + "percentage": {} + } + }, + "I612": { + "cls": "equips", + "name": "钻石护腿", + "canUseItemEffect": "true", + "text": "一个钻石护腿", + "equip": { + "type": 4, + "value": { + "def": 3 + }, + "percentage": {} + } + }, + "I613": { + "cls": "equips", + "name": "钻石靴子", + "canUseItemEffect": "true", + "text": "一个钻石靴子", + "equip": { + "type": 5, + "value": { + "def": 3 + }, + "percentage": {} + } + }, + "I614": { + "cls": "equips", + "name": "弓", + "canUseItemEffect": "true", + "text": "一个弓", + "equip": { + "type": 0, + "value": { + "atk": 4 + }, + "percentage": {} + } + }, + "I615": { + "cls": "tools", + "name": "箭", + "canUseItemEffect": "true", + "text": "一个箭" + }, + "I616": { + "cls": "tools", + "name": "剪刀", + "canUseItemEffect": "true", + "text": "一个剪刀" + }, + "I617": { + "cls": "equips", + "name": "木盾", + "canUseItemEffect": "true", + "text": "一个木盾", + "equip": { + "type": 1, + "value": { + "def": 1 + }, + "percentage": {} + } + }, + "I618": { + "cls": "equips", + "name": "铜盾", + "canUseItemEffect": "true", + "text": "一个铜盾", + "equip": { + "type": 1, + "value": { + "def": 2 + }, + "percentage": {} + } + }, + "I619": { + "cls": "equips", + "name": "钻石盾", + "canUseItemEffect": "true", + "text": "一个钻石盾", + "equip": { + "type": 1, + "value": { + "def": 3 + }, + "percentage": {} + } + }, + "I620": { + "cls": "tools", + "name": "不死图腾", + "canUseItemEffect": "true", + "text": "受到致命伤害时使你保留1点血,仅在物品栏时生效" + }, + "I621": { + "cls": "tools", + "name": "小麦种子", + "canUseItemEffect": "true", + "text": "一个小麦种子" + }, + "I622": { + "cls": "tools", + "name": "西瓜种子", + "canUseItemEffect": "true", + "text": "一个西瓜种子" + }, + "I623": { + "cls": "tools", + "name": "马铃薯", + "canUseItemEffect": "true", + "text": "一个马铃薯" + }, + "I624": { + "cls": "tools", + "name": "烤马铃薯", + "canUseItemEffect": "true", + "text": "食用恢复25饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 25;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I625": { + "cls": "tools", + "name": "小麦", + "canUseItemEffect": "true", + "text": "一个小麦" + }, + "I626": { + "cls": "tools", + "name": "面包", + "canUseItemEffect": "true", + "text": "食用恢复25饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 25;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}", + "isfood": true + }, + "I627": { + "cls": "tools", + "name": "胡萝卜", + "canUseItemEffect": "true", + "text": "食用恢复15饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 15;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I628": { + "cls": "tools", + "name": "苹果", + "canUseItemEffect": "true", + "text": "食用恢复15饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 15;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I629": { + "cls": "tools", + "name": "金苹果", + "canUseItemEffect": "true", + "text": "食用恢复50饱食度,并获得一些buff", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 50;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I630": { + "cls": "tools", + "name": "附魔金苹果", + "canUseItemEffect": "true", + "text": "食用恢复50饱食度,并获得大量buff", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 50;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I631": { + "cls": "tools", + "name": "西瓜片", + "canUseItemEffect": "true", + "text": "食用恢复10饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 10;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I632": { + "cls": "tools", + "name": "闪烁西瓜片", + "canUseItemEffect": "true", + "text": "食用恢复25饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 25;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I633": { + "cls": "tools", + "name": "金胡萝卜", + "canUseItemEffect": "true", + "text": "食用恢复80饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 80;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I634": { + "cls": "tools", + "name": "南瓜", + "canUseItemEffect": "true", + "text": "食用恢复10饱食度,不可种植", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 10;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I635": { + "cls": "tools", + "name": "雕刻南瓜", + "canUseItemEffect": "true", + "text": "一个雕刻南瓜" + }, + "I636": { + "cls": "tools", + "name": "发光浆果", + "canUseItemEffect": "true", + "text": "食用恢复25饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 25;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I637": { + "cls": "tools", + "name": "仙人掌", + "canUseItemEffect": "true", + "text": "一个仙人掌" + }, + "I638": { + "cls": "tools", + "name": "向日葵", + "canUseItemEffect": "true", + "text": "一个向日葵" + }, + "I639": { + "cls": "tools", + "name": "玫瑰", + "canUseItemEffect": "true", + "text": "一个玫瑰" + }, + "I640": { + "cls": "tools", + "name": "生鸡肉", + "canUseItemEffect": "true", + "text": "一个生鸡肉" + }, + "I641": { + "cls": "tools", + "name": "熟鸡肉", + "canUseItemEffect": "true", + "text": "食用恢复40饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 40;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I642": { + "cls": "tools", + "name": "生羊肉", + "canUseItemEffect": "true", + "text": "一个生羊肉" + }, + "I643": { + "cls": "tools", + "name": "熟羊肉", + "canUseItemEffect": "true", + "text": "食用恢复50饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 50;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I644": { + "cls": "tools", + "name": "生牛肉", + "canUseItemEffect": "true", + "text": "一个生牛肉" + }, + "I645": { + "cls": "tools", + "name": "熟牛排", + "canUseItemEffect": "true", + "text": "食用恢复50饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 50;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I646": { + "cls": "tools", + "name": "生兔肉", + "canUseItemEffect": "true", + "text": "一个生兔肉" + }, + "I647": { + "cls": "tools", + "name": "熟兔肉", + "canUseItemEffect": "true", + "text": "食用恢复40饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 40;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I648": { + "cls": "tools", + "name": "生鳕鱼", + "canUseItemEffect": "true", + "text": "一个生鳕鱼" + }, + "I649": { + "cls": "tools", + "name": "熟鳕鱼", + "canUseItemEffect": "true", + "text": "食用恢复45饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 45;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I650": { + "cls": "tools", + "name": "生鲑鱼", + "canUseItemEffect": "true", + "text": "一个生鲑鱼" + }, + "I651": { + "cls": "tools", + "name": "熟鲑鱼", + "canUseItemEffect": "true", + "text": "食用恢复45饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 45;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I652": { + "cls": "equips", + "name": "打火石", + "canUseItemEffect": "true", + "text": "一个打火石" + }, + "I653": { + "cls": "equips", + "name": "钓鱼竿", + "canUseItemEffect": "true", + "text": "用于钓鱼", + "equip": { + "type": 0, + "value": {}, + "percentage": {} + } + }, + "I654": { + "cls": "tools", + "name": "铁桶", + "canUseItemEffect": "true", + "text": "用于装水或岩浆" + }, + "I655": { + "cls": "tools", + "name": "水桶", + "canUseItemEffect": "true", + "text": "一个水桶" + }, + "I656": { + "cls": "tools", + "name": "熔岩桶", + "canUseItemEffect": "true", + "text": "一个熔岩桶" + }, + "I657": { + "cls": "constants", + "name": "时钟", + "canUseItemEffect": "true", + "text": "用于查看时间" + }, + "I658": { + "cls": "tools", + "name": "玻璃瓶", + "canUseItemEffect": "true", + "text": "一个玻璃瓶" + }, + "I659": { + "cls": "tools", + "name": "水瓶", + "canUseItemEffect": "true", + "text": "一个水瓶" + }, + "I660": { + "cls": "tools", + "name": "蜂蜜瓶", + "canUseItemEffect": "true", + "text": "食用恢复30饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 30;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I661": { + "cls": "tools", + "name": "木棍", + "canUseItemEffect": "true", + "text": "一个木棍" + }, + "I662": { + "cls": "tools", + "name": "木船", + "canUseItemEffect": "true", + "text": "放在水上可划船" + }, + "I663": { + "cls": "tools", + "name": "木门", + "canUseItemEffect": "true", + "text": "敌怪无法通行" + }, + "I664": { + "cls": "tools", + "name": "火把", + "canUseItemEffect": "true", + "text": "一个火把" + }, + "I665": { + "cls": "tools", + "name": "提灯", + "canUseItemEffect": "true", + "text": "提供小范围照明,天黑自启动" + }, + "I666": { + "cls": "tools", + "name": "骨粉", + "canUseItemEffect": "true", + "equip": null, + "text": "催生植物生长" + }, + "I667": { + "cls": "tools", + "name": "骨头", + "canUseItemEffect": "true", + "text": "一个骨头" + }, + "I668": { + "cls": "tools", + "name": "火药", + "canUseItemEffect": "true", + "text": "一个火药" + }, + "I669": { + "cls": "tools", + "name": "金粒", + "canUseItemEffect": "true", + "text": "一个金粒", + "equip": null + }, + "I670": { + "cls": "tools", + "name": "木炭", + "canUseItemEffect": "true", + "text": "用作燃料" + }, + "I671": { + "cls": "tools", + "name": "皮革", + "canUseItemEffect": "true", + "text": "一个皮革" + }, + "I672": { + "cls": "tools", + "name": "兔子皮", + "canUseItemEffect": "true", + "text": "一个兔子皮" + }, + "I673": { + "cls": "tools", + "name": "书", + "canUseItemEffect": "true", + "text": "一个书" + }, + "I674": { + "cls": "tools", + "name": "燧石", + "canUseItemEffect": "true", + "text": "一个燧石" + }, + "I675": { + "cls": "tools", + "name": "线", + "canUseItemEffect": "true", + "text": "一个线" + }, + "I676": { + "cls": "tools", + "name": "羽毛", + "canUseItemEffect": "true", + "text": "一个羽毛" + }, + "I677": { + "cls": "tools", + "name": "纸", + "canUseItemEffect": "true", + "text": "一个纸" + }, + "I678": { + "cls": "tools", + "name": "蜂巢", + "canUseItemEffect": "true", + "text": "自动产蜂蜜" + }, + "I679": { + "cls": "tools", + "name": "甘蔗", + "canUseItemEffect": "true", + "text": "一个甘蔗,不可种植" + }, + "I680": { + "cls": "tools", + "name": "糖", + "canUseItemEffect": "true", + "text": "一个糖" + }, + "I681": { + "cls": "tools", + "name": "竹子", + "canUseItemEffect": "true", + "text": "一个竹子" + }, + "I682": { + "cls": "tools", + "name": "棕色蘑菇", + "canUseItemEffect": "true", + "text": "一个棕色蘑菇" + }, + "I683": { + "cls": "tools", + "name": "红色蘑菇", + "canUseItemEffect": "true", + "text": "一个红色蘑菇" + }, + "I684": { + "cls": "tools", + "name": "棕色蘑菇块", + "canUseItemEffect": "true", + "text": "一个棕色蘑菇块" + }, + "I685": { + "cls": "tools", + "name": "红色蘑菇块", + "canUseItemEffect": "true", + "useItemEvent": null, + "useItemEffect": "", + "text": "一个红色蘑菇块" + }, + "I686": { + "cls": "tools", + "name": "海带", + "canUseItemEffect": "true", + "text": "可合成干海带。食用恢复10饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 10;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I687": { + "cls": "tools", + "name": "干海带", + "canUseItemEffect": "true", + "text": "用作燃料" + }, + "I688": { + "cls": "tools", + "name": "黄珊瑚", + "canUseItemEffect": "true", + "text": "一个黄珊瑚", + "equip": null + }, + "I689": { + "cls": "tools", + "name": "红珊瑚", + "canUseItemEffect": "true", + "text": "一个红珊瑚", + "equip": null + }, + "I690": { + "cls": "tools", + "name": "蓝珊瑚", + "canUseItemEffect": "true", + "text": "一个蓝珊瑚" + }, + "I691": { + "cls": "tools", + "name": "粉珊瑚", + "canUseItemEffect": "true", + "text": "一个粉珊瑚", + "equip": null, + "useItemEvent": [], + "hideInToolbox": false + }, + "I692": { + "cls": "tools", + "name": "黄珊瑚块", + "canUseItemEffect": "true", + "text": "一个黄珊瑚块" + }, + "I693": { + "cls": "tools", + "name": "红珊瑚块", + "canUseItemEffect": "true", + "text": "一个红珊瑚块" + }, + "I694": { + "cls": "tools", + "name": "蓝珊瑚块", + "canUseItemEffect": "true", + "text": "一个蓝珊瑚块", + "equip": null + }, + "I695": { + "cls": "tools", + "name": "粉珊瑚块", + "canUseItemEffect": "true", + "text": "一个粉珊瑚块", + "equip": null + }, + "I696": { + "cls": "tools", + "name": "腐肉", + "canUseItemEffect": "true", + "text": "食用恢复20饱食度", + "equip": null, + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 20;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I697": { + "cls": "tools", + "name": "河豚", + "canUseItemEffect": "true", + "text": "食用恢复20饱食度", + "equip": null, + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 20;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I698": { + "cls": "tools", + "name": "蜘蛛眼", + "canUseItemEffect": "true", + "text": "食用恢复20饱食度", + "useItemEffect": "if (flags.eatcool == 0) {\n\tvar hun = 20;\n\tflags.hunger[0] += Math.min(hun, flags.hunger[1] - flags.hunger[0]);\n\tflags.hunger[2] = flags.hunger[3];\n\tif (core.rand2(2) == 0) {\n\t\tcore.playSound(\"eat1.ogg\");\n\t} else {\n\t\tcore.playSound(\"eat2.ogg\");\n\t}\n\tflags.eatcool = 1;\n\tsetTimeout(function () {\n\t\tflags.eatcool = 0;\n\t}, 1700);\n}" + }, + "I699": { + "cls": "tools", + "name": "兔子脚", + "canUseItemEffect": "true", + "text": "一个兔子脚" + }, + "I700": { + "cls": "tools", + "name": "幻翼膜", + "canUseItemEffect": "true", + "text": "一个幻翼膜" + }, + "I701": { + "cls": "tools", + "name": "凝胶", + "canUseItemEffect": "true", + "text": "一个凝胶" + }, + "I702": { + "cls": "tools", + "name": "末影珍珠", + "canUseItemEffect": "true", + "text": "一个末影珍珠" + }, + "I703": { + "cls": "tools", + "name": "末影之眼", + "canUseItemEffect": "true", + "text": "用于寻找要塞" + }, + "I704": { + "cls": "items", + "name": "铜币", + "canUseItemEffect": "true", + "text": "一个铜币", + "hideInToolbox": true + }, + "I705": { + "cls": "tools", + "name": "经验球", + "canUseItemEffect": "true", + "text": "一个经验球", + "equip": null, + "hideInToolbox": true, + "itemEffect": "flags.level[1] += 1;\ncore.plugin.jingyandengji();", + "useItemEffect": "flags.level[1] += 1;\ncore.plugin.jingyandengji();" + }, + "I706": { + "cls": "tools", + "name": "TNT", + "canUseItemEffect": "true", + "text": "一个TNT" + }, + "I707": { + "cls": "tools", + "name": "烈焰棒", + "canUseItemEffect": "true", + "text": "一个烈焰棒" + }, + "I708": { + "cls": "tools", + "name": "烈焰粉", + "canUseItemEffect": "true", + "text": "一个烈焰粉" + }, + "I709": { + "cls": "tools", + "name": "神殿钥匙", + "canUseItemEffect": "true", + "text": "一个神殿钥匙" + }, + "I710": { + "cls": "tools", + "name": "震颤胶质", + "canUseItemEffect": "true", + "text": "一个震颤胶质" + }, + "I711": { + "cls": "tools", + "name": "血腥孢子", + "canUseItemEffect": "true", + "text": "一个血腥孢子" + }, + "I712": { + "cls": "tools", + "name": "晶化骨骼", + "canUseItemEffect": "true", + "text": "一个烈焰粉" + }, + "I713": { + "cls": "tools", + "name": "琼光鳞", + "canUseItemEffect": "true", + "text": "一个烈焰粉" + }, + "I714": { + "cls": "tools", + "name": "聚焦镜片", + "canUseItemEffect": "true", + "text": "一个烈焰粉" + }, + "I715": { + "cls": "tools", + "name": "灰烬粉尘", + "canUseItemEffect": "true", + "text": "一个烈焰粉" + }, + "I716": { + "cls": "tools", + "name": "幽灵鳍", + "canUseItemEffect": "true", + "text": "一个烈焰粉" + }, + "I717": { + "cls": "tools", + "name": "附魔之瓶", + "canUseItemEffect": "true", + "text": "一个附魔之瓶", + "useItemEffect": "flags.level[1] += core.rand2(31) + 10;\nvar rand1 = core.rand2(3);\nvar sound;\nswitch (rand1) {\ncase 0:\n\tsound = \"glass1.ogg\";\n\tbreak;\ncase 1:\n\tsound = \"glass2.ogg\";\n\tbreak;\ncase 2:\n\tsound = \"glass3.ogg\";\n\tbreak;\n}\ncore.playSound(sound);\ncore.playSound(\"orb.ogg\");\ncore.plugin.jingyandengji();" + }, + "I718": { + "cls": "tools", + "name": "银币", + "canUseItemEffect": "true", + "text": "一个银币" + }, + "I719": { + "cls": "tools", + "name": "金币", + "canUseItemEffect": "true", + "text": "一个金币" + }, + "I720": { + "cls": "tools", + "name": "月币", + "canUseItemEffect": "true", + "text": "一个月币" + }, + "I721": { + "cls": "tools", + "name": "苔石", + "canUseItemEffect": "true", + "text": "一个苔石" + }, + "I722": { + "cls": "tools", + "name": "石砖", + "canUseItemEffect": "true", + "text": "一个石砖" + }, + "I723": { + "cls": "tools", + "name": "苔石砖", + "canUseItemEffect": "true", + "text": "一个苔石砖" + }, + "I724": { + "cls": "tools", + "name": "裂纹石砖", + "canUseItemEffect": "true", + "text": "一个裂纹石砖" + }, + "I725": { + "cls": "tools", + "name": "下界岩", + "canUseItemEffect": "true", + "text": "一个下界岩" + }, + "I726": { + "cls": "tools", + "name": "橙色陶瓦", + "canUseItemEffect": "true", + "text": "一个橙色陶瓦" + }, + "I727": { + "cls": "tools", + "name": "雕纹砂岩", + "canUseItemEffect": "true", + "text": "一个雕纹砂岩" + }, + "I728": { + "cls": "tools", + "name": "红砂岩", + "canUseItemEffect": "true", + "text": "一个红砂岩" + }, + "I729": { + "cls": "tools", + "name": "红雕纹砂岩", + "canUseItemEffect": "true", + "text": "一个红雕纹砂岩" + }, + "I730": { + "cls": "tools", + "name": "下界之星", + "canUseItemEffect": "true", + "text": "一个下界之星" + }, + "I731": { + "cls": "tools", + "name": "龙息", + "canUseItemEffect": "true", + "text": "一个龙息" + }, + "I732": { + "cls": "tools", + "name": "晶挞", + "canUseItemEffect": "true", + "text": "一个晶挞" + }, + "I733": { + "cls": "tools", + "name": "远古残骸", + "canUseItemEffect": "true", + "text": "一个远古残骸" + }, + "I734": { + "cls": "tools", + "name": "下界合金碎片", + "canUseItemEffect": "true", + "text": "一个下界合金碎片" + }, + "I735": { + "cls": "tools", + "name": "下界合金锭", + "canUseItemEffect": "true", + "text": "一个下界合金锭" + }, + "I736": { + "cls": "tools", + "name": "下界合金块", + "canUseItemEffect": "true", + "text": "一个下界合金块" + }, + "I737": { + "cls": "equips", + "name": "下界合金剑", + "canUseItemEffect": "true" + }, + "I738": { + "cls": "equips", + "name": "下界合金斧", + "canUseItemEffect": "true" + }, + "I739": { + "cls": "equips", + "name": "下界合金镐", + "canUseItemEffect": "true" + }, + "I740": { + "cls": "equips", + "name": "下界合金锹", + "canUseItemEffect": "true" + }, + "I741": { + "cls": "equips", + "name": "下界合金锄", + "canUseItemEffect": "true" + }, + "I742": { + "cls": "equips", + "name": "下界合金头盔", + "canUseItemEffect": "true" + }, + "I743": { + "cls": "equips", + "name": "下界合金胸甲", + "canUseItemEffect": "true" + }, + "I744": { + "cls": "equips", + "name": "下界合金护腿", + "canUseItemEffect": "true" + }, + "I745": { + "cls": "equips", + "name": "下界合金靴子", + "canUseItemEffect": "true" + }, + "I746": { + "cls": "tools", + "name": "空气", + "canUseItemEffect": "true", + "text": "只是占位用" + }, + "I747": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I748": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I749": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I750": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I751": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I752": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I753": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I754": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I755": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I756": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I757": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I758": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I759": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I760": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I761": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I762": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I763": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I764": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I765": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I766": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I767": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I768": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I769": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I770": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I771": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I772": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I773": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I774": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I775": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I776": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I777": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I778": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I779": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I780": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I781": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I782": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I783": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I784": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I785": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I786": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I787": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I788": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I789": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I790": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I791": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I792": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I793": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I794": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I795": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I796": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I797": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I798": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I799": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I800": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I801": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I802": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I803": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I804": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I805": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I806": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I807": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I808": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I809": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I810": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I811": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I812": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I813": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I814": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I815": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I816": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I817": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I818": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I819": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I820": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I821": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I822": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I823": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I824": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I825": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I826": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I827": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I828": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I829": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I830": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I831": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I832": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I833": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I834": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I835": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I836": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I837": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I838": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I839": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I840": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I841": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I842": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I843": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I844": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I845": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I846": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I847": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I848": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I849": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I850": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I851": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I852": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I853": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I854": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I855": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I856": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I857": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I858": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I859": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I860": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I861": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I862": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I863": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I864": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I865": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I866": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I867": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I868": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I869": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I870": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I871": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I872": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I873": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I874": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I875": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I876": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + }, + "I877": { + "cls": "items", + "name": "新物品", + "canUseItemEffect": "true" + } +} \ No newline at end of file diff --git a/loader.d.ts b/loader.d.ts new file mode 100644 index 0000000..32c4ff0 --- /dev/null +++ b/loader.d.ts @@ -0,0 +1,73 @@ +/** + * 负责资源的加载 + */ +interface Loader { + /** + * 加载一系列图片 + * @param dir 图片所在目录 + * @param names 图片名称列表 + * @param toSave 要保存到的对象 + * @param callback 加载完毕后的回调函数 + */ + loadImages( + dir: string, + names: string[], + toSave: Record, + callback?: () => void + ): void; + + /** + * 加载某一张图片 + * @param dir 图片所在目录 + * @param imgName 图片名称 + * @param callback 加载完毕的回调函数 + */ + loadImage( + dir: string, + imgName: string, + callback?: (name: string, img: HTMLImageElement) => void + ): void; + + /** + * 从zip中加载一系列图片 + * @param url 图片所在目录 + * @param names 图片名称列表 + */ + loadImagesFromZip( + url: string, + names: string, + toSave: Record, + onprogress?: (loaded: number, total: number) => void, + onfinished?: () => void + ): void; + + /** + * 加载一个音乐 + * @param name 要加载的音乐的名称 + */ + loadOneMusic(name: BgmIds): void; + + /** + * 加载一个音效 + * @param name 要加载的音效的名称 + */ + loadOneSound(name: SoundIds): void; + + /** + * 加载一个bgm + * @param name 加载的bgm的id或名称 + */ + loadBgm(name: BgmIds | NameMapIn): void; + + /** + * 释放一个bgm的缓存 + * @param name 要释放的bgm的id或名称 + */ + freeBgm(name: BgmIds | NameMapIn): void; + + _loadMaterials_afterLoad(): void; + + _loadAnimate(data: string): Animate; +} + +declare const loader: new () => Loader; diff --git a/loader.js b/loader.js new file mode 100644 index 0000000..d4da0ec --- /dev/null +++ b/loader.js @@ -0,0 +1,516 @@ + +/* +loader.js:负责对资源的加载 + + */ +"use strict"; + +function loader () { + this._init(); +} + +loader.prototype._init = function () { + +} + +////// 设置加载进度条进度 ////// +loader.prototype._setStartProgressVal = function (val) { + core.dom.startTopProgress.style.width = val + '%'; +} + +////// 设置加载进度条提示文字 ////// +loader.prototype._setStartLoadTipText = function (text) { + core.dom.startTopLoadTips.innerText = text; +} + +loader.prototype._load = function (callback) { + if (main.useCompress) { + this._load_async(callback); + } else { + this._load_sync(callback); + } +} + +loader.prototype._load_sync = function (callback) { + this._loadAnimates_sync(); + this._loadMusic_sync(); + core.loader._loadMaterials_sync(function () { + core.loader._loadExtraImages_sync(function () { + core.loader._loadAutotiles_sync(function () { + core.loader._loadTilesets_sync(callback); + }) + }) + }); +} + +loader.prototype._load_async = function (callback) { + core.loader._setStartLoadTipText('正在加载资源文件...'); + var all = {}; + + var _makeOnProgress = function (name) { + if (!all[name]) all[name] = { loaded: 0, total: 0, finished: false }; + return function (loaded, total) { + all[name].loaded = loaded; + all[name].total = total; + var allLoaded = 0, allTotal = 0; + for (var one in all) { + allLoaded += all[one].loaded; + allTotal += all[one].total; + } + if (allTotal > 0) { + if (allLoaded == allTotal) { + core.loader._setStartLoadTipText("正在处理资源文件... 请稍候..."); + } else { + core.loader._setStartLoadTipText('正在加载资源文件... ' + + core.formatSize(allLoaded) + " / " + core.formatSize(allTotal) + + " (" + (allLoaded / allTotal * 100).toFixed(2) + "%)"); + } + core.loader._setStartProgressVal(allLoaded / allTotal * 100); + } + }; + } + var _makeOnFinished = function (name) { + return function () { + setTimeout(function () { + all[name].finished = true; + for (var one in all) { + if (!all[one].finished) return; + } + callback(); + }); + } + } + + this._loadAnimates_async(_makeOnProgress('animates'), _makeOnFinished('animates')); + this._loadMusic_async(_makeOnProgress('sounds'), _makeOnFinished('sounds')); + this._loadMaterials_async(_makeOnProgress('materials'), _makeOnFinished('materials')); + this._loadExtraImages_async(_makeOnProgress('images'), _makeOnFinished('images')); + this._loadAutotiles_async(_makeOnProgress('autotiles'), _makeOnFinished('autotiles')); + this._loadTilesets_async(_makeOnProgress('tilesets'), _makeOnFinished('tilesets')); +} + +// ----- 加载资源文件 ------ // + +loader.prototype._loadMaterials_sync = function (callback) { + this._setStartLoadTipText("正在加载资源文件..."); + this.loadImages("materials", core.materials, core.material.images, function () { + core.loader._loadMaterials_afterLoad(); + callback(); + }); +} + +loader.prototype._loadMaterials_async = function (onprogress, onfinished) { + this.loadImagesFromZip('project/materials/materials.h5data', core.materials, core.material.images, onprogress, function () { + core.loader._loadMaterials_afterLoad(); + onfinished(); + }); +} + +loader.prototype._loadMaterials_afterLoad = function () { + var images = core.splitImage(core.material.images['icons']); + for (var key in core.statusBar.icons) { + if (typeof core.statusBar.icons[key] == 'number') { + core.statusBar.icons[key] = images[core.statusBar.icons[key]]; + if (core.statusBar.image[key] != null) + core.statusBar.image[key].src = core.statusBar.icons[key].src; + } + } +} + +// ------ 加载使用的图片 ------ // + +loader.prototype._loadExtraImages_sync = function (callback) { + core.material.images.images = {}; + this._setStartLoadTipText("正在加载图片文件..."); + core.loadImages("images", core.images, core.material.images.images, callback); +} + +loader.prototype._loadExtraImages_async = function (onprogress, onfinished) { + core.material.images.images = {}; + var images = core.images; + + // Check .gif + var gifs = images.filter(function (name) { + return name.toLowerCase().endsWith('.gif'); + }); + images = images.filter(function (name) { + return !name.toLowerCase().endsWith('.gif'); + }); + + this.loadImagesFromZip('project/images/images.h5data', images, core.material.images.images, onprogress, onfinished); + // gif没有被压缩在zip中,延迟加载... + gifs.forEach(function (gif) { + this.loadImage("images", gif, function (id, image) { + if (image != null) { + core.material.images.images[gif] = image; + } + }); + }, this); +} + +// ------ 加载自动元件 ------ // + +loader.prototype._loadAutotiles_sync = function (callback) { + core.material.images.autotile = {}; + var keys = Object.keys(core.material.icons.autotile); + var autotiles = {}; + + this._setStartLoadTipText("正在加载自动元件..."); + this.loadImages("autotiles", keys, autotiles, function () { + core.loader._loadAutotiles_afterLoad(keys, autotiles); + callback(); + }); +} + +loader.prototype._loadAutotiles_async = function (onprogress, onfinished) { + core.material.images.autotile = {}; + var keys = Object.keys(core.material.icons.autotile); + var autotiles = {}; + + this.loadImagesFromZip('project/autotiles/autotiles.h5data', keys, autotiles, onprogress, function () { + core.loader._loadAutotiles_afterLoad(keys, autotiles); + onfinished(); + }); +} + +loader.prototype._loadAutotiles_afterLoad = function (keys, autotiles) { + // autotile需要保证顺序 + keys.forEach(function (v) { + core.material.images.autotile[v] = autotiles[v]; + }); + + setTimeout(function () { + core.maps._makeAutotileEdges(); + }); + +} + +// ------ 加载额外素材 ------ // + +loader.prototype._loadTilesets_sync = function (callback) { + core.material.images.tilesets = {}; + this._setStartLoadTipText("正在加载额外素材..."); + this.loadImages("tilesets", core.tilesets, core.material.images.tilesets, function () { + core.loader._loadTilesets_afterLoad(); + callback(); + }); +} + +loader.prototype._loadTilesets_async = function (onprogress, onfinished) { + core.material.images.tilesets = {}; + this.loadImagesFromZip('project/tilesets/tilesets.h5data', core.tilesets, core.material.images.tilesets, onprogress, function () { + core.loader._loadTilesets_afterLoad(); + onfinished(); + }); +} + +loader.prototype._loadTilesets_afterLoad = function () { + // 检查宽高是32倍数,如果出错在控制台报错 + for (var imgName in core.material.images.tilesets) { + var img = core.material.images.tilesets[imgName]; + if (img.width % 32 != 0 || img.height % 32 != 0) { + console.warn("警告!" + imgName + "的宽或高不是32的倍数!"); + } + if (img.width * img.height > 32 * 32 * 3000) { + console.warn("警告!" + imgName + "上的图块素材个数大于3000!"); + } + } +} + +// ------ 实际加载一系列图片 ------ // + +loader.prototype.loadImages = function (dir, names, toSave, callback) { + if (!names || names.length == 0) { + if (callback) callback(); + return; + } + var items = 0; + for (var i = 0; i < names.length; i++) { + this.loadImage(dir, names[i], function (id, image) { + core.loader._setStartLoadTipText('正在加载图片 ' + id + "..."); + if (toSave[id] !== undefined) { + if (image != null) + toSave[id] = image; + return; + } + toSave[id] = image; + items++; + core.loader._setStartProgressVal(items * (100 / names.length)); + if (items == names.length) { + if (callback) callback(); + } + }) + } +} + +loader.prototype.loadImage = function (dir, imgName, callback) { + try { + var name = imgName; + if (name.indexOf(".") < 0) + name = name + ".png"; + var image = new Image(); + image.onload = function () { + image.setAttribute('_width', image.width); + image.setAttribute('_height', image.height); + callback(imgName, image); + } + image.onerror = function () { + callback(imgName, null); + } + image.src = 'project/' + dir + '/' + name + "?v=" + main.version; + if (name.endsWith('.gif')) + callback(imgName, null); + } + catch (e) { + console.error(e); + } +} + +// ------ 从zip中加载一系列图片 ------ // + +loader.prototype.loadImagesFromZip = function (url, names, toSave, onprogress, onfinished) { + if (!names || names.length == 0) { + if (onfinished) onfinished(); + return; + } + + core.unzip(url + "?v=" + main.version, function (data) { + var cnt = 1; + names.forEach(function (name) { + var imgName = name; + if (imgName.indexOf('.') < 0) imgName += '.png'; + if (imgName in data) { + var img = new Image(); + var url = URL.createObjectURL(data[imgName]); + cnt++; + img.onload = function () { + cnt--; + URL.revokeObjectURL(url); + img.setAttribute('_width', img.width); + img.setAttribute('_height', img.height); + if (cnt == 0 && onfinished) onfinished(); + } + img.src = url; + toSave[name] = img; + } + }); + cnt--; + if (cnt == 0 && onfinished) onfinished(); + }, null, false, onprogress); +} + +// ------ 加载动画文件 ------ // + +loader.prototype._loadAnimates_sync = function () { + this._setStartLoadTipText("正在加载动画文件..."); + + if (main.supportBunch) { + if (core.animates.length > 0) { + core.http('GET', '__all_animates__?v=' + main.version + '&id=' + core.animates.join(','), null, function (content) { + var u = content.split('@@@~~~###~~~@@@'); + for (var i = 0; i < core.animates.length; ++i) { + if (u[i] != '') { + core.material.animates[core.animates[i]] = core.loader._loadAnimate(u[i]); + } else { + console.error('无法找到动画文件' + core.animates[i] + '!'); + } + } + }, "text/plain; charset=x-user-defined"); + } + return; + } + + core.animates.forEach(function (t) { + core.http('GET', 'project/animates/' + t + ".animate?v=" + main.version, null, function (content) { + core.material.animates[t] = core.loader._loadAnimate(content); + }, function (e) { + console.error(e); + core.material.animates[t] = null; + }, "text/plain; charset=x-user-defined") + }); +} + +loader.prototype._loadAnimates_async = function (onprogress, onfinished) { + core.unzip('project/animates/animates.h5data?v=' + main.version, function (animates) { + for (var name in animates) { + if (name.endsWith(".animate")) { + var t = name.substring(0, name.length - 8); + if (core.animates.indexOf(t) >= 0) + core.material.animates[t] = core.loader._loadAnimate(animates[name]); + } + } + onfinished(); + }, null, true, onprogress); +} + +loader.prototype._loadAnimate = function (content) { + try { + content = JSON.parse(content); + var data = {}; + data.ratio = content.ratio; + data.se = content.se; + data.pitch = content.pitch; + data.images = []; + content.bitmaps.forEach(function (t2) { + if (!t2) { + data.images.push(null); + } + else { + try { + var image = new Image(); + image.src = t2; + data.images.push(image); + } catch (e) { + console.error(e); + data.images.push(null); + } + } + }) + data.frame = content.frame_max; + data.frames = []; + content.frames.forEach(function (t2) { + var info = []; + t2.forEach(function (t3) { + info.push({ + 'index': t3[0], + 'x': t3[1], + 'y': t3[2], + 'zoom': t3[3], + 'opacity': t3[4], + 'mirror': t3[5] || 0, + 'angle': t3[6] || 0, + }) + }) + data.frames.push(info); + }); + return data; + } + catch (e) { + console.error(e); + return null; + } +} + +// ------ 加载音乐和音效 ------ // + +loader.prototype._loadMusic_sync = function () { + this._setStartLoadTipText("正在加载音效文件..."); + core.bgms.forEach(function (t) { + core.loader.loadOneMusic(t); + }); + core.sounds.forEach(function (t) { + core.loader.loadOneSound(t); + }); + // 直接开始播放 + core.playBgm(main.startBgm); +} + +loader.prototype._loadMusic_async = function (onprogress, onfinished) { + core.bgms.forEach(function (t) { + core.loader.loadOneMusic(t); + }); + core.unzip('project/sounds/sounds.h5data?v=' + main.version, function (data) { + // 延迟解析 + setTimeout(function () { + for (var name in data) { + if (core.sounds.indexOf(name) >= 0) { + core.loader._loadOneSound_decodeData(name, data[name]); + } + } + }); + onfinished(); + }, null, false, onprogress); + + // 直接开始播放 + core.playBgm(main.startBgm); +} + +loader.prototype.loadOneMusic = function (name) { + var music = new Audio(); + music.preload = 'none'; + if (main.bgmRemote) music.src = main.bgmRemoteRoot + core.firstData.name + '/' + name; + else music.src = 'project/bgms/' + name; + music.loop = 'loop'; + core.material.bgms[name] = music; +} + +loader.prototype.loadOneSound = function (name) { + core.http('GET', 'project/sounds/' + name + "?v=" + main.version, null, function (data) { + core.loader._loadOneSound_decodeData(name, data); + }, function (e) { + console.error(e); + core.material.sounds[name] = null; + }, null, 'arraybuffer'); +} + +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); + }) + }); + return; + } + try { + core.musicStatus.audioContext.decodeAudioData(data, function (buffer) { + core.material.sounds[name] = buffer; + }, function (e) { + console.error(e); + core.material.sounds[name] = null; + }) + } + catch (e) { + console.error(e); + core.material.sounds[name] = null; + } +} + +loader.prototype.loadBgm = function (name) { + name = core.getMappedName(name); + if (!core.material.bgms[name]) return; + // 如果没开启音乐,则不预加载 + if (!core.musicStatus.bgmStatus) return; + // 是否已经预加载过 + var index = core.musicStatus.cachedBgms.indexOf(name); + if (index >= 0) { + core.musicStatus.cachedBgms.splice(index, 1); + } + else { + // 预加载BGM + this._preloadBgm(core.material.bgms[name]); + // core.material.bgms[name].load(); + // 清理尾巴 + if (core.musicStatus.cachedBgms.length == core.musicStatus.cachedBgmCount) { + this.freeBgm(core.musicStatus.cachedBgms.pop()); + } + } + // 移动到缓存最前方 + core.musicStatus.cachedBgms.unshift(name); +} + +loader.prototype._preloadBgm = function (bgm) { + bgm.volume = 0; + bgm.play(); +} + +loader.prototype.freeBgm = function (name) { + name = core.getMappedName(name); + if (!core.material.bgms[name]) return; + // 从cachedBgms中删除 + core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function (t) { + return t != name; + }); + // 清掉缓存 + core.material.bgms[name].removeAttribute("src"); + core.material.bgms[name].load(); + core.material.bgms[name] = null; + if (name == core.musicStatus.playingBgm) { + core.musicStatus.playingBgm = null; + } + // 三秒后重新加载 + setTimeout(function () { + core.loader.loadOneMusic(name); + }, 3000); +} \ No newline at end of file diff --git a/localSave.js b/localSave.js new file mode 100644 index 0000000..bb97118 --- /dev/null +++ b/localSave.js @@ -0,0 +1,110 @@ +/** + * 离线游戏使用本地存储扩展。 + * 开启本拓展后,将会把所有存档存至 _saves 目录下。 + * 需配合样板V2.8.2+使用 + */ + +"use strict"; + +(function () { + // 将这一行改成 false 可以禁用本拓展 + var __enabled = true; + + if (window.jsinterface || !window.fs || !__enabled) return; + + function rewrite() { + core.utils._setLocalForage_set = function (name, str, callback) { + var data = LZString.compressToBase64(str); + core.saves.cache[name] = data; + fs.writeFile('_saves/' + name, data, 'utf-8', callback); + } + + core.utils._getLocalForage_get = function (name, callback) { + fs.readFile('_saves/' + name, 'utf-8', function (err, data) { + if (err) return callback(err); + callback(null, data); + }); + } + + core.utils.decompress = function (data) { + try { + return JSON.parse(LZString.decompressFromBase64(data)) + } catch (e) { + return null; + } + } + + core.utils._removeLocalForage_remove = function (name, callback) { + fs.deleteFile('_saves/' + name, callback); + } + + core.utils.clearLocalForage = function (callback) { + fs.deleteFile('_saves', function () { + fs.mkdir('_saves', callback); + }) + } + + core.utils.iterateLocalForage = function (iter, callback) { + fs.readdir('_saves', function (err, data) { + if (err) callback(err); + else { + data.forEach(function (one) { + iter(null, one, null); + }); + callback(); + } + }); + } + + core.utils.keysLocalForage = function (callback) { + fs.readdir('_saves', callback); + } + + core.utils.lengthLocalForage = function (callback) { + fs.readdir('_saves', function (err, data) { + if (err) callback(err); + else callback(null, data.length); + }); + } + } + + + var _export = function () { + var toExport = []; + + localforage.iterate(function (value, key, n) { + if (value == null || !key.startsWith(core.firstData.name)) return; + value = core.decompress(value); + if (value == null) return; + var str = JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function (chr) { + return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4) + }); + str = LZString.compressToBase64(str); + toExport.push(key); + core.saves.cache[key] = str; + fs.writeFile('_saves/' + key, str, 'utf-8', function () {}); + }, function () { + if (toExport.length > 0) { + alert('提示!本塔已开启存档本地化!原始存档已全部导出至 _saves/ 目录下。'); + } + fs.writeFile('_saves/.exported', '1', 'utf-8', function () {}); + rewrite(); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); + }); + } + + fs.mkdir('_saves', function (err) { + if (err) return; + + fs.readFile('_saves/.exported', 'utf-8', function(err, data) { + if (!err && data) { + rewrite(); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); + return; + } + _export(); + }); + }); +})(); + + diff --git a/map.d.ts b/map.d.ts new file mode 100644 index 0000000..5571239 --- /dev/null +++ b/map.d.ts @@ -0,0 +1,1402 @@ +type NotCopyPropertyInCompressedMap = + | 'firstArrive' + | 'eachArrive' + | 'blocks' + | 'parallelDo' + | 'map' + | 'bgmap' + | 'fgmap' + | 'events' + | 'changeFloor' + | 'beforeBattle' + | 'afterBattle' + | 'afterGetItem' + | 'afterOpenDoor' + | 'cannotMove' + | 'cannotMoveIn'; + +/** + * 压缩后的地图 + */ +type CompressedMap = Omit< + Floor, + NotCopyPropertyInCompressedMap +>; + +interface Block = Exclude> { + /** + * 横坐标 + */ + x: number; + + /** + * 纵坐标 + */ + y: number; + + /** + * 图块数字 + */ + id: N; + + disable: boolean; + + /** + * 事件信息 + */ + event: { + /** + * 图块类型 + */ + cls: ClsOf; + + /** + * 图块id + */ + id: NumberToId[N]; + + /** + * 图块动画帧数 + */ + animate: FrameOf>; + + /** + * 图块是否不可通行 + */ + noPass: boolean; + + /** + * 图块高度 + */ + height: 32 | 48; + + /** + * 触发器 + */ + trigger?: MotaTrigger; + + /** + * 是否可被破 + */ + canBreak?: boolean; + + /** + * 门信息 + */ + doorInfo?: DoorInfo; + + data?: any; + script?: string; + event?: MotaEvent; + }; +} + +interface FloorBase { + /** + * 楼层id + */ + floorId: T; + + /** + * 楼层在状态栏的名称 + */ + name: string; + + /** + * 地图宝石倍率 + */ + ratio: number; + + /** + * 地图的宽度 + */ + width: number; + + /** + * 地图的高度 + */ + height: number; + + /** + * 地板样式 + */ + defaultGround: AllIds; + + /** + * 楼层贴图 + */ + image: FloorAnimate[]; + + /** + * 楼层名称 + */ + title: string; + + /** + * 是否能飞出 + */ + canFlyFrom: boolean; + + /** + * 是否能飞到 + */ + canFlyTo: boolean; + + /** + * 是否能使用快捷商店 + */ + canUseQuickShop: boolean; + + /** + * 是否不可浏览 + */ + cannotViewMap?: boolean; + + /** + * 是否不能瞬移 + */ + cannotMoveDirectly?: boolean; + + /** + * 是否是地下层 + */ + underGround?: boolean; + + /** + * 自动事件 + */ + autoEvent: Record; + + /** + * 天气 + */ + weather?: [type: string, level: WeatherLevel]; + + /** + * 事件层地图 + */ + map: number[][]; + + /** + * 并行脚本 + */ + parallelDo?: string; + + /** + * 色调 + */ + color?: Color; + + /** + * 背景音乐 + */ + bgm?: BgmIds | BgmIds[]; +} + +interface Floor extends FloorBase { + /** + * 图块信息 + */ + blocks: Block[]; + + /** + * 是否被砍层 + */ + deleted?: boolean; + + /** + * 是否被强制砍层 + */ + forceDelete?: boolean; +} + +interface ResolvedFloor extends FloorBase { + /** + * 战后事件 + */ + afterBattle: Record; + + /** + * 获得道具后事件 + */ + afterGetItem: Record; + + /** + * 开门后事件 + */ + afterOpenDoor: Record; + + /** + * 战前事件 + */ + beforeBattle: Record; + + /** + * 不可出方向 + */ + cannotMove: Record; + + /** + * 不可入方向 + */ + cannotMoveIn: Record; + + /** + * 普通事件 + */ + events: Record; + + /** + * 背景层 + */ + bgmap: number[][]; + + /** + * 前景层 + */ + fgmap: number[][]; + + /** + * 楼层切换 + */ + changeFloor: Record; + + /** + * 首次到达事件 + */ + firstArrive?: MotaEvent; + + /** + * 每次到达事件 + */ + eachArrive?: MotaEvent; +} + +interface BlockInfo { + /** + * 图块数字 + */ + number: T; + + /** + * 图块id + */ + id: NumberToId[T]; + + /** + * 图块类型 + */ + cls: ClsOf; + + /** + * 图块名称 + */ + name: string; + + /** + * 图片信息 + */ + image: HTMLImageElement; + + /** + * 图块所在图片的横坐标 + */ + posX: number; + + /** + * 图块所在图片的纵坐标 + */ + posY: number; + + /** + * 门信息 + */ + doorInfo: DoorInfo; + + /** + * 图片的高度 + */ + height: 32 | 48; + + /** + * faceId信息 + */ + faceIds: Record; + + /** + * 动画帧数 + */ + animate: FrameOf>; + + /** + * 朝向 + */ + face: Dir; + + /** + * 大怪物信息 + */ + bigImage: HTMLImageElement; +} + +interface DrawMapConfig { + /** + * 是否是重绘 + */ + redraw: boolean; + + /** + * 要绘制到的画布 + */ + ctx: CtxRefer; + + /** + * 是否是在地图画布上绘制的 + */ + onMap: boolean; +} + +interface DrawThumbnailConfig { + /** + * 勇士的位置 + */ + heroLoc: LocArr; + + /** + * 勇士的图标 + */ + heroIcon: ImageIds; + + /** + * 是否绘制显伤 + */ + damage: boolean; + + /** + * 变量信息,存读档时使用,可以无视 + */ + flags: Flags; + + /** + * 绘制到的画布 + */ + ctx: CtxRefer; + + /** + * 绘制位置的横坐标 + */ + x: number; + + /** + * 绘制位置的纵坐标 + */ + y: number; + + /** + * 绘制大小,比例数字,例如1代表与实际地图大小相同 + */ + size: number; + + /** + * 绘制全图 + */ + all: boolean; + + /** + * 绘制的视野中心横坐标 + */ + centerX: number; + + /** + * 绘制的视野中心纵坐标 + */ + centerY: number; + + /** + * 是否不是高清画布,存读档时使用,可以无视 + */ + noHD: boolean; + + /** + * 是否使用v2优化 + */ + v2: boolean; + + /** + * 是否在小地图中出现 + */ + inFlyMap: boolean; + + /** + * 小地图模式下的宽度 + */ + w: number; + + /** + * 小地图模式下的高度 + */ + h: number; +} + +interface BlockFilter { + /** + * 高斯模糊 + */ + blur: number; + + /** + * 色相旋转 + */ + hue: number; + + /** + * 饱和度 + */ + grayscale: number; + + /** + * 反色 + */ + invert: boolean; + + /** + * 阴影 + */ + shadow: boolean; +} + +interface SearchedBlock { + /** + * 横坐标 + */ + x: number; + + /** + * 纵坐标 + */ + y: number; + + /** + * 楼层id + */ + floorId: FloorIds; + + /** + * 图块信息 + */ + block: Block; +} + +/** + * 负责一切和地图相关的处理内容 + */ +interface Maps { + /** + * 图块信息 + */ + blocksInfo: { + [P in keyof NumberToId]: MapDataOf

      ; + }; + + /** + * 加载某个楼层 + * @param floorId 楼层id + * @param map 地图信息,不填表示直接从原地图中获取 + */ + loadFloor( + floorId: T, + map?: number[][] | { map: number[][] } + ): ResolvedFloor; + + /** + * 解析地图信息 + * @param map 地图id或地图对象 + */ + extractBlocks(map?: FloorIds | ResolvedFloor): void; + + /** + * 根据需求为UI解析出blocks + * @param map 地图信息 + * @param flags 变量信息 + */ + extractBlocksForUI(map?: ResolvedFloor, flags?: Record): void; + + /** + * 根据图块id得到数字(地图矩阵中的值) + * @example core.getNumberById('yellowWall'); // 1 + * @param id 图块id + * @returns 图块的数字 + */ + getNumberById(id: T): IdToNumber[T]; + + /** + * 根据数字获得图块 + * @param number 图块数字 + */ + getBlockByNumber(number: T): Block; + + /** + * 根据ID获得图块 + * @param id 图块的id + */ + getBlockById(id: T): Block; + + /** + * 获得当前事件点的ID + */ + getIdOfThis(id?: 'this' | AllIds): string; + + /** + * 初始化一个图块 + * @param x 横坐标 + * @param y 纵坐标 + * @param id 图块的id + * @param addInfo 是否添加触发器信息等 + * @param eventFloor 所在地图信息 + */ + initBlock( + x: number, + y: number, + id: AllIds | AllNumbers, + addInfo?: boolean, + eventFloor?: ResolvedFloor + ): Block; + + /** + * 压缩地图 + * @param mapArr 地图数组 + * @param floorId 地图id + */ + compressMap(mapArr?: number[][], floorId?: FloorIds): number[][]; + + /** + * 设置图块的不透明度 + * @param opacity 不透明度 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + */ + setBlockOpacity( + opacity: number, + x: number, + y: number, + floorId?: FloorIds + ): void; + + /** + * 设置图块的滤镜 + * @param filter 滤镜信息 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + */ + setBlockFilter( + filter: string, + x: number, + y: number, + floorId?: FloorIds + ): void; + + /** + * 获取某个图块是否被强制启用或禁用 + * @param floorId 楼层id + * @param x 横坐标 + * @param y 纵坐标 + * @param flags 变量信息 + */ + isMapBlockDisabled( + floorId: FloorIds, + x: number, + y: number, + flags?: Record + ); + + /** + * 设置某个点的图块强制启用/禁用状态 + * @param floorId 楼层id + * @param x 横坐标 + * @param y 纵坐标 + * @param disable 是否禁用 + */ + setMapBlockDisabled( + floorId: FloorIds, + x: number, + y: number, + disable: boolean + ); + + /** + * 解压缩地图 + * @param mapArr 地图信息 + * @param floorId 地图id + */ + decompressMap(mapArr?: number[][], floorId?: FloorIds): number[][]; + + /** + * 将所有地图重新变成数字,以便于存档 + */ + saveMap(): { [P in FloorIds]?: Partial> }; + /** + * 将某个地图重新变成数字,以便于存档 + */ + saveMap(floorId: FloorIds): Partial; + + /** + * 将存档中的地图信息重新读取出来 + * @param data 多个楼层的信息 + * @param floorId 在这里没有用 + * @param flags 变量信息 + */ + loadMap( + data: { [P in T]: Partial> }, + floorId?: undefined, + flags?: Record + ): { [P in T]: ResolvedFloor }; + /** + * 加载某个楼层的信息 + * @param data 多个楼层的信息 + * @param floorId 加载的楼层 + */ + loadMap( + data: { [P in T]?: Partial> }, + floorId: T + ): ResolvedFloor; + + /** + * 更改地图画布的尺寸 + * @param floorId 楼层id + */ + resizeMap(floorId?: FloorIds): void; + + /** + * 生成事件层矩阵 + * @example core.getMapArray('MT0'); // 生成主塔0层的事件层矩阵,隐藏的图块视为0 + * @param floorId 地图id,不填视为当前地图 + * @param noCache 是否清空缓存 + * @returns 事件层矩阵,注意对其阵元的访问是[y][x] + */ + getMapArray(floorId?: FloorIds, noCache?: boolean): AllNumbers[][]; + + /** + * 获取图块的事件层数字 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param noCache 是否清空缓存 + */ + getMapNumber( + x: number, + y: number, + floorId?: FloorIds, + noCache?: boolean + ): AllNumbers; + + /** + * 以x,y的形式返回每个点的事件 + * @param floorId 楼层id + * @param noCache 是否不使用缓存 + */ + getMapBlocksObj( + floorId?: FloorIds, + noCache?: boolean + ): Record; + + /** + * 获取背景层的图块矩阵 + * @param floorId 楼层id + */ + getBgMapArray(floorId: FloorIds): AllNumbers[][]; + + /** + * 获取前景层的图块矩阵 + * @param floorId 楼层id + */ + getFgMapArray(floorId: FloorIds): AllNumbers[][]; + + /** + * 判定背景层的一个位置是什么 + * @example core.getBgNumber(); // 判断主角脚下的背景层图块的数字 + * @param x 横坐标,不填为当前勇士坐标 + * @param y 纵坐标,不填为当前勇士坐标 + * @param floorId 地图id,不填视为当前地图 + * @param noCache 是否不使用缓存 + */ + getBgNumber( + x?: number, + y?: number, + floorId?: FloorIds, + noCache?: boolean + ): AllNumbers; + + /** + * 判定前景层的一个位置是什么 + * @example core.getFgNumber(); // 判断主角脚下的前景层图块的数字 + * @param x 横坐标,不填为当前勇士坐标 + * @param y 纵坐标,不填为当前勇士坐标 + * @param floorId 地图id,不填视为当前地图 + * @param noCache 是否不使用缓存 + */ + getFgNumber( + x?: number, + y?: number, + floorId?: FloorIds, + noCache?: boolean + ): AllNumbers; + + /** + * 可通行性判定 + * @example core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动 + * @param floorId 地图id,不填视为当前地图 + * @returns 从各点可移动方向的三维数组 + */ + generateMovableArray(floorId?: FloorIds): Dir[][][]; + + /** + * 单点单朝向的可通行性判定,不判断nopass + * @exmaple core.canMoveHero(); // 判断主角是否可以前进一步 + * @param x 起点横坐标,不填视为主角当前的 + * @param y 起点纵坐标,不填视为主角当前的 + * @param direction 移动的方向,不填视为主角面对的方向 + * @param floorId 地图id,不填视为当前地图 + * @returns true表示可移动,false表示不可移动 + */ + canMoveHero( + x?: number, + y?: number, + direction?: Dir, + floorId?: FloorIds + ): boolean; + + /** + * 能否瞬移到某点,并求出节约的步数。 + * @example core.canMoveDirectly(0, 0); // 能否瞬移到地图左上角 + * @param destX 目标点的横坐标 + * @param destY 目标点的纵坐标 + * @returns 正数表示节约的步数,-1表示不可瞬移 + */ + canMoveDirectly(destX: number, destY: number): number; + + /** + * 获得某些点可否通行的信息 + * @param locs 目标路径 + * @param canMoveArray 可通行信息 + */ + canMoveDirectlyArray(locs: LocArr[], canMoveArray?: Dir[][][]): number[]; + + /** + * 自动寻路 + * @example core.automaticRoute(0, 0); // 自动寻路到地图左上角 + * @param destX 目标点的横坐标 + * @param destY 目标点的纵坐标 + * @returns 每步走完后主角的loc属性组成的一维数组 + */ + automaticRoute(destX: number, destY: number): DiredLoc[]; + + /** + * 绘制一个图块 + * @param block 要绘制的图块 + * @param animate 绘制图块的第几帧 + * @param ctx 绘制到的画布 + */ + drawBlock(block?: Block, animate?: number, ctx?: CtxRefer): void; + + /** + * 生成groundPattern + * @param floorId 楼层id + */ + generateGroundPattern(floorId?: FloorIds): void; + + /** + * 地图绘制 + * @example core.drawMap(); // 绘制当前地图 + * @param floorId 地图id,不填表示当前楼层 + */ + drawMap(floorId?: FloorIds): void; + + /** + * 重绘地图 + */ + redrawMap(): void; + + /** + * 绘制背景层(含贴图,其与背景层矩阵的绘制顺序可通过复写此函数来改变) + * @example core.drawBg(); // 绘制当前地图的背景层 + * @param floorId 地图id,不填视为当前地图 + * @param config 配置信息 + */ + drawBg(floorId?: FloorIds, config?: Partial): void; + + /** + * 绘制事件层 + * @example core.drawEvents(); // 绘制当前地图的事件层 + * @param floorId 地图id,不填视为当前地图 + * @param blocks 一般不需要 + * @param config 配置信息 + */ + drawEvents( + floorId?: FloorIds, + blocks?: Block[], + config?: Partial + ): void; + + /** + * 绘制前景层(含贴图,其与前景层矩阵的绘制顺序可通过复写此函数来改变) + * @example core.drawFg(); // 绘制当前地图的前景层 + * @param floorId 地图id,不填视为当前地图 + * @param config 配置信息 + */ + drawFg(floorId?: FloorIds, config?: Partial): void; + + /** + * 绘制缩略图 + * @example core.drawThumbnail(); // 绘制当前地图的缩略图 + * @param floorId 地图id,不填视为当前地图 + * @param blocks 一般不需要 + * @param options 额外的绘制项,可以增绘主角位置和朝向、采用不同于游戏中的主角行走图、增绘显伤、提供flags用于存读档 + */ + drawThumbnail( + floorId?: FloorIds, + blocks?: Block[], + options?: Partial + ): void; + + /** + * 判定某个点是否不可被踏入(不基于主角生命值和图块cannotIn属性) + * @example core.noPass(0, 0); // 判断地图左上角能否被踏入 + * @param x 目标点的横坐标 + * @param y 目标点的纵坐标 + * @param floorId 目标点所在的地图id,不填视为当前地图 + */ + noPass(x: number, y: number, floorId?: FloorIds): boolean; + + /** + * 某个点是否存在NPC + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + */ + npcExists(x: number, y: number, floorId?: FloorIds): boolean; + + /** + * 某个点是否存在(指定的)地形 + * @param x 横坐标 + * @param y 纵坐标 + * @param id 地形的id + * @param floorId 楼层id + */ + terrainExists( + x: number, + y: number, + id?: AllIdsOf<'terrains'>, + floorId?: FloorIds + ): boolean; + + /** + * 某个点是否存在楼梯 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + */ + stairExists(x: number, y: number, floorId?: FloorIds): boolean; + + /** + * 当前位置是否在楼梯边;在楼传平面塔模式下对箭头也有效 + */ + nearStair(): boolean; + + /** + * 某个点是否存在(指定的)怪物 + * @param x 横坐标 + * @param y 纵坐标 + * @param id 怪物的id + * @param floorId 楼层id + */ + enemyExists( + x: number, + y: number, + id?: AllIdsOf<'enemys' | 'enemy48'>, + floorId?: FloorIds + ): boolean; + + /** + * 获得某个点的block + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param showDisable 被禁用的图块是否也能被获取 + */ + getBlock( + x: number, + y: number, + floorId?: FloorIds, + showDisable?: boolean + ): Block; + + /** + * 判定某个点的图块id + * @example + * // 一个简单的机关门事件,打败或炸掉这一对绿头怪和红头怪就开门 + * if ( + * core.getBlockId(x1, y1) !== 'greenSlime' && + * core.getBlockId(x2, y2) !== 'redSlime' + * ) + * core.openDoor(x3, y3); + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 被禁用的图块是否也能被获取 + * @returns 图块id,该点无图块则返回null + */ + getBlockId( + x: number, + y: number, + floorId?: FloorIds, + showDisable?: boolean + ): AllIds | null; + + /** + * 判定某个点的图块数字 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 被禁用的图块是否也能被获取 + */ + getBlockNumber( + x: number, + y: number, + floorId?: FloorIds, + showDisable?: boolean + ): AllNumbers; + + /** + * 判定某个点的图块类型 + * @example + * // 另一个简单的机关门事件,打败或炸掉这一对不同身高的敌人就开门 + * if (core.getBlockCls(x1, y1) !== 'enemys' && core.getBlockCls(x2, y2) !== 'enemy48') core.openDoor(x3, y3); + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 被禁用的图块是否也能被获取 + */ + getBlockCls( + x: number, + y: number, + floorId?: FloorIds, + showDisable?: boolean + ): Cls | null; + + /** + * 获取图块的不透明度 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 被禁用的图块是否也能被获取 + */ + getBlockOpacity( + x: number, + y: number, + floorId?: FloorIds, + showDisable?: boolean + ): number | null; + + /** + * 获取图块的滤镜 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 被禁用的图块是否也能被获取 + */ + getBlockFilter( + x: number, + y: number, + floorId?: FloorIds, + showDisable?: boolean + ): BlockFilter | null; + + /** + * 获得某个图块或素材的信息 + * @param block 图块信息,可以填图块,数字,id + */ + getBlockInfo( + block?: Block | NumberToId[T] | T + ): BlockInfo; + + /** + * 搜索图块, 支持通配符 + * @example core.searchBlock('*Door'); // 搜索当前地图的所有门 + * @param id 图块id,支持星号表示任意多个(0个起)字符 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 隐藏点是否计入,true表示计入 + */ + searchBlock( + id: string, + floorId?: FloorIds | FloorIds[], + showDisable?: boolean + ): SearchedBlock[]; + + /** + * 根据给定的筛选函数搜索全部满足条件的图块 + * @example + * // 搜索当前地图的所有门 + * core.searchBlockWithFilter(function (block) { return block.event.id.endsWith('Door'); }); + * @param blockFilter 筛选函数,可接受block输入,应当返回一个boolean值 + * @param floorId 地图id,不填视为当前地图 + * @param showDisable 隐藏点是否计入,true表示计入 + * @returns 一个详尽的数组 + */ + searchBlockWithFilter( + blockFilter: (block: Block) => boolean, + floorId?: FloorIds | FloorIds[], + showDisable?: boolean + ): SearchedBlock[]; + + /** + * 获得某个图块对应行走图朝向向下的那一项的id,如果不存在行走图绑定则返回自身id + * @param block 要获得的图块 + */ + getFaceDownId(block: Block | AllIds | AllNumbers): AllIds; + + /** + * 显示(隐藏或显示的)图块,此函数将被“显示事件”指令和勾选了“不消失”的“移动/跳跃事件”指令(如阻击怪)的终点调用 + * @example core.showBlock(0, 0); // 显示地图左上角的图块 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + */ + showBlock(x: number, y: number, floorId?: FloorIds): void; + + /** + * 隐藏一个图块,对应于「隐藏事件」且不删除 + * @example core.hideBlock(0, 0); // 隐藏地图左上角的图块 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + */ + hideBlock(x: number, y: number, floorId?: FloorIds): void; + + /** + * 根据图块的索引来隐藏图块 + * @param index 要隐藏的图块的索引 + * @param floorId 地图id + */ + hideBlockByIndex(index: number, floorId?: FloorIds): void; + + /** + * 一次性隐藏多个block + * @param indexes 索引列表 + * @param floorId 地图id + */ + hideBlockByIndexes(indexes: number[], floorId?: FloorIds): void; + + /** + * 删除一个图块,对应于「隐藏事件」并同时删除 + * @example core.removeBlock(0, 0); // 尝试删除地图左上角的图块 + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @returns 有没有删除成功 + */ + removeBlock(x: number, y: number, floorId?: FloorIds): boolean; + + /** + * 根据block的索引(尽可能)删除该块 + * @param index 要删除的图块的索引 + * @param floorId 地图id + */ + removeBlockByIndex(index: number, floorId?: FloorIds): void; + + /** + * 一次性删除多个block + * @param indexes 索引列表 + * @param floorId 地图id + */ + removeBlockByIndexes(indexes: number[], floorId?: FloorIds): void; + + /** + * 显示前景/背景地图 + * @param name 图层名 + * @param loc 要显示的坐标列表 + * @param floorId 楼层id + * @param callback 显示完毕的回调函数 + */ + showBgFgMap( + name: 'bg' | 'bg2' | 'fg' | 'fg2', + loc: LocArr | LocArr[], + floorId?: FloorIds, + callback?: () => void + ): void; + + /** + * 隐藏前景/背景地图 + * @param name 图层名 + * @param loc 要显示的坐标列表 + * @param floorId 楼层id + * @param callback 显示完毕的回调函数 + */ + hideBgFgMap( + name: 'bg' | 'bg2' | 'fg' | 'fg2', + loc: LocArr | LocArr[], + floorId?: FloorIds, + callback?: () => void + ): void; + + /** + * 显示一个楼层贴图 + * @param loc 楼层贴图的位置 + * @param floorId 楼层id + * @param callback 显示完毕后的回调函数 + */ + showFloorImage( + loc?: LocArr | LocArr[], + floorId?: FloorIds, + callback?: () => void + ): void; + + /** + * 隐藏一个楼层贴图 + * @param loc 楼层贴图的位置 + * @param floorId 楼层id + * @param callback 显示完毕后的回调函数 + */ + hideFloorImage( + loc?: LocArr | LocArr[], + floorId?: FloorIds, + callback?: () => void + ): void; + + /** + * 转变图块 + * @example core.setBlock(1, 0, 0); // 把地图左上角变成黄墙 + * @param number 新图块的数字或id + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + * @param noredraw 是否不重绘地图 + */ + setBlock( + number: AllIds | AllNumbers | `${AllNumbers}`, + x: number, + y: number, + floorId?: FloorIds, + noredraw?: boolean + ): void; + + /** + * 动画形式转变某点图块 + * @param number 要转变成的图块的数字或id + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 楼层id + * @param time 转变时间 + * @param callback 转变开始或完成后的回调函数 + */ + animateSetBlock( + number: AllIds | AllNumbers | `${AllNumbers}`, + x: number, + y: number, + floorId?: FloorIds, + time?: number, + callback?: () => void + ): void; + + /** + * 动画形式转变若干点图块 + * @param number 要转变成的图块的数字或id + * @param locs 坐标数组 + * @param floorId 楼层id + * @param time 转变时间 + * @param callback 转变开始或完成后的回调函数 + */ + animateSetBlocks( + number: AllIds | AllNumbers | `${AllNumbers}`, + locs: LocArr | LocArr[], + floorId?: FloorIds, + time?: number, + callback?: () => void + ): void; + + /** + * 某个图块转向 + * @param direction 转向的方向 + * @param x 图块所在横坐标 + * @param y 图块所在纵坐标 + * @param floorId 楼层id + */ + turnBlock( + direction: HeroTurnDir, + x: number, + y: number, + floorId?: FloorIds + ): void; + + /** + * 批量替换图块 + * @example core.replaceBlock(21, 22, core.floorIds); // 把游戏中地上当前所有的黄钥匙都变成蓝钥匙 + * @param fromNumber 旧图块的数字 + * @param toNumber 新图块的数字 + * @param floorId 地图id或其数组,不填视为当前地图 + */ + replaceBlock( + fromNumber: AllNumbers, + toNumber: AllNumbers, + floorId?: FloorIds | FloorIds[] + ): void; + + /** + * 转变图层块 + * @example core.setBgFgBlock('bg', 167, 6, 6); // 把当前地图背景层的中心块改为滑冰 + * @param name 背景还是前景 + * @param number 新图层块的数字或id + * @param x 横坐标 + * @param y 纵坐标 + * @param floorId 地图id,不填视为当前地图 + */ + setBgFgBlock( + name: 'bg' | 'fg' | 'bg2' | 'fg2', + number: AllIds | AllNumbers | `${AllNumbers}`, + x: number, + y: number, + floorId?: FloorIds + ): void; + + /** + * 重置地图,注意该功能原则上只用于调试,游戏中不应当出现 + * @param floorId 楼层id或数组 + */ + resetMap(floorId?: FloorIds | FloorIds[]): void; + + /** + * 移动图块 + * @example core.moveBlock(0, 0, ['down']); // 令地图左上角的图块下移一格,用时半秒,再花半秒淡出 + * @param x 起点的横坐标 + * @param y 起点的纵坐标 + * @param steps 步伐数组 + * @param time 单步和淡出用时,单位为毫秒。不填视为半秒 + * @param keep 是否不淡出,true表示不淡出 + * @param callback 移动或淡出后的回调函数 + */ + moveBlock( + x: number, + y: number, + steps: Step[], + time?: number, + keep?: boolean, + callback?: () => void + ): void; + + /** + * 跳跃图块;从V2.7开始不再有音效 + * @example core.jumpBlock(0, 0, 0, 0); // 令地图左上角的图块原地跳跃半秒,再花半秒淡出 + * @param sx 起点的横坐标 + * @param sy 起点的纵坐标 + * @param ex 终点的横坐标 + * @param ey 终点的纵坐标 + * @param time 单步和淡出用时,单位为毫秒。不填视为半秒 + * @param keep 是否不淡出,true表示不淡出 + * @param callback 落地或淡出后的回调函数 + */ + jumpBlock( + sx: number, + sy: number, + ex: number, + ey: number, + time?: number, + keep?: boolean, + callback?: () => void + ): void; + + /** + * 显示/隐藏某个块时的动画效果 + * @param loc 要显示或隐藏的坐标数组 + * @param type 显示还是隐藏还是移除,填数字表示设置不透明度 + * @param time 动画时间 + * @param callback 动画完毕后的回调函数 + */ + animateBlock( + loc: LocArr | LocArr[], + type: 'show' | 'hide' | 'remove' | number, + time: number, + callback?: () => void + ): void; + + /** + * 添加一个全局动画 + * @param block 图块信息 + */ + addGlobalAnimate(block?: Block): void; + + /** + * 删除所有全局动画 + */ + removeGlobalAnimate(): void; + /** + * 删除一个全局动画 + * @param x 横坐标 + * @param y 纵坐标 + */ + removeGlobalAnimate(x?: number, y?: number): void; + + /** + * 绘制UI层的box动画 + */ + drawBoxAnimate(): void; + + /** + * 播放动画,注意即使指定了主角的坐标也不会跟随主角移动,如有需要请使用core.drawHeroAnimate(name, callback)函数 + * @example core.drawAnimate('attack', core.nextX(), core.nextY(), false, core.vibrate); // 在主角面前一格播放普攻动画,动画停止后视野左右抖动1秒 + * @param name 动画文件名,不含后缀 + * @param x 绝对横坐标 + * @param y 绝对纵坐标 + * @param alignWindow 是否是相对窗口的坐标 + * @param callback 动画停止后的回调函数 + * @returns 一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数) + */ + drawAnimate( + name: AnimationIds | NameMapIn, + x: number, + y: number, + alignWindow?: boolean, + callback?: () => void + ): number; + + /** + * 播放跟随勇士的动画 + * @param name 动画名 + * @param callback 动画停止后的回调函数 + * @returns 一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数) + */ + drawHeroAnimate( + name: AnimationIds | NameMapIn, + callback?: () => void + ): number; + + /** + * 获得当前正在播放的所有(指定)动画的id列表 + * @param name 指定名称 + */ + getPlayingAnimates(name?: AnimationIds): number[]; + + /** + * 立刻停止一个动画播放 + * @param id 播放动画的编号,即drawAnimate或drawHeroAnimate返回值,不填则停止所有的 + * @param doCallback 是否执行该动画的回调函数 + */ + stopAnimate(id?: number, doCallback?: boolean): void; + + _makeAutotileEdges(): void; + + _initDetachedBlock( + info: BlockInfo, + x: number, + y: number, + displayDamage: boolean + ): { + headCanvas: string | null; + bodyCanvas: string; + damageCanvas: string | null; + }; + + _getBigImageInfo(bigImage: HTMLImageElement, face: Dir, posX: number): any; +} + +declare const maps: new () => Maps; diff --git a/maps.js b/maps.js new file mode 100644 index 0000000..22114d9 --- /dev/null +++ b/maps.js @@ -0,0 +1,1002 @@ +var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = +{ + "1": {"cls":"animates","id":"yellowWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, + "2": {"cls":"animates","id":"whiteWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, + "3": {"cls":"animates","id":"blueWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, + "4": {"cls":"animates","id":"star","name":"星空"}, + "5": {"cls":"animates","id":"lava","name":"岩浆"}, + "6": {"cls":"animates","id":"ice","doorInfo":{"time":160,"openSound":"破冰镐","closeSound":"door.mp3","keys":{"icePickaxe":1}},"animate":1}, + "7": {"cls":"terrains","id":"blueShopLeft","canPass":true}, + "8": {"cls":"terrains","id":"blueShopRight"}, + "9": {"cls":"terrains","id":"pinkShopLeft"}, + "10": {"cls":"terrains","id":"pinkShopRight"}, + "11": {"cls":"animates","id":"lavaNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 血网的伤害效果移动到 checkBlock 中处理\n\n\t// 如果要做一次性血网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();","name":"血网"}, + "12": {"cls":"animates","id":"poisonNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'poison');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"毒网"}, + "13": {"cls":"animates","id":"weakNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'weak');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"衰网"}, + "14": {"cls":"animates","id":"curseNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'curse');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"咒网"}, + "15": {"cls":"animates","id":"blueLava"}, + "16": {"cls":"animates","id":"water"}, + "20": {"cls":"autotile","id":"autotile"}, + "21": {"cls":"items","id":"yellowKey"}, + "22": {"cls":"items","id":"blueKey"}, + "23": {"cls":"items","id":"redKey"}, + "24": {"cls":"items","id":"greenKey"}, + "25": {"cls":"items","id":"steelKey"}, + "26": {"cls":"items","id":"bigKey"}, + "27": {"cls":"items","id":"redGem"}, + "28": {"cls":"items","id":"blueGem"}, + "29": {"cls":"items","id":"greenGem"}, + "30": {"cls":"items","id":"yellowGem"}, + "31": {"cls":"items","id":"redPotion"}, + "32": {"cls":"items","id":"bluePotion"}, + "33": {"cls":"items","id":"greenPotion"}, + "34": {"cls":"items","id":"yellowPotion"}, + "35": {"cls":"items","id":"sword1"}, + "36": {"cls":"items","id":"shield1"}, + "37": {"cls":"items","id":"sword2"}, + "38": {"cls":"items","id":"shield2"}, + "39": {"cls":"items","id":"sword3"}, + "40": {"cls":"items","id":"shield3"}, + "41": {"cls":"items","id":"sword4"}, + "42": {"cls":"items","id":"shield4"}, + "43": {"cls":"items","id":"sword5"}, + "44": {"cls":"items","id":"shield5"}, + "45": {"cls":"items","id":"book"}, + "46": {"cls":"items","id":"fly"}, + "47": {"cls":"items","id":"pickaxe"}, + "48": {"cls":"items","id":"icePickaxe"}, + "49": {"cls":"items","id":"bomb"}, + "50": {"cls":"items","id":"centerFly"}, + "51": {"cls":"items","id":"upFly"}, + "52": {"cls":"items","id":"downFly"}, + "53": {"cls":"items","id":"coin"}, + "54": {"cls":"items","id":"freezeBadge"}, + "55": {"cls":"items","id":"cross"}, + "56": {"cls":"items","id":"superPotion"}, + "57": {"cls":"items","id":"earthquake"}, + "58": {"cls":"items","id":"poisonWine"}, + "59": {"cls":"items","id":"weakWine"}, + "60": {"cls":"items","id":"curseWine"}, + "61": {"cls":"items","id":"superWine"}, + "62": {"cls":"items","id":"dagger"}, + "63": {"cls":"items","id":"pack"}, + "64": {"cls":"items","id":"amulet"}, + "65": {"cls":"items","id":"hammer"}, + "68": {"cls":"items","id":"lifeWand"}, + "69": {"cls":"items","id":"jumpShoes"}, + "70": {"cls":"items","id":"sword0"}, + "71": {"cls":"items","id":"shield0"}, + "72": {"cls":"items","id":"skill1"}, + "73": {"cls":"items","id":"wand"}, + "81": {"cls":"animates","id":"yellowDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"yellowKey":1}},"name":"黄门"}, + "82": {"cls":"animates","id":"blueDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"blueKey":1}},"name":"蓝门"}, + "83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"redKey":1}},"name":"红门"}, + "84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"greenKey":1}},"name":"绿门"}, + "85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"specialKey":1}},"name":"机关门"}, + "86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}},"name":"铁门"}, + "87": {"cls":"terrains","id":"upFloor","canPass":true}, + "88": {"cls":"terrains","id":"downFloor","canPass":true}, + "89": {"cls":"animates","id":"portal","canPass":true}, + "90": {"cls":"animates","id":"starPortal","canPass":true}, + "91": {"cls":"animates","id":"upPortal","canPass":true}, + "92": {"cls":"animates","id":"leftPortal","canPass":true}, + "93": {"cls":"animates","id":"downPortal","canPass":true}, + "94": {"cls":"animates","id":"rightPortal","canPass":true}, + "101": {"cls":"animates","id":"crystalUp"}, + "102": {"cls":"animates","id":"crystalBottom"}, + "103": {"cls":"animates","id":"fire"}, + "104": {"cls":"animates","id":"switch"}, + "109": {"cls":"animates","id":"magentaWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{}}}, + "121": {"cls":"npcs","id":"man"}, + "122": {"cls":"npcs","id":"trader"}, + "123": {"cls":"npcs","id":"thief"}, + "124": {"cls":"npcs","id":"fairy"}, + "125": {"cls":"npcs","id":"wizard"}, + "126": {"cls":"npcs","id":"recluse"}, + "127": {"cls":"npcs","id":"king"}, + "128": {"cls":"npcs","id":"youngMan"}, + "129": {"cls":"npcs","id":"sign"}, + "130": {"cls":"npcs","id":"expShop"}, + "131": {"cls":"npcs","id":"moneyShop"}, + "132": {"cls":"npcs","id":"princess"}, + "133": {"cls":"npc48","id":"npc0","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, + "134": {"cls":"npc48","id":"npc1","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, + "135": {"cls":"npc48","id":"npc2","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, + "136": {"cls":"npc48","id":"npc3","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, + "137": {"cls":"npcs","id":"greenMan"}, + "138": {"cls":"npcs","id":"blueTrader"}, + "151": {"cls":"autotile","id":"autotile1"}, + "152": {"cls":"autotile","id":"autotile2"}, + "153": {"cls":"autotile","id":"autotile3"}, + "161": {"cls":"terrains","id":"arrowUp","canPass":true,"cannotOut":["left","right","down"],"cannotIn":["up"]}, + "162": {"cls":"terrains","id":"arrowDown","canPass":true,"cannotOut":["left","right","up"],"cannotIn":["down"]}, + "163": {"cls":"terrains","id":"arrowLeft","canPass":true,"cannotOut":["up","down","right"],"cannotIn":["left"]}, + "164": {"cls":"terrains","id":"arrowRight","canPass":true,"cannotOut":["up","down","left"],"cannotIn":["right"]}, + "165": {"cls":"terrains","id":"light","trigger":"null","canPass":true,"script":"(function () {\n\tcore.setBlock(core.getNumberById('darkLight'), core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();"}, + "166": {"cls":"terrains","id":"darkLight"}, + "167": {"cls":"terrains","id":"ski","trigger":"ski","canPass":true}, + "168": {"cls":"terrains","id":"flower","canPass":true}, + "169": {"cls":"terrains","id":"box","trigger":"pushBox"}, + "170": {"cls":"terrains","id":"boxed","trigger":null}, + "201": {"cls":"enemys","id":"greenSlime"}, + "202": {"cls":"enemys","id":"redSlime"}, + "203": {"cls":"enemys","id":"blackSlime"}, + "204": {"cls":"enemys","id":"slimelord"}, + "205": {"cls":"enemys","id":"bat"}, + "206": {"cls":"enemys","id":"bigBat"}, + "207": {"cls":"enemys","id":"redBat"}, + "208": {"cls":"enemys","id":"vampire"}, + "209": {"cls":"enemys","id":"skeleton"}, + "210": {"cls":"enemys","id":"skeletonWarrior"}, + "211": {"cls":"enemys","id":"skeletonCaptain"}, + "212": {"cls":"enemys","id":"ghostSoldier"}, + "213": {"cls":"enemys","id":"zombie"}, + "214": {"cls":"enemys","id":"zombieKnight"}, + "215": {"cls":"enemys","id":"rock"}, + "216": {"cls":"enemys","id":"slimeman"}, + "217": {"cls":"enemys","id":"bluePriest"}, + "218": {"cls":"enemys","id":"redPriest"}, + "219": {"cls":"enemys","id":"brownWizard"}, + "220": {"cls":"enemys","id":"redWizard"}, + "221": {"cls":"enemys","id":"yellowGateKeeper"}, + "222": {"cls":"enemys","id":"blueGateKeeper"}, + "223": {"cls":"enemys","id":"redGateKeeper"}, + "224": {"cls":"enemys","id":"swordsman"}, + "225": {"cls":"enemys","id":"soldier"}, + "226": {"cls":"enemys","id":"yellowKnight"}, + "227": {"cls":"enemys","id":"redKnight"}, + "228": {"cls":"enemys","id":"darkKnight"}, + "229": {"cls":"enemys","id":"blackKing"}, + "230": {"cls":"enemys","id":"yellowKing"}, + "231": {"cls":"enemys","id":"greenKing"}, + "232": {"cls":"enemys","id":"blueKnight"}, + "233": {"cls":"enemys","id":"goldSlime"}, + "234": {"cls":"enemys","id":"poisonSkeleton"}, + "235": {"cls":"enemys","id":"poisonBat"}, + "236": {"cls":"enemys","id":"ironRock"}, + "237": {"cls":"enemys","id":"skeletonPriest"}, + "238": {"cls":"enemys","id":"skeletonKing"}, + "239": {"cls":"enemys","id":"skeletonPresbyter"}, + "240": {"cls":"enemys","id":"skeletonKnight"}, + "241": {"cls":"enemys","id":"evilHero"}, + "242": {"cls":"enemys","id":"devilWarrior"}, + "243": {"cls":"enemys","id":"demonPriest"}, + "244": {"cls":"enemys","id":"goldHornSlime"}, + "245": {"cls":"enemys","id":"redKing"}, + "246": {"cls":"enemys","id":"blueKing"}, + "247": {"cls":"enemys","id":"magicMaster"}, + "248": {"cls":"enemys","id":"silverSlime"}, + "249": {"cls":"enemys","id":"blademaster"}, + "250": {"cls":"enemys","id":"whiteHornSlime"}, + "251": {"cls":"enemys","id":"evilPrincess"}, + "252": {"cls":"enemys","id":"evilFairy"}, + "253": {"cls":"enemys","id":"yellowPriest"}, + "254": {"cls":"enemys","id":"redSwordsman"}, + "255": {"cls":"enemys","id":"whiteSlimeman"}, + "256": {"cls":"enemys","id":"poisonZombie"}, + "257": {"cls":"enemys","id":"dragon"}, + "258": {"cls":"enemys","id":"octopus"}, + "259": {"cls":"enemys","id":"fairyEnemy"}, + "260": {"cls":"enemys","id":"princessEnemy"}, + "261": {"cls":"enemy48","id":"E1"}, + "262": {"cls":"enemy48","id":"E2"}, + "263": {"cls":"enemy48","id":"E3"}, + "264": {"cls":"enemy48","id":"E4"}, + "265": {"cls":"enemys","id":"silverSlimelord"}, + "266": {"cls":"enemys","id":"goldSlimelord"}, + "267": {"cls":"enemys","id":"grayRock"}, + "270": {"cls":"enemys","id":"greenKnight"}, + "271": {"cls":"enemys","id":"bowman"}, + "272": {"cls":"enemys","id":"purpleBowman"}, + "275": {"cls":"enemys","id":"watcherSlime"}, + "277": {"cls":"enemys","id":"frostBat"}, + "278": {"cls":"enemys","id":"devilKnight"}, + "279": {"cls":"enemys","id":"grayPriest"}, + "280": {"cls":"enemys","id":"greenGateKeeper"}, + "300": {"cls":"terrains","id":"ground","name":"默认地面"}, + "301": {"cls":"terrains","id":"sWallT","name":"薄墙-上","cannotOut":[],"cannotIn":[]}, + "302": {"cls":"terrains","id":"T302","name":"基岩","cannotOut":[],"cannotIn":[],"blocktype":6,"hardness":7}, + "303": {"cls":"terrains","id":"T303","name":"基岩泥土","cannotOut":[],"cannotIn":[],"blocktype":6,"hardness":3}, + "304": {"cls":"terrains","id":"T304","name":"平滑石","cannotOut":[],"cannotIn":[],"hardness":4,"blocktype":1}, + "305": {"cls":"terrains","id":"grass","blocktype":3,"hardness":2,"name":"浅草地"}, + "306": {"cls":"terrains","id":"T306","name":"熔炉","cannotOut":[],"cannotIn":[],"canPass":false,"blocktype":1,"hardness":3,"script":"flags.box_xy = (core.nextX() * 1000 + core.nextY()).toString().padStart(6, '0') + core.status.floorId;\nif (flags.furnacelist[flags.box_xy]) {\n\tflags.interface = \"熔炉\";\n} else {\n\tflags.furnacelist[flags.box_xy] = [\"\", \"\", \"\"];\n\tflags.furnacelistNumber[flags.box_xy] = [0, 0, 0];\n\tflags.furnacelist[flags.box_xy][3] = -1;\n\tflags.furFuel[flags.box_xy] = 0;\n\tflags.interface = \"熔炉\";\n}"}, + "307": {"cls":"terrains","id":"T307","name":"工作台","cannotOut":[],"cannotIn":[],"blocktype":2,"hardness":3,"script":"flags.interface = \"工作台\";"}, + "308": {"cls":"terrains","id":"grass2","blocktype":3,"hardness":2,"name":"深草地"}, + "309": {"cls":"terrains","id":"T309","name":"机器1","cannotOut":[],"cannotIn":[],"blocktype":2,"hardness":3,"script":"flags.box_xy = (core.nextX() * 1000 + core.nextY()).toString().padStart(6, '0') + core.status.floorId;\nif (!flags.farmland2[flags.box_xy]) {\n\tflags.farmland2[flags.box_xy] = [\"I492\", 1, 0, 0];\n\tflags.crops[flags.box_xy] = [\"I492\", 0, flags.cropslist[\"I492\"][0], 1];\n}\ncore.plugin.zidongnongtian();"}, + "310": {"cls":"terrains","id":"T310","name":"机器2","cannotOut":[],"cannotIn":[],"blocktype":2,"hardness":3,"script":"flags.box_xy = (core.nextX() * 1000 + core.nextY()).toString().padStart(6, '0') + core.status.floorId;\nif (!flags.farmland2[flags.box_xy]) {\n\tflags.farmland2[flags.box_xy] = [\"I507\", 1, 0, 0];\n\tflags.crops[flags.box_xy] = [\"I507\", 0, flags.cropslist[\"I507\"][0], 1];\n}\ncore.plugin.zidongnongtian();"}, + "311": {"cls":"terrains","id":"ground2"}, + "312": {"cls":"terrains","id":"T312","name":"机器3","cannotOut":[],"cannotIn":[],"blocktype":2,"hardness":3,"script":"flags.box_xy = (core.nextX() * 1000 + core.nextY()).toString().padStart(6, '0') + core.status.floorId;\nif (!flags.farmland2[flags.box_xy]) {\n\tflags.farmland2[flags.box_xy] = [\"I508\", 1, 0, 0];\n\tflags.crops[flags.box_xy] = [\"I508\", 0, flags.cropslist[\"I508\"][0], 1];\n}\ncore.plugin.zidongnongtian();"}, + "313": {"cls":"terrains","id":"ground3"}, + "314": {"cls":"terrains","id":"T314","name":"箱子","cannotOut":[],"cannotIn":[],"blocktype":2,"hardness":3,"script":"flags.box_xy = (core.nextX() * 1000 + core.nextY()).toString().padStart(6, '0') + core.status.floorId;\nif (flags.boxlist[flags.box_xy]) {\n\tflags.interface = \"箱子\";\n} else {\n\tflags.boxlist[flags.box_xy] = [''];\n\tflags.boxlistNumber[flags.box_xy] = [0];\n\tflags.interface = \"箱子\";\n}"}, + "315": {"cls":"terrains","id":"T315","name":"耕地","cannotOut":[],"cannotIn":[],"event":null,"blocktype":3,"hardness":2}, + "316": {"cls":"terrains","id":"T316","name":"小麦田","cannotOut":[],"cannotIn":[],"blocktype":3,"hardness":1,"script":"","canPass":true}, + "317": {"cls":"terrains","id":"T317","name":"土豆田","cannotOut":[],"cannotIn":[],"blocktype":3,"hardness":1,"script":"","canPass":true}, + "318": {"cls":"terrains","id":"T318","name":"胡萝卜田","cannotOut":[],"cannotIn":[],"blocktype":3,"hardness":1,"script":"","canPass":true}, + "319": {"cls":"npc48","id":"tallYellowDoor","trigger":"openDoor","name":"高黄门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"yellowKey":1}}}, + "320": {"cls":"npc48","id":"tallBlueDoor","trigger":"openDoor","name":"高蓝门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"blueKey":1}}}, + "321": {"cls":"npc48","id":"tallRedDoor","trigger":"openDoor","name":"高红门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"redKey":1}}}, + "322": {"cls":"npc48","id":"tallGreenDoor","trigger":"openDoor","name":"高绿门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"greenKey":1}}}, + "323": {"cls":"npc48","id":"tallSpecialDoor","trigger":"openDoor","name":"高机关门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"specialKey":1}}}, + "324": {"cls":"npc48","id":"tallSteelDoor","trigger":"openDoor","name":"高铁门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}}}, + "325": {"cls":"enemys","id":"keiskeiFairy"}, + "326": {"cls":"enemys","id":"tulipFairy"}, + "327": {"cls":"enemy48","id":"E5"}, + "328": {"cls":"enemy48","id":"E6"}, + "329": {"cls":"enemy48","id":"E7"}, + "330": {"cls":"enemy48","id":"E8"}, + "331": {"cls":"terrains","id":"T331","blocktype":3,"hardness":1,"canPass":true,"name":"成熟小麦"}, + "332": {"cls":"terrains","id":"T332","blocktype":3,"hardness":1,"canPass":true,"name":"成熟土豆"}, + "333": {"cls":"terrains","id":"T333","blocktype":3,"hardness":1,"canPass":true,"name":"成熟胡萝卜"}, + "334": {"cls":"terrains","id":"T334","blocktype":2,"hardness":3,"name":"橡木"}, + "335": {"cls":"terrains","id":"T335","blocktype":2,"hardness":3,"name":"橡木木板"}, + "336": {"cls":"terrains","id":"T336","blocktype":1,"hardness":3,"script":"if (!flags.revive[3] && !flags.revive[4] && !flags.revive[5]) {\n\tflags.revive[3] = core.status.floorId;\n\tflags.revive[4] = core.nextX();\n\tflags.revive[5] = core.nextY();\n\tcore.drawTip(\"重生点已设置\");\n} else if (flags.revive[3] != core.status.floorId || flags.revive[4] != core.nextX() || flags.revive[5] != core.nextY()) {\n\tflags.revive[3] = core.status.floorId;\n\tflags.revive[4] = core.nextX();\n\tflags.revive[5] = core.nextY();\n\tcore.drawTip(\"重生点已设置\");\n} else {\n\tcore.drawTip(\"你已经设置过这个重生点了\");\n}","name":"蓝床"}, + "337": {"cls":"terrains","id":"T337","blocktype":2,"hardness":1}, + "338": {"cls":"terrains","id":"T338","blocktype":2,"hardness":3}, + "339": {"cls":"terrains","id":"T339","blocktype":3,"hardness":2,"name":"森林草地"}, + "340": {"cls":"terrains","id":"T340","blocktype":3,"hardness":2,"name":"丛林草地"}, + "341": {"cls":"terrains","id":"T341","blocktype":3,"hardness":2,"name":"针叶林草地"}, + "342": {"cls":"terrains","id":"T342","blocktype":3,"hardness":1,"name":"雪地"}, + "343": {"cls":"terrains","id":"T343","blocktype":3,"hardness":1,"name":"沙子"}, + "344": {"cls":"terrains","id":"T344","blocktype":3,"hardness":1,"name":"红沙"}, + "345": {"cls":"terrains","id":"T345","blocktype":6,"hardness":1,"name":"深水","canPass":true}, + "346": {"cls":"terrains","id":"T346","blocktype":1,"hardness":3,"name":"石头"}, + "347": {"cls":"terrains","id":"T347","blocktype":3,"hardness":2,"name":"菌丝"}, + "348": {"cls":"terrains","id":"T348","blocktype":6,"hardness":1,"name":"浅水","canPass":true}, + "349": {"cls":"terrains","id":"T349","blocktype":3,"hardness":1,"name":"沙砾"}, + "350": {"cls":"terrains","id":"T350","blocktype":2,"hardness":2,"name":"橡树","bigImage":null}, + "351": {"cls":"terrains","id":"T351","blocktype":2,"hardness":2,"name":"白桦树","bigImage":null}, + "352": {"cls":"terrains","id":"T352","blocktype":2,"hardness":2,"name":"云杉树","bigImage":null}, + "353": {"cls":"terrains","id":"T353","blocktype":2,"hardness":2,"name":"丛林树","bigImage":null}, + "354": {"cls":"terrains","id":"T354","name":"金合欢树","blocktype":2,"hardness":2,"bigImage":null}, + "355": {"cls":"terrains","id":"T355","name":"橡树树苗","blocktype":4,"hardness":1,"canPass":true}, + "356": {"cls":"terrains","id":"T356","name":"白桦树苗","blocktype":4,"hardness":1,"canPass":true}, + "357": {"cls":"terrains","id":"T357","name":"云杉树苗","blocktype":4,"hardness":1,"canPass":true}, + "358": {"cls":"terrains","id":"T358","name":"丛林树苗","blocktype":4,"hardness":1,"canPass":true}, + "359": {"cls":"terrains","id":"T359","name":"金合欢树苗","blocktype":4,"hardness":1,"canPass":true}, + "360": {"cls":"terrains","id":"T360","name":"橡木原木","blocktype":2,"hardness":1}, + "361": {"cls":"terrains","id":"T361","name":"白桦原木","blocktype":2,"hardness":1}, + "362": {"cls":"terrains","id":"T362","blocktype":2,"hardness":1,"name":"云杉原木"}, + "363": {"cls":"terrains","id":"T363","blocktype":2,"hardness":1,"name":"丛林原木"}, + "364": {"cls":"terrains","id":"T364","blocktype":2,"hardness":1,"name":"金合欢原木"}, + "365": {"cls":"terrains","id":"T365","blocktype":2,"hardness":1,"name":"橡木木板"}, + "366": {"cls":"terrains","id":"T366","blocktype":2,"hardness":1,"name":"白桦木板"}, + "367": {"cls":"terrains","id":"T367","blocktype":2,"hardness":1,"name":"云杉木板"}, + "368": {"cls":"terrains","id":"T368","blocktype":2,"hardness":1,"name":"丛林木板"}, + "369": {"cls":"terrains","id":"T369","blocktype":2,"hardness":1,"name":"金合欢木板"}, + "370": {"cls":"terrains","id":"T370","blocktype":5,"hardness":1,"name":"木船"}, + "371": {"cls":"terrains","id":"T371","blocktype":2,"hardness":1,"name":"木门","canPass":true,"script":"core.playSound(\"door_open.ogg\");"}, + "372": {"cls":"terrains","id":"T372","blocktype":4,"hardness":1,"name":"草丛","canPass":true}, + "373": {"cls":"terrains","id":"T373","blocktype":4,"hardness":1,"name":"大草丛","canPass":true}, + "374": {"cls":"terrains","id":"T374","script":"","blocktype":4,"hardness":1,"name":"向日葵","canPass":true}, + "375": {"cls":"terrains","id":"T375","blocktype":4,"hardness":1,"name":"玫瑰","canPass":true}, + "376": {"cls":"terrains","id":"T376","blocktype":2,"hardness":2,"name":"蜂巢","canPass":true}, + "377": {"cls":"terrains","id":"T377","blocktype":2,"hardness":3,"name":"带蜜蜂巢","canPass":true}, + "378": {"cls":"terrains","id":"T378","blocktype":4,"hardness":2,"name":"甘蔗","canPass":true}, + "379": {"cls":"terrains","id":"T379","blocktype":2,"hardness":1,"name":"南瓜","canPass":true}, + "380": {"cls":"terrains","id":"T380","blocktype":2,"hardness":1,"name":"雕刻南瓜"}, + "381": {"cls":"terrains","id":"T381","event":null,"blocktype":2,"hardness":1,"name":"西瓜","canPass":true}, + "382": {"cls":"terrains","id":"T382","script":"","blocktype":4,"hardness":1,"name":"竹笋","canPass":true}, + "383": {"cls":"terrains","id":"T383","script":"","blocktype":2,"hardness":1,"name":"竹子","canPass":true}, + "384": {"cls":"terrains","id":"T384","blocktype":2,"hardness":1,"script":"","name":"空浆果丛","canPass":true}, + "385": {"cls":"terrains","id":"T385","name":"浆果丛","script":"","canBreak":false,"blocktype":2,"hardness":2,"canPass":true}, + "386": {"cls":"terrains","id":"T386","blocktype":4,"hardness":1,"name":"枯萎灌木","canPass":true}, + "387": {"cls":"terrains","id":"T387","canPass":true,"blocktype":2,"hardness":1,"name":"仙人掌"}, + "388": {"cls":"terrains","id":"T388","canPass":true,"blocktype":2,"hardness":2,"name":"仙人掌花"}, + "389": {"cls":"terrains","id":"T389","canPass":false,"blocktype":2,"hardness":3,"name":"巨型棕蘑菇"}, + "390": {"cls":"terrains","id":"T390","blocktype":2,"hardness":3,"script":"","name":"巨型红蘑菇"}, + "391": {"cls":"terrains","id":"T391","blocktype":2,"hardness":1,"name":"棕蘑菇块"}, + "392": {"cls":"terrains","id":"T392","blocktype":2,"hardness":1,"name":"红蘑菇块"}, + "393": {"cls":"terrains","id":"T393","blocktype":4,"hardness":1,"name":"水草","canPass":true}, + "394": {"cls":"terrains","id":"T394","blocktype":4,"hardness":1,"script":"","name":"海带","canPass":true}, + "395": {"cls":"terrains","id":"T395","blocktype":4,"hardness":1,"name":"黄珊瑚","canPass":true}, + "396": {"cls":"terrains","id":"T396","blocktype":4,"hardness":1,"name":"红珊瑚","canPass":true}, + "397": {"cls":"terrains","id":"T397","blocktype":4,"hardness":1,"name":"蓝珊瑚","canPass":true}, + "398": {"cls":"terrains","id":"T398","blocktype":4,"hardness":1,"name":"粉珊瑚","canPass":true}, + "399": {"cls":"terrains","id":"T399","blocktype":2,"hardness":1,"name":"黄珊瑚块"}, + "400": {"cls":"terrains","id":"T400","blocktype":2,"hardness":1,"name":"红珊瑚块"}, + "401": {"cls":"terrains","id":"T401","blocktype":2,"hardness":1,"name":"蓝珊瑚块"}, + "402": {"cls":"terrains","id":"T402","blocktype":2,"hardness":1,"name":"粉珊瑚块"}, + "403": {"cls":"terrains","id":"T403","blocktype":1,"hardness":3,"name":"煤炭矿"}, + "404": {"cls":"terrains","id":"T404","blocktype":1,"hardness":3,"name":"铁矿"}, + "405": {"cls":"terrains","id":"T405","blocktype":1,"hardness":3,"name":"铜矿"}, + "406": {"cls":"terrains","id":"T406","blocktype":1,"hardness":4,"name":"金矿"}, + "407": {"cls":"terrains","id":"T407","canPass":false,"blocktype":1,"hardness":4,"name":"红石矿"}, + "408": {"cls":"terrains","id":"T408","canPass":false,"blocktype":1,"hardness":4,"name":"青金石矿"}, + "409": {"cls":"terrains","id":"T409","canPass":false,"blocktype":1,"hardness":4,"name":"钻石矿"}, + "410": {"cls":"terrains","id":"T410","blocktype":1,"hardness":4,"name":"绿宝石矿"}, + "411": {"cls":"terrains","id":"T411","blocktype":1,"hardness":4,"name":"紫水晶"}, + "412": {"cls":"terrains","id":"T412","blocktype":4,"hardness":1,"name":"棕蘑菇","canPass":true}, + "413": {"cls":"terrains","id":"T413","blocktype":4,"hardness":1,"name":"红蘑菇","canPass":true}, + "414": {"cls":"terrains","id":"T414","blocktype":5,"hardness":3,"name":"工作台"}, + "415": {"cls":"terrains","id":"T415","name":"熔炉","blocktype":5}, + "416": {"cls":"terrains","id":"T416","name":"酿造台","blocktype":5}, + "417": {"cls":"terrains","id":"T417","name":"附魔台","blocktype":5}, + "418": {"cls":"terrains","id":"T418","name":"铁砧","blocktype":5}, + "419": {"cls":"terrains","id":"T419","name":"床","blocktype":5}, + "420": {"cls":"terrains","id":"T420","name":"箱子","blocktype":5,"script":"// flags.box_xy = (core.nextX() * 1000 + core.nextY()).toString().padStart(6, '0') + core.status.floorId;\n// if (flags.boxlist[flags.box_xy]) {\n// \tflags.interface = \"箱子\";\n// } else {\n// \tflags.boxlist[flags.box_xy] = [''];\n// \tflags.boxlistNumber[flags.box_xy] = [0];\n// \tflags.interface = \"箱子\";\n// }"}, + "421": {"cls":"terrains","id":"T421","name":"末影箱","blocktype":5}, + "422": {"cls":"terrains","id":"T422","name":"熄灭篝火","blocktype":2,"hardness":1}, + "423": {"cls":"terrains","id":"T423","name":"篝火","blocktype":2,"hardness":1}, + "424": {"cls":"terrains","id":"T424","name":"灯","blocktype":1,"hardness":2,"canPass":true}, + "425": {"cls":"terrains","id":"T425","name":"垃圾桶","blocktype":1,"hardness":2}, + "426": {"cls":"terrains","id":"T426","name":"耕地","blocktype":3,"hardness":2,"canPass":true}, + "427": {"cls":"terrains","id":"T427","name":"西瓜田","blocktype":4,"hardness":1,"canPass":true}, + "428": {"cls":"terrains","id":"T428","name":"小麦田","blocktype":4,"hardness":1,"canPass":true}, + "429": {"cls":"terrains","id":"T429","name":"马铃薯田","blocktype":4,"hardness":1,"canPass":true}, + "430": {"cls":"terrains","id":"T430","name":"胡萝卜田","blocktype":4,"hardness":1,"canPass":true}, + "431": {"cls":"terrains","id":"T431","name":"发光浆果丛","blocktype":4,"hardness":1,"canPass":true}, + "432": {"cls":"terrains","id":"T432","name":"空浆果丛","blocktype":4,"hardness":1,"canPass":true}, + "433": {"cls":"terrains","id":"T433","name":"玻璃","blocktype":1,"hardness":1}, + "434": {"cls":"enemys","id":"E434"}, + "435": {"cls":"enemys","id":"E435"}, + "436": {"cls":"enemys","id":"E436"}, + "437": {"cls":"enemys","id":"E437"}, + "438": {"cls":"enemys","id":"E438"}, + "439": {"cls":"enemys","id":"E439"}, + "440": {"cls":"enemys","id":"E440"}, + "441": {"cls":"enemys","id":"E441"}, + "442": {"cls":"enemys","id":"E442"}, + "443": {"cls":"enemys","id":"E443"}, + "444": {"cls":"enemys","id":"E444"}, + "445": {"cls":"enemys","id":"E445"}, + "446": {"cls":"enemys","id":"E446"}, + "447": {"cls":"enemys","id":"E447"}, + "448": {"cls":"enemys","id":"E448"}, + "449": {"cls":"enemys","id":"E449"}, + "450": {"cls":"enemys","id":"E450"}, + "451": {"cls":"enemys","id":"E451"}, + "452": {"cls":"enemys","id":"E452"}, + "453": {"cls":"enemys","id":"E453"}, + "454": {"cls":"enemys","id":"E454"}, + "455": {"cls":"enemys","id":"E455"}, + "456": {"cls":"enemys","id":"E456"}, + "457": {"cls":"enemys","id":"E457"}, + "458": {"cls":"enemys","id":"E458"}, + "459": {"cls":"enemys","id":"E459"}, + "460": {"cls":"enemys","id":"E460"}, + "461": {"cls":"enemys","id":"E461"}, + "462": {"cls":"enemys","id":"E462"}, + "463": {"cls":"animates","id":"A463"}, + "464": {"cls":"animates","id":"A464"}, + "465": {"cls":"animates","id":"A465"}, + "466": {"cls":"items","id":"I466"}, + "467": {"cls":"items","id":"I467"}, + "468": {"cls":"items","id":"I468"}, + "469": {"cls":"items","id":"I469"}, + "470": {"cls":"items","id":"I470"}, + "471": {"cls":"items","id":"I471"}, + "472": {"cls":"items","id":"I472"}, + "473": {"cls":"items","id":"I473"}, + "474": {"cls":"items","id":"I474"}, + "475": {"cls":"items","id":"I475"}, + "476": {"cls":"items","id":"I476"}, + "477": {"cls":"items","id":"I477"}, + "478": {"cls":"items","id":"I478"}, + "479": {"cls":"items","id":"I479"}, + "480": {"cls":"items","id":"I480"}, + "481": {"cls":"items","id":"I481"}, + "482": {"cls":"items","id":"I482"}, + "483": {"cls":"items","id":"I483"}, + "484": {"cls":"items","id":"I484"}, + "485": {"cls":"items","id":"I485"}, + "486": {"cls":"items","id":"I486"}, + "487": {"cls":"items","id":"I487"}, + "488": {"cls":"items","id":"I488"}, + "489": {"cls":"items","id":"I489"}, + "490": {"cls":"items","id":"I490"}, + "491": {"cls":"items","id":"I491"}, + "492": {"cls":"items","id":"I492"}, + "493": {"cls":"items","id":"I493"}, + "494": {"cls":"items","id":"I494"}, + "495": {"cls":"items","id":"I495"}, + "496": {"cls":"items","id":"I496"}, + "497": {"cls":"items","id":"I497"}, + "498": {"cls":"items","id":"I498"}, + "499": {"cls":"items","id":"I499"}, + "500": {"cls":"items","id":"I500"}, + "501": {"cls":"items","id":"I501"}, + "502": {"cls":"items","id":"I502"}, + "503": {"cls":"items","id":"I503"}, + "504": {"cls":"items","id":"I504"}, + "505": {"cls":"items","id":"I505"}, + "506": {"cls":"items","id":"I506"}, + "507": {"cls":"items","id":"I507"}, + "508": {"cls":"items","id":"I508"}, + "509": {"cls":"items","id":"I509"}, + "510": {"cls":"items","id":"I510"}, + "511": {"cls":"items","id":"I511"}, + "512": {"cls":"items","id":"I512"}, + "513": {"cls":"items","id":"I513"}, + "514": {"cls":"items","id":"I514"}, + "515": {"cls":"items","id":"I515"}, + "516": {"cls":"items","id":"I516"}, + "517": {"cls":"items","id":"I517"}, + "518": {"cls":"items","id":"I518"}, + "519": {"cls":"items","id":"I519"}, + "520": {"cls":"items","id":"I520"}, + "521": {"cls":"items","id":"I521"}, + "522": {"cls":"items","id":"I522"}, + "523": {"cls":"items","id":"I523"}, + "524": {"cls":"items","id":"I524"}, + "525": {"cls":"items","id":"I525"}, + "526": {"cls":"items","id":"I526"}, + "527": {"cls":"items","id":"I527"}, + "528": {"cls":"items","id":"I528"}, + "529": {"cls":"items","id":"I529"}, + "530": {"cls":"items","id":"I530"}, + "531": {"cls":"items","id":"I531"}, + "532": {"cls":"items","id":"I532"}, + "533": {"cls":"items","id":"I533"}, + "534": {"cls":"items","id":"I534"}, + "535": {"cls":"items","id":"I535"}, + "536": {"cls":"items","id":"I536"}, + "537": {"cls":"items","id":"I537"}, + "538": {"cls":"items","id":"I538"}, + "539": {"cls":"items","id":"I539"}, + "540": {"cls":"items","id":"I540"}, + "541": {"cls":"items","id":"I541"}, + "542": {"cls":"items","id":"I542"}, + "543": {"cls":"items","id":"I543"}, + "544": {"cls":"items","id":"I544"}, + "545": {"cls":"items","id":"I545"}, + "546": {"cls":"items","id":"I546"}, + "547": {"cls":"items","id":"I547"}, + "548": {"cls":"items","id":"I548"}, + "549": {"cls":"items","id":"I549"}, + "550": {"cls":"items","id":"I550"}, + "551": {"cls":"items","id":"I551"}, + "552": {"cls":"items","id":"I552"}, + "553": {"cls":"items","id":"I553"}, + "554": {"cls":"items","id":"I554"}, + "555": {"cls":"items","id":"I555"}, + "556": {"cls":"items","id":"I556"}, + "557": {"cls":"items","id":"I557"}, + "558": {"cls":"items","id":"I558"}, + "559": {"cls":"items","id":"I559"}, + "560": {"cls":"items","id":"I560"}, + "561": {"cls":"items","id":"I561"}, + "562": {"cls":"items","id":"I562"}, + "563": {"cls":"items","id":"I563"}, + "564": {"cls":"items","id":"I564"}, + "565": {"cls":"items","id":"I565"}, + "566": {"cls":"items","id":"I566"}, + "567": {"cls":"items","id":"I567"}, + "568": {"cls":"items","id":"I568"}, + "569": {"cls":"items","id":"I569"}, + "570": {"cls":"items","id":"I570"}, + "571": {"cls":"items","id":"I571"}, + "572": {"cls":"items","id":"I572"}, + "573": {"cls":"items","id":"I573"}, + "574": {"cls":"items","id":"I574"}, + "575": {"cls":"items","id":"I575"}, + "576": {"cls":"items","id":"I576"}, + "577": {"cls":"items","id":"I577"}, + "578": {"cls":"items","id":"I578"}, + "579": {"cls":"items","id":"I579"}, + "580": {"cls":"items","id":"I580"}, + "581": {"cls":"items","id":"I581"}, + "582": {"cls":"items","id":"I582"}, + "583": {"cls":"items","id":"I583"}, + "584": {"cls":"items","id":"I584"}, + "585": {"cls":"items","id":"I585"}, + "586": {"cls":"items","id":"I586"}, + "587": {"cls":"items","id":"I587"}, + "588": {"cls":"items","id":"I588"}, + "589": {"cls":"items","id":"I589"}, + "590": {"cls":"items","id":"I590"}, + "591": {"cls":"items","id":"I591"}, + "592": {"cls":"items","id":"I592"}, + "593": {"cls":"items","id":"I593"}, + "594": {"cls":"items","id":"I594"}, + "595": {"cls":"items","id":"I595"}, + "596": {"cls":"items","id":"I596"}, + "597": {"cls":"items","id":"I597"}, + "598": {"cls":"items","id":"I598"}, + "599": {"cls":"items","id":"I599"}, + "600": {"cls":"items","id":"I600"}, + "601": {"cls":"items","id":"I601"}, + "602": {"cls":"items","id":"I602"}, + "603": {"cls":"items","id":"I603"}, + "604": {"cls":"items","id":"I604"}, + "605": {"cls":"items","id":"I605"}, + "606": {"cls":"items","id":"I606"}, + "607": {"cls":"items","id":"I607"}, + "608": {"cls":"items","id":"I608"}, + "609": {"cls":"items","id":"I609"}, + "610": {"cls":"items","id":"I610"}, + "611": {"cls":"items","id":"I611"}, + "612": {"cls":"items","id":"I612"}, + "613": {"cls":"items","id":"I613"}, + "614": {"cls":"items","id":"I614"}, + "615": {"cls":"items","id":"I615"}, + "616": {"cls":"items","id":"I616"}, + "617": {"cls":"items","id":"I617"}, + "618": {"cls":"items","id":"I618"}, + "619": {"cls":"items","id":"I619"}, + "620": {"cls":"items","id":"I620"}, + "621": {"cls":"items","id":"I621"}, + "622": {"cls":"items","id":"I622"}, + "623": {"cls":"items","id":"I623"}, + "624": {"cls":"items","id":"I624"}, + "625": {"cls":"items","id":"I625"}, + "626": {"cls":"items","id":"I626"}, + "627": {"cls":"items","id":"I627"}, + "628": {"cls":"items","id":"I628"}, + "629": {"cls":"items","id":"I629"}, + "630": {"cls":"items","id":"I630"}, + "631": {"cls":"items","id":"I631"}, + "632": {"cls":"items","id":"I632"}, + "633": {"cls":"items","id":"I633"}, + "634": {"cls":"items","id":"I634"}, + "635": {"cls":"items","id":"I635"}, + "636": {"cls":"items","id":"I636"}, + "637": {"cls":"items","id":"I637"}, + "638": {"cls":"items","id":"I638"}, + "639": {"cls":"items","id":"I639"}, + "640": {"cls":"items","id":"I640"}, + "641": {"cls":"items","id":"I641"}, + "642": {"cls":"items","id":"I642"}, + "643": {"cls":"items","id":"I643"}, + "644": {"cls":"items","id":"I644"}, + "645": {"cls":"items","id":"I645"}, + "646": {"cls":"items","id":"I646"}, + "647": {"cls":"items","id":"I647"}, + "648": {"cls":"items","id":"I648"}, + "649": {"cls":"items","id":"I649"}, + "650": {"cls":"items","id":"I650"}, + "651": {"cls":"items","id":"I651"}, + "652": {"cls":"items","id":"I652"}, + "653": {"cls":"items","id":"I653"}, + "654": {"cls":"items","id":"I654"}, + "655": {"cls":"items","id":"I655"}, + "656": {"cls":"items","id":"I656"}, + "657": {"cls":"items","id":"I657"}, + "658": {"cls":"items","id":"I658"}, + "659": {"cls":"items","id":"I659"}, + "660": {"cls":"items","id":"I660"}, + "661": {"cls":"items","id":"I661"}, + "662": {"cls":"items","id":"I662"}, + "663": {"cls":"items","id":"I663"}, + "664": {"cls":"items","id":"I664"}, + "665": {"cls":"items","id":"I665"}, + "666": {"cls":"items","id":"I666"}, + "667": {"cls":"items","id":"I667"}, + "668": {"cls":"items","id":"I668"}, + "669": {"cls":"items","id":"I669"}, + "670": {"cls":"items","id":"I670"}, + "671": {"cls":"items","id":"I671"}, + "672": {"cls":"items","id":"I672"}, + "673": {"cls":"items","id":"I673"}, + "674": {"cls":"items","id":"I674"}, + "675": {"cls":"items","id":"I675"}, + "676": {"cls":"items","id":"I676"}, + "677": {"cls":"items","id":"I677"}, + "678": {"cls":"items","id":"I678"}, + "679": {"cls":"items","id":"I679"}, + "680": {"cls":"items","id":"I680"}, + "681": {"cls":"items","id":"I681"}, + "682": {"cls":"items","id":"I682"}, + "683": {"cls":"items","id":"I683"}, + "684": {"cls":"items","id":"I684"}, + "685": {"cls":"items","id":"I685"}, + "686": {"cls":"items","id":"I686"}, + "687": {"cls":"items","id":"I687"}, + "688": {"cls":"items","id":"I688"}, + "689": {"cls":"items","id":"I689"}, + "690": {"cls":"items","id":"I690"}, + "691": {"cls":"items","id":"I691"}, + "692": {"cls":"items","id":"I692"}, + "693": {"cls":"items","id":"I693"}, + "694": {"cls":"items","id":"I694"}, + "695": {"cls":"items","id":"I695"}, + "696": {"cls":"items","id":"I696"}, + "697": {"cls":"items","id":"I697"}, + "698": {"cls":"items","id":"I698"}, + "699": {"cls":"items","id":"I699"}, + "700": {"cls":"items","id":"I700"}, + "701": {"cls":"items","id":"I701"}, + "702": {"cls":"items","id":"I702"}, + "703": {"cls":"items","id":"I703"}, + "704": {"cls":"items","id":"I704"}, + "705": {"cls":"items","id":"I705"}, + "706": {"cls":"items","id":"I706"}, + "707": {"cls":"items","id":"I707"}, + "708": {"cls":"items","id":"I708"}, + "709": {"cls":"items","id":"I709"}, + "710": {"cls":"items","id":"I710"}, + "711": {"cls":"items","id":"I711"}, + "712": {"cls":"items","id":"I712"}, + "713": {"cls":"items","id":"I713"}, + "714": {"cls":"items","id":"I714"}, + "715": {"cls":"items","id":"I715"}, + "716": {"cls":"items","id":"I716"}, + "717": {"cls":"items","id":"I717"}, + "718": {"cls":"items","id":"I718"}, + "719": {"cls":"items","id":"I719"}, + "720": {"cls":"items","id":"I720"}, + "721": {"cls":"items","id":"I721"}, + "722": {"cls":"items","id":"I722"}, + "723": {"cls":"items","id":"I723"}, + "724": {"cls":"items","id":"I724"}, + "725": {"cls":"items","id":"I725"}, + "726": {"cls":"items","id":"I726"}, + "727": {"cls":"items","id":"I727"}, + "728": {"cls":"items","id":"I728"}, + "729": {"cls":"items","id":"I729"}, + "730": {"cls":"items","id":"I730"}, + "731": {"cls":"items","id":"I731"}, + "732": {"cls":"items","id":"I732"}, + "733": {"cls":"items","id":"I733"}, + "734": {"cls":"items","id":"I734"}, + "735": {"cls":"items","id":"I735"}, + "736": {"cls":"items","id":"I736"}, + "737": {"cls":"items","id":"I737"}, + "738": {"cls":"items","id":"I738"}, + "739": {"cls":"items","id":"I739"}, + "740": {"cls":"items","id":"I740"}, + "741": {"cls":"items","id":"I741"}, + "742": {"cls":"items","id":"I742"}, + "743": {"cls":"items","id":"I743"}, + "744": {"cls":"items","id":"I744"}, + "745": {"cls":"items","id":"I745"}, + "746": {"cls":"items","id":"I746"}, + "747": {"cls":"items","id":"I747"}, + "748": {"cls":"items","id":"I748"}, + "749": {"cls":"items","id":"I749"}, + "750": {"cls":"items","id":"I750"}, + "751": {"cls":"items","id":"I751"}, + "752": {"cls":"items","id":"I752"}, + "753": {"cls":"items","id":"I753"}, + "754": {"cls":"items","id":"I754"}, + "755": {"cls":"items","id":"I755"}, + "756": {"cls":"items","id":"I756"}, + "757": {"cls":"items","id":"I757"}, + "758": {"cls":"items","id":"I758"}, + "759": {"cls":"items","id":"I759"}, + "760": {"cls":"items","id":"I760"}, + "761": {"cls":"items","id":"I761"}, + "762": {"cls":"items","id":"I762"}, + "763": {"cls":"items","id":"I763"}, + "764": {"cls":"items","id":"I764"}, + "765": {"cls":"items","id":"I765"}, + "766": {"cls":"items","id":"I766"}, + "767": {"cls":"items","id":"I767"}, + "768": {"cls":"items","id":"I768"}, + "769": {"cls":"items","id":"I769"}, + "770": {"cls":"items","id":"I770"}, + "771": {"cls":"items","id":"I771"}, + "772": {"cls":"items","id":"I772"}, + "773": {"cls":"items","id":"I773"}, + "774": {"cls":"items","id":"I774"}, + "775": {"cls":"items","id":"I775"}, + "776": {"cls":"items","id":"I776"}, + "777": {"cls":"items","id":"I777"}, + "778": {"cls":"items","id":"I778"}, + "779": {"cls":"items","id":"I779"}, + "780": {"cls":"items","id":"I780"}, + "781": {"cls":"items","id":"I781"}, + "782": {"cls":"items","id":"I782"}, + "783": {"cls":"items","id":"I783"}, + "784": {"cls":"items","id":"I784"}, + "785": {"cls":"items","id":"I785"}, + "786": {"cls":"items","id":"I786"}, + "787": {"cls":"items","id":"I787"}, + "788": {"cls":"items","id":"I788"}, + "789": {"cls":"items","id":"I789"}, + "790": {"cls":"items","id":"I790"}, + "791": {"cls":"items","id":"I791"}, + "792": {"cls":"items","id":"I792"}, + "793": {"cls":"items","id":"I793"}, + "794": {"cls":"items","id":"I794"}, + "795": {"cls":"items","id":"I795"}, + "796": {"cls":"items","id":"I796"}, + "797": {"cls":"items","id":"I797"}, + "798": {"cls":"items","id":"I798"}, + "799": {"cls":"items","id":"I799"}, + "800": {"cls":"items","id":"I800"}, + "801": {"cls":"items","id":"I801"}, + "802": {"cls":"items","id":"I802"}, + "803": {"cls":"items","id":"I803"}, + "804": {"cls":"items","id":"I804"}, + "805": {"cls":"items","id":"I805"}, + "806": {"cls":"items","id":"I806"}, + "807": {"cls":"items","id":"I807"}, + "808": {"cls":"items","id":"I808"}, + "809": {"cls":"items","id":"I809"}, + "810": {"cls":"items","id":"I810"}, + "811": {"cls":"items","id":"I811"}, + "812": {"cls":"items","id":"I812"}, + "813": {"cls":"items","id":"I813"}, + "814": {"cls":"items","id":"I814"}, + "815": {"cls":"items","id":"I815"}, + "816": {"cls":"items","id":"I816"}, + "817": {"cls":"items","id":"I817"}, + "818": {"cls":"items","id":"I818"}, + "819": {"cls":"items","id":"I819"}, + "820": {"cls":"items","id":"I820"}, + "821": {"cls":"items","id":"I821"}, + "822": {"cls":"items","id":"I822"}, + "823": {"cls":"items","id":"I823"}, + "824": {"cls":"items","id":"I824"}, + "825": {"cls":"items","id":"I825"}, + "826": {"cls":"items","id":"I826"}, + "827": {"cls":"items","id":"I827"}, + "828": {"cls":"items","id":"I828"}, + "829": {"cls":"items","id":"I829"}, + "830": {"cls":"items","id":"I830"}, + "831": {"cls":"items","id":"I831"}, + "832": {"cls":"items","id":"I832"}, + "833": {"cls":"items","id":"I833"}, + "834": {"cls":"items","id":"I834"}, + "835": {"cls":"items","id":"I835"}, + "836": {"cls":"items","id":"I836"}, + "837": {"cls":"items","id":"I837"}, + "838": {"cls":"items","id":"I838"}, + "839": {"cls":"items","id":"I839"}, + "840": {"cls":"items","id":"I840"}, + "841": {"cls":"items","id":"I841"}, + "842": {"cls":"items","id":"I842"}, + "843": {"cls":"items","id":"I843"}, + "844": {"cls":"items","id":"I844"}, + "845": {"cls":"items","id":"I845"}, + "846": {"cls":"items","id":"I846"}, + "847": {"cls":"items","id":"I847"}, + "848": {"cls":"items","id":"I848"}, + "849": {"cls":"items","id":"I849"}, + "850": {"cls":"items","id":"I850"}, + "851": {"cls":"items","id":"I851"}, + "852": {"cls":"items","id":"I852"}, + "853": {"cls":"items","id":"I853"}, + "854": {"cls":"items","id":"I854"}, + "855": {"cls":"items","id":"I855"}, + "856": {"cls":"items","id":"I856"}, + "857": {"cls":"items","id":"I857"}, + "858": {"cls":"items","id":"I858"}, + "859": {"cls":"items","id":"I859"}, + "860": {"cls":"items","id":"I860"}, + "861": {"cls":"items","id":"I861"}, + "862": {"cls":"items","id":"I862"}, + "863": {"cls":"items","id":"I863"}, + "864": {"cls":"items","id":"I864"}, + "865": {"cls":"items","id":"I865"}, + "866": {"cls":"items","id":"I866"}, + "867": {"cls":"items","id":"I867"}, + "868": {"cls":"items","id":"I868"}, + "869": {"cls":"items","id":"I869"}, + "870": {"cls":"items","id":"I870"}, + "871": {"cls":"items","id":"I871"}, + "872": {"cls":"items","id":"I872"}, + "873": {"cls":"items","id":"I873"}, + "874": {"cls":"items","id":"I874"}, + "875": {"cls":"items","id":"I875"}, + "876": {"cls":"items","id":"I876"}, + "877": {"cls":"items","id":"I877"}, + "878": {"cls":"npcs","id":"N878"}, + "879": {"cls":"npcs","id":"N879"}, + "880": {"cls":"npcs","id":"N880"}, + "881": {"cls":"npcs","id":"N881"}, + "882": {"cls":"npcs","id":"N882"}, + "883": {"cls":"npcs","id":"N883"}, + "884": {"cls":"npcs","id":"N884"}, + "885": {"cls":"npcs","id":"N885"}, + "886": {"cls":"npcs","id":"N886"}, + "887": {"cls":"npcs","id":"N887"}, + "888": {"cls":"npcs","id":"N888"}, + "889": {"cls":"npcs","id":"N889"}, + "890": {"cls":"npcs","id":"N890"}, + "891": {"cls":"npcs","id":"N891"}, + "892": {"cls":"npcs","id":"N892"}, + "893": {"cls":"npcs","id":"N893"}, + "894": {"cls":"npcs","id":"N894"}, + "895": {"cls":"npcs","id":"N895"}, + "896": {"cls":"terrains","id":"T896","name":"黑曜石","blocktype":1,"hardness":5}, + "897": {"cls":"terrains","id":"T897","name":"雕纹砂岩","blocktype":1,"hardness":4}, + "898": {"cls":"terrains","id":"T898","name":"白色羊毛","blocktype":2,"hardness":1}, + "899": {"cls":"terrains","id":"T899","name":"煤炭块","blocktype":1,"hardness":3}, + "900": {"cls":"terrains","id":"T900","name":"铁块","blocktype":1,"hardness":3}, + "901": {"cls":"terrains","id":"T901","name":"铜块","blocktype":1,"hardness":3}, + "902": {"cls":"terrains","id":"T902","name":"金块","blocktype":1,"hardness":3}, + "903": {"cls":"terrains","id":"T903","name":"红石块","blocktype":1,"hardness":3}, + "904": {"cls":"terrains","id":"T904","name":"青金石块","blocktype":1,"hardness":3}, + "905": {"cls":"terrains","id":"T905","name":"钻石块","blocktype":1,"hardness":3}, + "906": {"cls":"terrains","id":"T906","name":"绿宝石块","blocktype":1,"hardness":3}, + "907": {"cls":"terrains","id":"T907","name":"海晶石","blocktype":1,"hardness":3}, + "908": {"cls":"terrains","id":"T908","name":"海晶石砖","blocktype":1,"hardness":3}, + "909": {"cls":"terrains","id":"T909","name":"暗海晶石","blocktype":1,"hardness":4}, + "910": {"cls":"terrains","id":"T910","name":"熔岩","blocktype":6,"canPass":true}, + "911": {"cls":"terrains","id":"T911","name":"火","blocktype":4,"canPass":true}, + "912": {"cls":"terrains","id":"T912","name":"下界传送门","blocktype":6,"canPass":true}, + "913": {"cls":"terrains","id":"T913","name":"末地传送门","blocktype":6,"canPass":true}, + "914": {"cls":"terrains","id":"T914","name":"空的末地传送门框架","blocktype":6}, + "915": {"cls":"terrains","id":"T915","name":"末地传送门框架","blocktype":6}, + "916": {"cls":"terrains","id":"T916","name":"圆石","blocktype":1,"hardness":3}, + "917": {"cls":"terrains","id":"T917","name":"平滑石","blocktype":1,"hardness":4}, + "918": {"cls":"terrains","id":"T918","blocktype":4,"hardness":2,"name":"成熟小麦","canPass":true}, + "919": {"cls":"terrains","id":"T919","blocktype":4,"hardness":2,"name":"成熟马铃薯","canPass":true}, + "920": {"cls":"terrains","id":"T920","blocktype":4,"hardness":2,"name":"成熟胡萝卜","canPass":true}, + "921": {"cls":"terrains","id":"T921","blocktype":3,"hardness":2,"name":"泥土"}, + "922": {"cls":"terrains","id":"T922","name":"石砖","blocktype":1,"hardness":4}, + "923": {"cls":"terrains","id":"T923","name":"苔石砖","blocktype":1,"hardness":4}, + "924": {"cls":"terrains","id":"T924","name":"下界岩","blocktype":1,"hardness":3}, + "925": {"cls":"terrains","id":"T925","name":"橙色陶瓦","blocktype":1,"hardness":3}, + "926": {"cls":"terrains","id":"T926","name":"苔石","blocktype":1,"hardness":3}, + "927": {"cls":"terrains","id":"T927","name":"裂纹石砖","blocktype":1,"hardness":3}, + "928": {"cls":"terrains","id":"T928","name":"砂岩","blocktype":1,"hardness":3}, + "929": {"cls":"terrains","id":"T929","name":"红砂岩","blocktype":1,"hardness":3}, + "930": {"cls":"terrains","id":"T930","name":"雕纹红砂岩","blocktype":1,"hardness":4}, + "931": {"cls":"terrains","id":"T931"}, + "932": {"cls":"terrains","id":"T932"}, + "933": {"cls":"terrains","id":"T933"}, + "934": {"cls":"terrains","id":"T934"}, + "935": {"cls":"terrains","id":"T935"}, + "936": {"cls":"terrains","id":"T936"}, + "937": {"cls":"terrains","id":"T937"}, + "938": {"cls":"terrains","id":"T938"}, + "939": {"cls":"terrains","id":"T939"}, + "940": {"cls":"terrains","id":"T940"}, + "941": {"cls":"terrains","id":"T941"}, + "942": {"cls":"terrains","id":"T942"}, + "943": {"cls":"terrains","id":"T943"}, + "944": {"cls":"terrains","id":"T944"}, + "945": {"cls":"terrains","id":"T945"}, + "946": {"cls":"terrains","id":"T946"}, + "947": {"cls":"terrains","id":"T947"}, + "948": {"cls":"terrains","id":"T948"}, + "949": {"cls":"terrains","id":"T949"}, + "950": {"cls":"terrains","id":"T950"}, + "951": {"cls":"terrains","id":"T951"}, + "952": {"cls":"terrains","id":"T952"}, + "953": {"cls":"terrains","id":"T953"}, + "954": {"cls":"terrains","id":"T954"}, + "955": {"cls":"terrains","id":"T955"}, + "956": {"cls":"terrains","id":"T956"}, + "957": {"cls":"terrains","id":"T957"}, + "958": {"cls":"terrains","id":"T958"}, + "959": {"cls":"terrains","id":"T959"}, + "960": {"cls":"terrains","id":"T960"}, + "961": {"cls":"terrains","id":"T961"}, + "962": {"cls":"terrains","id":"T962"}, + "963": {"cls":"terrains","id":"T963"}, + "964": {"cls":"terrains","id":"T964"}, + "965": {"cls":"terrains","id":"T965"}, + "966": {"cls":"terrains","id":"T966"}, + "967": {"cls":"terrains","id":"T967"}, + "968": {"cls":"terrains","id":"T968"}, + "969": {"cls":"terrains","id":"T969"}, + "970": {"cls":"terrains","id":"T970"}, + "971": {"cls":"terrains","id":"T971"}, + "972": {"cls":"terrains","id":"T972"}, + "973": {"cls":"terrains","id":"T973"}, + "974": {"cls":"terrains","id":"T974"}, + "975": {"cls":"terrains","id":"T975"}, + "976": {"cls":"terrains","id":"T976"}, + "977": {"cls":"terrains","id":"T977"}, + "978": {"cls":"terrains","id":"T978"}, + "979": {"cls":"terrains","id":"T979"}, + "980": {"cls":"terrains","id":"T980"}, + "981": {"cls":"terrains","id":"T981"}, + "982": {"cls":"terrains","id":"T982"}, + "983": {"cls":"terrains","id":"T983"}, + "984": {"cls":"terrains","id":"T984"}, + "985": {"cls":"terrains","id":"T985"}, + "986": {"cls":"terrains","id":"T986"}, + "987": {"cls":"enemys","id":"E987"}, + "988": {"cls":"enemys","id":"E988"}, + "989": {"cls":"enemys","id":"E989"}, + "990": {"cls":"enemys","id":"E990"}, + "991": {"cls":"enemys","id":"E991"}, + "992": {"cls":"enemys","id":"E992"}, + "993": {"cls":"enemys","id":"E993"}, + "994": {"cls":"enemys","id":"E994"}, + "995": {"cls":"enemys","id":"E995"}, + "996": {"cls":"enemys","id":"E996"}, + "997": {"cls":"enemys","id":"E997"}, + "998": {"cls":"enemys","id":"E998"}, + "999": {"cls":"enemys","id":"E999"}, + "1000": {"cls":"enemys","id":"E1000"}, + "1001": {"cls":"enemys","id":"E1001"}, + "1002": {"cls":"enemys","id":"E1002"}, + "1003": {"cls":"enemys","id":"E1003"}, + "1004": {"cls":"enemys","id":"E1004"}, + "1005": {"cls":"enemys","id":"E1005"}, + "1006": {"cls":"enemys","id":"E1006"}, + "1007": {"cls":"enemys","id":"E1007"}, + "1008": {"cls":"enemys","id":"E1008"}, + "1009": {"cls":"enemys","id":"E1009"}, + "1010": {"cls":"enemys","id":"E1010"}, + "1011": {"cls":"enemys","id":"E1011"}, + "1012": {"cls":"enemys","id":"E1012"}, + "1013": {"cls":"enemys","id":"E1013"}, + "1014": {"cls":"enemys","id":"E1014"}, + "1015": {"cls":"enemys","id":"E1015"}, + "1016": {"cls":"enemys","id":"E1016"}, + "1017": {"cls":"enemys","id":"E1017"}, + "1018": {"cls":"enemys","id":"E1018"}, + "1019": {"cls":"enemys","id":"E1019"}, + "1020": {"cls":"enemys","id":"E1020"}, + "1021": {"cls":"enemys","id":"E1021"}, + "1022": {"cls":"enemys","id":"E1022"}, + "1023": {"cls":"enemys","id":"E1023"}, + "1024": {"cls":"enemys","id":"E1024"}, + "1025": {"cls":"enemys","id":"E1025"}, + "1026": {"cls":"enemys","id":"E1026"}, + "1027": {"cls":"enemys","id":"E1027"}, + "1028": {"cls":"enemys","id":"E1028"}, + "1029": {"cls":"enemys","id":"E1029"}, + "1030": {"cls":"enemys","id":"E1030"}, + "1031": {"cls":"enemys","id":"E1031"}, + "1032": {"cls":"enemys","id":"E1032"}, + "1033": {"cls":"enemys","id":"E1033"}, + "1034": {"cls":"enemys","id":"E1034"}, + "1035": {"cls":"enemys","id":"E1035"}, + "1036": {"cls":"enemys","id":"E1036"}, + "1037": {"cls":"enemys","id":"E1037"}, + "1038": {"cls":"enemy48","id":"E9"}, + "1039": {"cls":"enemy48","id":"E10"}, + "1040": {"cls":"enemy48","id":"E11"}, + "1041": {"cls":"enemy48","id":"E12"}, + "1042": {"cls":"enemy48","id":"E13"}, + "1043": {"cls":"enemy48","id":"E14"}, + "1044": {"cls":"enemy48","id":"E15"}, + "1045": {"cls":"enemy48","id":"E16"}, + "1046": {"cls":"enemy48","id":"E17"}, + "1047": {"cls":"enemy48","id":"E18"}, + "1048": {"cls":"enemy48","id":"E19"}, + "1049": {"cls":"enemy48","id":"E20"}, + "1050": {"cls":"enemy48","id":"E21"}, + "1051": {"cls":"enemy48","id":"E22"}, + "1052": {"cls":"enemy48","id":"E23"}, + "1053": {"cls":"enemy48","id":"E24"}, + "1054": {"cls":"enemy48","id":"E25"}, + "1055": {"cls":"enemy48","id":"E26"}, + "1056": {"cls":"enemy48","id":"E27"}, + "1057": {"cls":"enemy48","id":"E28"}, + "1058": {"cls":"enemy48","id":"E29"}, + "1059": {"cls":"enemy48","id":"E30"}, + "1060": {"cls":"enemy48","id":"E31"}, + "1061": {"cls":"enemy48","id":"E32"}, + "1062": {"cls":"enemy48","id":"E33"}, + "1063": {"cls":"enemy48","id":"E34"}, + "1064": {"cls":"enemy48","id":"E35"}, + "1065": {"cls":"enemy48","id":"E36"}, + "1066": {"cls":"enemy48","id":"E37"}, + "1067": {"cls":"enemy48","id":"E38"}, + "1068": {"cls":"enemy48","id":"E39"}, + "1069": {"cls":"enemy48","id":"E40"}, + "1070": {"cls":"enemy48","id":"E41"}, + "1071": {"cls":"enemy48","id":"E42"}, + "1072": {"cls":"enemy48","id":"E43"}, + "1073": {"cls":"enemy48","id":"E44"}, + "1074": {"cls":"enemy48","id":"E45"}, + "1075": {"cls":"enemy48","id":"E46"}, + "1076": {"cls":"enemy48","id":"E47"}, + "1077": {"cls":"enemy48","id":"E48"}, + "1078": {"cls":"enemy48","id":"E49"}, + "1079": {"cls":"enemy48","id":"E50"}, + "1080": {"cls":"enemy48","id":"E51"}, + "1081": {"cls":"enemy48","id":"E52"}, + "1082": {"cls":"enemy48","id":"E53"}, + "1083": {"cls":"enemy48","id":"E54"}, + "1084": {"cls":"enemy48","id":"E55"}, + "1085": {"cls":"enemy48","id":"E56"}, + "1086": {"cls":"enemy48","id":"E57"}, + "1087": {"cls":"enemy48","id":"E58"}, + "1088": {"cls":"enemy48","id":"E59"}, + "1089": {"cls":"enemy48","id":"E60"}, + "1090": {"cls":"enemy48","id":"E61"}, + "1091": {"cls":"enemy48","id":"E62"}, + "1092": {"cls":"enemy48","id":"E63"}, + "1093": {"cls":"enemy48","id":"E64"}, + "1094": {"cls":"enemy48","id":"E65"}, + "1095": {"cls":"enemy48","id":"E66"}, + "1096": {"cls":"enemy48","id":"E67"}, + "1097": {"cls":"enemy48","id":"E68"}, + "1098": {"cls":"enemy48","id":"E69"}, + "1099": {"cls":"enemy48","id":"E70"}, + "1100": {"cls":"enemy48","id":"E71"}, + "1101": {"cls":"enemy48","id":"E72"}, + "1102": {"cls":"enemy48","id":"E73"}, + "1103": {"cls":"enemy48","id":"E74"}, + "1104": {"cls":"enemy48","id":"E75"}, + "1105": {"cls":"enemy48","id":"E76"} +} \ No newline at end of file diff --git a/personalization.md b/personalization.md new file mode 100644 index 0000000..344105f --- /dev/null +++ b/personalization.md @@ -0,0 +1,441 @@ +# 个性化 + +?> 在这一节中,让我们来了解更多对样板个性化的修改 + +有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 + +## 图层的说明 + +HTML5魔塔是使用画布(canvas)来绘制,存在若干个图层,它们之间有一个覆盖关系,后面的图层将覆盖前面的图层。 + +所有图层从低往高依次如下:(加[B]的代表该层是大地图,[D]代表由系统按需动态创建,z-index代表该层的纵向高度) + +- bg**[B]**:背景层;绘制背景图层素材bgmap,和背景贴图 (z-index: 10) +- event**[B]**:事件层;所有事件(道具、墙壁、NPC、怪物等)都绘制在这一层进行处理 (z-index: 30) +- hero:勇士层;主要用来绘制勇士 (z-index: 40) +- event2**[B]**:事件2层;本层主要用来绘制48x32的图片素材的上半部分(避免和勇士错位) (z-index: 50) +- fg**[B]**:前景层;绘制前景图层素材fgmap,和前景贴图 (z-index: 60) +- damage**[B]**:显伤层;主要用来绘制怪物显伤和领域显伤 (z-index: 65) +- animate:动画层;主要用来绘制动画。 (z-index: 70) +- weather**[D]**:天气层;主要用来绘制天气(雨/雪/雾) (z-index: 80) +- route**[D]**:路线层;主要用来绘制勇士的行走路线图。 (z-index: 95) +- curtain:色调层;用来控制当前楼层的画面色调 (z-index: 125) +- image1\~50**[D]**:图片层;用来绘制图片等操作。(z-index: 100+code, 101~150) +- uievent**[D]**:自定义UI绘制层;用来进行自定义UI绘制等操作。(z-index:135,可以通过事件设置该值) +- ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 140) +- data:数据层;用来绘制一些顶层的或更新比较快的数据,如左上角的提示,战斗界面中数据的变化等等。 (z-index: 170) + +请注意:显示图片事件将自动创建一个图片层,z-index是100+图片编号。 + +而,色调层的z-index是25,ui层的z-index是140;因此,图片编号在1~24的将被色调层遮挡,25~40的将被ui层遮挡,41~50的将遮挡UI层。 + +uievent层为自定义UI绘制所在的层,其z值初始是135,可以通过事件设置;自定义绘制的闪烁光标所在层的z值永远比该值大1。 + +### 动态创建canvas + +从V2.5.3开始,可以在H5样板中任意动态创建canvas并进行使用。 + +使用`core.createCanvas(name, x, y, w, h, z)`来动态创建一个画布。 + +其中name为动态canvas名称,x,y,w,h为创建的画布相对窗口左上角的像素坐标和长宽,z为画布的纵向高度。 + +例如:`core.createCanvas('test', 10, 20, 100, 200, 74)` 创建了一个名为test的画布,其左上角相对窗口的像素坐标为(10,20),宽100高200,纵向高度74(在动画层和天气层之间)。 + +该函数会返回画布的context,也可以通过 `core.dymCanvas[name]` 来获得;例如 `core.dymCanvas.test` 就是我们上面创建的画布的context,然后进行操作。 + +也可以简单的使用`core.fillText()`, `core.fillRect()`, `core.strokeRect()`等等对画布进行任意绘制。 + +``` js +core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); // 绘制一段文本 +``` + +使用 `core.deleteCanvas(name)` 删除一个动态创建的画布,例如 `core.deleteCanvas('test')`。 + +`core.deleteAllCanvas()`可以删除所有动态创建的画布,`core.relocateCanvas(name, x, y)`和`core.resizeCanvas(name, x, y)`可以对画布的位置和大小进行改变。 + +更多详细API请参见[API列表](api)。 + +## 单点多事件 + +带有独立开关的商人算是一个最为简单的单点多事件的例子了(提供在了事件编辑器左侧的“常用事件模板”),单点多事件最常用的指令就是“转变图块”(或关门)和“条件分歧”。下面以几个具体例子来进行详细说明: +1. 打怪变成门或道具、开门变成怪或道具:怪物、门、道具都是系统触发器,直接利用afterXxx事件。如: + * 战后事件:关门`yellowDoor`(原地关上了一扇黄门) + * 战后事件:转变图块为`yellowKey`(怪物掉落了黄钥匙) + * 开门后事件:转变图块为`greenSlime`或`yellowKey`(开门变成了史莱姆或黄钥匙) + * 打怪变成别的怪、开门关上别的门、门和怪来回变的,用独立开关计数。 + * 如有批量需求,请使用“脚本编辑 - `afterXxx`函数或怪物的(批量)战前/战后事件。 +2. 打怪/开门/捡道具后变成传送点或npc: + * 这时的传送点就不能用“楼梯、传送门”事件了,而应该用只有一条“场景切换”指令的普通事件。 + * 此事件不勾选“覆盖触发器”,这样怪物、门和道具的系统触发器会触发。 + * 在对应的`afterXxx`事件中“转变图块为”传送点或npc,淡入效果更佳哦。 + * 请注意,因为道具是可通行的,如果勇士走到道具上,就会和npc重合。 +3. (懒人的选择)利用`图块类别:x,y`和`图块ID:x,y`等值块集中处理: + * 如果您实在搞不清楚“覆盖触发器”和“通行状态”这两项,那就干脆勾选前者并把后者设为不可通行,本希望可以通行的场合就得用“无视地形移动勇士”指令前进一步。 + * 用自动事件去转变图块,然后利用上述两个值块判定当前图块的状态等,再用“强制战斗”、“开门(需要钥匙)”和“增加道具”分别处理吧。 + +## 并行脚本:即时制的希望? + +如前所述,自动事件可以让您不用考虑可能导致各值块发生变化的缘由,而是简单地在刷新状态栏时检测这些值块是否满足某些特定的关系。 + +然而,自动事件依然是通过插入到当前待执行指令队列的开头,或追加到队列的结尾来执行的。换言之,它占用的是事件流本身的线程资源。 + +那形如bgs、bgv、飘云飘雾、地图背景旋转等即使玩家挂机也会照常执行的特效,该怎么办呢? + +js语言其实是没有多线程的,但我们可以写一些浏览器帧刷新时执行的脚本,也就是“并行脚本”。 + +并行脚本分为两种,全塔并行和楼层并行。前者在“脚本编辑—并行脚本”,后者在楼层属性。一般来说,当您有多个楼层需要执行相同的并行脚本时,建议写在前者并通过对`core.status.floorId`的范围进行判定(注意判空!)来决定具体执行的内容。 + +并行脚本将被系统反复执行,执行的时机是“浏览器帧刷新”,换言之相邻两次执行的间隔取决于浏览器或设备的性能,上限为60fps即每秒60次。 + +如果有一个bgs是1秒长的心跳声,我们把它注册到了全塔属性的音效中。假设这个bgs要被用于MT0层,那么我们在MT0层的“楼层属性——并行脚本”中写这样的代码: + +``` js +if (core.hasFlag('frame')) { + // 剧情事件中将“变量:frame”设为正整数来开启bgs,设为0来关闭 + core.status.hero.flags.frame %= 60000; // 防止挂机太久导致溢出 + if (core.getFlag('frame', 0) % 60 === 0) + core.playSound('heartBeat.mp3'); // 每60帧即1秒播放一次,还可以指定音调。 + core.status.hero.flags.frame++; // 帧数加1 +} // 其他特效也是一样的用法。 +``` + +在并行脚本(全塔或楼层)里可以使用`timestamp`作为参数,表示从游戏开始到当前总共过了多少毫秒。 + +## 覆盖楼传事件 + +对于特殊的塔,我们可以考虑修改楼传事件来完成一些特殊的要求,比如镜子可以按楼传来切换表里。 + +要修改楼传事件,需要进行如下两步: + +1. 重写楼传的点击事件。在插件中对`core.control.useFly`进行重写。详细代码参见[重写点击楼传事件](script#重写点击楼传事件)。 +2. 修改楼传的使用事件。和其他永久道具一样,在地图编辑器的图块属性中修改楼传的`useItemEvent`(或`useItemEffect`)和`canUseItemEffect`两个内容。例如: +``` js +"useItemEvent": "[...]" // 执行某段自定义事件 +"canUseItemEffect": "true" // 任何时候可用 +``` +3. 将全塔属性里的`楼梯边楼传`的勾去掉。 + +除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyBoard`)即可。 + +## 自定义快捷键 + +如果需要绑定某个快捷键为处理一段事件,也是可行的。 + +要修改按键,我们可以在脚本编辑的`onKeyUp`进行处理: + +我们设置一个快捷键进行绑定,比如 `Y`,其 `keycode` 是 `89` 。 +(大键盘数字键 `0-9` 的 `keycode` 为 `48-57, A-Z` 键的 `keycode` 为 `65-90` ,其他键的 `keycode` 搜一下就能得到) + +然后在脚本编辑的`onKeyUp`函数的`switch`中进行处理。 + +``` js +case 89: // 使用该按键的keyCode,比如Y键就是89 + // 还可以再判定altKey是否被按下,即 if (altKey) { ... + + // ... 在这里写你要执行脚本 + // **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为** + if (core.hasItem('...')) { + core.status.route.push("key:0"); // 记录按键到录像中 + core.useItem('...', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + } + + break; +``` +强烈建议所有新增的自定义非数字快捷键均给个对应的永久道具可点击,以方便手机端的行为。 + +使用`core.status.route.push("key:"+keyCode)`可以将这次按键记录在录像中。 + +!> 如果记录了按键,且使用道具的话,需要将useItem的第二个参数设为true,避免重复记录! + +可以使用`altKey`来判断Alt键是否被同时按下(V2.8起,手机端的Alt键以粘滞键方式提供)。 + +## 左手模式 + +V2.7.3起,样板面向玩家提供了“左手模式”,可在ESC菜单中开启。开启后,wsad将用于勇士移动,ijkl将代替原本wsad的功能(存读档等)。 + +## 插件系统 + +在H5中,提供了“插件”系统。在V2.6中提供了一个插件下拉框,用户可以自行创建和写插件。 + +在插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。 + +下面是一个很简单的例子,我编写一个插件函数,其效果是让勇士生命值变成原来的x倍,并令面前的图块消失。 + +``` js +this.myfunc = function(x) { + core.status.hero.hp *= x; // 勇士生命翻若干倍 + core.insertAction([ // 自定义事件:令面前的图块消失。 + {"type": "setValue", "name": "flag:x", "value": "core.nextX()"}, + {"type": "setValue", "name": "flag:y", "value": "core.nextY()"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]} + ]); +} +``` + +然后比如我们在某个道具的使用效果 `useItemEffect` 中写 `core.plugin.myfunc(2)` 即可调用此插件函数。也可以在战后事件或自定义脚本等位置来写。 + +网站上也提供了一个[插件库](https://h5mota.com/plugins/),欢迎大家把自己写的插件进行共享。 + +从V2.6开始,在插件中用`this.xxx`定义的函数将会被转发到core中。例如上述的`myfunc`除了`core.plugin.myfunc`外也可以直接`core.myfunc`调用。 + +详见[函数的转发](script#函数的转发)。 + +## 手机端按键模式 + +从V2.5.3以后,我们可以给手机端增加按键了,这样将非常有利于技能的释放。 + +用户在竖屏模式下点击难度标签,就会在工具栏按钮和快捷键模式之间进行切换。 + +切换到快捷键模式后,可以点1-7,分别等价于在电脑端按键1-7。 + +V2.8起,点击最后的A键则相当于电脑端按下/松开Alt键(即粘滞键),可以用来快捷换装。 + +可以在脚本编辑的`onKeyUp`中定义每个快捷键的使用效果,比如使用道具或释放技能等。 + +默认值下,1使用破,2使用炸,3使用飞,4使用其他存在的道具,5读取上一个自动存档,6读取下一个自动存档,7轻按。可以相应修改成自己的效果。 + +也可以替换icons.png中的对应图标,以及修改main.js中`main.statusBar.image.btn1~8`中的onclick事件来自定义按钮和对应按键。 + +非竖屏模式下、回放录像中、隐藏状态栏中,将不允许进行切换。 + +## 自绘状态栏 + +从V2.5.3开始允许自绘状态栏。要自绘状态栏,则应该打开全塔属性中的`statusCanvas`开关。 + +自绘模式下,全塔属性中的`statusCanvasRowsOnMobile`将控制竖屏模式下的状态栏行数(下面的`rows`)。 + +开启自绘模式后,可以在脚本编辑的`drawStatusBar`中自行进行绘制。 + +横屏模式下的状态栏为`129x416`(15x15则是`149x480`);竖屏模式下的状态栏为`416*(32*rows+9)`(15x15是480)。 + +具体可详见脚本编辑的`drawStatusBar`函数。 + +自绘状态栏开启后,金币图标将失去打开快捷商店的功能,您可以修改脚本编辑的 `onStatusBarCLick` 函数来适配。 + +## 自定义状态栏的显示项 + +在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - 主样式)等等。 + +但是,如果我们还想新增其他项目的显示,比如攻速或者暴击,该怎么办? + +我们可以[自绘状态栏](#自绘状态栏),或者采用下面两个方式之一来新增。 + +### 利用已有项目 + +一个最为简单的方式是,直接利用已有项目。 + +例如,如果本塔中没有技能栏,则可以使用技能栏所对应的显示项。 + +1. 覆盖project/icons.png中技能的图标 +2. 打开全塔属性的enableSkill开关 +3. 在脚本编辑-updateStatusBar中可以直接替换技能栏的显示内容 + +``` + // 替换成你想显示的内容,比如你定义的一个flag:abc。 + core.setStatusBarInnerHTML('skill', core.getFlag("abc", 0)); +``` + +### 额外新增新项目 + +如果是在需要给状态栏新定义项目,则需要进行如下几个操作: + +1. 定义ID;比如攻速我就定义speed,暴击可以简单的定义baoji;你也可以定义其他的ID,但是不能和已有的重复。这里以speed为例。 +2. 在index.html的statusBar中,进行该状态栏项的定义。仿照其他几项,插在其应当显示的位置,注意替换掉相应的ID。 +``` html +

      + +

      +
      +``` +4. 使用便捷PS工具,打开project/icons.png,新增一行并将魔力的图标P上去;记下其索引比如37(从0开始数)。 +5. 在main.js的this.statusBar中增加图片、图标和内容的定义。 +``` js +this.statusBar = { + 'images': { + // ...其他略 + 'speed': document.getElementById("img-speed"), // 图片的定义 + }, + 'icons': { + // ...其他略 + 'speed': 37, // 图标的定义,这里对应的是icons.png中的索引 + }, + // ...其他略 + 'speed': document.getElementById('speed'), // 显示内容(数据)的定义 +} +``` +6. 显示内容的设置。在脚本编辑的updateStatusBar函数,可以对该状态栏显示内容进行设置,下面是几个例子。 +``` js +// 设置其显示内容为status:speed值;需要在project/data.js中firstData的hero那里新增初始值`"speed": 0`。 +core.setStatusBarInnerHTML('speed', core.getStatus('speed')); +// 设置其显示内容为flag:speed值,无需额外进行定义。 +core.setStatusBarInnerHTML('speed', core.getFlag('speed', 0)); +``` +总的来说不建议这样做,因为`main.js`和各种html文件不在`project`文件夹,会导致随样板更新迁移接档变得困难。 + +## 技能塔的支持 + +从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。要支持技能塔,可能需要如下几个方面: + +- 魔力(和上限)的添加;技能的定义 +- 状态栏的显示 +- 技能的触发(按键与录像问题) +- 技能的效果 + +### 魔力的定义添加;技能的定义 + +从V2.5开始,提供了 `status:mana` 选项,可以直接代表当前魔力值。可以在全塔属性勾选来启用它。 + +如果需要魔力上限,则可以使用 `status:manaMax` 来表示当前的魔力最大值,负数表示没有上限。 + +同时,我们可以使用flag:skill表示当前开启的技能编号,flag:skillName表示当前开启的技能名称。 + +如果flag:skill不为0,则代表当前处于某个技能开启状态,且状态栏显示flag:skillName值。伤害计算函数中只需要对flag:skill进行处理即可。 + +### 状态栏的显示 + +从V2.5开始,魔力值和技能名的状态栏项目已经被添加,可以直接使用。 + +在脚本编辑-updateStatusBar中,可以对状态栏显示内容进行修改。带有上限时如果一行放不下,可以想办法像生命一样拆成两行。 + +``` js +// 设置魔力值; status:manamax 只有在非负时才生效。 +if (core.status.hero.manamax != null && core.status.hero.manamax >= 0) { + core.status.hero.mana = Math.min(core.status.hero.mana, core.status.hero.manamax); + core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.status.hero.manamax); +} +else { + core.setStatusBarInnerHTML("mana", core.status.hero.mana); +} +// 设置技能栏 +// 可以用flag:skill表示当前开启的技能类型,flag:skillName显示技能名 +core.setStatusBarInnerHTML('skill', core.getFlag('skillName', '无')); +``` + +### 技能的触发 + +#### 使用道具作为技能 + +由于手机端按字母键不方便,虚拟键盘不好用,因此强烈推荐**给每个字母键技能设置一个道具,在道具栏点击使用!** + +下面是个很简单的例子,要制作一个技能"二倍斩"。 + +我们可以设置一个道具,其cls是`constants`(永久道具),ID比如是`skill1`。 + +该道具的使用判定`canUseItemEffect`是`true`(表示任意时候都可使用),使用效果`useItemEffect`是: + +``` js +if (core.getFlag('skill', 0)==0) { // 判断当前是否已经开了技能 + if (core.getStatus('mana')>=5) { // 这里要写当前能否开技能的条件判断,比如魔力值至少要多少 + core.setFlag('skill', 1); // 开技能1 + core.setFlag('skillName', '二倍斩'); // 设置技能名 + } + else { + core.drawTip("魔力不足,无法开技能"); + } +} +else { // 关闭技能 + core.setFlag('skill', 0); // 关闭技能状态 + core.setFlag('skillName', '无'); +} +``` + +简单的说,用flag:skill判断当前开启的技能,flag:skillName表示该技能名。(可在状态栏显示) + +该(技能)道具任何时候都可被使用;使用时,判断当前是否开启了技能,如果开启则关闭,没开则再判断是否允许开启(魔力值够不够等)。 + +V2.6.6起,道具提供了`useItemEvent`项,建议使用它而不是上面的脚本。'- + +#### 快捷键触发技能 + +在PC端,我们还可以按键触发技能。 + +在技能的道具定义完毕后,再将该道具绑定到一个快捷键上。有关绑定按键请参见[自定义快捷键](#自定义快捷键)。 + +下面是一个很简单的例子,当勇士按下 `F` 后,触发我们上面定义的二倍斩技能。 +``` js +case 70: // F:开启技能“二倍斩” + // 是否拥有“二倍斩”这个技能道具 + if (core.hasItem('skill1')) { + core.status.route.push("key:70"); + core.useItem('skill1', true); + } + break; +``` +在勇士处于停止的条件下,按下 `F` 键时,判断技能的道具是否存在,如果存在再使用它。 + +!> 由于现在手机端存在拓展键盘,也强烈建议直接覆盖1-7的使用效果,这样手机端使用也非常方便。 + +### 技能的效果 + +最后一点就是技能的效果;其实到了这里就和RM差不多了。 + +技能的效果要分的话有地图类技能,战斗效果类技能,后续影响类技能什么的,这里只介绍最简单的战斗效果类技能。 + +其他的几类技能根据需求可能更为麻烦,有兴趣可自行进行研究。 + +战斗效果内技能要改两个地方:战斗伤害计算,战后扣除魔力值。 + +战斗伤害计算在脚本编辑的`getDamageInfo`函数,有需求直接修改这个函数即可。 + +战后扣除魔力值则在脚本编辑的`afterBattle`中进行编辑即可。 + +举个例子,我设置一个勇士的技能:二倍斩,开启技能消耗5点魔力,下一场战斗攻击力翻倍。 + +那么,直接在脚本编辑的`getDamageInfo`中进行判断: + +``` js +if (core.getFlag('skill', 0)==1) { // 开启了技能1 + hero_atk *= 2; // 计算时攻击力翻倍 +} +``` + +然后在脚本编辑的`afterBattle`中进行魔力值的扣除: + +``` js +// 战后的技能处理,比如扣除魔力值 +if (core.flags.statusBarItems.indexOf('enableSkill')>=0) { + // 检测当前开启的技能类型 + var skill = core.getFlag('skill', 0); + if (skill==1) { // 技能1:二倍斩 + core.status.hero.mana-=5; // 扣除5点魔力值 + } + // 关闭技能 + core.setFlag('skill', 0); + core.setFlag('skillName', '无'); +} +``` + +!> 开启技能后,建议将全塔属性的`useLoop`置为`true`,即改用循环计算临界值,这样临界计算才不会出问题! + +  + +通过上述这几种方式,我们就能成功的让H5支持技能啦! + +## 系统使用的flag变量 + +众所周知,自定义flag变量都可以任意定义并取用(未定义直接取用的flag默认值为0)。 + +下面是一些可能会被系统设置或取用的flag变量: + +- **`flag:hard`**: 当前的难度标志;此flag变量在setInitData中被定义,可以直接取用来判定当前难度分歧。上传成绩时将根据此flag来对不同难度进行排序。 +- **`flag:posion`**, **`flag:weak`**, **`flag:curse`**: 中毒、衰弱、诅咒状态。 +- **`flag:no_zone`**, **`flag:no_repulse`**, **`flag:no_laser`**, **`flag:no_betweenAttack`**: 是否分别免疫领域、阻击、激光、夹击效果。 +- **`flag:hatred`**: 当前的仇恨数值。 +- **`flag:commonTimes`**: 全局商店共用次数时的访问次数。 +- **`flag:input`**: 接受用户输入的事件后,存放用户输入的结果。 +- **`flag:type`**, **`flag:keycode`**, **`flag:x`**, **`flag:y`**, **`flag:px`**, **`flag:py`**: 等待用户操作后,用户的操作类型,按键keycode或点击/像素坐标。 +- **`flag:skill`**, **`flag:skillName`**: 开启的技能编号和技能名。 +- **`flag:saveEquips`**: 快速换装时保存的套装。 +- **`flag:__visited__`**: 当前访问过的楼层。 +- **`flag:__atk_buff__`**, **`flag:__def_buff__`**, **`flag:__mdef_buff__`**: 当前攻防护盾的实际计算比例加成。 +- **`flag:__color__`**, **`flag:__weather__`**, **`flag:__volume__`**: 当前的画面色调、天气和音量。 +- **`flag:__events__`**: 当前保存的事件列表,读档时会恢复(适用于在事件中存档) +- **`flag:textAttribute`**, **`flag:globalAttribute`**, **`flag:globalFlags`**: 当前的剧情文本属性,当前的全局属性,当前的全局开关。 +- **`flag:cannotMoveDirectly`**, **`flag:__noClickMove__`**: 当前是否不允许瞬间移动,当前用户是否开启了单击瞬移。 +- **`flag:hideStatusBar`**, **`flag:showToolbox`**: 是否隐藏状态栏,是否显示工具栏。 +- **`flag:debug`**: 当前是否开启了调试模式。 +- **`flag:__seed__`**, **`flag:__rand__`**: 伪随机数生成种子和当前的状态 + +========================================================================================== + +[继续阅读脚本](script) diff --git a/plugin.d.ts b/plugin.d.ts new file mode 100644 index 0000000..e1bc16b --- /dev/null +++ b/plugin.d.ts @@ -0,0 +1,32 @@ +// 这里包含所有插件导出的函数及变量声明,声明的函数会在类型标注中标注到core上 + +type Ref = { + value: T; +}; + +type DragFn = (x: number, y: number, e: MouseEvent | TouchEvent) => void; + +type CanParseCss = keyof { + [P in keyof CSSStyleDeclaration as CSSStyleDeclaration[P] extends string + ? P extends string + ? P + : never + : never]: CSSStyleDeclaration[P]; +}; + +interface PluginDeclaration {} + +type Forward = { + [K in keyof T as T[K] extends Function + ? K extends `_${string}` + ? never + : K + : never]: T[K]; +}; + +type ForwardKeys = keyof Forward; + +declare const Mota: import('../index.d.ts').IMota; +interface Window { + Mota: import('../index.d.ts').IMota; +} diff --git a/plugins.js b/plugins.js new file mode 100644 index 0000000..8057fae --- /dev/null +++ b/plugins.js @@ -0,0 +1,14397 @@ +/// +var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = +{ + "init": function () { + + this.loadCss = function (href) { + var style = document.createElement('link'); + style.type = 'text/css'; + style.href = href; + style.rel = 'stylesheet'; + document.getElementsByTagName('head')[0].appendChild(style); + } + if (core.platform.isPC && main.mode == 'play') { + //core.plugin.loadCss('stars.css'); + } + + this._afterLoadResources = function () { + // 本函数将在所有资源加载完毕后,游戏开启前被执行 + + + } + }, + "drawLight": function () { + // 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...) + // 【参数说明】 + // name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建 + // color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。 + // lights:可选,一个数组,定义了每个独立的灯光。 + // 其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。 + // lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。 + // 比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。 + // 【调用样例】 + // core.plugin.drawLight('curtain'); // 在curtain层绘制全图不透明度0.9,等价于更改画面色调为[0,0,0,0.9]。 + // core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。 + // core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 + // core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 + // core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,40%后才开始衰减。 + this.drawLight = function (name, color, lights, lightDec) { + + // 清空色调层;也可以修改成其它层比如animate/weather层,或者用自己创建的canvas + var ctx = core.getContextByName(name); + if (ctx == null) { + if (typeof name == 'string') + ctx = core.createCanvas(name, 0, 0, core._PX_ || core.__PIXELS__, core._PY_ || core.__PIXELS__, 98); + else return; + } + + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + core.clearMap(name); + // 绘制色调层,默认不透明度 + if (color == null) color = 0.9; + ctx.fillStyle = "rgba(0,0,0," + color + ")"; + ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + lightDec = core.clamp(lightDec, 0, 1); + + // 绘制每个灯光效果 + ctx.globalCompositeOperation = 'destination-out'; + lights.forEach(function (light) { + // 坐标,半径,中心不透明度 + var x = light[0], + y = light[1], + r = light[2]; + // 计算衰减距离 + var decDistance = parseInt(r * lightDec); + // 正方形区域的直径和左上角坐标 + var grd = ctx.createRadialGradient(x, y, decDistance, x, y, r); + grd.addColorStop(0, "rgba(0,0,0,1)"); + grd.addColorStop(1, "rgba(0,0,0,0)"); + ctx.beginPath(); + ctx.fillStyle = grd; + ctx.arc(x, y, r, 0, 2 * Math.PI); + ctx.fill(); + }); + ctx.globalCompositeOperation = 'source-over'; + // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + } + }, + "shop": function () { + // 【全局商店】相关的功能 + // + // 打开一个全局商店 + // shopId:要打开的商店id;noRoute:是否不计入录像 + this.openShop = function (shopId, noRoute) { + var shop = core.status.shops[shopId]; + // Step 1: 检查能否打开此商店 + if (!this.canOpenShop(shopId)) { + core.drawTip("该商店尚未开启"); + return false; + } + + // Step 2: (如有必要)记录打开商店的脚本事件 + if (!noRoute) { + core.status.route.push("shop:" + shopId); + } + + // Step 3: 检查道具商店 or 公共事件 + if (shop.item) { + if (core.openItemShop) { + core.openItemShop(shopId); + } else { + core.playSound('操作失败'); + core.insertAction("道具商店插件不存在!请检查是否存在该插件!"); + } + return; + } + if (shop.commonEvent) { + core.insertCommonEvent(shop.commonEvent, shop.args); + return; + } + + _shouldProcessKeyUp = true; + + // Step 4: 执行标准公共商店 + core.insertAction(this._convertShop(shop)); + return true; + } + + ////// 将一个全局商店转变成可预览的公共事件 ////// + this._convertShop = function (shop) { + return [ + { "type": "function", "function": "function() {core.addFlag('@temp@shop', 1);}" }, + { + "type": "while", + "condition": "true", + "data": [ + // 检测能否访问该商店 + { + "type": "if", + "condition": "core.isShopVisited('" + shop.id + "')", + "true": [ + // 可以访问,直接插入执行效果 + { "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', false) }" }, + ], + "false": [ + // 不能访问的情况下:检测能否预览 + { + "type": "if", + "condition": shop.disablePreview, + "true": [ + // 不可预览,提示并退出 + { "type": "playSound", "name": "操作失败" }, + "当前无法访问该商店!", + { "type": "break" }, + ], + "false": [ + // 可以预览:将商店全部内容进行替换 + { "type": "tip", "text": "当前处于预览模式,不可购买" }, + { "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', true) }" }, + ] + } + ] + } + ] + }, + { "type": "function", "function": "function() {core.addFlag('@temp@shop', -1);}" } + ]; + } + + this._convertShop_replaceChoices = function (shopId, previewMode) { + var shop = core.status.shops[shopId]; + var choices = (shop.choices || []).filter(function (choice) { + if (choice.condition == null || choice.condition == '') return true; + try { return core.calValue(choice.condition); } catch (e) { return true; } + }).map(function (choice) { + var ableToBuy = core.calValue(choice.need); + return { + "text": choice.text, + "icon": choice.icon, + "color": ableToBuy && !previewMode ? choice.color : [153, 153, 153, 1], + "action": ableToBuy && !previewMode ? [{ "type": "playSound", "name": "商店" }].concat(choice.action) : [ + { "type": "playSound", "name": "操作失败" }, + { "type": "tip", "text": previewMode ? "预览模式下不可购买" : "购买条件不足" } + ] + }; + }).concat({ "text": "离开", "action": [{ "type": "playSound", "name": "取消" }, { "type": "break" }] }); + core.insertAction({ "type": "choices", "text": shop.text, "choices": choices }); + } + + /// 是否访问过某个快捷商店 + this.isShopVisited = function (id) { + if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {}); + var shops = core.getFlag("__shops__"); + if (!shops[id]) shops[id] = {}; + return shops[id].visited; + } + + /// 当前应当显示的快捷商店列表 + this.listShopIds = function () { + return Object.keys(core.status.shops).filter(function (id) { + return core.isShopVisited(id) || !core.status.shops[id].mustEnable; + }); + } + + /// 是否能够打开某个商店 + this.canOpenShop = function (id) { + if (this.isShopVisited(id)) return true; + var shop = core.status.shops[id]; + if (shop.item || shop.commonEvent || shop.mustEnable) return false; + return true; + } + + /// 启用或禁用某个快捷商店 + this.setShopVisited = function (id, visited) { + if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {}); + var shops = core.getFlag("__shops__"); + if (!shops[id]) shops[id] = {}; + if (visited) shops[id].visited = true; + else delete shops[id].visited; + } + + /// 能否使用快捷商店 + this.canUseQuickShop = function (id) { + // 如果返回一个字符串,表示不能,字符串为不能使用的提示 + // 返回null代表可以使用 + + // 检查当前楼层的canUseQuickShop选项是否为false + if (core.status.thisMap.canUseQuickShop === false) + return '当前楼层不能使用快捷商店。'; + return null; + } + + var _shouldProcessKeyUp = true; + + /// 允许商店X键退出 + core.registerAction('keyUp', 'shops', function (keycode) { + if (!core.status.lockControl || core.status.event.id != 'action') return false; + if ((keycode == 13 || keycode == 32) && !_shouldProcessKeyUp) { + _shouldProcessKeyUp = true; + return true; + } + + if (!core.hasFlag("@temp@shop") || core.status.event.data.type != 'choices') return false; + var data = core.status.event.data.current; + var choices = data.choices; + var topIndex = core.actions._getChoicesTopIndex(choices.length); + if (keycode == 88 || keycode == 27) { // X, ESC + core.actions._clickAction(core._HALF_WIDTH_ || core.__HALF_SIZE__, topIndex + choices.length - 1); + return true; + } + return false; + }, 60); + + /// 允许长按空格或回车连续执行操作 + core.registerAction('keyDown', 'shops', function (keycode) { + if (!core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != 'action') return false; + if (core.status.event.data.type != 'choices') return false; + core.status.onShopLongDown = true; + var data = core.status.event.data.current; + var choices = data.choices; + var topIndex = core.actions._getChoicesTopIndex(choices.length); + if (keycode == 13 || keycode == 32) { // Space, Enter + core.actions._clickAction(core._HALF_WIDTH_ || core.__HALF_SIZE__, topIndex + core.status.event.selection); + _shouldProcessKeyUp = false; + return true; + } + return false; + }, 60); + + // 允许长按屏幕连续执行操作 + core.registerAction('longClick', 'shops', function (x, y, px, py) { + if (!core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != 'action') return false; + if (core.status.event.data.type != 'choices') return false; + var data = core.status.event.data.current; + var choices = data.choices; + var topIndex = core.actions._getChoicesTopIndex(choices.length); + if (Math.abs(x - (core._HALF_WIDTH_ || core.__HALF_SIZE__)) <= 2 && y >= topIndex && y < topIndex + choices.length) { + core.actions._clickAction(x, y); + return true; + } + return false; + }, 60); + }, + "removeMap": function () { + // 高层塔砍层插件,删除后不会存入存档,不可浏览地图也不可飞到。 + // 推荐用法: + // 对于超高层或分区域塔,当在1区时将2区以后的地图删除;1区结束时恢复2区,进二区时删除1区地图,以此类推 + // 这样可以大幅减少存档空间,以及加快存读档速度 + + // 删除楼层 + // core.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层 + // core.removeMaps("MT10") 只删除MT10层 + this.removeMaps = function (fromId, toId) { + toId = toId || fromId; + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + if (toIndex < 0) toIndex = core.floorIds.length - 1; + flags.__visited__ = flags.__visited__ || {}; + flags.__removed__ = flags.__removed__ || []; + flags.__disabled__ = flags.__disabled__ || {}; + flags.__leaveLoc__ = flags.__leaveLoc__ || {}; + for (var i = fromIndex; i <= toIndex; ++i) { + var floorId = core.floorIds[i]; + if (core.status.maps[floorId].deleted) continue; + delete flags.__visited__[floorId]; + flags.__removed__.push(floorId); + delete flags.__disabled__[floorId]; + delete flags.__leaveLoc__[floorId]; + (core.status.autoEvents || []).forEach(function (event) { + if (event.floorId == floorId && event.currentFloor) { + core.autoEventExecuting(event.symbol, false); + core.autoEventExecuted(event.symbol, false); + } + }); + core.status.maps[floorId].deleted = true; + core.status.maps[floorId].canFlyTo = false; + core.status.maps[floorId].canFlyFrom = false; + core.status.maps[floorId].cannotViewMap = true; + } + } + + // 恢复楼层 + // core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层 + // core.resumeMaps("MT10") 只恢复MT10层 + this.resumeMaps = function (fromId, toId) { + toId = toId || fromId; + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + if (toIndex < 0) toIndex = core.floorIds.length - 1; + flags.__removed__ = flags.__removed__ || []; + for (var i = fromIndex; i <= toIndex; ++i) { + var floorId = core.floorIds[i]; + if (!core.status.maps[floorId].deleted) continue; + flags.__removed__ = flags.__removed__.filter(function (f) { return f != floorId; }); + core.status.maps[floorId] = core.loadFloor(floorId); + } + } + + // 分区砍层相关 + var inAnyPartition = function (floorId) { + var inPartition = false; + (core.floorPartitions || []).forEach(function (floor) { + var fromIndex = core.floorIds.indexOf(floor[0]); + var toIndex = core.floorIds.indexOf(floor[1]); + var index = core.floorIds.indexOf(floorId); + if (fromIndex < 0 || index < 0) return; + if (toIndex < 0) toIndex = core.floorIds.length - 1; + if (index >= fromIndex && index <= toIndex) inPartition = true; + }); + return inPartition; + } + + // 分区砍层 + this.autoRemoveMaps = function (floorId) { + if (main.mode != 'play' || !inAnyPartition(floorId)) return; + // 根据分区信息自动砍层与恢复 + (core.floorPartitions || []).forEach(function (floor) { + var fromIndex = core.floorIds.indexOf(floor[0]); + var toIndex = core.floorIds.indexOf(floor[1]); + var index = core.floorIds.indexOf(floorId); + if (fromIndex < 0 || index < 0) return; + if (toIndex < 0) toIndex = core.floorIds.length - 1; + if (index >= fromIndex && index <= toIndex) { + core.resumeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]); + } else { + core.removeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]); + } + }); + } + }, + "fiveLayers": function () { + // 是否启用五图层(增加背景2层和前景2层) 将__enable置为true即会启用;启用后请保存后刷新编辑器 + // 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层 + // 另外 请注意加入两个新图层 会让大地图的性能降低一些 + // 插件作者:ad + var __enable = false; + if (!__enable) return; + + // 创建新图层 + function createCanvas(name, zIndex) { + if (!name) return; + var canvas = document.createElement('canvas'); + canvas.id = name; + canvas.className = 'gameCanvas anti-aliasing'; + // 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高 + if (main.mode != "editor") canvas.style.zIndex = zIndex || 0; + // 将图层插入进游戏内容 + document.getElementById('gameDraw').appendChild(canvas); + var ctx = canvas.getContext('2d'); + core.canvas[name] = ctx; + canvas.width = core._PX_ || core.__PIXELS__; + canvas.height = core._PY_ || core.__PIXELS__; + return canvas; + } + + var bg2Canvas = createCanvas('bg2', 20); + var fg2Canvas = createCanvas('fg2', 63); + // 大地图适配 + core.bigmap.canvas = ["bg2", "fg2", "bg", "event", "event2", "fg", "damage"]; + core.initStatus.bg2maps = {}; + core.initStatus.fg2maps = {}; + + if (main.mode == 'editor') { + /*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/ + // 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层) + // 背景层2(bg2) 插入事件层(event)之前(即bg与event之间) + document.getElementById('mapEdit').insertBefore(bg2Canvas, document.getElementById('event')); + // 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后) + document.getElementById('mapEdit').insertBefore(fg2Canvas, document.getElementById('ebm')); + // 原本有三个图层 从4开始添加 + var num = 4; + // 新增图层存入editor.dom中 + editor.dom.bg2c = core.canvas.bg2.canvas; + editor.dom.bg2Ctx = core.canvas.bg2; + editor.dom.fg2c = core.canvas.fg2.canvas; + editor.dom.fg2Ctx = core.canvas.fg2; + editor.dom.maps.push('bg2map', 'fg2map'); + editor.dom.canvas.push('bg2', 'fg2'); + + // 创建编辑器上的按钮 + var createCanvasBtn = function (name) { + // 电脑端创建按钮 + var input = document.createElement('input'); + // layerMod4/layerMod5 + var id = 'layerMod' + num++; + // bg2map/fg2map + var value = name + 'map'; + input.type = 'radio'; + input.name = 'layerMod'; + input.id = id; + input.value = value; + editor.dom[id] = input; + input.onchange = function () { + editor.uifunctions.setLayerMod(value); + } + return input; + }; + + var createCanvasBtn_mobile = function (name) { + // 手机端往选择列表中添加子选项 + var input = document.createElement('option'); + var id = 'layerMod' + num++; + var value = name + 'map'; + input.name = 'layerMod'; + input.value = value; + editor.dom[id] = input; + return input; + }; + if (!editor.isMobile) { + var input = createCanvasBtn('bg2'); + var input2 = createCanvasBtn('fg2'); + // 获取事件层及其父节点 + var child = document.getElementById('layerMod'), + parent = child.parentNode; + // 背景层2插入事件层前 + parent.insertBefore(input, child); + // 不能直接更改背景层2的innerText 所以创建文本节点 + var txt = document.createTextNode('bg2'); + // 插入事件层前(即新插入的背景层2前) + parent.insertBefore(txt, child); + // 向最后插入前景层2(即插入前景层后) + parent.appendChild(input2); + var txt2 = document.createTextNode('fg2'); + parent.appendChild(txt2); + parent.childNodes[2].replaceWith("bg"); + parent.childNodes[6].replaceWith("事件"); + parent.childNodes[8].replaceWith("fg"); + } else { + var input = createCanvasBtn_mobile('bg2'); + var input2 = createCanvasBtn_mobile('fg2'); + // 手机端因为是选项 所以可以直接改innerText + input.innerText = '背景层2'; + input2.innerText = '前景层2'; + var parent = document.getElementById('layerMod'); + parent.insertBefore(input, parent.children[1]); + parent.appendChild(input2); + } + } + + var _loadFloor_doNotCopy = core.maps._loadFloor_doNotCopy; + core.maps._loadFloor_doNotCopy = function () { + return ["bg2map", "fg2map"].concat(_loadFloor_doNotCopy()); + } + ////// 绘制背景和前景层 ////// + core.maps._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) { + config.ctx = cacheCtx; + core.maps._drawBg_drawBackground(floorId, config); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages(floorId, config.ctx, 'bg', null, null, config.onMap); + core.maps._drawBgFgMap(floorId, 'bg', config); + if (config.onMap) { + core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + core.clearMap('bg2'); + core.clearMap(cacheCtx); + } + core.maps._drawBgFgMap(floorId, 'bg2', config); + if (config.onMap) core.drawImage('bg2', cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + config.ctx = toDrawCtx; + } + core.maps._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) { + config.ctx = cacheCtx; + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages(floorId, config.ctx, 'fg', null, null, config.onMap); + core.maps._drawBgFgMap(floorId, 'fg', config); + if (config.onMap) { + core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + core.clearMap('fg2'); + core.clearMap(cacheCtx); + } + core.maps._drawBgFgMap(floorId, 'fg2', config); + if (config.onMap) core.drawImage('fg2', cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + config.ctx = toDrawCtx; + } + ////// 移动判定 ////// + core.maps._generateMovableArray_arrays = function (floorId) { + return { + bgArray: this.getBgMapArray(floorId), + fgArray: this.getFgMapArray(floorId), + eventArray: this.getMapArray(floorId), + bg2Array: this._getBgFgMapArray('bg2', floorId), + fg2Array: this._getBgFgMapArray('fg2', floorId) + }; + } + }, + "itemShop": function () { + // 道具商店相关的插件 + // 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑(如果找不到可以在入口方块中找) + + var shopId = null; // 当前商店ID + var type = 0; // 当前正在选中的类型,0买入1卖出 + var selectItem = 0; // 当前正在选中的道具 + var selectCount = 0; // 当前已经选中的数量 + var page = 0; + var totalPage = 0; + var totalMoney = 0; + var list = []; + var shopInfo = null; // 商店信息 + var choices = []; // 商店选项 + var use = 'money'; + var useText = '金币'; + + var bigFont = core.ui._buildFont(20, false), + middleFont = core.ui._buildFont(18, false); + + this._drawItemShop = function () { + // 绘制道具商店 + + // Step 1: 背景和固定的几个文字 + core.ui._createUIEvent(); + core.clearMap('uievent'); + core.ui.clearUIEventSelector(); + core.setTextAlign('uievent', 'left'); + core.setTextBaseline('uievent', 'top'); + core.fillRect('uievent', 0, 0, 416, 416, 'black'); + core.drawWindowSkin('winskin.png', 'uievent', 0, 0, 416, 56); + core.drawWindowSkin('winskin.png', 'uievent', 0, 56, 312, 56); + core.drawWindowSkin('winskin.png', 'uievent', 0, 112, 312, 304); + core.drawWindowSkin('winskin.png', 'uievent', 312, 56, 104, 56); + core.drawWindowSkin('winskin.png', 'uievent', 312, 112, 104, 304); + core.setFillStyle('uievent', 'white'); + core.setStrokeStyle('uievent', 'white'); + core.fillText("uievent", "购买", 32, 74, 'white', bigFont); + core.fillText("uievent", "卖出", 132, 74); + core.fillText("uievent", "离开", 232, 74); + core.fillText("uievent", "当前" + useText, 324, 66, null, middleFont); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", core.formatBigNumber(core.status.hero[use]), 405, 89); + core.setTextAlign("uievent", "left"); + core.ui.drawUIEventSelector(1, "winskin.png", 22 + 100 * type, 66, 60, 33); + if (selectItem != null) { + core.setTextAlign('uievent', 'center'); + core.fillText("uievent", type == 0 ? "买入个数" : "卖出个数", 364, 320, null, bigFont); + core.fillText("uievent", "< " + selectCount + " >", 364, 350); + core.fillText("uievent", "确定", 364, 380); + } + + // Step 2:获得列表并展示 + list = choices.filter(function (one) { + if (one.condition != null && one.condition != '') { + try { if (!core.calValue(one.condition)) return false; } catch (e) { } + } + return (type == 0 && one.money != null) || (type == 1 && one.sell != null); + }); + var per_page = 6; + totalPage = Math.ceil(list.length / per_page); + page = Math.floor((selectItem || 0) / per_page) + 1; + + // 绘制分页 + if (totalPage > 1) { + var half = 156; + core.setTextAlign('uievent', 'center'); + core.fillText('uievent', page + " / " + totalPage, half, 388, null, middleFont); + if (page > 1) core.fillText('uievent', '上一页', half - 80, 388); + if (page < totalPage) core.fillText('uievent', '下一页', half + 80, 388); + } + core.setTextAlign('uievent', 'left'); + + // 绘制每一项 + var start = (page - 1) * per_page; + for (var i = 0; i < per_page; ++i) { + var curr = start + i; + if (curr >= list.length) break; + var item = list[curr]; + core.drawIcon('uievent', item.id, 10, 125 + i * 40); + core.setTextAlign('uievent', 'left'); + core.fillText('uievent', core.material.items[item.id].name, 50, 132 + i * 40, null, bigFont); + core.setTextAlign('uievent', 'right'); + core.fillText('uievent', (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)) + useText + "/个", 300, 133 + i * 40, null, middleFont); + core.setTextAlign("uievent", "left"); + if (curr == selectItem) { + // 绘制描述,文字自动放缩 + var text = core.material.items[item.id].text || "该道具暂无描述"; + try { text = core.replaceText(text); } catch (e) { } + for (var fontSize = 20; fontSize >= 8; fontSize -= 2) { + var config = { left: 10, fontSize: fontSize, maxWidth: 403 }; + var height = core.getTextContentHeight(text, config); + if (height <= 50) { + config.top = (56 - height) / 2; + core.drawTextContent("uievent", text, config); + break; + } + } + core.ui.drawUIEventSelector(2, "winskin.png", 8, 120 + i * 40, 295, 40); + if (type == 0 && item.number != null) { + core.fillText("uievent", "存货", 324, 132, null, bigFont); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", item.number, 406, 132, null, null, 40); + } else if (type == 1) { + core.fillText("uievent", "数量", 324, 132, null, bigFont); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", core.itemCount(item.id), 406, 132, null, null, 40); + } + core.setTextAlign("uievent", "left"); + core.fillText("uievent", "预计" + useText, 324, 250); + core.setTextAlign("uievent", "right"); + totalMoney = selectCount * (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)); + core.fillText("uievent", core.formatBigNumber(totalMoney), 405, 280); + + core.setTextAlign("uievent", "left"); + core.fillText("uievent", type == 0 ? "已购次数" : "已卖次数", 324, 170); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", (type == 0 ? item.money_count : item.sell_count) || 0, 405, 200); + } + } + + core.setTextAlign('uievent', 'left'); + core.setTextBaseline('uievent', 'alphabetic'); + } + + var _add = function (item, delta) { + if (item == null) return; + selectCount = core.clamp( + selectCount + delta, 0, + Math.min(type == 0 ? Math.floor(core.status.hero[use] / core.calValue(item.money)) : core.itemCount(item.id), + type == 0 && item.number != null ? item.number : Number.MAX_SAFE_INTEGER) + ); + } + + var _confirm = function (item) { + if (item == null || selectCount == 0) return; + if (type == 0) { + core.status.hero[use] -= totalMoney; + core.getItem(item.id, selectCount); + core.stopSound(); + core.playSound('确定'); + if (item.number != null) item.number -= selectCount; + item.money_count = (item.money_count || 0) + selectCount; + } else { + core.status.hero[use] += totalMoney; + core.removeItem(item.id, selectCount); + core.playSound('确定'); + core.drawTip("成功卖出" + selectCount + "个" + core.material.items[item.id].name, item.id); + if (item.number != null) item.number += selectCount; + item.sell_count = (item.sell_count || 0) + selectCount; + } + selectCount = 0; + } + + this._performItemShopKeyBoard = function (keycode) { + var item = list[selectItem] || null; + // 键盘操作 + switch (keycode) { + case 38: // up + if (selectItem == null) break; + if (selectItem == 0) selectItem = null; + else selectItem--; + selectCount = 0; + break; + case 37: // left + if (selectItem == null) { + if (type > 0) type--; + break; + } + _add(item, -1); + break; + case 39: // right + if (selectItem == null) { + if (type < 2) type++; + break; + } + _add(item, 1); + break; + case 40: // down + if (selectItem == null) { + if (list.length > 0) selectItem = 0; + break; + } + if (list.length == 0) break; + selectItem = Math.min(selectItem + 1, list.length - 1); + selectCount = 0; + break; + case 13: + case 32: // Enter/Space + if (selectItem == null) { + if (type == 2) + core.insertAction({ "type": "break" }); + else if (list.length > 0) + selectItem = 0; + break; + } + _confirm(item); + break; + case 27: // ESC + if (selectItem == null) { + core.insertAction({ "type": "break" }); + break; + } + selectItem = null; + break; + } + } + + this._performItemShopClick = function (px, py) { + var item = list[selectItem] || null; + // 鼠标操作 + if (px >= 22 && px <= 82 && py >= 71 && py <= 102) { + // 买 + if (type != 0) { + type = 0; + selectItem = null; + selectCount = 0; + } + return; + } + if (px >= 122 && px <= 182 && py >= 71 && py <= 102) { + // 卖 + if (type != 1) { + type = 1; + selectItem = null; + selectCount = 0; + } + return; + } + if (px >= 222 && px <= 282 && py >= 71 && py <= 102) // 离开 + return core.insertAction({ "type": "break" }); + // < > + if (px >= 318 && px <= 341 && py >= 348 && py <= 376) + return _add(item, -1); + if (px >= 388 && px <= 416 && py >= 348 && py <= 376) + return _add(item, 1); + // 确定 + if (px >= 341 && px <= 387 && py >= 380 && py <= 407) + return _confirm(item); + + // 上一页/下一页 + if (px >= 45 && px <= 105 && py >= 388) { + if (page > 1) { + selectItem -= 6; + selectCount = 0; + } + return; + } + if (px >= 208 && px <= 268 && py >= 388) { + if (page < totalPage) { + selectItem = Math.min(selectItem + 6, list.length - 1); + selectCount = 0; + } + return; + } + + // 实际区域 + if (px >= 9 && px <= 300 && py >= 120 && py < 360) { + if (list.length == 0) return; + var index = parseInt((py - 120) / 40); + var newItem = 6 * (page - 1) + index; + if (newItem >= list.length) newItem = list.length - 1; + if (newItem != selectItem) { + selectItem = newItem; + selectCount = 0; + } + return; + } + } + + this._performItemShopAction = function () { + if (flags.type == 0) return this._performItemShopKeyBoard(flags.keycode); + else return this._performItemShopClick(flags.px, flags.py); + } + + this.openItemShop = function (itemShopId) { + shopId = itemShopId; + type = 0; + page = 0; + selectItem = null; + selectCount = 0; + core.isShopVisited(itemShopId); + shopInfo = flags.__shops__[shopId]; + if (shopInfo.choices == null) shopInfo.choices = core.clone(core.status.shops[shopId].choices); + choices = shopInfo.choices; + use = core.status.shops[shopId].use; + if (use != 'exp') use = 'money'; + useText = use == 'money' ? '金币' : '经验'; + + core.insertAction([{ + "type": "while", + "condition": "true", + "data": [ + { "type": "function", "function": "function () { core.plugin._drawItemShop(); }" }, + { "type": "wait" }, + { "type": "function", "function": "function() { core.plugin._performItemShopAction(); }" } + ] + }, + { + "type": "function", + "function": "function () { core.deleteCanvas('uievent'); core.ui.clearUIEventSelector(); }" + } + ]); + } + + }, + "enemyLevel": function () { + // 此插件将提供怪物手册中的怪物境界显示 + // 使用此插件需要先给每个怪物定义境界,方法如下: + // 点击怪物的【配置表格】,找到“【怪物】相关的表格配置”,然后在【名称】仿照增加境界定义: + /* + "level": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "境界" + }, + */ + // 然后保存刷新,可以看到怪物的属性定义中出现了【境界】。再开启本插件即可。 + + // 是否开启本插件,默认禁用;将此改成 true 将启用本插件。 + var __enable = false; + if (!__enable) return; + + // 这里定义每个境界的显示颜色;可以写'red', '#RRGGBB' 或者[r,g,b,a]四元数组 + var levelToColors = { + "萌新一阶": "red", + "萌新二阶": "#FF0000", + "萌新三阶": [255, 0, 0, 1], + }; + + // 复写 _drawBook_drawName + var originDrawBook = core.ui._drawBook_drawName; + core.ui._drawBook_drawName = function (index, enemy, top, left, width) { + // 如果没有境界,则直接调用原始代码绘制 + if (!enemy.level) return originDrawBook.call(core.ui, index, enemy, top, left, width); + // 存在境界,则额外进行绘制 + core.setTextAlign('ui', 'center'); + if (enemy.specialText.length == 0) { + core.fillText('ui', enemy.name, left + width / 2, + top + 27, '#DDDDDD', this._buildFont(17, true)); + core.fillText('ui', enemy.level, left + width / 2, + top + 51, core.arrayToRGBA(levelToColors[enemy.level] || '#DDDDDD'), this._buildFont(14, true)); + } else { + core.fillText('ui', enemy.name, left + width / 2, + top + 20, '#DDDDDD', this._buildFont(17, true), width); + switch (enemy.specialText.length) { + case 1: + core.fillText('ui', enemy.specialText[0], left + width / 2, + top + 38, core.arrayToRGBA((enemy.specialColor || [])[0] || '#FF6A6A'), + this._buildFont(14, true), width); + break; + case 2: + // Step 1: 计算字体 + var text = enemy.specialText[0] + " " + enemy.specialText[1]; + core.setFontForMaxWidth('ui', text, width, this._buildFont(14, true)); + // Step 2: 计算总宽度 + var totalWidth = core.calWidth('ui', text); + var leftWidth = core.calWidth('ui', enemy.specialText[0]); + var rightWidth = core.calWidth('ui', enemy.specialText[1]); + // Step 3: 绘制 + core.fillText('ui', enemy.specialText[0], left + (width + leftWidth - totalWidth) / 2, + top + 38, core.arrayToRGBA((enemy.specialColor || [])[0] || '#FF6A6A')); + core.fillText('ui', enemy.specialText[1], left + (width + totalWidth - rightWidth) / 2, + top + 38, core.arrayToRGBA((enemy.specialColor || [])[1] || '#FF6A6A')); + break; + default: + core.fillText('ui', '多属性...', left + width / 2, + top + 38, '#FF6A6A', this._buildFont(14, true), width); + } + core.fillText('ui', enemy.level, left + width / 2, + top + 56, core.arrayToRGBA(levelToColors[enemy.level] || '#DDDDDD'), this._buildFont(14, true)); + } + } + + // 也可以复写其他的属性颜色如怪物攻防等,具体参见下面的例子的注释部分 + core.ui._drawBook_drawRow1 = function (index, enemy, top, left, width, position) { + // 绘制第一行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), + f13 = this._buildFont(13, false); + var col1 = left, + col2 = left + width * 9 / 25, + col3 = left + width * 17 / 25; + core.fillText('ui', '生命', col1, position, '#DDDDDD', f13); + core.fillText('ui', core.formatBigNumber(enemy.hp || 0), col1 + 30, position, /*'red' */ null, b13); + core.fillText('ui', '攻击', col2, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.atk || 0), col2 + 30, position, /* '#FF0000' */ null, b13); + core.fillText('ui', '防御', col3, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.def || 0), col3 + 30, position, /* [255, 0, 0, 1] */ null, b13); + } + }, + "multiHeros": function () { + // 多角色插件 + // Step 1: 启用本插件 + // Step 2: 定义每个新的角色各项初始数据(参见下方注释) + // Step 3: 在游戏中的任何地方都可以调用 `core.changeHero()` 进行切换;也可以 `core.changeHero(1)` 来切换到某个具体的角色上 + + // 是否开启本插件,默认禁用;将此改成 true 将启用本插件。 + var __enable = false; + if (!__enable) return; + + // 在这里定义全部的新角色属性 + // 请注意,在这里定义的内容不会多角色共用,在切换时会进行恢复。 + // 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化, + // 多角色共用hp的话则删除hp,等等。总之,不共用的属性都在这里进行定义就好。 + var hero1 = { + "floorId": "MT0", // 该角色初始楼层ID;如果共用楼层可以注释此项 + "image": "brave.png", // 角色的行走图名称;此项必填不然会报错 + "name": "1号角色", + "lv": 1, + "hp": 10000, // 如果HP共用可注释此项 + "atk": 1000, + "def": 1000, + "mdef": 0, + // "money": 0, // 如果要不共用金币则取消此项注释 + // "exp": 0, // 如果要不共用经验则取消此项注释 + "loc": { "x": 0, "y": 0, "direction": "up" }, // 该角色初始位置;如果共用位置可注释此项 + "items": { + "tools": {}, // 如果共用消耗道具(含钥匙)则可注释此项 + // "constants": {}, // 如果不共用永久道具(如手册)可取消注释此项 + "equips": {}, // 如果共用在背包的装备可注释此项 + }, + "equipment": [], // 如果共用装备可注释此项;此项和上面的「共用在背包的装备」需要拥有相同状态,不然可能出现问题 + }; + // 也可以类似新增其他角色 + // 新增的角色,各项属性共用与不共用的选择必须和上面完全相同,否则可能出现问题。 + // var hero2 = { ... + + var heroCount = 2; // 包含默认角色在内总共多少个角色,该值需手动修改。 + + this.initHeros = function () { + core.setFlag("hero1", core.clone(hero1)); // 将属性值存到变量中 + // core.setFlag("hero2", core.clone(hero2)); // 更多的角色也存入变量中;每个定义的角色都需要新增一行 + + // 检测是否存在装备 + if (hero1.equipment) { + if (!hero1.items || !hero1.items.equips) { + alert('多角色插件的equipment和道具中的equips必须拥有相同状态!'); + } + // 存99号套装为全空 + var saveEquips = core.getFlag("saveEquips", []); + saveEquips[99] = []; + core.setFlag("saveEquips", saveEquips); + } else { + if (hero1.items && hero1.items.equips) { + alert('多角色插件的equipment和道具中的equips必须拥有相同状态!'); + } + } + } + + // 在游戏开始注入initHeros + var _startGame_setHard = core.events._startGame_setHard; + core.events._startGame_setHard = function () { + _startGame_setHard.call(core.events); + core.initHeros(); + } + + // 切换角色 + // 可以使用 core.changeHero() 来切换到下一个角色 + // 也可以 core.changeHero(1) 来切换到某个角色(默认角色为0) + this.changeHero = function (toHeroId) { + var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID + if (toHeroId == null) { + toHeroId = (currHeroId + 1) % heroCount; + } + if (currHeroId == toHeroId) return; + + var saveList = Object.keys(hero1); + + // 保存当前内容 + var toSave = {}; + // 暂时干掉 drawTip 和 音效,避免切装时的提示 + var _drawTip = core.ui.drawTip; + core.ui.drawTip = function () { }; + var _playSound = core.control.playSound; + core.control.playSound = function () { } + // 记录当前录像,因为可能存在换装问题 + core.clearRouteFolding(); + var routeLength = core.status.route.length; + // 优先判定装备 + if (hero1.equipment) { + core.items.quickSaveEquip(100 + currHeroId); + core.items.quickLoadEquip(99); + } + + saveList.forEach(function (name) { + if (name == 'floorId') toSave[name] = core.status.floorId; // 楼层单独设置 + else if (name == 'items') { + toSave.items = core.clone(core.status.hero.items); + Object.keys(toSave.items).forEach(function (one) { + if (!hero1.items[one]) delete toSave.items[one]; + }); + } else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象 + }); + + core.setFlag("hero" + currHeroId, toSave); // 将当前角色信息进行保存 + var data = core.getFlag("hero" + toHeroId); // 获得要切换的角色保存内容 + + // 设置角色的属性值 + saveList.forEach(function (name) { + if (name == "floorId"); + else if (name == "items") { + Object.keys(core.status.hero.items).forEach(function (one) { + if (data.items[one]) core.status.hero.items[one] = core.clone(data.items[one]); + }); + } else { + core.status.hero[name] = core.clone(data[name]); + } + }); + // 最后装上装备 + if (hero1.equipment) { + core.items.quickLoadEquip(100 + toHeroId); + } + + core.ui.drawTip = _drawTip; + core.control.playSound = _playSound; + core.status.route = core.status.route.slice(0, routeLength); + core.control._bindRoutePush(); + + // 插入事件:改变角色行走图并进行楼层切换 + var toFloorId = data.floorId || core.status.floorId; + var toLoc = data.loc || core.status.hero.loc; + core.insertAction([ + { "type": "setHeroIcon", "name": data.image || "hero.png" }, // 改变行走图 + // 同层则用changePos,不同层则用changeFloor;这是为了避免共用楼层造成触发eachArrive + toFloorId != core.status.floorId ? { + "type": "changeFloor", + "floorId": toFloorId, + "loc": [toLoc.x, toLoc.y], + "direction": toLoc.direction, + "time": 0 // 可以在这里设置切换时间 + } : { "type": "changePos", "loc": [toLoc.x, toLoc.y], "direction": toLoc.direction } + // 你还可以在这里执行其他事件,比如增加或取消跟随效果 + ]); + core.setFlag("heroId", toHeroId); // 保存切换到的角色ID + } + }, + "heroFourFrames": function () { + + // 样板的勇士/跟随者移动时只使用2、4两帧,观感较差。本插件可以将四帧全用上。 + + // 是否启用本插件 + var __enable = true; + if (!__enable) return; + + ["up", "down", "left", "right"].forEach(function (one) { + // 指定中间帧动画 + core.material.icons.hero[one].midFoot = 2; + }); + + var heroMoving = function (timestamp) { + if (core.status.heroMoving <= 0) return; + if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) { + core.animateFrame.leftLeg++; + core.animateFrame.moveTime = timestamp; + } + core.drawHero(['stop', 'leftFoot', 'midFoot', 'rightFoot'][core.animateFrame.leftLeg % 4], 4 * core.status.heroMoving); + } + core.registerAnimationFrame('heroMoving', true, heroMoving); + + core.events._eventMoveHero_moving = function (step, moveSteps) { + var curr = moveSteps[0]; + var direction = curr[0], + x = core.getHeroLoc('x'), + y = core.getHeroLoc('y'); + // ------ 前进/后退 + var o = direction == 'backward' ? -1 : 1; + if (direction == 'forward' || direction == 'backward') direction = core.getHeroLoc('direction'); + var faceDirection = direction; + if (direction == 'leftup' || direction == 'leftdown') faceDirection = 'left'; + if (direction == 'rightup' || direction == 'rightdown') faceDirection = 'right'; + core.setHeroLoc('direction', direction); + if (curr[1] <= 0) { + core.setHeroLoc('direction', faceDirection); + moveSteps.shift(); + return true; + } + if (step <= 4) core.drawHero('stop', 4 * o * step); + else if (step <= 8) core.drawHero('leftFoot', 4 * o * step); + else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8)); + else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) { + if (step == 8 || step == 16) { + core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true); + core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true); + core.updateFollowers(); + curr[1]--; + if (curr[1] <= 0) moveSteps.shift(); + core.setHeroLoc('direction', faceDirection); + return step == 16; + } + return false; + } + }, + "routeFixing": function () { + // 是否开启本插件,true 表示启用,false 表示禁用。 + var __enable = true; + if (!__enable) return; + /* + 使用说明:启用本插件后,录像回放时您可以用数字键1或6分别切换到原速或24倍速, + 暂停播放时按数字键7(电脑按N)可以单步播放。(手机端可以点击难度单词切换出数字键) + 数字键2-5可以进行录像自助精修,具体描述见下(实际弹窗请求您输入时不要带有任何空格): + + up down left right 勇士向某个方向「行走一步或撞击」 + item:ID 使用某件道具,如 item:bomb 表示使用炸弹 + unEquip:n 卸掉身上第(n+1)件装备(n从0开始),如 unEquip:1 默认表示卸掉盾牌 + equip:ID 穿上某件装备,如 equip:sword1 表示装上铁剑 + saveEquip:n 将身上的当前套装保存到第n套快捷套装(n从0开始) + loadEquip:n 快捷换上之前保存好的第n套套装 + fly:ID 使用楼传飞到某一层,如 fly:MT10 表示飞到主塔10层 + choices:none 确认框/选择项「超时」(作者未设置超时时间则此项视为缺失) + choices:n 确认框/选择项选择第(n+1)项(选择项n从0开始,确认框n为0表示「确定」,1表示「取消」) + 选择项n为负数时表示选择倒数第 -n 项,如 -1 表示最后一项(V2.8.2起标准全局商店的「离开」项) + 此项缺失的话,确认框将选择作者指定的默认项(初始光标位置),选择项将弹窗请求补选(后台录像验证中选最后一项,可以复写函数来修改) + shop:ID 打开某个全局商店,如 shop:itemShop 表示打开道具商店。因此连载塔千万不要中途修改商店ID! + turn 单击勇士(Z键)转身,core.turnHero() 会产生此项,因此通过事件等方式强制让勇士转向应该用 core.setHeroLoc() + turn:dir 勇士转向某个方向,dir 可以为 up down left right(此项一般是读取自动存档产生的,属于样板的不良特性,请勿滥用) + getNext 轻按获得身边道具,优先获得面前的(面前没有则按上下左右顺序依次获得),身边如果没有道具则此项会被跳过 + input:none “等待用户操作事件”中超时(作者未设置超时时间则此项会导致报错) + input:xxx 可能表示“等待用户操作事件”的一个操作(如按键操作将直接记录 input:keycode ), + 也可能表示一个“接受用户输入数字”的输入,后者的情况下 xxx 为输入的整数。此项缺失的话前者将直接报错,后者将用0代替(后者现在支持负数了) + input2:xxx 可能表示“读取全局存储(core.getGlobal)”读取到的值,也可能表示一个“接受用户输入文本”的输入, + 两种情况下 xxx 都为 base64 编码。此项缺失的话前者将重新现场读取,后者将用空字符串代替 + no 走到可穿透的楼梯上不触发楼层切换事件,通过本插件可以让勇士停在旁边没有障碍物的楼梯上哦~ + move:x:y 尝试瞬移到 [x,y] 点(不改变朝向),该点甚至可以和勇士相邻或者位于视野外 + key:n 松开键值为n的键,如 key:49 表示松开大键盘数字键1,默认会触发使用破墙镐 + click:n:px:py 点击自绘状态栏,n为0表示横屏1表示竖屏,[px,py] 为点击的像素坐标 + random:n 生成了随机数n,即 core.rand2(num) 的返回结果,n必须在 [0,num-1] 范围,num必须为正整数。此项缺失将导致现场重新随机生成数值,可能导致回放结果不一致! + 作者自定义的新项(一般为js对象,可以先JSON.stringify()再core.encodeBase64()得到纯英文数字的内容)需要用(半角圆括弧)括起来。 + + 当您使用数字键5将一些项追加到即将播放内容的开头时,请注意要逆序逐项追加,或者每追加一项就按下数字键7或字母键N单步播放一步。 + 但是【input input2 random choices】是被动读取的,单步播放如果触发了相应的事件就会连续读取,这时候只能提前逐项追加好。 + 电脑端熟练以后推荐直接在控制台操作 core.status.route 和 core.status.replay.toReplay(后者录像回放时才有),配合 core.push() 和 core.unshift() 更加灵活自由哦! + */ + core.actions.registerAction('onkeyUp', '_sys_onkeyUp_replay', function (e) { + if (this._checkReplaying()) { + if (e.keyCode == 27) // ESCAPE + core.stopReplay(); + else if (e.keyCode == 90) // Z + core.speedDownReplay(); + else if (e.keyCode == 67) // C + core.speedUpReplay(); + else if (e.keyCode == 32) // SPACE + core.triggerReplay(); + else if (e.keyCode == 65) // A + core.rewindReplay(); + else if (e.keyCode == 83) // S + core.control._replay_SL(); + else if (e.keyCode == 88) // X + core.control._replay_book(); + else if (e.keyCode == 33 || e.keyCode == 34) // PgUp/PgDn + core.control._replay_viewMap(); + else if (e.keyCode == 78) // N + core.stepReplay(); + else if (e.keyCode == 84) // T + core.control._replay_toolbox(); + else if (e.keyCode == 81) // Q + core.control._replay_equipbox(); + else if (e.keyCode == 66) // B + core.ui._drawStatistics(); + else if (e.keyCode == 49 || e.keyCode == 54) // 1/6,原速/24倍速播放 + core.setReplaySpeed(e.keyCode == 49 ? 1 : 24); + else if (e.keyCode > 49 && e.keyCode < 54) { // 2-5,录像精修 + switch (e.keyCode - 48) { + case 2: // pop + alert("您已移除已录制内容的最后一项:" + core.status.route.pop()); + break; + case 3: // push + core.utils.myprompt("请输入您要追加到已录制内容末尾的项:", "", function (value) { + if (value != null) core.status.route.push(value); + }); + break; + case 4: // shift + alert("您已移除即将播放内容的第一项:" + core.status.replay.toReplay.shift()); + break; + case 5: // unshift + core.utils.myprompt("请输入您要追加到即将播放内容开头的项:", "", function (value) { + if (value != null) core.status.replay.toReplay.unshift(value); + }); + } + } + return true; + } + }, 100); + }, + "numpad": function () { + // 样板自带的整数输入事件为白屏弹窗且可以误输入任意非法内容但不支持负整数,观感较差。本插件可以将其美化成仿RM样式,使其支持负整数同时带有音效 + // 另一方面,4399等第三方平台不允许使用包括 core.myprompt() 和 core.myconfirm() 在内的弹窗,因此也需要此插件来替代,不然类似生命魔杖的道具就不好实现了 + // 关于负整数输入,V2.8.2原生支持其录像的压缩和解压,只是默认的 core.events._action_input() 函数将负数取了绝对值,可以只复写下面的 core.isReplaying() 部分来取消 + + // 是否启用本插件,false表示禁用,true表示启用 + var __enable = true; + if (!__enable) return; + + core.events._action_input = function (data, x, y, prefix) { // 复写整数输入事件 + if (core.isReplaying()) { // 录像回放时,处理方式不变,但增加负整数支持 + core.events.__action_getInput(core.replaceText(data.text, prefix), false, function (value) { + value = parseInt(value) || 0; // 去掉了取绝对值的步骤 + core.status.route.push("input:" + value); + core.setFlag("input", value); + core.doAction(); + }); + } else { + // 正常游戏中,采用暂停录制的方式然后用事件流循环“绘制-等待-变量操作”三板斧实现(按照13*13适配的)。 + // 您可以自行修改循环内的内容来适配15*15或其他需求,或干脆作为公共事件编辑。 + core.insertAction([ + // 记录当前录像长度,下面的循环结束后裁剪。达到“暂停录制”的效果 + { "type": "function", "function": "function(){flags['@temp@length']=core.status.route.length}" }, + { "type": "setValue", "name": "flag:input", "value": "0" }, + { + "type": "while", + "condition": "true", + "data": [ + { "type": "drawBackground", "background": "winskin.png", "x": 16, "y": 16, "width": 384, "height": 384 }, + { "type": "drawIcon", "id": "X10181", "x": 32, "y": 288 }, + { "type": "drawIcon", "id": "X10185", "x": 64, "y": 288 }, + { "type": "drawIcon", "id": "X10186", "x": 96, "y": 288 }, + { "type": "drawIcon", "id": "X10187", "x": 128, "y": 288 }, + { "type": "drawIcon", "id": "X10188", "x": 160, "y": 288 }, + { "type": "drawIcon", "id": "X10189", "x": 192, "y": 288 }, + { "type": "drawIcon", "id": "X10193", "x": 224, "y": 288 }, + { "type": "drawIcon", "id": "X10194", "x": 256, "y": 288 }, + { "type": "drawIcon", "id": "X10195", "x": 288, "y": 288 }, + { "type": "drawIcon", "id": "X10196", "x": 320, "y": 288 }, + { "type": "drawIcon", "id": "X10197", "x": 352, "y": 288 }, + { "type": "drawIcon", "id": "X10286", "x": 32, "y": 352 }, + { "type": "drawIcon", "id": "X10169", "x": 96, "y": 352 }, + { "type": "drawIcon", "id": "X10232", "x": 128, "y": 352 }, + { "type": "drawIcon", "id": "X10185", "x": 320, "y": 352 }, + { "type": "drawIcon", "id": "X10242", "x": 352, "y": 352 }, + { "type": "fillBoldText", "x": 48, "y": 256, "style": [255, 255, 255, 1], "font": "bold 32px Consolas", "text": "${flag:input}" }, + { "type": "fillBoldText", "x": 32, "y": 48, "style": [255, 255, 255, 1], "font": "16px Consolas", "text": core.replaceText(data.text, prefix) }, + { + "type": "wait", + "forceChild": true, + "data": [{ + "case": "keyboard", + "keycode": "48,49,50,51,52,53,54,55,56,57", + "action": [ + // 按下数字键,追加到已输入内容的末尾,但禁止越界。变量:keycode-48就是末位数字 + { "type": "playSound", "name": "光标移动" }, + { + "type": "if", + "condition": "(flag:input<0)", + "true": [ + { "type": "setValue", "name": "flag:input", "value": "10*flag:input-(flag:keycode-48)" }, + ], + "false": [ + { "type": "setValue", "name": "flag:input", "value": "10*flag:input+(flag:keycode-48)" }, + ] + }, + { "type": "setValue", "name": "flag:input", "value": "core.clamp(flag:input,-9e15,9e15)" }, + ] + }, + { + "case": "keyboard", + "keycode": "189", + "action": [ + // 按下减号键,变更已输入内容的符号 + { "type": "playSound", "name": "跳跃" }, + { "type": "setValue", "name": "flag:input", "value": "-flag:input" }, + ] + }, + { + "case": "keyboard", + "keycode": "8", + "action": [ + // 按下退格键,从已输入内容的末尾删除一位 + { "type": "playSound", "name": "取消" }, + { "type": "setValue", "name": "flag:input", "operator": "//=", "value": "10" }, + ] + }, + { + "case": "keyboard", + "keycode": "27", + "action": [ + // 按下ESC键,清空已输入内容 + { "type": "playSound", "name": "读档" }, + { "type": "setValue", "name": "flag:input", "value": "0" }, + ] + }, + { + "case": "keyboard", + "keycode": "13", + "action": [ + // 按下回车键,确定 + { "type": "break", "n": 1 }, + ] + }, + { + "case": "mouse", + "px": [32, 63], + "py": [288, 320], + "action": [ + // 点击减号,变号。右边界写63防止和下面重叠 + { "type": "playSound", "name": "跳跃" }, + { "type": "setValue", "name": "flag:input", "value": "-flag:input" }, + ] + }, + { + "case": "mouse", + "px": [64, 384], + "py": [288, 320], + "action": [ + // 点击数字,追加到已输入内容的末尾,但禁止越界。变量:x-2就是末位数字 + { "type": "playSound", "name": "光标移动" }, + { + "type": "if", + "condition": "(flag:input<0)", + "true": [ + { "type": "setValue", "name": "flag:input", "value": "10*flag:input-(flag:x-2)" }, + ], + "false": [ + { "type": "setValue", "name": "flag:input", "value": "10*flag:input+(flag:x-2)" }, + ] + }, + { "type": "setValue", "name": "flag:input", "value": "core.clamp(flag:input,-9e15,9e15)" }, + ] + }, + { + "case": "mouse", + "px": [32, 64], + "py": [352, 384], + "action": [ + // 点击左箭头,退格 + { "type": "playSound", "name": "取消" }, + { "type": "setValue", "name": "flag:input", "operator": "//=", "value": "10" }, + ] + }, + { + "case": "mouse", + "px": [96, 160], + "py": [352, 384], + "action": [ + // 点击CE,清空 + { "type": "playSound", "name": "读档" }, + { "type": "setValue", "name": "flag:input", "value": "0" }, + ] + }, + { + "case": "mouse", + "px": [320, 384], + "py": [352, 384], + "action": [ + // 点击OK,确定 + { "type": "break", "n": 1 }, + ] + } + ] + } + ] + }, + { "type": "clearMap" }, + // 裁剪录像,只保留'input:n',然后继续录制 + { "type": "function", "function": "function(){core.status.route.splice(flags['@temp@length']);core.status.route.push('input:'+core.getFlag('input',0))}" } + ], x, y); + core.events.doAction(); + } + } + }, + "sprites": function () { + // // 基于canvas的sprite化,摘编整理自万宁魔塔 + // // + // // ---------------------------------------- 第一部分 js代码 (必装) --------------------------------------- // + + // /* ---------------- 用法说明 ---------------- * + // * 1. 创建sprite: var sprite = new Sprite(x, y, w, h, z, reference, name); + // * 其中x y w h为画布的横纵坐标及长宽,reference为参考系,只能填game(相对于游戏画面)和window(相对于窗口) + // * 且当为相对游戏画面时,长宽与坐标将会乘以放缩比例(相当于用createCanvas创建) + // * z为纵深,表示不同元素之间的覆盖关系,大的覆盖小的 + // * name为自定义名称,可以不填 + // * 2. 删除: sprite.destroy(); + // * 3. 设置css特效: sprite.setCss(css); + // * 其中css直接填 box-shadow: 0px 0px 10px black;的形式即可,与style标签与css文件内写法相同 + // * 对于已设置的特效,如果之后不需要再次设置,可以不填 + // * 4. 添加事件监听器: sprite.addEventListener(); 用法与html元素的addEventListener完全一致 + // * 5. 移除事件监听器: sprite.removeEventListener(); 用法与html元素的removeEventListener完全一致 + // * 6. 属性列表 + // * (1) sprite.x | sprite.y | sprite.width | sprite.height | sprite.zIndex | sprite.reference 顾名思义 + // * (2) sprite.canvas 该sprite的画布 + // * (3) sprite.context 该画布的CanvasRenderingContext2d对象,即样板中常见的ctx + // * (4) sprite.count 不要改这个玩意 + // * 7. 使用样板api进行绘制 + // * 示例: + // * var ctx = sprite.context; + // * core.fillText(ctx, 'xxx', 100, 100); + // * core.fillRect(ctx, 0, 0, 50, 50); + // * 当然也可以使用原生js + // * ctx.moveTo(0, 0); + // * ctx.bezierCurveTo(50, 50, 100, 0, 100, 50); + // * ctx.stroke(); + // * ---------------- 用法说明 ---------------- */ + + // var count = 0; + + // /** 创建一个sprite画布 + // * @param {number} x + // * @param {number} y + // * @param {number} w + // * @param {number} h + // * @param {number} z + // * @param {'game' | 'window'} reference 参考系,游戏画面或者窗口 + // * @param {string} name 可选,sprite的名称,方便通过core.dymCanvas获取 + // */ + // function Sprite(x, y, w, h, z, reference, name) { + // this.x = x; + // this.y = y; + // this.width = w; + // this.height = h; + // this.zIndex = z; + // this.reference = reference; + // this.canvas = null; + // this.context = null; + // this.count = 0; + // this.name = name || '_sprite_' + count; + // this.style = null; + // /** 初始化 */ + // this.init = function () { + // if (reference === 'window') { + // var canvas = document.createElement('canvas'); + // this.canvas = canvas; + // this.context = canvas.getContext('2d'); + // canvas.width = w; + // canvas.height = h; + // canvas.style.width = w + 'px'; + // canvas.style.height = h + 'px'; + // canvas.style.position = 'absolute'; + // canvas.style.top = y + 'px'; + // canvas.style.left = x + 'px'; + // canvas.style.zIndex = z.toString(); + // document.body.appendChild(canvas); + // this.style = canvas.style; + // } else { + // this.context = core.createCanvas(this.name || '_sprite_' + count, x, y, w, h, z); + // this.canvas = this.context.canvas; + // this.canvas.style.pointerEvents = 'auto'; + // this.style = this.canvas.style; + // } + // this.count = count; + // count++; + // } + // this.init(); + + // /** 设置css特效 + // * @param {string} css + // */ + // this.setCss = function (css) { + // css = css.replace('\n', ';').replace(';;', ';'); + // var effects = css.split(';'); + // var self = this; + // effects.forEach(function (v) { + // var content = v.split(':'); + // var name = content[0]; + // var value = content[1]; + // name = name.trim().split('-').reduce(function (pre, curr, i, a) { + // if (i === 0 && curr !== '') return curr; + // if (a[0] === '' && i === 1) return curr; + // return pre + curr.toUpperCase()[0] + curr.slice(1); + // }, ''); + // var canvas = self.canvas; + // if (name in canvas.style) canvas.style[name] = value; + // }); + // return this; + // } + + // /** + // * 移动sprite + // * @param {boolean} isDelta 是否是相对位置,如果是,那么sprite会相对于原先的位置进行移动 + // */ + // this.move = function (x, y, isDelta) { + // if (x !== undefined && x !== null) this.x = x; + // if (y !== undefined && y !== null) this.y = y; + // if (this.reference === 'window') { + // var ele = this.canvas; + // ele.style.left = x + (isDelta ? parseFloat(ele.style.left) : 0) + 'px'; + // ele.style.top = y + (isDelta ? parseFloat(ele.style.top) : 0) + 'px'; + // } else core.relocateCanvas(this.context, x, y, isDelta); + // return this; + // } + + // /** + // * 重新设置sprite的大小 + // * @param {boolean} styleOnly 是否只修改css效果,如果是,那么将会不高清,如果不是,那么会清空画布 + // */ + // this.resize = function (w, h, styleOnly) { + // if (w !== undefined && w !== null) this.w = w; + // if (h !== undefined && h !== null) this.h = h; + // if (reference === 'window') { + // var ele = this.canvas; + // ele.style.width = w + 'px'; + // ele.style.height = h + 'px'; + // if (!styleOnly) { + // ele.width = w; + // ele.height = h; + // } + // } else core.resizeCanvas(this.context, w, h, styleOnly); + // return this; + // } + + // /** + // * 旋转画布 + // */ + // this.rotate = function (angle, cx, cy) { + // if (this.reference === 'window') { + // var left = this.x; + // var top = this.y; + // this.canvas.style.transformOrigin = (cx - left) + 'px ' + (cy - top) + 'px'; + // if (angle === 0) { + // canvas.style.transform = ''; + // } else { + // canvas.style.transform = 'rotate(' + angle + 'deg)'; + // } + // } else { + // core.rotateCanvas(this.context, angle, cx, cy); + // } + // return this; + // } + + // /** + // * 清除sprite + // */ + // this.clear = function (x, y, w, h) { + // if (this.reference === 'window') { + // this.context.clearRect(x, y, w, h); + // } else { + // core.clearMap(this.context, x, y, w, h); + // } + // return this; + // } + + // /** 删除 */ + // this.destroy = function () { + // if (this.reference === 'window') { + // if (this.canvas) document.body.removeChild(this.canvas); + // } else { + // core.deleteCanvas(this.name || '_sprite_' + this.count); + // } + // } + + // /** 添加事件监听器 */ + // this.addEventListener = function () { + // this.canvas.addEventListener.apply(this.canvas, arguments); + // } + + // /** 移除事件监听器 */ + // this.removeEventListener = function () { + // this.canvas.removeEventListener.apply(this.canvas, arguments); + // } + // } + + // window.Sprite = Sprite; + }, + "hotReload": function () { + + /* ---------- 功能说明 ---------- * + + 1. 当 libs/ main.js index.html 中的任意一个文件被更改后,会自动刷新塔的页面 + 2. 修改楼层文件后自动在塔的页面上显示出来,不需要刷新 + 3. 修改脚本编辑或插件编写后也能自动更新更改的插件或脚本,但不保证不会出问题(一般都不会有问题的 + 4. 修改图块属性、怪物属性等后会自动更新 + 5. 当全塔属性被修改时,会自动刷新塔的页面 + 6. 样板的 styles.css 被修改后也可以直接显示,不需要刷新 + 7. 其余内容修改后不会自动更新也不会刷新 + + /* ---------- 使用方式 ---------- * + + 1. 前往 https://nodejs.org/en/ 下载node.js的LTS版本(点左边那个绿色按钮)并安装 + 2. 将该插件复制到插件编写中 + 3. 在造塔群的群文件-魔塔样板·改中找到server.js,下载并放到塔的根目录(与启动服务同一级) + 4. 在该目录下按下shift+鼠标右键(win11只按右键即可),选择在终端打开或在powershell打开 + 5. 运行node server.js即可 + + */ + + if (main.mode !== 'play' || main.replayChecking) return; + + /** + * 发送请求 + * @param {string} url + * @param {string} type + * @param {string} data + * @returns {Promise} + */ + // async function post(url, type, data) { + // const xhr = new XMLHttpRequest(); + // xhr.open(type, url); + // xhr.send(data); + // const res = await new Promise(res => { + // xhr.onload = e => { + // if (xhr.status !== 200) { + // console.error(`hot reload: http ${xhr.status}`); + // res('@error'); + // } else res('success'); + // }; + // xhr.onerror = e => { + // res('@error'); + // console.error(`hot reload: error on connection`); + // }; + // }); + // if (res === 'success') return xhr.response; + // else return '@error'; + // } + + /** + * 热重载css + * @param {string} data + */ + // function reloadCss(data) { + // const all = Array.from(document.getElementsByTagName('link')); + // all.forEach(v => { + // if (v.rel !== 'stylesheet') return; + // if (v.href === `http://127.0.0.1:3000/${data}`) { + // v.remove(); + // const link = document.createElement('link'); + // link.rel = 'stylesheet'; + // link.type = 'text/css'; + // link.href = data; + // document.head.appendChild(link); + // console.log(`css hot reload: ${data}`); + // } + // }); + // } + + /** + * 热重载楼层 + * @param {string} data + */ + // async function reloadFloor(data) { + // // 首先重新加载main.floors对应的楼层 + // await import(`/project/floors/${data}.js?v=${Date.now()}`); + // // 然后写入core.floors并解析 + // core.floors[data] = main.floors[data]; + // const floor = core.loadFloor(data); + // if (core.isPlaying()) { + // core.status.maps[data] = floor; + // delete core.status.mapBlockObjs[data]; + // core.extractBlocks(data); + // if (data === core.status.floorId) { + // core.drawMap(data); + // core.setWeather( + // core.animateFrame.weather.type, + // core.animateFrame.weather.level + // ); + // } + // core.updateStatusBar(true, true); + // } + // console.log(`floor hot reload: ${data}`); + // } + + /** + * 热重载脚本编辑及插件编写 + * @param {string} data + */ + // async function reloadScript(data) { + // if (data === 'plugins') { + // // 插件编写比较好办 + // const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; + // // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 + // const script = document.createElement('script'); + // script.src = `/project/plugins.js?v=${Date.now()}`; + // document.body.appendChild(script); + // await new Promise(res => { + // script.onload = () => res('success'); + // }); + // const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; + // // 找到差异的函数 + // for (const id in before) { + // const fn = before[id]; + // if (typeof fn !== 'function') continue; + // if (fn.toString() !== after[id]?.toString()) { + // try { + // core.plugin[id] = after[id]; + // core.plugin[id].call(core.plugin); + // core.updateStatusBar(true, true); + // console.log(`plugin hot reload: ${id}`); + // } catch (e) { + // console.error(e); + // } + // } + // } + // } else if (data === 'functions') { + // // 脚本编辑略微麻烦点 + // const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; + // // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 + // const script = document.createElement('script'); + // script.src = `/project/functions.js?v=${Date.now()}`; + // document.body.appendChild(script); + // await new Promise(res => { + // script.onload = () => res('success'); + // }); + // const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; + // // 找到差异的函数 + // for (const mod in before) { + // const fns = before[mod]; + // for (const id in fns) { + // const fn = fns[id]; + // if (typeof fn !== 'function' || id === 'hasSpecial') + // continue; + // const now = after[mod][id]; + // if (fn.toString() !== now.toString()) { + // try { + // if (mod === 'events') { + // core.events.eventdata[id] = now; + // } else if (mod === 'enemys') { + // core.enemys.enemydata[id] = now; + // } else if (mod === 'actions') { + // core.actions.actionsdata[id] = now; + // } else if (mod === 'control') { + // core.control.controldata[id] = now; + // } else if (mod === 'ui') { + // core.ui.uidata[id] = now; + // } + // core.updateStatusBar(true, true); + // console.log( + // `function hot reload: ${mod}.${id}` + // ); + // } catch (e) { + // console.error(e); + // } + // } + // } + // } + // } + // } + + /** + * 属性热重载,包括全塔属性等 + * @param {string} data + */ + // async function reloadData(data) { + // const script = document.createElement('script'); + // script.src = `/project/${data}.js?v=${Date.now()}`; + // document.body.appendChild(script); + // await new Promise(res => { + // script.onload = () => res('success'); + // }); + + // let after; + // if (data === 'data') + // after = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + // if (data === 'enemys') + // after = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; + // if (data === 'icons') + // after = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; + // if (data === 'items') + // after = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a; + // if (data === 'maps') + // after = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; + // if (data === 'events') + // after = events_c12a15a8_c380_4b28_8144_256cba95f760; + + // if (data === 'enemys') { + // core.enemys.enemys = after; + // for (var enemyId in after) { + // core.enemys.enemys[enemyId].id = enemyId; + // } + // core.material.enemys = core.getEnemys(); + // } else if (data === 'icons') { + // core.icons.icons = after; + // core.material.icons = core.getIcons(); + // } else if (data === 'items') { + // core.items.items = after; + // for (var itemId in after) { + // core.items.items[itemId].id = itemId; + // } + // core.material.items = core.getItems(); + // } else if (data === 'maps') { + // core.maps.blocksInfo = after; + // core.status.mapBlockObjs = {}; + // core.status.number2block = {}; + // Object.values(core.status.maps).forEach(v => delete v.blocks); + // core.extractBlocks(); + // core.setWeather( + // core.animateFrame.weather.type, + // core.animateFrame.weather.level + // ); + // core.drawMap(); + // } else if (data === 'events') { + // core.events.commonEvent = after.commonEvent; + // } else if (data === 'data') { + // location.reload(); + // } + // core.updateStatusBar(true, true); + // console.log(`data hot reload: ${data}`); + // } + + // 初始化 + (async function () { + // const data = await post('/reload', 'POST', 'test'); + // if (data === '@error') { + // console.log(`未检测到node服务,热重载插件将无法使用`); + // } else { + // console.log(`热重载插件加载成功`); + // // reload + // setInterval(async () => { + // const res = await post('/reload', 'POST'); + // if (res === '@error') return; + // if (res === 'true') location.reload(); + // else return; + // }, 1000); + + // // hot reload + // setInterval(async () => { + // const res = await post('/hotReload', 'POST'); + // const data = res.split('@@'); + // data.forEach(v => { + // if (v === '') return; + // const [type, file] = v.split(':'); + // if (type === 'css') reloadCss(file); + // if (type === 'data') reloadData(file); + // if (type === 'floor') reloadFloor(file); + // if (type === 'script') reloadScript(file); + // }); + // }, 1000); + // } + })(); + }, + "高级动画": function () { + + // -------------------- 插件说明 -------------------- // + + // github仓库:https://github.com/unanmed/animate + // npm包名:mutate-animate + // npm地址:https://www.npmjs.com/package/mutate-animate + + // 不要去尝试读这个插件,这个插件是经过了打包的,不是人类可读的( + // 想读的话可以去github读 + + // 该插件是一个轻量型多功能动画插件,可以允许你使用内置或自定义的速率曲线或轨迹等 + // 除此之外,你还可以自定义绘制函数,来让你的动画可视化 + + // -------------------- 安装说明 -------------------- // + + // 直接复制到插件中即可,注意所有插件中不能出现插件名为animate的插件 + // 该插件分为动画和渐变两部分,教程分开,动画在前,渐变在后 + + // -------------------- 动画使用教程 -------------------- // + + // 1. 首先创建一个异步函数 + // async function ani() { } + + // 2. 引入插件中的类和函数,引入内容要看个人需求,所有可用的函数在本插件末尾可以看到 + // const { Animation, linear, bezier, circle, hyper, trigo, power, inverseTrigo, shake, sleep } = core.plugin.animate + + // 3. 在函数内部创建一个动画 + // const animate = new Animation(); + + // 4. 为动画创建一个绘制函数,这里以绘制一个矩形为例,当然也可以使用core.fillRect替代ctx.fillRect来绘制矩形 + // const ctx = core.createCanvas('animate', 0, 0, 416, 416, 100); + // ctx.save(); + // const fn = () => { + // ctx.restore(); + // ctx.save(); + // ctx.clearRect(0, 0, 800, 800); + // ctx.translate(animate.x, animate.y); + // ctx.rotate(animate.angle * Math.PI / 180); + // const size = animate.size; + // ctx.fillRect(-30 * size, -30 * size, 60 * size, 60 * size); + // } + // animate.ticker.add(fn); + + // 5. 执行动画 + + // 下面先对一些概念进行解释 + + // 动画分为很多种,内置的有move(移动至某一点) rotate(旋转) scale(放缩) moveAs(以指定路径移动) shake(震动) + // 对于不同的动画种类,其所对应的属性也不同,move moveAs shake均对应x和y这两个属性 + // rotate对应angle,scale对应size。你也可以自定义属性,这个之后会提到 + + // 除了执行动画之外,这里还提供了三个等待函数,可以等待某个动画执行完毕,以及一个等待指定时长的函数 + // 分别是animate.n(等待指定数量的动画执行完毕) + // animate.w(等待指定类型的动画执行完毕,也可以是自定义类型) + // animate.all(等待所有动画执行完毕) + // sleep(等待指定时长) + + // 执行动画时,要求一个渐变函数,当然这个插件内置了非常丰富的渐变函数,也就是速率曲线。 + + // 线性渐变函数 linear(),该函数返回一个线性变化函数 + + // 三角渐变函数 trigo('sin' | 'sec', EaseMode),该函数返回一个指定属性的三角函数变化函数 + // 其中EaseMode可以填'in' 'out' 'in-out' 'center' + // 分别表示 慢-快 快-慢 慢-快-慢 快-慢-快 + + // 幂函数渐变 power(n, EaseMode),该函数返回一个以x^n变化的函数,n是指数 + + // 双曲渐变函数 hyper('sin' | 'tan' | 'sec', EaseMode),该函数返回一个双曲函数,分别是双曲正弦、双曲正切、双曲正割 + + // 反三角渐变函数 inverseTrigo('sin' | 'tan', EaseMode),该函数返回一个反三角函数 + + // 贝塞尔曲线渐变函数 bezier(...cps),参数为贝塞尔曲线的控制点纵坐标(横坐标不能自定义,毕竟一个时刻不能对应多个速率) + // 示例:bezier(0.4, 0.2, 0.7); // 三个控制点的四次贝塞尔曲线渐变函数 + + // 了解完渐变函数以后,这里还有一个特殊的渐变函数-shake + // shake(power, timing),这个函数是一个震荡函数,会让一个值来回变化,实现震动的效果 + // 其中power是震动的最大值,timing是渐变函数,描述了power在震动时大小的变化 + + // 下面,我们就可以进行动画的执行了,我们以 运动 + 旋转 + 放缩为例 + + // animate.mode(hyper('sin', 'out')) // 设置渐变函数为 双曲正弦 快 -> 慢,注意不能加分号 + // .time(1000) // 设置动画的执行时间为1000毫秒 + // .move(300, 300) // 移动至[300, 300]的位置 + // .relative() // 设置相对模式为相对之前,与之前为相加的关系 + // .mode(power(3, 'center')) // 设置为 x^3 快-慢-快 的渐变函数 + // .time(3000) + // .rotate(720) // 旋转720度 + // .absolute() // 设置相对模式为绝对 + // .mode(trigo('sin', 'in')) // 设置渐变函数为 正弦 慢 -> 快 + // .time(1500) + // .scale(3); // 放缩大小至3倍 + + // 这样,我们就把三种基础动画都执行了一遍,同时,这种写法非常直观,出现问题时也可以很快地找到问题所在 + // 下面,我们需要等待动画执行完毕,因为同一种动画不可能同时执行两个 + + // await animate.n(1); // 等待任意一个动画执行完毕,别把await忘了 + // await animate.w('scale'); // 等待放缩动画执行完毕 + // await animate.all(); // 等待所有动画执行完毕 + // await sleep(1000); // 等待1000毫秒 + + // 下面,还有一个特殊的动画函数-moveAs + // 这是一个非常强大的函数,它允许你让你的物体按照指定路线运动 + // 说到这,我们需要先了解一下运动函数。 + // 该插件内置了两个运动函数,分别是圆形运动和贝塞尔曲线运动 + + // 圆形运动 circle(r, n, timing, inverse),r是圆的半径,n是圈数,timing描述半径大小的变化,inverse说明了是否翻转timing函数,后面三个可以不填 + + // 贝塞尔曲线 bezierPath(start, end, ...cps) + // 其中start和end是起点和结束点,应当填入[x, y]数组,cps是控制点,也是[x, y]数组 + // 示例:bezierPath([0, 0], [200, 200], [100, 50], [300, 150], [200, 180]); + // 这是一个起点为 [0, 0],终点为[200, 200],有三个控制点的四次贝塞尔曲线 + + // 下面,我们就可以使用路径函数了 + + // animate.mode(hyper('sin', 'in-out')) // 设置渐变曲线 + // .time(5000) + // .relative() // 设置为相对模式,这个比较必要,不然的话很可能出现瞬移 + // .moveAs(circle(100, 5, linear())) // 创建一个5圈的半径从0至100逐渐变大的圆轨迹(是个螺旋线)并让物体沿着它运动 + // + // 最后,还有一个震动函数 shake(x, y),x和y表示了在横向上和纵向上的震动幅度,1表示为震动幅度的100% + // 示例: + // animate.mode(shake(5, hyper('sin', 'in')), true) // 这里第二个参数说明是震动函数 + // .time(2000) + // .shake(1, 0.5) + + // 这样,所有内置动画就已经介绍完毕 + + // 6. 自定义动画属性 + + // 本插件允许你自定义一个动画属性,但功能可能不会像自带的属性那么强大 + // 你可以在创建动画之后使用animate.register(key, init)来注册一个自定义属性 + // 其中key是自定义属性的名称,init是自定义属性的初始值,这个值应当在0-1之间变化 + + // 你可以通过animate.value[key]来获取你注册的自定义属性 + + // 对于自定义属性的动画,你应当使用animate.apply(key, n, first) + // 其中,key是你的自定义属性的名称,n是其目标值,first是一个布尔值,说明了是否将该动画插入到目前所有的动画之前,即每帧会优先执行该动画 + + // 下面是一个不透明度的示例 + + // animate.register('opacity', 1); // 这句话应该放到刚创建动画之后 + + // ctx.globalAlpha = animate.value.opacity; // 这句话应当放到每帧绘制的函数里面,放在绘制之前 + + // animate.mode(bezier(0.9, 0.1, 0.05)) // 设置渐变函数 + // .time(2000) + // .absolute() + // .apply('opacity', 0.3); // 将不透明度按照渐变曲线更改为0.3 + + // 7. 运行动画 + + // 还记得刚开始定义的async function 吗,直接调用它就能执行动画了! + // 示例:ani(); // 执行刚刚写的所有动画 + + // 8. 自定义速率曲线和路径 + + // 该插件中,速率曲线和路径均可自定义 + + // 对于速率曲线,其类型为 (input: number) => number + // 它接受一个范围在 0-1 的值,输出一个 0-1 的值,表示了动画的完成度,1表示动画已完成,0表示动画刚开始(当前大于1小于0也不会报错,也会执行相应的动画) + + // 对于路径,其类型为 (input: number) => [number, number] + // 它与速率曲线类似,接收一个 0-1 的值,输出一个坐标数组 + + // 9. 多个属性绑定 + + // 该插件中,你可以绑定多个动画属性,你可以使用ani.bind(...attr)来绑定。 + // 绑定之后,这三个动画属性可以被一个返回了长度为3的数组的渐变函数执行。 + // 绑定使用ani.bind,设置渐变函数仍然使用ani.mode,注意它与单个动画属性是分开的,也就是它不会影响正常的渐变函数。 + // 然后使用ani.applyMulti即可执行动画 + + // 例如: + // // 自定义的一个三属性渐变函数 + // function b(input) { + // return [input * 100, input ** 2 * 100, input ** 3 * 100]; + // } + // ani.bind('a', 'b', 'c') // 这样会绑定abc这三个动画属性 + // .mode(b) // 自定义的一个返回了长度为3的数组的函数 + // .time(5000) + // .absolute() + // .applyMulti(); // 执行这个动画 + + // 9. 监听 动画的生命周期钩子 + + // 这个插件还允许你去监听动画的状态,可以监听动画的开始、结束、运行 + // 你可以使用 animate.listen(type, fn)来监听,fn的类型是 (a: Animation, type: string) => void + // 当然,一般情况下你不会用到这个功能,插件中已经帮你包装了三个等待函数,他们就是以这些监听为基础的 + + // 10. 自定义时间获取函数 + + // 你可以修改ani.getTime来修改动画的时间获取函数,例如想让动画速度变成一半可以写ani.getTime = () => Date.now() / 2 + // 这样可以允许你随意控制动画的运行速度,暂停,甚至是倒退。该值默认为`Date.now` + + // -------------------- 渐变使用教程 -------------------- // + + // 相比于动画,渐变属于一种较为简便的动画,它可以让你在设置一个属性后使属性缓慢变化值目标值而不是突变至目标值 + // 现在假设你已经了解了动画的使用,下面我们来了解渐变。 + + // 1. 创建一个渐变实例 + // 与动画类似,你需要使用new来实例化一个渐变,当然别忘了引入 + // const { Transition } = core.plugin.animate; + // const tran = new Transition(); + + // 2. 绘制 + // const ctx = core.createCanvas('transition', 0, 0, 416, 416, 100); + // ctx.save(); + // const fn = () => { + // ctx.restore(); + // ctx.save(); + // ctx.clearRect(0, 0, 800, 800); + // ctx.beginPath(); + // ctx.arc(tran.value.x, tran.value.y, 50, 0, Math.PI * 2); // 使用tran.value.xxx获取当前的属性 + // ctx.fill(); + // // 当然也可以用样板的api,例如core.fillCircle();等 + // } + // animate.ticker.add(fn); + + // 3. 设置渐变 + // 同样,与动画类似,你可以使用tran.time()设置渐变时间,使用tran.mode()设置渐变函数,使用tran.absolute()和tran.relative()设置相对模式 + // 例如: + // tran.time(1000) + // .mode(hyper('sin', 'out')) + // .absolute(); + + // 4. 初始化渐变属性 + // 与动画不同的是,动画在执行一个自定义属性前都需要register,而渐变不需要。 + // 你可以通过tran.value.xxx = yyy来设置动画属性或使用tran.transition('xxx', yyy)来设置 + // 你的首次赋值即是初始化了渐变属性,这时是不会执行渐变的,例如: + // tran.value.x = 200; + // tran.transition('y', 200); + // 上述例子便是将 x 和 y 初始化成了200 + + // 5. 执行渐变 + // 初始化完成后,便可以直接执行渐变了,有两种方法 + // tran.value.x = 400; // 将 x 缓慢移动至400 + // tran.transition('y', 400); // 将 y 缓慢移动至400 + + // 6. 自定义时间获取函数 + // 与动画类似,你依然可以通过修改tran.getTime来修改时间获取函数 + + if (main.replayChecking) return core.plugin.animate = {}; + + var M = Object.defineProperty; + var E = (n, s, t) => s in n ? M(n, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[s] = t; + var o = (n, s, t) => (E(n, typeof s != "symbol" ? s + "" : s, t), t); + let b = []; + const k = (n) => { + for (const s of b) + if (s.status === "running") + try { + for (const t of s.funcs) + t(n - s.startTime); + } catch (t) { + s.destroy(), console.error(t); + } + requestAnimationFrame(k); + }; + requestAnimationFrame(k); + class I { + constructor() { + o(this, "funcs", []); + o(this, "status", "stop"); + o(this, "startTime", 0); + this.status = "running", b.push(this), requestAnimationFrame((s) => this.startTime = s); + } + add(s, t = !1) { + return t ? this.funcs.unshift(s) : this.funcs.push(s), this; + } + remove(s) { + const t = this.funcs.findIndex((e) => e === s); + if (t === -1) + /*throw new ReferenceError( + "You are going to remove nonexistent ticker function." + );*/ + return this.funcs.splice(t, 1), this; + } + clear() { + this.funcs = []; + } + destroy() { + this.clear(), this.stop(); + } + stop() { + this.status = "stop", b = b.filter((s) => s !== this); + } + } + class F { + constructor() { + o(this, "timing"); + o(this, "relation", "absolute"); + o(this, "easeTime", 0); + o(this, "applying", {}); + o(this, "getTime", Date.now); + o(this, "ticker", new I()); + o(this, "value", {}); + o(this, "listener", {}); + this.timing = (s) => s; + } + async all() { + if (Object.values(this.applying).every((s) => s === !0)) + throw new ReferenceError("There is no animates to be waited."); + await new Promise((s) => { + const t = () => { + Object.values(this.applying).every((e) => e === !1) && (this.unlisten("end", t), s("all animated.")); + }; + this.listen("end", t); + }); + } + async n(s) { + const t = Object.values(this.applying).filter((i) => i === !0).length; + if (t < s) + throw new ReferenceError( + `You are trying to wait ${s} animate, but there are only ${t} animate animating.` + ); + let e = 0; + await new Promise((i) => { + const r = () => { + e++, e === s && (this.unlisten("end", r), i(`${s} animated.`)); + }; + this.listen("end", r); + }); + } + async w(s) { + if (this.applying[s] === !1) + throw new ReferenceError(`The ${s} animate is not animating.`); + await new Promise((t) => { + const e = () => { + this.applying[s] === !1 && (this.unlisten("end", e), t(`${s} animated.`)); + }; + this.listen("end", e); + }); + } + listen(s, t) { + var e, i; + (i = (e = this.listener)[s]) != null || (e[s] = []), this.listener[s].push(t); + } + unlisten(s, t) { + const e = this.listener[s].findIndex((i) => i === t); + if (e === -1) + throw new ReferenceError( + "You are trying to remove a nonexistent listener." + ); + this.listener[s].splice(e, 1); + } + hook(...s) { + const t = Object.entries(this.listener).filter( + (e) => s.includes(e[0]) + ); + for (const [e, i] of t) + for (const r of i) + r(this, e); + } + } + + function T(n) { + return n != null; + } + async function R(n) { + return new Promise((s) => setTimeout(s, n)); + } + class Y extends F { + constructor() { + super(); + o(this, "shakeTiming"); + o(this, "path"); + o(this, "multiTiming"); + o(this, "value", {}); + o(this, "size", 1); + o(this, "angle", 0); + o(this, "targetValue", { + system: { + move: [0, 0], + moveAs: [0, 0], + resize: 0, + rotate: 0, + shake: 0, + "@@bind": [] + }, + custom: {} + }); + o(this, "animateFn", { + system: { + move: [() => 0, () => 0], + moveAs: () => 0, + resize: () => 0, + rotate: () => 0, + shake: () => 0, + "@@bind": () => 0 + }, + custom: {} + }); + o(this, "ox", 0); + o(this, "oy", 0); + o(this, "sx", 0); + o(this, "sy", 0); + o(this, "bindInfo", []); + this.timing = (t) => t, this.shakeTiming = (t) => t, this.multiTiming = (t) => [t, t], this.path = (t) => [t, t], this.applying = { + move: !1, + scale: !1, + rotate: !1, + shake: !1 + }, this.ticker.add(() => { + const { running: t } = this.listener; + if (T(t)) + for (const e of t) + e(this, "running"); + }); + } + get x() { + return this.ox + this.sx; + } + get y() { + return this.oy + this.sy; + } + mode(t, e = !1) { + return typeof t(0) == "number" ? e ? this.shakeTiming = t : this.timing = t : this.multiTiming = t, this; + } + time(t) { + return this.easeTime = t, this; + } + relative() { + return this.relation = "relative", this; + } + absolute() { + return this.relation = "absolute", this; + } + bind(...t) { + return this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), this.bindInfo = t, this; + } + unbind() { + return this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), this.bindInfo = [], this; + } + move(t, e) { + return this.applying.move && this.end(!0, "move"), this.applySys("ox", t, "move"), this.applySys("oy", e, "move"), this; + } + rotate(t) { + return this.applySys("angle", t, "rotate"), this; + } + scale(t) { + return this.applySys("size", t, "resize"), this; + } + shake(t, e) { + this.applying.shake === !0 && this.end(!0, "shake"), this.applying.shake = !0; + const { easeTime: i, shakeTiming: r } = this, h = this.getTime(); + if (this.hook("start", "shakestart"), i <= 0) + return this.end(!1, "shake"), this; + const l = () => { + const c = this.getTime() - h; + if (c > i) { + this.ticker.remove(l), this.applying.shake = !1, this.sx = 0, this.sy = 0, this.hook("end", "shakeend"); + return; + } + const a = c / i, + m = r(a); + this.sx = m * t, this.sy = m * e; + }; + return this.ticker.add(l), this.animateFn.system.shake = l, this; + } + moveAs(t) { + this.applying.moveAs && this.end(!0, "moveAs"), this.applying.moveAs = !0, this.path = t; + const { easeTime: e, relation: i, timing: r } = this, h = this.getTime(), [l, u] = [this.x, this.y], [c, a] = (() => { + if (i === "absolute") + return t(1); { + const [d, f] = t(1); + return [l + d, u + f]; + } + })(); + if (this.hook("start", "movestart"), e <= 0) + return this.end(!1, "moveAs"), this; + const m = () => { + const f = this.getTime() - h; + if (f > e) { + this.end(!0, "moveAs"); + return; + } + const v = f / e, + [g, w] = t(r(v)); + i === "absolute" ? (this.ox = g, this.oy = w) : (this.ox = l + g, this.oy = u + w); + }; + return this.ticker.add(m, !0), this.animateFn.system.moveAs = m, this.targetValue.system.moveAs = [c, a], this; + } + register(t, e) { + if (typeof this.value[t] == "number") + return this.error( + `Property ${t} has been regietered twice.`, + "reregister" + ); + this.value[t] = e, this.applying[t] = !1; + } + apply(t, e, i = !1) { + this.applying[t] === !0 && this.end(!1, t), t in this.value || this.error( + `You are trying to execute nonexistent property ${t}.` + ), this.applying[t] = !0; + const r = this.value[t], + h = this.getTime(), + { timing: l, relation: u, easeTime: c } = this, + a = u === "absolute" ? e - r : e; + if (this.hook("start"), c <= 0) + return this.end(!1, t), this; + const m = () => { + const f = this.getTime() - h; + if (f > c) { + this.end(!1, t); + return; + } + const v = f / c, + g = l(v); + this.value[t] = r + g * a; + }; + return this.ticker.add(m, i), this.animateFn.custom[t] = m, this.targetValue.custom[t] = a + r, this; + } + applyMulti(t = !1) { + this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), this.applying["@@bind"] = !0; + const e = this.bindInfo, + i = e.map((m) => this.value[m]), + r = this.getTime(), + { multiTiming: h, relation: l, easeTime: u } = this, + c = h(1); + if (c.length !== i.length) + throw new TypeError( + `The number of binded animate attributes and timing function returns's length does not match. binded: ${e.length}, timing: ${c.length}` + ); + if (this.hook("start"), u <= 0) + return this.end(!1, "@@bind"), this; + const a = () => { + const d = this.getTime() - r; + if (d > u) { + this.end(!1, "@@bind"); + return; + } + const f = d / u, + v = h(f); + e.forEach((g, w) => { + l === "absolute" ? this.value[g] = v[w] : this.value[g] = i[w] + v[w]; + }); + }; + return this.ticker.add(a, t), this.animateFn.custom["@@bind"] = a, this.targetValue.system["@@bind"] = c, this; + } + applySys(t, e, i) { + i !== "move" && this.applying[i] === !0 && this.end(!0, i), this.applying[i] = !0; + const r = this[t], + h = this.getTime(), + l = this.timing, + u = this.relation, + c = this.easeTime, + a = u === "absolute" ? e - r : e; + if (this.hook("start", `${i}start`), c <= 0) + return this.end(!1, i); + const m = () => { + const f = this.getTime() - h; + if (f > c) { + this.end(!0, i); + return; + } + const v = f / c, + g = l(v); + this[t] = r + a * g, t !== "oy" && this.hook(i); + }; + this.ticker.add(m, !0), t === "ox" ? this.animateFn.system.move[0] = m : t === "oy" ? this.animateFn.system.move[1] = m : this.animateFn.system[i] = m, i === "move" ? (t === "ox" && (this.targetValue.system.move[0] = a + r), t === "oy" && (this.targetValue.system.move[1] = a + r)) : i !== "shake" && (this.targetValue.system[i] = a + r); + } + error(t, e) { + throw e === "repeat" ? new Error( + `Cannot execute the same animation twice. Info: ${t}` + ) : e === "reregister" ? new Error( + `Cannot register an animated property twice. Info: ${t}` + ) : new Error(t); + } + end(t, e) { + if (t === !0) + if (this.applying[e] = !1, e === "move" ? (this.ticker.remove(this.animateFn.system.move[0]), this.ticker.remove(this.animateFn.system.move[1])) : e === "moveAs" ? this.ticker.remove(this.animateFn.system.moveAs) : e === "@@bind" ? this.ticker.remove(this.animateFn.system["@@bind"]) : this.ticker.remove( + this.animateFn.system[e] + ), e === "move") { + const [i, r] = this.targetValue.system.move; + this.ox = i, this.oy = r, this.hook("moveend", "end"); + } else if (e === "moveAs") { + const [i, r] = this.targetValue.system.moveAs; + this.ox = i, this.oy = r, this.hook("moveend", "end"); + } else + e === "rotate" ? (this.angle = this.targetValue.system.rotate, this.hook("rotateend", "end")) : e === "resize" ? (this.size = this.targetValue.system.resize, this.hook("resizeend", "end")) : e === "@@bind" ? this.bindInfo.forEach((r, h) => { + this.value[r] = this.targetValue.system["@@bind"][h]; + }) : (this.sx = 0, this.sy = 0, this.hook("shakeend", "end")); + else + this.applying[e] = !1, this.ticker.remove(this.animateFn.custom[e]), this.value[e] = this.targetValue.custom[e], this.hook("end"); + } + } + class j extends F { + constructor() { + super(); + o(this, "now", {}); + o(this, "target", {}); + o(this, "transitionFn", {}); + o(this, "value"); + o(this, "handleSet", (t, e, i) => (this.transition(e, i), !0)); + o(this, "handleGet", (t, e) => this.now[e]); + this.timing = (t) => t, this.value = new Proxy(this.target, { + set: this.handleSet, + get: this.handleGet + }); + } + mode(t) { + return this.timing = t, this; + } + time(t) { + return this.easeTime = t, this; + } + relative() { + return this.relation = "relative", this; + } + absolute() { + return this.relation = "absolute", this; + } + transition(t, e) { + if (e === this.target[t]) + return this; + if (!T(this.now[t])) + return this.now[t] = e, this; + this.applying[t] && this.end(t, !0), this.applying[t] = !0, this.hook("start"); + const i = this.getTime(), + r = this.easeTime, + h = this.timing, + l = this.now[t], + u = e + (this.relation === "absolute" ? 0 : l), + c = u - l; + this.target[t] = u; + const a = () => { + const d = this.getTime() - i; + if (d >= r) { + this.end(t); + return; + } + const f = d / r; + this.now[t] = h(f) * c + l, this.hook("running"); + }; + return this.transitionFn[t] = a, r <= 0 ? (this.end(t), this) : (this.ticker.add(a), this); + } + end(t, e = !1) { + const i = this.transitionFn[t]; + if (!T(i)) + /*throw new ReferenceError( + `You are trying to end an ended transition: ${t}` + );*/ + this.ticker.remove(this.transitionFn[t]), delete this.transitionFn[t], this.applying[t] = !1, this.hook("end"), e || (this.now[t] = this.target[t]); + } + } + const x = (...n) => n.reduce((s, t) => s + t, 0), + y = (n) => { + if (n === 0) + return 1; + let s = n; + for (; n > 1;) + n--, s *= n; + return s; + }, + A = (n, s) => Math.round(y(s) / (y(n) * y(s - n))), + p = (n, s, t = (e) => 1 - s(1 - e)) => n === "in" ? s : n === "out" ? t : n === "in-out" ? (e) => e < 0.5 ? s(e * 2) / 2 : 0.5 + t((e - 0.5) * 2) / 2 : (e) => e < 0.5 ? t(e * 2) / 2 : 0.5 + s((e - 0.5) * 2) / 2, + $ = Math.cosh(2), + z = Math.acosh(2), + V = Math.tanh(3), + P = Math.atan(5); + + function O() { + return (n) => n; + } + + function q(...n) { + const s = [0].concat(n); + s.push(1); + const t = s.length, + e = Array(t).fill(0).map((i, r) => A(r, t - 1)); + return (i) => { + const r = e.map((h, l) => h * s[l] * (1 - i) ** (t - l - 1) * i ** l); + return x(...r); + }; + } + + function U(n, s) { + if (n === "sin") { + const t = (i) => Math.sin(i * Math.PI / 2); + return p(s, (i) => 1 - t(1 - i), t); + } + if (n === "sec") { + const t = (i) => 1 / Math.cos(i); + return p(s, (i) => t(i * Math.PI / 3) - 1); + } + throw new TypeError( + "Unexpected parameters are delivered in trigo timing function." + ); + } + + function C(n, s) { + if (!Number.isInteger(n)) + throw new TypeError( + "The first parameter of power timing function only allow integer." + ); + return p(s, (e) => e ** n); + } + + function G(n, s) { + if (n === "sin") + return p(s, (e) => (Math.cosh(e * 2) - 1) / ($ - 1)); + if (n === "tan") { + const t = (i) => Math.tanh(i * 3) * 1 / V; + return p(s, (i) => 1 - t(1 - i), t); + } + if (n === "sec") { + const t = (i) => 1 / Math.cosh(i); + return p(s, (i) => 1 - (t(i * z) - 0.5) * 2); + } + throw new TypeError( + "Unexpected parameters are delivered in hyper timing function." + ); + } + + function N(n, s) { + if (n === "sin") { + const t = (i) => Math.asin(i) / Math.PI * 2; + return p(s, (i) => 1 - t(1 - i), t); + } + if (n === "tan") { + const t = (i) => Math.atan(i * 5) / P; + return p(s, (i) => 1 - t(1 - i), t); + } + throw new TypeError( + "Unexpected parameters are delivered in inverse trigo timing function." + ); + } + + function B(n, s = () => 1) { + let t = -1; + return (e) => (t *= -1, e < 0.5 ? n * s(e * 2) * t : n * s((1 - e) * 2) * t); + } + + function D(n, s = 1, t = [0, 0], e = 0, i = (h) => 1, r = !1) { + return (h) => { + const l = s * h * Math.PI * 2 + e * Math.PI / 180, + u = Math.cos(l), + c = Math.sin(l), + a = n * i(i(r ? 1 - h : h)); + return [a * u + t[0], a * c + t[1]]; + }; + } + + function H(n, s, ...t) { + const e = [n].concat(t); + e.push(s); + const i = e.length, + r = Array(i).fill(0).map((h, l) => A(l, i - 1)); + return (h) => { + const l = r.map((c, a) => c * e[a][0] * (1 - h) ** (i - a - 1) * h ** a), + u = r.map((c, a) => c * e[a][1] * (1 - h) ** (i - a - 1) * h ** a); + return [x(...l), x(...u)]; + }; + } + + if ('animate' in core.plugin) throw new ReferenceError(`插件中已存在名为animate的属性!`); + + core.plugin.animate = { + Animation: Y, + AnimationBase: F, + Ticker: I, + Transition: j, + sleep: R, + circle: D, + bezierPath: H, + linear: O, + bezier: q, + trigo: U, + power: C, + hyper: G, + inverseTrigo: N, + shake: B + } + + + }, + "平滑移动视角": function () { + + // ---------- 插件说明 ---------- // + // 该插件允许你能够平滑移动大地图的镜头,达到更好的视觉体验 + // 直接复制进插件内即可,注意别忘了安装前置插件,前置插件要放到该插件前面 + + // ---------- 配置说明 ---------- // + // 你可以在插件的第 60 - 80 行更改平滑移动的信息,包括速率曲线、移动时间等 + + + + if (main.replayChecking) return; + + const { Transition, hyper } = core.plugin.animate; + + function debounce(func, time) { + let timeout = 0; + + // @ts-ignore + return (...params) => { + clearTimeout(timeout); + timeout = window.setTimeout(() => { + func(...params); + }, time); + }; + } + + const tran = new Transition(); + tran.value.x = 0; + tran.value.y = 0; + + let needSmooth = false; + + tran.ticker.add(() => { + if (core.isPlaying() && needSmooth) { + core.setViewport(tran.value.x, tran.value.y); + } + }); + + const func = debounce(() => { + needSmooth = false; + }, 700); + + control.prototype._drawHero_updateViewport = function (x, y, offset) { + const ox = core.clamp( + (x - core._HALF_WIDTH_) * 32 + offset.x, + 0, + Math.max(32 * core.bigmap.width - core._PX_, 0) + ); + const oy = core.clamp( + (y - core._HALF_HEIGHT_) * 32 + offset.y, + 0, + Math.max(32 * core.bigmap.height - core._PY_, 0) + ); + + if (core.isReplaying()) { + core.bigmap.offsetX = ox; + core.bigmap.offsetY = oy; + core.control.updateViewport(); + return; + } + + tran.transition('x', ox).transition('y', oy); + + if (tran.easeTime > 0) { + needSmooth = true; + func(); + } else { + core.setViewport(tran.value.x, tran.value.y); + } + }; + + let time2 = Date.now(); + const origin1 = control.prototype._moveAction_moving; + control.prototype._moveAction_moving = function (...params) { + // 防止瞬移+移动的情况 + if (Date.now() - time2 > 30) + tran.mode(hyper('sin', 'out')).time(230).absolute(); + return origin1.call(this, ...params); + }; + + const origin2 = control.prototype.moveDirectly; + control.prototype.moveDirectly = function (...params) { + time2 = Date.now(); + tran.mode(hyper('sin', 'out')).time(230).absolute(); + return origin2.call(this, ...params); + }; + + /*const origin3 = events.prototype._changeFloor_beforeChange; + events.prototype._changeFloor_beforeChange = function (...params) { + tran.time(0).absolute(); + return origin3.call(this, ...params); + };*/ + + + + }, + "大地图视角移动修复": function () { + ////// 清除地图 ////// + ui.prototype.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.dom.gif.innerHTML = ""; + core.removeGlobalAnimate(); + core.deleteCanvas(function (one) { return one.startsWith('_bigImage_'); }); + core.setWeather(null); + } else { + var ctx = this.getContextByName(name); + if (ctx) { + if (x != null && y != null && width != null && height != null) { + ctx.clearRect(x, y, width, height); + } else { + if (core.bigmap.v2 && core.bigmap.canvas.indexOf(name) != -1) { + if (ctx.canvas.getAttribute('isHD')) { + const sx = -32 / core.domStyle.scale / devicePixelRatio; + const sy = -32 / core.domStyle.scale / devicePixelRatio; + const width = ctx.canvas.width / core.domStyle.scale / devicePixelRatio; + const height = ctx.canvas.height / core.domStyle.scale / devicePixelRatio; + ctx.clearRect(sx, sy, width, height); + } else { + ctx.clearRect(-32, -32, ctx.canvas.width, ctx.canvas.height); + } + } else { + if (ctx.canvas.getAttribute('isHD')) { + const width = ctx.canvas.width / core.domStyle.scale / devicePixelRatio; + const height = ctx.canvas.height / core.domStyle.scale / devicePixelRatio; + ctx.clearRect(0, 0, width, height); + } else { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + } + } + } + } + } + } + }, + "1-flag初始化": function () { + this.initVariable = function () { + //todo 开局飞行器清掉 + console.log("flag初始化完成"); + + + flags.craftGuide11 = {I554 /*橡木木板*/: [["I549","","","",2],], I555 /*白桦木板*/: [["I550","","","",2],], I556 /*云杉木板*/: [["I551","","","",2],], I557 /*丛林木板*/: [["I552","","","",2],], I558 /*金合欢木板*/: [["I553","","","",2],], I560 /*工作台*/: [["I554","I554","I554","I554",1],["I555","I555","I555","I555",1],["I556","I556","I556","I556",1],["I557","I557","I557","I557",1],["I558","I558","I558","I558",1],], I661 /*木棍*/: [["I681","","","",6],["I554","","I554","",4],["I555","","I555","",4],["I556","","I556","",4],["I557","","I557","",4],["I558","","I558","",4],], I684 /*棕色蘑菇块*/: [["I682","I682","I682","I682",1],], I685 /*红色蘑菇块*/: [["I683","I683","I683","I683",1],], I692 /*黄珊瑚块*/: [["I688","I688","I688","I688",2],], I693 /*红珊瑚块*/: [["I689","I689","I689","I689",2],], I694 /*蓝珊瑚块*/: [["I690","I690","I690","I690",2],], I695 /*粉珊瑚块*/: [["I691","I691","I691","I691",2],], I664 /*火把*/: [["I670","","I661","",4],["I523","","I661","",4],], I671 /*皮革*/: [["I672","I672","","",1],], I522 /*羊毛*/: [["I675","I675","I675","I675",1],], I523 /*煤炭*/: [["I535","","","",9],], I621 /*小麦种子*/: [["I625","","","",1],], I622 /*西瓜种子*/: [["I631","","","",1],], }; + flags.craftGuide12 = {I527 /*铁锭*/: [["I536","","","",9],], I528 /*铜锭*/: [["I537","","","",9],], I529 /*金锭*/: [["I538","","","",9],["","","","",],], I530 /*红石*/: [["I539","","","",9],], I531 /*青金石*/: [["I540","","","",9],], I616 /*剪刀*/: [["I527","","","I527",1],], I635 /*雕刻南瓜*/: [["I634","I616","","",1],], I666 /*骨粉*/: [["I667","","","",3],], I669 /*金粒*/: [["I529","","","",9],], I671 /*皮革*/: [["I696","I696","I696","I696",1],], I674 /*燧石*/: [["I520","I520","I520","I520",2],], I680 /*糖*/: [["I679","","","",1],], }; + flags.craftGuide13 = {I543 /*紫水晶块*/: [["I534","I534","I534","I534",1],], I687 /*干海带*/: [["I686","I686","I686","I686",1],], }; + flags.craftGuide14 = {}; + flags.craftGuide15 = {I703 /*末影之眼*/: [["I702","I708","","",1],], }; + flags.craftGuide16 = {I554 /*橡木木板*/: [["I549","","","",2],], }; + flags.craftGuide17 = {}; + flags.craftGuide18 = {I554 /*橡木木板*/: [["I549","","","",2],], }; + flags.craftGuide19 = {I554 /*橡木木板*/: [["I549","","","",2],], }; + flags.craftGuide1a = {I554 /*橡木木板*/: [["I549","","","",2],], }; + flags.craftGuide21 = {I554 /*橡木木板*/: [["","","","","I549","","","","",2],], I555 /*白桦木板*/: [["","","","","I550","","","","",2],], I556 /*云杉木板*/: [["","","","","I551","","","","",2],], I557 /*丛林木板*/: [["","","","","I552","","","","",2],], I558 /*金合欢木板*/: [["","","","","I553","","","","",2],], I559 /*箱子*/: [["I554","I554","I554","I554","","I554","I554","I554","I554",1],["I555","I555","I555","I555","","I555","I555","I555","I555",1],["I556","I556","I556","I556","","I556","I556","I556","I556",1],["I557","I557","I557","I557","","I557","I557","I557","I557",1],["I558","I558","I558","I558","","I558","I558","I558","I558",1],], I560 /*工作台*/: [["I554","I554","","I554","I554","","","","",1],["I555","I555","","I555","I555","","","","",1],["I556","I556","","I556","I556","","","","",1],["I557","I557","","I557","I557","","","","",1],["I558","I558","","I558","I558","","","","",1],], I567 /*篝火*/: [["","I661","","I661","I670","I661","I549","I549","I549",1],["","I661","","I661","I670","I661","I550","I550","I550",1],["","I661","","I661","I670","I661","I551","I551","I551",1],["","I661","","I661","I670","I661","I552","I552","I552",1],["","I661","","I661","I670","I661","I553","I553","I553",1],], I569 /*木剑*/: [["","I554","","","I554","","","I661","",1],["","I555","","","I555","","","I661","",1],["","I556","","","I556","","","I661","",1],["","I557","","","I557","","","I661","",1],["","I558","","","I558","","","I661","",1],], I574 /*木斧*/: [["I554","I554","","I554","I661","","","I661","",1],["I555","I555","","I555","I661","","","I661","",1],["I556","I556","","I556","I661","","","I661","",1],["I557","I557","","I557","I661","","","I661","",1],["I558","I558","","I558","I661","","","I661","",1],], I579 /*木镐*/: [["I554","I554","I554","","I661","","","I661","",1],["I555","I555","I555","","I661","","","I661","",1],["I556","I556","I556","","I661","","","I661","",1],["I557","I557","I557","","I661","","","I661","",1],["I558","I558","I558","","I661","","","I661","",1],], I584 /*木锹*/: [["","I554","","","I661","","","I661","",1],["","I555","","","I661","","","I661","",1],["","I556","","","I661","","","I661","",1],["","I557","","","I661","","","I661","",1],["","I558","","","I661","","","I661","",1],], I589 /*木锄*/: [["I554","I554","","","I661","","","I661","",1],["I555","I555","","","I661","","","I661","",1],["I556","I556","","","I661","","","I661","",1],["I557","I557","","","I661","","","I661","",1],["I558","I558","","","I661","","","I661","",1],], I570 /*石剑*/: [["","I515","","","I515","","","I661","",1],], I575 /*石斧*/: [["I515","I515","","I515","I661","","","I661","",1],], I580 /*石镐*/: [["I515","I515","I515","","I661","","","I661","",1],], I585 /*石锹*/: [["","I515","","","I661","","","I661","",1],], I590 /*石锄*/: [["I515","I515","","","I661","","","I661","",1],], I594 /*皮革帽子*/: [["I671","I671","I671","I671","","I671","","","",1],], I595 /*皮革胸甲*/: [["I671","","I671","I671","I671","I671","I671","I671","I671",1],], I596 /*皮革护腿*/: [["I671","I671","I671","I671","","I671","I671","","I671",1],], I597 /*皮革靴子*/: [["","","","I671","","I671","I671","","I671",1],], I598 /*铜头盔*/: [["I528","I528","I528","I528","","I528","","","",1],], I599 /*铜胸甲*/: [["I528","","I528","I528","I528","I528","I528","I528","I528",1],], I600 /*铜护腿*/: [["I528","I528","I528","I528","","I528","I528","","I528",1],], I601 /*铜靴子*/: [["","","","I528","","I528","I528","","I528",1],], I617 /*木盾*/: [["I554","","I554","I554","I554","I554","","I554","",1],["I555","","I555","I555","I555","I555","","I555","",1],["I556","","I556","I556","I556","I556","","I556","",1],["I557","","I557","I557","I557","I557","","I557","",1],["I558","","I558","I558","I558","I558","","I558","",1],], I618 /*铜盾*/: [["I528","","I528","I528","I528","I528","","I528","",1],], I661 /*木棍*/: [["","","","","I681","","","","",6],["","I554","","","I554","","","","",4],["","I555","","","I555","","","","",4],["","I556","","","I556","","","","",4],["","I557","","","I557","","","","",4],["","I558","","","I558","","","","",4],], I662 /*木船*/: [["","","","I554","","I554","I554","I554","I554",1],["","","","I555","","I555","I555","I555","I555",1],["","","","I556","","I556","I556","I556","I556",1],["","","","I557","","I557","I557","I557","I557",1],["","","","I558","","I558","I558","I558","I558",1],], I663 /*木门*/: [["I554","I554","","I554","I554","","I554","I554","",1],["I555","I555","","I555","I555","","I555","I555","",1],["I556","I556","","I556","I556","","I556","I556","",1],["I557","I557","","I557","I557","","I557","I557","",1],["I558","I558","","I558","I558","","I558","I558","",1],], I684 /*棕色蘑菇块*/: [["I682","I682","","I682","I682","","","","",1],], I685 /*红色蘑菇块*/: [["I683","I683","","I683","I683","","","","",1],], I692 /*黄珊瑚块*/: [["I688","I688","","I688","I688","","","","",2],], I693 /*红珊瑚块*/: [["I689","I689","","I689","I689","","","","",2],], I694 /*蓝珊瑚块*/: [["I690","I690","","I690","I690","","","","",2],], I695 /*粉珊瑚块*/: [["I691","I691","","I691","I691","","","","",2],], I664 /*火把*/: [["I670","","","I661","","","","","",4],["I523","","","I661","","","","","",4],], I671 /*皮革*/: [["I672","I672","","","","","","","",1],], I522 /*羊毛*/: [["I675","I675","","I675","I675","","","","",1],], I535 /*煤炭块*/: [["I523","I523","I523","I523","I523","I523","I523","I523","I523",1],], I523 /*煤炭*/: [["","","","","I535","","","","",9],], I561 /*熔炉*/: [["I515","I515","I515","I515","","I515","I515","I515","I515",1],], I621 /*小麦种子*/: [["","","","","I625","","","","",1],], I622 /*西瓜种子*/: [["","","","","I631","","","","",1],], I626 /*面包*/: [["","","","I625","I625","I625","","","",1],], }; + flags.craftGuide22 = {I527 /*铁锭*/: [["","","","","I536","","","","",9],], I528 /*铜锭*/: [["","","","","I537","","","","",9],], I529 /*金锭*/: [["","","","","I538","","","","",9],["I669","I669","I669","I669","I669","I669","I669","I669","I669",1],], I530 /*红石*/: [["","","","","I539","","","","",9],], I531 /*青金石*/: [["","","","","I540","","","","",9],], I536 /*铁块*/: [["I527","I527","I527","I527","I527","I527","I527","I527","I527",1],], I537 /*铜块*/: [["I528","I528","I528","I528","I528","I528","I528","I528","I528",1],], I538 /*金块*/: [["I529","I529","I529","I529","I529","I529","I529","I529","I529",1],], I539 /*红石块*/: [["I530","I530","I530","I530","I530","I530","I530","I530","I530",1],], I540 /*青金石块*/: [["I531","I531","I531","I531","I531","I531","I531","I531","I531",1],], I571 /*铁剑*/: [["","I527","","","I527","","","I661","",1],], I576 /*铁斧*/: [["I527","I527","","I527","I661","","","I661","",1],], I581 /*铁镐*/: [["I527","I527","I527","","I661","","","I661","",1],], I586 /*铁锹*/: [["","I527","","","I661","","","I661","",1],], I591 /*铁锄*/: [["I527","I527","","","I661","","","I661","",1],], I572 /*金剑*/: [["","I529","","","I529","","","I661","",1],], I577 /*金斧*/: [["I529","I529","","I529","I661","","","I661","",1],], I582 /*金镐*/: [["I529","I529","I529","","I661","","","I661","",1],], I587 /*金锹*/: [["","I529","","","I661","","","I661","",1],], I592 /*金锄*/: [["I529","I529","","","I661","","","I661","",1],], I602 /*铁头盔*/: [["I527","I527","I527","I527","","I527","","","",1],], I603 /*铁胸甲*/: [["I527","","I527","I527","I527","I527","I527","I527","I527",1],], I604 /*铁护腿*/: [["I527","I527","I527","I527","","I527","I527","","I527",1],], I605 /*铁靴子*/: [["","","","I527","","I527","I527","","I527",1],], I606 /*金头盔*/: [["I529","I529","I529","I529","","I529","","","",1],], I607 /*金胸甲*/: [["I529","","I529","I529","I529","I529","I529","I529","I529",1],], I608 /*金护腿*/: [["I529","I529","I529","I529","","I529","I529","","I529",1],], I609 /*金靴子*/: [["","","","I529","","I529","I529","","I529",1],], I614 /*弓*/: [["","I661","I675","I661","","I675","","I661","I675",1],], I615 /*箭*/: [["","I674","","","I661","","","I676","",4],], I616 /*剪刀*/: [["I527","","","","I527","","","","",1],], I635 /*雕刻南瓜*/: [["I634","I616","","","","","","","",1],], I565 /*床*/: [["","","","I522","I522","I522","","","",1],], I568 /*垃圾桶*/: [["I528","I528","I528","I527","","I527","I527","I527","I527",1],], I653 /*钓鱼竿*/: [["I661","","","I675","I661","","I675","","I661",1],], I654 /*桶*/: [["","","","I527","","I527","","I527","",1],], I657 /*时钟*/: [["","I529","","I529","I530","I529","","I529","",1],], I658 /*玻璃瓶*/: [["","","","I518","","I518","","I518","",3],], I665 /*提灯*/: [["","","","","I664","","","I527","",1],], I666 /*骨粉*/: [["","","","","I667","","","","",3],], I667 /*骨头*/: [["","","","I666","I666","I666","","","",1],], I669 /*金粒*/: [["","","","","I529","","","","",9],], I671 /*皮革*/: [["I696","I696","","I696","I696","","","","",1],], I673 /*书*/: [["","","","","","I671","I677","I677","I677",1],], I674 /*燧石*/: [["I520","I520","","I520","I520","","","","",2],], I677 /*纸*/: [["","","","I679","I679","I679","","","",3],], I680 /*糖*/: [["","","","","I679","","","","",1],], }; + flags.craftGuide23 = {I541 /*钻石块*/: [["I532","I532","I532","I532","I532","I532","I532","I532","I532",1],], I542 /*绿宝石块*/: [["I533","I533","I533","I533","I533","I533","I533","I533","I533",1],], I543 /*紫水晶块*/: [["I534","I534","","I534","I534","","","","",1],], I573 /*钻石剑*/: [["","I532","","","I532","","","I661","",1],], I578 /*钻石斧*/: [["I532","I532","","I532","I661","","","I661","",1],], I583 /*钻石镐*/: [["I532","I532","I532","","I661","","","I661","",1],], I588 /*钻石锹*/: [["","I532","","","I661","","","I661","",1],], I593 /*钻石锄*/: [["I532","I532","","","I661","","","I661","",1],], I610 /*钻石头盔*/: [["I532","I532","I532","I532","","I532","","","",1],], I611 /*钻石胸甲*/: [["I532","","I532","I532","I532","I532","I532","I532","I532",1],], I612 /*钻石护腿*/: [["I532","I532","I532","I532","","I532","I532","","I532",1],], I613 /*钻石靴子*/: [["","","","I532","","I532","I532","","I532",1],], I619 /*钻石盾*/: [["I532","","I532","I532","I532","I532","","I532","",1],], I564 /*铁砧*/: [["I536","I536","I536","","I536","","I536","I536","I536",1],], I687 /*干海带*/: [["I686","I686","","I686","I686","","","","",1],], I706 /*TNT*/: [["I513","I668","I513","I668","I513","I668","I513","I668","I513",1],], }; + flags.craftGuide24 = {I563 /*附魔台*/: [["","I673","","I532","I519","I532","I519","I519","I519",1],], }; + flags.craftGuide25 = {I562 /*酿造台*/: [["","I707","","","I515","","I515","I515","I515",1],], I566 /*末影箱*/: [["I519","I519","I519","I519","I703","I519","I519","I519","I519",1],], I629 /*金苹果*/: [["I538","I538","I538","I538","I628","I538","I538","I538","I538",1],], I632 /*闪烁西瓜片*/: [["I529","I529","I529","I529","I631","I529","I529","I529","I529",1],], I633 /*金胡萝卜*/: [["I529","I529","I529","I529","I627","I529","I529","I529","I529",1],], I708 /*烈焰粉*/: [["","","","","I707","","","","",2],], I703 /*末影之眼*/: [["I702","I708","","","","","","","",1],], }; + flags.craftGuide26 = {I554 /*橡木木板*/: [["","","","","I549","","","","",2],], }; + flags.craftGuide27 = {I629 /*金苹果*/: [["I529","I529","I529","I529","I628","I529","I529","I529","I529",1],], }; + flags.craftGuide28 = {I554 /*橡木木板*/: [["","","","","I549","","","","",2],], }; + flags.craftGuide29 = {I554 /*橡木木板*/: [["","","","","I549","","","","",2],], }; + flags.craftGuide2a = {I554 /*橡木木板*/: [["","","","","I549","","","","",2],], }; + flags.weapon = {I569 /*木剑*/: [20, 1, -8, 1, 2, ],I570 /*石剑*/: [50, 2, -8, 1, 2, ],I571 /*铁剑*/: [120, 5, -8, 1, 2, ],I572 /*金剑*/: [60, 4, -8, 1, 2, ],I573 /*钻石剑*/: [480, 10, -8, 1, 2, ],I614 /*弓*/: [150, 4, -34, 3, 3, ],}; + flags.tool = {I574 /*木斧*/: [20, 2, 2],I575 /*石斧*/: [50, 3, 2],I576 /*铁斧*/: [120, 4, 2],I577 /*金斧*/: [35, 7, 2],I578 /*钻石斧*/: [480, 5, 2],I579 /*木镐*/: [20, 2, 1],I580 /*石镐*/: [50, 3, 1],I581 /*铁镐*/: [120, 4, 1],I582 /*金镐*/: [35, 7, 1],I583 /*钻石镐*/: [480, 5, 1],I584 /*木锹*/: [20, 2, 3],I585 /*石锹*/: [50, 3, 3],I586 /*铁锹*/: [120, 4, 3],I587 /*金锹*/: [35, 7, 3],I588 /*钻石锹*/: [480, 5, 3],I589 /*木锄*/: [20, 1, 1],I590 /*石锄*/: [50, 1, 1],I591 /*铁锄*/: [120, 1, 1],I592 /*金锄*/: [35, 1, 1],I593 /*钻石锄*/: [480, 1, 1],I653 /*钓鱼竿*/: [60, 1, 1],}; + flags.equip = {I617 /*木盾*/: [60, 1],I618 /*铜盾*/: [150, 2],I619 /*钻石盾*/: [525, 3],I595 /*皮革胸甲*/: [85, 1],I599 /*铜胸甲*/: [130, 2],I603 /*铁胸甲*/: [265, 3],I607 /*金胸甲*/: [155, 2],I611 /*钻石胸甲*/: [575, 4],I596 /*皮革护腿*/: [75, 1],I600 /*铜护腿*/: [110, 2],I604 /*铁护腿*/: [230, 2],I608 /*金护腿*/: [135, 2],I612 /*钻石护腿*/: [500, 3],I594 /*皮革帽子*/: [60, 1],I598 /*铜头盔*/: [90, 1],I602 /*铁头盔*/: [180, 2],I606 /*金头盔*/: [110, 1],I610 /*钻石头盔*/: [420, 3],I597 /*皮革靴子*/: [55, 1],I601 /*铜靴子*/: [80, 1],I605 /*铁靴子*/: [165, 2],I609 /*金靴子*/: [95, 1],I613 /*钻石靴子*/: [360, 3],}; + flags.wfk = {305: [["I511", 1, 100],],339: [["I511", 1, 100],],340: [["I511", 1, 100],],341: [["I511", 1, 100],],342: [["I512", 1, 100],],343: [["I513", 1, 100],],344: [["I514", 1, 100],],345: [["I746", 1, 0],],346: [["I515", 1, 100],],347: [["I517", 1, 100],],348: [["I746", 1, 0],],349: [["I520", 1, 100],["I674", 1, 10],],350: [["I549", 4, 100],["I544", 2, 70],["I628", 1, 5],],351: [["I550", 4, 100],["I545", 1, 100],["I545", 1, 40],],352: [["I551", 5, 100],["I546", 1, 60],["I546", 1, 60],],353: [["I552", 6, 100],["I547", 1, 50],["I547", 1, 50],],354: [["I553", 4, 100],["I548", 1, 70],["I548", 1, 70],],355: [["I544", 1, 100],],356: [["I545", 1, 100],],357: [["I546", 1, 100],],358: [["I547", 1, 100],],359: [["I548", 1, 100],],360: [["I549", 1, 100],],361: [["I550", 1, 100],],362: [["I551", 1, 100],],363: [["I552", 1, 100],],364: [["I553", 1, 100],],365: [["I554", 1, 100],],366: [["I555", 1, 100],],367: [["I556", 1, 100],],368: [["I557", 1, 100],],369: [["I558", 1, 100],],370: [["I662", 1, 100],],371: [["I663", 1, 100],],372: [["I746", 1, 0],],373: [["I746", 1, 0],],374: [["I638", 1, 100],["I638", 1, 5],],375: [["I639", 1, 100],["I639", 1, 5],],376: [["I678", 1, 100],],377: [["I678", 1, 100],],378: [["I679", 1, 100],["I679", 2, 50],["I679", 2, 50],],379: [["I634", 1, 100],["I705", 1, 10],],380: [["I635", 1, 100],],381: [["I631", 2, 100],["I631", 1, 50],["I705", 1, 10],],382: [["I681", 1, 100],],383: [["I681", 2, 100],["I681", 2, 50],],386: [["I661", 1, 40],["I661", 1, 40],],387: [["I637", 1, 100],["I705", 1, 10],],388: [["I637", 1, 100],["I705", 1000, 100],],389: [["I682", 3, 95],["I682", 5, 50],["I684", 4, 100],],390: [["I683", 3, 95],["I683", 5, 50],["I685", 4, 100],],391: [["I684", 1, 100],],392: [["I685", 1, 100],],393: [["I746", 1, 0],],394: [["I686", 1, 100],["I686", 1, 50],],395: [["I688", 1, 100],["I705", 1, 10],],396: [["I689", 1, 100],["I705", 1, 10],],397: [["I690", 1, 100],["I705", 1, 10],],398: [["I691", 1, 100],["I705", 1, 10],],399: [["I692", 1, 100],],400: [["I693", 1, 100],],401: [["I694", 1, 100],],402: [["I695", 1, 100],],403: [["I523", 1, 100],["I705", 3, 50],["I523", 1, 50],],404: [["I524", 1, 100],["I705", 3, 50],],405: [["I525", 1, 100],["I705", 3, 50],],406: [["I526", 1, 100],["I705", 3, 50],],407: [["I530", 2, 100],["I705", 3, 50],["I530", 1, 50],],408: [["I531", 2, 100],["I705", 3, 50],["I531", 1, 50],],409: [["I532", 1, 100],["I705", 5, 40],],410: [["I533", 1, 100],["I705", 5, 40],],411: [["I534", 2, 100],["I705", 8, 10],["I534", 2, 50],],412: [["I682", 1, 100],],413: [["I683", 1, 100],],414: [["I560", 1, 100],],415: [["I561", 1, 100],],416: [["I562", 1, 100],],417: [["I563", 1, 100],],418: [["I564", 1, 100],],419: [["I565", 1, 100],],420: [["I559", 1, 100],],421: [["I519", 8, 100],],422: [["I567", 1, 100],],423: [["I567", 1, 100],],424: [["I665", 1, 100],],425: [["I568", 1, 100],],426: [["I511", 1, 100],],427: [["I631", 1, 100],["I631", 1, 50],["I631", 1, 50],],428: [["I621", 1, 100],],429: [["I623", 1, 100],],430: [["I627", 1, 100],],431: [["I636", 1, 100],],432: [["I746", 1, 0],],433: [["I746", 1, 0],],896: [["I519", 1, 100],],897: [["I727", 1, 100],],898: [["I522", 1, 100],],899: [["I535", 1, 100],],900: [["I536", 1, 100],],901: [["I537", 1, 100],],902: [["I538", 1, 100],],903: [["I539", 1, 100],],904: [["I540", 1, 100],],905: [["I541", 1, 100],],906: [["I542", 1, 100],],907: [["I746", 1, 0],],908: [["I746", 1, 0],],909: [["I746", 1, 0],],910: [["I746", 1, 0],],911: [["I746", 1, 0],],912: [["I746", 1, 0],],913: [["I746", 1, 0],],914: [["I746", 1, 0],],915: [["I746", 1, 0],],916: [["I515", 1, 100],],917: [["I746", 1, 0],],918: [["I625", 4, 100],["I621", 3, 100],["I705", 1, 20],],919: [["I623", 1, 100],["I623", 2, 60],["I705", 1, 20],],920: [["I627", 2, 100],["I627", 2, 60],["I705", 1, 20],],921: [["I511", 1, 100],],928: [["I521", 1, 100],],926: [["I721", 1, 100],],922: [["I722", 1, 100],],923: [["I723", 1, 100],],927: [["I724", 1, 90],],924: [["I725", 1, 100],],925: [["I726", 1, 100],],929: [["I728", 1, 100],],930: [["I729", 1, 100],],}; + flags.ffk = {I512: [[342], [1, 1, 1], [0]], I513: [[343], [1, 1, 1], [0]], I514: [[344], [1, 1, 1], [0]], I515: [[916], [1, 1, 1], [0]], I516: [[346], [1, 1, 1], [0]], I517: [[347], [1, 1, 1], [0]], I520: [[349], [1, 1, 1], [0]], I544: [[355], [0, 1, 0], [1,305,339,340,341,921]], I545: [[356], [0, 1, 0], [1,305,339,340,341,921]], I546: [[357], [0, 1, 0], [1,305,339,340,341,921]], I547: [[358], [0, 1, 0], [1,305,339,340,341,921]], I548: [[359], [0, 1, 0], [1,305,339,340,341,921]], I549: [[360], [1, 1, 1], [0]], I550: [[361], [1, 1, 1], [0]], I551: [[362], [1, 1, 1], [0]], I552: [[363], [1, 1, 1], [0]], I553: [[364], [1, 1, 1], [0]], I554: [[365], [1, 1, 1], [0]], I555: [[366], [1, 1, 1], [0]], I556: [[367], [1, 1, 1], [0]], I557: [[368], [1, 1, 1], [0]], I558: [[369], [1, 1, 1], [0]], I662: [[370], [0, 1, 0], [3,345,348]], I663: [[371], [0, 1, 0], [0]], I638: [[374], [0, 1, 0], [1,305,339,340,341,921]], I639: [[375], [0, 1, 0], [1,305,339,340,341,921]], I678: [[376], [0, 1, 0], [0]], I634: [[379], [0, 1, 0], [0]], I635: [[380], [0, 1, 0], [0]], I681: [[382], [0, 1, 0], [1,305,339,340,341,921]], I637: [[387], [0, 1, 0], [1,343,344]], I684: [[391], [1, 1, 1], [0]], I685: [[392], [1, 1, 1], [0]], I688: [[395], [0, 1, 0], [1,345,348]], I689: [[396], [0, 1, 0], [1,345,348]], I690: [[397], [0, 1, 0], [1,345,348]], I691: [[398], [0, 1, 0], [1,345,348]], I692: [[399], [1, 1, 1], [0]], I693: [[400], [1, 1, 1], [0]], I694: [[401], [1, 1, 1], [0]], I695: [[402], [1, 1, 1], [0]], I543: [[411], [1, 1, 1], [0]], I682: [[412], [0, 1, 0], [1,347]], I683: [[413], [0, 1, 0], [1,347]], I560: [[414], [0, 1, 0], [0]], I561: [[415], [0, 1, 0], [0]], I562: [[416], [0, 1, 0], [0]], I563: [[417], [0, 1, 0], [0]], I564: [[418], [0, 1, 0], [0]], I565: [[419], [0, 1, 0], [0]], I559: [[420], [0, 1, 0], [0]], I566: [[421], [0, 1, 0], [0]], I567: [[423], [0, 1, 0], [0]], I665: [[424], [0, 1, 0], [0]], I568: [[425], [0, 1, 0], [0]], I622: [[427], [0, 1, 0], [1,426]], I621: [[428], [0, 1, 0], [1,426]], I623: [[429], [0, 1, 0], [1,426]], I627: [[430], [0, 1, 0], [1,426]], I518: [[433], [1, 1, 1], [0]], I519: [[896], [1, 1, 1], [0]], I521: [[928], [1, 1, 1], [0]], I522: [[898], [1, 1, 1], [0]], I535: [[899], [1, 1, 1], [0]], I536: [[900], [1, 1, 1], [0]], I537: [[901], [1, 1, 1], [0]], I538: [[902], [1, 1, 1], [0]], I539: [[903], [1, 1, 1], [0]], I540: [[904], [1, 1, 1], [0]], I541: [[905], [1, 1, 1], [0]], I542: [[906], [1, 1, 1], [0]], I511: [[921], [1, 1, 1], [6]], I655: [[348], [1, 0, 1], [7]], I666: [[372], [0, 1, 0], [8]], I589: [[426], [1, 0, 0], [5]], I590: [[426], [1, 0, 0], [5]], I591: [[426], [1, 0, 0], [5]], I592: [[426], [1, 0, 0], [5]], I593: [[426], [1, 0, 0], [5]], I721: [[926], [1, 1, 1], [0]], I722: [[922], [1, 1, 1], [0]], I723: [[923], [1, 1, 1], [0]], I724: [[927], [1, 1, 1], [0]], I725: [[924], [1, 1, 1], [0]], I726: [[925], [1, 1, 1], [0]], I727: [[897], [1, 1, 1], [0]], I728: [[929], [1, 1, 1], [0]], I729: [[930], [1, 1, 1], [0]], }; + flags.fuelGuide = {I522 /*羊毛*/: [5],I661 /*木棍*/: [5],I701 /*凝胶*/: [5],I549 /*橡木原木*/: [9],I550 /*白桦原木*/: [9],I551 /*云杉原木*/: [9],I552 /*丛林原木*/: [9],I553 /*金合欢原木*/: [9],I554 /*橡木木板*/: [9],I555 /*白桦木板*/: [9],I556 /*云杉木板*/: [9],I557 /*丛林木板*/: [9],I558 /*金合欢木板*/: [9],I687 /*干海带*/: [9],I670 /*木炭*/: [24],I523 /*煤炭*/: [24],I535 /*煤炭块*/: [240],}; + flags.furGuide = {I513 /*沙子-玻璃*/: ["I518"],I514 /*红沙-玻璃*/: ["I518"],I515 /*圆石-石头*/: ["I516"],I524 /*粗铁-铁锭*/: ["I527"],I525 /*粗铜-铜锭*/: ["I528"],I526 /*粗金-金锭*/: ["I529"],I549 /*橡木原木-木炭*/: ["I670"],I550 /*白桦原木-木炭*/: ["I670"],I551 /*云杉原木-木炭*/: ["I670"],I552 /*丛林原木-木炭*/: ["I670"],I553 /*金合欢原木-木炭*/: ["I670"],I640 /*生鸡肉-熟鸡肉*/: ["I641"],I642 /*生羊肉-熟羊肉*/: ["I643"],I644 /*生牛肉-熟牛排*/: ["I645"],I646 /*生兔肉-熟兔肉*/: ["I647"],I648 /*生鳕鱼-熟鳕鱼*/: ["I649"],I650 /*生鲑鱼-熟鲑鱼*/: ["I651"],I655 /*水桶-桶*/: ["I654"],I659 /*水瓶-玻璃瓶*/: ["I658"],I667 /*骨头-骨粉*/: ["I666"],I639 /*玫瑰-糖*/: ["I680"],I679 /*甘蔗-糖*/: ["I680"],I686 /*海带-干海带*/: ["I687"],I696 /*腐肉-皮革*/: ["I671"],}; + flags.cropslist = {I544 /*橡树树苗*/: [4500,[355,350],[305,339,340,341,921]],I545 /*白桦树苗*/: [4500,[356,351],[305,339,340,341,921]],I546 /*云杉树苗*/: [5500,[357,352],[305,339,340,341,921]],I547 /*丛林树苗*/: [6500,[358,353],[305,339,340,341,921]],I548 /*金合欢树苗*/: [4500,[359,354],[305,339,340,341,921]],I621 /*小麦种子*/: [3000,[428,918],[426]],I622 /*西瓜种子*/: [3000,[427,381],[426]],I623 /*马铃薯*/: [3000,[429,919],[426]],I627 /*胡萝卜*/: [3000,[430,920],[426]],I682 /*棕色蘑菇*/: [4500,[412,389],[347]],I683 /*红色蘑菇*/: [4500,[413,390],[347]],I681 /*竹子*/: [4500,[382,383],[305,339,340,341,921]],}; + flags.enemyGuide = {E1:[[0.6,0.6,0.8,85,80,1,5,0,0.8,0.8,1],["I701",[1,1],40],[]],E5:[[0.5,0.7,0.9,85,80,1,5,0,0.8,0.8,1],["I701",[1,1],40],[]],E9:[[0.8,0.9,1,74,78,1,7,0,1,1,0],["I696",[1,1],30],["I527",[1,1],4]],E13:[[0.8,0.9,1,76,78,1,7,0,1,1,0],["I696",[1,1],30],["I529",[1,1],3]],E17:[[0.8,0.9,1,74,78,1,8,0,1,1,0],["I696",[1,1],30],["I528",[1,1],5]],E21:[[0.5,0.8,0.9,60,78,3,8,0,1,1,0],["I667",[1,1],30],["I614",[1,1],10]],E25:[[0.5,2,0,70,78,1,5,0,1,1,0],["I668",[1,1],30],[]],E29:[[0.7,1.5,0,75,76,2,5,1,1.6,1.6,0],["I668",[1,2],100],[]],E33:[[0.8,0.9,1,80,84,1,8,0,1,1,1],["I675",[1,2],40],[]],E37:[[1.2,1.2,1.4,70,96,1,10,0,1.6,1.6,1],["I702",[1,1],20],[]],E41:[[0.8,0.8,0.7,81,92,1,6,0,0.8,0.8,1],["I671",[1,2],40],[]],E45:[[0.7,0.1,0.8,74,80,2,6,1,1,1,0],["I648",[1,1],40],[]],E53:[[1,1.1,1.2,82,98,1,10,0,1.2,1.2,0],["I700",[1,1],30],["I676",[1,1],40]],E57:[[0.8,0.7,1,65,84,2,7,0,1,1,0],["I533",[1,1],40],["I614",[1,1],10]],E61:[[2,0.4,1.6,45,80,3,8,1,2.5,2.5,0],["I533",[1,1],40],["I620",[1,1],100]],E65:[[0.3,0.1,0.3,90,100,1,14,1,0,0,0],[],[]],E69:[[1,1,1,68,88,1,7,0,1,1,0],["I533",[1,1],40],["I576",[1,1],10]],E73:[[2.2,1.2,1.8,50,50,1,9,0,1.2,1.2,0],["I533",[1,1],40],[]],}; + flags.tech2 = {灵感: [["解锁铁、金合成表","解锁红石科技、钻石合成表","解锁附魔、下界合成表","解锁酿造、下界合金合成表","解锁灼热科技","解锁末地合成表","解锁虚空科技","解锁更多的虚空科技","解锁更多的虚空科技",],["解锁铁、金合成表","解锁红石科技、钻石合成表","解锁附魔、下界合成表","解锁酿造、下界合金合成表","解锁灼热科技","解锁末地合成表","解锁虚空科技","解锁更多的虚空科技","解锁更多的虚空科技",],["","","","","","","","","",],["解锁更多的合成配方"]],领悟: [["打怪经验+1","打怪经验+2","打怪经验+3","打怪经验+4","打怪经验+5","打怪经验+6","打怪经验+7","打怪经验+8","打怪经验+9","打怪经验+10",],["打怪经验+1","打怪经验+1 -> 打怪经验+2","打怪经验+2 -> 打怪经验+3","打怪经验+3 -> 打怪经验+4","打怪经验+4 -> 打怪经验+5","打怪经验+5 -> 打怪经验+6","打怪经验+6 -> 打怪经验+7","打怪经验+7 -> 打怪经验+8","打怪经验+8 -> 打怪经验+9","打怪经验+9 -> 打怪经验+10",],["","","","","","","","","","",],["每一次战斗都一场修行,使你能从中吸收更多的经验"]],生命之力: [["基础生命上限+4","基础生命上限+8","基础生命上限+12","基础生命上限+18","基础生命上限+24","基础生命上限+30","基础生命上限+36","基础生命上限+44","基础生命上限+52","基础生命上限+60",],["基础生命上限+4","基础生命上限+4 -> 基础生命上限+8","基础生命上限+8 -> 基础生命上限+12","基础生命上限+12 -> 基础生命上限+18","基础生命上限+18 -> 基础生命上限+24","基础生命上限+24 -> 基础生命上限+30","基础生命上限+30 -> 基础生命上限+36","基础生命上限+36 -> 基础生命上限+44","基础生命上限+44 -> 基础生命上限+52","基础生命上限+52 -> 基础生命上限+60",],["","","","","","","","","","",],["使你的生命力得到强化"]],增幅: [["基础攻击+1","基础攻击+2","基础攻击+4","基础攻击+6","基础攻击+9","基础攻击+12","基础攻击+16","基础攻击+20","基础攻击+24","基础攻击+28",],["基础攻击+1","基础攻击+1 -> 基础攻击+2","基础攻击+2 -> 基础攻击+4","基础攻击+4 -> 基础攻击+6","基础攻击+6 -> 基础攻击+9","基础攻击+9 -> 基础攻击+12","基础攻击+12 -> 基础攻击+16","基础攻击+16 -> 基础攻击+20","基础攻击+20 -> 基础攻击+24","基础攻击+24 -> 基础攻击+28",],["","","","","","","","","","",],["激发内在的力量,提高你的攻击力"]],坚定: [["基础防御+1","基础防御+2","基础防御+3","基础防御+4","基础防御+5","基础防御+6","基础防御+7","基础防御+8","基础防御+9","基础防御+10",],["基础防御+1","基础防御+1 -> 基础防御+2","基础防御+2 -> 基础防御+3","基础防御+3 -> 基础防御+4","基础防御+4 -> 基础防御+5","基础防御+5 -> 基础防御+6","基础防御+6 -> 基础防御+7","基础防御+7 -> 基础防御+8","基础防御+8 -> 基础防御+9","基础防御+9 -> 基础防御+10",],["","","","","","","","","","",],["使你能够抵御更强大的攻击"]],敏捷: [["基础攻速+1","基础攻速+2","基础攻速+3","基础攻速+4","基础攻速+5","基础攻速+6","基础攻速+7","基础攻速+8","基础攻速+9","基础攻速+10",],["基础攻速+1","基础攻速+1 -> 基础攻速+2","基础攻速+2 -> 基础攻速+3","基础攻速+3 -> 基础攻速+4","基础攻速+4 -> 基础攻速+5","基础攻速+5 -> 基础攻速+6","基础攻速+6 -> 基础攻速+7","基础攻速+7 -> 基础攻速+8","基础攻速+8 -> 基础攻速+9","基础攻速+9 -> 基础攻速+10",],["","","","","","","","","","",],["提升攻击速度,让你在战斗中更加灵活"]],耐力: [["饱食度上限+10","饱食度上限+20","饱食度上限+30","饱食度上限+40","饱食度上限+50","饱食度上限+60","饱食度上限+70","饱食度上限+80","饱食度上限+90","饱食度上限+100",],["饱食度上限+10","饱食度上限+10 -> 饱食度上限+20","饱食度上限+20 -> 饱食度上限+30","饱食度上限+30 -> 饱食度上限+40","饱食度上限+40 -> 饱食度上限+50","饱食度上限+50 -> 饱食度上限+60","饱食度上限+60 -> 饱食度上限+70","饱食度上限+70 -> 饱食度上限+80","饱食度上限+80 -> 饱食度上限+90","饱食度上限+90 -> 饱食度上限+100",],["","","","","","","","","","",],["增强你的耐力,提高饱食度上限"]],空间扩展: [["箱子存储空间+3","箱子存储空间+6","箱子存储空间+9","箱子存储空间+12","箱子存储空间+15","箱子存储空间+18","箱子存储空间+21","箱子存储空间+24","箱子存储空间+27",],["箱子存储空间+3","箱子存储空间+3 -> 箱子存储空间+6","箱子存储空间+6 -> 箱子存储空间+9","箱子存储空间+9 -> 箱子存储空间+12","箱子存储空间+12 -> 箱子存储空间+15","箱子存储空间+15 -> 箱子存储空间+18","箱子存储空间+18 -> 箱子存储空间+21","箱子存储空间+21 -> 箱子存储空间+24","箱子存储空间+24 -> 箱子存储空间+27",],["","","","","","","","","",],["扩展箱子存储空间"]],守护之韧: [["护甲和盾牌基础耐久+5","护甲和盾牌基础耐久+10","护甲和盾牌基础耐久+15","护甲和盾牌基础耐久+20","护甲和盾牌基础耐久+30","护甲和盾牌基础耐久+40","护甲和盾牌基础耐久+50","护甲和盾牌基础耐久+65","护甲和盾牌基础耐久+80","护甲和盾牌基础耐久+100",],["护甲和盾牌基础耐久+5","护甲和盾牌基础耐久+5 -> 护甲和盾牌基础耐久+10","护甲和盾牌基础耐久+10 -> 护甲和盾牌基础耐久+15","护甲和盾牌基础耐久+15 -> 护甲和盾牌基础耐久+20","护甲和盾牌基础耐久+20 -> 护甲和盾牌基础耐久+30","护甲和盾牌基础耐久+30 -> 护甲和盾牌基础耐久+40","护甲和盾牌基础耐久+40 -> 护甲和盾牌基础耐久+50","护甲和盾牌基础耐久+50 -> 护甲和盾牌基础耐久+65","护甲和盾牌基础耐久+65 -> 护甲和盾牌基础耐久+80","护甲和盾牌基础耐久+80 -> 护甲和盾牌基础耐久+100",],["","","","","","","","","","",],["提高护甲和盾牌的耐久度"]],锋锐恒久: [["武器和工具基础耐久+5","武器和工具基础耐久+10","武器和工具基础耐久+15","武器和工具基础耐久+20","武器和工具基础耐久+30","武器和工具基础耐久+40","武器和工具基础耐久+50","武器和工具基础耐久+65","武器和工具基础耐久+80","武器和工具基础耐久+100",],["武器和工具基础耐久+5","武器和工具基础耐久+5 -> 武器和工具基础耐久+10","武器和工具基础耐久+10 -> 武器和工具基础耐久+15","武器和工具基础耐久+15 -> 武器和工具基础耐久+20","武器和工具基础耐久+20 -> 武器和工具基础耐久+30","武器和工具基础耐久+30 -> 武器和工具基础耐久+40","武器和工具基础耐久+40 -> 武器和工具基础耐久+50","武器和工具基础耐久+50 -> 武器和工具基础耐久+65","武器和工具基础耐久+65 -> 武器和工具基础耐久+80","武器和工具基础耐久+80 -> 武器和工具基础耐久+100",],["","","","","","","","","","",],["使你的武器和工具更加耐用"]],生机: [["植物生长加速5%","植物生长加速10%","植物生长加速15%","植物生长加速20%","植物生长加速25%","植物生长加速30%","植物生长加速35%","植物生长加速40%","植物生长加速45%","植物生长加速50%",],["植物生长加速5%","植物生长加速5% -> 植物生长加速10%","植物生长加速10% -> 植物生长加速15%","植物生长加速15% -> 植物生长加速20%","植物生长加速20% -> 植物生长加速25%","植物生长加速25% -> 植物生长加速30%","植物生长加速30% -> 植物生长加速35%","植物生长加速35% -> 植物生长加速40%","植物生长加速40% -> 植物生长加速45%","植物生长加速45% -> 植物生长加速50%",],["","","","","","","","","","",],["你种下的植物长势良好"]],商贾之道: [["交易优惠5%","交易优惠10%","交易优惠15%","交易优惠20%","交易优惠25%","交易优惠30%","交易优惠35%","交易优惠40%","交易优惠45%","交易优惠50%",],["交易优惠5%","交易优惠5% -> 交易优惠10%","交易优惠10% -> 交易优惠15%","交易优惠15% -> 交易优惠20%","交易优惠20% -> 交易优惠25%","交易优惠25% -> 交易优惠30%","交易优惠30% -> 交易优惠35%","交易优惠35% -> 交易优惠40%","交易优惠40% -> 交易优惠45%","交易优惠45% -> 交易优惠50%",],["","","","","","","","","","",],["你能以更优惠的条件进行交易"]],}; + flags.tech3 = {灵感: [[0,0,0,0,0,0,0,8,9,],[0,0,0,0,0,0,0,5280,6210,],[2,3,4,5,6,7,8,8,8,]],领悟: [[1,2,3,4,5,6,7,8,9,10,],[420,900,1440,2040,2700,3420,4200,5040,5940,6900,],[1,2,3,4,5,6,7,7,8,]],生命之力: [[1,2,3,4,5,6,7,8,9,10,],[420,900,1440,2040,2700,3420,4200,5040,5940,6900,],[1,1,2,3,4,5,6,7,8,]],增幅: [[1,2,3,4,5,6,7,8,9,10,],[420,900,1440,2040,2700,3420,4200,5040,5940,6900,],[1,1,2,3,4,5,6,7,8,]],坚定: [[1,2,3,4,5,6,7,8,9,10,],[420,900,1440,2040,2700,3420,4200,5040,5940,6900,],[1,1,2,3,4,5,6,7,8,]],敏捷: [[1,2,3,4,5,6,7,8,9,10,],[420,810,1290,1830,2430,3070,3780,4530,5340,6210,],[1,1,2,3,4,5,6,7,8,]],耐力: [[1,1,2,2,3,4,5,6,7,8,],[420,450,960,1020,1620,2280,3000,3780,4620,5520,],[1,1,2,3,4,5,6,7,8,]],空间扩展: [[1,1,1,2,2,2,3,3,3,],[420,480,510,1080,1140,1200,1890,1980,2070,],[1,1,1,2,2,2,3,3,3,]],守护之韧: [[1,1,2,2,3,4,5,6,7,8,],[420,450,960,1020,1620,2280,3000,3780,4620,5520,],[1,1,2,3,4,5,6,7,8,]],锋锐恒久: [[1,1,2,2,3,4,5,6,7,8,],[420,450,960,1020,1620,2280,3000,3780,4620,5520,],[1,1,2,3,4,5,6,7,8,]],生机: [[1,1,1,2,2,2,3,3,4,4,],[420,450,480,1020,1080,1140,1800,1890,2640,2760,],[1,1,1,2,2,2,3,3,4,]],商贾之道: [[1,2,3,4,5,6,7,8,9,10,],[420,900,1440,2040,2700,3420,4200,5040,5940,6900,],[1,1,2,3,4,5,6,7,8,]],}; + flags.digLevel = {1: {1:[[6],[9],[-1],[-1],[-1]],2:[[5],[7],[9],[-1],[-1]],3:[[4],[6],[8],[-1],[-1]],4:[[3],[5],[7],[10],[-1]],5:[[2],[4],[6],[9],[12]],6:[[2],[3],[5],[8],[10]],7:[[1],[2],[4],[-1],[-1]]},2: {1:[[5],[8],[-1]],2:[[5],[7],[10]],3:[[4],[6],[9]],4:[[3],[5],[8]],5:[[2],[4],[7]],6:[[2],[3],[6]],7:[[1],[2],[4]]},3: {1:[[4],[6]],2:[[4],[5]],3:[[3],[4]],4:[[3],[3]],5:[[2],[2]],6:[[2],[2]],7:[[1],[1]]},4: [1],5: [6],6: [-1],}; + flags.quality = {I511: 1, I512: 1, I513: 1, I514: 1, I515: 1, I516: 1, I517: 1, I518: 1, I519: 1, I520: 1, I521: 1, I522: 1, I523: 1, I524: 1, I525: 1, I526: 1, I527: 1, I528: 1, I529: 1, I530: 1, I531: 1, I532: 1, I533: 1, I534: 1, I535: 1, I536: 1, I537: 1, I538: 1, I539: 1, I540: 1, I541: 1, I542: 1, I543: 1, I544: 1, I545: 1, I546: 1, I547: 1, I548: 1, I549: 1, I550: 1, I551: 1, I552: 1, I553: 1, I554: 1, I555: 1, I556: 1, I557: 1, I558: 1, I559: 1, I560: 1, I561: 1, I562: 1, I563: 1, I564: 1, I565: 1, I566: 1, I567: 1, I568: 1, I569: 3, I570: 3, I571: 3, I572: 3, I573: 3, I574: 3, I575: 3, I576: 3, I577: 3, I578: 3, I579: 3, I580: 3, I581: 3, I582: 3, I583: 3, I584: 3, I585: 3, I586: 3, I587: 3, I588: 3, I589: 3, I590: 3, I591: 3, I592: 3, I593: 3, I594: 3, I595: 3, I596: 3, I597: 3, I598: 3, I599: 3, I600: 3, I601: 3, I602: 3, I603: 3, I604: 3, I605: 3, I606: 3, I607: 3, I608: 3, I609: 3, I610: 3, I611: 3, I612: 3, I613: 3, I614: 3, I615: 1, I616: 1, I617: 3, I618: 3, I619: 3, I620: 2, I621: 1, I622: 1, I623: 1, I624: 1, I625: 1, I626: 1, I627: 1, I628: 1, I629: 3, I630: 4, I631: 1, I632: 1, I633: 1, I634: 1, I635: 1, I636: 1, I637: 1, I638: 1, I639: 1, I640: 1, I641: 1, I642: 1, I643: 1, I644: 1, I645: 1, I646: 1, I647: 1, I648: 1, I649: 1, I650: 1, I651: 1, I652: 1, I653: 1, I654: 1, I655: 1, I656: 1, I657: 1, I658: 1, I659: 1, I660: 1, I661: 1, I662: 1, I663: 1, I664: 1, I665: 1, I666: 1, I667: 1, I668: 1, I669: 1, I670: 1, I671: 1, I672: 1, I673: 1, I674: 1, I675: 1, I676: 1, I677: 1, I678: 1, I679: 1, I680: 1, I681: 1, I682: 1, I683: 1, I684: 1, I685: 1, I686: 1, I687: 1, I688: 1, I689: 1, I690: 1, I691: 1, I692: 1, I693: 1, I694: 1, I695: 1, I696: 1, I697: 1, I698: 1, I699: 1, I700: 1, I701: 1, I702: 1, I703: 1, I704: 1, I705: 1, I706: 1, I707: 1, I708: 1, I709: 2, I710: 1, I711: 1, I712: 1, I713: 1, I714: 1, I715: 1, I716: 1, } + flags.structure = {水井:[[897,0,897],[0,0,0],[897,0,897],], 村庄小屋:[[360,360,360,360,360,],[360,414,424,419,360,],[360,0,0,0,360,],[360,415,0,420,360,],[360,360,371,360,360,],], 村庄农田:[[360,360,360,360,360,],[360,918,0,918,360,],[360,919,0,918,360,],[360,920,0,918,360,],[360,360,360,360,360,],], 村庄铁匠铺:[[916,360,360,360,360,360,360,916,],[360,0,0,0,0,0,0,360,],[360,0,420,0,0,0,0,360,],[916,916,916,916,360,0,0,360,],[916,0,0,916,360,0,0,360,],[916,916,916,415,360,371,371,360,],], 雪屋:[[373,342,342,342,342,342,375,],[342,342,342,342,342,342,342,],[342,342,414,0,419,342,342,],[342,342,0,0,0,342,342,],[342,342,424,0,415,342,342,],[372,342,342,0,342,342,372,],[0, 0,342,371,342, 0,0,],], 前哨站:[[0,916,362,362,362,0,362,362,362,916,0,],[916,362,424,0,362,362,362,0,424,362,916,],[362,0,362,0,0,0,0,0,362,0,362,],[362, 0, 0, 0, 0, 0, 0, 0, 0, 0,362,],[362,362,0,0,916,916,916,0,0,362,362,],[0,362,0,0,916,420,916,0,0,362,0,],[362,362,0,0,916,916,916,0,0,362,362,],[362,0,0,0,0,0,0,0,0,0,362,],[362,0,362,0,0,0,0,0,362,0,362,],[916,362,424,0,362,362,362,0,424,362,916,],[0,916,362,362,362,0,362,362,362,916,0,],], 沉船:[[0,0,0,0,365,365,365,365,365,365,365,365,365,365,365,365,365,365,0,0,0,0,],[0,365,365,365,365,365,367,367,367,367,367,365,367,367,367,367,367,367,365,365,365,365,0,0,0,0,0,],[365,365,365,365,365,365,367,362,362,362,367,367,367,362,360,420,362,367,367,367,367,367,365,365,365,0,0,],[365,365,365,365,365,365,367,360,362,360,362,360,362,362,362,362,360,367,367,367,367,367,367,367,367,367,367,],[365,365,365,365,365,365,367,360,420,362,367,367,367,360,360,362,362,367,367,367,367,367,365,365,365,0,0,],[0,365,365,365,365,365,367,367,367,367,367,365,367,367,367,367,367,367,365,365,365,365,0,0,0,0,0,],[0,0,0,0,365,365,365,365,365,365,365,365,365,365,365,365,365,365,0,0,0,0,],], 废墟:[[],], 岩浆池:[[],], 废弃传送门:[[],], 沙漠神殿:[[0,0,0,0,0,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,0,0,0,0,0],[0,0,0,0,0,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,897,0,0,0,0,0],[0,0,0,0,0,897,897,0,0,0,0,0,0,0,0,0,0,0,0,0,897,897,0,0,0,0,0],[0,0,0,0,0,897,897, 0,897,897,897,897,897, 897,897,897,897,897,897,0,897,897,0,0,0,0,0 ],[0,0,0,0,0,897,897,0,897,0,0,0,0,0,0,0,0,0,897,0,897,897,0,0,0,0,0 ],[0,0,0,0,0,897,897,0,897, 0,897,897,897, 928,897,897,897,0,897,0,897,897,0,0,0,0,0 ],[0,0,0,0,0,897,897,0,897,0,897,0,0,0,0,0,897,0,897,0,897,897,0,0,0,0,0 ],[0,0,0,0,0,897,897,0,897,0,897,0,897,928,897,0,897,0,897,0,897,897,0,0,0,0,0 ],[0,0,0,0,0,897,897,0,928,0,897,0,928,89,928,0,897,0,928,0,897,897,0,0,0,0, ],[0,0,897,897,897,897,897,0,897,0,897,0,897,928,897,0,897,0,897,0,897,897,897,897,897,0,0 ],[0,0,897,897,897,897,897,0,897,0,897,0,0,0,0,0,897,0,897,0,897,897,897,897,897,0,0 ],[0,897,897,897,897,897,897,0,897,0,897,897,897,928,897,897,897,0,897,0,897,897,897,897,897,897,0 ],[897,897,925,925,925,897,897,0,897,0,0,0,0,0,0,0,0,0,897,0,897,897,925,925,925,897,897 ],[897,897,925,897,925,897,897,0,897,897,897,897,897,897,897,897,897,897,897,0,897,897,925,897,925,897,897 ],[897,897,897,925,897,897,897,0,0,0,0,0,0,0,0,0,0,0,0,0,897,897,897,925,897,897,897 ],[897,897,925,897,925,897,897,897,897,897,897,897,925,928,925,897,897,897,897,897,897,897,925,897,925,897,897 ],[897,897,897,925,897,897,897,897,897,897,897,897,897,928,897,897,897,897,897,897,897,897,897,925,897,897,897 ],[897,897,897,925,897,897,897,0,0,0,0,897,0,0,0,897,0,0,0,0,897,897,897,925,897,897,897 ],[897,897,897,897,897,897,897,0,0,0,0,0,0,0,0,0,0,0,0,0,897,897,897,897,897,897,897],], 丛林神庙:[[0,0,926,0,926,0,926,0,926,0,926,0,926,],[926,923,923,923,923,923,926,926,926,923,923,923,923,923,926,],[926,923,926,926,926,926,926,926,926,926,926,926,926,923,926,],[0,923,926,926,923,923,923,927,923,923,926,923,926,923,0,],[926,923,926,926,923,0,926,0,923,926,926,923,926,923,926,],[926,923,926,923,923,926,923,926,923,923,923,923,926,923,926,],[0,923,926,927,0,926,923,926,0,926,0,927,926,923,0,],[926,923,926,923,926,923,923,923,923,926,923,923,926,923,926,],[926,923,926,923,926,926,926,926,926,926,0,923,926,923,926,],[0,923,926,923,926,923,923,0,923,923,926,923,926,923,0,],[926,923,926,923,926,923,926,926,926,923,926,923,926,923,926,],[926,923,926,927,0,923,926,420,926,923,926,923,926,923,926,],[0,923,926,923,926,923,926,926,926,923,926,923,926,923,0,],[926,923,926,923,926,923,923,0,923,923,926,923,926,923,926,],[926,923,926,923,0,926,926,926,926,926,0,927,926,923,926,],[0,923,926,923,923,923,923,923,923,923,923,923,926,923,0,],[926,923,926,926,926,926,926,926,926,926,926,926,926,923,926,],[926,923,923,923,923,923,926,926,926,923,923,923,923,923,926,],[0,0,926,0,926,0,926,0,926,0,926,0,926,],], 海底神殿:[[908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,],[908,907,907,908,908,907,907,907,908,908,907,907,907,908,908,908,908,908,908,908,908,907,907,907,908,908,907,907,907,908,908,907,907,908,],[908,907,0,0,0,0,0,0,908,908,907,907,907,908,0,0,0,0,0,0,908,907,907,907,908,908,0,0,0,0,0,0,907,908,],[908,908,0,907,908,908,908,0,908,908,907,907,907,908,0,0,0,0,0,0,908,907,907,907,908,908,0,908,908,908,907,0,908,908,],[908,907,0,0,0,908,908,0,908,908,908,908,908,908,0,909,908,908,909,0,908,908,908,908,908,908,0,908,908,0,0,0,907,908,],[908,907,907,908,0,0,0,0,0,0,0,0,0,0,0,908,900,0,908,0,0,0,0,0,0,0,0,0,0,0,908,907,907,908,],[908,908,908,908,0,0,908,908,908,908,908,908,908,908,908,908,0,900,908,908,908,908,908,908,908,908,908,908,0,0,908,908,908,908,],[908,907,907,908,0,0,907,907,907,0,0,0,907,0,0,0,0,0,0,0,0,907,0,0,0,907,907,907,0,0,908,907,907,908,],[908,907,907,908,0,0,907,908,908,0,908,0,908,0,908,0,907,907,0,908,0,908,0,908,0,908,908,907,0,0,908,907,907,908,],[908,908,0,0,0,0,0,908,0,0,907,0,0,0,907, 0,907,907,0,907,0,0,0,907,0,0,908,0,0,0,0,0,908,908,],[908,907,907,908,908,908,0,908,0,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,0,908,0,908,908,908,907,907,908,],[908,907,907,909,909,908,0,908,0,909,907,907,907,907,907,907,907,907,907,907,907,907,907,907,909,0,908,0,908,909,909,907,907,908,],[908,908,909,904,909,908,0,908,0,909,907,909,909,909,909,909,909,909,909,909,909,909,909,907,909,0,908,0,908,909,904,909,908,908,],[908,907,907,909,909,908,0,908,0,909,907,909,0,0,0,0,0,0,0,0,0,0,909,907,909,0,908,0,908,909,909,907,907,908,],[908,907,907,908,908,908,0,908,0,909,907,909,0,909,909,0,0,0,0,909,909,0,909,907,909,0,908,0,908,908,908,907,907,908,],[908,908,0,0,0,0,0,908,0,909,907,909,0,909,909,0,0,0,0,909,909,0,909,907,909,0,908,0,0,0,0,0,908,908,],[908,907,907,908,0,0,908,0,0,909,907,909,0,0,0,0,0,0,0,0,0,0,909,907,909,0,0,908,0,0,908,907,907,908,],[908,907,907,908,0,0,907,0,908,909,907,909,0, 0,0,0,0,0,0,0,0,0,909,907,909,908,0,907,0,0,908,907,907,908,],[908,908,908,908,0,0,907,0,908,909,907,909,907, 0,0,0,0,0,0,0,0,907,909,907,909,908,0,907,0,0,908,908,908,908,],[908,907,907,908,0,0,907,0,908,909,907,909,907,0,0,0,0,0,0,0,0,907,909,907,909,908,0,907,0,0,908,907,907,908,],[908,907,907,908,0,0,908,0,0,909,907,909,907,909,909,0,0,0,0,909,909,907,909,907,909,0,0,908,0,0,908,907,907,908,],[908,908,0,0,0,0,0,908,0,909,907,909,907,909,909,0,0,0,0,909,909,907,909,907,909,0,908,0,0,0,0,0,908,908,],[908,908,0,909,908,909,0,908,0,909,907,909,907,907,907,907,0,0,907,907,907,907,909,907,909,0,908,0,909,908,909,0,907,908,],[908,908,0,908,907,908,0,908,0,909,907,909,909,909,909,909,909,0,909,909,909,909,909,907,909,0,908,0,908,907,908,0,907,908,],[908,908,0,908,907,908,0,908,0,909,907,907,907,907,907,909,0,0,909,907,907,907,907,907,909,0,908,0,908,907,908,0,908,908,],[908,908,0,909,908,909,0,908,0,909,909,909,909,909,909,909,0,909,909,909,909,909,909,909,909,0,908,0,909,908,909,0,907,908,],[908,908,0,0,0,0,0,908,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,908,0,0,0,0,0,907,908,],[908,908,908,908,0,0,908,908,0,908,908,0,0,908,908,908,908,908,908,908,908,0,0,908,908,0,908,908,0,0,908,908,908,908,],[908,907,907,908,0,0,908,908,0,908,908,0,0,0,0,0,908,908,0,0,0,0,0,908,908,0,908,908,0,0,908,907,907,908,],[908,907,0,0,0,0,0,0,0,908,908,908,908,908,0,0,0,0,0,0,908,908,908,908,908,0,0,0,0,0,0,0,907,908,],[908,908,0,907,0,908,908,908,908,908,908,0,0,0,0,0,908,908,0,0,0,0,0,908,908,908,908,908,908,0,907,0,908,908,],[908,907,0,0,0,907,908,907,907,908,908,0,0,908,908,908,0,0,908,908,908,0,0,908,908,907,907,908,907,0,0,0,907,908,],[908,907,907,908,907,907,908,907,907,908,908,0,0,0,0,0,908,908,0,0,0,0,0,908,908,907,907,908,907,907,908,907,907,908,],[908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,0,0,0,0,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,],], } + flags.structurebg = {水井:[[0,0,0],[0,348,0],[0,0,0],], 村庄小屋:[[365,365,365,365,365,],[365,365,365,365,365,],[365,365,365,365,365,],[365,365,365,365,365,],[365,365,365,365,365,],], 村庄农田:[[0,0,0,0,0,],[0,426,348,426,0,],[0,426,348,426,0,],[0,426,348,426,0,],[0,0,0,0,0,],], 村庄铁匠铺:[[365,365,365,365,365,365,365,365,],[365,365,365,365,365,365,365,365,],[365,365,365,365,365,365,365,365,],[0,0,0,0,0,365,365,365,],[0,910,910,0,0,365,365,365,],[0,0,0,0,0,0,0,0,],], 雪屋:[[0,0,0,0,0,0,0,],[0,0,0,0,0,0,0,],[0,0,898,898,898,0,0,],[0,0,898,898,898,0,0,],[0,0,898,898,898,0,0,],[0,0,898,898,898,0,0,],[0,0,0,0,0,0,0,],], 前哨站:[[0,0,0,0,0,0,0,0,0,0,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,366,366,366,366,366,366,366,366,366,0,],[0,0,0,0,0,0,0,0,0,0,0,],], 岩浆池:[[],], 废弃传送门:[[],], } + flags.chestloot = {蘑菇宝箱:[[12,18], ["I682", [1,3], 100], ["I683", [1,3], 100], ["I523", [1,2], 50], ["I524", [1,2], 50], ["I525", [1,2], 50], ["I526", [1,2], 50], ["I530", [1,2], 50], ["I531", [1,2], 50], ["I532", [1,2], 20], ["I620", [1,1], 10], ["I630", [1,1], 10], ], 平原村庄宝箱:[[6,12], ["I626", [1,3], 100], ["I628", [1,3], 100], ["I623", [1,3], 100], ["I640", [1,2], 50], ["I642", [1,2], 50], ["I544", [1,2], 30], ["I676", [1,3], 100], ["I673", [1,3], 50], ["I671", [2,5], 50], ["I533", [1,2], 100], ], 沙漠村庄宝箱:[[6,12], ["I625", [1,3], 50], ["I637", [1,2], 100], ["I626", [1,3], 50], ["I627", [1,3], 50], ["I646", [1,2], 50], ["I672", [2,4], 50], ["I673", [1,2], 50], ["I533", [1,3], 100], ], 针叶林村庄宝箱:[[6,12], ["I551", [1,3], 50], ["I546", [1,2], 50], ["I626", [1,3], 100], ["I623", [1,3], 100], ["I634", [1,2], 100], ["I644", [1,2], 50], ["I533", [1,2], 100], ["I669", [3,7], 30], ], 铁匠铺宝箱:[[8,12], ["I626", [1,3], 200], ["I628", [1,2], 200], ["I581", [1,1], 5], ["I602", [1,1], 5], ["I603", [1,1], 5], ["I604", [1,1], 5], ["I605", [1,1], 5], ["I519", [1,2], 100], ["I527", [1,2], 200], ["I532", [1,3], 5], ], 前哨站宝箱:[[22,30], ["I549", [2,5], 100], ["I550", [2,5], 100], ["I551", [2,5], 100], ["I552", [2,5], 100], ["I553", [2,5], 100], ["I625", [3,6], 50], ["I623", [3,6], 50], ["I627", [3,6], 50], ["I614", [1,1], 20], ["I615", [8,16], 100], ["I675", [2,4], 50], ["I533", [2,4], 300], ["I717", [1,1], 30], ], 沙漠神殿宝箱:[[22,30], ["I513", [2,5], 200], ["I514", [2,5], 200], ["I696", [2,3], 200], ["I675", [2,3], 200], ["I667", [2,3], 200], ["I668", [2,3], 200], ["I527", [2,4], 100], ["I669", [5,10], 200], ["I533", [1,2], 100], ["I532", [1,2], 30], ["I629", [1,1], 15], ["I630", [1,1], 5], ], 丛林神庙宝箱:[[22,30], ["I667", [2,4], 100], ["I696", [2,4], 100], ["I681", [1,3], 100], ["I674", [2,4], 100], ["I524", [1,4], 200], ["I530", [4,8], 300], ["I532", [1,2], 30], ["I533", [1,4], 150], ["I630", [1,1], 10], ], 海底神殿宝箱:[[18,27], ["I648", [2,4], 100], ["I650", [2,4], 100], ["I539", [1,2], 100], ["I534", [1,3], 100], ["I532", [1,3], 100], ["I629", [1,1], 20], ["I630", [1,1], 10], ], 沉船宝箱:[[12,24], ["I623", [1,2], 50], ["I625", [2,3], 50], ["I627", [1,2], 50], ["I696", [1,2], 50], ["I671", [1,2], 50], ["I668", [1,2], 50], ["I677", [1,3], 50], ["I673", [1,2], 50], ["I657", [1,1], 5], ["I706", [1,2], 50], ["I523", [1,2], 50], ["I524", [1,2], 40], ["I526", [1,2], 10], ["I531", [1,6], 40], ["I533", [1,4], 40], ["I717", [1,1], 10], ], 陆地废墟宝箱:[[6,10], ["I625", [1,3], 100], ["I696", [1,2], 100], ["I677", [1,3], 100], ["I673", [1,2], 100], ["I675", [1,3], 100], ["I668", [1,3], 100], ["I706", [1,2], 50], ["I523", [1,2], 50], ["I669", [4,8], 50], ["I524", [1,2], 50], ["I525", [1,2], 50], ], 海底废墟宝箱:[[6,12], ["I648", [1,3], 100], ["I650", [1,3], 100], ["I625", [1,3], 100], ["I653", [1,1], 20], ["I671", [1,3], 100], ["I523", [1,2], 50], ["I669", [4,8], 50], ["I524", [1,2], 50], ["I525", [1,2], 50], ], 废弃传送门宝箱:[[4,8], ["I674", [1,6], 400], ["I606", [1,1], 10], ["I607", [1,1], 10], ["I608", [1,1], 10], ["I609", [1,1], 10], ["I572", [1,1], 20], ["I577", [1,1], 20], ["I582", [1,1], 20], ["I587", [1,1], 20], ["I592", [1,1], 20], ["I669", [1,8], 400], ["I633", [1,2], 50], ["I632", [1,2], 50], ["I629", [1,1], 10], ["I630", [1,1], 5], ], 初始奖励箱:[[15,18], ["I544", [1,3], 100], ["I548", [1,3], 100], ["I545", [1,3], 100], ["I549", [1,3], 100], ["I550", [1,3], 100], ["I554", [1,9], 100], ["I555", [1,9], 100], ["I628", [1,4], 100], ["I634", [1,4], 100], ["I574", [1,1], 10], ["I579", [1,1], 10], ["I584", [1,1], 10], ["I569", [1,1], 10], ["I617", [1,1], 10], ["I594", [1,1], 10], ["I595", [1,1], 10], ["I596", [1,1], 10], ["I597", [1,1], 10], ], } + flags.enchantlist1 = {木剑: ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ],石剑: ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ],铁剑: ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ],金剑: ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ],钻石剑: ["经验修补", "耐久", "锋利", "海洋杀手", "天空杀手", "无视防御", "横扫之刃", "抢夺", ],木斧: ["经验修补", "耐久", "效率", ],石斧: ["经验修补", "耐久", "效率", ],铁斧: ["经验修补", "耐久", "效率", ],金斧: ["经验修补", "耐久", "效率", ],钻石斧: ["经验修补", "耐久", "效率", ],木镐: ["经验修补", "耐久", "时运", "效率", ],石镐: ["经验修补", "耐久", "时运", "效率", ],铁镐: ["经验修补", "耐久", "时运", "效率", ],金镐: ["经验修补", "耐久", "时运", "效率", ],钻石镐: ["经验修补", "耐久", "时运", "效率", ],木锹: ["经验修补", "耐久", "效率", ],石锹: ["经验修补", "耐久", "效率", ],铁锹: ["经验修补", "耐久", "效率", ],金锹: ["经验修补", "耐久", "效率", ],钻石锹: ["经验修补", "耐久", "效率", ],木锄: ["经验修补", "耐久", ],石锄: ["经验修补", "耐久", ],铁锄: ["经验修补", "耐久", ],金锄: ["经验修补", "耐久", ],钻石锄: ["经验修补", "耐久", ],弓: ["经验修补", "耐久", "力量", "暴击", "真实伤害", "轻巧", "射程", ],钓鱼竿: ["经验修补", "耐久", ],皮革帽子: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下呼吸", ],铜头盔: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下呼吸", ],铁头盔: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下呼吸", ],金头盔: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下呼吸", ],钻石头盔: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下呼吸", ],皮革胸甲: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下速掘", ],铜胸甲: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下速掘", ],铁胸甲: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下速掘", ],金胸甲: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下速掘", ],钻石胸甲: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "水下速掘", ],皮革护腿: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", ],铜护腿: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", ],铁护腿: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", ],金护腿: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", ],钻石护腿: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", ],皮革靴子: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "凌波", ],铜靴子: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "凌波", ],铁靴子: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "凌波", ],金靴子: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "凌波", ],钻石靴子: ["经验修补", "耐久", "保护", "爆炸保护", "弹射物保护", "荆棘", "韧性", "凌波", ],木盾: ["经验修补", "耐久", ],铜盾: ["经验修补", "耐久", ],钻石盾: ["经验修补", "耐久", ]} + flags.enchantlist2 = {木剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],石剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石剑: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],木斧: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],石斧: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁斧: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金斧: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石斧: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],木镐: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],石镐: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁镐: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金镐: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石镐: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],木锹: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],石锹: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁锹: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金锹: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石锹: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],木锄: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],石锄: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁锄: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金锄: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石锄: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],弓: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钓鱼竿: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],皮革帽子: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铜头盔: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁头盔: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金头盔: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石头盔: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],皮革胸甲: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铜胸甲: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁胸甲: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金胸甲: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石胸甲: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],皮革护腿: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铜护腿: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁护腿: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金护腿: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石护腿: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],皮革靴子: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铜靴子: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铁靴子: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],金靴子: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石靴子: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],木盾: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],铜盾: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],],钻石盾: [["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],["",0,0],]} + flags.enchantlv = {锋利: 5, 海洋杀手: 5, 天空杀手: 5, 无视防御: 2, 横扫之刃: 2, 力量: 3, 暴击: 1, 真实伤害: 1, 轻巧: 2, 射程: 1, 保护: 5, 爆炸保护: 3, 弹射物保护: 3, 荆棘: 2, 韧性: 1, 经验修补: 3, 耐久: 3, 时运: 3, 抢夺: 3, 效率: 5, 水下呼吸: 3, 水下速掘: 4, 凌波: 1, } + flags.enchantmutex = {锋利:["海洋杀手","天空杀手"], 海洋杀手:["锋利","天空杀手"], 天空杀手:["锋利","海洋杀手"], 无视防御:["横扫之刃"], 横扫之刃:["无视防御"], 暴击:["真实伤害"], 真实伤害:["暴击"], 保护:["爆炸保护","弹射物保护"], 爆炸保护:["保护","弹射物保护"], 弹射物保护:["保护","爆炸保护"]} + flags.task1 = {1:["砍伐树木",50,[1,"橡木原木","I549",5]],2:["砍伐树木",95,[1,"橡木原木","I549",9]],3:["砍伐树木",190,[1,"橡木原木","I549",28]],4:["杀怪",50,[2,"经验","exp",100]],5:["杀怪",100,[2,"经验","exp",300]],6:["开采铁矿",50,[1,"粗铁","I524",5]],7:["开采铁矿",100,[1,"粗铁","I524",15]],8:["开采金矿",50,[1,"粗金","I526",4]],9:["开采金矿",100,[1,"粗金","I526",12]],10:["开采红石矿",50,[1,"红石","I530",10]],11:["开采红石矿",100,[1,"红石","I530",25]],12:["开采钻石矿",10,[1,"钻石","I532",1]],13:["开采钻石矿",20,[1,"钻石","I532",2]],14:["收割小麦",55,[1,"面包","I626",6]],15:["收割小麦",110,[1,"面包","I626",13]],16:["收割小麦",220,[1,"面包","I626",38]],17:["收割马铃薯",50,[1,"烤马铃薯","I624",5]],18:["收割马铃薯",100,[1,"烤马铃薯","I624",10]],19:["收割马铃薯",200,[1,"烤马铃薯","I624",30]],20:["收割胡萝卜",100,[1,"金胡萝卜","I633",4]],21:["收割胡萝卜",200,[1,"金胡萝卜","I633",10]],22:["熔炉烧制",95,[1,"煤炭","I523",4]],23:["熔炉烧制",195,[1,"煤炭块","I535",1]],} + flags.achievement = {"Minecraft":["在物品栏中获得工作台",0,1,"历程类"],"石器时代":["在物品栏中获得圆石",0,1,"历程类"],"整装上阵":["在物品栏中获得任意铜质盔甲",0,1,"历程类"],"来硬的":["在物品栏中获得铁锭",0,1,"历程类"],"冰桶挑战":["在物品栏中获得黑曜石",0,1,"历程类"],"钻石!":["在物品栏中获得钻石",0,1,"历程类"],"钻石护体":["在物品栏中获得任意钻石盔甲",0,1,"历程类"],"甜蜜的梦":["在床上睡觉以改变你的重生点",0,1,"历程类"],"开荒垦地":["种下一颗种子",0,1,"历程类"],"冒险家":["打开一个战利品箱",0,1,"历程类"],"打发时间":["玩上100天(约等于现实中40个小时)",0,1,"历程类"],"伐木工":["累计砍伐50棵树",0,50,"收集类"],"伐伐木工":["累计砍伐200棵树",0,200,"收集类"],"伐伐伐木工":["累计砍伐1000棵树",0,1000,"收集类"],"伐伐伐伐木工":["累计砍伐5000棵树",0,5000,"收集类"],"熟能生巧":["累计合成50次",0,50,"收集类"],"能工巧匠":["累计合成200次",0,200,"收集类"],"巧夺天工":["累计合成1000次",0,1000,"收集类"],"匠心独运":["累计合成5000次",0,5000,"收集类"],"怪物猎人":["累计击杀1只敌怪",0,1,"收集类"],"怪物猎人Ⅱ":["累计击杀100只敌怪",0,100,"收集类"],"怪物猎人Ⅲ":["累计击杀500只敌怪",0,500,"收集类"],"怪物猎人Ⅳ":["累计击杀2000只敌怪",0,2000,"收集类"],"怪物猎人Ⅴ":["累计击杀10000只敌怪",0,10000,"收集类"],"白手起家":["累计获得100铜币",0,100,"收集类"],"小有积蓄":["累计获得1,000铜币",0,1000,"收集类"],"家财万贯":["累计获得10,000铜币",0,10000,"收集类"],"富甲一方":["累计获得100,000铜币",0,100000,"收集类"],"富可敌国":["累计获得1,000,000铜币",0,1000000,"收集类"],"走近科学":["累计升级科技1次",0,1,"收集类"],"走近科学第二集":["累计升级科技20次",0,20,"收集类"],"走近科学第三集":["累计升级科技100次",0,100,"收集类"],"走近科学第四集":["累计升级科技500次",0,500,"收集类"],"走近科学第五集":["累计升级科技1000次",0,1000,"收集类"],"于沙漠中绽放的玫瑰":["找到并采摘一株仙人掌花",0,1,"挑战类"],"均衡饮食":["尝遍天下食材,即便是对身体不好的",0,9999,"挑战类"],"赛博朋克街区":["升级到世界等级十",0,1,"挑战类"],"狂战士":["使基础攻击大于生命",0,1,"挑战类"],"漂流者":["连续吃上30个海带",0,30,"彩蛋类"],"粉丝团":["使场上同时存在15只敌怪",0,1,"彩蛋类"],"世界与你同在":["发现地球彩蛋",0,1,"彩蛋类"],"头号玩家":["通关二周目",0,1,"彩蛋类"],} + flags.cavelayer = {陶瓦洞穴:[[929,800],[403,60],[405,60],[404,20],[406,90],], 砂岩洞穴:[[928,500],[349,180],[403,70],[405,70],[404,25],[406,10],[407,120],[410,10],], 繁茂洞穴:[[921,400],[339,100],[349,100],[432,100],[431,100],[403,50],[405,60],[404,15],[406,5],[408,25],[410,5],], 水晶洞穴:[[302,300],[346,900],[407,50],[409,5],[411,100],], 深层洞穴:[[302,200],[346,400],[404,30],[406,30],[408,30],[407,120],[409,10],], 普通洞穴:[[346,500],[349,140],[403,100],[405,100],[404,20],[406,10],[408,20],[407,50],], } + flags.refenemy = {前期: {草地: {白天: [["E1",10],["E5",10],],夜晚: [["E9",10],["E21",10],["E25",10],["E33",10],["E37",2],],},雪林: {白天: [["E1",10],["E5",10],["E41",20],],夜晚: [["E9",10],["E21",10],["E25",10],["E33",10],["E37",2],["E41",10],],},沙漠: {白天: [["E13",10],["E37",2],],夜晚: [["E13",10],["E21",10],["E25",10],["E33",10],["E37",5],],},水下: {白天: [["E17",10],],夜晚: [["E17",10],],},蘑菇岛: {白天: [],夜晚: [],},其他: {白天: [["E1",10],["E5",10],],夜晚: [["E9",10],["E21",10],["E25",10],["E29",1],["E33",10],["E37",2],],},},中期: {草地: {白天: [["E1",10],["E5",10],["E9",10],["E25",2],],夜晚: [["E9",10],["E21",10],["E25",10],["E33",10],["E37",10],["E29",4],],},雪林: {白天: [["E1",10],["E5",10],["E41",20],["E9",10],["E25",2],],夜晚: [["E9",10],["E21",10],["E25",10],["E33",10],["E37",10],["E41",10],["E29",4],],},沙漠: {白天: [["E13",10],["E37",10],["E25",2],],夜晚: [["E13",10],["E21",10],["E25",10],["E33",10],["E37",10],["E29",4],],},水下: {白天: [["E17",10],],夜晚: [["E17",10],],},蘑菇岛: {白天: [["E1",10],["E5",10],],夜晚: [["E1",10],["E5",10],],},其他: {白天: [["E1",10],["E5",10],["E9",10],["E25",2],],夜晚: [["E9",10],["E21",10],["E25",10],["E29",4],["E33",10],["E37",10],],},},后期: {草地: {白天: [["E1",10],["E5",10],["E9",10],["E25",4],],夜晚: [["E9",10],["E21",10],["E25",10],["E33",10],["E37",10],["E29",4],["E53",6],],},雪林: {白天: [["E1",10],["E5",10],["E41",20],["E9",10],["E25",4],],夜晚: [["E9",10],["E21",10],["E25",10],["E33",10],["E37",10],["E41",10],["E29",4],["E53",6],],},沙漠: {白天: [["E13",10],["E37",10],["E25",4],],夜晚: [["E13",10],["E21",10],["E25",10],["E33",10],["E37",10],["E29",4],["E53",6],],},水下: {白天: [["E17",10],["E45",10],],夜晚: [["E17",10],["E45",10],],},蘑菇岛: {白天: [["E1",10],["E5",10],["E25",4],],夜晚: [["E1",10],["E5",10],["E25",10],["E53",6],],},其他: {白天: [["E1",10],["E5",10],["E9",10],["E25",4],],夜晚: [["E9",10],["E21",10],["E25",10],["E29",4],["E33",10],["E37",10],["E53",6],],},},}; + + flags.farmland1 = { + I492: [100, 100, 100, 100, 100, 100, 100, 100, 100, 0, "2种子->3种子", "3种子->3种子", "3种子->4种子", "4种子->4种子", "4种子->5种子", "5种子->5种子", "5种子->6种子", "6种子->7种子", "7种子->8种子", "已满级——8种子", "3小麦->4小麦", "4小麦->5小麦", "5小麦->6小麦", "6小麦->7小麦", "7小麦->8小麦", "8小麦->9小麦", "9小麦->10小麦", "10小麦->11小麦", "11小麦->12小麦", "已满级——12小麦", "200空间->200空间", "200空间->400空间", "400空间->400空间", "400空间->600空间", "600空间->600空间", "600空间->700空间", "700空间->800空间", "800空间->900空间", "900空间->1000空间", "已满级——1000空间", 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 200, 200, 400, 400, 600, 600, 700, 800, 900, 1000], + I508: [100, 100, 100, 100, 100, 100, 100, 100, 100, 0, "", "", "", "", "", "", "", "", "", "", "2胡萝卜->3胡萝卜", "3胡萝卜->3胡萝卜", "3胡萝卜->4胡萝卜", "4胡萝卜->4胡萝卜", "4胡萝卜->5胡萝卜", "5胡萝卜->5胡萝卜", "5胡萝卜->6胡萝卜", "6胡萝卜->7胡萝卜", "7胡萝卜->8胡萝卜", "已满级——8胡萝卜", "200空间->200空间", "200空间->400空间", "400空间->400空间", "400空间->600空间", "600空间->600空间", "600空间->700空间", "700空间->800空间", "800空间->900空间", "900空间->1000空间", "已满级——1000空间", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 200, 200, 400, 400, 600, 600, 700, 800, 900, 1000], + I507: [100, 100, 100, 100, 100, 100, 100, 100, 100, 0, "", "", "", "", "", "", "", "", "", "", "2马铃薯->3马铃薯", "3马铃薯->3马铃薯", "3马铃薯->4马铃薯", "4马铃薯->4马铃薯", "4马铃薯->5马铃薯", "5马铃薯->5马铃薯", "5马铃薯->6马铃薯", "6马铃薯->7马铃薯", "7马铃薯->8马铃薯", "已满级——8马铃薯", "200空间->200空间", "200空间->400空间", "400空间->400空间", "400空间->600空间", "600空间->600空间", "600空间->700空间", "700空间->800空间", "800空间->900空间", "900空间->1000空间", "已满级——1000空间", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 200, 200, 400, 400, 600, 600, 700, 800, 900, 1000], + }; + + if (flags.tip == undefined) flags.tip = [""]; + if (flags.interface == undefined) flags.interface = "物品栏"; + if (flags.bag == undefined) flags.bag = [["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0],]; + if (flags.bagbp == undefined) flags.bagbp = []; + if (flags.cursor1 == undefined) flags.cursor1 = 1; + if (flags.cursor2 == undefined) flags.cursor2 = 0; + if (flags.curCache2 == undefined) flags.curCache2 = [0, 0]; + if (flags.bagCache == undefined) flags.bagCache = 0; + if (flags.cursplit == undefined) flags.cursplit = 0; + if (flags.curdou == undefined) flags.curdou = 0; + if (flags.curlight == undefined) flags.curlight = [[0, 0], [0, 0]]; + if (flags.curarr == undefined) flags.curarr = []; + if (flags.mingan == undefined) flags.mingan = 0; + if (flags.longleft == undefined) flags.longleft = 0; + if (flags.longright == undefined) flags.longright = 0; + if (flags.splitCache2 == undefined) flags.splitCache2 = 0; + if (flags.splitCache1 == undefined) flags.splitCache1 = {}; + if (flags.weaDur == undefined) flags.weaDur = JSON.parse(JSON.stringify(flags.weapon)); + if (flags.toolDur == undefined) flags.toolDur = JSON.parse(JSON.stringify(flags.tool)); + if (flags.equDur == undefined) flags.equDur = JSON.parse(JSON.stringify(flags.equip)); + if (flags.enchantlist3 == undefined) flags.enchantlist3 = JSON.parse(JSON.stringify(flags.enchantlist2)); + if (flags.craftGuide1 == undefined) flags.craftGuide1 = Object.assign({}, flags.craftGuide11, /* flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15, flags.craftGuide16, flags.craftGuide17, flags.craftGuide18, flags.craftGuide19, flags.craftGuide1a*/); + if (flags.craftGuide2 == undefined) flags.craftGuide2 = Object.assign({}, flags.craftGuide21, /* flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25, flags.craftGuide26, flags.craftGuide27, flags.craftGuide28, flags.craftGuide29, flags.craftGuide2a*/); + if (flags.boxlist == undefined) flags.boxlist = {}; + if (flags.furnacelist == undefined) flags.furnacelist = {}; + if (flags.comlist1 == undefined) flags.comlist1 = [["", 0], ["", 0], ["", 0], ["", 0], ["", 0],]; + if (flags.comlist2 == undefined) flags.comlist2 = [["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0],]; + if (flags.time == undefined) flags.time = 480; + if (flags.timePeriod == undefined) flags.timePeriod = ""; + if (flags.enemyold == undefined) flags.enemyold = {}; + if (flags.enemylist == undefined) flags.enemylist = {}; + if (flags.revive == undefined) flags.revive = ["zhu13", 30, 30, "", 0, 0]; + if (flags.revivePo == undefined) flags.revivePo = ["", 0, 0]; + if (flags.property == undefined) flags.property = { + atkSpeed: [84, 1, 0], + touchRange: [1, 2], + atkSea: 0, + atkFly: 0, + igdef1: 0, + igdef2: 0, + crit: 1, + reduceDa: 0, + reduceBoom: 0, + reduceBul: 0, + backDa: 0, + undead: 0, + exp: 0, + }; + if (flags.playerAr == undefined) flags.playerAr = 0; + if (flags.tech == undefined) flags.tech = 0; + if (flags.tech1 == undefined) flags.tech1 = { + 灵感: [1, 10], + 领悟: [1, 11], + 生命之力: [1, 11], + 增幅: [1, 11], + 坚定: [1, 11], + 敏捷: [1, 11], + 耐力: [1, 11], + 空间扩展: [1, 10], + 守护之韧: [1, 11], + 锋锐恒久: [1, 11], + 生机: [1, 11], + 商贾之道: [1, 11], + }; + if (flags.tpointnum == undefined) flags.tpointnum = 0; + if (flags.tpointsum == undefined) flags.tpointsum = 0; + if (flags.coppernum == undefined) flags.coppernum = 0; + if (flags.level == undefined) flags.level = [0, 0, 100]; + if (flags.worldlevel == undefined) flags.worldlevel = 1; + if (flags.CustomKey == undefined) flags.CustomKey = 0; + if (flags.pagenumber == undefined) flags.pagenumber = 1; + if (flags.curKey == undefined) flags.curKey = 0; + if (flags.keylist == undefined) flags.keylist = [["向上移动", 87], ["向左移动", 65], ["向下移动", 83], ["向右移动", 68], ["打开背包", 69], ["打开设置", 80], ["打开大地图", 77], ["打开科技", 84], ["打开指南", -1], ["打开成就", -1], ["按住可快捷的转移物品", 16], ["打开自定义快捷键界面", 75], ["", -1], ["", -1], ["", -1], ["", -1], ["", -1], ["", -1]]; + if (flags.transfer == undefined) flags.transfer = 0; + if (flags.dig == undefined) flags.dig = [0, 0, 0, 0, 0]; + if (flags.box_xy == undefined) flags.box_xy = ""; + if (flags.crops == undefined) flags.crops = {}; + if (flags.farmland2 == undefined) flags.farmland2 = {}; + if (flags.farmUI == undefined) flags.farmUI = 0; + if (flags.hunger == undefined) flags.hunger = [100, 100, 0, 10]; + if (flags.worldlist == undefined) flags.worldlist = { + 0: { + 世界名称: "新的世界", + 世界存档: [], + 世界选项: ["默认", "开", "关", "中"], + 游戏模式: "生存", + 难度: "普通", + 游戏规则: ["开", "开", "开", "开", "开", "开", "开", "开", "开"], + 地形: { + 平原: [1,], + 森林: [1,], + 丛林: [1,], + 针叶林: [1,], + 雪原: [1,], + 沙漠: [1,], + 恶地: [1,], + 海洋: [1,], + 丘陵: [1,], + 蘑菇岛: [1,], + 河流: [1,], + }, + 结构: { + 水井: [1,], + 废墟: [1,], + 岩浆池: [1,], + 废弃传送门: [1,], + 雪屋: [1,], + 前哨站: [1,], + 村庄: [1,], + 沉船: [1,], + 沙漠神殿: [1,], + 丛林神庙: [1,], + }, + 单一群系: ["随机",] + } + }; + if (flags.worldDescribe == undefined) flags.worldDescribe = ""; + if (flags.worldindex == undefined) flags.worldindex = 0; + if (flags.smeltcheck == undefined) flags.smeltcheck = 0; + if (flags.buff == undefined) flags.buff = { + 生命恢复: 0, + 中毒: 0, + 凋零: 0, + + }; + if (flags.dying == undefined) flags.dying = 0; + if (flags.textalpha == undefined) flags.textalpha = []; + if (flags.digcold == undefined) flags.digcold = 0; + if (flags.rightstatus == undefined) flags.rightstatus = ["", ""]; + if (flags.mapicon == undefined) flags.mapicon = { zhu: {}, dong: {}, }; + if (flags.step == undefined) flags.step = 0; + if (flags.digvolume == undefined) flags.digvolume = 0; + if (flags.attrui == undefined) flags.attrui = 0; + if (flags.taskui == undefined) flags.taskui = 0; + if (flags.achiui == undefined) flags.achiui = 0; + if (flags.worldcur == undefined) flags.worldcur = 0; + if (flags.worldcur2 == undefined) flags.worldcur2 = 0; + if (flags.limit == undefined) flags.limit = 100; + if (flags.rand3 == undefined) flags.rand3 = core.plugin.LCGrand(); + if (flags.createrate == undefined) flags.createrate = 1; + if (flags.extraDur == undefined) flags.extraDur = [0, 0]; + if (flags.extraGrow == undefined) flags.extraGrow = 0; + if (flags.extraGrid == undefined) flags.extraGrid = 0; + if (flags.worldpage == undefined) flags.worldpage = 1; + if (flags.breathe == undefined) flags.breathe = [10, 10]; + if (flags.lavaocc == undefined) flags.lavaocc = 0; + if (flags.breocc == undefined) flags.breocc = 0; + if (flags.dimension == undefined) flags.dimension = ["主世界", "地表"]; + if (flags.eatcool == undefined) flags.eatcool = 0; + if (flags.mapview == undefined) flags.mapview = ["dong1", "dong2", "dong3", "dong4", "dong5", "dong6", "dong7", "dong8", "dong9",]; + if (flags.sleep == undefined) flags.sleep = 0; + if (flags.nearstr == undefined) flags.nearstr = {}; + if (flags.bombcenter == undefined) flags.bombcenter = []; + if (flags.discard == undefined) flags.discard = 0; + + + if (!core.getLocalStorage('world')) core.setLocalStorage('world', {}); + if (!core.getLocalStorage('maplist')) core.setLocalStorage('maplist', {}); + + for (var bgm1 in core.material.bgms) { + core.material.bgms[bgm1].loop = false; + } + }; + + function myCallback() { + core.plugin.initVariable(); + } + // 定义一个函数,用于判断条件并执行回调 + var check = function (a, callback) { + if (a == true) { + callback(); // 当a为真时,执行回调函数 + } + else { + setTimeout(function () { check(core.isPlaying(), callback) }, 100);//否则每隔100ms检查一次 + } + } + check(core.isPlaying(), myCallback); + + }, + "2-左键点击": function () { + // 获取元素 + var myElement = document.getElementById('data'); + var lastClickTime = 0; // 用于记录上次点击的时间 + var delay = 200; // 双击间隔时间(毫秒) + // 鼠标按下 + myElement.addEventListener('mousedown', function (e) { + // 获取鼠标双击的绝对坐标 + var absX = e.clientX; + var absY = e.clientY; + // 获取元素的相对位置 + var rect = myElement.getBoundingClientRect(); + // 计算相对坐标 + var rx = Math.ceil((absX - rect.left) * 416 / 415); + var ry = Math.ceil((absY - rect.top) * 416 / 415); + // 计算格子坐标 + var mx = Math.ceil(rx / 32) - 1; + var my = Math.ceil(ry / 32) - 1; + // 检查是否为左键按下 + if (e.button === 0) { + // 获取当前点击时间 + var clickTime = new Date().getTime(); + // 计算两次点击之间的时间差 + var timeDiff = clickTime - lastClickTime; + // 更新最后点击时间 + lastClickTime = clickTime; + // 如果两次点击的时间差小于设定的双击间隔时间,则认为是双击 + if (timeDiff < delay) { + // 调用插件的方法来处理左键双击事件 + core.plugin.ondblclicked(mx, my, rx, ry); + // 防止触发单击事件 + e.preventDefault(); + } else { + core.plugin.leftMouseDown(mx, my); + } + } else if (e.button === 2) { + core.plugin.rightMouseDown(); + } + }); + // 鼠标抬起 + myElement.addEventListener('mouseup', function (e) { + // 获取鼠标点击的绝对坐标 + var absX = e.clientX; + var absY = e.clientY; + // 获取元素的相对位置 + var rect = myElement.getBoundingClientRect(); + // 计算相对坐标 + var rx = Math.ceil((absX - rect.left) * 416 / 415); + var ry = Math.ceil((absY - rect.top) * 416 / 415); + // 计算格子坐标 + var mx = Math.ceil(rx / 32) - 1; + var my = Math.ceil(ry / 32) - 1; + //console.log((absY - rect.top)) + if (e.button === 0) { + core.plugin.leftMouseUp(); + core.plugin.onclicked(mx, my, rx, ry); + } else if (e.button === 2) { + core.plugin.rightMouseUp(); + core.plugin.rightClick(mx, my, rx, ry); + } + }); + + // 主程序,当鼠标左键点击时进行判断 + this.onclicked = function (mouse_x, mouse_y, px, py) { + //console.log("左键事件"); + //console.log("左键坐标", mouse_x, mouse_y, px, py); + if (!window.core) return; + if (!core.isPlaying) return; + if (!core.isPlaying()) return; + var offsetx = Math.round(core.bigmap.offsetX / 32); + var offsety = Math.round(core.bigmap.offsetY / 32); + if (flags.interface == "物品栏") { + flags.box_xy = ((mouse_x + offsetx) * 1000 + (mouse_y + offsety)).toString().padStart(6, '0') + core.status.floorId; + } + if (mouse_x == 11 && mouse_y == 12 && core.status.hero.hp > 0 && flags.tech == 0) { + if (flags.interface == "物品栏") { + flags.interface = "背包"; + } else { + if (flags.interface == "箱子" && flags.discard == 0) { + core.playSound("chestclosed.ogg"); + } + flags.interface = "物品栏"; + if (flags.discard == 1) { + delete flags.boxlist[flags.box_xy]; + flags.discard = 0; + } + } + if (flags.interface == "物品栏") { + for (var i = 0; i < 4; i++) { + if (flags.comlist1[i][0]) { + core.addItem(flags.comlist1[i][0], flags.comlist1[i][1]); + flags.comlist1[i] = ["", 0]; + } + } + for (var i = 0; i < 9; i++) { + if (flags.comlist2[i][0]) { + core.addItem(flags.comlist2[i][0], flags.comlist2[i][1]); + flags.comlist2[i] = ["", 0]; + } + } + } + } + core.plugin.invenInter(mouse_x, mouse_y, offsetx, offsety); + flags.cursor2 = core.plugin.clickloc(mouse_x, mouse_y); + core.plugin.bagInter(mouse_x, mouse_y); + core.plugin.boxInter(mouse_x, mouse_y); + core.plugin.workInter(mouse_x, mouse_y); + core.plugin.furInter(mouse_x, mouse_y); + core.plugin.dieInter(mouse_x, mouse_y); + core.plugin.techInter(px, py); + core.plugin.keyInter(px, py); + core.plugin.attrInter(px, py); + core.plugin.taskInter(px, py); + core.plugin.achiInter(px, py); + //core.plugin.farmInter(mouse_x, mouse_y, px, py, offsetx, offsety); + core.plugin.mcjiemian(); + flags.mingan = 0; + core.plugin.beibaocunchu(); + + if (flags.bag[0][0] != "" && flags.bag[0][1] > 0 && flags.interface != "物品栏" && flags.interface != "锁定") { + //core.ui._createUIEvent(); + if (core.getContextByName("viscous") == null) { + core.createCanvas("viscous", 0, 0, 416, 416, 136); + } else { + core.clearMap('viscous'); + } + core.drawIcon("viscous", flags.bag[0][0], px - 16, py - 16); + if (flags.bag[0][1] > 0 && flags.bag[0][1] < 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 20, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[0][1] >= 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 12, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else { + core.deleteCanvas('viscous'); + //core.clearMap('viscous'); + } + }; + // 主程序,当鼠标左键双击时进行判断 + this.ondblclicked = function (mouse_x, mouse_y, px, py) { + if (flags.interface != "锁定" && flags.interface != "物品栏") { + flags.curdou = 1; + flags.cursor2 = core.plugin.clickloc(mouse_x, mouse_y); + core.plugin.bagInter(mouse_x, mouse_y); + core.plugin.workInter(mouse_x, mouse_y); + core.plugin.mcjiemian(); + core.plugin.beibaocunchu(); + if (flags.bag[0][0] != "") { + //core.ui._createUIEvent(); + if (core.getContextByName("viscous") == null) { + core.createCanvas("viscous", 0, 0, 416, 416, 136); + } else { + core.clearMap('viscous'); + } + core.drawIcon("viscous", flags.bag[0][0], px - 16, py - 16); + if (flags.bag[0][1] > 0 && flags.bag[0][1] < 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 20, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[0][1] >= 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 12, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else { + core.deleteCanvas('viscous'); + core.clearMap('viscous'); + } + } + }; + + // 物品栏界面点击事件 + this.invenInter = function (mouse_x, mouse_y, offsetx, offsety) { + if (flags.interface == "物品栏" && core.status.hero.hp > 0) { + for (var i = 2; i < 11; i++) { + if (mouse_x == i && mouse_y == 12) { + flags.cursor1 = i - 1; + break; + } + } + if (!(mouse_x == 2 && mouse_y == 12) && + !(mouse_x == 3 && mouse_y == 12) && + !(mouse_x == 4 && mouse_y == 12) && + !(mouse_x == 5 && mouse_y == 12) && + !(mouse_x == 6 && mouse_y == 12) && + !(mouse_x == 7 && mouse_y == 12) && + !(mouse_x == 8 && mouse_y == 12) && + !(mouse_x == 9 && mouse_y == 12) && + !(mouse_x == 10 && mouse_y == 12) && + !(mouse_x == 11 && mouse_y == 12)) { + core.plugin.applyItem(); + core.plugin.battleDamage(mouse_x, mouse_y, offsetx, offsety); + } + } + } //主手栏为道具时,点击任意位置使用 + this.applyItem = function () { + var id = flags.bag[flags.cursor1][0]; + if (id == "fly") return; + if (core.items.items[id]) { + if ((core.items.items[id].cls == "constants" || core.items.items[id].cls == "tools") && + ("useItemEvent" in core.items.items[id] || "useItemEffect" in core.items.items[id]) && + flags.eatcool == 0) { + if (core.material.items[id].text == undefined) { + core.useItem(id); + } else if (core.material.items[id].text.slice(0, 2) != "食用" || flags.hunger[1] - flags.hunger[0] >= 5) { + core.useItem(id); + } + } + } + } //根据玩家攻速对攻击范围内的敌怪造成伤害 + this.battleDamage = function (mouse_x, mouse_y, offsetx, offsety) { + if (flags.property.atkSpeed[2]) return; + var x = mouse_x + offsetx; + var y = mouse_y + offsety; + var hx = core.status.hero.loc.x; + var hy = core.status.hero.loc.y; + var ra = 1; + var clickar = 1; + if (y <= hy && Math.abs(x - hx) <= Math.abs(y - hy)) { + clickar = 1; + //core.drawAnimate("atk_u", hx - offsetx, hy - offsety, true); + } else if (x >= hx && Math.abs(x - hx) >= Math.abs(y - hy)) { + clickar = 2; + //core.drawAnimate("atk_r", hx - offsetx, hy - offsety, true); + } else if (y >= hy && Math.abs(x - hx) <= Math.abs(y - hy)) { + clickar = 3; + //core.drawAnimate("atk_d", hx - offsetx, hy - offsety, true); + } else if (x <= hx && Math.abs(x - hx) >= Math.abs(y - hy)) { + clickar = 4; + //core.drawAnimate("atk_l", hx - offsetx, hy - offsety, true); + } + if (core.getBlockCls(x, y) == "enemys" || + core.getBlockCls(x, y) == "enemy48") { + if (Math.abs(hx - x) <= flags.property.touchRange[0] && Math.abs(hy - y) <= flags.property.touchRange[0]) { + if (y <= hy && Math.abs(x - hx) <= Math.abs(y - hy)) { + core.plugin.makehurt(x, y, 1); + } else if (x >= hx && Math.abs(x - hx) >= Math.abs(y - hy)) { + core.plugin.makehurt(x, y, 2); + } else if (y >= hy && Math.abs(x - hx) <= Math.abs(y - hy)) { + core.plugin.makehurt(x, y, 3); + } else if (x <= hx && Math.abs(x - hx) >= Math.abs(y - hy)) { + core.plugin.makehurt(x, y, 4); + } + return; + } + } + if (core.getEquip(0) in flags.weapon) { + do { + for (var i = -ra; i <= ra; i++) { + for (var j = -ra; j <= ra; j++) { + if (core.getBlockCls(hx + i, hy + j) == "enemys" || + core.getBlockCls(hx + i, hy + j) == "enemy48") { + switch (clickar) { + case 1: + if (hy + j <= hy && Math.abs(hx + i - hx) <= Math.abs(hy + j - hy)) { + core.plugin.makehurt(hx + i, hy + j, clickar); + return; + } + break; + case 2: + if (hx + i >= hx && Math.abs(hx + i - hx) >= Math.abs(hy + j - hy)) { + core.plugin.makehurt(hx + i, hy + j, clickar); + return; + } + break; + case 3: + if (hy + j >= hy && Math.abs(hx + i - hx) <= Math.abs(hy + j - hy)) { + core.plugin.makehurt(hx + i, hy + j, clickar); + return; + } + break; + case 4: + if (hx + i <= hx && Math.abs(hx + i - hx) >= Math.abs(hy + j - hy)) { + core.plugin.makehurt(hx + i, hy + j, clickar); + return; + } + break; + } + } + } + } + ra += 1; + } while (ra <= flags.property.touchRange[0]); + } + core.plugin.digBlock(mouse_x, mouse_y, offsetx, offsety); + } //在玩家触碰范围内挖掘方块,前景->事件->背景 + this.digBlock = function (mouse_x, mouse_y, offsetx, offsety) { + if (flags.digcold == 1) return; + if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) != 0 || core.getBlockCls(mouse_x + offsetx, mouse_y + offsety) == "terrains" || core.getFgNumber(mouse_x + offsetx, mouse_y + offsety) != 0) { + var lx, yd; + if (core.getFgNumber(mouse_x + offsetx, mouse_y + offsety) != 0) { + lx = core.getBlockInfo(core.getFgNumber(mouse_x + offsetx, mouse_y + offsety)).blocktype; + yd = core.getBlockInfo(core.getFgNumber(mouse_x + offsetx, mouse_y + offsety)).hardness; + } else if (core.getBlockCls(mouse_x + offsetx, mouse_y + offsety) == "terrains") { + lx = core.getBlockInfo(core.getBlockId(mouse_x + offsetx, mouse_y + offsety)).blocktype; + yd = core.getBlockInfo(core.getBlockId(mouse_x + offsetx, mouse_y + offsety)).hardness; + } else if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) != 0) { + lx = core.getBlockInfo(core.getBgNumber(mouse_x + offsetx, mouse_y + offsety)).blocktype; + yd = core.getBlockInfo(core.getBgNumber(mouse_x + offsetx, mouse_y + offsety)).hardness; + } + for (var i = -flags.property.touchRange[1]; i <= flags.property.touchRange[1]; i++) { + for (var j = -flags.property.touchRange[1]; j <= flags.property.touchRange[1]; j++) { + if (mouse_x + offsetx == core.status.hero.loc.x + i && mouse_y + offsety == core.status.hero.loc.y + j) { + var dig1 = 0; + for (var to in flags.tool) { + if (core.getEquip(0) == to) { + if (lx == 1 || lx == 2 || lx == 3) { + if (lx == flags.tool[to][2]) { + flags.dig[0] = flags.digLevel[lx][flags.tool[to][1]][yd - 1][0]; + dig1 = 1; + } + } else if (lx == 4 || lx == 5 || lx == 6) { + flags.dig[0] = flags.digLevel[lx][0]; + dig1 = 1; + } + break; + } + } + if (dig1 == 0) { + if (lx == 1 || lx == 2 || lx == 3) { + flags.dig[0] = flags.digLevel[lx][1][yd - 1][0]; + } else if (lx == 4 || lx == 5 || lx == 6) { + flags.dig[0] = flags.digLevel[lx][0]; + } + } + for (var cr in flags.cropslist) { + if (core.getBlockNumber(mouse_x + offsetx, mouse_y + offsety) == flags.cropslist[cr][1]) { + delete flags.crops[flags.box_xy]; + } + } + var d2 = flags.dig[2]; + var d3 = flags.dig[3]; + flags.dig[2] = core.status.hero.loc.x + i; + flags.dig[3] = core.status.hero.loc.y + j; + if (d2 == flags.dig[2] && d3 == flags.dig[3]) { + flags.dig[1] += 1; + } else { + flags.dig[1] = 1; + } + flags.dig[0] -= flags.dig[1]; + flags.dig[4] = 2000; + function getnumber(x, y) { + if (core.getFgNumber(x, y) != 0) { + return core.getFgNumber(x, y); + } else if (core.getBlockCls(x, y) == "terrains") { + return core.getBlockNumber(x, y); + } else if (core.getBgNumber(x, y) != 0) { + return core.getBgNumber(x, y); + } + } + if (flags.dig[0] > 0 && (flags.digvolume == 2 || flags.dig[1] == 1)) { + flags.digvolume = 0; + core.plugin.digsound(getnumber(flags.dig[2], flags.dig[3])); + } else { + flags.digvolume += 1; + } + flags.rightstatus = ["图块", getnumber(flags.dig[2], flags.dig[3])]; + if (flags.dig[0] == 0) { + flags.dig = [0, 0, 0, 0, 0]; + var number; + function gitem(number) { + if (flags.wfk[number][0] && flags.wfk[number][0][2] > core.rand2(100)) { + core.addItem(flags.wfk[number][0][0], flags.wfk[number][0][1]); + //core.playSound("pop.ogg"); + if (flags.wfk[number][0][0] == "I705") { + flags.level[1] += flags.wfk[number][0][1]; + core.plugin.jingyandengji(); + } + } + if (flags.wfk[number][1] && flags.wfk[number][1][2] > core.rand2(100)) { + core.addItem(flags.wfk[number][1][0], flags.wfk[number][1][1]); + //core.playSound("pop.ogg"); + if (flags.wfk[number][1][0] == "I705") { + flags.level[1] += flags.wfk[number][1][1]; + core.plugin.jingyandengji(); + } + } + if (flags.wfk[number][2] && flags.wfk[number][2][2] > core.rand2(100)) { + core.addItem(flags.wfk[number][2][0], flags.wfk[number][2][1]); + //core.playSound("pop.ogg"); + if (flags.wfk[number][1][0] == "I705") { + flags.level[1] += flags.wfk[number][1][1]; + core.plugin.jingyandengji(); + } + } + } + if (core.getFgNumber(mouse_x + offsetx, mouse_y + offsety) != 0) { + number = core.getFgNumber(mouse_x + offsetx, mouse_y + offsety); + gitem(number); + if (number == "433") { + var rand1 = core.rand2(3); + var sound; + switch (rand1) { + case 0: + sound = "glass1.ogg"; + break; + case 1: + sound = "glass2.ogg"; + break; + case 2: + sound = "glass3.ogg"; + break; + } + core.playSound(sound); + } else { + core.plugin.putsound(number); + } + core.setBgFgBlock('fg', 0, mouse_x + offsetx, mouse_y + offsety); + } else if (core.getBlockCls(mouse_x + offsetx, mouse_y + offsety) == "terrains") { + number = core.getBlockNumber(mouse_x + offsetx, mouse_y + offsety); + gitem(number); + if (number == "433") { + var rand1 = core.rand2(3); + var sound; + switch (rand1) { + case 0: + sound = "glass1.ogg"; + break; + case 1: + sound = "glass2.ogg"; + break; + case 2: + sound = "glass3.ogg"; + break; + } + core.playSound(sound); + } else { + core.plugin.putsound(number); + } + core.removeBlock(core.status.hero.loc.x + i, core.status.hero.loc.y + j); + } else if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) != 0) { + number = core.getBgNumber(mouse_x + offsetx, mouse_y + offsety); + gitem(number); + if (number == "433") { + var rand1 = core.rand2(3); + var sound; + switch (rand1) { + case 0: + sound = "glass1.ogg"; + break; + case 1: + sound = "glass2.ogg"; + break; + case 2: + sound = "glass3.ogg"; + break; + } + core.playSound(sound); + } else { + core.plugin.putsound(number); + } + core.setBgFgBlock('bg', 0, mouse_x + offsetx, mouse_y + offsety); + } + core.plugin.cunchushuju(mouse_x + offsetx, mouse_y + offsety, core.status.floorId); + core.plugin.reduceDur("wea"); + core.plugin.reduceDur("tool"); + core.plugin.baoshidu(); + flags.digcold = 1; + setTimeout(function () { + flags.digcold = 0; + }, 200); + } else if (flags.dig[0] < 0) { + flags.dig[0] = 0; + flags.dig[1] = 0; + } + core.plugin.wajuejindu(); + } + } + } + } else if (flags.worldlevel >= 2 && core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == 0 || core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == 300) { + if (mouse_x + offsetx != core.status.hero.loc.x || mouse_y + offsety != core.status.hero.loc.y) return; + function godown(floor) { + core.changeFloor(floor, void 0, { x: core.status.hero.loc.x, y: core.status.hero.loc.y, direction: 'down' }, 500,); + flags.dimension = ["主世界", "洞穴"]; + } + switch (core.status.floorId) { + case "zhu7": + godown("dong1"); + break; + case "zhu8": + godown("dong2"); + break; + case "zhu9": + godown("dong3"); + break; + case "zhu12": + godown("dong4"); + break; + case "zhu13": + godown("dong5"); + break; + case "zhu14": + godown("dong6"); + break; + case "zhu17": + godown("dong7"); + break; + case "zhu18": + godown("dong8"); + break; + case "zhu19": + godown("dong9"); + break; + } + + } + } //挖掘后检查存储类方块还在不在 + this.cunchushuju = function (x, y, floor) { + delete flags.boxlist[flags.box_xy]; + delete flags.furnacelist[flags.box_xy]; + delete flags.crops[flags.box_xy]; + // //检测箱子还在不在,不在了就删掉对应箱子数据 + // if (core.getBlockId(x, y, floor) != "T420") { + // delete flags.boxlist[flags.box_xy]; + // } + // //检测熔炉还在不在,不在了就删掉对应熔炉数据 + // if (core.getBlockId(x, y, floor) != "T415") { + // delete flags.furnacelist[flags.box_xy]; + // } + // //删除生长列表数据 + + } //显示挖掘进度圆圈 + this.wajuejindu = function () { + if (core.getContextByName("dig") == null) { + core.createCanvas("dig", 0, 0, 416, 416, 61); + } else { + core.clearMap('dig'); + } + if (flags.dig[2] != null && flags.dig[3] != null && flags.dig[4] > 0 && flags.interface == "物品栏") { + core.fillArc("dig", flags.dig[2] * 32 + 16 - core.bigmap.offsetX, flags.dig[3] * 32 + 16 - core.bigmap.offsetY, 6, 1.5 * Math.PI, (flags.dig[1] / (flags.dig[0] + flags.dig[1])) * 2 * Math.PI - (0.5 * Math.PI), "#9dfd8b"); + core.strokeArc("dig", flags.dig[2] * 32 + 16 - core.bigmap.offsetX, flags.dig[3] * 32 + 16 - core.bigmap.offsetY, 8, 0, 2 * Math.PI, "#2ff49d", 1); + } + if (flags.dig[4] <= 0) { + flags.dig = [0, 0, 0, 0, 0]; + core.deleteCanvas("dig"); + } + } //背包界面点击事件 + this.bagInter = function (mouse_x, mouse_y) { + if (flags.interface == "背包") { + function replace(type) { + core.playSound("armour.ogg"); + if (flags.bag[0][0] && core.material.items[flags.bag[0][0]].cls == "equips" && core.getEquipTypeById(flags.bag[0][0]) == type) { + if (core.getEquip(type)) { + if (core.getEquip(type) == flags.bag[0][0]) return; + for (var i = 1; i < 37; i++) { + if (!flags.bag[i][0] || (flags.bag[i][0] == core.getEquip(type) && flags.bag[i][1] < 64)) { + core.unloadEquip(type); + } + } + } + flags.bag[0][1] -= 1; + core.addItem(flags.bag[0][0], 1); + core.loadEquip(flags.bag[0][0]); + if (flags.bag[0][1] <= 0) { + flags.bag[0] = ["", 0]; + //core.deleteCanvas('viscous'); + core.clearMap('viscous'); + } + } else { + if (core.getEquip(type)) { + for (var i = 1; i < 37; i++) { + if (!flags.bag[i][0] || (flags.bag[i][0] == core.getEquip(type) && flags.bag[i][1] < 64)) { + core.unloadEquip(type); + } + } + } + } + } + if (mouse_x == 6 && mouse_y == 6) replace(1); + else if (mouse_x == 2 && mouse_y == 3) replace(2); + else if (mouse_x == 2 && mouse_y == 4) replace(3); + else if (mouse_x == 2 && mouse_y == 5) replace(4); + else if (mouse_x == 2 && mouse_y == 6) replace(5); + if (mouse_x == 10 && mouse_y == 4) { + flags.curdou = 0; + } + if (flags.transfer == 1) { + trans: for (var mx = 2; mx < 11; mx++) { + if ((mouse_x == mx && mouse_y == 12) || + (mouse_x == mx && mouse_y == 8) || + (mouse_x == mx && mouse_y == 9) || + (mouse_x == mx && mouse_y == 10)) { + + } + if (mouse_x == mx && mouse_y == 12) { + core.plugin.transfer(flags.bag[mx - 1], flags.bag, 10, 37, 0); + break trans; + } else if ((mouse_x == mx && mouse_y == 8) || + (mouse_x == mx && mouse_y == 9) || + (mouse_x == mx && mouse_y == 10)) { + var c = mx - 1 + (mouse_y - 7) * 9 + core.plugin.transfer(flags.bag[c], flags.bag, 1, 10, 0); + break trans; + } else if ((mouse_x == 7 && mouse_y == 3) || + (mouse_x == 8 && mouse_y == 3) || + (mouse_x == 7 && mouse_y == 4) || + (mouse_x == 8 && mouse_y == 4)) { + var c = mouse_x - 7 + (mouse_y - 3) * 2; + core.plugin.transfer(flags.comlist1[c], flags.bag, 1, 37, 1, 1); + break trans; + } else if (mouse_x == 10 && mouse_y == 4) { + for (var n = 0; n < 64; n++) { + if (flags.comlist1[4][0] != "" && flags.comlist1[4][1] != 0) { + for (var i = 0; i < 4; i++) { + if (flags.comlist1[i][0] && flags.comlist1[i][1] <= 0) { + flags.comlist1[4] = ["", 0]; + break trans; + } + } + core.addItem(flags.comlist1[4][0], flags.comlist1[4][1]); + for (var i = 0; i < 4; i++) { + if (flags.comlist1[i][1] > 0) { + flags.comlist1[i][1] -= 1; + } + } + } else { + flags.comlist1[4] = ["", 0]; + break trans; + } + } + } + } + } + if (flags.comlist1[4][0] != "" && flags.comlist1[4][1] != 0) { + if (mouse_x == 10 && mouse_y == 4) { + if (flags.bag[0][0] == "" || (flags.bag[0][0] == flags.comlist1[4][0] && flags.bag[0][1] != 64)) { + flags.bag[0][0] = flags.comlist1[4][0]; + flags.bag[0][1] += flags.comlist1[4][1]; + } else { + core.addItem(flags.comlist1[4][0], flags.comlist1[4][1]); + } + for (var i = 0; i < 4; i++) { + if (flags.comlist1[i][1] > 0) { + flags.comlist1[i][1] -= 1; + } + } + flags.cursplit = 0; + } + } + } + } //箱子界面点击事件 + this.boxInter = function (mouse_x, mouse_y) { + if (flags.interface == "箱子") { + if (flags.transfer == 1) { + var grid = 27; + switch (flags.extraGrid) { + case 0: + grid = 27; + break; + case 3: + grid = 30; + break; + case 6: + grid = 33; + break; + case 9: + grid = 36; + break; + case 12: + grid = 39; + break; + case 15: + grid = 42; + break; + case 18: + grid = 45; + break; + case 21: + grid = 48; + break; + case 24: + grid = 51; + break; + case 27: + grid = 54; + break; + } + trans: for (var mx = 2; mx < 11; mx++) { + if ((mouse_x == mx && mouse_y == 12) || (mouse_x == mx && mouse_y == 8) || (mouse_x == mx && mouse_y == 9) || (mouse_x == mx && mouse_y == 10)) { + var cc; + if (mouse_y == 12) cc = mx - 1; + else cc = mx - 1 + (mouse_y - 7) * 9; + core.plugin.transfer(flags.bag[cc], flags.boxlist[flags.box_xy], 0, grid, 1, -1); + break trans; + } else if ((mouse_x == mx && mouse_y == 1) || (mouse_x == mx && mouse_y == 2) || (mouse_x == mx && mouse_y == 3) || (mouse_x == mx && mouse_y == 4) || (mouse_x == mx && mouse_y == 5) || (mouse_x == mx && mouse_y == 6)) { + if ((mouse_x == mx && mouse_y == 1) || (mouse_x == mx && mouse_y == 2) || (mouse_x == mx && mouse_y == 3)) { + if (grid < 27) break trans; + } else if ((mouse_x == 2 && mouse_y == 4) || (mouse_x == 3 && mouse_y == 4) || (mouse_x == 4 && mouse_y == 4)) { + if (grid < 30) break trans; + } else if ((mouse_x == 5 && mouse_y == 4) || (mouse_x == 6 && mouse_y == 4) || (mouse_x == 7 && mouse_y == 4)) { + if (grid < 33) break trans; + } else if ((mouse_x == 8 && mouse_y == 4) || (mouse_x == 9 && mouse_y == 4) || (mouse_x == 10 && mouse_y == 4)) { + if (grid < 36) break trans; + } else if ((mouse_x == 2 && mouse_y == 5) || (mouse_x == 3 && mouse_y == 5) || (mouse_x == 4 && mouse_y == 5)) { + if (grid < 39) break trans; + } else if ((mouse_x == 5 && mouse_y == 5) || (mouse_x == 6 && mouse_y == 5) || (mouse_x == 7 && mouse_y == 5)) { + if (grid < 42) break trans; + } else if ((mouse_x == 8 && mouse_y == 5) || (mouse_x == 9 && mouse_y == 5) || (mouse_x == 10 && mouse_y == 5)) { + if (grid < 45) break trans; + } else if ((mouse_x == 2 && mouse_y == 6) || (mouse_x == 3 && mouse_y == 6) || (mouse_x == 4 && mouse_y == 6)) { + if (grid < 48) break trans; + } else if ((mouse_x == 5 && mouse_y == 6) || (mouse_x == 6 && mouse_y == 6) || (mouse_x == 7 && mouse_y == 6)) { + if (grid < 51) break trans; + } else if ((mouse_x == 8 && mouse_y == 6) || (mouse_x == 9 && mouse_y == 6) || (mouse_x == 10 && mouse_y == 6)) { + if (grid < 54) break trans; + } + var cc = mx - 2 + (mouse_y - 1) * 9; + core.plugin.transfer(flags.boxlist[flags.box_xy][cc], flags.bag, 1, 37, 1, 1); + break trans; + } + } + } + } + } //工作台界面点击事件 + this.workInter = function (mouse_x, mouse_y) { + if (flags.interface == "工作台") { + if (mouse_x == 9 && mouse_y == 5) { + flags.curdou = 0; + } + if (flags.transfer == 1) { + trans: for (var mx = 2; mx < 11; mx++) { + if ((mouse_x == mx && mouse_y == 12) || (mouse_x == mx && mouse_y == 8) || (mouse_x == mx && mouse_y == 9) || (mouse_x == mx && mouse_y == 10)) { + var cc; + if (mouse_y == 12) { + cc = mx - 1; + } else { + cc = mx - 1 + (mouse_y - 7) * 9; + } + core.plugin.transfer(flags.bag[cc], flags.comlist2, 4, 5, 1, -1); + core.plugin.transfer(flags.bag[cc], flags.comlist2, 0, 9, 1, -1); + break trans; + } else if (mouse_x >= 4 && mouse_x <= 6 && mouse_y >= 4 && mouse_y <= 6) { + var cc = (mouse_x - 4) + (mouse_y - 4) * 3; + core.plugin.transfer(flags.comlist2[cc], flags.bag, 1, 37, 1, 1); + break trans; + } else if (mouse_x == 9 && mouse_y == 5) { + for (var num = 0; num < 64; num++) { + if (flags.comlist2[9][0] != "" && flags.comlist2[9][1] != 0) { + for (var i = 0; i < 9; i++) { + if (flags.comlist2[i][0] && flags.comlist2[i][1] <= 0) { + flags.comlist2[9] = ["", 0]; + break trans; + } + } + core.addItem(flags.comlist2[9][0], flags.comlist2[9][1]); + for (var i = 0; i < 9; i++) { + if (flags.comlist2[i][1] > 0) { + flags.comlist2[i][1] -= 1; + } + } + } else { + flags.comlist2[9] = ["", 0]; + break trans; + } + } + } + } + } + if (flags.comlist2[9][0] != "" && flags.comlist2[9][1] != 0) { + if (mouse_x == 9 && mouse_y == 5) { + if (flags.bag[0][0] == "" || (flags.bag[0][0] == flags.comlist2[9][0] && flags.bag[0][1] != 64)) { + flags.bag[0][0] = flags.comlist2[9][0]; + flags.bag[0][1] += flags.comlist2[9][1]; + } else { + core.addItem(flags.comlist2[9][0], flags.comlist2[9][1]); + } + for (var i = 0; i < 9; i++) { + if (flags.comlist2[i][1] > 0) { + flags.comlist2[i][1] -= 1; + } + } + flags.cursplit = 0; + } + } + } + } //熔炉界面点击事件 + this.furInter = function (mouse_x, mouse_y) { + if (flags.interface == "熔炉") { + if (flags.transfer == 1) { + trans: for (var mx = 2; mx < 11; mx++) { + if ((mouse_x == mx && mouse_y == 12) || (mouse_x == mx && mouse_y == 8) || (mouse_x == mx && mouse_y == 9) || (mouse_x == mx && mouse_y == 10)) { + var cc; + if (mouse_y == 12) { + cc = mx - 1; + } else { + cc = mx - 1 + (mouse_y - 7) * 9; + } + for (var fm in flags.furGuide) { + if (flags.bag[cc][0] == fm) { + core.plugin.transfer(flags.bag[cc], flags.furnacelist[flags.box_xy], 1, 2, 1, -1); + break trans; + } + } + for (var ff in flags.fuelGuide) { + if (flags.bag[cc][0] == ff) { + core.plugin.transfer(flags.bag[cc], flags.furnacelist[flags.box_xy], 0, 1, 1, -1); + break trans; + } + } + } else if (mouse_x == 5 && mouse_y == 6) { + core.plugin.transfer(flags.furnacelist[flags.box_xy][0], flags.bag, 1, 37, 1, 1); + break trans; + } else if (mouse_x == 5 && mouse_y == 4) { + core.plugin.transfer(flags.furnacelist[flags.box_xy][1], flags.bag, 1, 37, 1, 1); + break trans; + } else if (mouse_x == 8 && mouse_y == 5) { + if (flags.furnacelist[flags.box_xy][2][0] != "" && flags.furnacelist[flags.box_xy][2][1] != 0) { + core.addItem(flags.furnacelist[flags.box_xy][2][0], flags.furnacelist[flags.box_xy][2][1]); + flags.furnacelist[flags.box_xy][2] = ["", 0]; + } else { + break trans; + } + } + } + } + if (flags.furnacelist[flags.box_xy][2][0] != "" && flags.furnacelist[flags.box_xy][2][1] != 0 && + (flags.bag[0][0] == "" || + (flags.bag[0][0] == flags.furnacelist[flags.box_xy][2][0] && flags.bag[0][1] != 64))) { + if (mouse_x == 8 && mouse_y == 5) { + flags.bag[0][0] = flags.furnacelist[flags.box_xy][2][0]; + flags.bag[0][1] += flags.furnacelist[flags.box_xy][2][1]; + flags.furnacelist[flags.box_xy][2] = ["", 0]; + } + } + if (flags.smeltcheck == 0) core.plugin.ronglujiance(); + } + } //死亡界面点击事件 + this.dieInter = function (mouse_x, mouse_y) { + if (core.status.hero.hp <= 0) { + if ((mouse_x == 4 && mouse_y == 7) || (mouse_x == 5 && mouse_y == 7) || (mouse_x == 6 && mouse_y == 7) || (mouse_x == 7 && mouse_y == 7) || (mouse_x == 8 && mouse_y == 7)) { + core.deleteCanvas("die"); + for (var num = 0; num < 11; num++) { + if (flags.revive[3] && flags.revive[4] && flags.revive[5]) { + var rn = core.rand2(9) + 1; + var n = 0; + place: for (var i = -1; i <= 1; i++) { + for (var j = -1; j <= 1; j++) { + n += 1; + if (n == rn) { + flags.revivePo[0] = flags.revive[3]; + flags.revivePo[1] = flags.revive[4] + i; + flags.revivePo[2] = flags.revive[5] + j; + break place; + } + } + } + } else { + var rn = core.rand2(49) + 1; + var n = 0; + place: for (var i = -3; i <= 3; i++) { + for (var j = -3; j <= 3; j++) { + n += 1; + if (n == rn) { + flags.revivePo[0] = flags.revive[0]; + flags.revivePo[1] = flags.revive[1] + i; + flags.revivePo[2] = flags.revive[2] + j; + break place; + } + } + } + } + if (flags.revivePo[1] > 0 && + flags.revivePo[1] < core.bigmap.width - 1 && + flags.revivePo[2] > 0 && + flags.revivePo[2] < core.bigmap.height - 1) { + core.plugin.jiazaitu("jz5.png", 1000); + flags.interface = "锁定"; + core.setHeroLoc("x", flags.revivePo[1]); + core.setHeroLoc("y", flags.revivePo[2]); + core.changeFloor(flags.revivePo[0]); + break; + } else if (num == 10) { + core.plugin.jiazaitu("jz5.png", 1000); + flags.interface = "锁定"; + flags.revive[3] = ""; + flags.revive[4] = 0; + flags.revive[5] = 0; + core.setHeroLoc("x", flags.revive[1]); + core.setHeroLoc("y", flags.revive[2]); + core.changeFloor(flags.revive[0]); + } + } + flags.revivePo = ["", 0, 0]; + core.status.hero.hp = core.status.hero.hpmax; + flags.interface = "物品栏"; + } else if ((mouse_x == 4 && mouse_y == 9) || (mouse_x == 5 && mouse_y == 9) || (mouse_x == 6 && mouse_y == 9) || (mouse_x == 7 && mouse_y == 9) || (mouse_x == 8 && mouse_y == 9)) { + core.doSL(flags.worldlist["0"]["世界存档"][0], "save"); + setTimeout(function () { + core.restart(); + }, 100); + } + } + } //科技界面点击事件 + this.techInter = function (px, py) { + if (px >= 350 && py >= 386 && flags.tech == 1) { //退出 + //core.playSound("确定"); + flags.tip[0] = ""; + flags.tech = 0; + flags.interface = "物品栏"; + return; + } + if (px >= 323 && px <= 392 && py >= 332 && py <= 379 && flags.tech >= 2) { //从小界面返回 + //core.playSound("确定"); + flags.tech = 1; + core.plugin.techUI(); + return; + } + if (flags.tech == -1) { //从说明返回 + flags.tech = 1; + core.plugin.techUI(); + return; + } else if (px >= 368 && px <= 395 && py >= 9 && py <= 36 && flags.tech == 1) { //进入说明 + flags.tech = -1; + core.plugin.techUI(); + return; + } + if (px >= 255 && px <= 322 && py >= 332 && py <= 379 && flags.tech >= 2) { //升级 + var num = 2; + for (var te in flags.tech1) { + if (flags.tech == num) { + if (flags.worldlevel < flags.tech3[te][2][flags.tech1[te][0] - 1]) { + core.playSound("确定"); + flags.tip[0] = "需求世界等级未达到"; + core.ui.drawStatusBar(); + } else if (flags.tech1[te][0] < flags.tech1[te][1] && flags.tpointnum >= flags.tech3[te][0][flags.tech1[te][0] - 1] && flags.coppernum >= flags.tech3[te][1][flags.tech1[te][0] - 1]) { + core.playSound("levelup.ogg"); + flags.tpointsum += flags.tech3[te][0][flags.tech1[te][0] - 1]; + flags.tpointnum -= flags.tech3[te][0][flags.tech1[te][0] - 1]; + flags.coppernum -= flags.tech3[te][1][flags.tech1[te][0] - 1]; + flags.tech1[te][0] += 1; + core.plugin.upgradeTech(te, flags.tech1[te][0]); + flags.tip[0] = "升级成功"; + core.ui.drawStatusBar(); + } else { + core.playSound("确定"); + flags.tip[0] = "科技点或钱币数量不足"; + core.ui.drawStatusBar(); + } + break; + } else { + num += 1; + } + } + core.plugin.techUI(); + return; + } + if (flags.tech == 1) { //进入小界面 + if (px >= 15 && px <= 212 && py >= 57 && py <= 114) { + //core.playSound("确定"); + flags.tech = 2; + } else if (px >= 213 && px <= 415 && py >= 57 && py <= 114) { + //core.playSound("确定"); + flags.tech = 3; + } else if (px >= 15 && px <= 212 && py >= 115 && py <= 171) { + //core.playSound("确定"); + flags.tech = 4; + } else if (px >= 213 && px <= 415 && py >= 115 && py <= 171) { + //core.playSound("确定"); + flags.tech = 5; + } else if (px >= 15 && px <= 212 && py >= 172 && py <= 228) { + //core.playSound("确定"); + flags.tech = 6; + } else if (px >= 213 && px <= 415 && py >= 172 && py <= 228) { + //core.playSound("确定"); + flags.tech = 7; + } else if (px >= 15 && px <= 212 && py >= 229 && py <= 285) { + //core.playSound("确定"); + flags.tech = 8; + } else if (px >= 213 && px <= 415 && py >= 229 && py <= 285) { + //core.playSound("确定"); + flags.tech = 9; + } else if (px >= 15 && px <= 212 && py >= 286 && py <= 342) { + //core.playSound("确定"); + flags.tech = 10; + } else if (px >= 213 && px <= 415 && py >= 286 && py <= 342) { + //core.playSound("确定"); + flags.tech = 11; + } else if (px >= 15 && px <= 212 && py >= 343 && py <= 399) { + //core.playSound("确定"); + flags.tech = 12; + } else if (px >= 213 && px <= 415 && py >= 343 && py <= 385) { + //core.playSound("确定"); + flags.tech = 13; + } else if (px >= 14 && px <= 155 && py >= 7 && py <= 43) { + if (flags.level[0] >= 5 && flags.tpointnum < 10) { + core.playSound("orb.ogg"); + flags.level[0] -= 5; + flags.tpointnum += 1; + } else if (flags.level[0] < 5) { + core.playSound("确定"); + flags.tip[0] = "至少需要5个等级才能兑换科技点"; + core.ui.drawStatusBar(); + } else if (flags.tpointnum >= 10) { + core.playSound("确定"); + flags.tip[0] = "科技点已达上限"; + core.ui.drawStatusBar(); + } + } + core.plugin.techUI(); + return; + } + } //升级科技实际效果 + this.upgradeTech = function (name, lv) { + switch (name) { + case "灵感": + switch (lv) { + case 2: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22); + break; + case 3: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23); + break; + case 4: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24); + break; + case 5: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25); + break; + case 6: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15, flags.craftGuide16); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25, flags.craftGuide26); + break; + case 7: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15, flags.craftGuide16, flags.craftGuide17); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25, flags.craftGuide26, flags.craftGuide27); + break; + case 8: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15, flags.craftGuide16, flags.craftGuide17, flags.craftGuide18); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25, flags.craftGuide26, flags.craftGuide27, flags.craftGuide28); + break; + case 9: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15, flags.craftGuide16, flags.craftGuide17, flags.craftGuide18, flags.craftGuide19); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25, flags.craftGuide26, flags.craftGuide27, flags.craftGuide28, flags.craftGuide29); + break; + case 10: + flags.craftGuide1 = Object.assign({}, flags.craftGuide11, flags.craftGuide12, flags.craftGuide13, flags.craftGuide14, flags.craftGuide15, flags.craftGuide16, flags.craftGuide17, flags.craftGuide18, flags.craftGuide19, flags.craftGuide1a); + flags.craftGuide2 = Object.assign({}, flags.craftGuide21, flags.craftGuide22, flags.craftGuide23, flags.craftGuide24, flags.craftGuide25, flags.craftGuide26, flags.craftGuide27, flags.craftGuide28, flags.craftGuide29, flags.craftGuide2a); + break; + } + break; + case "领悟": + switch (lv) { + case 2: + flags.property.exp += 1; + break; + case 3: + flags.property.exp += 1; + break; + case 4: + flags.property.exp += 1; + break; + case 5: + flags.property.exp += 1; + break; + case 6: + flags.property.exp += 1; + break; + case 7: + flags.property.exp += 1; + break; + case 8: + flags.property.exp += 1; + break; + case 9: + flags.property.exp += 1; + break; + case 10: + flags.property.exp += 1; + break; + case 11: + flags.property.exp += 1; + break; + } + break; + case "生命之力": + switch (lv) { + case 2: + core.status.hero.hpmax += 4; + break; + case 3: + core.status.hero.hpmax += 4; + break; + case 4: + core.status.hero.hpmax += 4; + break; + case 5: + core.status.hero.hpmax += 6; + break; + case 6: + core.status.hero.hpmax += 6; + break; + case 7: + core.status.hero.hpmax += 6; + break; + case 8: + core.status.hero.hpmax += 6; + break; + case 9: + core.status.hero.hpmax += 8; + break; + case 10: + core.status.hero.hpmax += 8; + break; + case 11: + core.status.hero.hpmax += 8; + break; + } + break; + case "增幅": + switch (lv) { + case 2: + core.status.hero.atk += 1; + break; + case 3: + core.status.hero.atk += 1; + break; + case 4: + core.status.hero.atk += 2; + break; + case 5: + core.status.hero.atk += 2; + break; + case 6: + core.status.hero.atk += 3; + break; + case 7: + core.status.hero.atk += 3; + break; + case 8: + core.status.hero.atk += 4; + break; + case 9: + core.status.hero.atk += 4; + break; + case 10: + core.status.hero.atk += 4; + break; + case 11: + core.status.hero.atk += 4; + break; + } + break; + case "坚定": + switch (lv) { + case 2: + core.status.hero.def += 1; + break; + case 3: + core.status.hero.def += 1; + break; + case 4: + core.status.hero.def += 1; + break; + case 5: + core.status.hero.def += 1; + break; + case 6: + core.status.hero.def += 1; + break; + case 7: + core.status.hero.def += 1; + break; + case 8: + core.status.hero.def += 1; + break; + case 9: + core.status.hero.def += 1; + break; + case 10: + core.status.hero.def += 1; + break; + case 11: + core.status.hero.def += 1; + break; + } + break; + case "敏捷": + switch (lv) { + case 2: + flags.property.atkSpeed[0] += 1; + break; + case 3: + flags.property.atkSpeed[0] += 1; + break; + case 4: + flags.property.atkSpeed[0] += 1; + break; + case 5: + flags.property.atkSpeed[0] += 1; + break; + case 6: + flags.property.atkSpeed[0] += 1; + break; + case 7: + flags.property.atkSpeed[0] += 1; + break; + case 8: + flags.property.atkSpeed[0] += 1; + break; + case 9: + flags.property.atkSpeed[0] += 1; + break; + case 10: + flags.property.atkSpeed[0] += 1; + break; + case 11: + flags.property.atkSpeed[0] += 1; + break; + } + break; + case "耐力": + switch (lv) { + case 2: + flags.hunger[1] += 10; + break; + case 3: + flags.hunger[1] += 10; + break; + case 4: + flags.hunger[1] += 10; + break; + case 5: + flags.hunger[1] += 10; + break; + case 6: + flags.hunger[1] += 10; + break; + case 7: + flags.hunger[1] += 10; + break; + case 8: + flags.hunger[1] += 10; + break; + case 9: + flags.hunger[1] += 10; + break; + case 10: + flags.hunger[1] += 10; + break; + case 11: + flags.hunger[1] += 10; + break; + } + break; + case "空间扩展": + switch (lv) { + case 2: + flags.extraGrid += 3; + break; + case 3: + flags.extraGrid += 3; + break; + case 4: + flags.extraGrid += 3; + break; + case 5: + flags.extraGrid += 3; + break; + case 6: + flags.extraGrid += 3; + break; + case 7: + flags.extraGrid += 3; + break; + case 8: + flags.extraGrid += 3; + break; + case 9: + flags.extraGrid += 3; + break; + case 10: + flags.extraGrid += 3; + break; + } + break; + case "守护之韧": + switch (lv) { + case 2: + flags.extraDur[0] += 5; + break; + case 3: + flags.extraDur[0] += 5; + break; + case 4: + flags.extraDur[0] += 5; + break; + case 5: + flags.extraDur[0] += 5; + break; + case 6: + flags.extraDur[0] += 10; + break; + case 7: + flags.extraDur[0] += 10; + break; + case 8: + flags.extraDur[0] += 10; + break; + case 9: + flags.extraDur[0] += 15; + break; + case 10: + flags.extraDur[0] += 15; + break; + case 11: + flags.extraDur[0] += 20; + break; + } + break; + case "锋锐恒久": + switch (lv) { + case 2: + flags.extraDur[1] += 5; + break; + case 3: + flags.extraDur[1] += 5; + break; + case 4: + flags.extraDur[1] += 5; + break; + case 5: + flags.extraDur[1] += 5; + break; + case 6: + flags.extraDur[1] += 10; + break; + case 7: + flags.extraDur[1] += 10; + break; + case 8: + flags.extraDur[1] += 10; + break; + case 9: + flags.extraDur[1] += 15; + break; + case 10: + flags.extraDur[1] += 15; + break; + case 11: + flags.extraDur[1] += 20; + break; + } + break; + case "生机": + switch (lv) { + case 2: + flags.extraGrow += 1; + break; + case 3: + flags.extraGrow += 1; + break; + case 4: + flags.extraGrow += 1; + break; + case 5: + flags.extraGrow += 1; + break; + case 6: + flags.extraGrow += 1; + break; + case 7: + flags.extraGrow += 1; + break; + case 8: + flags.extraGrow += 1; + break; + case 9: + flags.extraGrow += 1; + break; + case 10: + flags.extraGrow += 1; + break; + case 11: + flags.extraGrow += 1; + break; + } + break; + case "商贾之道": //未做 + switch (lv) { + case 2: + + break; + case 3: + + break; + case 4: + + break; + case 5: + + break; + case 6: + + break; + case 7: + + break; + case 8: + + break; + case 9: + + break; + case 10: + + break; + case 11: + + break; + } + break; + } + } //自定义按键界面点击事件 + this.keyInter = function (px, py) { + if (flags.CustomKey == 1) { + if (px >= 344 && py >= 370) { + //core.playSound("确定"); + flags.tip[0] = ""; + flags.CustomKey = 0; + flags.interface = "物品栏"; + return; + } else if (px >= 87 && px <= 204 && py >= 366 && py <= 406) { + //core.playSound("确定"); + if (flags.pagenumber >= 2) { + flags.pagenumber -= 1; + } else { + flags.tip[0] = "已经是第一页了"; + core.ui.drawStatusBar(); + } + } else if (px >= 234 && px <= 340 && py >= 366 && py <= 406) { + //core.playSound("确定"); + if (flags.pagenumber <= 2) { + flags.pagenumber += 1; + } else { + flags.tip[0] = "已经是第一页了"; + core.ui.drawStatusBar(); + } + } else if (px >= 272 && px <= 372 && py >= 82 && py <= 112) { + //core.playSound("确定"); + flags.curKey = 1 + (flags.pagenumber - 1) * 6; + flags.tip[0] = "请按下你希望设置的按键"; + core.ui.drawStatusBar(); + } else if (px >= 272 && px <= 372 && py >= 128 && py <= 158) { + //core.playSound("确定"); + flags.curKey = 2 + (flags.pagenumber - 1) * 6; + flags.tip[0] = "请按下你希望设置的按键"; + core.ui.drawStatusBar(); + } else if (px >= 272 && px <= 372 && py >= 174 && py <= 204) { + //core.playSound("确定"); + flags.curKey = 3 + (flags.pagenumber - 1) * 6; + flags.tip[0] = "请按下你希望设置的按键"; + core.ui.drawStatusBar(); + } else if (px >= 272 && px <= 372 && py >= 220 && py <= 250) { + //core.playSound("确定"); + flags.curKey = 4 + (flags.pagenumber - 1) * 6; + flags.tip[0] = "请按下你希望设置的按键"; + core.ui.drawStatusBar(); + } else if (px >= 272 && px <= 372 && py >= 266 && py <= 296) { + //core.playSound("确定"); + flags.curKey = 5 + (flags.pagenumber - 1) * 6; + flags.tip[0] = "请按下你希望设置的按键"; + core.ui.drawStatusBar(); + } else if (px >= 272 && px <= 372 && py >= 312 && py <= 342) { + //core.playSound("确定"); + flags.curKey = 6 + (flags.pagenumber - 1) * 6; + flags.tip[0] = "请按下你希望设置的按键"; + core.ui.drawStatusBar(); + } + core.plugin.zidingyianjian(); + } + } //属性界面点击事件 + this.attrInter = function (px, py) { + if (flags.attrui == 1) { + if (px >= 74 && px <= 343 && py >= 42 && py <= 377) { + if (px >= 266 && px <= 342 && py >= 339 && py <= 376) { + flags.tip[0] = ""; + flags.attrui = 0; + flags.interface = "物品栏"; + return; + } else if (px >= 87 && px <= 199 && py >= 107 && py <= 127) { + flags.tip[0] = "1颗心等于2生命,饱食度>\r[#61fff3]90%\r时会缓慢恢复生命"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 217 && py >= 128 && py <= 148) { + flags.tip[0] = "各项操作会消耗饱食度,饱食度为0则\r[#61fff3]消耗生命\r,可通过\r[#61fff3]饮食\r回复饱食度"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 212 && py >= 149 && py <= 169) { + flags.tip[0] = "不同武器有着不同的攻速,攻速条未满时会在\r[#61fff3]物品栏左侧\r显示出来"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 317 && py >= 107 && py <= 127) { + flags.tip[0] = "可以与5X5范围内的方块进行交互"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 317 && py >= 128 && py <= 148) { + flags.tip[0] = "可以对" + (flags.property.touchRange[0] * 2 + 1) + "X" + (flags.property.touchRange[0] * 2 + 1) + "范围内的生物进行攻击"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 317 && py >= 149 && py <= 169) { + flags.tip[0] = "击杀敌怪时的额外经验加成"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 152 && py >= 189 && py <= 208) { + flags.tip[0] = "核心属性,直接影响着击杀生物的次数"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 179 && py >= 209 && py <= 228) { + flags.tip[0] = "仅在面对\r[#61fff3]海洋生物\r时,造成更多伤害"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 179 && py >= 229 && py <= 248) { + flags.tip[0] = "仅在面对\r[#61fff3]飞行生物\r时,造成更多伤害"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 179 && py >= 249 && py <= 268) { + flags.tip[0] = "小于1时,对生物造成百分比攻击力的伤害;大于1时,对生物造成固定数值的额外伤害"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 179 && py >= 269 && py <= 288) { + flags.tip[0] = "对目标3X3范围的\r[#61fff3]其他生物\r造成额外伤害,该伤害无视防御"; + core.ui.drawStatusBar(); + } else if (px >= 87 && px <= 152 && py >= 289 && py <= 308) { + flags.tip[0] = "根据暴击系数造成伤害"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 294 && py >= 189 && py <= 208) { + flags.tip[0] = "抵挡\r[#61fff3]物理伤害\r,对走位良好的玩家可能不是必需的"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 294 && py >= 209 && py <= 228) { + flags.tip[0] = "对即将到来的\r[#61fff3]物理伤害\r进行一定系数的减免"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 322 && py >= 229 && py <= 248) { + flags.tip[0] = "仅对\r[#61fff3]爆炸\r进行一定系数的伤害减免"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 332 && py >= 249 && py <= 268) { + flags.tip[0] = "仅对\r[#61fff3]远程物理伤害\r进行一定系数的减免"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 294 && py >= 269 && py <= 288) { + flags.tip[0] = "受到伤害时,对目标反弹一定伤害"; + core.ui.drawStatusBar(); + } else if (px >= 225 && px <= 294 && py >= 289 && py <= 308) { + flags.tip[0] = "无敌期间免疫物理伤害和魔法伤害"; + core.ui.drawStatusBar(); + } + } else { + flags.tip[0] = ""; + flags.attrui = 0; + flags.interface = "物品栏"; + return; + } + + core.plugin.attrshow(); + } + } //委托界面点击事件 + this.taskInter = function (px, py) { + if (flags.taskui == 1) { + if (px >= 12 && px <= 405 && py >= 41 && py <= 388) { + if (px >= 321 && px <= 405 && py >= 347 && py <= 388) { + flags.tip[0] = ""; + flags.taskui = 0; + flags.interface = "物品栏"; + return; + } else if (px >= 287 && px <= 402 && py >= 49 && py <= 94) { + flags.tip[0] = "每天0点收获1委托点,刷新消耗1点,直接领奖消耗5点,无上限"; + core.ui.drawStatusBar(); + } else if (px >= 321 && px <= 388 && py >= 109 && py <= 144) { + flags.tip[0] = "刷新1"; + core.ui.drawStatusBar(); + } else if (px >= 321 && px <= 388 && py >= 145 && py <= 178) { + flags.tip[0] = "领取1"; + core.ui.drawStatusBar(); + } else if (px >= 321 && px <= 388 && py >= 193 && py <= 226) { + flags.tip[0] = "刷新2"; + core.ui.drawStatusBar(); + } else if (px >= 321 && px <= 388 && py >= 127 && py <= 261) { + flags.tip[0] = "领取2"; + core.ui.drawStatusBar(); + } else if (px >= 321 && px <= 388 && py >= 275 && py <= 310) { + flags.tip[0] = "刷新3"; + core.ui.drawStatusBar(); + } else if (px >= 321 && px <= 388 && py >= 311 && py <= 346) { + flags.tip[0] = "领取3"; + core.ui.drawStatusBar(); + } + } else { + flags.tip[0] = ""; + flags.taskui = 0; + flags.interface = "物品栏"; + return; + } + + core.plugin.taskshow(); + } + } //成就界面点击事件 + this.achiInter = function (px, py) { + if (flags.achiui == 1) { + if (px >= 317 && px <= 416 && py >= 369 && py <= 416) { + flags.tip[0] = ""; + flags.achiui = 0; + flags.interface = "物品栏"; + return; + } else if (px >= 23 && px <= 104 && py >= 71 && py <= 94) { + flags.tip[0] = "历程类"; + core.ui.drawStatusBar(); + } else if (px >= 23 && px <= 104 && py >= 95 && py <= 122) { + flags.tip[0] = "收集类"; + core.ui.drawStatusBar(); + } else if (px >= 23 && px <= 104 && py >= 123 && py <= 149) { + flags.tip[0] = "挑战类"; + core.ui.drawStatusBar(); + } else if (px >= 23 && px <= 104 && py >= 150 && py <= 174) { + flags.tip[0] = "彩蛋类"; + core.ui.drawStatusBar(); + } else if (px >= 18 && px <= 109 && py >= 330 && py <= 375) { + flags.tip[0] = "提交成绩"; + core.ui.drawStatusBar(); + } else if (px >= 282 && px <= 333 && py >= 45 && py <= 67) { + flags.tip[0] = "上一页"; + core.ui.drawStatusBar(); + } else if (px >= 348 && px <= 403 && py >= 45 && py <= 67) { + flags.tip[0] = "下一页"; + core.ui.drawStatusBar(); + } + core.plugin.achishow(); + } + } //自动农场界面点击事件 + this.farmInter = function (px, py) { + if (flags.farmUI == 1) { + if (px >= 288 && px <= 336 && py >= 292 && py <= 316) { + flags.farmUI = 0; + } else if (px >= 224 && px <= 272 && py >= 292 && py <= 316) { + var farmc = flags.farmland1[flags.farmland2[flags.box_xy][0]][flags.farmland2[flags.box_xy][1] - 1]; + if (flags.coppernum >= farmc && flags.farmland2[flags.box_xy][1] < 10) { + flags.coppernum -= farmc; + flags.farmland2[flags.box_xy][1] += 1; + core.plugin.zidongnongtian(); + } else { + flags.tip[0] = "铜币不足或已满级"; + core.ui.drawStatusBar(); + } + } else if (px >= 120 && px <= 168 && py >= 292 && py <= 316) { + if (flags.cropslist[flags.farmland2[flags.box_xy][0]][3] != "") { + core.addItem(flags.cropslist[flags.farmland2[flags.box_xy][0]][3], flags.farmland2[flags.box_xy][2]); + } + if (flags.cropslist[flags.farmland2[flags.box_xy][0]][5] != "") { + core.addItem(flags.cropslist[flags.farmland2[flags.box_xy][0]][5], flags.farmland2[flags.box_xy][3]); + } + flags.farmland2[flags.box_xy][2] = 0; + flags.farmland2[flags.box_xy][3] = 0; + core.plugin.zidongnongtian(); + } + } + } //MC界面点击光标位置 + this.clickloc = function (x, y) { + if (flags.interface == "背包") { + for (var i = 2; i < 11; i++) { + if (x == i && y == 12) { + return i - 1; + } else if (x == i && y == 8) { + return i + 8; + } else if (x == i && y == 9) { + return i + 17; + } else if (x == i && y == 10) { + return i + 26; + } + } + if (x == 7 && y == 3) { + return 37; + } else if (x == 8 && y == 3) { + return 38; + } else if (x == 7 && y == 4) { + return 39; + } else if (x == 8 && y == 4) { + return 40; + } + } else if (flags.interface == "箱子") { + for (var i = 2; i < 11; i++) { + if (x == i && y == 12) { + return i - 1; + } else if (x == i && y == 8) { + return i + 8; + } else if (x == i && y == 9) { + return i + 17; + } else if (x == i && y == 10) { + return i + 26; + } else if (x == i && y == 1) { + return i + 35; + } else if (x == i && y == 2) { + return i + 44; + } else if (x == i && y == 3) { + return i + 53; + } else if (x == i && y == 4) { + return i + 62; + } else if (x == i && y == 5) { + return i + 71; + } else if (x == i && y == 6) { + return i + 80; + } + } + } else if (flags.interface == "工作台") { + for (var i = 2; i < 11; i++) { + if (x == i && y == 12) { + return i - 1; + } else if (x == i && y == 8) { + return i + 8; + } else if (x == i && y == 9) { + return i + 17; + } else if (x == i && y == 10) { + return i + 26; + } + } + if (x == 4 && y == 4) { + return 37; + } else if (x == 5 && y == 4) { + return 38; + } else if (x == 6 && y == 4) { + return 39; + } else if (x == 4 && y == 5) { + return 40; + } else if (x == 5 && y == 5) { + return 41; + } else if (x == 6 && y == 5) { + return 42; + } else if (x == 4 && y == 6) { + return 43; + } else if (x == 5 && y == 6) { + return 44; + } else if (x == 6 && y == 6) { + return 45; + } + } else if (flags.interface == "熔炉") { + for (var i = 2; i < 11; i++) { + if (x == i && y == 12) { + return i - 1; + } else if (x == i && y == 8) { + return i + 8; + } else if (x == i && y == 9) { + return i + 17; + } else if (x == i && y == 10) { + return i + 26; + } + } + if (x == 5 && y == 6) { + return 37; + } else if (x == 5 && y == 4) { + return 38; + } + } + + } //按住shift快速转移物品 + this.transfer = function (b1, b2, n1, n2, changeNum, isget) { + for (var i = 1; i < 6; i++) { + if (flags.interface == "背包" && + b1[0] && + core.material.items[b1[0]].cls == "equips" && + core.getEquipTypeById(b1[0]) == i && + !core.getEquip(i) && + core.canEquip(b1[0])) { + b1[1] -= 1; + core.loadEquip(b1[0]); + core.playSound("armour.ogg"); + flags.cursor2 = 0; + flags.curCache2 = [0, 0]; + return; + } + } + for (var cur = n1; cur < n2; cur++) { + if (b1[0] && (b2[cur][0] == null || b2[cur][0] == "")) { + if (changeNum) core.addItem(b1[0], isget * b1[1]); + flags.bagCache = b2[cur][0]; + b2[cur][0] = b1[0]; + b1[0] = flags.bagCache; + var cn = b2[cur][1]; + b2[cur][1] = b1[1]; + b1[1] = cn; + if (b1[0] && changeNum) { + core.addItem(b1[0], -isget * b1[1]); + } + break; + } else if (b1[0] && b2[cur][0] == b1[0] && b2[cur][1] < 64) { + if (changeNum) core.addItem(b1[0], isget * b1[1]); + b2[cur][1] += b1[1]; + b1[1] = 0; + if (b2[cur][1] > 64) { + if (changeNum) core.addItem(b1[0], -isget * (b1[1] + b2[cur][1] - 64)); + b1[1] += b2[cur][1] - 64; + b2[cur][1] = 64; + core.plugin.transfer(b1, b2, n1, n2, changeNum, isget); + } + break; + } + } + flags.cursor2 = 0; + flags.curCache2 = [0, 0]; + core.drawMC(); + } //放方块音效播放 + this.putsound = function (number) { + var rand1; + var sound = ""; + var type = core.getBlockInfo(number).blocktype; + if (type == 1 || type == 6) { + rand1 = core.rand2(4); + switch (rand1) { + case 0: + sound = "stone1.ogg"; + break; + case 1: + sound = "stone2.ogg"; + break; + case 2: + sound = "stone3.ogg"; + break; + case 3: + sound = "stone4.ogg"; + break; + } + } else if (type == 2 || type == 5) { + rand1 = core.rand2(4); + switch (rand1) { + case 0: + sound = "wood1.ogg"; + break; + case 1: + sound = "wood2.ogg"; + break; + case 2: + sound = "wood3.ogg"; + break; + case 3: + sound = "wood4.ogg"; + break; + } + } else if (type == 3 || type == 4) { + switch (number) { + case 349: + rand1 = core.rand2(4); + switch (rand1) { + case 0: + sound = "gravel1.ogg"; + break; + case 1: + sound = "gravel2.ogg"; + break; + case 2: + sound = "gravel3.ogg"; + break; + case 3: + sound = "gravel4.ogg"; + break; + } + break; + case 343: + case 344: + rand1 = core.rand2(4); + switch (rand1) { + case 0: + sound = "sand1.ogg"; + break; + case 1: + sound = "sand2.ogg"; + break; + case 2: + sound = "sand3.ogg"; + break; + case 3: + sound = "sand4.ogg"; + break; + } + break; + case 342: + rand1 = core.rand2(4); + switch (rand1) { + case 0: + sound = "snow1.ogg"; + break; + case 1: + sound = "snow2.ogg"; + break; + case 2: + sound = "snow3.ogg"; + break; + case 3: + sound = "snow4.ogg"; + break; + } + break; + default:/*case 305 || 339 || 340 || 341 || 347 || 426 || 921:*/ + rand1 = core.rand2(4); + switch (rand1) { + case 0: + sound = "grass1.ogg"; + break; + case 1: + sound = "grass2.ogg"; + break; + case 2: + sound = "grass3.ogg"; + break; + case 3: + sound = "grass4.ogg"; + break; + } + break; + } + } + core.playSound(sound); + } //挖方块音效播放 + this.digsound = function (number) { + var sound = ""; + var type = core.getBlockInfo(number).blocktype; + if (type == 1 || type == 6) { + sound = "dig_stone.ogg"; + } else if (type == 2 || type == 5) { + sound = "dig_wood.ogg"; + } else if (type == 3 || type == 4) { + switch (number) { + case 343: + case 344: + sound = "dig_sand.ogg"; + break; + case 342: + sound = "dig_snow.ogg"; + break; + default: + sound = "dig_grass.ogg"; + break; + } + } + core.playSound(sound); + } + + + }, + "3-右键点击": function () { + var myElement = document.getElementById('data'); + myElement.addEventListener('contextmenu', function (e) { + //关闭右键菜单 + event.preventDefault(); + }); + + //主程序,当鼠标右键点击时进行判断 + this.rightClick = function (mouse_x, mouse_y, px, py) { + //console.log("右键事件"); + //console.log("右键坐标", mouse_x, mouse_y, px, py); + var offsetx = Math.round(core.bigmap.offsetX / 32); + var offsety = Math.round(core.bigmap.offsetY / 32); + if (flags.interface == "物品栏") { + flags.box_xy = ((mouse_x + offsetx) * 1000 + (mouse_y + offsety)).toString().padStart(6, '0') + core.status.floorId; + } + var sczz = 0; + for (var cr in flags.cropslist) { + if (flags.bag[flags.cursor1][0] == cr) { + sczz = 1; + break; + } + } + flags.cursplit = core.plugin.clickloc(mouse_x, mouse_y); + core.plugin.interUI(mouse_x, mouse_y, offsetx, offsety, sczz); + if (!flags.longleft) { + core.plugin.mcjiemian(); + core.plugin.beibaocunchu(); + } + flags.mingan = 0; + if (flags.bag[0][0] != "" && flags.interface != "物品栏" && flags.interface != "锁定") { + //core.ui._createUIEvent(); + if (core.getContextByName("viscous") == null) { + core.createCanvas("viscous", 0, 0, 416, 416, 136); + } else { + core.clearMap('viscous'); + } + core.drawIcon("viscous", flags.bag[0][0], px - 16, py - 16); + if (flags.bag[0][1] > 0 && flags.bag[0][1] < 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 20, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[0][1] >= 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 12, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else { + core.deleteCanvas('viscous'); + core.clearMap('viscous'); + } + if (core.getBlockCls(mouse_x + offsetx, mouse_y + offsety) == 'enemys' || + core.getBlockCls(mouse_x + offsetx, mouse_y + offsety) == 'enemy48') { + var eid = ((mouse_x + offsetx) * 1000 + mouse_y + offsety).toString().padStart(6, '0') + core.status.floorId; + flags.rightstatus = ["敌怪", eid]; + extend.update(); + } else if (core.getFgNumber(mouse_x + offsetx, mouse_y + offsety)) { + flags.rightstatus = ["图块", core.getFgNumber(mouse_x + offsetx, mouse_y + offsety)]; + extend.update(); + } else if (core.getBlockCls(mouse_x + offsetx, mouse_y + offsety) == 'terrains') { + var cropid = ((mouse_x + offsetx) * 1000 + mouse_y + offsety).toString().padStart(6, '0') + core.status.floorId; + if (cropid in flags.crops) { + flags.rightstatus = ["图块", core.getMapNumber(mouse_x + offsetx, mouse_y + offsety), cropid]; + } else { + flags.rightstatus = ["图块", core.getMapNumber(mouse_x + offsetx, mouse_y + offsety)]; + } + extend.update(); + } else if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety)) { + flags.rightstatus = ["图块", core.getBgNumber(mouse_x + offsetx, mouse_y + offsety)]; + extend.update(); + } else { + flags.rightstatus = ["", ""]; + extend.update(); + } + }; + //右键打开交互界面 + this.interUI = function (mouse_x, mouse_y, offsetx, offsety, sczz) { + for (var i = -flags.property.touchRange[1]; i <= flags.property.touchRange[1]; i++) { + for (var j = -flags.property.touchRange[1]; j <= flags.property.touchRange[1]; j++) { + if (mouse_x + offsetx == core.status.hero.loc.x + i && mouse_y + offsety == core.status.hero.loc.y + j) { + if (flags.interface == "物品栏") { + if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T420" || + core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T425") { + if (!flags.boxlist[flags.box_xy]) flags.boxlist[flags.box_xy] = [["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0],]; + flags.interface = "箱子"; + flags.cursplit = 0; + if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T420") { + core.playSound("chestopen.ogg"); + } + if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T425") { + flags.discard = 1; + } + return; + } else if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T414") { + flags.interface = "工作台"; + flags.cursplit = 0; + return; + } else if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T415") { + if (!flags.furnacelist[flags.box_xy]) { + flags.furnacelist[flags.box_xy] = [["", 0], ["", 0], ["", 0]]; + flags.furnacelist[flags.box_xy][3] = [-1, 0]; + } + flags.interface = "熔炉"; + flags.cursplit = 0; + return; + } else if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T309") { + if (!flags.farmland2[flags.box_xy]) { + flags.farmland2[flags.box_xy] = ["I492", 1]; + flags.crops[flags.box_xy] = ["I492", 0, flags.cropslist["I492"][0], 1]; + } + core.plugin.zidongnongtian(); + flags.cursplit = 0; + return; + } else if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T310") { + if (!flags.farmland2[flags.box_xy]) { + flags.farmland2[flags.box_xy] = ["I507", 1]; + flags.crops[flags.box_xy] = ["I507", 0, flags.cropslist["I507"][0], 1]; + } + core.plugin.zidongnongtian(); + flags.cursplit = 0; + return; + } else if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T312") { + if (!flags.farmland2[flags.box_xy]) { + flags.farmland2[flags.box_xy] = ["I508", 1]; + flags.crops[flags.box_xy] = ["I508", 0, flags.cropslist["I508"][0], 1]; + } + core.plugin.zidongnongtian(); + flags.cursplit = 0; + return; + } else if (core.getBlockId(mouse_x + offsetx, mouse_y + offsety) == "T419") { + core.plugin.chongshengdian(mouse_x + offsetx, mouse_y + offsety, core.status.floorId); + if (flags.timePeriod == "夜晚") { + flags.sleep = 0; + flags.interface = "锁定"; + core.lockControl(); + function sleep(time) { + setTimeout(function () { + time -= 100; + if (time <= 0) { + if ((flags.time >= 1200 && flags.time < 1440) || + (flags.time >= 0 && flags.time < 240)) { + flags.time = 240; + } + core.plugin.drawLight('fg2', 1 - Math.abs(time / 1500), [[0, 0, 0]], 1); + if (time <= -1500) { + flags.interface = "物品栏"; + core.unlockControl(); + return; + } + sleep(time); + } else if (time <= 500) { + core.plugin.drawLight('fg2', 1, [[0, 0, 0]], 1); + sleep(time); + } else { + core.plugin.drawLight('fg2', 1 - Math.abs((time - 500) / 2500), [[0, 0, 0]], 1); + sleep(time); + } + }, 100); + } + sleep(3000); + } + return; + } + } else { + return; + } + } + } + } + core.plugin.plant(mouse_x, mouse_y, offsetx, offsety, sczz); + core.plugin.putBlock(mouse_x, mouse_y, offsetx, offsety, sczz); + } //主手栏为种子时,点击触碰范围内的耕地背景块放置幼苗 + this.plant = function (mouse_x, mouse_y, offsetx, offsety, sczz) { + var offsetx = Math.round(core.bigmap.offsetX / 32); + var offsety = Math.round(core.bigmap.offsetY / 32); + if (sczz == 1) { + plant: for (var i = -flags.property.touchRange[1]; i <= flags.property.touchRange[1]; i++) { + for (var j = -flags.property.touchRange[1]; j <= flags.property.touchRange[1]; j++) { + if (mouse_x + offsetx == core.status.hero.loc.x + i && + mouse_y + offsety == core.status.hero.loc.y + j && + !core.getMapNumber(mouse_x + offsetx, mouse_y + offsety)) { + for (var cr in flags.cropslist) { + if (flags.bag[flags.cursor1][0] == cr) { + if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == flags.cropslist[cr][2][0] || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == flags.cropslist[cr][2][1] || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == flags.cropslist[cr][2][2] || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == flags.cropslist[cr][2][3] || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety) == flags.cropslist[cr][2][4]) { + core.playSound("grass2.ogg"); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.setBlock(flags.cropslist[cr][1][0], mouse_x + offsetx, mouse_y + offsety); + flags.crops[flags.box_xy] = [cr, 0, flags.cropslist[cr][0]]; + core.plugin.plantgrow(flags.box_xy); + core.plugin.baoshidu(); + core.plugin.beibaocunchu(); + } + break plant; + } + } + } + } + } + } + } //在玩家触碰范围内放置方块,背景->事件->前景 + this.putBlock = function (mouse_x, mouse_y, offsetx, offsety, sczz) { + var offsetx = Math.round(core.bigmap.offsetX / 32); + var offsety = Math.round(core.bigmap.offsetY / 32); + if (sczz == 0) { + var bg = core.getBgNumber(mouse_x + offsetx, mouse_y + offsety); + var bc = core.getMapNumber(mouse_x + offsetx, mouse_y + offsety); + var fg = core.getFgNumber(mouse_x + offsetx, mouse_y + offsety); + put: for (var i = -flags.property.touchRange[1]; i <= flags.property.touchRange[1]; i++) { + for (var j = -flags.property.touchRange[1]; j <= flags.property.touchRange[1]; j++) { + if (mouse_x + offsetx == core.status.hero.loc.x + i && mouse_y + offsety == core.status.hero.loc.y + j) { + if (bg != 0 && bc != 0 && fg != 0) { + if (mouse_x + offsetx != core.status.hero.loc.x || mouse_y + offsety != core.status.hero.loc.y) return; + function godown(floor) { + core.changeFloor(floor, void 0, { x: core.status.hero.loc.x, y: core.status.hero.loc.y, direction: 'down' }, 500,); + flags.dimension = ["主世界", "地表"]; + } + switch (core.status.floorId) { + case "dong1": + godown("zhu7"); + break; + case "dong2": + godown("zhu8"); + break; + case "dong3": + godown("zhu9"); + break; + case "dong4": + godown("zhu12"); + break; + case "dong5": + godown("zhu13"); + break; + case "dong6": + godown("zhu14"); + break; + case "dong7": + godown("zhu17"); + break; + case "dong8": + godown("zhu18"); + break; + case "dong9": + godown("zhu19"); + break; + } + break put; + } + for (var b in flags.ffk) { + if (b == flags.bag[flags.cursor1][0]) { + switch (flags.ffk[b][2][0]) { + case 0: + if (bg == 0 && flags.ffk[b][1][0] == 1) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBgFgBlock('bg', flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } else if (bg != 0 && bc == 0 && flags.ffk[b][1][1] == 1) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBlock(flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } else if (bg != 0 && bc != 0 && fg == 0 && flags.ffk[b][1][2] == 1) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBgFgBlock('fg', flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } + break; + case 1: + if (bc == 0 && ( + bg == flags.ffk[b][2][1] || + bg == flags.ffk[b][2][2] || + bg == flags.ffk[b][2][3] || + bg == flags.ffk[b][2][4] || + bg == flags.ffk[b][2][5] || + bg == flags.ffk[b][2][6] || + bg == flags.ffk[b][2][7] || + bg == flags.ffk[b][2][8] || + bg == flags.ffk[b][2][9])) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBlock(flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } + break; + case 2: + if (fg == 0 && ( + bc == flags.ffk[b][2][1] || + bc == flags.ffk[b][2][2] || + bc == flags.ffk[b][2][3] || + bc == flags.ffk[b][2][4] || + bc == flags.ffk[b][2][5] || + bc == flags.ffk[b][2][6] || + bc == flags.ffk[b][2][7] || + bc == flags.ffk[b][2][8] || + bc == flags.ffk[b][2][9])) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBgFgBlock('fg', flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } + break; + case 3: + if (bc == 0 && ( + fg == flags.ffk[b][2][1] || + fg == flags.ffk[b][2][2] || + fg == flags.ffk[b][2][3] || + fg == flags.ffk[b][2][4] || + fg == flags.ffk[b][2][5] || + fg == flags.ffk[b][2][6] || + fg == flags.ffk[b][2][7] || + fg == flags.ffk[b][2][8] || + fg == flags.ffk[b][2][9])) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBlock(flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } + break; + case 4: + break; + case 5: //305,339,340,341,921 + if (bc == 0 && fg == 0 && ( + bg == 305 || + bg == 339 || + bg == 340 || + bg == 341 || + bg == 921)) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBgFgBlock('bg', flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.dig = [0, 0, 0, 0, 0]; + core.plugin.reduceDur("tool"); + core.plugin.baoshidu(); + } + break; + case 6: + if (bg == 0 && flags.ffk[b][1][0] == 1) { + function change1(x, y) { + if (core.getBgNumber(x, y) == 305) { + return 305; + } else if (core.getBgNumber(x, y) == 339) { + return 339 + } else if (core.getBgNumber(x, y) == 340) { + return 340; + } else if (core.getBgNumber(x, y) == 341) { + return 341; + } else { + return 0; + } + } + function change2(x, y) { + if (change1(x, y - 1) != 0) { + core.plugin.putsound(change1(x, y - 1)); + core.setBgFgBlock('bg', change1(x, y - 1), x, y); + } else if (change1(x, y + 1) != 0) { + core.plugin.putsound(change1(x, y + 1)); + core.setBgFgBlock('bg', change1(x, y + 1), x, y); + } else if (change1(x - 1, y) != 0) { + core.plugin.putsound(change1(x - 1, y)); + core.setBgFgBlock('bg', change1(x - 1, y), x, y); + } else if (change1(x + 1, y) != 0) { + core.plugin.putsound(change1(x + 1, y)); + core.setBgFgBlock('bg', change1(x + 1, y), x, y); + } else { + core.plugin.putsound(921); + core.setBgFgBlock('bg', 921, x, y); + } + } + change2(mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } else if (bc == 0 && flags.ffk[b][1][1] == 1) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBlock(flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } else if (fg == 0 && flags.ffk[b][1][2] == 1) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBgFgBlock('fg', flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } + break; + case 7: + if (bg == 0 && flags.ffk[b][1][0] == 1) { + if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 345) { + core.plugin.putsound(345); + core.setBgFgBlock('bg', 345, mouse_x + offsetx, mouse_y + offsety); + } else { + core.plugin.putsound(348); + core.setBgFgBlock('bg', 348, mouse_x + offsetx, mouse_y + offsety); + } + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + flags.bag[flags.cursor1] = ["I654", 1]; + core.addItem(flags.bag[flags.cursor1][0], 1); + core.plugin.baoshidu(); + } else if (fg == 0 && flags.ffk[b][1][2] == 1) { + core.plugin.putsound(flags.ffk[b][0][0]); + core.setBgFgBlock('fg', flags.ffk[b][0][0], mouse_x + offsetx, mouse_y + offsety); + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + flags.bag[flags.cursor1] = ["I654", 1]; + core.addItem(flags.bag[flags.cursor1][0], 1); + core.plugin.baoshidu(); + } + break; + case 8: + if (bg == 0 && flags.ffk[b][1][1] == 1) { + if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 305 || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 339 || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 340 || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 341) { + core.plugin.putsound(372); + core.setBlock(372, mouse_x + offsetx, mouse_y + offsety); + } else if (core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 345 || + core.getBgNumber(mouse_x + offsetx, mouse_y + offsety - 1) == 348) { + core.plugin.putsound(393); + core.setBlock(393, mouse_x + offsetx, mouse_y + offsety); + } + flags.bag[flags.cursor1][1] -= 1; + core.addItem(flags.bag[flags.cursor1][0], -1); + core.plugin.baoshidu(); + } + break; + default: + break; + } + break put; + } + } + } + } + } + } + } + + }, + "4-鼠标长按": function () { + this.leftMouseDown = function (mouse_x, mouse_y) { + //console.log('左键按下'); + flags.longleft = 1; + } + this.leftMouseUp = function () { + //console.log('左键松开'); + flags.longleft = 0; + flags.curarr = []; + flags.bagbp = []; + flags.splitCache1 = 0; + flags.splitCache2 = {}; + if (flags.bag[0][0] == "" || flags.bag[0][1] <= 0) { + flags.bag[0] = ["", 0]; + core.deleteCanvas('viscous'); + //core.clearMap('viscous'); + } + } + this.rightMouseDown = function () { + //console.log('右键按下'); + if (flags.longleft) return; + flags.longright = 1; + } + this.rightMouseUp = function () { + //console.log('右键松开'); + if (flags.longleft) return; + flags.longright = 0; + flags.curarr = []; + flags.bagbp = []; + } + + }, + "5-鼠标移动": function () { + //注册一个鼠标移动行为函数 + core.registerAction( + 'onmove', + 'onmoved', + function (mouse_x, mouse_y, px, py) { + core.plugin.onmoved(mouse_x, mouse_y, px, py); + return false; + }, + 100 + ); + //主程序,当鼠标移动时进行判断 + this.onmoved = function (mouse_x, mouse_y, px, py) { + if (!window.core) return; + if (!core.isPlaying) return; + if (!flags.bag) return; + if (flags.interface != "锁定" && flags.interface != "物品栏") { + function durDisplay(b1) { + if (b1[0] != "") { + if (core.material.items[b1[0]].cls == "equips") { + if (b1[0] in flags.weapon) { + core.plugin.equipInfo(flags.weapon, flags.weaDur, b1[0], mouse_x, mouse_y); + } else if (b1[0] in flags.tool) { + core.plugin.equipInfo(flags.tool, flags.toolDur, b1[0], mouse_x, mouse_y); + } else if (b1[0] in flags.equip) { + core.plugin.equipInfo(flags.equip, flags.equDur, b1[0], mouse_x, mouse_y); + } + core.deleteCanvas("itemtext"); + //core.clearMap('itemtext'); + } else { + core.plugin.itemText(b1[0], mouse_x, mouse_y); + core.deleteCanvas("durable"); + //core.clearMap('durable'); + } + } else { + //core.deleteCanvas("durable"); + core.clearMap('durable'); + //core.deleteCanvas("itemtext"); + core.clearMap('itemtext'); + } + } + var xianshi = 0; + for (var mx = 2; mx < 11; mx++) { + if ((mouse_x == mx && mouse_y == 12) || + (mouse_x == mx && mouse_y == 8) || + (mouse_x == mx && mouse_y == 9) || + (mouse_x == mx && mouse_y == 10)) { + var cc; + if (mouse_y == 12) cc = mx - 1; + else cc = mx - 1 + (mouse_y - 7) * 9; + durDisplay(flags.bag[cc]); + xianshi = 1; + break; + } + } + if (xianshi == 0) { + if (flags.interface == "背包") { + if (mouse_x == 2 && mouse_y == 3 && core.getEquip(2)) { + core.plugin.equipInfo(flags.equip, flags.equDur, core.getEquip(2), mouse_x, mouse_y); + xianshi = 1; + } + else if (mouse_x == 2 && mouse_y == 4 && core.getEquip(3)) { + core.plugin.equipInfo(flags.equip, flags.equDur, core.getEquip(3), mouse_x, mouse_y); + xianshi = 1; + } + else if (mouse_x == 2 && mouse_y == 5 && core.getEquip(4)) { + core.plugin.equipInfo(flags.equip, flags.equDur, core.getEquip(4), mouse_x, mouse_y); + xianshi = 1; + } + else if (mouse_x == 2 && mouse_y == 6 && core.getEquip(5)) { + core.plugin.equipInfo(flags.equip, flags.equDur, core.getEquip(5), mouse_x, mouse_y); + xianshi = 1; + } + else if (mouse_x == 6 && mouse_y == 6 && core.getEquip(1)) { + core.plugin.equipInfo(flags.equip, flags.equDur, core.getEquip(1), mouse_x, mouse_y); + xianshi = 1; + } + else if ((mouse_x == 7 && mouse_y == 3) || + (mouse_x == 7 && mouse_y == 4) || + (mouse_x == 8 && mouse_y == 3) || + (mouse_x == 8 && mouse_y == 4)) { + var cc = mouse_x - 7 + (mouse_y - 3) * 2; + durDisplay(flags.comlist1[cc]); + xianshi = 1; + } else if (mouse_x == 10 && mouse_y == 4) { + durDisplay(flags.comlist1[4]); + xianshi = 1; + } + } else if (flags.interface == "箱子") { + for (var mx = 2; mx < 11; mx++) { + if ((mouse_x == mx && mouse_y == 1) || + (mouse_x == mx && mouse_y == 2) || + (mouse_x == mx && mouse_y == 3) || + (mouse_x == mx && mouse_y == 4) || + (mouse_x == mx && mouse_y == 5) || + (mouse_x == mx && mouse_y == 6)) { + var cc = mx - 2 + (mouse_y - 1) * 9; + durDisplay(flags.boxlist[flags.box_xy][cc]); + xianshi = 1; + break; + } + } + } else if (flags.interface == "工作台") { + if (mouse_x >= 4 && mouse_x <= 6 && mouse_y >= 4 && mouse_y <= 6) { + var cc = (mouse_x - 4) + (mouse_y - 4) * 3; + durDisplay(flags.comlist2[cc]); + xianshi = 1; + } else if (mouse_x == 9 && mouse_y == 5) { + durDisplay(flags.comlist2[9]); + xianshi = 1; + } + } else if (flags.interface == "熔炉") { + if (mouse_x == 5 && mouse_y == 4) { + durDisplay(flags.furnacelist[flags.box_xy][1]); + xianshi = 1; + } + else if (mouse_x == 5 && mouse_y == 6) { + durDisplay(flags.furnacelist[flags.box_xy][0]); + xianshi = 1; + } + else if (mouse_x == 8 && mouse_y == 5) { + durDisplay(flags.furnacelist[flags.box_xy][2]); + xianshi = 1; + } + } + } + if (xianshi == 0) { + //core.deleteCanvas("durable"); + core.clearMap('durable'); + //core.deleteCanvas("itemtext"); + core.clearMap('itemtext'); + } + if (flags.bag != undefined && flags.bag[0][0] != "") { + if (flags.longleft || flags.bag[0][1] > 0) { + //core.ui._createUIEvent(); + if (core.getContextByName("viscous") == null) { + core.createCanvas("viscous", 0, 0, 416, 416, 136); + } else { + core.clearMap('viscous'); + } + core.drawIcon("viscous", flags.bag[0][0], px - 16, py - 16); + if (flags.bag[0][1] > 0 && flags.bag[0][1] < 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 20, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[0][1] >= 10) { + core.ui.fillText("viscous", flags.bag[0][1].toString(), px - 16 + 12, py - 16 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + } + + core.plugin.highLight(mouse_x, mouse_y); + if (flags.transfer && flags.longleft) { + core.plugin.bagInter(mouse_x, mouse_y); + core.plugin.boxInter(mouse_x, mouse_y); + core.plugin.workInter(mouse_x, mouse_y); + core.plugin.furInter(mouse_x, mouse_y); + } + } + } + this.equipInfo = function (list, dur, id, mouse_x, mouse_y) { + if (flags.longleft || flags.longright) return; + var a = (mouse_x - 1) * 32; + var b1 = (mouse_y - 4) * 32; + var b2 = (mouse_y + 1) * 32; + var font1 = ui.prototype._buildFont(12, false); + var font2 = ui.prototype._buildFont(10, false); + var font3 = ui.prototype._buildFont(9, false); + var color1 = "#BBFFFF"; + var color2 = "#a3fabf"; + var color3 = "white"; + //自适应 + if (mouse_y <= 3) b1 = b2; + //创建画布 + if (core.getContextByName("durable") == null) { + core.createCanvas("durable", 0, 0, 416, 416, 140); + } else { + core.clearMap('durable'); + } + //绘制显示框 + core.fillRoundRect("durable", a, b1, 96, 128, 5, [54, 54, 54, 0.9]); + core.strokeRoundRect("durable", a, b1, 96, 128, 5, [255, 255, 255, 1], 2); + //绘制文本 + core.ui.fillText("durable", core.material.items[id].name, a + 4, b1 + 14, color1, font1, 96); + core.ui.fillText("durable", "耐久:" + dur[id][0] + "/" + list[id][0], a + 8, b1 + 26, color2, font2, 96); + // core.ui.fillText("durable", "亡灵杀手5", a + 4, b1 + 84, color3, font3, 96); + // core.ui.fillText("durable", "亡灵杀手5", a + 4, b1 + 96, color3, font3, 96); + // core.ui.fillText("durable", "亡灵杀手5", a + 4, b1 + 108, color3, font3, 96); + // core.ui.fillText("durable", "亡灵杀手5", a + 50, b1 + 84, color3, font3, 96); + // core.ui.fillText("durable", "亡灵杀手5", a + 50, b1 + 96, color3, font3, 96); + // core.ui.fillText("durable", "亡灵杀手5", a + 50, b1 + 108, color3, font3, 96); + if (list == flags.weapon) { + core.ui.fillText("durable", "装备时:", a + 4, b1 + 44, color3, font1, 96); + core.ui.fillText("durable", "攻击:" + "+" + list[id][1], a + 8, b1 + 58, color2, font2, 96); + //core.ui.fillText("durable", "攻速:" + list[id][2], a + 8, b1 + 70, color2, font2, 96); + } else if (list == flags.equip) { + core.ui.fillText("durable", "装备时:", a + 4, b1 + 44, color3, font1, 96); + core.ui.fillText("durable", "防御:" + "+" + list[id][1], a + 8, b1 + 58, color2, font2, 96); + } + } + this.highLight = function (mouse_x, mouse_y) { + var h = 0; + if (flags.interface == "背包") { + for (var i = 2; i < 11; i++) { + if ((mouse_x == i && mouse_y == 12) || + (mouse_x == i && mouse_y == 8) || + (mouse_x == i && mouse_y == 9) || + (mouse_x == i && mouse_y == 10)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } + if ((mouse_x == 2 && mouse_y == 3) || + (mouse_x == 2 && mouse_y == 4) || + (mouse_x == 2 && mouse_y == 5) || + (mouse_x == 2 && mouse_y == 6) || + (mouse_x == 6 && mouse_y == 6) || + (mouse_x == 7 && mouse_y == 3) || + (mouse_x == 8 && mouse_y == 3) || + (mouse_x == 7 && mouse_y == 4) || + (mouse_x == 8 && mouse_y == 4) || + (mouse_x == 10 && mouse_y == 4)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } else if (flags.interface == "箱子") { + for (var i = 2; i < 11; i++) { + if ((mouse_x == i && mouse_y == 12) || + (mouse_x == i && mouse_y == 8) || + (mouse_x == i && mouse_y == 9) || + (mouse_x == i && mouse_y == 10) || + (mouse_x == i && mouse_y == 1) || + (mouse_x == i && mouse_y == 2) || + (mouse_x == i && mouse_y == 3) || + (mouse_x == i && mouse_y == 4) || + (mouse_x == i && mouse_y == 5) || + (mouse_x == i && mouse_y == 6)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } + } else if (flags.interface == "工作台") { + for (var i = 2; i < 11; i++) { + if ((mouse_x == i && mouse_y == 12) || + (mouse_x == i && mouse_y == 8) || + (mouse_x == i && mouse_y == 9) || + (mouse_x == i && mouse_y == 10)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } + if ((mouse_x == 4 && mouse_y == 4) || + (mouse_x == 5 && mouse_y == 4) || + (mouse_x == 6 && mouse_y == 4) || + (mouse_x == 4 && mouse_y == 5) || + (mouse_x == 5 && mouse_y == 5) || + (mouse_x == 6 && mouse_y == 5) || + (mouse_x == 4 && mouse_y == 6) || + (mouse_x == 5 && mouse_y == 6) || + (mouse_x == 6 && mouse_y == 6) || + (mouse_x == 9 && mouse_y == 5)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } else if (flags.interface == "熔炉") { + for (var i = 2; i < 11; i++) { + if ((mouse_x == i && mouse_y == 12) || + (mouse_x == i && mouse_y == 8) || + (mouse_x == i && mouse_y == 9) || + (mouse_x == i && mouse_y == 10)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } + if ((mouse_x == 5 && mouse_y == 6) || + (mouse_x == 5 && mouse_y == 4) || + (mouse_x == 8 && mouse_y == 5)) { + flags.curlight[0] = [mouse_x, mouse_y]; + h = 1; + } + } + if (flags.curlight != undefined && flags.curlight[0][0] != 0) { + if (flags.curlight[0][0] != flags.curlight[1][0] || flags.curlight[0][1] != flags.curlight[1][1]) { + if ((flags.longleft == 0 && flags.longright == 0) || flags.bag[0][0] == "") { + if (core.getContextByName("curlight") == null) { + core.createCanvas("curlight", 0, 0, 416, 416, 127); + } else { + core.clearMap('curlight'); + } + } else { + if (!flags.curarr.length) { + var c = flags.curlight[0]; + flags.curlight[0] = flags.curlight[1]; + core.plugin.mcjiemian(); + flags.curlight[0] = c; + flags.mingan = 1; + } + core.plugin.mcjiemian(); + } + core.setAlpha("curlight", 0.4); + flags.curlight[1] = flags.curlight[0]; + core.ui.fillRect("curlight", flags.curlight[0][0] * 32 + 3, flags.curlight[0][1] * 32 + 3, 25, 25, "white"); + } else if (h == 0 && flags.longleft == 0 && flags.longright == 0) { + if (core.getContextByName("curlight") == null) { + core.createCanvas("curlight", 0, 0, 416, 416, 127); + } else { + core.clearMap('curlight'); + } + flags.curlight = [[0, 0], [0, 0]]; + } + } else { + flags.curlight = [[0, 0], [0, 0]]; + core.deleteCanvas("curlight"); + //core.clearMap('curlight'); + } + } + this.itemText = function (id, mouse_x, mouse_y) { + if (flags.bag[0][0] != "" || flags.longleft || flags.longright) return; + var cname = "itemtext"; + var text = core.material.items[id].text; + var length; + if (text == null) length = 1; + else length = text.length; + var a = (mouse_x - 1) * 32; + var b1 = (mouse_y - 1) * 32 - (Math.ceil(length / 8) - 1) * 12; + var b2 = (mouse_y + 1) * 32; + var font1 = ui.prototype._buildFont(12, false); + var font2 = ui.prototype._buildFont(10, false); + var color = "white"; + var color1 = "white"; //白色 + var color2 = "#eff683"; //黄色 + var color3 = "#BBFFFF"; //天青色 + var color4 = "#f872e4"; //品红色 + var color5 = "#a3fabf"; //绿色 + var height = 32; + if (flags.interface == "箱子") { + var grid = 27; + var tip = 0; + switch (flags.extraGrid) { + case 0: + grid = 27; + break; + case 3: + grid = 30; + break; + case 6: + grid = 33; + break; + case 9: + grid = 36; + break; + case 12: + grid = 39; + break; + case 15: + grid = 42; + break; + case 18: + grid = 45; + break; + case 21: + grid = 48; + break; + case 24: + grid = 51; + break; + case 27: + grid = 54; + break; + } + if ((mouse_y == 1) || (mouse_y == 2) || (mouse_y == 3)) { + if (grid < 27) tip = 1; + } else if ((mouse_x == 2 && mouse_y == 4) || (mouse_x == 3 && mouse_y == 4) || (mouse_x == 4 && mouse_y == 4)) { + if (grid < 30) tip = 1; + } else if ((mouse_x == 5 && mouse_y == 4) || (mouse_x == 6 && mouse_y == 4) || (mouse_x == 7 && mouse_y == 4)) { + if (grid < 33) tip = 1; + } else if ((mouse_x == 8 && mouse_y == 4) || (mouse_x == 9 && mouse_y == 4) || (mouse_x == 10 && mouse_y == 4)) { + if (grid < 36) tip = 1; + } else if ((mouse_x == 2 && mouse_y == 5) || (mouse_x == 3 && mouse_y == 5) || (mouse_x == 4 && mouse_y == 5)) { + if (grid < 39) tip = 1; + } else if ((mouse_x == 5 && mouse_y == 5) || (mouse_x == 6 && mouse_y == 5) || (mouse_x == 7 && mouse_y == 5)) { + if (grid < 42) tip = 1; + } else if ((mouse_x == 8 && mouse_y == 5) || (mouse_x == 9 && mouse_y == 5) || (mouse_x == 10 && mouse_y == 5)) { + if (grid < 45) tip = 1; + } else if ((mouse_x == 2 && mouse_y == 6) || (mouse_x == 3 && mouse_y == 6) || (mouse_x == 4 && mouse_y == 6)) { + if (grid < 48) tip = 1; + } else if ((mouse_x == 5 && mouse_y == 6) || (mouse_x == 6 && mouse_y == 6) || (mouse_x == 7 && mouse_y == 6)) { + if (grid < 51) tip = 1; + } else if ((mouse_x == 8 && mouse_y == 6) || (mouse_x == 9 && mouse_y == 6) || (mouse_x == 10 && mouse_y == 6)) { + if (grid < 54) tip = 1; + } + if (tip == 1) { + text = "无法使用该空间,请升级 空间扩展科技"; + color5 = "#ec3727"; + length = text.length; + b1 = (mouse_y - 1) * 32 - (Math.ceil(length / 8) - 1) * 12; + } + } + //自适应 + height = Math.max((Math.ceil(length / 8) - 1) * 12 + 32, 32); + if (mouse_y <= 3) b1 = b2; + if (flags.quality[id] == 1) color = color1; + else if (flags.quality[id] == 2) color = color2; + else if (flags.quality[id] == 3) color = color3; + else if (flags.quality[id] == 4) color = color4; + + //创建画布 + if (core.getContextByName(cname) == null) { + core.createCanvas(cname, 0, 0, 416, 416, 140); + } else { + core.clearMap(cname); + } + //绘制显示框 + core.fillRoundRect(cname, a, b1, 96, height, 5, [54, 54, 54, 0.9]); + core.strokeRoundRect(cname, a, b1, 96, height, 5, [255, 255, 255, 1], 2); + //绘制文本 + core.ui.fillText(cname, core.material.items[id].name, a + 4, b1 + 14, color, font1, 96); + for (var i = 0; i <= Math.ceil(length / 8); i++) { + if (text != null) core.ui.fillText(cname, text.substr(i * 8, 8), a + 8, b1 + 26 + (i * 12), color5, font2, 96); + } + } + + }, + "6-滚轮滑动": function () { + //注册一个滚轮行为函数 + core.registerAction( + 'onmousewheel', + 'onmousewheeled', + function (direct) { + core.plugin.onmousewheeled(direct); + return false; + }, + 100 + ); + function alpha1(time) { + setTimeout(function () { + if (time > 0) { + time -= 50; + alpha1(time); + } else { + //function alpha() { + var a = 10; + var alp = setInterval(function () { + if (a < 0) { + flags.textalpha = []; + clearInterval(alp); + } else { + a -= 1; + core.setOpacity("itemname", a / 10); + } + }, 50); + } + }, 50); + } + //主程序,当鼠标滚轮上下滑动时进行判断 + this.onmousewheeled = function (direct) { + if (!window.core) return; + if (!core.isPlaying) return; + if (!core.isPlaying()) return; + + core.plugin.mcjiemian(); + if (flags.interface == "物品栏" && core.status.hero.hp > 0) { + if (direct == 1) { + flags.cursor1 -= 1; + if (flags.cursor1 < 1) { + flags.cursor1 = 9; + } + } else if (direct == -1) { + flags.cursor1 += 1; + if (flags.cursor1 > 9) { + flags.cursor1 = 1; + } + } + if (flags.bag[flags.cursor1][0] != "") { + var arr = flags.textalpha; + arr.push(flags.cursor1); + var cur = flags.textalpha[flags.textalpha.length - 1]; + var leng = flags.textalpha.length; + core.clearMap('itemname'); + core.setOpacity("itemname", 1); + core.setTextAlign("itemname", "center"); + core.fillText("itemname", core.material.items[flags.bag[flags.cursor1][0]].name, 208, 330, "white", ui.prototype._buildFont(12, false), 96); + if (leng < 2) alpha1(1500); + } + } + core.plugin.drawMC(); + core.plugin.invMC(); + }; + + }, + "7-按键按下": function () { + //注册一个按下按键行为函数 + core.registerAction( + 'keyDown', + 'keyDowned', + function (keyCode) { + core.plugin.keyDowned(keyCode); + return false; + }, + 100 + ); + //主程序,当按键按下时进行判断 + this.keyDowned = function (keyCode) { + //console.log(core.plugin.keyc(keyCode)) + if (core.status.hero.hp > 0 && !core.getContextByName("jzt")) { + if (flags.curKey) { + if (keyCode == 27) { + flags.keylist[flags.curKey - 1][1] = -1; + flags.curKey = 0; + } else { + for (var i = 0; i < flags.keylist.length; i++) { + if (flags.keylist[i][1] == keyCode) { + core.playSound("确定"); + flags.tip[0] = "该按键已经被设置!"; + core.ui.drawStatusBar(); + flags.curKey = 0; + break; + } + } + } + + if (flags.curKey) { + core.playSound("orb.ogg"); + flags.tip[0] = "设置成功!"; + core.ui.drawStatusBar(); + flags.keylist[flags.curKey - 1][1] = keyCode; + flags.curKey = 0; + } + core.plugin.zidingyianjian(); + } + if (flags.interface == "物品栏" && + flags.tech == 0 && + flags.CustomKey == 0 && + flags.attrui == 0 && + flags.taskui == 0 && + flags.achiui == 0 && + flags.farmUI == 0 && + !core.status.lockControl) { + if (keyCode == flags.keylist[0][1]) { + core.moveHero('up'); + } + if (keyCode == flags.keylist[1][1]) { + core.moveHero('left'); + } + if (keyCode == flags.keylist[2][1]) { + core.moveHero('down'); + } + if (keyCode == flags.keylist[3][1]) { + core.moveHero('right'); + } + } + if (keyCode == flags.keylist[4][1]) { + if (flags.interface == "物品栏") { + flags.interface = "背包"; + } else if (flags.interface == "背包" || + flags.interface == "箱子" || + flags.interface == "工作台" || + flags.interface == "熔炉" + ) { + if (flags.interface == "箱子" && flags.discard == 0) { + core.playSound("chestclosed.ogg"); + } + flags.interface = "物品栏"; + if (flags.discard == 1) { + delete flags.boxlist[flags.box_xy]; + flags.discard = 0; + } + } + if (flags.interface == "物品栏") { + for (var i = 0; i < 4; i++) { + if (flags.comlist1[i][0]) { + core.addItem(flags.comlist1[i][0], flags.comlist1[i][1]); + flags.comlist1[i] = ["", 0]; + } + } + for (var i = 0; i < 9; i++) { + if (flags.comlist2[i][0]) { + core.addItem(flags.comlist2[i][0], flags.comlist2[i][1]); + flags.comlist2[i] = ["", 0]; + } + } + core.deleteCanvas('viscous'); + core.deleteCanvas("curlight"); + core.deleteCanvas("durable"); + core.deleteCanvas("itemtext"); + } + core.plugin.mcjiemian(); + core.plugin.beibaocunchu(); + } + if (keyCode == flags.keylist[5][1]) { + if (core.status.event.id == 'settings') { + core.closePanel(); + core.playSound('取消'); + flags.interface = "物品栏"; + } else if (flags.interface == "物品栏" && flags.tech == 0 && flags.CustomKey == 0 && core.status.event.id == null) { + core.clearUI(); + core.openSettings(); + flags.interface = "锁定"; + core.lockControl(); + } + } + if (keyCode == flags.keylist[6][1]) { + if (core.status.event.id == 'viewMaps') { + core.closePanel(); + core.drawMap(); + core.playSound('取消'); + flags.interface = "物品栏"; + } else if (flags.interface == "物品栏" && flags.tech == 0 && flags.CustomKey == 0 && core.status.event.id == null) { + flags.interface = "锁定"; + ui.prototype._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + } + } + if (keyCode == flags.keylist[7][1]) { + if (flags.interface == "物品栏" && flags.tech == 0 && flags.CustomKey == 0 && core.status.event.id == null) { + flags.tech = 1; + core.plugin.techUI(); + } else if (flags.tech != 0) { + flags.tech = 0; + flags.interface = "物品栏"; + core.clearMap('uievent'); + core.plugin.mcjiemian(); + } + } + if (keyCode == flags.keylist[8][1]) { + + } + if (keyCode == flags.keylist[9][1]) { + + } + if (keyCode == flags.keylist[10][1]) { + flags.transfer = 1; + } + if (keyCode == flags.keylist[11][1]) { + if (flags.interface == "物品栏" && flags.tech == 0 && flags.CustomKey == 0 && core.status.event.id == null) { + flags.CustomKey = 1; + core.plugin.zidingyianjian(); + } else { + flags.CustomKey = 0; + flags.interface = "物品栏"; + core.clearMap('uievent'); + core.plugin.mcjiemian(); + } + } + } + }; + //keycode对应的按键字符串显示 + this.keyc = function (k) { + if (k == 65) { + return "A"; + } else if (k == 66) { + return "B"; + } else if (k == 67) { + return "C"; + } else if (k == 68) { + return "D"; + } else if (k == 69) { + return "E"; + } else if (k == 70) { + return "F"; + } else if (k == 71) { + return "G"; + } else if (k == 72) { + return "H"; + } else if (k == 73) { + return "I"; + } else if (k == 74) { + return "J"; + } else if (k == 75) { + return "K"; + } else if (k == 76) { + return "L"; + } else if (k == 77) { + return "M"; + } else if (k == 78) { + return "N"; + } else if (k == 79) { + return "O"; + } else if (k == 80) { + return "P"; + } else if (k == 81) { + return "Q"; + } else if (k == 82) { + return "R"; + } else if (k == 83) { + return "S"; + } else if (k == 84) { + return "T"; + } else if (k == 85) { + return "U"; + } else if (k == 86) { + return "V"; + } else if (k == 87) { + return "W"; + } else if (k == 88) { + return "X"; + } else if (k == 89) { + return "Y"; + } else if (k == 90) { + return "Z"; + } else if (k == 48) { + return "0"; + } else if (k == 49) { + return "1"; + } else if (k == 50) { + return "2"; + } else if (k == 51) { + return "3"; + } else if (k == 52) { + return "4"; + } else if (k == 53) { + return "5"; + } else if (k == 54) { + return "6"; + } else if (k == 55) { + return "7"; + } else if (k == 56) { + return "8"; + } else if (k == 57) { + return "9"; + } else if (k == 8) { + return "BackSpace"; + } else if (k == 9) { + return "Tab"; + } else if (k == 12) { + return "Clear"; + } else if (k == 13) { + return "Enter"; + } else if (k == 16) { + return "Shift"; + } else if (k == 17) { + return "Control"; + } else if (k == 18) { + return "Alt"; + } else if (k == 20) { + return "CapeLock"; + } else if (k == 27) { + return "Esc"; + } else if (k == 32) { + return "Space"; + } else if (k == 33) { + return "PageUp"; + } else if (k == 34) { + return "PageDown"; + } else if (k == 35) { + return "End"; + } else if (k == 36) { + return "Home"; + } else if (k == 37) { + return "⬅️"; + } else if (k == 38) { + return "⬆️"; + } else if (k == 39) { + return "➡️"; + } else if (k == 40) { + return "⬇️"; + } else if (k == 45) { + return "Insert"; + } else if (k == 46) { + return "Delete"; + } else if (k == 144) { + return "NumLock"; + } else if (k == 186) { + return ";"; + } else if (k == 187) { + return "="; + } else if (k == 188) { + return ","; + } else if (k == 189) { + return "-"; + } else if (k == 190) { + return "."; + } else if (k == 191) { + return "/"; + } else if (k == 192) { + return "`"; + } else if (k == 219) { + return "["; + } else if (k == 220) { + return "|"; + } else if (k == 221) { + return "]"; + } else if (k == 222) { + return "'"; + } else if (k == -1) { + return ""; + } else { + return "未知"; + } + } + + }, + "8-按键松开": function () { + //注册一个松开按键行为函数 + core.registerAction( + 'keyUp', + 'keyUped', + function (keyCode) { + core.plugin.keyUped(keyCode); + return false; + }, + 100 + ); + //主程序,当松开某个键时进行判断 + this.keyUped = function (keyCode) { + //console.log(keyCode) + if (keyCode == flags.keylist[10][1]) { + flags.transfer = 0; + } + + } + + }, + "9-MC界面绘制": function () { + this.mcjiemian = function () { + core.plugin.lockMC(); + core.plugin.drawMC(); + core.plugin.invMC(); + core.plugin.bagMC(); + core.plugin.boxMC(); + core.plugin.workMC(); + core.plugin.furMC(); + core.plugin.comMC(); + }; + //打开任何界面都先锁定用户控制;科技、自定义按键等界面返回时,对物品栏解封 + this.lockMC = function () { + if (flags.interface == "物品栏") { + flags.cursor2 = 0; + flags.curCache2 = [0, 0]; + flags.curKey = 0; + flags.pagenumber = 1; + flags.discard = 0; + core.unlockControl(); + } else { + for (var eq in flags.weapon) { + if (core.getEquip(0) == eq) { + flags.property.atkSpeed[0] -= flags.weapon[eq][2]; + flags.property.touchRange[0] -= flags.weapon[eq][3]; + } + } + core.unloadEquip(0); + core.lockControl(); + } + //科技、自定义按键界面返回时,对物品栏解封 + if (flags.tech == 0 && + flags.CustomKey == 0 && + flags.attrui == 0 && + flags.taskui == 0 && + flags.achiui == 0 && + core.status.event.id == null && + flags.farmUI == 0 && + flags.sleep != 0) { + if (flags.interface == "锁定") { + core.deleteCanvas('uievent'); + core.deleteCanvas('mcui'); + flags.interface = "物品栏"; + } + } else if (core.status.event.id != null) { + //core.stopAutomaticRoute(); + flags.interface = "锁定"; + } + + };//各个界面的显示与切换;绘制图标和数量 + this.drawMC = function () { + if (flags.interface == "物品栏") { + //core.ui._createUIEvent(); + if (core.getContextByName("mcui") == null) { + core.createCanvas("mcui", 0, 0, 416, 416, 126); + } else { + core.clearMap('mcui'); + } + if (core.getContextByName("inventory") == null) { + core.createCanvas("inventory", 0, 0, 416, 416, 127); + } else { + core.clearMap('inventory'); + } + if (core.getContextByName("itemname") == null) { + core.createCanvas("itemname", 0, 0, 416, 416, 125); + } else { + //core.clearMap('itemname'); + } + if (core.getContextByName("uievent") == null) { + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } else { + core.clearMap('uievent'); + //core.createCanvas("uievent", 0, 0, 416, 416, 130); + } + core.drawImage("mcui", "inventory.png", 0, 0, 416, 416); + core.setAlpha("uievent", 0.8); + //检测到物品栏界面有物品时,绘制图标和数量 + for (var i = 1; i < 10; i++) { + if ((flags.bag[i][0] && flags.bag[i][1] > 0 && core.hasItem(flags.bag[i][0])) || (core.getEquip(0) == flags.bag[i][0] && flags.bag[i][0] != null)) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i + 1) + 2, 384 + 2, 28, 28); + if (flags.bag[i][1] >= 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 20, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 12, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (flags.bag[i][0] && !core.hasItem(flags.bag[i][0]) && core.getEquip(0) != flags.bag[i][0] || flags.bag[i][1] <= 0) { + flags.bag[i] = ["", 0]; + } + } + core.setAlpha("uievent", 1); + core.plugin.hpBar(3); + core.plugin.expBar(); + core.plugin.hungerBar(); + } else if (flags.interface == "背包") { + if (core.getContextByName("mcui") == null) { + core.createCanvas("mcui", 0, 0, 416, 416, 126); + } else { + core.clearMap('mcui'); + } + core.drawImage("mcui", "bag.png", 0, 0, 416, 416); + core.drawImage("mcui", "heropic1.png", 98, 98, 92, 124); + if (core.getContextByName("uievent") == null) { + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } else { + core.clearMap('uievent'); + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } + //检测到背包界面有物品时,绘制图标和数量 + for (var i = 1; i < 37; i++) { + if ((flags.bag[i][0] && flags.bag[i][1] > 0 && core.hasItem(flags.bag[i][0])) || (core.getEquip(0) == flags.bag[i][0] && flags.bag[i][0] != null)) { + if (i >= 1 && i < 10) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i + 1) + 2, 384 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 20, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 12, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 10 && i < 19) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 8) + 2, 256 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 20, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 12, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 19 && i < 28) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 17) + 2, 288 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 20, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 12, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 28 && i < 37) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 26) + 2, 320 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 20, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 12, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + } + if (flags.bag[i][0] && !core.hasItem(flags.bag[i][0]) && core.getEquip(0) != flags.bag[i][0] || flags.bag[i][1] <= 0) { + flags.bag[i] = ["", 0]; + } + } + //绘制已穿戴装备及耐久条 + for (var j in flags.equip) { + var ceil = Math.ceil((flags.equDur[j][0] / flags.equip[j][0]) * 26); + if (core.getEquip(1) == j) { + core.drawIcon("uievent", core.getEquip(1), 192 + 2, 192 + 2, 28, 28); + core.fillRect("uievent", 195, 217, 26, 3, "#2b2b2b"); + core.fillRect("uievent", 195, 217, ceil, 3, "#00bf29"); + } else if (core.getEquip(2) == j) { + core.drawIcon("uievent", core.getEquip(2), 64 + 2, 96 + 2, 28, 28); + core.fillRect("uievent", 67, 121, 26, 3, "#2b2b2b"); + core.fillRect("uievent", 67, 121, ceil, 3, "#00bf29"); + } else if (core.getEquip(3) == j) { + core.drawIcon("uievent", core.getEquip(3), 64 + 2, 128 + 2, 28, 28); + core.fillRect("uievent", 67, 153, 26, 3, "#2b2b2b"); + core.fillRect("uievent", 67, 153, ceil, 3, "#00bf29"); + } else if (core.getEquip(4) == j) { + core.drawIcon("uievent", core.getEquip(4), 64 + 2, 160 + 2, 28, 28); + core.fillRect("uievent", 67, 185, 26, 3, "#2b2b2b"); + core.fillRect("uievent", 67, 185, ceil, 3, "#00bf29"); + } else if (core.getEquip(5) == j) { + core.drawIcon("uievent", core.getEquip(5), 64 + 2, 192 + 2, 28, 28); + core.fillRect("uievent", 67, 217, 26, 3, "#2b2b2b"); + core.fillRect("uievent", 67, 217, ceil, 3, "#00bf29"); + } + } + //绘制2X2合成栏的物品的图标和数量 + var com = flags.comlist1; + if (com[0][0]) { + core.drawIcon("uievent", com[0][0], 224 + 2, 96 + 2, 28, 28); + if (com[0][1] >= 0 && com[0][1] < 10) { + core.ui.fillText("uievent", com[0][1].toString(), 224 + 20, 96 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[0][1] >= 10) { + core.ui.fillText("uievent", com[0][1].toString(), 224 + 12, 96 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[1][0]) { + core.drawIcon("uievent", com[1][0], 256 + 2, 96 + 2, 28, 28); + if (com[1][1] >= 0 && com[1][1] < 10) { + core.ui.fillText("uievent", com[1][1].toString(), 256 + 20, 96 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[1][1] >= 10) { + core.ui.fillText("uievent", com[1][1].toString(), 256 + 12, 96 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[2][0]) { + core.drawIcon("uievent", com[2][0], 224 + 2, 128 + 2, 28, 28); + if (com[2][1] >= 0 && com[2][1] < 10) { + core.ui.fillText("uievent", com[2][1].toString(), 224 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[2][1] >= 10) { + core.ui.fillText("uievent", com[2][1].toString(), 224 + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[3][0]) { + core.drawIcon("uievent", com[3][0], 256 + 2, 128 + 2, 28, 28); + if (com[3][1] >= 0 && com[3][1] < 10) { + core.ui.fillText("uievent", com[3][1].toString(), 256 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[3][1] >= 10) { + core.ui.fillText("uievent", com[3][1].toString(), 256 + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + for (var j = 0; j < 4; j++) { + if (com[j][0] && com[j][1] <= 0) { + com[j] = ["", 0]; + } + } + } else if (flags.interface == "箱子") { + if (core.getContextByName("mcui") == null) { + core.createCanvas("mcui", 0, 0, 416, 416, 126); + } else { + core.clearMap('mcui'); + } + var pic = "box1.png"; + switch (flags.extraGrid) { + case 0: + pic = "box1.png"; + break; + case 3: + pic = "box2.png"; + break; + case 6: + pic = "box3.png"; + break; + case 9: + pic = "box4.png"; + break; + case 12: + pic = "box5.png"; + break; + case 15: + pic = "box6.png"; + break; + case 18: + pic = "box7.png"; + break; + case 21: + pic = "box8.png"; + break; + case 24: + pic = "box9.png"; + break; + case 27: + pic = "box10.png"; + break; + } + core.drawImage("mcui", pic, 0, 0, 416, 416); + if (core.getContextByName("uievnet") == null) { + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } else { + core.clearMap('uievent'); + } + if (flags.discard == 0) { + core.fillText("uievent", "箱子", 56, 26, "black", ui.prototype._buildFont(14, false), 64); + } else if (flags.discard == 1) { + core.fillText("uievent", "垃圾桶", 56, 26, "black", ui.prototype._buildFont(14, false), 64); + } + //检测到箱子界面有物品时,绘制图标和数量 + for (var i = 1; i < 91; i++) { + if (i < 37 /*&& (flags.bag[i][0] && flags.bag[i][1] > 0 && core.hasItem(flags.bag[i][0])) || (core.getEquip(0) == flags.bag[i][0] && flags.bag[i][0] != null)*/) { + if (i >= 1 && i < 10) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i + 1) + 2, 384 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 20, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 12, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 10 && i < 19) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 8) + 2, 256 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 20, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 12, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 19 && i < 28) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 17) + 2, 288 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 20, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 12, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 28 && i < 37) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 26) + 2, 320 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 20, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 12, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + } + var box = flags.boxlist[flags.box_xy][i - 37]; + if (i >= 37 && flags.boxlist[flags.box_xy][i - 37][0] && flags.boxlist[flags.box_xy][i - 37][1] > 0) { + if (i >= 37 && i < 46) { + core.drawIcon("uievent", box[0], 32 * (i - 35) + 2, 32 + 2, 28, 28); + if (box[1] > 0 && box[1] < 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 35) + 20, 32 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (box[1] >= 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 35) + 12, 32 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 46 && i < 55) { + core.drawIcon("uievent", box[0], 32 * (i - 44) + 2, 64 + 2, 28, 28); + if (box[1] > 0 && box[1] < 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 44) + 20, 64 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (box[1] >= 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 44) + 12, 64 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 55 && i < 64) { + core.drawIcon("uievent", box[0], 32 * (i - 53) + 2, 96 + 2, 28, 28); + if (box[1] > 0 && box[1] < 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 53) + 20, 96 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (box[1] >= 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 53) + 12, 96 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 64 && i < 73) { + core.drawIcon("uievent", box[0], 32 * (i - 62) + 2, 128 + 2, 28, 28); + if (box[1] > 0 && box[1] < 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 62) + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (box[1] >= 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 62) + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 73 && i < 82) { + core.drawIcon("uievent", box[0], 32 * (i - 71) + 2, 160 + 2, 28, 28); + if (box[1] > 0 && box[1] < 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 71) + 20, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (box[1] >= 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 71) + 12, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 82 && i < 91) { + core.drawIcon("uievent", box[0], 32 * (i - 80) + 2, 192 + 2, 28, 28); + if (box[1] > 0 && box[1] < 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 80) + 20, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (box[1] >= 10) { + core.ui.fillText("uievent", box[1].toString(), 32 * (i - 80) + 12, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + } + if (flags.bag[i][0] && !core.hasItem(flags.bag[i][0]) && core.getEquip(0) != flags.bag[i][0] || flags.bag[i][1] <= 0) { + flags.bag[i] = ["", 0]; + } + if (flags.bag[i][0] == "" && flags.bag[i][1] != 0) { + flags.bag[i] = ["", 0]; + } + if (i >= 37 && box[0] && box[1] <= 0) { + flags.boxlist[flags.box_xy][i - 37] = ["", 0]; + } + } + } else if (flags.interface == "工作台") { + if (core.getContextByName("mcui") == null) { + core.createCanvas("mcui", 0, 0, 416, 416, 126); + } else { + core.clearMap('mcui'); + } + core.drawImage("mcui", "CraftingTable.png", 0, 0, 416, 416); + if (core.getContextByName("uievent") == null) { + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } else { + core.clearMap('uievent'); + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } + //检测到工作台界面有物品时,绘制图标和数量 + for (var i = 1; i < 37; i++) { + if ((flags.bag[i][0] && flags.bag[i][1] > 0 && core.hasItem(flags.bag[i][0])) || (core.getEquip(0) == flags.bag[i][0] && flags.bag[i][0] != null)) { + if (i >= 1 && i < 10) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i + 1) + 2, 384 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 20, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 12, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 10 && i < 19) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 8) + 2, 256 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 20, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 12, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 19 && i < 28) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 17) + 2, 288 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 20, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 12, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 28 && i < 37) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 26) + 2, 320 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 20, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 12, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + } + if (flags.bag[i][0] && !core.hasItem(flags.bag[i][0]) && core.getEquip(0) != flags.bag[i][0] || flags.bag[i][1] <= 0) { + flags.bag[i] = ["", 0]; + } + } + //绘制3X3合成栏的物品的图标和数量 + var com = flags.comlist2; + if (com[0][0]) { + core.drawIcon("uievent", com[0][0], 128 + 2, 128 + 2, 28, 28); + if (com[0][1] >= 0 && com[0][1] < 10) { + core.ui.fillText("uievent", com[0][1].toString(), 128 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[0][1] >= 10) { + core.ui.fillText("uievent", com[0][1].toString(), 128 + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[1][0]) { + core.drawIcon("uievent", com[1][0], 160 + 2, 128 + 2, 28, 28); + if (com[1][1] >= 0 && com[1][1] < 10) { + core.ui.fillText("uievent", com[1][1].toString(), 160 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[1][1] >= 10) { + core.ui.fillText("uievent", com[1][1].toString(), 160 + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[2][0]) { + core.drawIcon("uievent", com[2][0], 192 + 2, 128 + 2, 28, 28); + if (com[2][1] >= 0 && com[2][1] < 10) { + core.ui.fillText("uievent", com[2][1].toString(), 192 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[2][1] >= 10) { + core.ui.fillText("uievent", com[2][1].toString(), 192 + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[3][0]) { + core.drawIcon("uievent", com[3][0], 128 + 2, 160 + 2, 28, 28); + if (com[3][1] >= 0 && com[3][1] < 10) { + core.ui.fillText("uievent", com[3][1].toString(), 128 + 20, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[3][1] >= 10) { + core.ui.fillText("uievent", com[3][1].toString(), 128 + 12, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[4][0]) { + core.drawIcon("uievent", com[4][0], 160 + 2, 160 + 2, 28, 28); + if (com[4][1] >= 0 && com[4][1] < 10) { + core.ui.fillText("uievent", com[4][1].toString(), 160 + 20, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[4][1] >= 10) { + core.ui.fillText("uievent", com[4][1].toString(), 160 + 12, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[5][0]) { + core.drawIcon("uievent", com[5][0], 192 + 2, 160 + 2, 28, 28); + if (com[5][1] >= 0 && com[5][1] < 10) { + core.ui.fillText("uievent", com[5][1].toString(), 192 + 20, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[5][1] >= 10) { + core.ui.fillText("uievent", com[5][1].toString(), 192 + 12, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[6][0]) { + core.drawIcon("uievent", com[6][0], 128 + 2, 192 + 2, 28, 28); + if (com[6][1] >= 0 && com[6][1] < 10) { + core.ui.fillText("uievent", com[6][1].toString(), 128 + 20, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[6][1] >= 10) { + core.ui.fillText("uievent", com[6][1].toString(), 128 + 12, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[7][0]) { + core.drawIcon("uievent", com[7][0], 160 + 2, 192 + 2, 28, 28); + if (com[7][1] >= 0 && com[7][1] < 10) { + core.ui.fillText("uievent", com[7][1].toString(), 160 + 20, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[7][1] >= 10) { + core.ui.fillText("uievent", com[7][1].toString(), 160 + 12, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (com[8][0]) { + core.drawIcon("uievent", com[8][0], 192 + 2, 192 + 2, 28, 28); + if (com[8][1] >= 0 && com[8][1] < 10) { + core.ui.fillText("uievent", com[8][1].toString(), 192 + 20, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (com[8][1] >= 10) { + core.ui.fillText("uievent", com[8][1].toString(), 192 + 12, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + for (var j = 0; j < 9; j++) { + if (com[j][0] && com[j][1] <= 0) { + com[j] = ["", 0]; + } + } + } else if (flags.interface == "熔炉") { + if (core.getContextByName("mcui") == null) { + core.createCanvas("mcui", 0, 0, 416, 416, 126); + } else { + core.clearMap('mcui'); + } + core.drawImage("mcui", "Furnace.png", 0, 0, 416, 416); + if (core.getContextByName("uievent") == null) { + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } else { + core.clearMap('uievent'); + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } + //检测到熔炉界面有物品时,绘制图标和数量 + for (var i = 1; i < 37; i++) { + if ((flags.bag[i][0] && flags.bag[i][1] > 0 && core.hasItem(flags.bag[i][0])) || (core.getEquip(0) == flags.bag[i][0] && flags.bag[i][0] != null)) { + if (i >= 1 && i < 10) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i + 1) + 2, 384 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 20, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i + 1) + 12, 384 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 10 && i < 19) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 8) + 2, 256 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 20, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 8) + 12, 256 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 19 && i < 28) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 17) + 2, 288 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 20, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 17) + 12, 288 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } else if (i >= 28 && i < 37) { + core.drawIcon("uievent", flags.bag[i][0], 32 * (i - 26) + 2, 320 + 2, 28, 28); + if (flags.bag[i][1] > 0 && flags.bag[i][1] < 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 20, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (flags.bag[i][1] >= 10) { + core.ui.fillText("uievent", flags.bag[i][1].toString(), 32 * (i - 26) + 12, 320 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + } + if (flags.bag[i][0] && !core.hasItem(flags.bag[i][0]) && core.getEquip(0) != flags.bag[i][0] || flags.bag[i][1] <= 0) { + flags.bag[i] = ["", 0]; + } + } + var fur = flags.furnacelist[flags.box_xy]; + if (fur[0][0]) { + core.drawIcon("uievent", fur[0][0], 160 + 2, 192 + 2, 28, 28); + if (fur[0][1] >= 0 && fur[0][1] < 10) { + core.ui.fillText("uievent", fur[0][1].toString(), 160 + 20, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (fur[0][1] >= 10) { + core.ui.fillText("uievent", fur[0][1].toString(), 160 + 12, 192 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (fur[1][0]) { + core.drawIcon("uievent", fur[1][0], 160 + 2, 128 + 2, 28, 28); + if (fur[1][1] >= 0 && fur[1][1] < 10) { + core.ui.fillText("uievent", fur[1][1].toString(), 160 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (fur[1][1] >= 10) { + core.ui.fillText("uievent", fur[1][1].toString(), 160 + 12, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (fur[2][0]) { + core.drawIcon("uievent", fur[2][0], 256 + 2, 160 + 2, 28, 28); + if (fur[2][1] >= 0 && fur[2][1] < 10) { + core.ui.fillText("uievent", fur[2][1].toString(), 256 + 20, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } else if (fur[2][1] >= 10) { + core.ui.fillText("uievent", fur[2][1].toString(), 256 + 12, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + } + } + if (flags.furnacelist[flags.box_xy][3][0] > 0) { + core.ui.fillText("uievent", flags.furnacelist[flags.box_xy][3][0], 192 + 21, 160 + 10, "white", ui.prototype._buildFont(18, false), 64); + } + core.ui.fillText("uievent", flags.furnacelist[flags.box_xy][3][1], 213, 210, "white", ui.prototype._buildFont(18, false), 64); + for (var i = 0; i < 3; i++) { + if (fur[i][0] && fur[i][1] <= 0) { + fur[i] = ["", 0]; + } + } + } + };//绘制物品栏光标并实时检测主手物品 + this.invMC = function () { + //主手装备武器时,会根据武器ID进行攻速和攻击范围加成 + if (flags.interface == "物品栏") { + for (var i = 1; i < 10; i++) { + if (flags.cursor1 == i) { + core.strokeRect("inventory", 63 + ((i - 1) * 32), 384, 32, 32, "#ebebeb", 2); + // core.ui.drawLine("inventory", 64 - 1 + ((i - 1) * 32), 384, 96 + 1 + ((i - 1) * 32), 384, "white", 3); + // core.ui.drawLine("inventory", 64 + ((i - 1) * 32), 384 - 1, 64 + ((i - 1) * 32), 416 + 1, "white", 3); + // core.ui.drawLine("inventory", 64 - 1 + ((i - 1) * 32), 416, 96 + 1 + ((i - 1) * 32), 416, "white", 3); + // core.ui.drawLine("inventory", 96 + ((i - 1) * 32), 384 - 1, 96 + ((i - 1) * 32), 416 + 1, "white", 3); + } + } + } + for (var i = 1; i < 10; i++) { + if (flags.interface == "物品栏" && flags.cursor1 == i && flags.bag[i][0]) { + if (core.canEquip(flags.bag[i][0], true) && core.getEquip(0) && core.getEquip(0) != flags.bag[i][0] && core.getEquipTypeById(flags.bag[i][0]) == 0) { + for (var eq in flags.weapon) { + if (core.getEquip(0) == eq) { + flags.property.atkSpeed[0] -= flags.weapon[eq][2]; + flags.property.touchRange[0] -= flags.weapon[eq][3]; + } + } + core.unloadEquip(0); + core.loadEquip(flags.bag[i][0]); + for (var eq in flags.weapon) { + if (core.getEquip(0) == eq) { + flags.property.atkSpeed[0] += flags.weapon[eq][2]; + flags.property.touchRange[0] += flags.weapon[eq][3]; + } + } + } else if (core.canEquip(flags.bag[i][0], true) && !core.getEquip(0) && core.getEquipTypeById(flags.bag[i][0]) == 0) { + core.loadEquip(flags.bag[i][0]); + for (var eq in flags.weapon) { + if (core.getEquip(0) == eq) { + flags.property.atkSpeed[0] += flags.weapon[eq][2]; + flags.property.touchRange[0] += flags.weapon[eq][3]; + } + } + } else if ((!core.canEquip(flags.bag[i][0], true) && core.getEquip(0)) || (core.canEquip(flags.bag[i][0], true) && core.getEquipTypeById(flags.bag[i][0]) != 0)) { + for (var eq in flags.weapon) { + if (core.getEquip(0) == eq) { + flags.property.atkSpeed[0] -= flags.weapon[eq][2]; + flags.property.touchRange[0] -= flags.weapon[eq][3]; + } + } + core.unloadEquip(0); + } else if (!core.canEquip(flags.bag[i][0], true) && !core.getEquip(0)) { } + } else if (flags.interface == "物品栏" && flags.cursor1 == i && !flags.bag[i][0]) { + if (core.getEquip(0)) { + for (var eq in flags.weapon) { + if (core.getEquip(0) == eq) { + flags.property.atkSpeed[0] -= flags.weapon[eq][2]; + flags.property.touchRange[0] -= flags.weapon[eq][3]; + } + } + core.unloadEquip(0); + } + } + } + var t = 0; + for (var j in flags.tool) { + if (core.getEquip(0) == j) { + t = 1; + if (core.getContextByName("durableBar") == null) { + core.createCanvas("durableBar", 0, 0, 416, 416, 131); + } else { + core.clearMap('durableBar'); + } + core.fillRect("durableBar", (32 * (flags.cursor1 - 1)) + 67, 409, 26, 3, "#2b2b2b"); + core.fillRect("durableBar", (32 * (flags.cursor1 - 1)) + 67, 409, Math.ceil((flags.toolDur[j][0] / flags.tool[j][0]) * 26), 3, "#00bf29"); + } + } + for (var j in flags.weapon) { + if (core.getEquip(0) == j) { + t = 1; + if (core.getContextByName("durableBar") == null) { + core.createCanvas("durableBar", 0, 0, 416, 416, 131); + } else { + core.clearMap('durableBar'); + } + core.fillRect("durableBar", (32 * (flags.cursor1 - 1)) + 67, 409, 26, 3, "#2b2b2b"); + core.fillRect("durableBar", (32 * (flags.cursor1 - 1)) + 67, 409, Math.ceil((flags.weaDur[j][0] / flags.weapon[j][0]) * 26), 3, "#00bf29"); + } + } + if (t == 0) { + core.deleteCanvas("durableBar"); + //core.clearMap('durable'); + } + core.plugin.atkRange(); + + };//绘制背包光标;实现物品交换逻辑 + this.bagMC = function () { + if (flags.interface == "背包") { + manage(flags.comlist1, 40); + } + };//绘制箱子光标;实现物品交换逻辑 + this.boxMC = function () { + if (flags.interface == "箱子") { + var grid = 63; + switch (flags.extraGrid) { + case 0: + grid = 63; + break; + case 3: + grid = 66; + break; + case 6: + grid = 69; + break; + case 9: + grid = 72; + break; + case 12: + grid = 75; + break; + case 15: + grid = 78; + break; + case 18: + grid = 81; + break; + case 21: + grid = 84; + break; + case 24: + grid = 87; + break; + case 27: + grid = 90; + break; + } + manage(flags.boxlist[flags.box_xy], grid); + } + };//绘制工作台光标;实现物品交换逻辑 + this.workMC = function () { + if (flags.interface == "工作台") { + manage(flags.comlist2, 45); + } + };//绘制熔炉光标;实现物品交换逻辑 + this.furMC = function () { + if (flags.interface == "熔炉") { + manage(flags.furnacelist[flags.box_xy], 38); + } + };//当处于背包界面或合成界面时,遍历合成栏上的物品是否满足合成表 + this.comMC = function () { + if (flags.interface == "背包") { + var cancraft = 0; + var guide = flags.craftGuide1; + cra: for (var id in guide) { + var i = 0; + do { + if (guide[id][i][0] == flags.comlist1[0][0] && + guide[id][i][1] == flags.comlist1[1][0] && + guide[id][i][2] == flags.comlist1[2][0] && + guide[id][i][3] == flags.comlist1[3][0]) { + if (flags.comlist1[0][1] != 0 || + flags.comlist1[1][1] != 0 || + flags.comlist1[2][1] != 0 || + flags.comlist1[3][1] != 0) { + core.drawIcon("uievent", id, 320, 128); + core.ui.fillText("uievent", guide[id][i][4].toString(), 320 + 20, 128 + 29, "white", ui.prototype._buildFont(15, false), 64); + flags.comlist1[4] = [id, guide[id][i][4]]; + cancraft = 1; + break cra; + } + } + i++; + } while (guide[id][i] != undefined) + } + if (!cancraft) { + flags.comlist1[4] = ["", 0]; + } + } else if (flags.interface == "工作台") { + var cancraft = 0; + var guide = flags.craftGuide2; + cra: for (var id in guide) { + var i = 0; + do { + if (guide[id][i][0] == flags.comlist2[0][0] && + guide[id][i][1] == flags.comlist2[1][0] && + guide[id][i][2] == flags.comlist2[2][0] && + guide[id][i][3] == flags.comlist2[3][0] && + guide[id][i][4] == flags.comlist2[4][0] && + guide[id][i][5] == flags.comlist2[5][0] && + guide[id][i][6] == flags.comlist2[6][0] && + guide[id][i][7] == flags.comlist2[7][0] && + guide[id][i][8] == flags.comlist2[8][0]) { + if (flags.comlist2[0][1] != 0 || + flags.comlist2[1][1] != 0 || + flags.comlist2[2][1] != 0 || + flags.comlist2[3][1] != 0 || + flags.comlist2[4][1] != 0 || + flags.comlist2[5][1] != 0 || + flags.comlist2[6][1] != 0 || + flags.comlist2[7][1] != 0 || + flags.comlist2[8][1] != 0) { + core.drawIcon("uievent", id, 288, 160); + core.ui.fillText("uievent", guide[id][i][9].toString(), 288 + 20, 160 + 29, "white", ui.prototype._buildFont(15, false), 64); + flags.comlist2[9] = [id, guide[id][i][9]]; + cancraft = 1; + break cra; + } + } + i++; + } while (guide[id][i] != undefined) + } + if (!cancraft) { + flags.comlist2[9] = ["", 0]; + } + } + + }; + function switch1(b1, b2, m1, isget, changeNum) { + if (m1 == flags.cursor2 && !(b1[0] == b2[0] && b2[1] >= 64)) { + if (changeNum) { + if (isget) core.addItem(b1[0], -b1[1]); + else if (b1[0] != b2[0]) core.addItem(b1[0], b1[1]); + } + flags.bagCache = b2[0]; + b2[0] = b1[0]; + b1[0] = flags.bagCache; + if (b1[0] == b2[0]) { + if (b2[1] < 64) { + var min = 64 - b2[1]; + b2[1] += Math.min(min, b1[1]); + if (changeNum) core.addItem(b2[0], Math.min(min, b1[1])); + if (Math.min(min, b1[1]) == b1[1]) { + b1[0] = ""; + b1[1] = 0; + } else { + b1[0] = b2[0]; + b1[1] -= min; + } + } else { + b1[0] = b2[0]; + b1[1] -= b2[1] - 64; + } + } + else { + flags.bagCache = b2[1]; + b2[1] = b1[1]; + b1[1] = flags.bagCache; + if (changeNum && b1[0] && b1[0] != b2[0]) { + if (isget) core.addItem(b1[0], b1[1]); + else core.addItem(b1[0], -b1[1]); + } + } + flags.cursor2 = 0; + flags.cursplit = 0; + } + else if (m1 == flags.cursplit && (b1[0] == b2[0] && b2[1] < 64) || b2[0] == "") { + var num; + isget ? num = Math.ceil(b1[1] / 2) : num = 1; + if (changeNum) isget ? core.addItem(b1[0], -num) : core.addItem(b1[0], num); + b2[0] = b1[0]; + b2[1] += num; + b1[1] -= num; + if (b1[1] == 0) b1[0] = ""; + flags.cursor2 = 0; + flags.cursplit = 0; + } + } + function collect1(b1, n1) { + if (flags.curdou != 1 || flags.bag[0][0] == "" || flags.bag[0][1] >= 64) { + flags.curdou = 0; + return; + } + for (var i = 0; i < n1; i++) { + if (b1[i][0] == flags.bag[0][0] && b1[i][1] < 10) { + var min = Math.min(b1[i][1], 64 - flags.bag[0][1]); + flags.bag[0][1] += min; + b1[i][1] -= min; + if (flags.bag[0][1] >= 64) { + flags.curdou = 0; + return; + } + } + } + for (var i = 1; i < 37; i++) { + if (flags.bag[i][0] == flags.bag[0][0] && flags.bag[i][1] < 10) { + var min = Math.min(flags.bag[i][1], 64 - flags.bag[0][1]); + flags.bag[0][1] += min; + flags.bag[i][1] -= min; + core.addItem(flags.bag[i][0], -min); + if (flags.bag[0][1] >= 64) { + flags.curdou = 0; + return; + } + } + } + for (var i = 0; i < n1; i++) { + if (b1[i][0] == flags.bag[0][0]) { + var min = Math.min(b1[i][1], 64 - flags.bag[0][1]); + flags.bag[0][1] += min; + b1[i][1] -= min; + if (flags.bag[0][1] >= 64) { + flags.curdou = 0; + return; + } + } + } + for (var i = 1; i < 37; i++) { + if (flags.bag[i][0] == flags.bag[0][0]) { + var min = Math.min(flags.bag[i][1], 64 - flags.bag[0][1]); + flags.bag[0][1] += min; + flags.bag[i][1] -= min; + core.addItem(flags.bag[i][0], -min); + if (flags.bag[0][1] >= 64) { + flags.curdou = 0; + return; + } + } + } + } + function putmore(b1, b2, m1) { + flags.cursor2 = core.plugin.clickloc(flags.curlight[0][0], flags.curlight[0][1]); + if (flags.cursor2 == undefined) { + flags.cursor2 = 0; + flags.cursplit = 0; + return; + } + if (!flags.curarr.includes(flags.cursor2)) { + if (m1 == 1) { + if (flags.curarr.length == 0) { + flags.curarr.push(flags.cursor2); + } else { + if ((flags.curarr[0] < 37 && flags.cursor2 < 37) || + (flags.curarr[0] >= 37 && flags.cursor2 >= 37)) { + flags.curarr.push(flags.cursor2); + } else { + flags.cursor2 = 0; + flags.cursplit = 0; + return; + } + } + } else if (m1 == 2) { + flags.curarr.push(flags.cursor2); + } + } else { + flags.cursor2 = 0; + flags.cursplit = 0; + return; + } + var b; + if (flags.cursor2 < 37) { + b = flags.bag[flags.cursor2]; + if (b[0] == flags.bag[0][0] && !flags.cursor2 in flags.splitCache2 == false) { + var c = flags.cursor2; + flags.splitCache2[c] = [b[0], b[1]]; + } + } else { + b = b2[flags.cursor2 - 37]; + if (b[0] == flags.bag[0][0] && flags.cursor2 in flags.splitCache2 == false) { + var c = flags.cursor2; + flags.splitCache2[c] = [b[0], b[1]]; + } + } + if (m1 == 1) { + if (!flags.splitCache1) flags.splitCache1 = JSON.parse(JSON.stringify(b1)); + if (b != undefined && ((b[0] == b1[0] && b[1] < 64) || b[0] == "") && flags.curarr.length <= flags.splitCache1[1]) { + if (flags.cursor2 >= 1 && flags.cursor2 < 37) { + //core.addItem(b1[0], Math.floor(b1[1] / flags.curarr.length)); + b2 = flags.bag; + var long = 0; + for (var c in flags.curarr) { + if (b2[flags.curarr[c]][0] == flags.splitCache1[0] || b2[flags.curarr[c]][0] == "") { + long += 1; + } + } + for (var c in flags.curarr) { + if (b2[flags.curarr[c]][0] == flags.splitCache1[0] || b2[flags.curarr[c]][0] == "") { + b2[flags.curarr[c]][0] = b1[0]; + if (long > 1 && b2[flags.curarr[c]][1] > 0) { + if (flags.curarr[c] in flags.splitCache2 && + flags.splitCache2[flags.curarr[c]][0] != "" && + flags.splitCache2[flags.curarr[c]][0] == flags.splitCache1[0]) { + var num = b2[flags.curarr[c]][1] - flags.splitCache2[flags.curarr[c]][1]; + core.addItem(b2[flags.curarr[c]][0], -num); + b2[flags.curarr[c]][1] = flags.splitCache2[flags.curarr[c]][1]; + } else { + core.addItem(b2[flags.curarr[c]][0], -b2[flags.curarr[c]][1]); + b2[flags.curarr[c]][1] = 0; + } + } + b2[flags.curarr[c]][1] += Math.floor(flags.splitCache1[1] / long); + core.addItem(b2[flags.curarr[c]][0], Math.floor(flags.splitCache1[1] / long)); + } + } + b1[1] = flags.splitCache1[1]; + b1[1] = flags.splitCache1[1] - (Math.floor(flags.splitCache1[1] / long) * long); + } else if (flags.cursor2 >= 37 && flags.cursor2 < 91) { + var long = 0; + for (var c in flags.curarr) { + if (b2[flags.curarr[c] - 37][0] == flags.splitCache1[0] || b2[flags.curarr[c] - 37][0] == "") { + long += 1; + } + } + for (var c in flags.curarr) { + if (b2[flags.curarr[c] - 37][0] == flags.splitCache1[0] || b2[flags.curarr[c] - 37][0] == "") { + b2[flags.curarr[c] - 37][0] = b1[0]; + if (long > 1 && b2[flags.curarr[c] - 37][1] > 0) { + if (flags.curarr[c] in flags.splitCache2 && + flags.splitCache2[flags.curarr[c]][0] != "" && + flags.splitCache2[flags.curarr[c]][0] == flags.splitCache1[0]) { + b2[flags.curarr[c] - 37][1] = flags.splitCache2[flags.curarr[c]][1]; + } else { + b2[flags.curarr[c] - 37][1] = 0; + } + } + b2[flags.curarr[c] - 37][1] += Math.floor(flags.splitCache1[1] / long); + } + } + b1[1] = flags.splitCache1[1]; + b1[1] = flags.splitCache1[1] - (Math.floor(flags.splitCache1[1] / long) * long); + } + } + } else if (m1 == 2) { + if (b != undefined && ((b[0] == b1[0] && b[1] < 64) || b[0] == "")) { + if (flags.cursor2 >= 1 && flags.cursor2 < 37) { + core.addItem(b1[0], 1); + b[0] = b1[0]; + b[1] += 1; + b1[1] -= 1; + if (b1[1] == 0) { + b1[0] = ""; + core.deleteCanvas('viscous'); + //core.clearMap('viscous'); + } + } else if (flags.cursor2 >= 37 && flags.cursor2 < 91) { + b2[flags.cursor2 - 37][0] = b1[0]; + b2[flags.cursor2 - 37][1] += 1; + b1[1] -= 1; + if (b1[1] == 0) { + b1[0] = ""; + core.deleteCanvas('viscous'); + //core.clearMap('viscous'); + } + } + } + } + flags.cursor2 = 0; + flags.cursplit = 0; + } + function model(m1, list1, num1) { + if (flags.bag[0][0] == "") { + //拿起 + if (m1 >= 1 && m1 < 37 && flags.bag[m1][0]) { + flags.curCache2[0] = m1; + var bag0 = flags.bag[flags.curCache2[0]]; + switch1(bag0, flags.bag[0], m1, 1, 1); + } else if (m1 >= 37 && m1 < num1 && list1[m1 - 37][0]) { + flags.curCache2[0] = m1; + var list = list1[flags.curCache2[0] - 37]; + switch1(list, flags.bag[0], m1, 1, 0); + } + } else if (flags.bag[0][0] != "" && flags.longleft == 0 && flags.longright == 0) { + //放下 + if (m1 >= 1 && m1 < 37) { + flags.curCache2[0] = m1; + var bag0 = flags.bag[flags.curCache2[0]]; + switch1(flags.bag[0], bag0, m1, 0, 1); + } else if (m1 >= 37 && m1 < num1) { + flags.curCache2[0] = m1; + var list = list1[flags.curCache2[0] - 37]; + switch1(flags.bag[0], list, m1, 0, 0); + } + } + } + function manage(list1, num1) { + if (flags.transfer) return; + collect1(list1, num1 - 36); + if (!flags.mingan) { + model(flags.cursor2, list1, num1 + 1); + model(flags.cursplit, list1, num1 + 1); + } + if (flags.longleft) putmore(flags.bag[0], list1, 1); + else if (flags.longright && flags.bag[0][0] != "") putmore(flags.bag[0], list1, 2); + core.plugin.drawMC(); + core.plugin.comMC(); + } + + + }, + "10-背包存储": function () { + this.beibaocunchu = function () { + if ("I705" in core.status.hero.items.tools) { + flags.level[1] += core.status.hero.items.tools["I705"]; + core.plugin.jingyandengji(); + core.addItem("I705", -core.status.hero.items.tools["I705"]); + } + var singleItemSum = 0; + //将已获得的装备映射到MC背包 + for (let id in core.status.hero.items.equips) { + if (id) { + for (var j = 1; j <= 37; j++) { + if (id == flags.bag[j][0]) { + for (var k in flags.bag) { + if (k != '0' && id == flags.bag[k][0]) { + singleItemSum += flags.bag[k][1]; + } + } + if (singleItemSum == core.status.hero.items.equips[id] && core.getEquip(0) == flags.bag[j][0]) { + flags.bag[j][1] += 1; + singleItemSum += 1; + } + if (singleItemSum < core.status.hero.items.equips[id] && core.getEquip(0) != flags.bag[j][0]) { + flags.bag[j][1] += core.status.hero.items.equips[id] - singleItemSum; + } + if (singleItemSum < core.status.hero.items.equips[id] && core.getEquip(0) == flags.bag[j][0]) { + flags.bag[j][1] += core.status.hero.items.equips[id] - singleItemSum + 1; + } + if (singleItemSum > core.status.hero.items.equips[id] && core.getEquip(0) != flags.bag[j][0]) { + flags.bag[j][1] -= singleItemSum - core.status.hero.items.equips[id]; + } + if (singleItemSum > core.status.hero.items.equips[id] && core.getEquip(0) == flags.bag[j][0]) { + flags.bag[j][1] -= singleItemSum - core.status.hero.items.equips[id] - 1; + } + // core.plugin.lockMC(); + // core.plugin.drawMC(); + //实际7个,装备1个,背包6个,显示7个 + //实际5个,装备1个,背包4个,应该显示5个 + //实际7个,装备0个,背包7个,显示7个 + //实际5个,装备0个,背包5个,应该显示5个 + singleItemSum = 0; + break; + } + if (j == 37) { + for (var k = 1; k < 37; k++) { + if (!flags.bag[k][0]) { + flags.bag[k][0] = id; + if (core.getEquip(0) == flags.bag[k][0]) { + flags.bag[k][1] = core.status.hero.items.equips[id] + 1; + } else { + flags.bag[k][1] = core.status.hero.items.equips[id]; + } + // core.plugin.lockMC(); + // core.plugin.drawMC(); + break; + } + if (k == 36) { + //console.log("背包满了"); + } + } + } + } + } + } + //将已获得的消耗道具映射到MC背包 + for (let id in core.status.hero.items.tools) { + if (id) { + for (var j = 1; j <= 37; j++) { + if (id == flags.bag[j][0]) { + for (var k in flags.bag) { + if (k != '0' && id == flags.bag[k][0]) { + singleItemSum += flags.bag[k][1]; + } + } + if (singleItemSum < core.status.hero.items.tools[id]) { + flags.bag[j][1] += core.status.hero.items.tools[id] - singleItemSum; + } + if (singleItemSum > core.status.hero.items.tools[id]) { + flags.bag[j][1] -= singleItemSum - core.status.hero.items.tools[id]; + } + // core.plugin.lockMC(); + // core.plugin.drawMC(); + singleItemSum = 0; + break; + } + if (j == 37) { + for (var k = 1; k < 37; k++) { + if (!flags.bag[k][0]) { + flags.bag[k][0] = id; + flags.bag[k][1] = core.status.hero.items.tools[id]; + // core.plugin.lockMC(); + // core.plugin.drawMC(); + break; + } + if (k == 36) { + //console.log("背包满了"); + } + } + } + } + } + } + //将已获得的永久道具映射到MC背包 + for (let id in core.status.hero.items.constants) { + if (id) { + for (var j = 1; j <= 37; j++) { + if (id == flags.bag[j][0]) { + for (var k in flags.bag) { + if (k != '0' && id == flags.bag[k][0]) { + singleItemSum += flags.bag[k][1]; + } + } + if (singleItemSum != core.status.hero.items.constants[id]) { + flags.bag[j][1] = 1; + } + // core.plugin.lockMC(); + // core.plugin.drawMC(); + singleItemSum = 0; + break; + } + if (j == 37) { + for (var k = 1; k < 37; k++) { + if (!flags.bag[k][0]) { + flags.bag[k][0] = id; + flags.bag[k][1] = 1; + // core.plugin.lockMC(); + // core.plugin.drawMC(); + break; + } + if (k == 36) { + //console.log("背包满了"); + } + } + } + } + } + } + //背包内物品超过64个后的处理 + for (var i = 1; i < 37; i++) { + if (flags.bag[i] && flags.bag[i][1] > 64) { + var number = flags.bag[i][1]; + flags.bag[i][1] = 64; + for (var j = 1; j < 37; j++) { + if (flags.bag[j][0] == flags.bag[i][0] && flags.bag[j][1] < 64) { + flags.bag[j][1] += number - 64; + number = 0; + break; + } + } + if (number) { + for (var j = 1; j < 37; j++) { + if (!flags.bag[j][0]) { + flags.bag[j][0] = flags.bag[i][0]; + flags.bag[j][1] = number - 64; + break; + } + } + } + // core.plugin.lockMC(); + // core.plugin.drawMC(); + } + } + core.plugin.lockMC(); + core.plugin.drawMC(); + core.plugin.invMC(); + core.plugin.comMC(); + } + + }, + "11-熔炉熔炼检测": function () { + this.ronglujiance = function () { + //检测当某个熔炉燃料和原料都有且成品栏有空间时,执行熔炼行为 + flags.smeltcheck = 0; + for (var i in flags.furnacelist) { + if (flags.furnacelist[i][1][1] > 0 && (flags.furnacelist[i][0][1] > 0 || flags.furnacelist[i][3][1] > 0)) { + flags.smeltcheck = 1; + for (var j in flags.furGuide) { + if (j == flags.furnacelist[i][1][0]) { + if (flags.furGuide[j][0] == flags.furnacelist[i][2][0] && flags.furnacelist[i][2][1] < 64) { + if (flags.furnacelist[i][3][0] == 1) { + flags.furnacelist[i][2][1] += 1; + flags.furnacelist[i][1][1] -= 1; + if (flags.furnacelist[i][1][1] <= 0) { + flags.furnacelist[i][1] = ["", 0]; + } + flags.furnacelist[i][3][0] = -1; + } else if (flags.furnacelist[i][3][0] == -1) { + flags.furnacelist[i][3][0] = 4; + } + } else if (!flags.furnacelist[i][2][0]) { + if (flags.furnacelist[i][3][0] == 1) { + flags.furnacelist[i][2][0] = flags.furGuide[j][0]; + flags.furnacelist[i][2][1] += 1; + flags.furnacelist[i][1][1] -= 1; + if (flags.furnacelist[i][1][1] <= 0) { + flags.furnacelist[i][1] = ["", 0]; + } + flags.furnacelist[i][3][0] = -1; + } else if (flags.furnacelist[i][3][0] == -1) { + flags.furnacelist[i][3][0] = 4; + } + } + } + } + } else { + flags.furnacelist[i][3][0] = -1; + } + //熔炉燃料值变化及自动补充燃料值 + if (flags.furnacelist[i][3][0] > 0) { + flags.furnacelist[i][3][0] -= 1; + if (flags.furnacelist[i][3][1] > 0) { + flags.furnacelist[i][3][1] -= 1; + } else { + if (flags.furnacelist[i][0][0]) { + flags.furnacelist[i][0][1] -= 1; + for (var j in flags.fuelGuide) { + if (j == flags.furnacelist[i][0][0]) { + flags.furnacelist[i][3][1] = flags.fuelGuide[j][0]; + flags.furnacelist[i][3][1] -= 1; + } + } + } + } + } + } + if (flags.interface == "熔炉") { + core.plugin.mcjiemian(); + core.plugin.beibaocunchu(); + } + setTimeout(function () { + if (flags.smeltcheck == 1) { + core.plugin.ronglujiance(); + } + }, 1000); + } + + }, + "12-并行处理": function () { + function playbgm() { + core.setVolume(0.8); + var bgm = ""; + if (core.status.floorId == null) { + if (core.rand2(2) == 0) { + bgm = 'menu1.mp3'; + } else { + bgm = 'menu3.mp3'; + } + } else { + var r1 = core.rand2(3); + if (r1 != 0) { + if (core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 343 || + core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 344) { + if (core.rand2(2) == 0) { + bgm = 'desert1.mp3'; + } else { + bgm = 'desert2.mp3'; + } + } else if (core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 339 || + core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 340) { + if (core.rand2(2) == 0) { + bgm = 'forest2.mp3'; + } else { + bgm = 'forest5.mp3'; + } + } else if (core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 345 || + core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 348) { + if (core.rand2(2) == 0) { + bgm = 'ocean1.mp3'; + } else { + bgm = 'ocean3.mp3'; + } + } else if (core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 341 || + core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y) == 342) { + if (core.rand2(2) == 0) { + bgm = 'snowfield1.mp3'; + } else { + bgm = 'snowfield2.mp3'; + } + } else { + var r2 = core.rand2(8); + switch (r2) { + case 0: + bgm = 'zhu1.mp3'; + break; + case 1: + bgm = 'zhu3.mp3'; + break; + case 2: + bgm = 'zhu4.mp3'; + break; + case 3: + bgm = 'zhu5.mp3'; + break; + case 4: + bgm = 'zhu10.mp3'; + break; + case 5: + bgm = 'zhu11.mp3'; + break; + case 6: + bgm = 'zhu12.mp3'; + break; + case 7: + bgm = 'zhu6.mp3'; + break; + default: + bgm = 'zhu1.mp3'; + break; + } + } + } else { + var r2 = core.rand2(8); + switch (r2) { + case 0: + bgm = 'zhu1.mp3'; + break; + case 1: + bgm = 'zhu3.mp3'; + break; + case 2: + bgm = 'zhu4.mp3'; + break; + case 3: + bgm = 'zhu5.mp3'; + break; + case 4: + bgm = 'zhu10.mp3'; + break; + case 5: + bgm = 'zhu11.mp3'; + break; + case 6: + bgm = 'zhu12.mp3'; + break; + case 7: + bgm = 'zhu16.mp3'; + break; + default: + bgm = 'zhu1.mp3'; + break; + } + } + } + core.playBgm(bgm); + //console.log(bgm) + } + setInterval(function () { + if (!window.core) return; + if (!core.isPlaying) return; + if (!core.isPlaying()) return; + if (flags.worldlist["0"]["游戏规则"][1] == "开") { + var recover = 1; + if (flags.worldlist["0"]["难度"] == "简单") { + recover = 3; + } else if (flags.worldlist["0"]["难度"] == "普通") { + recover = 2; + } else if (flags.worldlist["0"]["难度"] == "困难") { + recover = 1; + } + //饱食度满时恢复生命 + if (flags.hunger == undefined) return; + if (core.status.hero.hp > 0 && + flags.hunger[0] / flags.hunger[1] >= 0.9 && + core.status.hero.hp < core.status.hero.hpmax) { + if (flags.hunger[0] / flags.hunger[1] >= 0.98) { + core.status.hero.hp += recover * 2; + core.plugin.baoshidu(9); + } + core.status.hero.hp += recover; + if (core.status.hero.hp > core.status.hero.hpmax) core.status.hero.hp = core.status.hero.hpmax; + core.plugin.baoshidu(3); + //core.updateStatusBar(true, true); + core.plugin.hpBar(1); + } + } + //背景音乐切换 + if (core.musicStatus.playingBgm == null) { + playbgm(); + } + if (flags.step != 2) { + flags.step = 2; + } + }, 5000); + setInterval(function () { + if (!window.core) return; + if (!core.isPlaying) return; + if (!core.isPlaying()) return; + if (core.status.hero.loc.x == -1 && core.status.hero.loc.y == -1) return; + if (core.status.hero.hp > 0) { + //时间流逝 + if (flags.worldlist["0"]["游戏规则"][5] == "开") { + flags.time += 1; + flags.sleep += 1; + } else if (flags.worldlist["0"]["游戏规则"][5] == "永昼") { + flags.time = 720; + } else if (flags.worldlist["0"]["游戏规则"][5] == "永夜") { + flags.time = 0; + } + core.updateStatusBar(true); + if (flags.time >= 1440) flags.time = 0; + //时间段变化 + if (flags.dimension[0] == "主世界" && flags.dimension[1] == "地表") { + if (flags.time == 240) { + for (var eid in flags.enemylist) { + core.plugin.delenemy(eid, 0); + } + flags.tip[0] = "太阳升起了"; + } else if (flags.time == 1200) { + flags.tip[0] = "月亮升起了"; + } + if (flags.time >= 240 && flags.time < 360) { + flags.timePeriod = "日出"; + core.plugin.drawLight('fg1', 0.4 - (flags.time - 240) * 0.0033, [[0, 0, 0]], 1); + } else if (flags.time >= 360 && flags.time < 1080) { + flags.timePeriod = "白天"; + core.plugin.drawLight('fg1', 0, [[0, 0, 0]], 1); + } else if (flags.time >= 1080 && flags.time < 1200) { + flags.timePeriod = "黄昏"; + core.plugin.drawLight('fg1', (flags.time - 1080) * 0.0033, [[0, 0, 0]], 1); + } else if ((flags.time >= 1200 && flags.time < 1440) || (flags.time >= 0 && flags.time < 240)) { + flags.timePeriod = "夜晚"; + core.plugin.drawLight('fg1', 0.4, [[0, 0, 0]], 1); + } + } else if (flags.dimension[0] == "主世界" && flags.dimension[1] == "洞穴") { + if (flags.time >= 240 && flags.time < 360) { + flags.timePeriod = "日出"; + } else if (flags.time >= 360 && flags.time < 1080) { + flags.timePeriod = "白天"; + } else if (flags.time >= 1080 && flags.time < 1200) { + flags.timePeriod = "黄昏"; + } else if ((flags.time >= 1200 && flags.time < 1440) || (flags.time >= 0 && flags.time < 240)) { + flags.timePeriod = "夜晚"; + } + core.plugin.drawLight('fg1', 1, [[208, 208, 240]]); + } + if (core.status.floorId != "MT0" && core.status.floorId != "zzt1") { + var num = Object.keys(flags.enemylist).length; + var ref = (num * (num + 3) / 2) + 2; + if (flags.timePeriod != "夜晚") ref *= 3; + if (core.rand2(ref) == 0) core.plugin.shuaguai(); + } + + if (!core.getContextByName("bg1") || !core.getContextByName("sj1") || !core.getContextByName("fg1")) { + core.createCanvas("bg1", 0, 0, 416, 416, 11); + core.createCanvas("sj1", 0, 0, 416, 416, 31); + core.createCanvas("fg1", 0, 0, 416, 416, 61); + core.createCanvas("fg2", 0, 0, 416, 416, 101); + } + core.plugin.drawLight('bg1', 0.2, [ + [0, 0, 0] + ]); + core.plugin.drawLight('sj1', 0.2, [ + [0, 0, 0] + ]); + } + //自动保存 + if (flags.worldindex > 1) { + if (flags.worldindex % 600 == 0) { + core.doSL(flags.worldlist["0"]["世界存档"][0], "save"); + flags.tip[0] = "当前进度已保存"; + } + if (flags.worldindex % 3600 == 0) { + core.doSL(flags.worldlist["0"]["世界存档"][1], "save"); + } + flags.worldindex += 1; + } + //挖掘进度累减 + if (flags.dig && flags.dig[4] > 0) { + flags.dig[4] -= 1000; + if (flags.dig[4] <= 0) { + flags.dig = [0, 0, 0, 0, 0]; + core.deleteCanvas("dig"); + } + } + }, 1000); + + }, + "13-科技": function () { + this.techUI = function () {//todo 科技实际效果 + //显示科技界面及相关逻辑 + if (flags.tech == 1) { + if (core.getContextByName("uievent") == null) { + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } else { + core.clearMap('uievent'); + core.createCanvas("uievent", 0, 0, 416, 416, 130); + } + var ctx = "uievent"; + flags.interface = "锁定"; + core.drawImage(ctx, "tech1.png", 0, 0, 416, 416); + core.fillBoldText(ctx, "退出", 370, 406, "#ff6661", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText(ctx, "科技点:", 16, 28, "white", "black", ui.prototype._buildFont(18, false), 100); + core.ui.fillText(ctx, flags.tpointnum, 92, 28, "#fdffc2", ui.prototype._buildFont(16, false), 416); + core.fillBoldText(ctx, "世界等级:", 244, 28, "white", "black", ui.prototype._buildFont(18, false), 416); + core.ui.fillText(ctx, flags.worldlevel, 340, 28, "#941a00", ui.prototype._buildFont(16, false), 416); + for (var i = 1; i < 13; i++) { + var te = ""; + var icon = "I560"; + switch (i) { + case 1: + te = "灵感"; + icon = "I673"; + break; + case 2: + te = "领悟"; + icon = "I629"; + break; + case 3: + te = "生命之力"; + icon = "I636"; + break; + case 4: + te = "增幅"; + icon = "I708"; + break; + case 5: + te = "坚定"; + icon = "I618"; + break; + case 6: + te = "敏捷"; + icon = "I572"; + break; + case 7: + te = "耐力"; + icon = "I633"; + break; + case 8: + te = "空间扩展"; + icon = "I709"; + break; + case 9: + te = "守护之韧"; + icon = "I610"; + break; + case 10: + te = "锋锐恒久"; + icon = "I581"; + break; + case 11: + te = "生机"; + icon = "I625"; + break; + case 12: + te = "商贾之道"; + icon = "I533"; + break; + } + var lv1 = flags.tech1[te][0]; + var lv2 = flags.tech1[te][1]; + var text1 = te + lv1; + var text2 = lv1 >= lv2 ? flags.tech2[te][0][lv2 - 2] : flags.tech2[te][0][lv1 - 1]; + var text3 = "科技点" + flags.tpointnum + "/" + flags.tech3[te][0][lv1 - 1] + "," + "钱币" + flags.coppernum + "/" + flags.tech3[te][1][lv1 - 1]; + var x1 = i % 2 == 0 ? 218 : 16; + var y1 = (Math.ceil(i / 2) - 1) * 56 + 70; + var x2 = i % 2 == 0 ? 258 : 56; + var y2 = (Math.ceil(i / 2) - 1) * 56 + 86; + var x3 = i % 2 == 0 ? 258 : 56; + var y3 = (Math.ceil(i / 2) - 1) * 56 + 104; + var x4 = i % 2 == 0 ? 219 : 18; + var y4 = (Math.ceil(i / 2) - 1) * 56 + 74; + var color1 = "#e0f0ff"; + var color2 = "#197b39"; + var color3 = "#7d150c"; + //绘制文本 + core.drawIcon(ctx, icon, x4, y4, 32, 32); + core.ui.fillText(ctx, text1, x1, y1, color1, ui.prototype._buildFont(14, false), 416); + core.ui.fillText(ctx, text2, x2, y2, color1, ui.prototype._buildFont(14, false), 150); + if (lv1 < lv2) { + if (flags.tpointnum >= flags.tech3[te][0][lv1 - 1] && + flags.coppernum >= flags.tech3[te][1][lv1 - 1]) { + core.ui.fillText(ctx, text3, x3, y3, color2, ui.prototype._buildFont(14, false), 150); + } else { + core.ui.fillText(ctx, text3, x3, y3, color3, ui.prototype._buildFont(14, false), 150); + } + } + } + } else if (flags.tech != 1 && flags.tech != -1) { + core.clearMap('uievent'); + flags.interface = "锁定"; + core.drawImage("uievent", "tech2.png", 0, 0, 416, 416); + core.fillBoldText("uievent", "确定升级该科技吗?", 120, 52, "white", "black", ui.prototype._buildFont(20, false), 416); + core.fillBoldText("uievent", "升级所需", 270, 176, "white", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText("uievent", "升级", 272, 354, "white", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText("uievent", "返回", 336, 354, "white", "black", ui.prototype._buildFont(18, false), 416); + var num = 2; + for (var te in flags.tech2) { + if (flags.tech == num) { + var icon = "I560"; + switch (te) { + case "灵感": + icon = "I673"; + break; + case "领悟": + icon = "I629"; + break; + case "生命之力": + icon = "I636"; + break; + case "增幅": + icon = "I708"; + break; + case "坚定": + icon = "I618"; + break; + case "敏捷": + icon = "I572"; + break; + case "耐力": + icon = "I633"; + break; + case "空间扩展": + icon = "I709"; + break; + case "守护之韧": + icon = "I610"; + break; + case "锋锐恒久": + icon = "I581"; + break; + case "生机": + icon = "I625"; + break; + case "商贾之道": + icon = "I533"; + break; + } + var lv = flags.tech1[te]; + var t1 = flags.tech2[te][1]; + var t2 = flags.tech2[te][2]; + var t3 = flags.tech2[te][3]; + var needt = flags.tech3[te][0]; + var needc = flags.tech3[te][1]; + var needw = flags.tech3[te][2]; + var color1 = "black"; + var color2 = "#a3fabf"; + var color3 = "#7d150c"; + //名称+等级 + core.ui.fillText("uievent", te + lv[0], 200, 112, color1, ui.prototype._buildFont(15, false), 416); + core.drawIcon("uievent", icon, 151, 92, 32, 32); + //科技升级 + if (lv[0] < lv[1]) { + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: (te + lv[0] + " -> " + te + (lv[0] + 1)), + left: 10, + top: 160, + maxWidth: 224, + fontSize: 15, + color: color1, + align: "center" + }); + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: (t1[lv[0] - 1]), + left: 10, + top: 210, + maxWidth: 224, + fontSize: 15, + color: color1, + align: "center" + }); + } else { + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: (te + lv[0]), + left: 10, + top: 160, + maxWidth: 224, + fontSize: 15, + color: color1, + align: "center" + }); + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: ("已满级"), + left: 10, + top: 210, + maxWidth: 224, + fontSize: 15, + color: color1, + align: "center" + }); + } + //升级提升2 + if (t2[lv[0] - 1]) { + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: (t2[lv[0] - 1]), + left: 10, + top: 256, + maxWidth: 224, + fontSize: 15, + color: color1, + align: "center" + }); + } + //科技描述 + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: t3[0], + left: 14, + top: 330, + maxWidth: 224, + fontSize: 15, + color: color1, + align: "center" + }); + //升级条件 + if (lv[0] < lv[1]) { + core.ui.fillText("uievent", "科技点 " + flags.tpointnum + "/" + needt[lv[0] - 1], 270, 210, flags.tpointnum >= needt[lv[0] - 1] ? color2 : color3, ui.prototype._buildFont(15, false), 416); + core.ui.fillText("uievent", "钱币 " + flags.coppernum + "/" + needc[lv[0] - 1], 270, 240, flags.coppernum >= needc[lv[0] - 1] ? color2 : color3, ui.prototype._buildFont(15, false), 416); + } + //升级限制 + if (flags.worldlevel < needw[lv[0] - 1]) { + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: "需求世界等级" + (needw[lv[0] - 1]) + "级", + left: 270, + top: 316, + maxWidth: 128, + fontSize: 13, + color: color3, + align: "left" + }); + } + break; + } else { + num += 1; + } + } + } else if (flags.tech == -1) { + core.drawImage("uievent", "tech3.png", 0, 0, 416, 416); + core.ui._uievent_drawTextContent({ + ctx: "uievent", + text: "1、初始世界等级为1级,满级10级。世界等级会影响到敌怪属性、敌怪掉落、地图资源生成、BOSS奖励箱内资源丰厚程度等。\n2、每次世界等级提升后,都可以在该界面领取丰厚的世界奖励,每级世界等级仅限一次。\n3、目前已消耗(${flags.tpointsum})个科技点。\n4、再次点击右上角的“问号”按钮可以返回科技界面。", + left: 32, + top: 64, + maxWidth: 352, + fontSize: 15, + color: "black", + align: "left" + }); + } + //世界等级设定 + if (flags.tpointsum >= 1 && flags.worldlevel == 1 || + flags.tpointsum >= 6 && flags.worldlevel == 2 || + flags.tpointsum >= 19 && flags.worldlevel == 3 || + flags.tpointsum >= 47 && flags.worldlevel == 4 || + flags.tpointsum >= 95 && flags.worldlevel == 5 || + flags.tpointsum >= 170 && flags.worldlevel == 6 || + flags.tpointsum >= 263 && flags.worldlevel == 7 || + flags.tpointsum >= 374 && flags.worldlevel == 8 || + flags.tpointsum >= 505 && flags.worldlevel == 9) { + flags.worldlevel += 1; + core.playSound("worldlevelup.ogg"); + flags.tip[0] = "世界等级提升了,欢迎来到" + flags.worldlevel + "级世界!"; + core.ui.drawStatusBar(); + } + } + + }, + "14-自定义按键": function () { + this.zidingyianjian = function () { + //显示自定义按键界面 + if (flags.CustomKey == 1) { + var ctx = "uievent"; + flags.interface = "锁定"; + if (core.getContextByName(ctx) == null) { + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } else { + core.clearMap(ctx); + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } + core.drawImage(ctx, "anjian.png", 0, 0, 416, 416); + core.fillBoldText(ctx, "退出", 354, 392, "#ff6661", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText(ctx, "自定义按键", 160, 42, "white", "black", ui.prototype._buildFont(20, false), 416); + core.fillBoldText(ctx, "上一页", 100, 392, "white", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText(ctx, "下一页", 270, 392, "white", "black", ui.prototype._buildFont(18, false), 416); + core.ui.fillText(ctx, flags.pagenumber, 206, 390, "black", ui.prototype._buildFont(15, false), 416); + for (var i = 1; i < 4; i++) { + for (var j = 0; j < 6; j++) { + if (i == flags.pagenumber) { + core.ui.fillText(ctx, flags.keylist[(i - 1) * 6 + j][0], 44, 100 + (46 * j), "black", ui.prototype._buildFont(15, false), 416); + + core.drawTextContent(ctx, core.plugin.keyc(flags.keylist[(i - 1) * 6 + j][1]), { left: 274, top: 88 + (46 * j), maxWidth: 100, fontSize: 15, color: "black", align: "center" }); + } + } + } + //显示自定义快捷键界面光标 + if (flags.curKey) { + if (flags.pagenumber * 6 >= flags.curKey && flags.pagenumber * 6 - 5 <= flags.curKey) { + core.strokeRect(ctx, 272, 82 + 46 * (flags.curKey - ((flags.pagenumber - 1) * 6) - 1), 100, 30, "white"); + } else { + flags.curKey = 0; + } + } + } + } + + }, + "15-饱食度检测": function () { + this.baoshidu = function (a = 0) { + //饱食度与饥饿值 + if (flags.hunger[0] > 0) { + flags.hunger[2] += core.rand2(2 + a) + 1 + a; + if (flags.hunger[2] >= flags.hunger[3]) { + flags.hunger[2] = 0; + flags.hunger[0] -= 1; + } + } else if (flags.hunger[0] <= 0) { + flags.hunger[0] = 0; + flags.hunger[2] = 0; + core.status.hero.hp -= 1; + } + core.plugin.hungerBar(); + } + + }, + "16-耐久度检测": function () { + this.reduceDur = function (type, damage) { + var list; + var dur; + switch (type) { + case "wea": + list = flags.weapon; + dur = flags.weaDur; + break; + case "tool": + list = flags.tool; + dur = flags.toolDur; + break; + case "equ": + list = flags.equip; + dur = flags.equDur; + break; + } + if (type == "wea" || type == "tool") { + for (var i in list) { + if (core.getEquip(0) == i) { + dur[i][0] -= 1; + if (dur[i][0] <= 0 && core.hasEquip(i)) { + // 装备耐久度为零后消失 + core.unloadEquip(core.getEquipTypeById(i)); + core.addItem(i, -1); + dur[i][0] = list[i][0] + flags.extraDur[1]; + core.playSound("break1.ogg"); + } + break; + } + } + } else if (type == "equ") { + var a = Math.floor(damage / 5); + var b = damage % 5; + for (var i in list) { + for (var j = 1; j < 6; j++) { + if (core.getEquip(j) == i) { + dur[i][0] -= a; + if (b != 0) { + dur[i][0] -= 1; + b -= 1; + } + if (dur[i][0] <= 0 && core.hasEquip(i)) { + // 装备耐久度为零后消失 + core.unloadEquip(core.getEquipTypeById(i)); + core.addItem(i, -1); + dur[i][0] = list[i][0] + flags.extraDur[0]; + core.playSound("break2.ogg"); + } + } + } + } + } + + } + }, + "17-经验和等级": function () { + this.jingyandengji = function () { + //玩家获得经验并检测是否升级 + var levelup = 0; + do { + flags.level[2] = Math.floor(flags.level[0] / 10) * 5 + 100; + if (flags.level[1] >= flags.level[2]) { + flags.level[0] += 1; + flags.level[1] -= flags.level[2]; + levelup = 1; + } + } while (flags.level[1] >= flags.level[2]); + core.playSound("orb.ogg"); + if (levelup == 1) core.playSound("levelup.ogg"); + core.plugin.expBar(); + } + + }, + "18-自绘全屏标题": function () { + // 横屏下,使标题画面可以覆盖状态栏 554*422 + // 竖屏下,标题画面依然和游戏画面大小相同 422*422 + // 可能存在bug。遇到bug的话,可以在技术群内,找“理派四阵”反馈 + // 本插件需要开启“标题事件化”才能使用,且同时具有“标题”和“难度分歧”2个功能 + if (!core.flags.startUsingCanvas) return; + //请按照“低-高”的顺序,填写各个难度的英文名;默认的flags.hard依次为1、2、3……以此类推 + //和hard同样的顺序,填写各个难度的颜色 + //var hard = ["hard1", "hard2", "hard3"], + //hardColor = ["#3333DD", "#33DD33", "#DD3333"]; + + //以下部分,需要自行编写具体内容 + //绘制部分:需要横竖屏分别绘制 + + //绘制标题界面 + this.drawTitle = function () { + var ctx = document.getElementById("title").getContext('2d'); + core.clearMap(ctx); + if (1/*!core.domStyle.isVertical*/) { } + switch (core.getFlag("title", 0)) { + case "开始界面": + core.drawImage(ctx, "jz2.png", 0, 0); + core.fillBoldText(ctx, "我的世界:生存", 110, 70, "white", "black", + ui.prototype._buildFont(50, false), 400); + core.fillText(ctx, "MC_survival 1.0.0", 5, 398, "white", ui.prototype._buildFont(13, false), 400); + core.fillText(ctx, "@ 天堑无涯", 5, 415, "white", ui.prototype._buildFont(13, false), 400); + if (flags.worldcur != 5) core.strokeRect(ctx, 445, 368, 100, 30, "white"); + core.fillBoldText(ctx, "点我全屏~", 450, 390, "white", "black", + ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "作者在这里祝大家——新年快乐!", 330, 415, "white", ui.prototype._buildFont(15, false), 400); + core.drawImage(ctx, 'buttonA.png', 152, 140, 250, 36); + core.drawImage(ctx, 'buttonA.png', 152, 180, 250, 36); + core.drawImage(ctx, 'buttonA.png', 152, 240, 120, 36); + core.drawImage(ctx, 'buttonA.png', 282, 240, 120, 36); + core.drawTextContent(ctx, '单人游戏', { left: 72, top: 145, align: 'center', fontSize: 20 }); + core.drawTextContent(ctx, '多人游戏', { left: 72, top: 185, align: 'center', fontSize: 20 }); + core.drawTextContent(ctx, '教程关卡', { left: 137, top: 245, align: 'center', fontSize: 20 }); + core.drawTextContent(ctx, 'Mod', { left: 6, top: 245, align: 'center', fontSize: 20 }); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 154, 142, 246, 32, "#ebebeb"); + break; + case 2: + core.strokeRect(ctx, 154, 182, 246, 32, "#ebebeb"); + break; + case 3: + core.strokeRect(ctx, 154, 242, 116, 32, "#ebebeb"); + break; + case 4: + core.strokeRect(ctx, 284, 242, 116, 32, "#ebebeb"); + break; + case 5: + core.strokeRect(ctx, 405, 328, 140, 80, "white"); + break; + } + break; + case "选择世界": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.9); + core.fillRect(ctx, 170, 30, 220, 35, "black"); + core.setAlpha(ctx, 0.8); + core.fillRect(ctx, 0, 80, 554, 250, "black"); + core.setAlpha(ctx, 1); + core.strokeRect(ctx, 172, 32, 216, 31, "#9c9c9c"); + core.drawImage(ctx, 'buttonA.png', 85, 340, 190, 30); + core.drawImage(ctx, 'buttonA.png', 285, 340, 190, 30); + core.drawImage(ctx, 'buttonA.png', 85, 380, 90, 30); + core.drawImage(ctx, 'buttonA.png', 185, 380, 90, 30); + core.drawImage(ctx, 'buttonA.png', 285, 380, 90, 30); + core.drawImage(ctx, 'buttonA.png', 385, 380, 90, 30); + + core.fillText(ctx, "选择世界", 250, 20, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "进入选中的世界", 124, 360, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "创建新的世界", 330, 360, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "备份", 114, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "删除", 214, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "重命名", 309, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "返回", 414, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "<", 440, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldpage, 470, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, ">", 500, 70, "white", ui.prototype._buildFont(16, false), 400); + + if (flags.worldcur2 != 0) { + core.drawImage(ctx, 'buttonA.png', 6, 380, 68, 30); + core.fillText(ctx, "访问历史存档", 10, 398, "white", ui.prototype._buildFont(10, false), 400); + } + + if (core.getLocalStorage('world')) { + for (var i = 0; i < 4; i++) { + for (var j = 0; j < 2; j++) { + var num = (i * 4) + (j * 2) + 1; + if (core.getLocalStorage('world')[num + (flags.worldpage - 1) * 16]) { + var world = core.getLocalStorage('world')[num + (flags.worldpage - 1) * 16]; + core.fillRect(ctx, 70 + (220 * j), 90 + (58 * i), 50, 50, "white"); + core.fillText(ctx, world["世界名称"], 126 + (220 * j), 104 + (58 * i), "white", ui.prototype._buildFont(14, false), 400); + core.fillText(ctx, world["创建时间"], 126 + (220 * j), 120 + (58 * i), "#9c9c9c", ui.prototype._buildFont(13, false), 400); + core.fillText(ctx, world["游戏模式"] + "模式," + world["难度"], 126 + (220 * j), 136 + (58 * i), "#9c9c9c", ui.prototype._buildFont(13, false), 400); + } + } + } + } + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 87, 342, 186, 26, "#ebebeb"); + break; + case 2: + core.strokeRect(ctx, 287, 342, 186, 26, "#ebebeb"); + break; + case 3: + core.strokeRect(ctx, 87, 382, 86, 26, "#ebebeb"); + break; + case 4: + core.strokeRect(ctx, 187, 382, 86, 26, "#ebebeb"); + break; + case 5: + core.strokeRect(ctx, 287, 382, 86, 26, "#ebebeb"); + break; + case 6: + core.strokeRect(ctx, 387, 382, 86, 26, "#ebebeb"); + break; + case 7: + core.strokeRect(ctx, 65, 85, 220, 60, "#ebebeb"); + break; + case 8: + core.strokeRect(ctx, 285, 85, 220, 60, "#ebebeb"); + break; + case 9: + core.strokeRect(ctx, 65, 143, 220, 60, "#ebebeb"); + break; + case 10: + core.strokeRect(ctx, 285, 143, 220, 60, "#ebebeb"); + break; + case 11: + core.strokeRect(ctx, 65, 201, 220, 60, "#ebebeb"); + break; + case 12: + core.strokeRect(ctx, 285, 201, 220, 60, "#ebebeb"); + break; + case 13: + core.strokeRect(ctx, 65, 259, 220, 60, "#ebebeb"); + break; + case 14: + core.strokeRect(ctx, 285, 259, 220, 60, "#ebebeb"); + break; + } + switch (flags.worldcur2) { + case 1: + core.strokeRect(ctx, 65, 85, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 82, 100); + break; + case 2: + core.strokeRect(ctx, 285, 85, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 302, 100); + break; + case 3: + core.strokeRect(ctx, 65, 143, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 82, 158); + break; + case 4: + core.strokeRect(ctx, 285, 143, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 302, 158); + break; + case 5: + core.strokeRect(ctx, 65, 201, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 82, 216); + break; + case 6: + core.strokeRect(ctx, 285, 201, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 302, 216); + break; + case 7: + core.strokeRect(ctx, 65, 259, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 82, 274); + break; + case 8: + core.strokeRect(ctx, 285, 259, 220, 60, "#ebebeb"); + core.drawImage(ctx, "playarrow.png", 302, 274); + break; + } + break; + case "教程": + core.drawImage(ctx, "jz2.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 385, 380, 90, 30); + core.fillText(ctx, "敬请期待", 220, 100, "white", ui.prototype._buildFont(30, false), 400); + core.fillText(ctx, "返回", 414, 400, "white", ui.prototype._buildFont(16, false), 400); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 387, 382, 86, 26, "#ebebeb"); + break; + } + break; + case "模组": + core.drawImage(ctx, "jz2.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 385, 380, 90, 30); + core.fillText(ctx, "敬请期待", 220, 100, "white", ui.prototype._buildFont(30, false), 400); + core.fillText(ctx, "返回", 414, 400, "white", ui.prototype._buildFont(16, false), 400); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 387, 382, 86, 26, "#ebebeb"); + break; + } + break; + case "多人游戏": + core.drawImage(ctx, "jz2.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 385, 380, 90, 30); + core.fillText(ctx, "敬请期待", 220, 100, "white", ui.prototype._buildFont(30, false), 400); + core.fillText(ctx, "返回", 414, 400, "white", ui.prototype._buildFont(16, false), 400); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 387, 382, 86, 26, "#ebebeb"); + break; + } + break; + case "创建世界_游戏": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.9); + core.fillRect(ctx, 166, 80, 220, 35, "black"); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.strokeRect(ctx, 168, 82, 216, 31, "#9c9c9c"); + core.drawImage(ctx, 'buttonA.png', 36, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 196, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 356, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 166, 125, 220, 30); + core.drawImage(ctx, 'buttonA.png', 166, 165, 220, 30); + core.drawImage(ctx, 'buttonA.png', 166, 205, 220, 30); + core.drawImage(ctx, 'buttonA.png', 166, 245, 220, 30); + core.drawImage(ctx, 'buttonA.png', 85, 380, 180, 30); + core.drawImage(ctx, 'buttonA.png', 285, 380, 180, 30); + core.fillText(ctx, "游戏", 98, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界", 258, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "更多", 420, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界名称", 166, 72, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["世界名称"], 172, 100, "#9c9c9c", ui.prototype._buildFont(14, false), 400); + core.fillText(ctx, "游戏模式:" + flags.worldlist["0"]["游戏模式"], 226, 144, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "难度:" + flags.worldlist["0"]["难度"], 242, 184, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "游戏规则", 244, 224, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "MOD", 256, 264, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "创建新的世界", 130, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "取消", 358, 400, "white", ui.prototype._buildFont(16, false), 400); + core.drawTextContent(ctx, flags.worldDescribe, { left: 400, top: 145, align: 'left', color: "white", fontSize: 16, maxWidth: 150 }); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 198, 12, 156, 31, "#ebebeb"); + break; + case 2: + core.strokeRect(ctx, 358, 12, 156, 31, "#ebebeb"); + break; + case 3: + core.strokeRect(ctx, 168, 82, 216, 31, "#ebebeb"); + break; + case 4: + core.strokeRect(ctx, 168, 127, 216, 26, "#ebebeb"); + break; + case 5: + core.strokeRect(ctx, 168, 167, 216, 26, "#ebebeb"); + break; + case 6: + core.strokeRect(ctx, 168, 207, 216, 26, "#ebebeb"); + break; + case 7: + core.strokeRect(ctx, 168, 247, 216, 26, "#ebebeb"); + break; + case 8: + core.strokeRect(ctx, 87, 382, 176, 26, "#ebebeb"); + break; + case 9: + core.strokeRect(ctx, 287, 382, 176, 26, "#ebebeb"); + break; + } + break; + case "创建世界_世界": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.9); + core.fillRect(ctx, 84, 136, 380, 35, "black"); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.strokeRect(ctx, 86, 138, 376, 31, "#9c9c9c"); + core.drawImage(ctx, 'buttonA.png', 36, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 196, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 356, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 85, 380, 180, 30); + core.drawImage(ctx, 'buttonA.png', 285, 380, 180, 30); + core.drawImage(ctx, 'buttonA.png', 404, 180, 60, 30); + core.drawImage(ctx, 'buttonA.png', 404, 215, 60, 30); + core.drawImage(ctx, 'buttonA.png', 404, 250, 60, 30); + core.drawImage(ctx, 'buttonA.png', 84, 80, 180, 35); + core.drawImage(ctx, 'buttonA.png', 284, 80, 180, 35); + core.fillText(ctx, "游戏", 98, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界", 258, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "更多", 420, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界类型:" + flags.worldlist["0"]["世界选项"][0], 120, 102, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "自定义", 346, 102, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界生成器的种子", 86, 132, "white", ui.prototype._buildFont(14, false), 400); + core.fillText(ctx, flags.seed1 == undefined ? "留空以生成随机种子" : flags.seed1, 93, 158, "#9c9c9c", ui.prototype._buildFont(14, false), 400); + core.fillText(ctx, "生成结构", 86, 200, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "初始奖励箱", 86, 234, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界大小", 86, 268, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["世界选项"][1], 426, 200, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["世界选项"][2], 426, 234, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["世界选项"][3], 426, 268, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "创建新的世界", 130, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "取消", 358, 400, "white", ui.prototype._buildFont(16, false), 400); + core.drawTextContent(ctx, flags.worldDescribe, { left: 90, top: 280, align: 'left', color: "white", fontSize: 16, maxWidth: 300 }); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 38, 12, 156, 31, "#ebebeb"); + break; + case 2: + core.strokeRect(ctx, 358, 12, 156, 31, "#ebebeb"); + break; + case 3: + core.strokeRect(ctx, 86, 82, 176, 31, "#ebebeb"); + break; + case 4: + core.strokeRect(ctx, 286, 82, 176, 31, "#ebebeb"); + break; + case 5: + core.strokeRect(ctx, 86, 138, 376, 31, "#ebebeb"); + break; + case 6: + core.strokeRect(ctx, 406, 182, 56, 26, "#ebebeb"); + break; + case 7: + core.strokeRect(ctx, 406, 217, 56, 26, "#ebebeb"); + break; + case 8: + core.strokeRect(ctx, 406, 252, 56, 26, "#ebebeb"); + break; + case 9: + core.strokeRect(ctx, 87, 382, 176, 26, "#ebebeb"); + break; + case 10: + core.strokeRect(ctx, 287, 382, 176, 26, "#ebebeb"); + break; + } + break; + case "创建世界_更多": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 36, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 196, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 356, 10, 160, 35); + core.drawImage(ctx, 'buttonA.png', 85, 380, 180, 30); + core.drawImage(ctx, 'buttonA.png', 285, 380, 180, 30); + core.fillText(ctx, "游戏", 98, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界", 258, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "更多", 420, 30, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "创建新的世界", 130, 400, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "取消", 358, 400, "white", ui.prototype._buildFont(16, false), 400); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 38, 12, 156, 31, "#ebebeb"); + break; + case 2: + core.strokeRect(ctx, 198, 12, 156, 31, "#ebebeb"); + break; + case 3: + core.strokeRect(ctx, 87, 382, 176, 26, "#ebebeb"); + break; + case 4: + core.strokeRect(ctx, 287, 382, 176, 26, "#ebebeb"); + break; + } + break; + case "游戏规则": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 385, 50, 60, 30); + core.drawImage(ctx, 'buttonA.png', 385, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 385, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 385, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 385, 230, 60, 30); + core.drawImage(ctx, 'buttonA.png', 385, 290, 60, 30); + core.drawImage(ctx, 'buttonA.png', 385, 320, 60, 30); + core.drawImage(ctx, 'buttonA.png', 285, 380, 180, 30); + core.fillText(ctx, "玩家", 260, 40, "yellow", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "死亡经验惩罚", 100, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "生命自然恢复", 100, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "生物", 260, 130, "yellow", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "允许破坏性生物行为", 100, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "生成", 260, 190, "yellow", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "启用特殊事件", 100, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "生成幻翼", 100, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "世界规则", 240, 280, "yellow", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "游戏内时间流逝", 100, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "火焰蔓延", 100, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][0], 408, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][1], 408, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][2], 408, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][3], 408, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][4], 408, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][5], 408, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["游戏规则"][6], 408, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "完成", 358, 400, "white", ui.prototype._buildFont(16, false), 400); + switch (flags.worldcur) { + case 1: + core.strokeRect(ctx, 387, 52, 56, 26, "#ebebeb"); + break; + case 2: + core.strokeRect(ctx, 387, 82, 56, 26, "#ebebeb"); + break; + case 3: + core.strokeRect(ctx, 387, 142, 56, 26, "#ebebeb"); + break; + case 4: + core.strokeRect(ctx, 387, 202, 56, 26, "#ebebeb"); + break; + case 5: + core.strokeRect(ctx, 387, 232, 56, 26, "#ebebeb"); + break; + case 6: + core.strokeRect(ctx, 387, 292, 56, 26, "#ebebeb"); + break; + case 7: + core.strokeRect(ctx, 387, 322, 56, 26, "#ebebeb"); + break; + case 8: + core.strokeRect(ctx, 287, 382, 176, 26, "#ebebeb"); + break; + } + break; + case "自定义世界": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 100, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 260, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 320, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 260, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 320, 60, 30); + core.drawImage(ctx, 'buttonA.png', 240, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 310, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 310, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 310, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 310, 260, 60, 30); + core.drawImage(ctx, 'buttonA.png', 310, 320, 60, 30); + core.drawImage(ctx, 'buttonA.png', 380, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 380, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 380, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 380, 260, 60, 30); + core.drawImage(ctx, 'buttonA.png', 380, 320, 60, 30); + + core.drawImage(ctx, 'buttonA.png', 285, 380, 180, 30); + core.fillText(ctx, "自定义", 240, 30, "white", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "平原", 100, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "森林", 100, 130, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "丛林", 100, 190, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "针叶林", 100, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "雪原", 100, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "沙漠", 170, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "恶地", 170, 130, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "海洋", 170, 190, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "丘陵", 170, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "蘑菇岛", 170, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "河流", 240, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "水井", 310, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "废墟", 310, 130, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "岩浆池", 310, 190, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "废弃传送门", 310, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "雪屋", 310, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "前哨站", 380, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "村庄", 380, 130, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "沉船", 380, 190, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "沙漠神殿", 380, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "丛林神庙", 380, 310, "white", ui.prototype._buildFont(16, false), 400); + + core.fillText(ctx, flags.worldlist["0"]["地形"]["平原"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["平原"][0] == 2 ? "密集" : "稀疏"), 114, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["森林"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["森林"][0] == 2 ? "密集" : "稀疏"), 114, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["丛林"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["丛林"][0] == 2 ? "密集" : "稀疏"), 114, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["针叶林"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["针叶林"][0] == 2 ? "密集" : "稀疏"), 114, 280, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["雪原"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["雪原"][0] == 2 ? "密集" : "稀疏"), 114, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["沙漠"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["沙漠"][0] == 2 ? "密集" : "稀疏"), 184, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["恶地"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["恶地"][0] == 2 ? "密集" : "稀疏"), 184, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["海洋"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["海洋"][0] == 2 ? "密集" : "稀疏"), 184, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["丘陵"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["丘陵"][0] == 2 ? "密集" : "稀疏"), 184, 280, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["蘑菇岛"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["蘑菇岛"][0] == 2 ? "密集" : "稀疏"), 184, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["地形"]["河流"][0] == 1 ? "正常" : (flags.worldlist["0"]["地形"]["河流"][0] == 2 ? "密集" : "稀疏"), 254, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["水井"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["水井"][0] == 2 ? "密集" : "稀疏"), 324, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["废墟"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["废墟"][0] == 2 ? "密集" : "稀疏"), 324, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["岩浆池"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["岩浆池"][0] == 2 ? "密集" : "稀疏"), 324, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["废弃传送门"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["废弃传送门"][0] == 2 ? "密集" : "稀疏"), 324, 280, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["雪屋"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["雪屋"][0] == 2 ? "密集" : "稀疏"), 324, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["前哨站"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["前哨站"][0] == 2 ? "密集" : "稀疏"), 394, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["村庄"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["村庄"][0] == 2 ? "密集" : "稀疏"), 394, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["沉船"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["沉船"][0] == 2 ? "密集" : "稀疏"), 394, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["沙漠神殿"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["沙漠神殿"][0] == 2 ? "密集" : "稀疏"), 394, 280, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, flags.worldlist["0"]["结构"]["丛林神庙"][0] == 1 ? "正常" : (flags.worldlist["0"]["结构"]["丛林神庙"][0] == 2 ? "密集" : "稀疏"), 394, 340, "white", ui.prototype._buildFont(16, false), 400); + + core.fillText(ctx, "完成", 358, 400, "white", ui.prototype._buildFont(16, false), 400); + break; + case "单一群系": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 0, 554, 422, "black"); + core.setAlpha(ctx, 1); + core.drawImage(ctx, 'buttonA.png', 100, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 260, 60, 30); + core.drawImage(ctx, 'buttonA.png', 100, 320, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 80, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 140, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 200, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 260, 60, 30); + core.drawImage(ctx, 'buttonA.png', 170, 320, 60, 30); + core.drawImage(ctx, 'buttonA.png', 240, 80, 60, 30); + + core.drawImage(ctx, 'buttonA.png', 285, 380, 180, 30); + core.fillText(ctx, "自定义", 240, 30, "white", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, "平原", 100, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "森林", 100, 130, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "丛林", 100, 190, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "针叶林", 100, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "雪原", 100, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "沙漠", 170, 70, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "恶地", 170, 130, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "海洋", 170, 190, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "丘陵", 170, 250, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "蘑菇岛", 170, 310, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "随机", 240, 70, "white", ui.prototype._buildFont(16, false), 400); + + core.fillText(ctx, "选我", 114, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 114, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 114, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 114, 280, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 114, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 184, 100, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 184, 160, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 184, 220, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 184, 280, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 184, 340, "white", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "选我", 254, 100, "white", ui.prototype._buildFont(16, false), 400); + + core.fillText(ctx, "完成", 358, 400, "white", ui.prototype._buildFont(16, false), 400); + break; + case "确认删除框": + core.drawImage(ctx, "jz3.png", 0, 0); + core.setAlpha(ctx, 0.8); + core.fillRect(ctx, 0, 80, 554, 250, "black"); + core.setAlpha(ctx, 1); + if (core.getLocalStorage('world')) { + for (var i = 0; i < 4; i++) { + for (var j = 0; j < 2; j++) { + var num = (i * 4) + (j * 2) + 1; + if (core.getLocalStorage('world')[num]) { + var world = core.getLocalStorage('world')[num]; + core.fillRect(ctx, 70 + (220 * j), 90 + (58 * i), 50, 50, "white"); + core.fillText(ctx, world["世界名称"], 126 + (220 * j), 104 + (58 * i), "white", ui.prototype._buildFont(14, false), 400); + core.fillText(ctx, world["创建时间"], 126 + (220 * j), 120 + (58 * i), "#9c9c9c", ui.prototype._buildFont(13, false), 400); + core.fillText(ctx, world["游戏模式"] + "模式," + world["难度"], 126 + (220 * j), 136 + (58 * i), "#9c9c9c", ui.prototype._buildFont(13, false), 400); + } + } + } + } + core.drawImage(ctx, 'buttonA.png', 140, 330, 90, 30); + core.drawImage(ctx, 'buttonA.png', 320, 330, 90, 30); + core.fillText(ctx, "确认删除世界吗?该操作无法撤销", 90, 60, "white", ui.prototype._buildFont(30, false), 400); + core.fillText(ctx, "删除", 170, 350, "red", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "返回", 350, 350, "white", ui.prototype._buildFont(16, false), 400); + + break; + + } + + }//开始界面操作 + function StartInter(px, py, click) { + if (px >= 153 && px <= 404 && py >= 141 && py < 175) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "选择世界"); + } else if (px >= 153 && px <= 404 && py >= 182 && py < 216) { + flags.worldcur = 2; + if (click == 0) return; + core.playSound("取消"); + core.setFlag('title', "多人游戏"); + } else if (px >= 151 && px <= 273 && py >= 240 && py < 278) { + flags.worldcur = 3; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "教程"); + } else if (px >= 283 && px <= 404 && py >= 241 && py < 275) { + flags.worldcur = 4; + if (click == 0) return; + core.playSound("取消"); + flags.worldcur = 0; + core.setFlag('title', "模组"); + } else if (px >= 400 && px <= 554 && py >= 330 && py < 422) { + flags.worldcur = 5; + if (click == 0) return; + core.playSound("取消"); + flags.worldcur = 0; + core.plugin.triggerFulscreen(true); + } else { + flags.worldcur = 0; + } + }//进入选中的世界 + function Enterworld() { + var save = (flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16; + flags.worldlist["0"]["世界存档"] = [save, save + 1]; + core.removeFlag('start'); + document.getElementById('title').style.display = 'none'; + core.setFlag('title', 'character'); + core.doSL(save, "load"); + core.plugin.siwangjiance(); + }//选择世界操作 + function ChooseInter(px, py, click) { + if (px >= 85 && px <= 277 && py >= 340 && py < 370) {//进入选中世界 + flags.worldcur = 1; + if (click == 0) return; + core.playSound("取消"); + if (!flags.worldcur2) return; + Enterworld(); + } else if (px >= 286 && px <= 477 && py >= 340 && py < 370) {//创建世界 + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_游戏"); + } else if (px >= 85 && px <= 176 && py >= 380 && py < 411) {//备份 + flags.worldcur = 3; + if (click == 0) return; + core.playSound("取消"); + if (!flags.worldcur2) return; + for (var i = 1; i <= 1000; i++) { + if (!core.hasSave(i * 2 - 1) || !core.hasSave(i * 2)) { + core.getLocalForage("save" + ((flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16), null, data => { + core.setLocalForage("save" + (i * 2 - 1), data); + }); + core.getLocalForage("save" + ((flags.worldcur2 - 1) * 2 + 2 + (flags.worldpage - 1) * 16), null, data => { + core.setLocalForage("save" + (i * 2), data); + }); + var world = core.getLocalStorage('world'); + world[i * 2 - 1] = { + 世界名称: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界名称"], + 世界存档: [i * 2 - 1, i * 2], + 世界选项: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界选项"], + 游戏模式: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["游戏模式"], + 难度: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["难度"], + 游戏规则: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["游戏规则"], + 创建时间: core.formatDate().slice(0, 16), + }; + core.setLocalStorage('world', world); + alert("备份成功!"); + break; + } + } + } else if (px >= 185 && px <= 276 && py >= 381 && py < 411) {//删除世界 + flags.worldcur = 4; + if (click == 0) return; + core.playSound("取消"); + if (!flags.worldcur2) return; + core.setFlag('title', "确认删除框"); + } else if (px >= 286 && px <= 375 && py >= 382 && py < 409) {//重命名 + flags.worldcur = 5; + if (click == 0) return; + core.playSound("取消"); + if (!flags.worldcur2) return; + core.myprompt("请输入世界名称", "", function (result) { + if (result) { + var world = core.getLocalStorage('world'); + world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16] = { + 世界名称: result, + 世界存档: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界存档"], + 世界选项: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界选项"], + 游戏模式: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["游戏模式"], + 难度: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["难度"], + 游戏规则: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["游戏规则"], + 创建时间: world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["创建时间"], + }; + core.setLocalStorage('world', world); + return; + } + }); + } else if (px >= 385 && px <= 477 && py >= 380 && py < 409) {//返回开始界面 + flags.worldcur = 6; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "开始界面"); + } else if (px >= 2 && px <= 77 && py >= 375 && py < 414) {//访问历史存档 + if (click == 0 || !flags.worldcur2) return; + core.playSound("取消"); + var save = (flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16; + flags.worldlist["0"]["世界存档"] = [save, save + 1]; + core.removeFlag('start'); + document.getElementById('title').style.display = 'none'; + core.setFlag('title', 'character'); + core.doSL(save + 1, "load"); + core.plugin.siwangjiance(); + } else if (px >= 417 && px <= 476 && py >= 50 && py < 78) {//上一页 + if (click == 0) return; + if (flags.worldpage > 1) flags.worldpage -= 1; + flags.worldcur2 = 0; + } else if (px >= 477 && px <= 540 && py >= 50 && py < 78) {//下一页 + if (click == 0) return; + flags.worldpage += 1; + flags.worldcur2 = 0; + } else if (px >= 60 && px <= 280 && py >= 80 && py < 144) { + var file = 1 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 7; + if (click == 0) return; + if (flags.worldcur2 == 1) Enterworld(); + else flags.worldcur2 = 1; + } else if (px >= 281 && px <= 500 && py >= 80 && py < 144) { + var file = 3 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 8; + if (click == 0) return; + if (flags.worldcur2 == 2) Enterworld(); + else flags.worldcur2 = 2; + } else if (px >= 60 && px <= 280 && py >= 144 && py < 208) { + var file = 5 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 9; + if (click == 0) return; + if (flags.worldcur2 == 3) Enterworld(); + else flags.worldcur2 = 3; + } else if (px >= 281 && px <= 500 && py >= 144 && py < 208) { + var file = 7 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 10; + if (click == 0) return; + if (flags.worldcur2 == 4) Enterworld(); + else flags.worldcur2 = 4; + } else if (px >= 60 && px <= 280 && py >= 208 && py < 272) { + var file = 9 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 11; + if (click == 0) return; + if (flags.worldcur2 == 5) Enterworld(); + else flags.worldcur2 = 5; + } else if (px >= 281 && px <= 500 && py >= 208 && py < 272) { + var file = 11 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 12; + if (click == 0) return; + if (flags.worldcur2 == 6) Enterworld(); + else flags.worldcur2 = 6; + } else if (px >= 60 && px <= 280 && py >= 272 && py < 336) { + var file = 13 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 13; + if (click == 0) return; + if (flags.worldcur2 == 7) Enterworld(); + else flags.worldcur2 = 7; + } else if (px >= 281 && px <= 500 && py >= 272 && py < 336) { + var file = 15 + (flags.worldpage - 1) * 16; + if (!core.getLocalStorage('world')[file.toString()]) { + flags.worldcur = 0; + return; + } + flags.worldcur = 14; + if (click == 0) return; + if (flags.worldcur2 == 8) Enterworld(); + else flags.worldcur2 = 8; + } else { + flags.worldcur = 0; + if (click == 0) return; + //flags.worldcur2 = 0; + } + }//教程界面操作 + function GuideInter(px, py, click) { + if (px >= 386 && px <= 477 && py >= 380 && py < 409) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "开始界面"); + } else { + flags.worldcur = 0; + } + }//模组界面操作 + function ModInter(px, py, click) { + if (px >= 386 && px <= 477 && py >= 380 && py < 409) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "开始界面"); + } else { + flags.worldcur = 0; + } + }//多人游戏界面操作 + function MulInter(px, py, click) { + if (px >= 386 && px <= 477 && py >= 380 && py < 409) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "开始界面"); + } else { + flags.worldcur = 0; + } + }//创建世界 + function CreateWorld() { + for (var i = 1; i <= 1000; i++) { + if (!core.hasSave(i * 2 - 1) || !core.hasSave(i * 2)) { + flags.worldlist["0"]["世界存档"] = [i * 2 - 1, i * 2]; + flags.worldindex = 1; + break; + } + } + if (flags.seed1 == undefined || typeof flags.seed1 !== 'number') flags.seed1 = core.rand(100000000); + core.plugin.LCGrand(); + var world = core.getLocalStorage('world'); + world[flags.worldlist["0"]["世界存档"][0]] = { + 世界名称: flags.worldlist["0"]["世界名称"], + 世界存档: flags.worldlist["0"]["世界存档"], + 世界选项: flags.worldlist["0"]["世界选项"], + 游戏模式: flags.worldlist["0"]["游戏模式"], + 难度: flags.worldlist["0"]["难度"], + 游戏规则: flags.worldlist["0"]["游戏规则"], + 创建时间: core.formatDate().slice(0, 16), + 世界种子: flags.seed1, + }; + core.setLocalStorage('world', world); + flags.time = 480; + if (flags.maplist == undefined) { + flags.maplist = {}; + for (var floor in core.status.maps) { + flags.maplist[floor] = []; + for (var i = 0; i < 63; i++) { + flags.maplist[floor][i] = []; + } + } + } + if (flags.bglist == undefined) { + flags.bglist = {}; + for (var floor in core.status.maps) { + flags.bglist[floor] = []; + for (var i = 0; i < 63; i++) { + flags.bglist[floor][i] = []; + } + } + } + //core.plugin.jiazaitu("jz4.png", 2000); + core.setFlag('title', 'createrate'); + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + core.plugin.WorldGeneration2(); + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + core.plugin.WorldGeneration(); + } + //core.plugin.Maplist(); + if (flags.worldlist["0"]["世界选项"][2] == "开") { + core.setBlock(420, flags.revive[1], flags.revive[2], flags.revive[0]); + core.plugin.Chestloot("初始奖励箱", (flags.revive[1] * 1000 + flags.revive[2]).toString().padStart(6, '0') + flags.revive[0], 27); + } + //core.setLocalStorage('maplist', flags.maplist); + core.setFlag("maplist"); + core.setFlag("bglist"); + //core.setFlag('__' + "bg" + 'v__'); + //core.plugin.jiazaitu("jz4.png", 4000); + if (flags.worldlist["0"]["难度"] == "简单") { + flags.property.crit += 0.5; + flags.property.reduceDa += 0.4; + flags.hunger[3] = 30; + } else if (flags.worldlist["0"]["难度"] == "普通") { + flags.property.reduceDa += 0.2; + flags.hunger[3] = 15; + } else if (flags.worldlist["0"]["难度"] == "困难") { + flags.hunger[3] = 10; + } + setTimeout(function () { + if (flags.worldindex == 1 && core.status.hero.loc.x >= 0 && core.status.hero.loc.y >= 0) { + var save = flags.worldlist["0"]["世界存档"]; + if (!core.hasSave(save[0]) || + !core.hasSave(save[1])) { + for (var si = 0; si < 2; si++) { + core.doSL(save[si], "save"); + } + } + flags.worldindex = 2; + flags.tip[0] = "进度已保存"; + } + }, 2000); + core.removeFlag('start'); + document.getElementById('title').style.display = 'none'; + core.setFlag('title', 'character'); + + + + }//创建世界_游戏 操作 + function Create_Game(px, py, click) { + if (px >= 197 && px <= 357 && py >= 12 && py < 42) {//切换到世界页签 + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_世界"); + } else if (px >= 358 && px <= 516 && py >= 12 && py < 42) {//切换到更多页签 + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_更多"); + } else if (px >= 167 && px <= 387 && py >= 82 && py < 113) {//命名 + flags.worldcur = 3; + if (click == 0) return; + core.myprompt("请输入世界名称", flags.worldlist["0"]["世界名称"], function (result) { + if (result) { + flags.worldlist["0"]["世界名称"] = result; + return; + } + }); + } else if (px >= 167 && px <= 387 && py >= 127 && py < 153) {//游戏模式 + flags.worldcur = 4; + if (flags.worldlist["0"]["游戏模式"] == "生存") { + flags.worldDescribe = "探索未知的世界,尽情建造、收集、合成并与怪物战斗"; + } else if (flags.worldlist["0"]["游戏模式"] == "创造") { + flags.worldDescribe = "这个模式还没做,所以选这个也是生存"; + } else if (flags.worldlist["0"]["游戏模式"] == "冒险") { + flags.worldDescribe = "这个模式还没做,所以选这个也是生存"; + } else if (flags.worldlist["0"]["游戏模式"] == "极限") { + flags.worldDescribe = "这个模式还没做,所以选这个也是生存"; + } + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏模式"] == "生存") { + flags.worldlist["0"]["游戏模式"] = "创造"; + flags.worldDescribe = "这个模式还没做,所以选这个也是生存"; + } else if (flags.worldlist["0"]["游戏模式"] == "创造") { + flags.worldlist["0"]["游戏模式"] = "冒险"; + flags.worldDescribe = "这个模式还没做,所以选这个也是生存"; + } else if (flags.worldlist["0"]["游戏模式"] == "冒险") { + flags.worldlist["0"]["游戏模式"] = "极限"; + flags.worldDescribe = "这个模式还没做,所以选这个也是生存"; + } else if (flags.worldlist["0"]["游戏模式"] == "极限") { + flags.worldlist["0"]["游戏模式"] = "生存"; + flags.worldDescribe = "探索未知的世界,尽情建造、收集、合成并与怪物战斗"; + } + } else if (px >= 167 && px <= 387 && py >= 167 && py < 196) {//难度 + flags.worldcur = 5; + if (flags.worldlist["0"]["难度"] == "简单") { + flags.worldDescribe = "简单模式:更多的生命恢复,额外增伤和减伤加成,不容易饿。但无法完成挑战成就"; + } else if (flags.worldlist["0"]["难度"] == "普通") { + flags.worldDescribe = "普通模式:适中的生命恢复,额外减伤加成。但委托2倍消耗"; + } else if (flags.worldlist["0"]["难度"] == "困难") { + flags.worldDescribe = "困难模式:较少的生命恢复,委托2倍消耗"; + } + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["难度"] == "简单") { + flags.worldlist["0"]["难度"] = "普通"; + flags.worldDescribe = "普通模式:适中的生命恢复,额外减伤加成。但委托2倍消耗"; + } else if (flags.worldlist["0"]["难度"] == "普通") { + flags.worldlist["0"]["难度"] = "困难"; + flags.worldDescribe = "困难模式:较少的生命恢复,委托2倍消耗"; + } else if (flags.worldlist["0"]["难度"] == "困难") { + flags.worldlist["0"]["难度"] = "简单"; + flags.worldDescribe = "简单模式:更多的生命恢复,额外增伤和减伤加成,不容易饿。但无法完成挑战成就"; + } + } else if (px >= 167 && px <= 387 && py >= 207 && py < 235) {//游戏规则 + flags.worldcur = 6; + flags.worldDescribe = "一些可调整的游戏规则"; + if (click == 0) return; + core.playSound("取消"); + core.setFlag('title', "游戏规则"); + } else if (px >= 167 && px <= 387 && py >= 246 && py < 276) {//模组 + flags.worldcur = 7; + flags.worldDescribe = "敬请期待"; + if (click == 0) return; + core.playSound("取消"); + flags.worldDescribe = "敬请期待"; + } else if (px >= 85 && px <= 264 && py >= 379 && py < 411) {//生成世界 + flags.worldcur = 8; + if (click == 0) return; + core.playSound("取消"); + CreateWorld(); + } else if (px >= 286 && px <= 467 && py >= 379 && py < 411) {//返回 + flags.worldcur = 9; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "选择世界"); + } else { + flags.worldcur = 0; + flags.worldDescribe = ""; + if (click == 0) return; + } + }//创建世界_世界 操作 + function Create_World(px, py, click) { + if (px >= 36 && px <= 196 && py >= 12 && py < 42) {//切换到游戏页签 + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_游戏"); + } else if (px >= 358 && px <= 516 && py >= 12 && py < 42) {//切换到更多页签 + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_更多"); + } else if (px >= 84 && px <= 264 && py >= 82 && py < 113) {//世界类型 + flags.worldcur = 3; + if (flags.worldlist["0"]["世界选项"][0] == "默认") { + flags.worldDescribe = "探索设定好的数十个生物群系"; + } else if (flags.worldlist["0"]["世界选项"][0] == "单一群系") { + flags.worldDescribe = "整个世界只包含一个生物群系!可以指定或随机"; + } + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["世界选项"][0] == "默认") { + flags.worldlist["0"]["世界选项"][0] = "单一群系"; + flags.worldDescribe = "整个世界只包含一个生物群系!可以指定或随机"; + } else if (flags.worldlist["0"]["世界选项"][0] == "单一群系") { + flags.worldlist["0"]["世界选项"][0] = "默认"; + flags.worldDescribe = "探索设定好的数十个生物群系"; + } + } else if (px >= 284 && px <= 465 && py >= 82 && py < 113) {//自定义 + flags.worldcur = 4; + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["世界选项"][0] == "默认") { + core.setFlag('title', "自定义世界"); + } else if (flags.worldlist["0"]["世界选项"][0] == "单一群系") { + core.setFlag('title', "单一群系"); + } + } else if (px >= 86 && px <= 463 && py >= 137 && py < 168) {//种子 + flags.worldcur = 5; + if (click == 0) return; + core.myprompt("请输入世界种子", flags.seed1, function (result) { + if (result) { + flags.seed1 = parseInt(result); + return; + } + }); + } else if (px >= 403 && px <= 464 && py >= 181 && py < 209) {//是否生成结构 + flags.worldcur = 6; + flags.worldDescribe = "是否生成村庄、沉船等结构"; + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["世界选项"][1] == "开") { + flags.worldlist["0"]["世界选项"][1] = "关"; + } else if (flags.worldlist["0"]["世界选项"][1] == "关") { + flags.worldlist["0"]["世界选项"][1] = "开"; + } + } else if (px >= 403 && px <= 464 && py >= 216 && py < 246) {//是否生成奖励箱 + flags.worldcur = 7; + flags.worldDescribe = "是否生成初始奖励箱"; + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["世界选项"][2] == "开") { + flags.worldlist["0"]["世界选项"][2] = "关"; + } else if (flags.worldlist["0"]["世界选项"][2] == "关") { + flags.worldlist["0"]["世界选项"][2] = "开"; + } + } else if (px >= 403 && px <= 464 && py >= 251 && py < 281) {//世界大小 + flags.worldcur = 8; + if (flags.worldlist["0"]["世界选项"][3] == "大") { + flags.worldDescribe = "主世界地表为5X5的区域,其他层为3X3的区域,性能压力和跑图压力较高,不推荐"; + } else if (flags.worldlist["0"]["世界选项"][3] == "中") { + flags.worldDescribe = "所有层均为3X3的区域,跑图压力小"; + } else if (flags.worldlist["0"]["世界选项"][3] == "小") { + flags.worldDescribe = "所有层均为2X2的区域,麻雀虽小五脏俱全(注:这个还没做,所以也是中型世界)"; + } + if (click == 0) return; + core.playSound("取消"); + if (flags.worldlist["0"]["世界选项"][3] == "大") { + flags.worldlist["0"]["世界选项"][3] = "小"; + flags.worldDescribe = "所有层均为2X2的区域,麻雀虽小五脏俱全(注:这个还没做,所以也是中型世界)"; + } else if (flags.worldlist["0"]["世界选项"][3] == "中") { + flags.worldlist["0"]["世界选项"][3] = "大"; + flags.worldDescribe = "主世界地表为5X5的区域,其他层为3X3的区域,性能压力和跑图压力较高,不推荐"; + } else if (flags.worldlist["0"]["世界选项"][3] == "小") { + flags.worldlist["0"]["世界选项"][3] = "中"; + flags.worldDescribe = "所有层均为3X3的区域,跑图压力小"; + } + } else if (px >= 85 && px <= 264 && py >= 379 && py < 411) {//生成世界 + flags.worldcur = 9; + if (click == 0) return; + core.playSound("取消"); + CreateWorld(); + } else if (px >= 286 && px <= 467 && py >= 379 && py < 411) {//返回 + flags.worldcur = 10; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "选择世界"); + } else { + flags.worldcur = 0; + flags.worldDescribe = ""; + if (click == 0) return; + } + }//创建世界_更多 操作 + function Create_More(px, py, click) { + if (px >= 36 && px <= 196 && py >= 12 && py < 42) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_游戏"); + } else if (px >= 198 && px <= 356 && py >= 12 && py < 42) { + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_世界"); + } else if (px >= 85 && px <= 264 && py >= 379 && py < 411) { + flags.worldcur = 3; + if (click == 0) return; + core.playSound("取消"); + CreateWorld(); + } else if (px >= 286 && px <= 467 && py >= 379 && py < 411) { + flags.worldcur = 4; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "选择世界"); + } else { + flags.worldcur = 0; + if (click == 0) return; + flags.worldDescribe = ""; + } + }//游戏规则 + function GameRule(px, py, click) { + if (px >= 385 && px <= 445 && py >= 50 && py < 80) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][0] == "开") flags.worldlist["0"]["游戏规则"][0] = "关"; + else flags.worldlist["0"]["游戏规则"][0] = "开"; + } else if (px >= 385 && px <= 445 && py >= 81 && py < 110) { + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][1] == "开") flags.worldlist["0"]["游戏规则"][1] = "关"; + else flags.worldlist["0"]["游戏规则"][1] = "开"; + } else if (px >= 385 && px <= 445 && py >= 140 && py < 170) { + flags.worldcur = 3; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][2] == "开") flags.worldlist["0"]["游戏规则"][2] = "关"; + else flags.worldlist["0"]["游戏规则"][2] = "开"; + } else if (px >= 385 && px <= 445 && py >= 200 && py < 230) { + flags.worldcur = 4; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][3] == "开") flags.worldlist["0"]["游戏规则"][3] = "关"; + else flags.worldlist["0"]["游戏规则"][3] = "开"; + } else if (px >= 385 && px <= 445 && py >= 231 && py < 260) { + flags.worldcur = 5; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][4] == "开") flags.worldlist["0"]["游戏规则"][4] = "关"; + else flags.worldlist["0"]["游戏规则"][4] = "开"; + } else if (px >= 385 && px <= 445 && py >= 290 && py < 320) { + flags.worldcur = 6; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][5] == "开") { + flags.worldlist["0"]["游戏规则"][5] = "永昼"; + } else if (flags.worldlist["0"]["游戏规则"][5] == "永昼") { + flags.worldlist["0"]["游戏规则"][5] = "永夜"; + } else if (flags.worldlist["0"]["游戏规则"][5] == "永夜") { + flags.worldlist["0"]["游戏规则"][5] = "开"; + } + } else if (px >= 385 && px <= 445 && py >= 321 && py < 350) { + flags.worldcur = 7; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + if (flags.worldlist["0"]["游戏规则"][6] == "开") flags.worldlist["0"]["游戏规则"][6] = "关"; + else flags.worldlist["0"]["游戏规则"][6] = "开"; + } else if (px >= 286 && px <= 467 && py >= 379 && py < 411) { + flags.worldcur = 8; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_游戏"); + } else { + flags.worldcur = 0; + if (click == 0) return; + flags.worldDescribe = ""; + } + }//自定义世界 + function DIYworld(px, py, click) { + if (px >= 100 && px <= 160 && py >= 80 && py < 110) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "平原"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 100 && px <= 160 && py >= 140 && py < 170) { + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "森林"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 100 && px <= 160 && py >= 200 && py < 230) { + flags.worldcur = 3; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "丛林"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 100 && px <= 160 && py >= 260 && py < 290) { + flags.worldcur = 4; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "针叶林"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 100 && px <= 160 && py >= 320 && py < 350) { + flags.worldcur = 5; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "雪原"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 170 && px <= 230 && py >= 80 && py < 110) { + flags.worldcur = 6; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "沙漠"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 170 && px <= 230 && py >= 140 && py < 170) { + flags.worldcur = 7; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "恶地"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 170 && px <= 230 && py >= 200 && py < 230) { + flags.worldcur = 8; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "海洋"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 170 && px <= 230 && py >= 260 && py < 290) { + flags.worldcur = 9; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "丘陵"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 170 && px <= 230 && py >= 320 && py < 350) { + flags.worldcur = 10; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "蘑菇岛"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 240 && px <= 300 && py >= 80 && py < 110) { + flags.worldcur = 11; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "地形"; + var t2 = "河流"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 310 && px <= 370 && py >= 80 && py < 110) { + flags.worldcur = 12; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "水井"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 310 && px <= 370 && py >= 140 && py < 170) { + flags.worldcur = 13; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "废墟"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 310 && px <= 370 && py >= 200 && py < 230) { + flags.worldcur = 14; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "岩浆池"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 310 && px <= 370 && py >= 260 && py < 290) { + flags.worldcur = 15; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "废弃传送门"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 310 && px <= 370 && py >= 320 && py < 250) { + flags.worldcur = 16; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "雪屋"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 380 && px <= 440 && py >= 80 && py < 110) { + flags.worldcur = 17; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "前哨站"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 380 && px <= 440 && py >= 140 && py < 170) { + flags.worldcur = 18; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "村庄"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 380 && px <= 440 && py >= 200 && py < 230) { + flags.worldcur = 19; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "沉船"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 380 && px <= 440 && py >= 260 && py < 290) { + flags.worldcur = 20; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "沙漠神殿"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 380 && px <= 440 && py >= 320 && py < 350) { + flags.worldcur = 21; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var t1 = "结构"; + var t2 = "丛林神庙"; + if (flags.worldlist["0"][t1][t2][0] == 1) { + flags.worldlist["0"][t1][t2][0] = 2; + } else if (flags.worldlist["0"][t1][t2][0] == 2) { + flags.worldlist["0"][t1][t2][0] = 0.5; + } else if (flags.worldlist["0"][t1][t2][0] == 0.5) { + flags.worldlist["0"][t1][t2][0] = 1; + } + } else if (px >= 286 && px <= 467 && py >= 379 && py < 411) { + flags.worldcur = 8; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_世界"); + } else { + flags.worldcur = 0; + if (click == 0) return; + flags.worldDescribe = ""; + } + }//单一群系 + function SingleFormation(px, py, click) { + if (px >= 100 && px <= 160 && py >= 80 && py < 110) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "平原"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 100 && px <= 160 && py >= 140 && py < 170) { + flags.worldcur = 2; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "森林"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 100 && px <= 160 && py >= 200 && py < 230) { + flags.worldcur = 3; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "丛林"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 100 && px <= 160 && py >= 260 && py < 290) { + flags.worldcur = 4; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "针叶林"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 100 && px <= 160 && py >= 320 && py < 350) { + flags.worldcur = 5; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "雪原"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 170 && px <= 230 && py >= 80 && py < 110) { + flags.worldcur = 6; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "沙漠"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 170 && px <= 230 && py >= 140 && py < 170) { + flags.worldcur = 7; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "恶地"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 170 && px <= 230 && py >= 200 && py < 230) { + flags.worldcur = 8; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "海洋"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 170 && px <= 230 && py >= 260 && py < 290) { + flags.worldcur = 9; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "丘陵"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 170 && px <= 230 && py >= 320 && py < 350) { + flags.worldcur = 10; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + flags.worldlist["0"]["单一群系"][0] = "蘑菇岛"; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 240 && px <= 300 && py >= 80 && py < 110) { + flags.worldcur = 11; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var p = ["平原", "森林", "丛林", "针叶林", "雪原", "沙漠", "恶地", "海洋", "丘陵", "蘑菇岛"]; + flags.worldlist["0"]["单一群系"][0] = p[core.rand2(p.length)]; + core.setFlag('title', "创建世界_世界"); + } else if (px >= 286 && px <= 467 && py >= 379 && py < 411) { + flags.worldcur = 8; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "创建世界_世界"); + } else { + flags.worldcur = 0; + if (click == 0) return; + flags.worldDescribe = ""; + } + }//确认删除框 + function DelWorld(px, py, click) { + if (px >= 140 && px <= 230 && py >= 330 && py < 360) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + var world = core.getLocalStorage('world'); + if (core.saves.ids[world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界存档"][0].toString()]) { + core.removeSave(world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界存档"][0].toString()); + core.removeSave(world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]["世界存档"][1].toString()); + } + delete world[(flags.worldcur2 - 1) * 2 + 1 + (flags.worldpage - 1) * 16]; + core.setLocalStorage('world', world); + flags.worldcur2 = 0; + core.setFlag('title', "选择世界"); + } else if (px >= 320 && px <= 410 && py >= 330 && py < 360) { + flags.worldcur = 1; + if (click == 0) return; + flags.worldcur = 0; + core.playSound("取消"); + core.setFlag('title', "选择世界"); + } else { + flags.worldcur = 0; + } + } + + //处理玩家的点击操作 + this.performTitle = function (e) { + var px = parseInt(e.offsetX / core.domStyle.scale), + py = parseInt(e.offsetY / core.domStyle.scale); + //console.log(px, py); + switch (core.getFlag("title", 0)) { + case "开始界面": + flags.worldcur = 0; + StartInter(px, py, 1); + break; + case "选择世界": + flags.worldcur = 0; + ChooseInter(px, py, 1); + break; + case "教程": + flags.worldcur = 0; + GuideInter(px, py, 1); + break; + case "模组": + flags.worldcur = 0; + ModInter(px, py, 1); + break; + case "多人游戏": + flags.worldcur = 0; + MulInter(px, py, 1); + break; + case "创建世界_游戏": + flags.worldcur = 0; + Create_Game(px, py, 1); + break; + case "创建世界_世界": + flags.worldcur = 0; + Create_World(px, py, 1); + break; + case "创建世界_更多": + flags.worldcur = 0; + Create_More(px, py, 1); + break; + case "游戏规则": + flags.worldcur = 0; + GameRule(px, py, 1); + break; + case "自定义世界": + flags.worldcur = 0; + DIYworld(px, py, 1); + break; + case "单一群系": + flags.worldcur = 0; + SingleFormation(px, py, 1); + break; + case "确认删除框": + flags.worldcur = 0; + DelWorld(px, py, 1); + break; + } + }//处理玩家鼠标移动操作 + this.performMove = function (px, py) { + //console.log(px, py); + switch (core.getFlag("title", 0)) { + case "开始界面": + StartInter(px, py, 0); + break; + case "选择世界": + ChooseInter(px, py, 0); + break; + case "教程": + GuideInter(px, py, 0); + break; + case "模组": + ModInter(px, py, 0); + break; + case "多人游戏": + flags.worldcur = 0; + MulInter(px, py, 0); + break; + case "创建世界_游戏": + Create_Game(px, py, 0); + break; + case "创建世界_世界": + Create_World(px, py, 0); + break; + case "创建世界_更多": + Create_More(px, py, 0); + break; + case "游戏规则": + GameRule(px, py, 0); + break; + case "自定义世界": + DIYworld(px, py, 0); + break; + case "单一群系": + SingleFormation(px, py, 0); + break; + case "确认删除框": + DelWorld(px, py, 0); + break; + } + }//处理玩家滚轮操作 + this.performScroll = function (delta) { + //console.log(delta); + switch (core.getFlag("title", 0)) { + case "选择世界": + break; + } + }//默认全屏/横屏 + this.triggerFulscreen = async function (full) { + if (!!document.fullscreenElement && !full) { + if (window.jsinterface) { + window.jsinterface.requestPortrait(); + return; + } + await document.exitFullscreen(); + } + if (full && !document.fullscreenElement) { + if (window.jsinterface) { + window.jsinterface.requestLandscape(); + return; + } + await document.body.requestFullscreen(); + } + } + + + + + + + + + + + + + + + + + this.performTitle_hardSelect = function (px, py) { //横屏下,难度分歧页面的点击处理 + if (px >= 20 && px <= 100 && py >= 211 && py <= 253) core.plugin.selectHard(1); + if (px >= 73 && px <= 153 && py >= 270 && py <= 314) core.plugin.selectHard(2); + if (px >= 140 && px <= 222 && py >= 331 && py <= 374) core.plugin.selectHard(3); //这里的1、2、3,就是你希望的flags.hard取几。强烈建议按照1、2、3……的顺序,否则需要修改很多关联内容 + //你可以设置更多的难度 + } + ////之后的部分请勿修改 + ////之后的部分请勿修改 + ////之后的部分请勿修改 + this.title = function () { //主程序 + //resize后,数值初始化 + var width = parseFloat(document.getElementById('statusBar').style.width.slice(0, -2)); + if (document.getElementById("title")) { //重启游戏时 + document.getElementById("title").style.display = "block"; + core.setFlag("title", "开始界面"); + core.plugin.drawTitle(); + return; + } + //第一次打开页面时 + var newCanvas = document.createElement("canvas"); + newCanvas.id = "title"; + newCanvas.style.display = 'block'; + newCanvas.style.left = '0px'; + newCanvas.style.top = '0px'; + newCanvas.style.left = '0px'; + newCanvas.style.top = '0px'; + newCanvas.style.zIndex = 210; + newCanvas.style.position = 'absolute'; + if (core.domStyle.isVertical) { + newCanvas.style.top = core.dom.gameDraw.style.top; + newCanvas.width = newCanvas.height = 422; + newCanvas.style.width = newCanvas.style.height = width + 'px'; + core.maps._setHDCanvasSize(newCanvas.getContext('2d'), 422, 422); + } else { + newCanvas.width = 554; + newCanvas.height = 422; + newCanvas.style.width = '554px'; + newCanvas.style.height = '422px'; + core.maps._setHDCanvasSize(newCanvas.getContext('2d'), 554, 422); + } + newCanvas.onclick = function (e) { //点击-处理-重绘,如此循环。和自绘ui一个原理 + e.stopPropagation(); + core.plugin.performTitle(e); + core.plugin.drawTitle(); + } + function MouseMove(e) { //鼠标移动监听 + var x = parseInt(e.offsetX / core.domStyle.scale); + var y = parseInt(e.offsetY / core.domStyle.scale); + core.plugin.performMove(x, y); + core.plugin.drawTitle(); + } + // 添加监听器 + newCanvas.addEventListener('mousemove', MouseMove); + + newCanvas.onwheel = function (e) { //滚轮监听 + e.stopPropagation(); + // 获取滚轮的滚动量,这里假设我们关心的是垂直方向的滚动 + var delta = e.deltaY || e.detail || e.wheelDelta; + // 根据滚轮的滚动量进行相应的处理 + core.plugin.performScroll(delta * 1.5 / 100); + // 重绘标题或其他需要更新的元素 + core.plugin.drawTitle(); + }; + + core.dom.gameGroup.appendChild(newCanvas); //注册该画布 + core.control.resize(); //调整分辨率 + + core.setFlag("title", "开始界面"); + core.plugin.drawTitle(); + } + + this.selectHard = function (num) { //选中了某难度时,num表示flags.hard应设为num + core.setFlag("hard", num); + core.status.hard = hard[num - 1]; + core.setFlag('__hardColor__', hardColor[num - 1]); + core.status.route.push("hard:" + num); + //隐藏标题画布,继续后面的“开场剧情” + core.removeFlag("start"); + document.getElementById("title").style.display = "none"; + } + + this.openSL = function () { + document.getElementById("title").style.display = "none"; + core.insertAction([ + { "type": "callLoad" }, + { "type": "function", "function": "function(){ if (core.status.event.id === 'action') {document.getElementById('title').style.display = 'block'} }" } + ]); + } + + this.replayHardSelect = function (action, fromOpening) { + if (action.indexOf("hard:") < 0) return false; + if (!fromOpening) { //把难度选项移到了开场剧情中执行,实际回放时不执行以免录像重复 + setTimeout(core.replay, core.control.__replay_getTimeout()); + return true; + } + var num = parseInt(action.substr(5)); + + if (!Number.isInteger(num)) return false; + + core.status.route.push(action); + core.setFlag("hard", num); + core.status.hard = hard[num - 1]; + core.setFlag('__hardColor__', hardColor[num - 1]); + setTimeout(core.replay, core.control.__replay_getTimeout()); + return true; + } + + core.control._resize_canvas = function (obj) { + var innerSize = (obj.CANVAS_WIDTH * core.domStyle.scale) + "px"; + for (var i = 0; i < core.dom.gameCanvas.length; ++i) + core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[i].style.height = innerSize; + core.dom.gif.style.width = core.dom.gif.style.height = innerSize; + core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; + core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = innerSize; + core.dom.gameDraw.style.top = obj.statusBarHeightInVertical + "px"; + core.dom.gameDraw.style.right = 0; + core.dom.gameDraw.style.border = obj.border; + // resize bigmap + core.bigmap.canvas.forEach(function (cn) { + var ratio = core.canvas[cn].canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1; + core.canvas[cn].canvas.style.width = core.canvas[cn].canvas.width / ratio * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = core.canvas[cn].canvas.height / ratio * core.domStyle.scale + "px"; + }); + // resize dynamic canvas + for (var name in core.dymCanvas) { + var ctx = core.dymCanvas[name], + canvas = ctx.canvas; + var ratio = canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1; + canvas.style.width = canvas.width / ratio * core.domStyle.scale + "px"; + canvas.style.height = canvas.height / ratio * core.domStyle.scale + "px"; + canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + } + // resize next + main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px"; + main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; + //resize title + var title = document.getElementById('title'); + if (title) { + if (core.domStyle.isVertical) { + title.style.width = newCanvas.style.height = 422 * core.domStyle.scale + 'px'; + } else { + title.style.width = 554 * core.domStyle.scale + 'px'; + title.style.height = 422 * core.domStyle.scale + 'px'; + } + } + } + core.control.registerResize("canvas", control.prototype._resize_canvas); + core.control.registerReplayAction("hard", core.plugin.replayHardSelect); + }, + "19-地形生成": function () { + //创建大型世界 + this.WorldGeneration = function () { + var formation1 = ["平原,森林", "平原,丘陵", "平原,丛林", "森林,丛林", "沙漠,恶地", "森林,雪原", "森林,针叶林", "针叶林,雪原", "针叶林,丘陵", "沙漠,丘陵", "平原,雪原", "平原,针叶林", "平原,沙漠", "平原,恶地", "森林,沙漠"]; + var formation2 = ["平原,丘陵", "平原,丛林", "森林,丛林", "沙漠,恶地", "森林,雪原", "针叶林,雪原", "针叶林,丘陵", "沙漠,丘陵", "平原,恶地"]; + var sea = ["海洋", "海底神殿", "海洋,丘陵", "海洋,蘑菇岛"]; + var floor = ["zhu1", "zhu2", "zhu3", "zhu4", "zhu5", "zhu6", "zhu7", "zhu8", "zhu9", "zhu10", "zhu11", "zhu12", "zhu13", "zhu14", "zhu15", "zhu16", "zhu17", "zhu18", "zhu19", "zhu20", "zhu21", "zhu22", "zhu23", "zhu24", "zhu25",]; + var searand = core.plugin.rand3(6); + switch (searand) { + case 0: + var r1 = core.plugin.rand3(4); + core.plugin.MapGeneration("zhu11", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(3); + core.plugin.MapGeneration("zhu16", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(2); + core.plugin.MapGeneration("zhu21", sea[r1]); + sea.splice(r1, 1); + core.plugin.MapGeneration("zhu22", sea[0]); + sea.splice(0, 1); + floor.splice(floor.indexOf("zhu11"), 1); + floor.splice(floor.indexOf("zhu16"), 1); + floor.splice(floor.indexOf("zhu21"), 1); + floor.splice(floor.indexOf("zhu22"), 1); + break; + case 1: + var r1 = core.plugin.rand3(4); + core.plugin.MapGeneration("zhu16", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(3); + core.plugin.MapGeneration("zhu21", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(2); + core.plugin.MapGeneration("zhu22", sea[r1]); + sea.splice(r1, 1); + core.plugin.MapGeneration("zhu23", sea[0]); + sea.splice(0, 1); + floor.splice(floor.indexOf("zhu16"), 1); + floor.splice(floor.indexOf("zhu21"), 1); + floor.splice(floor.indexOf("zhu22"), 1); + floor.splice(floor.indexOf("zhu23"), 1); + break; + case 2: + var r1 = core.plugin.rand3(4); + core.plugin.MapGeneration("zhu21", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(3); + core.plugin.MapGeneration("zhu22", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(2); + core.plugin.MapGeneration("zhu23", sea[r1]); + sea.splice(r1, 1); + core.plugin.MapGeneration("zhu24", sea[0]); + sea.splice(0, 1); + floor.splice(floor.indexOf("zhu21"), 1); + floor.splice(floor.indexOf("zhu22"), 1); + floor.splice(floor.indexOf("zhu23"), 1); + floor.splice(floor.indexOf("zhu24"), 1); + break; + case 3: + var r1 = core.plugin.rand3(4); + core.plugin.MapGeneration("zhu22", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(3); + core.plugin.MapGeneration("zhu23", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(2); + core.plugin.MapGeneration("zhu24", sea[r1]); + sea.splice(r1, 1); + core.plugin.MapGeneration("zhu25", sea[0]); + sea.splice(0, 1); + floor.splice(floor.indexOf("zhu22"), 1); + floor.splice(floor.indexOf("zhu23"), 1); + floor.splice(floor.indexOf("zhu24"), 1); + floor.splice(floor.indexOf("zhu25"), 1); + break; + case 4: + var r1 = core.plugin.rand3(4); + core.plugin.MapGeneration("zhu20", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(3); + core.plugin.MapGeneration("zhu23", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(2); + core.plugin.MapGeneration("zhu24", sea[r1]); + sea.splice(r1, 1); + core.plugin.MapGeneration("zhu25", sea[0]); + sea.splice(0, 1); + floor.splice(floor.indexOf("zhu20"), 1); + floor.splice(floor.indexOf("zhu23"), 1); + floor.splice(floor.indexOf("zhu24"), 1); + floor.splice(floor.indexOf("zhu25"), 1); + break; + case 5: + var r1 = core.plugin.rand3(4); + core.plugin.MapGeneration("zhu15", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(3); + core.plugin.MapGeneration("zhu20", sea[r1]); + sea.splice(r1, 1); + r1 = core.plugin.rand3(2); + core.plugin.MapGeneration("zhu24", sea[r1]); + sea.splice(r1, 1); + core.plugin.MapGeneration("zhu25", sea[0]); + sea.splice(0, 1); + floor.splice(floor.indexOf("zhu15"), 1); + floor.splice(floor.indexOf("zhu20"), 1); + floor.splice(floor.indexOf("zhu24"), 1); + floor.splice(floor.indexOf("zhu25"), 1); + break; + } + for (var i = 0; i < 15; i++) { + rand1 = core.plugin.rand3(15 - i); + core.plugin.MapGeneration(floor[0], formation1[rand1]); + floor.splice(0, 1); + formation1.splice(rand1, 1); + } + for (var i = 0; i < 6; i++) { + rand1 = core.plugin.rand3(9 - i); + core.plugin.MapGeneration(floor[0], formation2[rand1]); + floor.splice(0, 1); + formation2.splice(rand1, 1); + } + core.plugin.smoothTerrain(); + core.plugin.Cavedec(); + core.plugin.GenerateBedrock(); // 基岩 + core.plugin.CreateRate("正在进入世界..."); + }//创建中型世界 + this.WorldGeneration2 = function () { + var formation1 = ["类型1", "类型2", "类型3", "类型4", "类型5", "类型6", "类型7", "类型8", "类型9"]; + var floor = ["zhu7", "zhu8", "zhu9", "zhu12", "zhu13", "zhu14", "zhu17", "zhu18", "zhu19"]; + var rand1; + for (var i = 0; i < 9; i++) { + do { + rand1 = core.plugin.rand3(9 - i); + } while (floor[0] == "zhu13" && formation1[rand1] == "类型3"); + //console.log(floor[0], formation1[rand1]); + core.plugin.MapGeneration(floor[0], formation1[rand1]); + floor.splice(0, 1); + formation1.splice(rand1, 1); + } + core.plugin.smoothTerrain(); + core.plugin.Cavedec(); + core.plugin.GenerateBedrock(); // 基岩 + core.plugin.CreateRate("正在进入世界..."); + } //根据地形组合创建地形 + this.MapGeneration = function (floor, formation) { + var b1 = 305; // 浅草地 + var b2 = 339; // 森林草地 + var b3 = 340; // 丛林草地 + var b4 = 341; // 针叶林草地 + var b5 = 342; // 雪地 + var b6 = 343; // 沙子 + var b7 = 344; // 红沙 + var b8 = 345; // 深水 + var b9 = 346; // 石头 + var b10 = 347; // 菌丝 + var b11 = 348; // 浅水 + + var floor1 = ""; + switch (floor) { + case "zhu7": + floor1 = "dong1"; + break; + case "zhu8": + floor1 = "dong2"; + break; + case "zhu9": + floor1 = "dong3"; + break; + case "zhu12": + floor1 = "dong4"; + break; + case "zhu13": + floor1 = "dong5"; + break; + case "zhu14": + floor1 = "dong6"; + break; + case "zhu17": + floor1 = "dong7"; + break; + case "zhu18": + floor1 = "dong8"; + break; + case "zhu19": + floor1 = "dong9"; + break; + } + + if (flags.worldlist["0"]["世界选项"][0] == "单一群系") { + var q = flags.worldlist["0"]["单一群系"][0]; + switch (q) { + case "平原": + core.plugin.GenerateTerrain(20, b1, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "森林": + core.plugin.GenerateTerrain(20, b2, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "丛林": + core.plugin.GenerateTerrain(20, b3, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "针叶林": + core.plugin.GenerateTerrain(20, b4, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "雪原": + core.plugin.GenerateTerrain(20, b5, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "沙漠": + core.plugin.GenerateTerrain(20, b6, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "恶地": + core.plugin.GenerateTerrain(20, b7, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "海洋": + core.plugin.GenerateTerrain(20, b8, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "丘陵": + core.plugin.GenerateTerrain(20, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + case "蘑菇岛": + core.plugin.GenerateTerrain(20, b10, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, q); + break; + } + return; + } + + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + if (formation == "类型1") { + core.plugin.GenerateTerrain(10, b5, floor); + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.GenerateTerrain(10, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "雪原"); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } else if (formation == "类型2") { + core.plugin.GenerateTerrain(10, b8, floor); + core.plugin.GenerateTerrain(10, b9, floor); + core.plugin.GenerateTerrain(10, b10, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "海洋"); + core.plugin.GenerateResourceGroup(floor, "蘑菇岛"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } else if (formation == "类型3") { + core.plugin.GenerateTerrain(20, b8, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "海洋"); + core.plugin.GenerateResourceGroup(floor, "海底神殿"); + } else if (formation == "类型4") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.GenerateTerrain(10, b5, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "雪原"); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + } else if (formation == "类型5") { + core.plugin.GenerateTerrain(10, b2, floor); + core.plugin.GenerateTerrain(10, b3, floor); + core.plugin.GenerateTerrain(10, b3, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "森林"); + core.plugin.GenerateResourceGroup(floor, "丛林"); + core.plugin.GenerateResourceGroup(floor, "丛林"); + } else if (formation == "类型6") { + core.plugin.GenerateTerrain(10, b2, floor); + core.plugin.GenerateTerrain(10, b6, floor); + core.plugin.GenerateTerrain(10, b7, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + core.plugin.GenerateResourceGroup(floor, "恶地"); + core.plugin.GenerateResourceGroup(floor, "森林"); + } else if (formation == "类型7") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b2, floor); + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "森林"); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + } else if (formation == "类型8") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b3, floor); + core.plugin.GenerateTerrain(10, b6, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + core.plugin.GenerateResourceGroup(floor, "丛林"); + } else if (formation == "类型9") { + core.plugin.GenerateTerrain(10, b6, floor); + core.plugin.GenerateTerrain(10, b7, floor); + core.plugin.GenerateTerrain(10, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + core.plugin.GenerateResourceGroup(floor, "恶地"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + if (formation == "平原,森林") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b2, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "森林"); + } else if (formation == "平原,丘陵") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(2, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } else if (formation == "平原,丛林") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b3, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "丛林"); + } else if (formation == "森林,丛林") { + core.plugin.GenerateTerrain(10, b2, floor); + core.plugin.GenerateTerrain(10, b3, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "森林"); + core.plugin.GenerateResourceGroup(floor, "丛林"); + } else if (formation == "沙漠,恶地") { + core.plugin.GenerateTerrain(10, b6, floor); + core.plugin.GenerateTerrain(10, b7, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + core.plugin.GenerateResourceGroup(floor, "恶地"); + } else if (formation == "森林,雪原") { + core.plugin.GenerateTerrain(2, b2, floor); + core.plugin.GenerateTerrain(10, b5, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "森林"); + core.plugin.GenerateResourceGroup(floor, "雪原"); + } else if (formation == "海洋") { + core.plugin.GenerateTerrain(20, b8, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "海洋"); + } else if (formation == "海底神殿") { + core.plugin.GenerateTerrain(20, b8, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "海洋"); + core.plugin.GenerateResourceGroup(floor, "海底神殿"); + } else if (formation == "海洋,蘑菇岛") { + core.plugin.GenerateTerrain(10, b8, floor); + core.plugin.GenerateTerrain(2, b10, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "海洋"); + core.plugin.GenerateResourceGroup(floor, "蘑菇岛"); + } else if (formation == "森林,针叶林") { + core.plugin.GenerateTerrain(10, b2, floor); + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "森林"); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + } else if (formation == "针叶林,雪原") { + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.GenerateTerrain(10, b5, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + core.plugin.GenerateResourceGroup(floor, "雪原"); + } else if (formation == "针叶林,丘陵") { + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.GenerateTerrain(5, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } else if (formation == "沙漠,丘陵") { + core.plugin.GenerateTerrain(2, b6, floor); + core.plugin.GenerateTerrain(10, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } else if (formation == "平原,雪原") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b5, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "雪原"); + } else if (formation == "平原,针叶林") { + core.plugin.GenerateTerrain(10, b1, floor); + core.plugin.GenerateTerrain(10, b4, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "针叶林"); + } else if (formation == "平原,沙漠") { + core.plugin.GenerateTerrain(2, b1, floor); + core.plugin.GenerateTerrain(10, b6, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + } else if (formation == "平原,恶地") { + core.plugin.GenerateTerrain(1, b1, floor); + core.plugin.GenerateTerrain(10, b7, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "平原"); + core.plugin.GenerateResourceGroup(floor, "恶地"); + } else if (formation == "森林,沙漠") { + core.plugin.GenerateTerrain(2, b2, floor); + core.plugin.GenerateTerrain(10, b6, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "森林"); + core.plugin.GenerateResourceGroup(floor, "沙漠"); + } else if (formation == "海洋,丘陵") { + core.plugin.GenerateTerrain(10, b8, floor); + core.plugin.GenerateTerrain(2, b9, floor); + core.plugin.ArrangeTerrain(floor); + if (floor1) core.plugin.ArrangeTerrain(floor1); + core.plugin.GenerateResourceGroup(floor, "海洋"); + core.plugin.GenerateResourceGroup(floor, "丘陵"); + } + } + + + } //生成地形 + this.GenerateTerrain = function (bDense, bg, floor) { + core.plugin.CreateRate("正在生成地形"); + //生成地形 + var n1 = 0; + var box = 0; + var floor1 = ""; + var bg1; + switch (floor) { + case "zhu7": + floor1 = "dong1"; + break; + case "zhu8": + floor1 = "dong2"; + break; + case "zhu9": + floor1 = "dong3"; + break; + case "zhu12": + floor1 = "dong4"; + break; + case "zhu13": + floor1 = "dong5"; + break; + case "zhu14": + floor1 = "dong6"; + break; + case "zhu17": + floor1 = "dong7"; + break; + case "zhu18": + floor1 = "dong8"; + break; + case "zhu19": + floor1 = "dong9"; + break; + } + switch (bg) { + case 346: + case 347: + case 305: + var rand1 = core.plugin.rand3(125); + if (rand1 < 20) { + bg1 = 305; + } else if (rand1 < 40) { + bg1 = 339; + } else { + bg1 = 340; + } + break; + case 339: + case 340: + case 341: + var rand1 = core.plugin.rand3(140); + if (rand1 < 50) { + bg1 = 341; //繁茂 + } else if (rand1 < 60) { + bg1 = 305; //深层 + } else if (rand1 < 70) { + bg1 = 339; //水晶 + } else { + bg1 = 340; //普通 + } + break; + case 342: + case 345: + var rand1 = core.plugin.rand3(205); + if (rand1 < 100) { + bg1 = 305; + } else if (rand1 < 120) { + bg1 = 339; + } else { + bg1 = 340; + } + break; + case 343: + bg1 = 343; + break; + case 344: + bg1 = 344; + break; + } + do { + //1、随机点的寻找 + var r1 = core.plugin.rand3(63); + var r2 = core.plugin.rand3(63); + if (core.getBgNumber(r1, r2, floor) == 300 || core.getBgNumber(r1, r2, floor) == 0 || core.getBgNumber(r1, r2, floor) == bg) { + core.setBgFgBlock('bg', bg, r1, r2, floor); + flags.bglist[floor][r2][r1] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1, r2, floor1); + flags.bglist[floor1][r2][r1] = bg; + } + n1 += 1; + if (bg == 347 && box == 0) { + core.setBlock(420, r1, r2, floor); + flags.maplist[floor][r2][r1] = 420; + core.plugin.Chestloot("蘑菇宝箱", (r1 * 1000 + r2).toString().padStart(6, '0') + floor, 30); + box = 1; + } + //2、随机半径 + var r3 = core.plugin.rand3(4) + 7; + var r4 = core.plugin.rand3(4) + 7; + //3、内部填充+边缘不规则 + for (var i = 0; i <= r3; i++) { + for (var j = 0; j <= r4; j++) { + if (i == r3 || j == r4) { + if (r1 + i >= 63 || r2 + j >= 63 || r1 + i < 0 || r2 + j < 0) { } else if (core.plugin.rand3(2) == 1 && (core.getBgNumber(r1 + i, r2 + j, floor) == 300 || core.getBgNumber(r1 + i, r2 + j, floor) == 0)) { + core.setBgFgBlock('bg', bg, r1 + i, r2 + j, floor); + flags.bglist[floor][r2 + j][r1 + i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 + i, r2 + j, floor1); + flags.bglist[floor1][r2 + j][r1 + i] = bg; + } + } + if (r1 + i >= 63 || r2 - j >= 63 || r1 + i < 0 || r2 - j < 0) { } else if (core.plugin.rand3(2) == 1 && (core.getBgNumber(r1 + i, r2 - j, floor) == 300 || core.getBgNumber(r1 + i, r2 - j, floor) == 0)) { + core.setBgFgBlock('bg', bg, r1 + i, r2 - j, floor); + flags.bglist[floor][r2 - j][r1 + i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 + i, r2 - j, floor1); + flags.bglist[floor1][r2 - j][r1 + i] = bg; + } + } + if (r1 - i >= 63 || r2 + j >= 63 || r1 - i < 0 || r2 + j < 0) { } else if (core.plugin.rand3(2) == 1 && (core.getBgNumber(r1 - i, r2 + j, floor) == 300 || core.getBgNumber(r1 - i, r2 + j, floor) == 0)) { + core.setBgFgBlock('bg', bg, r1 - i, r2 + j, floor); + flags.bglist[floor][r2 + j][r1 - i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 - i, r2 + j, floor1); + flags.bglist[floor1][r2 + j][r1 - i] = bg; + } + } + if (r1 - i >= 63 || r2 - j >= 63 || r1 - i < 0 || r2 - j < 0) { } else if (core.plugin.rand3(2) == 1 && (core.getBgNumber(r1 - i, r2 - j, floor) == 300 || core.getBgNumber(r1 - i, r2 - j, floor) == 0)) { + core.setBgFgBlock('bg', bg, r1 - i, r2 - j, floor); + flags.bglist[floor][r2 - j][r1 - i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 - i, r2 - j, floor1); + flags.bglist[floor1][r2 - j][r1 - i] = bg; + } + } + } else { + if (r1 + i >= 63 || r2 + j >= 63 || r1 + i < 0 || r2 + j < 0) { } else if (core.getBgNumber(r1 + i, r2 + j, floor) == 300 || core.getBgNumber(r1 + i, r2 + j, floor) == 0) { + core.setBgFgBlock('bg', bg, r1 + i, r2 + j, floor); + flags.bglist[floor][r2 + j][r1 + i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 + i, r2 + j, floor1); + flags.bglist[floor1][r2 + j][r1 + i] = bg; + } + } + if (r1 + i >= 63 || r2 - j >= 63 || r1 + i < 0 || r2 - j < 0) { } else if (core.getBgNumber(r1 + i, r2 - j, floor) == 300 || core.getBgNumber(r1 + i, r2 - j, floor) == 0) { + core.setBgFgBlock('bg', bg, r1 + i, r2 - j, floor); + flags.bglist[floor][r2 - j][r1 + i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 + i, r2 - j, floor1); + flags.bglist[floor1][r2 - j][r1 + i] = bg; + } + } + if (r1 - i >= 63 || r2 + j >= 63 || r1 - i < 0 || r2 + j < 0) { } else if (core.getBgNumber(r1 - i, r2 + j, floor) == 300 || core.getBgNumber(r1 - i, r2 + j, floor) == 0) { + core.setBgFgBlock('bg', bg, r1 - i, r2 + j, floor); + flags.bglist[floor][r2 + j][r1 - i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 - i, r2 + j, floor1); + flags.bglist[floor1][r2 + j][r1 - i] = bg; + } + } + if (r1 - i >= 63 || r2 - j >= 63 || r1 - i < 0 || r2 - j < 0) { } else if (core.getBgNumber(r1 - i, r2 - j, floor) == 300 || core.getBgNumber(r1 - i, r2 - j, floor) == 0) { + core.setBgFgBlock('bg', bg, r1 - i, r2 - j, floor); + flags.bglist[floor][r2 - j][r1 - i] = bg; + if (floor1) { + core.setBgFgBlock('bg', bg1, r1 - i, r2 - j, floor1); + flags.bglist[floor1][r2 - j][r1 - i] = bg; + } + } + } + } + } + } + } while (n1 < bDense); + } //平整地形 + this.ArrangeTerrain = function (floor) { + //平整地形 + var n1 = 0; + do { + for (var i = 0; i < 63; i++) { + for (var j = 0; j < 63; j++) { + if (core.getBgNumber(i, j, floor) == 300 || core.getBgNumber(i, j, floor) == 0) { + if (i - 1 >= 0 && core.getBgNumber(i - 1, j, floor) != 300 && core.getBgNumber(i - 1, j, floor) != 0) { + core.setBgFgBlock('bg', core.getBgNumber(i - 1, j, floor), i, j, floor); + flags.bglist[floor][j][i] = core.getBgNumber(i - 1, j, floor); + } else if (j + 1 < 63 && core.getBgNumber(i, j + 1, floor) != 300 && core.getBgNumber(i, j + 1, floor) != 0) { + core.setBgFgBlock('bg', core.getBgNumber(i, j + 1, floor), i, j, floor); + flags.bglist[floor][j][i] = core.getBgNumber(i, j + 1, floor); + } else if (i + 1 < 63 && core.getBgNumber(i + 1, j, floor) != 300 && core.getBgNumber(i + 1, j, floor) != 0) { + core.setBgFgBlock('bg', core.getBgNumber(i + 1, j, floor), i, j, floor); + flags.bglist[floor][j][i] = core.getBgNumber(i + 1, j, floor); + } else if (j - 1 >= 0 && core.getBgNumber(i, j - 1, floor) != 300 && core.getBgNumber(i, j - 1, floor) != 0) { + core.setBgFgBlock('bg', core.getBgNumber(i, j - 1, floor), i, j, floor); + flags.bglist[floor][j][i] = core.getBgNumber(i, j - 1, floor); + } + } + } + } + n1 += 1; + } while (n1 < 128); + } //平滑过渡地形 + this.smoothTerrain = function () { + core.plugin.CreateRate("正在平滑过渡地形"); + // if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + + // } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + + // } + var floor; + var floornum; + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + floornum = 9; + floor = ["zhu7", "zhu8", "zhu9", "zhu12", "zhu13", "zhu14", "zhu17", "zhu18", "zhu19"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + floornum = 25; + floor = ["zhu1", "zhu2", "zhu3", "zhu4", "zhu5", "zhu6", "zhu7", "zhu8", "zhu9", "zhu10", "zhu11", "zhu12", "zhu13", "zhu14", "zhu15", "zhu16", "zhu17", "zhu18", "zhu19", "zhu20", "zhu21", "zhu22", "zhu23", "zhu24", "zhu25",]; + } + var floor2 = []; + var floors; + for (var i = 0; i < floornum; i++) { + var rand1 = core.plugin.rand3(floornum - i); + floors = core.plugin.Floors(floor[rand1]); + for (var j = 0; j < 4; j++) { + if (floors[j] != "" && floor2.indexOf(floors[j]) == -1) { + var x, y, x1, y1; + switch (j) { + case 0: + x = 57; + y = 31; + x1 = 0; + y1 = 31; + break; + case 1: + x = 31; + y = 5; + x1 = 31; + y1 = 62; + break; + case 2: + x = 5; + y = 31; + x1 = 62; + y1 = 31; + break; + case 3: + x = 31; + y = 57; + x1 = 31; + y1 = 0; + break; + } + var bg1 = core.getBgNumber(x, y, floors[j]); + var bg2; + var r1 = core.plugin.rand3(4) + 2; + var r2 = 30, r3 = 30; + if (bg1 == 305 || bg1 == 339 || bg1 == 340 || bg1 == 341 || bg1 == 346) { + bg2 = bg1; + } else if (bg1 == 342) { + bg2 = 341; + } else if (bg1 == 343 || bg1 == 344) { + bg2 = 305; + } else if (bg1 == 345 || bg1 == 347 || bg1 == 348) { + bg2 = 343; + } else { + continue; + } + for (var r = 0; r < r1; r++) { + var x2, y2, n = 1; + function randrange(r2, n) { + var x3, y3; + for (var rr = 1; rr < r2; rr++) { + if (n == 1) { + x3 = x2 + rr; + y3 = y2; + } else if (n == 2) { + x3 = x2 - rr; + y3 = y2; + } else if (n == 3) { + x3 = x2; + y3 = y2 + rr; + } else if (n == 4) { + x3 = x2; + y3 = y2 - rr; + } + core.setBgFgBlock('bg', bg2, x3, y3, floor[rand1]); + flags.bglist[floor[rand1]][y3][x3] = bg2; + if (core.getBlockNumber(x3, y3, floor[rand1]) != 91 && + core.getBlockNumber(x3, y3, floor[rand1]) != 92 && + core.getBlockNumber(x3, y3, floor[rand1]) != 93 && + core.getBlockNumber(x3, y3, floor[rand1]) != 94 && + core.getBlockNumber(x3, y3, floor[rand1]) != 302) { + core.setBlock(0, x3, y3, floor[rand1]); + flags.maplist[floor[rand1]][y3][x3] = 0; + } + } + } + if (y1 == 0) { + x2 = x1; + y2 = y1 + r; + n = 1; + } else if (y1 == 62) { + x2 = x1; + y2 = y1 - r; + n = 1; + } else if (x1 == 0) { + x2 = x1 + r; + y2 = y1; + n = 3; + } else if (x1 == 62) { + x2 = x1 - r; + y2 = y1; + n = 3; + } + core.setBgFgBlock('bg', bg2, x2, y2, floor[rand1]); + flags.bglist[floor[rand1]][y2][x2] = bg2; + if (core.getBlockNumber(x2, y2, floor[rand1]) != 91 && + core.getBlockNumber(x2, y2, floor[rand1]) != 92 && + core.getBlockNumber(x2, y2, floor[rand1]) != 93 && + core.getBlockNumber(x2, y2, floor[rand1]) != 94 && + core.getBlockNumber(x2, y2, floor[rand1]) != 302) { + core.setBlock(0, x2, y2, floor[rand1]); + flags.maplist[floor[rand1]][y2][x2] = 0; + } + var r4 = core.plugin.rand3(r2) + 2; + randrange(r4, n); + var r5 = core.plugin.rand3(r3) + 2; + randrange(r5, n + 1); + r2 = r4; + r3 = r5; + if (r != 0 && r2 < r * 2 + 4 && r3 < r * 2 + 4 && r2 + r3 < 12) break; + } + } + } + floor2.splice(0, 0, floor[rand1]); + floor.splice(rand1, 1); + } + } //返回周围楼层 + this.Floors = function (floor) { + switch (floor) { + case "zhu1": + return ["", "zhu6", "zhu2", ""]; + case "zhu2": + return ["zhu1", "zhu7", "zhu3", ""]; + case "zhu3": + return ["zhu2", "zhu8", "zhu4", ""]; + case "zhu4": + return ["zhu3", "zhu9", "zhu5", ""]; + case "zhu5": + return ["zhu4", "zhu10", "", ""]; + case "zhu6": + return ["", "zhu11", "zhu7", "zhu1"]; + case "zhu7": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["", "zhu12", "zhu8", ""]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu6", "zhu12", "zhu8", "zhu2"]; + } + case "zhu8": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["zhu7", "zhu13", "zhu9", ""]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu7", "zhu13", "zhu9", "zhu3"]; + } + case "zhu9": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["zhu8", "zhu14", "", ""]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu8", "zhu14", "zhu10", "zhu4"]; + } + case "zhu10": + return ["zhu9", "zhu15", "", "zhu5"]; + case "zhu11": + return ["", "zhu16", "zhu12", "zhu6"]; + case "zhu12": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["", "zhu17", "zhu13", "zhu7"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu11", "zhu17", "zhu13", "zhu7"]; + } + case "zhu13": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["zhu12", "zhu18", "zhu14", "zhu8"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu12", "zhu18", "zhu14", "zhu8"]; + } + case "zhu14": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["zhu13", "zhu19", "", "zhu9"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu13", "zhu19", "zhu15", "zhu9"]; + } + case "zhu15": + return ["zhu14", "zhu20", "", "zhu10"]; + case "zhu16": + return ["", "zhu21", "zhu17", "zhu11"]; + case "zhu17": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["", "", "zhu18", "zhu12"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu16", "zhu22", "zhu18", "zhu12"]; + } + case "zhu18": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["zhu17", "", "zhu19", "zhu13"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu17", "zhu23", "zhu19", "zhu13"]; + } + case "zhu19": + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + return ["zhu18", "", "", "zhu14"]; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + return ["zhu18", "zhu24", "zhu20", "zhu14"]; + } + case "zhu20": + return ["zhu19", "zhu25", "", "zhu15"]; + case "zhu21": + return ["", "", "zhu22", "zhu16"]; + case "zhu22": + return ["zhu21", "", "zhu23", "zhu17"]; + case "zhu23": + return ["zhu22", "", "zhu24", "zhu18"]; + case "zhu24": + return ["zhu23", "", "zhu25", "zhu19"]; + case "zhu25": + return ["zhu24", "", "", "zhu20"]; + } + } //洞穴层布置 + this.Cavedec = function () { + var floors = ["dong1", "dong2", "dong3", "dong4", "dong5", "dong6", "dong7", "dong8", "dong9",]; + for (var id = 0; id < floors.length; id++) { + var floor = floors[id]; + for (var i = 1; i < 62; i++) { + for (var j = 1; j < 62; j++) { + var block; + var type = "普通洞穴"; + switch (core.getBgNumber(i, j, floor)) { + case 341: //繁茂 + type = "繁茂洞穴"; + break; + case 305: //深层 + type = "深层洞穴"; + break; + case 339: //水晶 + type = "水晶洞穴"; + break; + case 340: //普通 + type = "普通洞穴"; + break; + case 343: //砂岩 + type = "砂岩洞穴"; + break; + case 344: //陶瓦 + type = "陶瓦洞穴"; + break; + } + block = core.plugin.Caveblock(type, 1); + core.setBgFgBlock('bg', block, i, j, floor); + block = core.plugin.Caveblock(type, 2); + core.setBlock(block, i, j, floor); + } + } + } + } //根据区块生成资源组 + this.GenerateResourceGroup = function (floor, formation) { + var b1 = 305; // 浅草地 + var b2 = 339; // 森林草地 + var b3 = 340; // 丛林草地 + var b4 = 341; // 针叶林草地 + var b5 = 342; // 雪地 + var b6 = 343; // 沙子 + var b7 = 344; // 红沙 + var b8 = 345; // 深水 + var b9 = 346; // 石头 + var b10 = 347; // 菌丝 + var b11 = 348; // 浅水 + + var r1 = 350; // 橡树 + var r2 = 351; // 白桦树 + var r3 = 352; // 云杉树 + var r4 = 353; // 丛林树 + var r5 = 354; // 金合欢树 + var r6 = 372; // 小草丛 + var r7 = 373; // 大草丛 + var r8 = 374; // 向日葵 + var r9 = 375; // 玫瑰 + var r10 = 377; // 蜂巢 + var r11 = 379; // 南瓜 + var r12 = 381; // 西瓜 + var r13 = 383; // 竹子 + var r14 = 386; // 枯萎灌木 + var r15 = 387; // 仙人掌 + var r16 = 388; // 仙人掌花 + var r17 = 393; // 海草 + var r18 = 394; // 海带 + var r19 = 395; // 红珊瑚 + var r20 = 396; // 黄珊瑚 + var r21 = 397; // 蓝珊瑚 + var r22 = 398; // 粉珊瑚 + var r23 = 399; // 红珊瑚块 + var r24 = 400; // 黄珊瑚块 + var r25 = 401; // 蓝珊瑚块 + var r26 = 402; // 粉珊瑚块 + var r27 = 916; // 圆石 + var r28 = 403; // 煤矿 + var r29 = 404; // 铁矿 + var r30 = 405; // 铜矿 + var r31 = 389; // 红色蘑菇 + var r32 = 390; // 棕色蘑菇 + var r33 = 349; // 沙砾 + + var weight = 1; + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + weight = 1.5; + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + weight = 1; + } + + if (formation == "平原") { + var tile = flags.worldlist["0"]["地形"]["平原"][0]; + core.plugin.GenerateResource(floor, b1, 50 / tile, r6); // 小草丛 + core.plugin.GenerateResource(floor, b1, 50 / tile, r7); // 大草丛 + core.plugin.GenerateResource(floor, b1, 50 / tile, r9); // 玫瑰 + core.plugin.GenerateResource(floor, b1, 60 / tile, r8); // 向日葵 + core.plugin.GenerateResource(floor, b1, 100 / tile, r1); // 橡树 + core.plugin.GenerateResource(floor, b1, 60 / tile, r5); // 金合欢树 + core.plugin.GenerateResource(floor, b1, 100 / tile, r11); // 南瓜 + core.plugin.GenerateResource(floor, b1, 500 / tile, r10); // 蜂巢 + core.plugin.GenerateStructure(floor, b1, weight * 3000 / flags.worldlist["0"]["结构"]["岩浆池"][0], "岩浆池"); + core.plugin.GenerateStructure(floor, b1, weight * 6000 / flags.worldlist["0"]["结构"]["废弃传送门"][0], "废弃传送门"); + core.plugin.GenerateStructure(floor, b1, weight * 8000 / flags.worldlist["0"]["结构"]["前哨站"][0], "前哨站"); + core.plugin.GenerateStructure(floor, b1, weight * 3000 / flags.worldlist["0"]["结构"]["村庄"][0], "村庄"); + } else if (formation == "森林") { + var tile = flags.worldlist["0"]["地形"]["森林"][0]; + core.plugin.GenerateResource(floor, b2, 10 / tile, r6); // 小草丛 + core.plugin.GenerateResource(floor, b2, 10 / tile, r7); // 大草丛 + core.plugin.GenerateResource(floor, b2, 10 / tile, r1); // 橡树 + core.plugin.GenerateResource(floor, b2, 10 / tile, r2); // 白桦树 + core.plugin.GenerateResource(floor, b2, 30 / tile, r11); // 南瓜 + core.plugin.GenerateResource(floor, b2, 50 / tile, r9); // 玫瑰 + core.plugin.GenerateResource(floor, b2, 200 / tile, r10); // 蜂巢 + core.plugin.GenerateStructure(floor, b2, weight * 1500 / flags.worldlist["0"]["结构"]["废墟"][0], "废墟"); + } else if (formation == "丛林") { + var tile = flags.worldlist["0"]["地形"]["丛林"][0]; + core.plugin.GenerateResource(floor, b3, 100 / tile, r11); // 南瓜 + core.plugin.GenerateResource(floor, b3, 50 / tile, r12); // 西瓜 + core.plugin.GenerateResource(floor, b3, 7 / tile, r13); // 竹子 + core.plugin.GenerateResource(floor, b3, 7 / tile, r4); // 丛林树 + core.plugin.GenerateStructure(floor, b3, weight * 1500 / flags.worldlist["0"]["结构"]["废墟"][0], "废墟"); + core.plugin.GenerateStructure(floor, b3, weight * 3500 / flags.worldlist["0"]["结构"]["丛林神庙"][0], "丛林神庙"); + } else if (formation == "针叶林") { + var tile = flags.worldlist["0"]["地形"]["针叶林"][0]; + core.plugin.GenerateResource(floor, b4, 8 / tile, r3); // 云杉树 + core.plugin.GenerateResource(floor, b4, 50 / tile, r11); // 南瓜 + core.plugin.GenerateStructure(floor, b4, weight * 3000 / flags.worldlist["0"]["结构"]["村庄"][0], "村庄"); + } else if (formation == "雪原") { + var tile = flags.worldlist["0"]["地形"]["雪原"][0]; + core.plugin.GenerateResource(floor, b5, 20 / tile, r3); // 云杉树 + core.plugin.GenerateStructure(floor, b5, weight * 3000 / flags.worldlist["0"]["结构"]["雪屋"][0], "雪屋"); + core.plugin.GenerateStructure(floor, b5, weight * 3000 / flags.worldlist["0"]["结构"]["前哨站"][0], "前哨站"); + } else if (formation == "沙漠") { + var tile = flags.worldlist["0"]["地形"]["沙漠"][0]; + core.plugin.GenerateResource(floor, b6, 30 / tile, r14); // 枯萎灌木 + core.plugin.GenerateResource(floor, b6, 40 / tile, r15); // 仙人掌 + core.plugin.GenerateResource(floor, b6, 5000, r16); // 仙人掌花 + core.plugin.GenerateStructure(floor, b6, 800 / flags.worldlist["0"]["结构"]["水井"][0], "水井"); + core.plugin.GenerateStructure(floor, b6, weight * 2500 / flags.worldlist["0"]["结构"]["村庄"][0], "村庄"); + core.plugin.GenerateStructure(floor, b6, weight * 3500 / flags.worldlist["0"]["结构"]["沙漠神殿"][0], "沙漠神殿"); + } else if (formation == "恶地") { + var tile = flags.worldlist["0"]["地形"]["恶地"][0]; + core.plugin.GenerateResource(floor, b7, 20 / tile, r14); // 枯萎灌木 + core.plugin.GenerateStructure(floor, b7, weight * 2000 / flags.worldlist["0"]["结构"]["岩浆池"][0], "岩浆池"); + core.plugin.GenerateStructure(floor, b7, weight * 3000 / flags.worldlist["0"]["结构"]["废弃传送门"][0], "废弃传送门"); + core.plugin.GenerateStructure(floor, b7, weight * 3000 / flags.worldlist["0"]["结构"]["前哨站"][0], "前哨站"); + core.plugin.GenerateStructure(floor, b7, weight * 3000 / flags.worldlist["0"]["结构"]["沙漠神殿"][0], "沙漠神殿"); + } else if (formation == "海洋") { + var tile = flags.worldlist["0"]["地形"]["海洋"][0]; + core.plugin.GenerateResource(floor, b8, 50 / tile, r33); // 沙砾 + core.plugin.GenerateResource(floor, b8, 50 / tile, r17); // 海草 + core.plugin.GenerateResource(floor, b8, 30 / tile, r19); // 红珊瑚 + core.plugin.GenerateResource(floor, b8, 30 / tile, r20); // 黄珊瑚 + core.plugin.GenerateResource(floor, b8, 30 / tile, r21); // 蓝珊瑚 + core.plugin.GenerateResource(floor, b8, 30 / tile, r22); // 粉珊瑚 + core.plugin.GenerateResource(floor, b8, 10 / tile, r18); // 海带 + core.plugin.GenerateStructure(floor, b8, weight * 1500 / flags.worldlist["0"]["结构"]["废墟"][0], "废墟"); + core.plugin.GenerateStructure(floor, b8, weight * 3000 / flags.worldlist["0"]["结构"]["沉船"][0], "沉船"); + } else if (formation == "海底神殿") { + core.plugin.CreateRate("正在生成海底神殿"); + var r1 = core.plugin.rand3(24) + 2; + var r2 = core.plugin.rand3(24) + 2; + if (floor in Object.keys(flags.nearstr)) { + flags.nearstr[floor].push([r1 + 17, r2 + 17, 20, "海底神殿"]); + } else { + flags.nearstr[floor] = [[r1 + 17, r2 + 17, 20, "海底神殿"],]; + } + + for (var i = 1; i < 62; i++) { + for (var j = 1; j < 62; j++) { + if (i == r1 && j == r2) { + for (var y = 0; y < flags.structure["海底神殿"].length; y++) { + for (var x = 0; x < flags.structure["海底神殿"][y].length; x++) { + if (flags.structure["海底神殿"] && i + x < 62 && j + y < 62) { + if (flags.structure["海底神殿"][y][x] == 900) { + var rand1 = core.plugin.rand3(100); + if (rand1 < 90) { + core.setBlock(900, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = 900; + } else if (rand1 < 98) { + core.setBlock(902, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = 902; + } else { + core.setBlock(905, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = 905; + } + } else { + core.setBlock(flags.structure["海底神殿"][y][x], i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = flags.structure["海底神殿"][y][x]; + } + } + } + } + return; + } + } + } + } else if (formation == "丘陵") { + var tile = flags.worldlist["0"]["地形"]["丘陵"][0]; + core.plugin.GenerateResource(floor, b9, 10 / tile, r27); // 圆石 + core.plugin.GenerateResource(floor, b9, 50 / tile, r33); // 沙砾 + core.plugin.GenerateResource(floor, b9, 50 / tile, r28); // 煤矿 + core.plugin.GenerateResource(floor, b9, 80 / tile, r30); // 铜矿 + core.plugin.GenerateResource(floor, b9, 100 / tile, r29); // 铁矿 + core.plugin.GenerateStructure(floor, b9, weight * 1000 / flags.worldlist["0"]["结构"]["岩浆池"][0], "岩浆池"); + core.plugin.GenerateStructure(floor, b9, weight * 2500 / flags.worldlist["0"]["结构"]["废弃传送门"][0], "废弃传送门"); + } else if (formation == "蘑菇岛") { + var tile = flags.worldlist["0"]["地形"]["蘑菇岛"][0]; + core.plugin.GenerateResource(floor, b10, 30 / tile, r31); // 红色蘑菇 + core.plugin.GenerateResource(floor, b10, 30 / tile, r32); // 棕色蘑菇 + } else if (formation == "河流") { + var tile = flags.worldlist["0"]["地形"]["河流"][0]; + core.plugin.GenerateResource(floor, b11, 10 / tile, r17); // 海草 + core.plugin.GenerateResource(floor, b11, 10 / tile, r33); // 沙砾 + } + } //根据ID生成资源 + this.GenerateResource = function (floor, bg, rDense, resource) { + core.plugin.CreateRate("正在生成资源"); + //生成资源 + for (var i = 1; i < 62; i++) { + for (var j = 1; j < 62; j++) { + if (core.getBgNumber(i, j, floor) == bg && !core.getBlockId(i, j, floor)) { + var rand1 = core.plugin.rand3(rDense); + if (rand1 == 0) { + core.setBlock(resource, i, j, floor); + flags.maplist[floor][j][i] = resource; + } + } + } + } + } //生成结构 + this.GenerateStructure = function (floor, bg, weight, stru) { + core.plugin.CreateRate("正在生成结构"); + if (flags.worldlist["0"]["世界选项"][1] == "关") return; + var end = [0, 0]; + var s1 = 0, s2 = 0, s3 = 0, s4 = 0; + function Stru1(s1, s2, s3, s4) { + for (var i = 4; i < 62; i++) { + for (var j = 4; j < 62; j++) { + if (core.getBgNumber(i - 1, j - 1, floor) == bg && + core.getBgNumber(i - 1, j, floor) == bg && + core.getBgNumber(i - 1, j + 1, floor) == bg && + core.getBgNumber(i, j - 1, floor) == bg && + core.getBgNumber(i, j, floor) == bg && + core.getBgNumber(i, j + 1, floor) == bg && + core.getBgNumber(i + 1, j - 1, floor) == bg && + core.getBgNumber(i + 1, j, floor) == bg && + core.getBgNumber(i + 1, j + 1, floor) == bg) { + if (core.plugin.rand3(weight) == 0 && i + end[0] < 62 && j + end[1] < 62) { + //console.log(s1) + var skin = ""; + switch (bg) { + case 341: + if (stru == "村庄") { + skin = "云杉皮肤"; + } + break; + case 343: + if (stru == "村庄") { + skin = "砂岩皮肤"; + } + break; + case 345: + if (stru == "废墟") { + skin = "海底废墟皮肤"; + } + break; + case 344: + if (stru == "沙漠神殿") { + skin = "红沙神殿皮肤"; + } + break; + } + function Str(str) { + if (skin == "云杉皮肤") { + if (str == 360) return 362; + } else if (skin == "砂岩皮肤") { + if (str == 360) return 928; + } else if (skin == "红沙神殿皮肤") { + if (str == 928) return 929; + if (str == 897) return 930; + } + return str; + } + function Strbg(strbg) { + if (skin == "云杉皮肤") { + if (strbg == 365) return 367; + } else if (skin == "砂岩皮肤") { + if (strbg == 365) return 343; + } else if (skin == "海底废墟皮肤") { + return 345; + } + return strbg; + } + function isbox(s1, str, x, y) { + if (s1 == "村庄小屋") { + if (str == 414 || str == 419 || str == 415 || str == 420) { + var r = core.plugin.rand3(12); + if (r == 0) { + core.setBlock(414, x, y, floor); + flags.maplist[floor][y][x] = 414; + } else if (r == 1) { + core.setBlock(419, x, y, floor); + flags.maplist[floor][y][x] = 419; + } else if (r == 2) { + core.setBlock(415, x, y, floor); + flags.maplist[floor][y][x] = 415; + } else if (r == 3) { + core.setBlock(420, x, y, floor); + flags.maplist[floor][y][x] = 420; + if (skin == "") { + core.plugin.Chestloot("平原村庄宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 36); + } else if (skin == "云杉皮肤") { + core.plugin.Chestloot("针叶林村庄宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 36); + } else if (skin == "砂岩皮肤") { + core.plugin.Chestloot("沙漠村庄宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 36); + } + } + return; + } + } else if (s1 == "村庄农田") { + if (str == 918 || str == 919 || str == 920) { + var r = core.plugin.rand3(4); + if (r == 0) { + core.setBlock(918, x, y, floor); + flags.maplist[floor][y][x] = 918; + } else if (r == 1) { + core.setBlock(919, x, y, floor); + flags.maplist[floor][y][x] = 919; + } else if (r == 2) { + core.setBlock(920, x, y, floor); + flags.maplist[floor][y][x] = 920; + } + return; + } + } else if (s1 == "村庄铁匠铺") { + if (str == 420) { + core.setBlock(420, x, y, floor); + flags.maplist[floor][y][x] = 420; + core.plugin.Chestloot("铁匠铺宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 42); + return; + } + } else if (s1 == "前哨站") { + if (str == 420) { + core.setBlock(420, x, y, floor); + flags.maplist[floor][y][x] = 420; + core.plugin.Chestloot("前哨站宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 48); + return; + } + } else if (s1 == "雪屋") { + if (str == 372 || str == 375 || str == 373) { + var r = core.plugin.rand3(3); + if (r == 0) { + core.setBlock(372, x, y, floor); + flags.maplist[floor][y][x] = 372; + } else if (r == 1) { + core.setBlock(375, x, y, floor); + flags.maplist[floor][y][x] = 375; + } else if (r == 2) { + core.setBlock(373, x, y, floor); + flags.maplist[floor][y][x] = 373; + } + return; + } else if (str == 414 || str == 419 || str == 415 || str == 424) { + var r = core.plugin.rand3(16); + if (r < 3) { + core.setBlock(414, x, y, floor); + flags.maplist[floor][y][x] = 414; + } else if (r < 6) { + core.setBlock(419, x, y, floor); + flags.maplist[floor][y][x] = 419; + } else if (r < 9) { + core.setBlock(415, x, y, floor); + flags.maplist[floor][y][x] = 415; + } else if (r < 12) { + core.setBlock(424, x, y, floor); + flags.maplist[floor][y][x] = 424; + } + return; + } + } else if (s1 == "沉船") { + if (str == 362 || str == 360) { + var r = core.plugin.rand3(3); + if (r == 0) { + core.setBlock(362, x, y, floor); + flags.maplist[floor][y][x] = 362; + } else if (r == 1) { + core.setBlock(360, x, y, floor); + flags.maplist[floor][y][x] = 360; + } + return; + } + } else if (s1 == "丛林神庙") { + if (str == 420) { + core.setBlock(420, x, y, floor); + flags.maplist[floor][y][x] = 420; + core.plugin.Chestloot("丛林神庙宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 54); + return; + } + } else if (s1 == "沉船") { + if (str == 420) { + core.setBlock(420, x, y, floor); + flags.maplist[floor][y][x] = 420; + core.plugin.Chestloot("沉船宝箱", (x * 1000 + y).toString().padStart(6, '0') + floor, 48); + return; + } + } + var block1 = Str(str) + core.setBlock(block1, x, y, floor); + flags.maplist[floor][y][x] = block1; + } + if (s1 == "废墟") { + var r1 = 0; //空气 + var r2 = 349; //沙砾 + var r3 = 916; //圆石 + var r4 = 404; //铁矿 + var r5 = 405; //铜矿 + var r6 = 406; //金矿 + var box = 0; + for (var y = 0; y < 7; y++) { + for (var x = 0; x < 7; x++) { + if (i + x < 62 && j + y < 62) { + function set() { + var rand1 = core.plugin.rand3(50); + var r; + if (rand1 < 30) { + r = r1; + } else if (rand1 < 36) { + r = r2; + } else if (rand1 < 45) { + r = r3; + } else if (rand1 < 47) { + r = r4; + } else if (rand1 < 49) { + r = r5; + } else { + r = r6; + } + return r; + } + var block1 = set() + core.setBlock(block1, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = block1; + var b1 = set(); + if (b1 != 0) { + var block3 = Strbg(b1); + core.setBgFgBlock('bg', block3, i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = block3; + } + if (box == 0 && x >= 2 && x <= 4 && y >= 2 && y <= 4 && core.plugin.rand3(40) == 0) { + core.setBlock(420, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = 420; + if (skin == "海底废墟皮肤") { + core.plugin.Chestloot("海底废墟宝箱", ((i + x) * 1000 + j + y).toString().padStart(6, '0') + floor, 39); + } else { + core.plugin.Chestloot("陆地废墟宝箱", ((i + x) * 1000 + j + y).toString().padStart(6, '0') + floor, 36); + } + box = 1; + } + } + } + } + } else if (s1 == "岩浆池") { + var r1 = 0; //空气 + var r2 = 916; //圆石 + var r3 = 910; //岩浆 + var rand1 = core.plugin.rand3(2) + 2; + for (var y = -rand1; y <= rand1; y++) { + for (var x = -rand1; x <= rand1; x++) { + if (i + x < 62 && j + y < 62) { + core.setBgFgBlock('bg', r3, i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = r3; + core.setBlock(r1, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = r1; + } + } + } + for (var y = -rand1; y <= rand1; y++) { + for (var x = -rand1; x <= rand1; x++) { + if (x >= -1 && x <= 1 && y >= -1 && y <= 1) continue; + if (core.getBgNumber(i + x + 1, j + y, floor) != r3) { + var rand2 = core.plugin.rand3(2); + if (rand2 == 0) { + core.setBgFgBlock('bg', core.getBgNumber(i + x + 1, j + y, floor), i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = core.getBgNumber(i + x + 1, j + y, floor); + } else { + core.setBgFgBlock('bg', r3, i + x + 1, j + y, floor); + flags.bglist[floor][j + y][i + x + 1] = r3; + core.setBlock(r1, i + x + 1, j + y, floor); + flags.maplist[floor][j + y][i + x + 1] = r1; + } + } else if (core.getBgNumber(i + x - 1, j + y, floor) != r3) { + var rand2 = core.plugin.rand3(2); + if (rand2 == 0) { + core.setBgFgBlock('bg', core.getBgNumber(i + x - 1, j + y, floor), i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = core.getBgNumber(i + x - 1, j + y, floor); + } else { + core.setBgFgBlock('bg', r3, i + x - 1, j + y, floor); + flags.bglist[floor][j + y][i + x - 1] = r3; + core.setBlock(r1, i + x - 1, j + y, floor); + flags.maplist[floor][j + y][i + x - 1] = r1; + } + } else if (core.getBgNumber(i + x, j + y + 1, floor) != r3) { + var rand2 = core.plugin.rand3(2); + if (rand2 == 0) { + core.setBgFgBlock('bg', core.getBgNumber(i + x, j + y + 1, floor), i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = core.getBgNumber(i + x, j + y + 1, floor); + } else { + core.setBgFgBlock('bg', r3, i + x, j + y + 1, floor); + flags.bglist[floor][j + y + 1][i + x] = r3; + core.setBlock(r1, i + x, j + y + 1, floor); + flags.maplist[floor][j + y + 1][i + x] = r1; + } + } else if (core.getBgNumber(i + x, j + y - 1, floor) != r3) { + var rand2 = core.plugin.rand3(2); + if (rand2 == 0) { + core.setBgFgBlock('bg', core.getBgNumber(i + x, j + y - 1, floor), i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = core.getBgNumber(i + x, j + y - 1, floor); + } else { + core.setBgFgBlock('bg', r3, i + x, j + y - 1, floor); + flags.bglist[floor][j + y - 1][i + x] = r3; + core.setBlock(r1, i + x, j + y - 1, floor); + flags.maplist[floor][j + y - 1][i + x] = r1; + } + } + } + } + } else if (s1 == "废弃传送门") { + var r1 = 0; //空气 + var r2 = 922; //石砖 + var r3 = 923; //苔石砖 + var r4 = 927; //裂纹石砖 + var r5 = 924; //下界岩 + var r6 = 896; //黑曜石 + var r7 = 910; //岩浆 + var rand1 = core.plugin.rand3(3) + 3; + var w1 = 0, w2 = 0; + var box = 0; + do { + for (var y = -w1; y <= w1; y++) { + for (var x = -w2; x <= w2; x++) { + if (x != -w2 && x != w2 && y != -w1 && y != w1) continue; + if (i + x < 62 && j + y < 62) { + if (Math.abs(w1) <= 2 && core.plugin.rand3(3) == 0) { + core.setBlock(r6, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = r6; + } else if (Math.abs(w1) > 2 && core.plugin.rand3(2) == 0) { + function set() { + var rand2 = core.plugin.rand3(44); + var r; + if (rand2 < 20) { + r = r1; + } else if (rand2 < 26) { + r = r2; + } else if (rand2 < 32) { + r = r3; + } else if (rand2 < 38) { + r = r4; + } else { + r = r5; + } + return r; + } + var block2 = set(); + core.setBlock(block2, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = block2; + if (core.plugin.rand3(25) < 15) { + core.setBgFgBlock('bg', r7, i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = r7; + } + if (box == 0 && core.plugin.rand3(30) == 0) { + core.setBlock(420, i + x, j + y, floor); + flags.maplist[floor][j + y][i + x] = 420; + core.plugin.Chestloot("废弃传送门宝箱", ((i + x) * 1000 + j + y).toString().padStart(6, '0') + floor, 54); + box = 1; + } + } + } + } + } + w1 += 1; + w2 += 1; + } while (w1 <= rand1); + } else { + var r1 = core.plugin.rand3(4) + 2; + var r2 = core.plugin.rand3(5) - 3; + var r3 = core.plugin.rand3(5) - 3; + var r4 = core.plugin.rand3(4) + 2; + var r5 = core.plugin.rand3(4) + 2; + var r6 = core.plugin.rand3(4) + 2; + if (flags.structure[s1]) { + if (s1 == "前哨站") { + if (floor in Object.keys(flags.nearstr)) { + flags.nearstr[floor].push([i + x + 6, j + y + 6, 9, "前哨站"]); + } else { + flags.nearstr[floor] = [[i + x + 6, j + y + 6, 9, "前哨站"],]; + } + } + for (var y = 0; y < flags.structure[s1].length; y++) { + for (var x = 0; x < flags.structure[s1][y].length; x++) { + if (i + x < 62 && j + y < 62) { + var str = flags.structure[s1][y][x]; + var strbg; + if (flags.structurebg[s1] && flags.structurebg[s1][y][x] != 0) { + strbg = flags.structurebg[s1][y][x]; + var block4 = Strbg(strbg); + core.setBgFgBlock('bg', block4, i + x, j + y, floor); + flags.bglist[floor][j + y][i + x] = block4; + } + isbox(s1, str, i + x, j + y); + } + } + } + } + if (flags.structure[s2]) { + for (var y = 0; y < flags.structure[s2].length; y++) { + for (var x = 0; x < flags.structure[s2][y].length; x++) { + if (i + x + flags.structure[s1][0].length + r1 < 62 && j + y + flags.structure[s1].length + r2 < 62) { + var str = flags.structure[s2][y][x]; + var strbg; + if (flags.structurebg[s2] && flags.structurebg[s2][y][x] != 0) { + strbg = flags.structurebg[s2][y][x]; + var block5 = Strbg(strbg); + core.setBgFgBlock('bg', block5, i + x + flags.structure[s1][0].length + r1, j + y + r2, floor); + flags.bglist[floor][j + y + r2][i + x + flags.structure[s1][0].length + r1] = block5; + } + isbox(s2, str, i + x + flags.structure[s1][0].length + r1, j + y + r2); + } + } + } + } + if (flags.structure[s3]) { + for (var y = 0; y < flags.structure[s3].length; y++) { + for (var x = 0; x < flags.structure[s3][y].length; x++) { + if (i + x + flags.structure[s1][0].length + r3 < 62 && j + y + flags.structure[s1].length + r4 < 62) { + var str = flags.structure[s3][y][x]; + var strbg; + if (flags.structurebg[s3] && flags.structurebg[s3][y][x] != 0) { + strbg = flags.structurebg[s3][y][x]; + var block6 = Strbg(strbg); + core.setBgFgBlock('bg', block6, i + x + r3, j + y + flags.structure[s1].length + r4, floor); + flags.bglist[floor][j + y + flags.structure[s1].length + r4][i + x + r3] = block6; + } + isbox(s3, str, i + x + r3, j + y + flags.structure[s1].length + r4); + } + } + } + } + if (flags.structure[s4]) { + for (var y = 0; y < flags.structure[s4].length; y++) { + for (var x = 0; x < flags.structure[s4][y].length; x++) { + if (i + x + flags.structure[s1][0].length + r5 < 62 && j + y + flags.structure[s1].length + r6 < 62) { + var str = flags.structure[s4][y][x]; + var strbg; + if (flags.structurebg[s4] && flags.structurebg[s4][y][x] != 0) { + strbg = flags.structurebg[s4][y][x]; + var block7 = Strbg(strbg); + core.setBgFgBlock('bg', block7, i + x + flags.structure[s1][0].length + r5, j + y + flags.structure[s1].length + r6, floor); + flags.bglist[floor][j + y + flags.structure[s1].length + r6][i + x + flags.structure[s1][0].length + r5] = block7; + } + isbox(s4, str, i + x + flags.structure[s1][0].length + r5, j + y + flags.structure[s1].length + r6); + } + } + } + } + //接下来一定范围内不能生成该结构 + i += end[0]; + j += end[1]; + } + } + } + } + } + } + switch (stru) { + case "水井": + end = [4, 4]; + s1 = "水井"; + break; + case "村庄": + end = [15, 15]; + var st = ["村庄小屋", "村庄小屋", "村庄小屋", "村庄小屋", "村庄小屋", "村庄农田", "村庄农田", "村庄铁匠铺", "",]; + var r1 = core.plugin.rand3(8); + s1 = st[r1]; + st.splice(r1, 1); + r1 = core.plugin.rand3(8); + s2 = st[r1]; + st.splice(r1, 1); + r1 = core.plugin.rand3(7); + s3 = st[r1]; + st.splice(r1, 1); + r1 = core.plugin.rand3(6); + s4 = st[r1]; + break; + case "雪屋": + end = [10, 10]; + s1 = "雪屋"; + break; + case "前哨站": + end = [15, 15]; + s1 = "前哨站"; + break; + case "沉船": + end = [30, 15]; + s1 = "沉船"; + break; + case "废墟": + end = [5, 5]; + s1 = "废墟"; + break; + case "岩浆池": + end = [5, 5]; + s1 = "岩浆池"; + break; + case "废弃传送门": + end = [5, 5]; + s1 = "废弃传送门"; + break; + case "沙漠神殿": + end = [30, 30]; + s1 = "沙漠神殿"; + break; + case "丛林神庙": + end = [20, 20]; + s1 = "丛林神庙"; + break; + } + Stru1(s1, s2, s3, s4); + + } //生成基岩 + this.GenerateBedrock = function () { + core.plugin.CreateRate("正在生成基岩"); + for (var id in core.floorIds) { + if (core.floors[core.floorIds[id]].width >= 63) { + var floor = core.floorIds[id]; + for (var i = 0; i < 63; i++) { + for (var j = 0; j < 63; j++) { + if (i == 0 || i == 63 - 1 || j == 0 || j == 63 - 1) { + if (core.getBlockNumber(i, j, floor) != 91 && core.getBlockNumber(i, j, floor) != 92 && core.getBlockNumber(i, j, floor) != 93 && core.getBlockNumber(i, j, floor) != 94) { + core.setBlock(302, i, j, floor); + flags.maplist[floor][j][i] = 302; + } + } + } + } + } + } + var floors1 = ["zhu7", "zhu8", "zhu9", "zhu12", "zhu13", "zhu14", "zhu17", "zhu18", "zhu19"]; + for (var id = 0; id < floors1.length; id++) { + var dir = [];//上下左右 + if (flags.worldlist["0"]["世界选项"][3] == "小" || flags.worldlist["0"]["世界选项"][3] == "中") { + switch (id) { + case 0: + dir = [0, 1, 0, 1]; + break; + case 1: + dir = [0, 1, 1, 1]; + break; + case 2: + dir = [0, 1, 1, 0]; + break; + case 3: + dir = [1, 1, 0, 1]; + break; + case 4: + dir = [1, 1, 1, 1]; + break; + case 5: + dir = [1, 1, 1, 0]; + break; + case 6: + dir = [1, 0, 0, 1]; + break; + case 7: + dir = [1, 0, 1, 1]; + break; + case 8: + dir = [1, 0, 1, 0]; + break; + } + } else if (flags.worldlist["0"]["世界选项"][3] == "大") { + dir = [1, 1, 1, 1]; + } + if (dir[0]) { + core.setBlock(91, 31, 0, floors1[id]); + flags.maplist[floors1[id]][0][31] = 91; + } + if (dir[1]) { + core.setBlock(93, 31, 62, floors1[id]); + flags.maplist[floors1[id]][62][31] = 93; + } + if (dir[2]) { + core.setBlock(92, 0, 31, floors1[id]); + flags.maplist[floors1[id]][31][0] = 92; + } + if (dir[3]) { + core.setBlock(94, 62, 31, floors1[id]); + flags.maplist[floors1[id]][31][62] = 94; + } + } + var floors2 = ["dong1", "dong2", "dong3", "dong4", "dong5", "dong6", "dong7", "dong8", "dong9",]; + for (var id = 0; id < floors2.length; id++) { + var dir = [];//上下左右 + switch (id) { + case 0: + dir = [0, 1, 0, 1]; + break; + case 1: + dir = [0, 1, 1, 1]; + break; + case 2: + dir = [0, 1, 1, 0]; + break; + case 3: + dir = [1, 1, 0, 1]; + break; + case 4: + dir = [1, 1, 1, 1]; + break; + case 5: + dir = [1, 1, 1, 0]; + break; + case 6: + dir = [1, 0, 0, 1]; + break; + case 7: + dir = [1, 0, 1, 1]; + break; + case 8: + dir = [1, 0, 1, 0]; + break; + } + if (dir[0]) { + core.setBlock(91, 31, 0, floors2[id]); + flags.maplist[floors2[id]][0][31] = 91; + } + if (dir[1]) { + core.setBlock(93, 31, 62, floors2[id]); + flags.maplist[floors2[id]][62][31] = 93; + } + if (dir[2]) { + core.setBlock(92, 0, 31, floors2[id]); + flags.maplist[floors2[id]][31][0] = 92; + } + if (dir[3]) { + core.setBlock(94, 62, 31, floors2[id]); + flags.maplist[floors2[id]][31][62] = 94; + } + } + } //填充战利品箱 + this.Chestloot = function (ch, po, grid = 54) { + core.plugin.CreateRate("正在生成宝藏"); + var c1 = flags.chestloot[ch]; + var weight1 = 0; + var weight2 = 0; + for (var i = 1; i < c1.length; i++) { + weight1 += c1[i][2]; + } + var rand2 = core.plugin.rand3(c1[0][1] - c1[0][0] + 1) + c1[0][0]; + var num = 0; + flags.boxlist[po] = [["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0], ["", 0],]; + do { + var rand1 = core.plugin.rand3(weight1); + box: for (var i = 1; i < c1.length; i++) { + var c = c1[i]; + if (rand1 <= c[2] + weight2) { + var n; + var rand3 = core.plugin.rand3(grid); + while (flags.boxlist[po][rand3][0] != ""/* && n < 100*/) { + rand3 = core.plugin.rand3(grid); + n += 1; + } + flags.boxlist[po][rand3][0] = c[0]; + flags.boxlist[po][rand3][1] = core.plugin.rand3(c[1][1] - c[1][0] + 1) + c[1][0]; + weight2 = 0; + break box; + } else { + weight2 += c[2]; + } + } + num += 1; + } while (num < rand2) + + } //根据地图数组放置图块 + this.Maplist = function () { + if (!flags.maplist) return; + for (var floor in flags.maplist) { + for (var y = 0; y < flags.maplist[floor].length; y++) { + for (var x = 0; x < flags.maplist[floor][y].length; x++) { + if (flags.maplist[floor][y][x] != undefined && flags.maplist[floor][y][x] != 0) { + core.setBlock(flags.maplist[floor][y][x], x, y, floor); + } + } + } + } + if (!flags.bglist) return; + for (var floor in flags.bglist) { + for (var y = 0; y < flags.bglist[floor].length; y++) { + for (var x = 0; x < flags.bglist[floor][y].length; x++) { + if (flags.bglist[floor][y][x] != undefined) { + core.setBgFgBlock('bg', flags.bglist[floor][y][x], x, y, floor); + } else { + core.setBgFgBlock('bg', 0, x, y, floor); + } + } + } + } + } //世界生成进度 + this.CreateRate = function (text = "") { + var ctx = document.getElementById("title").getContext('2d'); + core.clearMap(ctx); + core.drawImage(ctx, "jz4.png", 0, 0); + core.setAlpha(ctx, 0.6); + core.fillRect(ctx, 0, 350, 554, 100, "black"); + core.setAlpha(ctx, 1); + core.fillRoundRect(ctx, 50, 390, 450, 10, 3, "#a1a1a1"); + core.fillRoundRect(ctx, 50, 390, 450 * (flags.createrate / 100), 10, 3, "#5bfb62"); + core.fillText(ctx, text, 60, 380, "white", ui.prototype._buildFont(20, false), 400); + core.fillText(ctx, flags.createrate + " %", 440, 380, "white", ui.prototype._buildFont(20, false), 400); + } //随机洞穴层图块 + this.Caveblock = function (type, isbg) { + if (isbg != 1 && core.plugin.rand3(2) == 0) return 0; + var c1 = flags.cavelayer[type]; + var weight1 = 0; + var weight2 = 0; + for (var i = 0; i < c1.length; i++) { + weight1 += c1[i][1]; + } + var r1 = core.plugin.rand3(weight1); + for (var i = 0; i < c1.length; i++) { + var c = c1[i]; + if (r1 <= c[1] + weight2) { + // if (maps_90f36752_8815_4be8_b32b_d7fad1d0542e[c[0]].name in flags.a) { + // flags.a[maps_90f36752_8815_4be8_b32b_d7fad1d0542e[c[0]].name] +=1; + // } + // else { + // flags.a[maps_90f36752_8815_4be8_b32b_d7fad1d0542e[c[0]].name] = 0; + // } + return c[0]; + } else { + weight2 += c[1]; + } + } + } + + + + }, + "20-加载图": function () { + this.jiazaitu = function (name, time) { + //显示加载图 + if (!core.getContextByName("jzt")) { + core.createCanvas("jzt", 0, 0, 416, 416, 136); + core.drawImage("jzt", name, 0, 0, 416, 416); + flags.interface = "锁定"; + } + setTimeout(function () { + //加载图累减 + if (time > 0) { + time -= 100; + core.plugin.jiazaitu(name, time); + } else { + core.deleteCanvas("jzt"); + flags.interface = "物品栏"; + return; + } + }, 100); + + } + + }, + "21-出生点设置": function () { + this.chushengdian = function (floor) { + //出生点设置 + flags.revive[0] = floor; + flags.revive[1] = core.rand(core.status.maps[floor].width - 18) + 9; + flags.revive[2] = core.rand(core.status.maps[floor].height - 18) + 9; + } + this.chongshengdian = function (x, y, floor) { + if (flags.revive[3] != floor || flags.revive[4] != x || flags.revive[5] != y) { + flags.revive[3] = floor; + flags.revive[4] = x; + flags.revive[5] = y; + flags.tip[0] = "重生点已设置"; + core.ui.drawStatusBar(); + } + } + + }, + "22-角色死亡检测": function () { + this.siwangjiance = function () { + //角色死亡事件 + if (core.status.hero.hp <= 0 && !core.getContextByName("die")) { + core.status.hero.hp = 0; + flags.interface = "物品栏"; + core.plugin.drawMC(); + flags.interface = "锁定"; + flags.tech = 0; + flags.CustomKey = 0; + core.status.event.id = null; + //core.ui._createUIEvent(); + if (core.getContextByName("die") == null) { + core.createCanvas("die", 0, 0, 416, 416, 150); + } else { + core.clearMap('die'); + } + core.ui.setAlpha("die", 0.6); + core.ui.fillRect("die", 0, 0, 416, 416); + core.ui.setAlpha("die", 1); + core.ui.fillText("die", "你死了!", 160, 128 + 8, "white", ui.prototype._buildFont(30, false), 160); + core.fillRect("die", 128, 224, 160, 32, "#53534b"); + core.ui.fillText("die", "重生", 160 + 26, 224 + 22, "white", ui.prototype._buildFont(20, false), 160); + core.fillRect("die", 128, 288, 160, 32, "#53534b"); + core.ui.fillText("die", "返回标题界面", 146, 288 + 22, "white", ui.prototype._buildFont(20, false), 160); + for (var en in flags.enemylist) { + core.plugin.delenemy(en, 0); + } + if (flags.worldlist["0"]["游戏规则"][0] == "开") { + if (flags.worldlist["0"]["难度"] == "简单") { + flags.level[0] = Math.floor(flags.level[0] / 4 * 3); + } else if (flags.worldlist["0"]["难度"] == "普通") { + flags.level[0] = Math.floor(flags.level[0] / 2); + } else if (flags.worldlist["0"]["难度"] == "困难") { + flags.level[0] = 0; + } + flags.level[1] = 0; + core.plugin.jingyandengji(); + } + + } + } + + }, + "23-敌怪": function () { + //刷怪选取 + function refEnemy(land) { + var world = "前期"; + if (flags.worldlevel <= 3) { + world = "前期"; + } else if (flags.worldlevel <= 7) { + world = "中期"; + } else { + world = "后期"; + } + var re = flags.refenemy[world][land][flags.timePeriod == "夜晚" ? "夜晚" : "白天"]; + var weight1 = 0; + var weight2 = 0; + for (var i = 0; i < re.length; i++) { + weight1 += re[i][1]; + } + var r1 = core.rand2(weight1); + for (var i = 0; i < re.length; i++) { + var r = re[i]; + if (r1 <= r[1] + weight2) { + return r[0]; + } else { + weight2 += r[1]; + } + } + }//需要刷怪时,选择合适的位置刷不同的怪 + this.shuaguai = function () { + //确定坐标点 + var ex, ey, ex1, ey1; + var x = core.status.hero.loc.x; + var y = core.status.hero.loc.y; + function area() { + var x, y; + x = core.rand2(19) - 9; + if (core.rand2(2) == 0) y = core.rand2(3) + 7; + else y = core.rand2(3) - 9; + ex = x; + ey = y; + } + area(); + if (core.rand2(2) == 0) { + ex1 = x + ex; + ey1 = y + ey; + } else { + ex1 = x + ey; + ey1 = y + ex; + } + if (core.getBlockId(ex1, ey1) || ex1 <= 0 || ex1 >= 63 || ey1 <= 0 || ey1 >= 63) return; + + var enemy = "E1"; + //确定维度 + if (flags.dimension[0] == "主世界") { + if (core.status.floorId in flags.nearstr) { + var near = flags.nearstr[core.status.floorId]; + for (var i = 0; i < near.length; i++) { + if (Math.abs(ex1 - near[i][0]) <= near[i][2] || Math.abs(ey1 - near[i][1]) <= near[i][2]) { + if (near[i][3] == "前哨站") { + if (core.rand2(2) == 0) enemy = "E57"; + else enemy = "E69"; + } else if (near[i][3] == "海底神殿") { + enemy = "E45"; + } + } + } + } else if (flags.sleep >= 4320 && flags.timePeriod == "夜晚" && core.rand2(3) != 0) { + enemy = "E53"; + } else { + //确定背景块 + switch (core.getBgNumber(ex1, ey1)) { + case 921: + case 339: + case 340: + case 305: + enemy = refEnemy("草地"); + break; + case 341: + case 342: + enemy = refEnemy("雪林"); + break; + case 343: + case 344: + enemy = refEnemy("沙漠"); + break; + case 345: + case 348: + enemy = refEnemy("水下"); + break; + case 347: + enemy = refEnemy("蘑菇岛"); + break; + default: + enemy = refEnemy("其他"); + break; + } + } + + } + + //生成一只敌怪 + if (enemy == undefined) return; + core.setBlock(enemy, ex1, ey1); + var eid = (ex1 * 1000 + ey1).toString().padStart(6, '0') + core.status.floorId; + var hp1 = flags.enemyGuide[enemy][0][0]; + if (flags.worldlevel <= 3) { + hp1 *= 30; + } else if (flags.worldlevel <= 7) { + hp1 *= 50; + } else { + hp1 *= 100; + } + hp1 = Math.round(hp1); + var hpmax1 = hp1; + if ((enemy == "E33" || enemy == "E41") && flags.timePeriod == "夜晚") hp1 -= 1; + if (core.getBlockCls(ex1, ey1, core.status.floorId, true) == 'enemys' || + core.getBlockCls(ex1, ey1, core.status.floorId, true) == 'enemy48') { + flags.enemylist[eid] = { + id: enemy, + hp: hp1, + hpmax: hpmax1, + state: ["stand", "stand", "move", "chase", "attack", "observe", "flash", "bomb",],//flash闪现,bomb自爆 + move: [3, 0], + } + } + core.plugin.enemystate(eid, ex1, ey1, core.status.floorId); + }//敌怪当前生命为0就删除,找不到敌怪也删除 + this.delenemy = function (po, isget, x = parseInt(po.substr(0, 3)), y = parseInt(po.substr(3, 3)), floor = po.substr(6)) { + if (isget) { + var item = flags.enemyGuide[flags.enemylist[po].id]; + if (item[1] && item[1][2] > core.rand2(100)) { + var r1 = core.rand2(item[1][1][1] - item[1][1][0] + 1) + item[1][1][0]; + core.addItem(item[1][0], r1); + core.playSound("pop.ogg"); + } + if (item[2] && item[2][2] > core.rand2(100)) { + var r1 = core.rand2(item[2][1][1] - item[2][1][0] + 1) + item[2][1][0]; + core.addItem(item[2][0], r1); + core.playSound("pop.ogg"); + } + var cop = item[0][8]; + var exp = item[0][9]; + if (flags.worldlevel <= 3) { + cop *= 18; + exp *= 12; + } else if (flags.worldlevel <= 7) { + cop *= 22; + exp *= 15; + } else { + cop *= 25; + exp *= 18; + } + cop = Math.round(cop); + exp = Math.round(exp); + core.plugin.beibaocunchu(); + flags.coppernum += cop; + flags.level[1] += exp + flags.property.exp; + core.playSound("orb.ogg"); + core.plugin.jingyandengji(); + core.plugin.enemydiesound(flags.enemylist[po].id); + } + if (core.getBlockCls(x, y, floor, true) == 'enemys' || + core.getBlockCls(x, y, floor, true) == 'enemy48') { + core.removeBlock(x, y, floor); + } + delete flags.enemylist[po]; + }//敌怪状态 + this.enemystate = function (po, ex = parseInt(po.substr(0, 3)), ey = parseInt(po.substr(3, 3)), ef = po.substr(6)) { + if (!flags.enemylist[po]) return; + // 获取主角的位置 + var hx = core.getHeroLoc('x'); + var hy = core.getHeroLoc('y'); + var hf = core.status.floorId; + if (ef == hf && Math.abs(ex - hx) <= 15 && Math.abs(ey - hy) <= 15) { + function move(dir, ex1, ey1) { + if (document.visibilityState === 'visible' && + Math.abs(core.status.hero.loc.x - ex) <= 6 && + Math.abs(core.status.hero.loc.y - ey) <= 6) { + if (core.status.hero.loc.x != -1 && core.status.hero.loc.y != -1) core.moveBlock(ex, ey, [dir], 200, true); + core.removeBlock(ex, ey, ef); + } else { + core.removeBlock(ex, ey, ef); + setTimeout(function () { + if (flags.enemylist[po]) { + core.setBlock(flags.enemylist[po].id, ex1, ey1, ef); + } + }, 200); + } + + //core.deleteCanvas("xietiao" + po); + + var eid = (ex1 * 1000 + ey1).toString().padStart(6, '0') + ef; + flags.enemylist[eid] = core.clone(flags.enemylist[po]); + if (flags.rightstatus[0] == "敌怪" && flags.rightstatus[1] == po) { + flags.rightstatus[1] = eid; + } + delete flags.enemylist[po]; + + po = eid; + ex = ex1; + ey = ey1; + } + function move2(r = core.rand2(4) + 1) { + if (r == 1 && !core.getBlockCls(ex - 1, ey, ef) && + core.getMapNumber(ex - 1, ey) != 371 && core.getMapNumber(ex - 1, ey) != 383) { + move("left", ex - 1, ey); + } else if (r == 2 && !core.getBlockCls(ex + 1, ey, ef) && + core.getMapNumber(ex + 1, ey) != 371 && core.getMapNumber(ex + 1, ey) != 383) { + move("right", ex + 1, ey); + } else if (r == 3 && !core.getBlockCls(ex, ey - 1, ef) && + core.getMapNumber(ex, ey - 1) != 371 && core.getMapNumber(ex, ey - 1) != 383) { + move("up", ex, ey - 1); + } else if (r == 4 && !core.getBlockCls(ex, ey + 1, ef) && + core.getMapNumber(ex, ey + 1) != 371 && core.getMapNumber(ex, ey + 1) != 383) { + move("down", ex, ey + 1); + } + } + if (flags.enemylist[po].id == "E37" && core.rand2(16) == 0) { + //末影人闪现 + flags.enemylist[po].state[0] = flags.enemylist[po].state[6]; + } else if (core.nearHero(ex, ey, flags.enemyGuide[flags.enemylist[po].id][0][5]) && + (!flags.enemyGuide[flags.enemylist[po].id][0][10] || ( + flags.enemyGuide[flags.enemylist[po].id][0][10] && + flags.enemylist[po].hp < flags.enemylist[po].hpmax))) { + //攻击 + flags.enemylist[po].state[0] = flags.enemylist[po].state[4]; + if (flags.enemylist[po].id == "E25") { + flags.enemylist[po].state[0] = flags.enemylist[po].state[7]; + } + } else if (core.nearHero(ex, ey, flags.enemyGuide[flags.enemylist[po].id][0][6]) && + (!flags.enemyGuide[flags.enemylist[po].id][0][10] || ( + flags.enemyGuide[flags.enemylist[po].id][0][10] && + flags.enemylist[po].hp < flags.enemylist[po].hpmax))) { + //追击 + flags.enemylist[po].state[0] = flags.enemylist[po].state[3]; + } else if (flags.enemylist[po].move[0] <= 0) { + //移动 + flags.enemylist[po].state[0] = flags.enemylist[po].state[2]; + } else if (flags.enemylist[po].move[0] > 0) { + //站立 + flags.enemylist[po].state[0] = flags.enemylist[po].state[1]; + } + if (flags.enemylist[po].state[0] == flags.enemylist[po].state[7]) { + //flags.bombcenter.push([ef, ex, ey]); + core.playSound("E25_4.ogg"); + setTimeout(function () { + if (flags.enemylist[po]) { + if (core.rand2(2) == 0) core.playSound("E25_5.ogg"); + else core.playSound("E25_6.ogg"); + if (core.status.floorId == ef && + Math.abs(core.status.hero.loc.x - ex) <= 5 && + Math.abs(core.status.hero.loc.y - ey) <= 5) { + core.plugin.gethurt(po, ex, ey); + } + core.removeBlock(ex, ey, ef); + delete flags.enemylist[po]; + } + }, 2800); + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[6]) { + var fla = 0; + var ex1 = 0, ey1 = 0; + do { + ex1 = core.rand2(11) - 5; + ey1 = core.rand2(11) - 5; + fla += 1; + } while (fla < 10 && (ex + ex1 <= 0 || ex + ex1 >= 63 || ey + ey1 <= 0 || ey + ey1 >= 63 || core.getMapNumber(ex + ex1, ey + ey1))) + if (ex1 != 0 && ey1 != 0) { + ex1 += ex; + ey1 += ey; + core.removeBlock(ex, ey, ef); + core.setBlock(flags.enemylist[po].id, ex1, ey1, ef); + if (ef == core.status.floorId && + Math.abs(ex1 - core.status.hero.loc.x) <= 8 && + Math.abs(ey1 - core.status.hero.loc.y) <= 8) { + if (core.rand2(2) == 0) core.playSound("E37_9.ogg"); + else core.playSound("E37_10.ogg"); + } + var eid = (ex1 * 1000 + ey1).toString().padStart(6, '0') + ef; + flags.enemylist[eid] = core.clone(flags.enemylist[po]); + if (flags.rightstatus[0] == "敌怪" && flags.rightstatus[1] == po) { + flags.rightstatus[1] = eid; + } + delete flags.enemylist[po]; + po = eid; + ex = ex1; + ey = ey1; + } + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[5]) { + if (ex > hx && ey >= hy) { + move2(2); + } else if (ex >= hx && ey < hy) { + move2(3); + } else if (ex <= hx && ey > hy) { + move2(4); + } else if (ex < hx && ey <= hy) { + move2(1); + } + flags.enemylist[po].move[0] -= 1; + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[4]) { + if (document.visibilityState === 'visible') { + core.plugin.gethurt(po, ex, ey); + } + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[3]) { + // 使用 BFS(广度优先搜索)算法找到从敌怪到主角的路径 + var path = core.maps._automaticRoute_bfs(ex, ey, hx, hy, hf); + var ans = [], nowX = hx, nowY = hy; + while (nowX != ex || nowY != ey) { + var dir = path[nowX + "," + nowY]; + if (!dir) break;//敌怪无法直接走到主角处 + ans.push({ 'direction': dir, 'x': nowX, 'y': nowY }); + nowX -= core.utils.scan[dir].x; + nowY -= core.utils.scan[dir].y; + } + ans.reverse(dir); + + if (ans.length > 0 && ans.length < 20) { + var ex1, ey1; + switch (ans[0].direction) { + case "up": + ex1 = ex; + ey1 = ey - 1; + break; + case "down": + ex1 = ex; + ey1 = ey + 1; + break; + case "left": + ex1 = ex - 1; + ey1 = ey; + break; + case "right": + ex1 = ex + 1; + ey1 = ey; + break; + } + if (core.getMapNumber(ex1, ey1) != 371 && core.getMapNumber(ex1, ey1) != 383) { + move(ans[0].direction, ex1, ey1); + flags.enemylist[po].move[0] = 3; + } else { + move2(); + flags.enemylist[po].move[0] = 1; + } + } else { + move2(); + flags.enemylist[po].move[0] = 1; + } + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[2]) { + if (flags.enemylist[po].move[1] < 20) { + move2(); + flags.enemylist[po].move[0] = 3; + flags.enemylist[po].move[1] += 1; + } else { + core.plugin.delenemy(po, 0, ex, ey, ef); + return; + } + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[1]) { + flags.enemylist[po].move[0] -= 1; + } + //core.plugin.enemyHP(po, ex, ey); + } else { + if (flags.enemylist[po].move[1] < 20) { + flags.enemylist[po].move[1] += 1; + } else { + core.removeBlock(ex, ey, ef); + delete flags.enemylist[po]; + return; + } + } + var movespeed; + if (flags.enemylist[po].state[0] == flags.enemylist[po].state[7]) { + movespeed = 5000; + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[6]) { + movespeed = 1000; + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[5]) { + movespeed = 1000; + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[4]) { + movespeed = ((flags.enemyGuide[flags.enemylist[po].id][0][3] - 110) / -20 * 1000); + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[3]) { + movespeed = ((flags.enemyGuide[flags.enemylist[po].id][0][4] - 110) / -20 * 1000); + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[2]) { + movespeed = ((flags.enemyGuide[flags.enemylist[po].id][0][4] - 110) / -20 * 1000); + } else if (flags.enemylist[po].state[0] == flags.enemylist[po].state[1]) { + movespeed = ((flags.enemyGuide[flags.enemylist[po].id][0][4] - 110) / -20 * 1000); + movespeed = Math.max(1000, movespeed); + } else { + movespeed = 1000; + } + setTimeout(function () { + if (!flags.enemylist[po]) return; + if (core.rand2(20) == 0 && + ef == core.status.floorId && + Math.abs(ex - core.status.hero.loc.x) <= 10 && + Math.abs(ey - core.status.hero.loc.y) <= 10) { + core.plugin.enemymovesound(flags.enemylist[po].id); + } + core.plugin.enemystate(po, ex, ey, ef); + }, movespeed); + }//敌怪血条 + this.enemyHP = function (po, ex, ey) { + var name = "xietiao" + po; + if (core.nearHero(ex, ey, flags.enemyGuide[flags.enemylist[po].id][0][6])) { + var x = ex * 32 + 3 - core.bigmap.offsetX; + var y = ey * 32 - core.bigmap.offsetY; + if (core.getContextByName(name) == null) { + core.createCanvas(name, 0, 0, 416, 416, 100); + } else { + core.clearMap(name); + } + core.fillRect(name, x, y, 26, 3, "#2b2b2b"); + core.fillRect(name, x, y, Math.ceil((flags.enemylist[po].hp / flags.enemylist[po].hpmax) * 26), 3, "#f97524"); + } else { + core.deleteCanvas(name); + } + }//敌怪死亡音效 + this.enemydiesound = function (enemy) { + var sound = 0; + switch (enemy) { + case "E9": + case "E13": + case "E17": + sound = "E9_1"; + break; + case "E21": + sound = "E21_1"; + break; + case "E25": + case "E29": + sound = "E25_1"; + break; + case "E33": + sound = "E33_1"; + break; + case "E37": + sound = "E37_1"; + break; + case "E41": + sound = "E41_1"; + break; + case "E45": + sound = "E45_1"; + break; + case "E53": + sound = "E53_1"; + break; + case "E57": + sound = "E57_1"; + break; + case "E69": + sound = "E69_1"; + break; + } + if (sound != 0) core.playSound(sound + ".ogg"); + }//敌怪受伤音效 + this.enemyhitsound = function (enemy) { + var sound = 0; + switch (enemy) { + case "E9": + case "E13": + case "E17": + var r1 = core.rand2(2) + 2; + sound = "E9_" + r1; + break; + case "E21": + var r1 = core.rand2(4) + 2; + sound = "E21_" + r1; + break; + case "E25": + case "E29": + var r1 = core.rand2(2) + 2; + sound = "E25_" + r1; + break; + case "E37": + var r1 = core.rand2(2) + 2; + sound = "E37_" + r1; + break; + case "E41": + var r1 = core.rand2(3) + 2; + sound = "E41_" + r1; + break; + case "E53": + var r1 = core.rand2(3) + 2; + sound = "E53_" + r1; + break; + case "E57": + var r1 = core.rand2(3) + 2; + sound = "E57_" + r1; + break; + case "E69": + var r1 = core.rand2(2) + 2; + sound = "E69_" + r1; + break; + } + if (sound != 0) core.playSound(sound + ".ogg"); + }//敌怪移动音效 + this.enemymovesound = function (enemy) { + var sound = 0; + switch (enemy) { + case "E1": + case "E5": + var r1 = core.rand2(4) + 1; + sound = "E1_" + r1; + break; + case "E9": + case "E13": + case "E17": + var r1 = core.rand2(3) + 4; + sound = "E9_" + r1; + break; + case "E21": + case "E29": + sound = "E21_6"; + break; + case "E33": + var r1 = core.rand2(4) + 2; + sound = "E33_" + r1; + break; + case "E37": + var r1 = core.rand2(5) + 4; + sound = "E37_" + r1; + break; + case "E45": + var r1 = core.rand2(2) + 2; + sound = "E45_" + r1; + break; + case "E53": + var r1 = core.rand2(3) + 5; + sound = "E53_" + r1; + break; + case "E57": + var r1 = core.rand2(4) + 5; + sound = "E57_" + r1; + break; + case "E69": + var r1 = core.rand2(2) + 4; + sound = "E69_" + r1; + break; + } + if (sound != 0) core.playSound(sound + ".ogg"); + } + + }, + "24-跳字": function () { + this.tiaozi = function (neirong, yanse, x, y) { //跳字 + if (!core.status.replay.replaying) { + var shangsheng = 1; + var gaodu = 0; + var tiaozizhong = 0; + var name = "tiaozi" + Math.floor(Math.random() * 1000000); //如不需要连续跳字可不加随机数 + core.createCanvas(name, (x - 1) * 32 + 8 - core.bigmap.offsetX, (y - 1) * 32 + gaodu - core.bigmap.offsetY, 96, 64, 74); + core.fillText(name, neirong, 32, 32, yanse, "bold 17px Verdana"); + var fade = setInterval(function () { + if (gaodu >= -6 && shangsheng == 1) { + gaodu = gaodu - 1; + if (gaodu == -6) { shangsheng = 0 } + } else if (gaodu <= 12) { + gaodu = gaodu + 1; + if (gaodu == 12) { tiaozizhong = 1; } + } + core.relocateCanvas(name, (x - 1) * 32 + 8 - core.bigmap.offsetX, (y - 1) * 32 + gaodu - core.bigmap.offsetY); + if (tiaozizhong == 1) { + delete core.animateFrame.asyncId[fade]; + clearInterval(fade); + core.deleteCanvas(name); + } + }, 16); + core.animateFrame.asyncId[fade] = true; + } + } + }, + "25-植物生长": function () { + this.plantgrow = function (cr) { + if (!flags.crops[cr]) return; + if (flags.crops[cr][1] >= flags.crops[cr][2]) { + core.setBlock(flags.cropslist[flags.crops[cr][0]][1][1], parseInt(cr.substr(0, 3)), parseInt(cr.substr(3, 3)), cr.substr(6)); + delete flags.crops[cr]; + return; + } + setTimeout(function () { + if (!flags.crops[cr]) return; + flags.crops[cr][1] += core.rand2(21) + flags.extraGrow; + core.plugin.plantgrow(cr); + }, 10000); + } + + }, + "26-自动农田": function () { + this.zidongnongtian = function () { + //自动农田 + flags.farmUI = 1; + flags.interface = "锁定"; + core.drawImage("uievent", "nongtian.png", 0, 0, 416, 416); + if (flags.farmland2[flags.box_xy][1] < 10) { + core.ui._uievent_drawTextContent({ ctx: "uievent", text: (flags.farmland2[flags.box_xy][1] + "级" + "->" + (flags.farmland2[flags.box_xy][1] + 1) + "级"), left: 166, top: 160, maxWidth: 224, fontSize: 15, color: "black", align: "center" }); + } else { + core.ui._uievent_drawTextContent({ ctx: "uievent", text: ("10级"), left: 166, top: 160, maxWidth: 224, fontSize: 15, color: "black", align: "center" }); + } + + core.ui._uievent_drawTextContent({ ctx: "uievent", text: (flags.farmland1[flags.farmland2[flags.box_xy][0]][flags.farmland2[flags.box_xy][1] - 1 + 10]), left: 166, top: 200, maxWidth: 224, fontSize: 15, color: "black", align: "center" }); + core.ui._uievent_drawTextContent({ ctx: "uievent", text: (flags.farmland1[flags.farmland2[flags.box_xy][0]][flags.farmland2[flags.box_xy][1] - 1 + 20]), left: 166, top: 220, maxWidth: 224, fontSize: 15, color: "black", align: "center" }); + core.ui._uievent_drawTextContent({ ctx: "uievent", text: (flags.farmland1[flags.farmland2[flags.box_xy][0]][flags.farmland2[flags.box_xy][1] - 1 + 30]), left: 166, top: 240, maxWidth: 224, fontSize: 15, color: "black", align: "center" }); + core.fillText("uievent", "(铜币:" + flags.farmland1[flags.farmland2[flags.box_xy][0]][flags.farmland2[flags.box_xy][1] - 1] + ")", 230, 284, "black", ui.prototype._buildFont(15, false), 120); + core.fillText("uievent", flags.crops[flags.box_xy][1] + "/" + flags.crops[flags.box_xy][2], 110, 200, "black", ui.prototype._buildFont(15, false), 120); + core.fillText("uievent", "存储位1:" + flags.farmland2[flags.box_xy][2], 100, 240, "black", ui.prototype._buildFont(15, false), 120); + core.fillText("uievent", "存储位2:" + flags.farmland2[flags.box_xy][3], 100, 260, "black", ui.prototype._buildFont(15, false), 120); + + + // 120 292 48 24 224, 292, 48, 24 288, 292, 48, 24 + } + + }, + "27-伤害计算": function () { + this.makehurt = function (x, y, dir) { + if (flags.property.atkSpeed[1] == 1) { + flags.property.atkSpeed[1] = 0; + var sound1 = "attack1.ogg"; + for (var we in flags.weapon) { + if (core.getEquip(0) == we) { + switch (flags.weapon[we][4]) { + case 2: + sound1 = "attack2.ogg"; + break; + case 3: + sound1 = "attack3.ogg"; + break; + } + break; + } + } + core.playSound(sound1); + if (core.getEquip(0) != "I614") { + if (dir == 1) { + core.drawAnimate("atk_u", core.status.hero.loc.x, core.status.hero.loc.y); + } else if (dir == 2) { + core.drawAnimate("atk_r", core.status.hero.loc.x, core.status.hero.loc.y); + } else if (dir == 3) { + core.drawAnimate("atk_d", core.status.hero.loc.x, core.status.hero.loc.y); + } else if (dir == 4) { + core.drawAnimate("atk_l", core.status.hero.loc.x, core.status.hero.loc.y); + } + } + var id = core.getBlockInfo(core.getBlockNumber(x, y)).id; + var eid = (x * 1000 + y).toString().padStart(6, '0') + core.status.floorId; + var damage = 1; + var atk = core.status.hero.atk; + var atkSea = flags.property.atkSea; + var atkFly = flags.property.atkFly; + var igdef1 = flags.property.igdef1; + var igdef2 = flags.property.igdef2; + var crit = flags.property.crit; + if (flags.enemyGuide[id] == undefined) { + var n = 0; + do { + var E = id.slice(0, 1); + var en = Number(id.slice(1, 5)); + if (flags.enemyGuide[E + (en - 1)] != undefined) { + id = E + (en - 1); + break; + } + n += 1; + if (n >= 3) { + id = "E1"; + break; + } + } while (flags.enemyGuide[id] == undefined); + } + var Edef = flags.enemyGuide[id][0][2]; + if (flags.worldlevel <= 3) { + Edef *= 2; + } else if (flags.worldlevel <= 7) { + Edef *= 15; + } else { + Edef *= 25; + } + Edef = Math.round(Edef); + var Etype = core.enemys.enemys[id].type; + + if (igdef1 == 0 || igdef1 > 1) { + if (Etype == 0) { + damage = (atk - Edef) * crit + igdef1; + } else if (Etype == 1) { + damage = (atk + atkSea - Edef) * crit + igdef1; + } else if (Etype == 2) { + damage = (atk + atkFly - Edef) * crit + igdef1; + } + if (igdef2 != 0) { + for (var i = -1; i < 2; i++) { + for (var j = -1; j < 2; j++) { + if (core.getBlockCls(x + i, y + j) == "enemys" || + core.getBlockCls(x + i, y + j) == "enemy48") { + if (i != 0 || j != 0) { + //var id1 = core.getBlockInfo(core.getBlockNumber(x + i, y + j)).id; + var id1 = ((x + i) * 1000 + y + j).toString().padStart(6, '0') + core.status.floorId; + flags.enemylist[id1].hp -= igdef2; + + if (flags.enemylist[id1].hp <= 0) { + core.plugin.delenemy(id1, 1); + } + core.plugin.tiaozi(igdef2, "#ecf127", x + i, y + j); + } + } + } + } + } + } else { + damage = atk * crit; + } + damage = Math.max(1, damage); + if (core.getEquip(0) == "I614") { + if (core.itemCount("I615") > 0) { + core.addItem("I615", -1); + } else { + damage = 1; + } + } + flags.enemylist[eid].hp -= damage; + if (flags.enemylist[eid].hp <= 0) { + core.plugin.delenemy(eid, 1); + } else { + core.plugin.enemyhitsound(id); + } + core.plugin.atkSpeedBar(0); + if (crit > 1) core.plugin.tiaozi(damage, "#c1180b", x, y); + else core.plugin.tiaozi(damage, "#ecf127", x, y); + core.plugin.reduceDur("wea"); + core.plugin.reduceDur("tool"); + core.plugin.baoshidu(1); + flags.rightstatus = ["敌怪", eid]; + } + if (flags.property.atkSpeed[1] == 0 && flags.property.atkSpeed[2] == 0) { + flags.property.atkSpeed[2] = 1; + setTimeout(function () { + flags.property.atkSpeed[1] = 1; + flags.property.atkSpeed[2] = 0; + }, (flags.property.atkSpeed[0] - 110) / -20 * 1000); + } + + } + this.gethurt = function (po, ex, ey) { + var rand1 = core.rand2(3); + if (rand1 == 0) { + core.playSound("hit1.ogg"); + } else if (rand1 == 1) { + core.playSound("hit2.ogg"); + } else if (rand1 == 2) { + core.playSound("hit3.ogg"); + } + var undead = flags.property.undead; + if (undead == 1) return; + var id = flags.enemylist[po].id; + var Edamage = 1; + var ErealHurt = flags.enemyGuide[id][0][8]; + var Eatk = flags.enemyGuide[id][0][1]; + if (flags.worldlevel <= 3) { + Eatk *= 8; + } else if (flags.worldlevel <= 7) { + Eatk *= 24; + } else { + Eatk *= 40; + } + Eatk = Math.round(Eatk); + var Erange = flags.enemyGuide[id][0][5]; + var def = core.status.hero.def; + var reduceDa = flags.property.reduceDa; + var reduceBoom = flags.property.reduceBoom; + var reduceBul = flags.property.reduceBul; + var backDa = flags.property.backDa; + + if (ErealHurt == 1) { + Edamage = Eatk; + } else { + if (id == "E25" || id == "E29") { + Edamage = (Eatk - def) * (1 - reduceBoom - reduceDa); + } else if (Erange > 1) { + Edamage = (Eatk - def) * (1 - reduceBul - reduceDa); + } else { + Edamage = (Eatk - def) * (1 - reduceDa); + } + } + core.status.hero.hp -= Math.max(1, Edamage); + if (backDa != 0) { + flags.enemylist[po].hp -= backDa; + core.plugin.tiaozi(backDa, "#ecf127", ex, ey); + if (flags.enemylist[po].hp <= 0) { + core.plugin.delenemy(po, 1); + } + } + core.plugin.hpBar(2); + core.plugin.reduceDur("equ", Math.max(1, Edamage)); + core.plugin.siwangjiance(); + } + + }, + "28-角色状态": function () { + //绘制血条 + this.hpBar = function (state = 0, time = 0) { + //state状态:1为治疗,2为受伤,3为刷新 + var ctx = "hpbar"; + var heartpic = []; + var goldheartpic = ["goldheart.png", "goldheart3.png", "goldheart2.png", "goldheartw.png", "goldheartw3.png", "goldheartw2.png"]; + if (flags.buff["凋零"] > 0) { + heartpic = ["blackheart.png", "blackheart3.png", "blackheart2.png", "blackheartw.png", "blackheartw3.png", "blackheartw2.png"]; + } else if (flags.buff["中毒"] > 0) { + heartpic = ["greenheart.png", "greenheart3.png", "greenheart2.png", "greenheartw.png", "greenheartw3.png", "greenheartw2.png"]; + } else { + heartpic = ["heart.png", "heart3.png", "heart2.png", "heartw.png", "heartw3.png", "heartw2.png"]; + } + if (core.getContextByName(ctx) == null) { + for (var i = 1; i < 21; i++) { + core.createCanvas(ctx + i, 64 + ((i - 1) * 13) - (130 * Math.floor((i - 1) / 10)), 361 - (14 * Math.floor((i - 1) / 10)), 14, 14, 101); + } + } + function showheart(whole, half, empty, whole1, half1, empty1,) { + for (var i = 1; i < 21; i++) { + core.clearMap(ctx + i); + } + var hp = core.status.hero.hp; + var hpmax = core.status.hero.hpmax; + for (var i = 1; i < 21; i++) { + var pic = empty; + if (hp >= 2 && hpmax >= 2) { + pic = whole; + hp -= 2; + hpmax -= 2; + } else if (hp >= 1 && hpmax >= 2) { + pic = half; + hp -= 1; + hpmax -= 2; + } else if (hp == 0 && hpmax >= 2) { + pic = empty; + hpmax -= 2; + } else { + pic = ""; + } + core.drawImage(ctx + i, pic, 0, 0); + } + if (hp > 0) { + for (var i = 1; i < 21; i++) { + var pic = empty1; + if (hp >= 2 && hpmax >= 2) { + pic = whole1; + hp -= 2; + hpmax -= 2; + } else if (hp >= 1 && hpmax >= 2) { + pic = half1; + hp -= 1; + hpmax -= 2; + } else if (hp == 0 && hpmax >= 2) { + pic = ""; + hpmax -= 2; + } else { + pic = ""; + } + core.drawImage(ctx + i, pic, 0, 0); + } + } + } + function showlight(i) { + if (i <= 0) return; + setTimeout(function () { + showheart(heartpic[3], heartpic[4], heartpic[5], goldheartpic[3], goldheartpic[4], goldheartpic[5]); + setTimeout(function () { + showheart(heartpic[0], heartpic[1], heartpic[2], goldheartpic[0], goldheartpic[1], goldheartpic[2]); + showlight(i - 1); + }, 100); + }, 100); + } + function jumpheart1(num, a, b, rate) { + var end = 0; + var gaodu = 0; + var up = 1; + var jump = setInterval(function () { + if (gaodu >= a && up == 1) { + gaodu = gaodu - 1; + if (gaodu == a) up = 0; + } else if (gaodu <= b) { + gaodu = gaodu + 1; + if (gaodu == b) end = 1; + } + core.relocateCanvas(ctx + num, 64 + ((num - 1) * 13) - (130 * Math.floor((num - 1) / 10)), 361 - (14 * Math.floor((num - 1) / 10)) + gaodu); + if (end == 1) { + clearInterval(jump); + } + }, rate); + } + function jumpheart2(i) { + if (i > 20) { + setTimeout(function () { + time -= 1000; + if (time > 0) jumpheart2(1); + else core.plugin.buffma("生命恢复", 0); + }, 1000); + return; + } + setTimeout(function () { + jumpheart1(i, -6, 0, 3); + jumpheart2(i + 1); + }, 40); + } + function jumpheart3(arr = []) { + if (core.status.hero.hp / core.status.hero.hpmax > 0.2 || flags.buff["生命恢复"] > 0) { + flags.dying = 0; + return; + } + setTimeout(function () { + if (arr.length == 0) arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + var i = core.rand2(arr.length); + jumpheart1(arr[i], -3, 1, 12); + arr.splice(i, 1); + jumpheart3(arr); + }, 100); + } + showheart(heartpic[0], heartpic[1], heartpic[2], goldheartpic[0], goldheartpic[1], goldheartpic[2]); + if (flags.buff["生命恢复"] > 0 && time != 0) { + jumpheart2(1); + } else if (state != 3 && core.status.hero.hp / core.status.hero.hpmax <= 0.2 && flags.dying == 0) { + flags.dying = 1; + jumpheart3(); + } + if (state == 1 && core.rand2(3) == 0) { + showlight(core.rand2(3) + 2); + } else if (state == 2) { + showlight(1); + } + }//绘制经验条 + this.expBar = function () { + var ctx = "expbar"; + if (core.getContextByName(ctx) == null) { + core.createCanvas(ctx, 0, 0, 416, 416, 101); + } else { + core.clearMap(ctx); + } + core.setAlpha(ctx, 0.6); + core.fillRoundRect(ctx, 64, 375, 288, 5, 3, "#2b2b2b"); + core.fillRoundRect(ctx, 64, 375, Math.max(4, ((flags.level[1] / flags.level[2]) * 288)), 5, 3, "#0bef15"); + core.drawTextContent( + ctx, + flags.level[0].toString(), + { left: 158, top: 358, color: "white", fontSize: 15, maxWidth: 100, align: "center" } + ); + }//绘制饥饿条 + this.hungerBar = function () { + var ctx = "hungerbar"; + var hungerpic = ["hunger.png", "hunger3.png", "hunger2.png", "hungerw.png", "hungerw3.png", "hungerw2.png"]; + if (core.getContextByName(ctx) == null) { + for (var i = 11; i > 1; i--) { + core.createCanvas(ctx + (12 - i), 210 + ((i - 1) * 13), 361, 14, 14, 101); + } + } else { + for (var i = 1; i < 11; i++) { + core.clearMap(ctx + i); + } + } + function showhunger(n, whole, half, empty) { + var hunger; + if (flags.hunger[0] > 100 && n == 1) { + hunger = 100; + } + else { + hunger = flags.hunger[0] % 100; + if (hunger == 0) hunger = 100; + } + for (var i = 1; i < 11; i++) { + var pic = empty; + if (hunger <= 0) { + pic = empty; + } + else if (hunger > 0 && hunger <= 5) { + pic = half; + hunger -= 5; + } else if (hunger > 5) { + pic = whole; + hunger -= 10; + } + core.drawImage(ctx + i, pic, 0, 0); + } + } + showhunger(1, hungerpic[0], hungerpic[1], hungerpic[2]); + if (flags.hunger[0] / 100 > 1) showhunger(2, hungerpic[3], hungerpic[4], hungerpic[5]); + }//显示角色攻击范围 + this.atkRange = function () { + if (flags.playerAr == 1 && !core.isMoving()) { + if (core.getContextByName("fw1") == null) { + core.createCanvas("fw1", 0, 0, 416, 416, 100); + } else { + core.clearMap('fw1'); + } + if (core.status.hero.loc.x < 6 || + core.status.hero.loc.y < 6 || + core.status.hero.loc.x > core.bigmap.width - 6 || + core.status.hero.loc.y > core.bigmap.width - 6) { + core.strokeRect("fw1", + (core.status.hero.loc.x - flags.property.touchRange[0]) * 32 - Math.round(core.bigmap.offsetX / 32) * 32, + (core.status.hero.loc.y - flags.property.touchRange[0]) * 32 - Math.round(core.bigmap.offsetY / 32) * 32, + (flags.property.touchRange[0] * 2 + 1) * 32, + (flags.property.touchRange[0] * 2 + 1) * 32, "yellow", 1); + } else { + core.strokeRect("fw1", + 32 * (6 - flags.property.touchRange[0]), + 32 * (6 - flags.property.touchRange[0]), + (flags.property.touchRange[0] * 2 + 1) * 32, + (flags.property.touchRange[0] * 2 + 1) * 32, "yellow", 1); + } + } else { + core.deleteCanvas("fw1"); + } + }//显示角色攻速条 + this.atkSpeedBar = function (bar) { + if (flags.property.atkSpeed[1] == 0) { + if (core.getContextByName("gst1") == null) { + core.createCanvas("gst1", 56, 384, 4, 30, 100); + } else { + core.clearMap('gst1'); + } + core.fillRect("gst1", 0, 0, 4, 30, "#fffe61"); + core.fillRect("gst1", 0, 0, 4, 30 - ((bar / ((flags.property.atkSpeed[0] - 110) / -20 * 1000)) * 30), "#2b2b2b"); + } else { + core.deleteCanvas("gst1"); + return; + } + setTimeout(function () { + bar += 100; + if (bar < ((flags.property.atkSpeed[0] - 110) / -20 * 1000)) { + core.plugin.atkSpeedBar(bar); + } else { + core.clearMap('gst1'); + } + }, 100); + }//绘制呼吸值 + this.breBar = function () { + if (flags.breathe[0] <= 0 || flags.breathe[0] == flags.breathe[1]) { + var ctx = "brebar"; + for (var i = 1; i < 11; i++) { + core.clearMap(ctx + i); + } + return; + } + var ctx = "brebar"; + var pic = "breathe.png"; + if (core.getContextByName(ctx) == null) { + for (var i = 11; i > 1; i--) { + core.createCanvas(ctx + (12 - i), 210 + ((i - 1) * 13), 361 - 14, 14, 14, 101); + } + } else { + for (var i = 1; i < 11; i++) { + core.clearMap(ctx + i); + } + } + var bre = flags.breathe[0]; + for (var i = 1; i < 11; i++) { + if (bre > 0) { + bre -= 1; + core.drawImage(ctx + i, pic, 0, 0); + } + } + }//脚步声 + this.step = function () { + if (flags.step == 2) { + flags.step = 0; + } else { + flags.step += 1; + return; + } + var { x, y } = core.status.hero.loc; + var number1 = core.getBgNumber(x, y); + if (!number1) return; + var type = core.getBlockInfo(number1).blocktype; + if (type == 1 || type == 6) { + if (number1 == 910) { + var sound = "lava.ogg"; + core.playSound(sound); + } else if (number1 == 345 || number1 == 348) { + var rand1 = core.rand2(4); + var sound = "step_swim1.ogg"; + switch (rand1) { + case 0: + sound = "step_swim1.ogg"; + break; + case 1: + sound = "step_swim2.ogg"; + break; + case 2: + sound = "step_swim3.ogg"; + break; + case 3: + sound = "step_swim4.ogg"; + break; + } + core.playSound(sound); + } else { + var rand1 = core.rand2(6); + var sound = "step_stone1.ogg"; + switch (rand1) { + case 0: + sound = "step_stone1.ogg"; + break; + case 1: + sound = "step_stone2.ogg"; + break; + case 2: + sound = "step_stone3.ogg"; + break; + case 3: + sound = "step_stone4.ogg"; + break; + case 4: + sound = "step_stone5.ogg"; + break; + case 5: + sound = "step_stone6.ogg"; + break; + } + core.playSound(sound); + } + + } else if (type == 2) { + var rand1 = core.rand2(6); + var sound = "step_wood1.ogg"; + switch (rand1) { + case 0: + sound = "step_wood1.ogg"; + break; + case 1: + sound = "step_wood2.ogg"; + break; + case 2: + sound = "step_wood3.ogg"; + break; + case 3: + sound = "step_wood4.ogg"; + break; + case 4: + sound = "step_wood5.ogg"; + break; + case 5: + sound = "step_wood6.ogg"; + break; + } + core.playSound(sound); + } else if (type == 3) { + if (number1 == 349) { //沙砾 + var rand1 = core.rand2(4); + var sound = "step_gravel1.ogg"; + switch (rand1) { + case 0: + sound = "step_gravel1.ogg"; + break; + case 1: + sound = "step_gravel2.ogg"; + break; + case 2: + sound = "step_gravel3.ogg"; + break; + case 3: + sound = "step_gravel4.ogg"; + break; + } + core.playSound(sound); + } else if (number1 == 342) { //雪 + var rand1 = core.rand2(4); + var sound = ""; + switch (rand1) { + case 0: + sound = "step_snow1.ogg"; + break; + case 1: + sound = "step_snow2.ogg"; + break; + case 2: + sound = "step_snow3.ogg"; + break; + case 3: + sound = "step_snow4.ogg"; + break; + } + core.playSound(sound); + } else if (number1 == 343 || number1 == 344) { //沙子 + var rand1 = core.rand2(5); + var sound = ""; + switch (rand1) { + case 0: + sound = "step_sand1.ogg"; + break; + case 1: + sound = "step_sand2.ogg"; + break; + case 2: + sound = "step_sand3.ogg"; + break; + case 3: + sound = "step_sand4.ogg"; + break; + case 4: + sound = "step_sand5.ogg"; + break; + } + core.playSound(sound); + } else { //草地和其他 + var rand1 = core.rand2(6); + var sound = "step_grass1.ogg"; + switch (rand1) { + case 0: + sound = "step_grass1.ogg"; + break; + case 1: + sound = "step_grass2.ogg"; + break; + case 2: + sound = "step_grass3.ogg"; + break; + case 3: + sound = "step_grass4.ogg"; + break; + case 4: + sound = "step_grass5.ogg"; + break; + case 5: + sound = "step_grass6.ogg"; + break; + } + core.playSound(sound); + } + } + } + + }, + "29-buff管理": function () { + this.buffma = function (buff, time) { + flags.buff[buff] = time; + if (time <= 0) return; + switch (buff) { + case "生命恢复": + core.plugin.hpBar(0, time); + break; + case "中毒": + core.plugin.hpBar(0, time); + setTimeout(function () { + flags.buff[buff] = 0; + core.plugin.hpBar(0, time); + }, time); + break; + case "凋零": + core.plugin.hpBar(0, time); + setTimeout(function () { + flags.buff[buff] = 0; + core.plugin.hpBar(0, time); + }, time); + break; + } + } + + }, + "30-右侧状态栏扩展": function () { + // 使用了古祠佬的sprite化插件(有微改) 拓展栏内容请下拉 看注释操作 + // 已经用了sprite插件可以只把拓展栏部分放到sprite插件下面 + + // 基于canvas的sprite化,摘编整理自万宁魔塔 + // + // ---------------------------------------- 第一部分 js代码 --------------------------------------- // + /* ---------------- 用法说明 ---------------- * + * 1. 创建sprite: var sprite = new Sprite(x, y, w, h, z, reference, name); + * 其中x y w h为画布的横纵坐标及长宽,reference为参考系,只能填game(相对于游戏画面)和window(相对于窗口) + * 且当为相对游戏画面时,长宽与坐标将会乘以放缩比例(相当于用createCanvas创建) + * z为纵深,表示不同元素之间的覆盖关系,大的覆盖小的 + * name为自定义名称,可以不填 + * 2. 删除: sprite.destroy(); + * 3. 设置css特效: sprite.setCss(css); + * 其中css直接填 box-shadow: 0px 0px 10px black;的形式即可,与style标签与css文件内写法相同 + * 对于已设置的特效,如果之后不需要再次设置,可以不填 + * 4. 添加事件监听器: sprite.addEventListener(); 用法与html元素的addEventListener完全一致 + * 5. 移除事件监听器: sprite.removeEventListener(); 用法与html元素的removeEventListener完全一致 + * 6. 属性列表 + * (1) sprite.x | sprite.y | sprite.width | sprite.height | sprite.zIndex | sprite.reference 顾名思义 + * (2) sprite.canvas 该sprite的画布 + * (3) sprite.context 该画布的CanvasRenderingContext2d对象,即样板中常见的ctx + * (4) sprite.count 不要改这个玩意 + * 7. 使用样板api进行绘制 + * 示例: + * var ctx = sprite.context; + * core.fillText(ctx, 'xxx', 100, 100); + * core.fillRect(ctx, 0, 0, 50, 50); + * 当然也可以使用原生js + * ctx.moveTo(0, 0); + * ctx.bezierCurveTo(50, 50, 100, 0, 100, 50); + * ctx.stroke(); + * ---------------- 用法说明 ---------------- */ + + var count = 0; + /** 创建一个sprite画布 + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @param {number} z + * @param {'game' | 'window'} reference 参考系,游戏画面或者窗口 + * @param {string} name 可选,sprite的名称,方便通过core.dymCanvas获取 + */ + function Sprite(x, y, w, h, z, reference, name) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.zIndex = z; + this.reference = reference; + this.canvas = null; + this.context = null; + this.count = 0; + this.name = name; + /** 初始化 */ + this.init = function () { + + if (reference === 'window') { + var canvas = document.createElement('canvas'); + this.canvas = canvas; + this.context = canvas.getContext('2d'); + canvas.width = devicePixelRatio * w * core.domStyle.scale; + // canvas.width = w; + canvas.height = devicePixelRatio * h * core.domStyle.scale; + // canvas.height = h; + //canvas.style.width = w * core.domStyle.scale + 'px'; + canvas.style.width = w * core.domStyle.scale + 'px'; + //canvas.style.height = h * core.domStyle.scale + 'px'; + canvas.style.height = h * core.domStyle.scale + 'px'; + canvas.style.position = 'absolute'; + canvas.style.top = y + 'px'; + canvas.style.left = x + 'px'; + canvas.style.zIndex = z.toString(); + document.body.appendChild(canvas); + } else { + this.context = core.createCanvas(this.name || '_sprite_' + count, x, y, w, h, z); + this.canvas = this.context.canvas; + this.count = count; + this.canvas.style.pointerEvents = 'auto'; + count++; + } + } + this.init(); + + /** 设置css特效 + * @param {string} css + */ + this.setCss = function (css) { + css = css.replace('\n', ';').replace(';;', ';'); + var effects = css.split(';'); + var self = this; + effects.forEach(function (v) { + var content = v.split(':'); + var name = content[0]; + var value = content[1]; + name = name.trim().split('-').reduce(function (pre, curr, i, a) { + if (i === 0 && curr !== '') return curr; + if (a[0] === '' && i === 1) return curr; + return pre + curr.toUpperCase()[0] + curr.slice(1); + }, ''); + var canvas = self.canvas; + if (name in canvas.style) canvas.style[name] = value; + }); + return this; + } + + /** + * 移动sprite + * @param {boolean} isDelta 是否是相对位置,如果是,那么sprite会相对于原先的位置进行移动 + */ + this.move = function (x, y, isDelta) { + if (x !== undefined && x !== null) this.x = x; + if (y !== undefined && y !== null) this.y = y; + if (this.reference === 'window') { + var ele = this.canvas; + ele.style.left = x + (isDelta ? parseFloat(ele.style.left) : 0) + 'px'; + ele.style.top = y + (isDelta ? parseFloat(ele.style.top) : 0) + 'px'; + } else core.relocateCanvas(this.context, x, y, isDelta); + return this; + } + + /** + * 重新设置sprite的大小 + * @param {boolean} styleOnly 是否只修改css效果,如果是,那么将会不高清,如果不是,那么会清空画布 + */ + this.resize = function (w, h, styleOnly) { + if (w !== undefined && w !== null) this.w = w; + if (h !== undefined && h !== null) this.h = h; + if (reference === 'window') { + var ele = this.canvas; + ele.style.width = w + 'px'; + ele.style.height = h + 'px'; + if (!styleOnly) { + ele.width = w; + ele.height = h; + } + } else core.resizeCanvas(this.context, x, y, styleOnly); + return this; + } + + /** 删除 */ + this.destroy = function () { + if (this.reference === 'window') { + if (this.canvas) document.body.removeChild(this.canvas); + } else { + core.deleteCanvas(this.name || '_sprite_' + this.count); + } + } + + /** 添加事件监听器 */ + this.addEventListener = function () { + this.canvas.addEventListener.apply(this.canvas, arguments); + } + + /** 移除事件监听器 */ + this.removeEventListener = function () { + this.canvas.removeEventListener.apply(this.canvas, arguments); + } + } + window.Sprite = Sprite; + + /* -----拓展栏说明----- * + * 在玩家有可能拉伸或放缩的情况下,拓展栏大小与游戏界面大小(指不包括拓展栏,包括状态栏工具栏和地图)按某个比例相关 + * 这个比例 = 拓展栏的宽或高 : 游戏界面的宽或高 + * 横屏下默认宽度比为0.237(相当于与状态栏等宽 约151px?) 高度等于1(即与游戏界面等高) + * 竖屏的话默认宽度比为1 高为0.055(缩放比例1的设置下约等于36px,意为比一格略大) + * 过宽或过高玩家有可能遇到显示不全的问题 + * 游戏过程中需要动态变化请使用extend.resetScale(type, w, h) + * type=1代表横屏 =2代表竖屏 如extend.resetScale(2, 1, 0.11)将拓展栏高度在竖屏下变为默认值的两倍 + * 如果更改完这里的值读档拓展栏大小不变 请手动resetScale一下 + * -----拓展栏说明----- */ + + var scale = { // 需要改的有三个地方 这里的scale影响拓展栏大小 + hpos: { // 横屏 + width: 0.237, + height: 1 + }, + vpos: { // 竖屏 + width: 1, + height: 0.165 + } + } + function makedamage(po) { + var id = flags.enemylist[po].id; + var damage = 1; + var atk = core.status.hero.atk; + var atkSea = flags.property.atkSea; + var atkFly = flags.property.atkFly; + var igdef1 = flags.property.igdef1; + var crit = flags.property.crit; + var Edef = flags.enemyGuide[id][0][2]; + if (flags.worldlevel <= 3) { + Edef *= 2; + } else if (flags.worldlevel <= 7) { + Edef *= 15; + } else { + Edef *= 25; + } + Edef = Math.round(Edef); + var Etype = core.enemys.enemys[id].type; + + if (igdef1 == 0 || igdef1 > 1) { + if (Etype == 0) { + damage = (atk - Edef) * crit + igdef1; + } else if (Etype == 1) { + damage = (atk + atkSea - Edef) * crit + igdef1; + } else if (Etype == 2) { + damage = (atk + atkFly - Edef) * crit + igdef1; + } + } else { + damage = atk * crit; + } + if (core.getEquip(0) == "I614" && core.itemCount("I615") <= 0) { + damage = 1; + } + return Math.ceil(flags.enemylist[po].hp / Math.max(1, damage)); + } + function getdamage(po) { + var id = flags.enemylist[po].id; + var Edamage = 1; + var ErealHurt = flags.enemyGuide[id][0][8]; + var Eatk = flags.enemyGuide[id][0][1]; + if (flags.worldlevel <= 3) { + Eatk *= 8; + } else if (flags.worldlevel <= 7) { + Eatk *= 24; + } else { + Eatk *= 40; + } + Eatk = Math.round(Eatk); + var Erange = flags.enemyGuide[id][0][5]; + var def = core.status.hero.def; + var reduceDa = flags.property.reduceDa; + var reduceBoom = flags.property.reduceBoom; + var reduceBul = flags.property.reduceBul; + + if (ErealHurt == 1) { + Edamage = Eatk; + } else { + if (id == "E440" || id == "E441") { + Edamage = (Eatk - def) * (1 - reduceBoom - reduceDa); + } else if (Erange > 1) { + Edamage = (Eatk - def) * (1 - reduceBul - reduceDa); + } else { + Edamage = (Eatk - def) * (1 - reduceDa); + } + } + return Math.ceil(core.status.hero.hpmax / Math.max(1, Edamage)); + } + function update() { + if (extend.values.hidden) return; + var ctx = extend.context, + canvas = extend.canvas; + var width = canvas.width, + height = canvas.height; + ctx.clearRect(0, 0, width, height); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(devicePixelRatio * core.domStyle.scale, devicePixelRatio * core.domStyle.scale); + + if (!canvas.style.background) canvas.style.background = 'url("project/materials/wood1.png") repeat'; + // 背景设置 + //if (flags.dimension == undefined) flags.dimension = ["主世界", "地表"]; + // if (flags.dimension[0] == "主世界" && flags.dimension[1] == "地表") { + // canvas.style.background = 'url("project/materials/wood1.png") repeat'; + // } else if (flags.dimension[0] == "主世界" && flags.dimension[1] == "洞穴") { + // canvas.style.background = 'url("project/materials/stone1.png") repeat'; + // } + //extend.setBorder(); // 注释这句可以删掉边框 + var ctx, fill = function (text, x, y, style, strokeStyle, font, maxWidth) { + core.ui.setFont(ctx, (/\w+/.test(text) ? 'italic ' : '') + 'bold 12px Verdana'); + core.ui.fillBoldText(ctx, text, x, y, style, strokeStyle, font, maxWidth); + }; + if (!core.domStyle.isVertical) { // 横屏 + //图块信息展示 + core.setAlpha(ctx, 0.6); + core.fillRoundRect(ctx, 4, 4, 122, 70, 3, "#6198ff"); + core.setAlpha(ctx, 1); + if (!flags.rightstatus) flags.rightstatus = ["", ""]; + if (flags.rightstatus[0] == "图块") { + //图块信息 + core.drawIcon(ctx, flags.rightstatus[1], 8, 10, 20, 20); + //图块名称 + var inf = core.getBlockInfo(flags.rightstatus[1]); + fill(inf.name, 32, 24, "white", "", ui.prototype._buildFont(12, false), 100); + //挖掘信息 + var lx = inf.blocktype; + var yd = inf.hardness; + var num = 0; + var dig1 = 0; + for (var to in flags.tool) { + if (core.getEquip(0) == to) { + if (lx == 1 || lx == 2 || lx == 3) { + if (lx == flags.tool[to][2]) { + num = flags.digLevel[lx][flags.tool[to][1]][yd - 1][0]; + dig1 = 1; + } + } else if (lx == 4 || lx == 5 || lx == 6) { + num = flags.digLevel[lx][0]; + dig1 = 1; + } + break; + } + } + if (dig1 == 0) { + if (lx == 1 || lx == 2 || lx == 3) { + num = flags.digLevel[lx][1][yd - 1][0]; + } else if (lx == 4 || lx == 5 || lx == 6) { + num = flags.digLevel[lx][0]; + } + } + function dig() { + if (!flags.wfk[flags.rightstatus[1]]) return; + fill("挖掘掉落:", 12, 64, "white", "", ui.prototype._buildFont(10, false), 100); + var n1 = 0; + for (var i = 0; i < flags.wfk[flags.rightstatus[1]].length; i++) { + if (i == 0 && flags.wfk[flags.rightstatus[1]][i][2] != 0) { + core.drawIcon(ctx, flags.wfk[flags.rightstatus[1]][i][0], 60 + (n1 * 22), 50, 20, 20); + n1 += 1; + } else if ((i == 1 && flags.wfk[flags.rightstatus[1]][0][0] != flags.wfk[flags.rightstatus[1]][1][0]) || + (i == 2 && flags.wfk[flags.rightstatus[1]][0][0] != flags.wfk[flags.rightstatus[1]][2][0] + && flags.wfk[flags.rightstatus[1]][1][0] != flags.wfk[flags.rightstatus[1]][2][0] + )) { + if (flags.wfk[flags.rightstatus[1]][i][2] != 0) { + core.drawIcon(ctx, flags.wfk[flags.rightstatus[1]][i][0], 60 + (n1 * 22), 50, 20, 20); + n1 += 1; + } + } + } + } + if (num > 0) { + if (flags.rightstatus[2] != undefined) { + fill("生长时间:" + flags.crops[flags.rightstatus[2]][1] + "/" + flags.crops[flags.rightstatus[2]][2], + 12, 44, "white", "", ui.prototype._buildFont(10, false), 200); + } else { + fill("挖掘次数:", 12, 44, "white", "", ui.prototype._buildFont(10, false), 100); + fill(num, 64, 44, "#ffb599", "", ui.prototype._buildFont(20, false), 100); + } + dig(); + } else { + var n = 0; + dig: for (var i = 2; i < 6; i++) { + if (lx == 1 || lx == 2 || lx == 3) { + if (flags.digLevel[lx][i][yd - 1][0] > 0) { + for (var to in flags.tool) { + if (flags.tool[to][1] == i && flags.tool[to][2] == lx) { + fill("需要:", 12, 44, "white", "", ui.prototype._buildFont(10, false), 100); + core.drawIcon(ctx, to, 40, 34, 14, 14); + fill(core.getBlockInfo(to).name, 56, 44, "white", "", ui.prototype._buildFont(10, false), 100); + dig(); + n = 1; + break dig; + } + } + } + } else if (lx == 6) { + break dig; + } + } + if (n == 0) fill("不可挖掘", 12, 44, "white", "", ui.prototype._buildFont(10, false), 100); + } + } else if (flags.rightstatus[0] == "敌怪" && flags.enemylist[flags.rightstatus[1]]) { + //怪物信息 + var po = flags.rightstatus[1]; + var inf = core.getBlockInfo(flags.enemylist[po].id); + core.drawIcon(ctx, flags.enemylist[po].id, 8, 0, 20, 30); + fill(inf.name, 32, 24, "white", "", ui.prototype._buildFont(12, false), 60); + + //危险程度 + if (flags.enemyGuide[flags.enemylist[po].id][0][10] && + flags.enemylist[po].hp == flags.enemylist[po].hpmax) { + fill("中立", 100, 24, "white", "", ui.prototype._buildFont(12, false), 40); + } else if ((!flags.enemyGuide[flags.enemylist[po].id][0][10]) || + (flags.enemyGuide[flags.enemylist[po].id][0][10] && + flags.enemylist[po].hp < flags.enemylist[po].hpmax)) { + if (getdamage(po) < 5) { + fill("致命", 100, 24, "#ff5647", "", ui.prototype._buildFont(12, false), 40); + } else if (getdamage(po) < 10) { + fill("危险", 100, 24, "#ffcb47", "", ui.prototype._buildFont(12, false), 40); + } else if (getdamage(po) < 20) { + fill("轻微", 100, 24, "#ffcb47", "", ui.prototype._buildFont(12, false), 40); + } else { + fill("无害", 100, 24, "#f3e03f", "", ui.prototype._buildFont(12, false), 40); + } + } + //击杀所需次数 + fill("击杀:", 12, 44, "white", "", ui.prototype._buildFont(10, false), 100); + fill(makedamage(po), 42, 44, "#ffb599", "", ui.prototype._buildFont(20, false), 100); + core.drawIcon(ctx, "I571", 70, 34, 14, 14); + //掉落 + fill("击杀掉落:", 12, 64, "white", "", ui.prototype._buildFont(10, false), 100); + var item1 = flags.enemyGuide[flags.enemylist[po].id][1]; + var item2 = flags.enemyGuide[flags.enemylist[po].id][2]; + if (item1) core.drawIcon(ctx, item1[0], 60, 50, 20, 20); + if (item2) core.drawIcon(ctx, item2[0], 82, 50, 20, 20); + } + //维度、层 + core.setAlpha(ctx, 0.6); + core.fillRoundRect(ctx, 2, 80, 127, 240, 3, "#dbdbdb"); + core.setAlpha(ctx, 1); + core.drawIcon(ctx, 331, 8, 86, 20, 20); + var text, color1; + if (core.status.event.id == 'viewMaps') { + text = core.status.maps[core.status.event.data.floorId || "MT0"]["title"]; + color1 = "yellow"; + } else { + text = core.status.maps[core.status.floorId || "MT0"]["title"]; + color1 = "white"; + } + fill(text, 32, 100, color1, "black", ui.prototype._buildFont(12, false), 90); + + //坐标 + core.drawIcon(ctx, 306, 8, 108, 20, 20); + if (core.status.event.id == 'viewMaps') { + fill("正在预览...", 32, 122, "yellow", "", ui.prototype._buildFont(12, false), 100); + } else { + fill("( " + " , " + " )", 32, 122, "", "", ui.prototype._buildFont(12, false), 100); + if (core.status.hero.loc.x == Math.floor(core.bigmap.width / 2)) { + fill(core.status.hero.loc.x, 40, 122, "yellow", "", ui.prototype._buildFont(12, false), 100); + } else { + fill(core.status.hero.loc.x, 40, 122, "white", "", ui.prototype._buildFont(12, false), 100); + } + if (core.status.hero.loc.y == Math.floor(core.bigmap.height / 2)) { + fill(core.status.hero.loc.y, 64, 122, "yellow", "", ui.prototype._buildFont(12, false), 100); + } else { + fill(core.status.hero.loc.y, 64, 122, "white", "", ui.prototype._buildFont(12, false), 100); + } + } + + for (var i = 0; i < 6; i++) { + core.drawLine(ctx, 6, 136 + (i * 24), 126, 136 + (i * 24)); + core.drawLine(ctx, 6 + (i * 24), 136, 6 + (i * 24), 256); + } + //小地图 + function floornum(id) { + var floor; + switch (id) { + case "zhu1": + floor = 1; + break; + case "zhu2": + floor = 2; + break; + case "zhu3": + floor = 3; + break; + case "zhu4": + floor = 4; + break; + case "zhu5": + floor = 5; + break; + case "zhu6": + floor = 6; + break; + case "zhu7": + case "dong1": + floor = 7; + break; + case "zhu8": + case "dong2": + floor = 8; + break; + case "zhu9": + case "dong3": + floor = 9; + break; + case "zhu10": + floor = 10; + break; + case "zhu11": + floor = 11; + break; + case "zhu12": + case "dong4": + floor = 12; + break; + case "zhu13": + case "dong5": + floor = 13; + break; + case "zhu14": + case "dong6": + floor = 14; + break; + case "zhu15": + floor = 15; + break; + case "zhu16": + floor = 16; + break; + case "zhu17": + case "dong7": + floor = 17; + break; + case "zhu18": + case "dong8": + floor = 18; + break; + case "zhu19": + case "dong9": + floor = 19; + break; + case "zhu20": + floor = 20; + break; + case "zhu21": + floor = 21; + break; + case "zhu22": + floor = 22; + break; + case "zhu23": + floor = 23; + break; + case "zhu24": + floor = 24; + break; + case "zhu25": + floor = 25; + break; + default: + floor = 13; + break; + } + return floor; + } + core.setAlpha(ctx, 0.6); + core.fillRoundRect(ctx, 8 + (Math.floor((floornum(core.status.floorId) - 1) % 5) * 24), + 138 + (Math.floor((floornum(core.status.floorId) - 1) / 5) * 24), 20, 20, 1, "#6bff7a"); + if (core.status.event.id == 'viewMaps') { + core.fillRoundRect(ctx, 8 + (Math.floor((floornum(core.status.event.data.floorId) - 1) % 5) * 24) + 2, + 138 + (Math.floor((floornum(core.status.event.data.floorId) - 1) / 5) * 24) + 2, 16, 16, 1, "#6198ff"); + } + core.setAlpha(ctx, 1); + if (flags.mapicon) { + if (flags.dimension[0] == "主世界" && flags.dimension[1] == "地表") { + for (var i = 1; i < 26; i++) { + var pic = 0; + if (("zhu" + i) in flags.mapicon["zhu"]) { + switch (flags.mapicon["zhu"][("zhu" + i)]) { + case 1: + pic = 932; + break; + case 2: + pic = 933; + break; + case 3: + pic = 934; + break; + case 4: + pic = 935; + break; + } + } else { + pic = 931; + } + if (pic != 0) core.drawIcon(ctx, pic, 8 + (Math.floor((i - 1) % 5) * 24) + 3, + 138 + (Math.floor((i - 1) / 5) * 24) + 3, 14, 14); + } + } else if (flags.dimension[0] == "主世界" && flags.dimension[1] == "洞穴") { + for (var i = 1; i < 10; i++) { + var pic = 0; + if (("dong" + i) in flags.mapicon["dong"]) { + switch (flags.mapicon["dong"][("dong" + i)]) { + case 1: + pic = 932; + break; + case 2: + pic = 933; + break; + case 3: + pic = 934; + break; + case 4: + pic = 935; + break; + } + } else { + pic = 931; + } + var ii; + switch (i) { + case 1: + ii = 7; + break; + case 2: + ii = 8; + break; + case 3: + ii = 9; + break; + case 4: + ii = 12; + break; + case 5: + ii = 13; + break; + case 6: + ii = 14; + break; + case 7: + ii = 17; + break; + case 8: + ii = 18; + break; + case 9: + ii = 19; + break; + } + if (pic != 0) core.drawIcon(ctx, pic, 8 + (Math.floor((ii - 1) % 5) * 24) + 3, + 138 + (Math.floor((ii - 1) / 5) * 24) + 3, 14, 14); + } + } + + } + for (var i = 0; i < 3; i++) { + core.drawLine(ctx, 6, 264 + (i * 24), 126, 264 + (i * 24)); + } + for (var i = 0; i < 6; i++) { + core.drawLine(ctx, 6 + (i * 24), 264, 6 + (i * 24), 312); + } + core.drawIcon(ctx, 932, 8 + (24 * 0), 266, 20, 20); + core.drawIcon(ctx, 933, 8 + (24 * 1), 266, 20, 20); + core.drawIcon(ctx, 934, 8 + (24 * 2), 266, 20, 20); + core.drawIcon(ctx, 935, 8 + (24 * 3), 266, 20, 20); + + //core.fillRoundRect(ctx, 0, 0, 129, 416, 3, "#dbdbdb"); + } + } + function map(x, y) { + for (var i = 0; i < 5; i++) { + for (var j = 0; j < 5; j++) { + if (x > 8 + (i * 24) - 3 && x < 8 + (i * 24) + 20 + 3 && y > 138 + (j * 24) - 3 && y < 138 + (j * 24) + 20 + 3) { + var floor; + if (flags.dimension[0] == "主世界" && flags.dimension[1] == "地表") { + floor = "zhu" + (i + 1 + (j * 5)); + return floor; + } else if (flags.dimension[0] == "主世界" && flags.dimension[1] == "洞穴") { + switch (i + 1 + (j * 5)) { + case 7: + floor = "dong1"; + break; + case 8: + floor = "dong2"; + break; + case 9: + floor = "dong3"; + break; + case 12: + floor = "dong4"; + break; + case 13: + floor = "dong5"; + break; + case 14: + floor = "dong6"; + break; + case 17: + floor = "dong7"; + break; + case 18: + floor = "dong8"; + break; + case 19: + floor = "dong9"; + break; + } + return floor; + } + } + } + } + } + function mapicon(x, y) { + for (var i = 0; i < 5; i++) { + for (var j = 0; j < 2; j++) { + if (x > 8 + (i * 24) - 3 && x < 8 + (i * 24) + 20 + 3 && y > 264 + (j * 24) - 3 && y < 264 + (j * 24) + 20 + 3) { + var floor = (i + 1 + (j * 5)); + return floor; + } + } + } + } + function registerAction_extend() { + // 键盘操作还是建议在脚本编辑onkeyup中写 + // 如果点击是有效操作 记得core.status.route.push(replayAction) 配合registerReplayAction实现 + // 不要注册"extendClick"这种根据点击坐标触发的录像行为 在录像检测环境中不会生成拓展栏 + extend.addEventListener("click", function (e) { + e.stopPropagation(); + var x = e.clientX, + y = e.clientY; + var loc = getClickLoc(x, y), + x = loc.x, + y = loc.y; + + //console.log(x, y); + if (!core.domStyle.isVertical) { //横屏 + if (x > 1 && x < 128 && y > 1 && y < 70) { + flags.tip[0] = "右键一个图块/敌怪试试吧"; + core.ui.drawStatusBar(); + } else if (x > 1 && x < 128 && y > 80 && y < 108) { + flags.tip[0] = "显示当前维度、层和区域信息"; + core.ui.drawStatusBar(); + } else if (x > 1 && x < 128 && y > 108 && y < 130) { + flags.tip[0] = "显示当前坐标,\r[#61fff3]高亮\r时意味着尽头存在楼梯"; + core.ui.drawStatusBar(); + } else if (x > 6 && x < 128 && y > 134 && y < 255) { + flags.tip[0] = "显示当前区域的小地图,只能预览\r[#61fff3]已探索区域\r,点击任意位置返回"; + core.ui.drawStatusBar(); + } else if (x > 1 && x < 128 && y > 263 && y < 311) { + flags.tip[0] = "在预览时,可以标记正在预览的区域"; + core.ui.drawStatusBar(); + } + if (core.status.event.id == 'viewMaps') { + if (x > 8 && x < 124 && y > 138 && y < 254) { + if (map(x, y) != undefined && core.hasVisitedFloor(map(x, y))) { + flags.interface = "锁定"; + ui.prototype._drawViewMaps(core.floorIds.indexOf(map(x, y))); + extend.update(); + } + } else if (x > 8 && x < 124 && y > 264 && y < 308) { + if (mapicon(x, y) != undefined) { + if (flags.dimension[0] == "主世界" && flags.dimension[1] == "地表") { + flags.mapicon["zhu"][core.status.event.data.floorId] = mapicon(x, y); + } else if (flags.dimension[0] == "主世界" && flags.dimension[1] == "洞穴") { + flags.mapicon["dong"][core.status.event.data.floorId] = mapicon(x, y); + } + extend.update(); + } + } else { + core.closePanel(); + core.drawMap(); + core.playSound('取消'); + flags.interface = "物品栏"; + } + } else if (flags.interface == "物品栏" && + flags.tech == 0 && + flags.CustomKey == 0 && + core.status.event.id == null && + map(x, y) != undefined && + core.hasVisitedFloor(map(x, y))) { + flags.interface = "锁定"; + ui.prototype._drawViewMaps(core.floorIds.indexOf(map(x, y))); + extend.update(); + } + } + }); + extend.addEventListener("dblclick", function (e) { + e.stopPropagation(); + var x = e.clientX, + y = e.clientY; + var loc = getClickLoc(x, y), + x = loc.x, + y = loc.y; + + //console.log("双击的位置是" + x + "," + y); + }); + } + + function getClickLoc(x, y) { + return { + x: Math.round((x - extend.canvas.offsetLeft) / core.domStyle.scale), + y: Math.round((y - extend.canvas.offsetTop) / core.domStyle.scale) + } + } + + function getPosData() { + var gpos = { + left: cssToPixel(core.dom.gameGroup.style.left), + top: cssToPixel(core.dom.gameGroup.style.top), + width: cssToPixel(core.dom.gameGroup.style.width), + height: cssToPixel(core.dom.gameGroup.style.height) + }; + + var extendScale = Sprite.prototype.__extendScale; + if (window.extend) extendScale = extend.values.scale + var scale = !core.domStyle.isVertical ? extendScale.hpos : extendScale.vpos; + var expos = { + x: gpos.left + gpos.width, + y: gpos.top, + } + if (core.domStyle.isVertical) { + expos = { + x: gpos.left, + y: gpos.top + gpos.height, + } + } + + expos.width = gpos.width * scale.width; + expos.height = gpos.height * scale.height; + + return expos; + } + + function resetScale(type, w, h) { + if (!type) type = core.domStyle.isVertical ? 2 : 1; + var scale = extend.values.scale.hpos; + if (type == 2) scale = extend.values.scale.vpos; + + scale.width = w || scale.width; + scale.height = h || scale.height; + extend.resize_extend(); + core.resize(); + } + + function setBorder() { + extend.setCss("box-sizing: border-box;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;"); + var border = [ + "border-left: 0;border-bottom: 3px solid rgb(204, 204, 204);border-top: 3px solid rgb(204, 204, 204);border-right: 3px solid rgb(204, 204, 204);", + "border-left: 3px solid rgb(204, 204, 204);border-right: 3px solid rgb(204, 204, 204);border-top: 0;border-bottom: 3px solid rgb(204, 204, 204);" + ] + if (extend.values.border[0]) border[0] = extend.values.border[0]; + if (extend.values.border[1]) border[1] = extend.values.border[1]; + if (!core.domStyle.isVertical) { + extend.setCss(border[0]) + } else { + extend.setCss(border[1]) + } + } + + function changeBorder(border, border_vertical) { + if (border) extend.values.border[0] = border; + if (border_vertical) extend.values.border[1] = border_vertical; + extend.setBorder() + } + + function resize_extend(noRefresh) { + var data = extend.getPosData(); + extend.resize(data.width, data.height, true); + if (!noRefresh) extend.update(); + } + + function changePos(noRefresh) { + var extendPos = extend.getPosData(); + extend.move(extendPos.x, extendPos.y); + if (!noRefresh) extend.update(); + } + + function hide() { + extend.values.hidden = true; + extend.canvas.style.display = "none"; + } + + function show() { + extend.values.hidden = false; + extend.canvas.style.display = "block"; + extend.resize_extend(); + core.resize(); + } + + function cssToPixel(css) { + return parseFloat(css.replace("px", "")); + } + + function initExtend() { + if (window.extend) { + extend.update(); + return; + } + var pos = getPosData(); + window.extend = new Sprite(pos.x, pos.y, pos.width / core.domStyle.scale, pos.height / core.domStyle.scale, 220, "window", "extend"); + + extend.values = { + scale: Sprite.prototype.__extendScale, + border: [], + hideInVertical: false, + hidden: true + } + + extend.update = update; + extend.getPosData = getPosData; + extend.resetScale = resetScale; + extend.setBorder = setBorder; + extend.changeBorder = changeBorder; + extend.resize_extend = resize_extend; + extend.changePos = changePos; + extend.hide = hide; + extend.show = show; + registerAction_extend(); + } + + if (main.replayChecking) return; + + Sprite.prototype.__extendScale = core.clone(scale); + + var _up = core.control.updateStatusBar; + + core.control.updateStatusBar = function (d, i) { + _up.call(core.control, d, i); + if (window.extend && (core.domStyle.isVertical && extend.values.hideInVertical)) return extend.hide(); + if (core.getFlag("showExtend")) { + if (!window.extend) { + initExtend(); + } + if (extend.values.hidden) extend.show(); + else extend.update(); + } else if (window.extend && !extend.values.hidden) { + window.extend.hide(); + core.resize(); + } + } + + var _loadData = core.control.loadData, + _saveData = core.control.saveData; + + core.control.saveData = function () { + var data = _saveData.call(core.control); + if (!extend) return; + data._extend_values = extend.values; + return data; + } + + core.control.loadData = function (data, callback) { + _loadData.call(core.control, data, callback); + if (!data._extend_values) return; + extend.values = data._extend_values; + extend.resetScale() + } + + var _resetGame = core.events.resetGame; + core.events.resetGame = function (hero, hard, floorId, maps, values) { + _resetGame.call(core.events, hero, hard, floorId, maps, values); + initExtend(); + /*if (flags.showExtend)*/ extend.show(); + }; + var resize_gameGroup; + for (var i = 0; i < core.control.resizes.length; i++) { + if (core.control.resizes[i].name == "gameGroup") resize_gameGroup = core.control.resizes[i]; + } + + if (resize_gameGroup == null) alert("没有检测到resize_gameGroup,也许该插件兼容的版本与你并不相同,请去h5造塔群95939661问问吧"); + + resize_gameGroup.func = function (obj) { + var startBackground = core.domStyle.isVertical ? (main.styles.startVerticalBackground || main.styles.startBackground) : main.styles.startBackground; + if (main.dom.startBackground.getAttribute('__src__') != startBackground) { + main.dom.startBackground.setAttribute('__src__', startBackground); + main.dom.startBackground.src = startBackground; + } + + var gameGroup = core.dom.gameGroup; + var totalWidth, totalHeight; + if (core.domStyle.isVertical) { + totalWidth = obj.outerWidth; + totalHeight = obj.outerHeight + obj.statusBarHeightInVertical + obj.toolbarHeightInVertical + } else { + totalWidth = obj.outerWidth + obj.BAR_WIDTH * core.domStyle.scale + (obj.hideLeftStatusBar ? 0 : obj.BORDER); + totalHeight = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0); + } + gameGroup.style.width = totalWidth + "px"; + gameGroup.style.height = totalHeight + "px"; + // extend resize + if (window.extend && !extend.values.hidden) { + extend.resize_extend(true); + var extendPos = extend.getPosData(); + if (!core.domStyle.isVertical) totalWidth += extendPos.width; + else totalHeight += extendPos.height; + } + gameGroup.style.left = (obj.clientWidth - totalWidth) / 2 + "px"; + gameGroup.style.top = (obj.clientHeight - totalHeight) / 2 + "px"; + if (window.extend && !extend.values.hidden) extend.changePos(); + // floorMsgGroup + var floorMsgGroup = core.dom.floorMsgGroup; + floorMsgGroup.style = obj.globalAttribute.floorChangingStyle; + floorMsgGroup.style.width = obj.outerWidth - 2 * obj.BORDER + "px"; + floorMsgGroup.style.height = totalHeight - 2 * obj.BORDER + "px"; + floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; + // startPanel + core.dom.startPanel.style.fontSize = 16 * core.domStyle.scale + "px"; + // musicBtn + if (core.domStyle.isVertical || core.domStyle.scale < 1) { + core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px"; + } else { + core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px"; + core.dom.musicBtn.style.bottom = (obj.clientHeight - totalHeight) / 2 - 27 + "px"; + } + } + + }, + "31-角色属性界面": function () { + this.attrshow = function () { + if (flags.attrui == 1) { + var ctx = "uievent"; + flags.interface = "锁定"; + if (core.getContextByName(ctx) == null) { + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } else { + core.clearMap(ctx); + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } + core.drawImage(ctx, "attribute.png", 0, 0, 416, 416); + core.fillBoldText(ctx, "退出", 280, 362, "#ff6661", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText(ctx, "角色属性", 168, 80, "white", "black", ui.prototype._buildFont(20, false), 416); + var initx = 90; + var addx = 140; + var inity = 122; + var addy = 20; + core.fillText(ctx, "生命值:\r[#00a808]" + core.status.hero.hp + "/" + core.status.hero.hpmax + "\r", + initx + (addx * 0), inity + (addy * 0), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "饱食度:\r[#00a808]" + flags.hunger[0] + "/" + flags.hunger[1] + "\r", + initx + (addx * 0), inity + (addy * 1), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "攻速:\r[#00a808]" + flags.property.atkSpeed[0] + " (" + ((flags.property.atkSpeed[0] - 110) / -20) + "s)" + "\r", + initx + (addx * 0), inity + (addy * 2), "black", ui.prototype._buildFont(14, false), 200); + + core.fillText(ctx, "触碰范围:\r[#00a808]" + flags.property.touchRange[1] + "\r", initx + (addx * 1), inity + (addy * 0), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "攻击范围:\r[#00a808]" + flags.property.touchRange[0] + "\r", initx + (addx * 1), inity + (addy * 1), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "经验加成:\r[#00a808]" + flags.property.exp + "\r", initx + (addx * 1), inity + (addy * 2), "black", ui.prototype._buildFont(14, false), 200); + + core.fillText(ctx, "攻击:\r[#00a808]" + core.status.hero.atk + "\r", initx + (addx * 0), inity + (addy * 4), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "海洋攻击:\r[#00a808]" + flags.property.atkSea + "\r", initx + (addx * 0), inity + (addy * 5), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "飞行攻击:\r[#00a808]" + flags.property.atkFly + "\r", initx + (addx * 0), inity + (addy * 6), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "无视防御:\r[#00a808]" + flags.property.igdef1 + "\r", initx + (addx * 0), inity + (addy * 7), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "横扫之刃:\r[#00a808]" + flags.property.igdef2 + "\r", initx + (addx * 0), inity + (addy * 8), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "暴击:\r[#00a808]" + flags.property.crit + "\r", initx + (addx * 0), inity + (addy * 9), "black", ui.prototype._buildFont(14, false), 200); + + core.fillText(ctx, "防御:\r[#00a808]" + core.status.hero.def + "\r", initx + (addx * 1), inity + (addy * 4), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "减伤:\r[#00a808]" + flags.property.reduceDa + "\r", initx + (addx * 1), inity + (addy * 5), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "爆炸减伤:\r[#00a808]" + flags.property.reduceBoom + "\r", initx + (addx * 1), inity + (addy * 6), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "弹射物减伤:\r[#00a808]" + flags.property.reduceBul + "\r", initx + (addx * 1), inity + (addy * 7), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "反伤:\r[#00a808]" + flags.property.backDa + "\r", initx + (addx * 1), inity + (addy * 8), "black", ui.prototype._buildFont(14, false), 200); + core.fillText(ctx, "无敌:\r[#00a808]" + flags.property.undead + "\r", initx + (addx * 1), inity + (addy * 9), "black", ui.prototype._buildFont(14, false), 200); + + } + } + + }, + "32-委托界面": function () { + this.taskshow = function () { + if (flags.taskui == 1) { + var ctx = "uievent"; + flags.interface = "锁定"; + if (core.getContextByName(ctx) == null) { + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } else { + core.clearMap(ctx); + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } + core.drawImage(ctx, "task.png", 0, 0, 416, 416); + core.fillBoldText(ctx, "退出", 340, 370, "#ff6661", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText(ctx, "村民的委托", 158, 76, "white", "black", ui.prototype._buildFont(20, false), 416); + core.fillBoldText(ctx, "委托点:10", 300, 76, "white", "black", ui.prototype._buildFont(14, false), 416); + function tasktext(num) { + var n = 0; + if (num == 1) { + n = 0; + } else if (num == 2) { + n = 81; + } else if (num == 3) { + n = 166; + } + core.drawIcon(ctx, "I555", 47, 127 + n, 32, 32); + core.fillText(ctx, "砍伐树木", 110, 132 + n, "black", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "(0 / 50)", 200, 132 + n, "black", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "委托奖励:", 110, 164 + n, "black", ui.prototype._buildFont(16, false), 400); + core.fillText(ctx, "橡木原木 * 5", 200, 164 + n, "black", ui.prototype._buildFont(16, false), 400); + core.fillBoldText(ctx, "刷新委托", 320, 132 + n, "white", "black", ui.prototype._buildFont(16, false), 416); + core.fillBoldText(ctx, "领取奖励", 320, 164 + n, "white", "black", ui.prototype._buildFont(16, false), 416); + } + tasktext(1); + tasktext(2); + tasktext(3); + + } + } + + }, + "33-成就界面": function () { + this.achishow = function () { + if (flags.achiui == 1) { + var ctx = "uievent"; + flags.interface = "锁定"; + if (core.getContextByName(ctx) == null) { + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } else { + core.clearMap(ctx); + core.createCanvas(ctx, 0, 0, 416, 416, 130); + } + core.setTextAlign(ctx, "left"); + core.drawImage(ctx, "achi.png", 0, 0, 416, 416); + core.fillBoldText(ctx, "退出", 340, 394, "#ff6661", "black", ui.prototype._buildFont(18, false), 416); + core.fillBoldText(ctx, "成就", 188, 38, "white", "black", ui.prototype._buildFont(20, false), 416); + core.fillBoldText(ctx, "上一页", 284, 60, "white", "black", ui.prototype._buildFont(14, false), 416); + core.fillBoldText(ctx, "下一页", 354, 60, "white", "black", ui.prototype._buildFont(14, false), 416); + core.fillText(ctx, "1", 336, 60, "black", ui.prototype._buildFont(16, false), 400); + core.fillBoldText(ctx, "提交成绩", 36, 354, "white", "black", ui.prototype._buildFont(14, false), 416); + core.setTextAlign(ctx, "center"); + core.fillBoldText(ctx, "--历程类--", 62, 88, "#fdff61", "black", ui.prototype._buildFont(14, false), 416); + core.fillBoldText(ctx, "收集类", 62, 114, "white", "black", ui.prototype._buildFont(14, false), 416); + core.fillBoldText(ctx, "挑战类", 62, 140, "white", "black", ui.prototype._buildFont(14, false), 416); + core.fillBoldText(ctx, "彩蛋类", 62, 166, "white", "black", ui.prototype._buildFont(14, false), 416); + for (var i = 1; i < 4; i++) { + for (var j = 0; j < 6; j++) { + if (i == 1) { + core.drawIcon(ctx, "I555", 120, 84 + (47 * j), 32, 32); + core.drawTextContent( + ctx, "Minecraft 用下界合金锭升级一把锄,然后重新考虑你的人生抉择", + { left: 168, top: 84 + (47 * j), color: "white", fontSize: 13, maxWidth: 220 } + ); + core.drawTextContent( + ctx, "100000/1000000", + { left: 352, top: 101 + (47 * j), color: "white", fontSize: 12, maxWidth: 60 } + ); + } + } + } + } + } + + }, + "34-LCG随机数生成器": function () { + this.LCGrand = function () { + // 线性同余生成器算法的参数 + const a = 1664525; + const c = 1013904223; + const m = Math.pow(2, 32); + flags.seed2 = flags.seed1; + + return function () { + // 计算下一个随机数 + flags.seed2 = (a * flags.seed2 + c) % m; + return Math.floor(flags.seed2 / m * flags.limit); + }; + } + + this.rand3 = function (limit) { + if (limit) flags.limit = limit; + return flags.rand3(); + } + + }, + +} \ No newline at end of file diff --git a/script.md b/script.md new file mode 100644 index 0000000..64e7255 --- /dev/null +++ b/script.md @@ -0,0 +1,739 @@ +# 脚本 + +?> 在这一节中,让我们来了解如何使用控制台和使用脚本! + +在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用。 + +* [脚本教程视频](https://www.bilibili.com/video/BV1uL411J7yZ?share_source=copy_web) + +## 控制台的使用 + +在Chrome浏览器中,按(Ctrl+Shift+I)可打开控制台。 + +![](img/console.jpg) + +控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。 + +有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。 + +### Console:命令行 + +Console页为命令行。可以在这里输入一些命令进行调试。 + +比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。 + +更多的API可参见[附录:API列表](#附录:API列表)。 + +除此以外,游戏中的报错等信息也是可以在Console中进行查看的。 + +![](img/console1.jpg) + +### Sources:断点调试 + +Sources页可以查看JS源代码,并进行断点调试等。 + +例如,如果相对脚本编辑中的伤害计算函数进行断点调试: +1. 在左边找到`project/functions.js`,单击打开文件 +2. 并找到对应的行(可以Ctrl+F搜索),比如搜索`getDamageInfo` +3. 在行号上点一下打断点,会出现一个蓝色标签 + +之后,当代码运行到你的断点处时,将自动停止运行。 + +![](img/sources.jpg) + +可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。 + +图中红色框内有几个按钮,从左到右分别是:**继续执行**,**执行到下一行**,**进入当前函数**,**跳出当前函数**,**单步执行**。 + +通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。 + +红圈下方是Call Stack,即当前的函数调用链(从哪些地方调用过来的)。 + +Sources还有更多有趣的功能,在此不做介绍,有兴趣的可自行网上搜索了解。 + +### Elements:网页元素查看 + +Elements页可以查看网页的源代码,调整css布局等。 + +![](img/elements.jpg) + +不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。 + +手机模式下,左边可以对屏幕分辨率进行调整和模拟。 + +这可以很有效的帮我们进行测试样板在手机端的表现。 + +## 整体项目架构 + +``` text +├── /_server/ # 为可视化地图编辑器提供一些支持的目录 +├── /libs/ # ---- 系统库目录 ---- +│ ├─ /thirdparty/ # 游戏所用到的第三方库文件 +│ ├─ actions.js # 用户交互处理 +│ ├─ core.js # 系统核心文件(游戏入口,接口&转发) +│ ├─ control.js # 游戏逻辑控制 +│ ├─ data.js # 全塔属性等 +│ ├─ enemys.js # 怪物相关处理 +│ ├─ events.js # 各个事件的执行 +│ ├─ icons.js # 图标和素材 +│ ├─ items.js # 道具效果 +│ ├─ loader.js # 各个资源加载 +│ ├─ maps.js # 地图数据和绘制 +│ ├─ ui.js # UI窗口绘制 +│ └─ utils.js # 工具类函数 +├── /project/ # ---- 项目目录 ---- +│ ├─ /animates/ # 动画目录 +│ ├─ /floors/ # 楼层文件 +│ ├─ /images/ # 图片素材 +│ ├─ /sounds/ # bgm和音效 +│ ├─ data.js # 全塔属性 +│ ├─ enemys.js # 怪物属性 +│ ├─ events.js # 公共事件 +│ ├─ functions.js # 脚本编辑 +│ ├─ icons.js # 素材和ID的对应关系定义 +│ ├─ items.js # 道具的定义和效果 +│ ├─ maps.js # 地图和数字的对应关系 +│ └─ plugins.js # 自定义插件 +├── /常用工具/ # 辅助造塔的小工具 +├── editor.html # 地图编辑器 +├── editor-mobile.html # 手机版的地图编辑器 +├── index.html # 主程序,游戏的入口 +├── main.js # JS程序的入口,将动态对所需JS进行加载 +├── style.css # 游戏所需要用到的样式表 +└── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 +``` + +`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 + +`libs`为**系统库目录**,里面存放了各个系统核心函数。 + +从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 + +`project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 + +## 函数的转发 + +在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。 + +例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的: + +```js +////// 获得某个自定义变量或flag ////// +control.prototype.getFlag = function(name, defaultValue) { + if (!core.status.hero) return defaultValue; + var value = core.status.hero.flags[name]; + return value != null ? value : defaultValue; +} +``` + +也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。 + +但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`,而不需要中间的control。 + +为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数,转发到core中执行**。 + +上述`getFlag`代码的转发实际上是增加了如下函数: + +```js +////// getFlag函数的转发 ////// +core.getFlag = function (name, defaultValue) { + return core.control.getFlag(name, defaultValue); +} +// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag() +``` + +转发是自动完成的,其满足如下两条规则: +- **在libs中其他文件定义的函数,如果不以下划线`_`开头,就会进行转发。** +- **如果core中已经存在同名函数,则会在控制台中打出一条报错信息,并不转发该函数。** + +具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。 + +!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发! + +例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。 + +## 插件编写 + +插件编写是H5魔塔的一个重大特点,从V2.0.1引入,并逐渐发扬光大。 + +对于有一定脚本经验的人来说,可以编写插件来实现各种各样的功能,包括且不仅限于拓展功能的实现,系统代码的复写等等。 + +在V2.5.5以前,插件位置都在脚本编辑中;从V2.6开始则迁移到了新的下拉框中,并进行了切分。 + +你也可以创建自己的插件。 + +![](img/plugin.jpg) + +新的插件切分和原来的单插件使用方法完全一致,单纯进行了切分而已。可参见已有的`init`和`drawLight`的样例。 + +拆分的意义主要是将各个可能的功能独立出来,避免单个框内内容太长,过大和混杂等。 + +在V2.6中,应当每个独立的额外功能实现都新建一个自己的插件,这样也方便进行拓展,例如打包迁移到别的塔上,或发布在网页插件库中。 + +另外一点需要注意的是,所有插件的初始化都会在系统资源加载之前,此时图片等资源尚未进行加载。 + +在所有资源加载完毕时,将会执行init插件中的_afterLoadResources函数,可以在这里对资源进行一些操作,比如切分图片等。 + +```js +function () { + console.log("插件编写测试"); + + // 可以写一些直接执行的代码 + // 在这里写的代码将会在【资源加载前】被执行,此时图片等资源尚未被加载。 + // 请勿在这里对包括bgm,图片等资源进行操作。 + + + this._afterLoadResources = function () { + // 本函数将在所有资源加载完毕后,游戏开启前被执行 + // 可以在这个函数里面对资源进行一些操作,比如切分图片等。 + + // 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。 + // var arr = core.splitImage("assets.png", 32, 32); + // for (var i = 0; i < arr.length; i++) { + // core.material.images.images["asset"+i+".png"] = arr[i]; + // } + + } + + // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + // 从V2.6开始,插件中用this.XXX方式定义的函数也会被转发到core中,详见文档-脚本-函数的转发。 +} +``` + +网站上提供了一个插件库,[https://h5mota.com/plugins/](https://h5mota.com/plugins/),上面有一些大家分享的插件,可供使用。 + +可以查看附录中的[API列表](api)来查看所有的系统API内容。 + +## register系列函数 + +在API列表中,有一系列的函数以`register`开头;这一系列函数允许你将一部分自定义的代码注入到样板中,从而可以监听某一系列行为并进行处理。 + +下面会对这系列函数进行逐个介绍。 + +### registerAction + +``` +registerAction: fn(action: string, name: string, func: string|fn(params: ?), priority?: number) +此函数将注册一个用户交互行为。 +action: 要注册的交互类型,如 ondown, onup, keyDown 等等。 +name: 你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +func: 执行函数。 +如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 +priority: 优先级;优先级高的将会被执行。此项可不填,默认为0 +``` + +`registerAction`为register系列最常用的功能之一,它允许你任意监听用户的交互事件,包括且不仅限于按键、点击、拖动、长按,等等。 + +下面是一个例子: + +```js +// 当flag:abc是true时,点击屏幕左上角可以使用道具破墙镐 +// 注入一个 ondown 事件,名称为 my_pickaxe +core.registerAction('ondown', 'my_pickaxe', function (x, y, px, py) { + // 如果当前正在执行某个事件,则忽略之。 + if (core.status.lockControl) return false; + // 如果勇士正在行走中,则忽略之。 + if (core.isMoving()) return false; + // x, y 为点击坐标;px, py 为点击的像素坐标 + // 检查flag是否为true,且点击了(0,0)点 + if (core.hasFlag('abc') && x == 0 && y == 0) { + // 检查能否使用破墙镐 + if (core.canUseItem('pickaxe')) { + core.useItem('pickaxe'); + // 返回true将拦截其他交互函数对于点击的处理 + return true; + } + } + // 返回false后将会继续执行其他的交互函数(例如寻路) + return false; + +// 优先级设置为100,比系统优先级高,因此将会优先执行此注入的项目。 +}, 100); + +// 当你不再需要上述监听时,可以通过下面这一句取消注入。 +// core.unregisterActon('ondown', 'my_pickaxe'); +``` + +下面是另一个例子: + +```js +// 假设我们通过事件流做了一个商店,希望能通过”长按空格“和“长按屏幕”进行连续确认购买 +// 此时,需要使用一个flag,表示是否在商店中;例如 flag:inShop + +// 注入一个keyDown(按下)事件;请注意,当按下按键且不释放时,会连续触发keyDown。 +core.registerAction('keyDown', 'my_shop', function (keycode) { + // 检查是否确实在该商店事件中;进该商店前需要将flag:inShop设为true,退出后需要设为false + if (!core.hasFlag('inShop')) return false; + // 检查当前事件是一个”显示选择项“ + if (core.status.event.id != 'action' || core.status.event.data.type != 'choices') { + return false; + } + // 获得当前“显示选择项”的各个选项 + var choices = core.status.event.data.current.choices; + // 获得当前选项第一项在屏幕上的y坐标 + var topIndex = core.actions._getChoicesTopIndex(choices.length); + // 检查按键的键值,是否是空格或者回车 + if (keycode == 13 || keycode == 32) { + // 如果是,则视为直接点击屏幕上的选项 + // core.status.event.selection为当前光标选择项;第一项为0 + // 点击的x为HSIZE,即屏幕左右居中;y为topIndex + core.status.event.selection为该选择项 + core.actions._clickAction(core.actions.HSIZE, topIndex + core.status.event.selection); + // 不再执行其他的交互函数 + return true; + } + // 否则,继续执行其他的交互函数(如上下键光标选择) + return false; +}, 100); + + +// 还需要注册一个keyUp(放开)事件,拦截空格和回车 +// 这是因为,按一下空格会先触发keyDown再触发keyUp;这里不拦截的话会购买两次。 +var _my_shop_up = function (keycode) { + if (!core.hasFlag('inShop')) return false; + if (core.status.event.id != 'action' || core.status.event.data.type != 'choices') { + return false; + } + // 检查是否是空格或者回车,如果是则拦截操作。 + if (keycode == 13 || keycode == 32) return true; + return false; +}; +// 这里可以传入前面定义的函数体。 +core.registerAction('keyUp', 'my_shop', _my_shop_up, 100); + + +// 注册一个长按事件,允许长按连续购买 +this._my_shop_longClick = function (x, y, px, py) { + if (!core.hasFlag('inShop')) return false; + if (core.status.event.id != 'action' || core.status.event.data.type != 'choices') { + return false; + } + // 直接视为点击了此位置 + core.actions._clickAction(x, y, px, py); + return true; +} +// 如果是插件中使用`this.xxx`定义的函数,也可以直接传入函数名。 +core.registerAction('longClick', 'my_shop', '_my_shop_longClick', 100); +``` + +简单地说,`registerAction`这个函数**允许你在任何时候监听和拦截任何用户交互行为**。 + +目前,`registerAction`支持监听和拦截如下交互: + +- `keyDown`: 当一个键被按下时 + - 对应的函数参数:`function (keycode)`,为此时按下键的键值 + - 请注意:如果长按键不放开的话,会连续触发`keyDown`事件 +- `keyUp`: 当一个键被放开时 + - 对应的函数参数:`function (keycode, altKey, fromReplay)`,为此时放开键的键值、当前alt是否被按下、是否是录像播放中模拟的按键 +- `onKeyDown`: 当一个键被按下时 + - 对应的函数参数:`function (e)`,为此时按键的信息。这里的`e`是一个`KeyboardEvent`。 + - 请注意:如果长按键不放开的话,会连续触发`onKeyDown`事件。 + - 和上述`keyDown`的主要区别就是参数为原始的`KeyboardEvent`;仍然推荐直接使用`keyDown`。 +- `onKeyUp`: 当一个键被放开时 + - 对应的函数参数:`function (e)`,为此时按键的信息。这里的`e`是一个`KeyboardEvent`。 + - 和上述`keyUp`的主要区别就是参数为原始的`KeyboardEvent`;仍然推荐直接使用`keyUp`。 +- `ondown`: 当屏幕被鼠标或手指按下时 + - 对应的函数参数:`function (x, y, px, py)`,为此时按下时的格子坐标和像素坐标。 +- `onmove`: 当屏幕被鼠标滑动,或手指拖动时 + - 对应的函数参数:`function (x, y, px, py)`,为此时滑动或拖动时的格子坐标和像素坐标。 + - 如果是鼠标,只要在屏幕上滑动就会触发`onmove`,无需处于点击状态(也就是悬浮也是可以的)。 + - 如果是触摸屏,则只有手指按下滑动时才会触发`onmove`(并不存在什么悬浮的说法)。 +- `onup`: 当屏幕被鼠标或手指放开时 + - 对应的函数参数:`function (x, y, px, py)`,为此时放开时的格子坐标和像素坐标。 +- `onclick` 【已废弃】 + - 从V2.8.2起,此交互已被废弃,注册一个`onclick`会被直接转发至`ondown`。 +- `onmousewheel`: 当鼠标滚轮滚动时 + - 对应的函数参数:`function (direct)`,为此时滚轮方向。向下滚动是-1,向上滚动是1。 + - 目前在楼传、怪物手册、存读档、浏览地图等多个地方绑定了鼠标滚轮事件。 +- `keyDownCtrl`: 当Ctrl键处于被长按不放时 + - 对应的函数参数:`function ()`,即无参数。 + - 目前主要用于跳过剧情对话。 +- `longClick`: 当鼠标或手指长按屏幕时 + - 对应的函数参数:`function (x, y, px, py)`,为长按屏幕对应的格子坐标和像素坐标 + - 目前主要用于虚拟键盘,和跳过剧情对话。 +- `onStatusBarClick`: 当点击状态栏时 + - 对应的函数参数:`function (px, py, vertical)`,为状态栏点击的像素坐标。 + - 如果参数`vertical`不为`null`,表示该状态栏点击是录像播放时触发的,其值为录像记录时的横竖屏状态。 + +### registerAnimationFrame + +``` +registerAnimationFrame: fn(name: string, needPlaying: bool, func?: fn(timestamp: number)) +注册一个 animationFrame +name: 名称,可用来作为注销使用 +needPlaying: 是否只在游戏运行时才执行(在标题界面不执行) +func: 要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数 +``` + +`registerAnimationFrame`为其次常用的功能,可以用于需要反复执行的脚本,常用于动画之类。 + +目前,样板的全局动画(即怪物的帧振动)、人物行走动画、普通动画、天气效果、游戏计时、以及楼层并行事件等,都是通过`registerAnimationFrame`进行实现的。 + +通过`registerAnimationFrame`注册的函数会平均每16.6ms执行一次(视浏览器的性能而定,例如手机上可能执行频率会变慢)。可以通过函数的`timestamp`参数来获得从页面加载完毕到当前所经过的毫秒数(从而可以进一步控制执行频率,如每500ms才执行一次)。 + +**推荐所有的需要反复执行的脚本(如动画相关)都通过此函数来注册,而不是使用`setInterval`,以获得更好的性能。** + +下面是“人物血量动态变化”的插件的例子。 + +```js +var speed = 0.05; // 动态血量变化速度,越大越快。 +var _currentHp = null; // 当前正在变化的hp值 +var _lastStatus = null; // 上次人物的属性对象 + +// 注册一个 +core.registerAnimationFrame('dynamicHp', true, function (timestamp) { + // 检查人物所指向的对象是否发生了变化 + // 在例如读档后,core.status.hero被重置了,此时需要将_currentHp重置为正确的生命值。 + if (_lastStatus != core.status.hero) { + _lastStatus = core.status.hero; + _currentHp = core.status.hero.hp; + } + // 如果显示的hp值和当前实际hp值不同 + if (core.status.hero.hp != _currentHp) { + // 计算hp差值,并获得下个血量变化值。 + var dis = (_currentHp - core.status.hero.hp) * speed; + if (Math.abs(dis) < 2) { + // 如果很接近了,直接设为实际hp值。 + _currentHp = core.status.hero.hp; + } else { + // 否则,改变显示值使其更接近。 + _currentHp -= dis; + } + // 然后设置状态栏中生命的显示项。 + core.setStatusBarInnerHTML('hp', _currentHp); + } +}); +``` + +### registerReplayAction + +``` +registerReplayAction: fn(name: string, func: fn(action?: string) -> bool) +注册一个录像行为 +name: 自定义名称,可用于注销使用 +func: 具体执行录像的函数,可为一个函数或插件中的函数名; +需要接受一个action参数,代表录像回放时的下一个操作 +func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。 +``` + +`registerReplayAction`允许你自己定义和注册一个录像行为。 + +在游戏时,你的所有操作会被记录为一个数组`core.status.route`,如按上就是`up`,使用破墙镐就是`item:pickaxe`,瞬移到(1,2)就是`move:1:2`,等等。 + +在录像播放时,会依次从数组中取出一个个的录像操作,并模拟执行对应的操作。这里对于每个录像操作,都是靠`registerReplayAction`进行注册的。 + +下面是一个例子: + +```js +// 当flag:abc为true时,点击屏幕(0,0),(0,1),(0,2),(0,3)会分别执行不同的公共事件。 + +// 先通过registerAction注册屏幕点击事件 +core.registerAction('onclick', 'commonEvent', function (x, y, px, py) { + // 检查是否是自由状态、是否在移动,是否存在flag。 + if (core.status.lockControl || core.isMoving() || !core.hasFlag('abc')) return false; + // 公共事件名 + var eventName = null; + if (x == 0 && y == 0) { + eventName = "公共事件0"; + } else if (x == 0 && y == 1) { + eventName = "公共事件1"; + } else if (x == 0 && y == 2) { + eventName = "公共事件2"; + } // ... 可以继续写下去。 + + // 如果存在需要执行的公共事件 + if (eventName != null) { + // 重要!往录像中写入一个自定义的`commonEvent:`项,记录执行的公共事件名称。 + // 由于录像编码时只支持部分ascii码,而eventName作为公共事件名可能带有中文, + // 因此需要使用 core.encodeBase64() 对eventName进行编码 + core.status.route.push("commonEvent:" + core.encodeBase64(eventName)); + // 插入公共事件 + core.insertCommonEvent(eventName); + return true; + } + return false; +}); + +// 注册一个录像处理行为来处理上述的`commonEvent:`项。 +core.registerReplayAction('commonEvent', function (action) { + // 参数action为此时录像播放时执行到的项目。 + // 如果不是`commonEvent:`,则不应该被此函数处理。 + if (action.indexOf('commonEvent:') !== 0) return false; + // 二次检查flag:abc是否存在 + if (!core.hasFlag('abc')) { + core.control._replay_error(action); + return true; + } + // 如果是我们的录像行为,获得具体的公共事件名。 + // 上面使用encodeBase64()编码了录像,现在同样需要解码。 + var eventName = core.decodeBase64(action.substring(12)); + // 由于录像播放中存在二次记录,还需要将action写入到当前播放过程中的录像中。 + // 这样录像播放完毕后再次直接重头播放不会出现问题。 + core.status.route.push(action); + // 执行该公共事件。 + core.insertCommonEvent(eventName); + // 继续播放录像。 + core.replay(); + // 返回true表示我们已经成功处理了此录像行为。 + return true; +}); +``` + +### registerWeather + +``` +registerWeather: fn(name: string, initFunc: fn(level: number), frameFunc?: fn(timestamp: number, level: number)) +注册一个天气 +name: 要注册的天气名 +initFunc: 当切换到此天气时的初始化;接受level(天气等级)为参数;可用于创建多个节点(如初始化雪花) +frameFunc: 每帧的天气效果变化;可接受timestamp(从页面加载完毕到当前所经过的时间)和level(天气等级)作为参数 +天气应当仅在weather层进行绘制,推荐使用core.animateFrame.weather.nodes用于节点信息。 +``` + +`registerWeather`允许你注册一个天气。 + +在游戏时,楼层属性中可以设置天气如 `["snow", 5]`,或者脚本 `core.setWeather("snow", 5)` 来切换天气。 + +下面是一个例子: + +```js +// 注册一个”血“天气,每200ms就随机在界面上的绘制红色斑点 +core.registerWeather('blood', function (level) { + // 切换到此天气时应当执行的脚本吗,如播放一个音效 + core.playSound('blood.mp3'); +}, function (timestamp, level) { + // 我们希望每200ms就界面上随机绘制 level^2 个红点,半径在0~32像素之间 + + // 检查是否经过了200ms + if (timestamp - core.animateFrame.weather.time < 200) return; + // 当且仅当在weather层上绘制 + core.clearMap('weather'); + for (var i = 0; i < level * level; ++i) { + // 随机界面中的一个点,半径在0~32之间 + var px = Math.random() * core.__PIXELS__; + var py = Math.random() * core.__PIXELS__; + var r = Math.random() * 32; + core.fillCircle('weather', px, py, r, 'red'); + } + // 设置本次天气调用的时间 + core.animateFrame.weather.time = timestamp; +}); +``` + +值得注意的是,天气当且仅当在`weather`层进行绘制,推荐使用或设置`core.animateFrame.weather.time`作为上次天气调用的时间避免太过于频繁的调用。 + +推荐使用`core.animateFrame.weather.nodes`来存储天气的节点,这样会在取消天气时自动被移除。 + +样板的云`cloud`和雾`fog`均由多个图片叠加移动实现;如果你想实现类似效果,可直接使用`core.control.__animateFrame_weather_image`作为`frameFunc`,详见样板的云雾实现。 + +另外注意的是,注册的天气无法在事件编辑器的下拉框中选择;你可以选择脚本调用`core.setWeather`,或者修改`_server/MotaAction.g4`中的`Weather_List`: + +```js +Weather_List + : '无'|'雨'|'雪'|'晴'|'雾'|'云' + /*Weather_List ['null','rain','snow','sun','fog','cloud']*/; +``` + +### registerSystemEvent + +``` +registerSystemEvent: fn(type: string, func: fn(data?: ?, callback?: fn())) +注册一个系统事件 +type: 事件名 +func: 为事件的处理函数,可接受(data,callback)参数 +``` + +`registerSystemEvent`允许你注册一个系统事件。 + +在图块属性中,存在一个`trigger`选项,可以设置图块的触发器;当玩家碰触到对应的图块时,会根据对应图块的触发器来执行对应系统事件,即`registerSystemEvent`所注册的。 + +这里的`data`为触发该图块时的`block`信息,和`core.getBlock()`的返回值相同。`callback`为执行完毕时的回调。 + +下面是一个例子: + +```js +// 假设存在一个图块,当触碰时会触发一个公共事件,且需要图块坐标作为公共事件参数。 +// 首先需要将该图块的触发器设置为`custom`。 + +// 注册一个`custom`的系统事件;当角色碰触到触发器为`custom`的图块时会被执行。 +core.registerSystemEvent("custom", function (data, callback) { + // 这里的`data`为碰触到的图块信息。 + console.log(data); + // 插入一个公共事件(如“图块触碰”),把图块坐标作为公共事件参数传入。 + core.insertCommonEvent("图块触碰", /*args*/ [data.x, data.y], data.x, data.y); + if (callback) callback(); +}) +``` + +`registerSystemEvent`系列最大的好处是,他是和“图块”相关而不是和“点”相关的。也就是说,可以任意通过`setBlock`之类的函数移动图块,不会影响到事件的触发(而这个是靠红点所无法完成的)。 + +也可以通过`registerSystemEvent`来拦截默认的系统事件,例如 `core.registerSystemEvent("battle", ...)` 可以拦截当遇到没有覆盖触发器的怪物的系统处理(即不再直接战斗)。 + +### registerEvent + +``` +registerEvent: fn(type: string, func: fn(data: ?, x?: number, y?: number, prefix?: string)) +注册一个自定义事件 +type: 事件类型 +func: 事件的处理函数,可接受(data, x, y, prefix)参数 +data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀 +``` + +`registerEvent`允许你注册一个自定义事件,即事件流中 type:xxx 的行为。 + +```js +// 注册一个type:abc事件 +core.registerEvent("abc", function (data, x, y, prefix) { + // 直接打出当前事件信息 + console.log(data.name); + core.doAction(); +}); + +// 执行一个abc事件 +// 控制台会打出 "hahaha" +// core.insertAction([{"type": "abc", "name": "hahaha"}]); +``` + +此函数一般不是很常用,但是配合“编辑器修改”(即给事件编辑器修改或者增加事件图块)会有奇效。 + +具体例子可参见[修改编辑器](editor)中的`demo - 增加新语句图块`。 + +### registerResize + +``` +registerResize: fn(name: string, func: fn(obj: ?)) +注册一个resize函数 +name: 名称,可供注销使用 +func: 可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。 +``` + +`registerResize`允许你重写一个屏幕重定位函数。 + +此函数会在游戏开始前,以及每次屏幕大小发生改变时调用。系统通过此函数来对游戏界面进行调整,如横竖屏自适应等。 + +此函数会被较少用到。 + +## 复写函数 + +样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 + +在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: + +- 不容易记得自己修改过什么,而且如果改错了很麻烦 + - 例如,直接修改了某函数加了新功能,结果过段时间发现不需要,想删掉,但是这时候已经很难找到自己改过了什么了。 + - 或者,如果代码改错了,不断往上面打补丁,也只会使得libs越来越乱,最后连自己做过什么都不记得。 +- 不容易随着新样板接档进行迁移 +- 不方便能整理成新的插件在别的塔使用(总不能让别的塔也去修改libs吧) +- …… + +好消息是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 + +函数复写的好处如下: + +- 不会影响系统原有代码。 + - 即使写错了或不需要了,也只用把插件中的函数注释或删除即可,不会对原来的系统代码产生任何影响。 +- 清晰明了。很容易方便知道自己修改过什么,尤其是可以和系统原有代码进行对比。 +- 方便整理成新的插件,给其他的塔使用。 + +一般而言,复写规则如下: + +**对xxx文件中的yyy函数进行复写,规则是`core.xxx.yyy = function (参数列表) { ... }`。** + +但是,对于`register`系列函数是无效的,例如直接复写`core.control._animationFrame_globalAnimate`函数是没有效果的。对于这种情况引入的函数,需要注册同名函数,可参见最下面的样例。 + +下面是几个例子,从简单到复杂。 + +### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 + +直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 + +```js +// 重写ui.js中的_drawBook_drawBackground函数 +core.ui._drawBook_drawBackground = function () { + // core.__PIXELS__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 + core.drawBackground(0, 0, core.__PIXELS__, core.__PIXELS__); +} +``` + +### 重写点击楼传事件 + +重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 + +```js +// 重写events.js的useFly函数,即点击楼传按钮时的事件 +core.events.useFly = function (fromUserAction) { + if (core.isMoving()) { + core.drawTip("请先停止勇士行动"); + return; + } + if (core.status.lockControl || core.status.event.id != null) return; + + if (core.canUseItem('fly')) core.useItem('fly'); + else core.drawTip("当前无法使用"+core.material.items.fly.name); +} +``` + +其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 + +### 关门时播放一个动画 + +关门是在`events.js`中的`closeDoor`函数,因此需要对其进行重写。 + +然而,我们只需要在这个函数执行之前插一句播放动画,所以并不需要重写整个函数,而是直接插入一行就行。 + +```js +var closeDoor = core.events.closeDoor; // 先把原始函数用一个变量记录下来 +core.events.closeDoor = function (x, y, id, callback) { + core.drawAnimate('closeDoor', x, y); // 播放 closeDoor 这个动画 + return closeDoor(x, y, id, callback); // 直接调用原始函数 +} +``` + +### 每次跳跃角色前播放一个音效 + +跳跃角色在`events.js`的`jumpHero`函数,因此需要对其进行重写。 + +由于只需要额外在函数执行前增加一句音效播放,所以直接插入一行即可。 + +但是需要注意的是,`jumpHero`中使用了`this._jumpHero_doJump()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。 + +```js +var jumpHero = core.events.jumpHero; // 先把原始函数用一个变量记录下来 +core.events.jumpHero = function (ex, ey, time, callback) { + core.playSound("jump.mp3"); // 播放一个音效 + return jumpHero.call(core.events, ex, ey, time, callback); // 需要使用`call`来告知this是core.events +} +``` + +详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 + +### 复写全局动画绘制函数 + +全局动画绘制在`control.js`的`_animationFrame_globalAnimate`函数。 + +注意到此函数是由`registerAnimationFrame`注册的,因此直接复写是无效的。 + +其在control.js的注册的定义如下: + +```js +// 注册全局动画函数 +this.registerAnimationFrame("globalAnimate", true, this._animationFrame_globalAnimate); +``` + +因此,可以在插件中自行注册一个**同名**的函数来覆盖原始的内容。 + +```js +// 插件中复写全局动画绘制函数 +this.myGlobalAnimate = function (timestamp) { + // ...... 实际复写的函数内容 +} + +// 注册同名(globalAnimate)函数来覆盖系统原始内容 +core.registerAnimationFrame("globalAnimate", true, "myGlobalAnimate"); +``` + + +========================================================================================== + +[继续阅读下一章:修改编辑器](editor) + + diff --git a/search.min.js b/search.min.js new file mode 100644 index 0000000..9fd1931 --- /dev/null +++ b/search.min.js @@ -0,0 +1 @@ +!function(){"use strict";function e(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'\/]/g,function(e){return n[e]})}function n(e){var n=[];return h.dom.findAll("a:not([data-nosearch])").map(function(t){var o=t.href,i=t.getAttribute("href"),r=e.parse(o).path;r&&-1===n.indexOf(r)&&!Docsify.util.isAbsolutePath(i)&&n.push(r)}),n}function t(e){localStorage.setItem("docsify.search.expires",Date.now()+e),localStorage.setItem("docsify.search.index",JSON.stringify(g))}function o(e,n,t,o){void 0===n&&(n="");var i,r=window.marked.lexer(n),a=window.Docsify.slugify,s={};return r.forEach(function(n){if("heading"===n.type&&n.depth<=o)i=t.toURL(e,{id:a(n.text)}),s[i]={slug:i,title:n.text,body:""};else{if(!i)return;s[i]?s[i].body?s[i].body+="\n"+(n.text||""):s[i].body=n.text:s[i]={slug:i,title:"",body:""}}}),a.clear(),s}function i(n){var t=[],o=[];Object.keys(g).forEach(function(e){o=o.concat(Object.keys(g[e]).map(function(n){return g[e][n]}))}),n=n.trim();var i=n.split(/[\s\-\,\\\/]+/);1!==i.length&&(i=[].concat(n,i));for(var r=0;rl.length&&(d=l.length);var p="..."+e(l).substring(f,d).replace(o,''+n+"")+"...";s+=p}}),a)){var d={title:e(c),content:s,url:f};t.push(d)}}(r);return t}function r(e,i){h=Docsify;var r="auto"===e.paths,a=localStorage.getItem("docsify.search.expires")
      ',o=Docsify.dom.create("div",t),i=Docsify.dom.find("aside");Docsify.dom.toggleClass(o,"search"),Docsify.dom.before(i,o)}function c(e){var n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,".results-panel");if(!e)return t.classList.remove("show"),void(t.innerHTML="");var o=i(e),r="";o.forEach(function(e){r+='
      \n \n

      '+e.title+"

      \n

      "+e.content+"

      \n
      \n
      "}),t.classList.add("show"),t.innerHTML=r||'

      '+y+"

      "}function l(){var e,n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,"input");Docsify.dom.on(n,"click",function(e){return"A"!==e.target.tagName&&e.stopPropagation()}),Docsify.dom.on(t,"input",function(n){clearTimeout(e),e=setTimeout(function(e){return c(n.target.value.trim())},100)})}function f(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var o=Object.keys(e).filter(function(e){return n.indexOf(e)>-1})[0];t.placeholder=e[o]}}function d(e,n){if("string"==typeof e)y=e;else{var t=Object.keys(e).filter(function(e){return n.indexOf(e)>-1})[0];y=e[t]}}function p(e,n){var t=n.router.parse().query.s;a(),s(e,t),l(),t&&setTimeout(function(e){return c(t)},500)}function u(e,n){f(e.placeholder,n.route.path),d(e.noData,n.route.path)}var h,g={},y="",m={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5},v=function(e,n){var t=Docsify.util,o=n.config.search||m;Array.isArray(o)?m.paths=o:"object"==typeof o&&(m.paths=Array.isArray(o.paths)?o.paths:"auto",m.maxAge=t.isPrimitive(o.maxAge)?o.maxAge:m.maxAge,m.placeholder=o.placeholder||m.placeholder,m.noData=o.noData||m.noData,m.depth=o.depth||m.depth);var i="auto"===m.paths;e.mounted(function(e){p(m,n),!i&&r(m,n)}),e.doneEach(function(e){u(m,n),i&&r(m,n)})};$docsify.plugins=[].concat(v,$docsify.plugins)}(); \ No newline at end of file diff --git a/source.d.ts b/source.d.ts new file mode 100644 index 0000000..75c237c --- /dev/null +++ b/source.d.ts @@ -0,0 +1,99 @@ +/** + * 图块类型 + */ +type Cls = + | 'autotile' + | 'animates' + | 'enemys' + | 'items' + | 'npcs' + | 'terrains' + | 'enemy48' + | 'npc48' + | 'tileset'; + +/** + * 所有的可动画图块类型 + */ +type AnimatableCls = Exclude; + +/** + * 道具类型 + */ +type ItemCls = 'tools' | 'items' | 'equips' | 'constants'; + +/** + * 所有的道具id + */ +type AllIds = keyof IdToNumber; + +/** + * 所有的道具数字 + */ +type AllNumbers = keyof NumberToId | 0; + +/** + * 某种类型的图块的id + */ +type AllIdsOf = keyof { + [P in keyof IdToCls as IdToCls[P] extends T ? P : never]: P; +}; + +/** + * 某种类型的道具的id + */ +type ItemIdOf = keyof { + [P in keyof ItemDeclaration as ItemDeclaration[P] extends T ? P : never]: P; +}; + +/** + * 某个道具的类型 + */ +type ItemClsOf> = ItemDeclaration[T]; + +/** + * 获取某个图块的类型 + */ +type ClsOf = IdToCls[T]; + +/** + * 某种类型的图块数字 + */ +type AllNumbersOf = IdToNumber[AllIdsOf]; + +/** + * 选取在一段字符串中的映射名称 + */ +type NameMapIn = keyof { + [P in keyof NameMap as NameMap[P] extends T ? P : never]: NameMap[P]; +}; + +/** + * 所有的怪物id + */ +type EnemyIds = AllIdsOf<'enemys' | 'enemy48'>; + +/** + * 各种图块的动画数量 + */ +interface FrameNumbers { + autotile: 4; + animates: 4; + enemys: 2; + items: 1; + npcs: 2; + terrains: 1; + enemy48: 4; + npc48: 4; + tileset: 1; +} + +/** + * 动画帧数 + */ +type FrameOf = FrameNumbers[T]; + +/** + * 所有的文件名 + */ +type SourceIds = ImageIds | AnimationIds | SoundIds | BgmIds | FontIds; diff --git a/start.md b/start.md new file mode 100644 index 0000000..48a0f59 --- /dev/null +++ b/start.md @@ -0,0 +1,129 @@ +# 快速上手 + +?> 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! + +* [新版视频教程](https://www.bilibili.com/video/BV1SB4y1p7bg?share_source=copy_web) +* [脚本教程](https://www.bilibili.com/video/BV1uL411J7yZ?share_source=copy_web) + +## 前置需求 +你需要满足如下条件才能进行制作:[样板下载地址](https://github.com/ckcz123/mota-js/releases)。 +1. 操作系统: + - Win8或更高版本,Win7则需要安装 .Net Framework + 4.0(能打开根目录的“启动服务.exe”即可) + - 其他电脑则需安装[python + 3.9.6](https://www.python.org/downloads/)或更高版本,能运行根目录的server.py即可(Windows也可以这样做)。 + - 安卓手机需要安装[HTML5安卓造塔器](http://h5mota.com/games/_client/H5motaMaker.apk),推荐搭配ES文件浏览器。 +2. [谷歌chrome浏览器](http://google.cn/chrome)或win10自带的新版Edge浏览器:其他浏览器可能导致本地服务闪退。 +3. (强烈推荐)[VScode](http://code.visualstudio.com/download):最适合HTML5项目的文本编辑器,能进行跨文件的正则搜索和替换,也能完整发挥根目录的runtime.d.ts文件的作用。 + +只要满足了上述条件,你就可以开始做自己的塔啦! + +## 启动HTTP服务 + +与编辑器闭源的RPG Maker MV/MZ不同,本样板对文件的绝大部分修改是通过网页编辑器经由本地HTTP服务完成的,这样做也有助于编辑器跨平台并最大限度地复用运行时代码,还可以让玩家在在线游戏时查看游戏工程。 + +在根目录下有一个“启动服务.exe”,运行之。(非Windows电脑则需使用命令行运行`server.py`,安卓手机则使用造塔器) + +!> 使用python启动服务的话,整个塔的绝对路径必须是全英文! + +![image](img/server.jpg) + +* 启动游戏:打开[http://127.0.0.1:1055/index.html](http://127.0.0.1:1055/index.html),同时开启多个启动服务则1056、1057顺延(下同)。你能在里面看到现在游戏的效果。 +* 启动编辑器:打开[http://127.0.0.1:1055/editor.html](http://127.0.0.1:1055/editor.html)(竖屏则为editor-mobile.html),这是您整个制作流程的核心页面 + +以下为Windows专用的一些辅助工具(位于“辅助工具”文件夹),由C#编写: + +* 便捷PS工具:能方便地替换和新增32×32、32×48素材。V2.8.1对该工具进行了大幅强化,您甚至可以指定32×16的格子尺寸,作为前述两种尺寸的过渡操作。 +* 地图生成器:识别RPG Maker魔塔的地图截图,生成HTML5魔塔的地图数据。 +* 怪物数据导出:从RPG Maker XP 1.03游戏导出怪物数据,用于HTML5魔塔或使用Excel查看。 +* RM动画导出:从RPG Maker XP 1.03游戏导出动画,用于HTML5魔塔。 +* JS代码压缩:对js代码(限es5)和音像素材(背景音乐除外)进行压缩,从而减小文件体积,加快在线游戏的加载。一般无需手动运行,发布后我们的服务器会处理的。 +* 动画编辑器:编辑`project\animates`文件夹中的动画文件,或利用图片制作全新的动画。 +* 伤害和临界值计算器、帮助文档:如题,后者会打开本文档。 + +!> **整个造塔过程中,启动服务必须全程处于开启状态!切不可手滑关闭,否则做的都是无用功!** + +![image](img/V2_8server.jpg) + +V2.7.x中,“地图生成器”因为图片文件被拆分到两个文件夹的缘故而无法再使用,该问题已在V2.8中修复,同时该工具更名为“截图识别器”。 + +V2.8中,“额外素材合并”不再在本地进行而是改在作品发布时在服务器端用python实现,并把实现原理改为“将未用到的图块透明化后,全图裁剪到可能的最小尺寸”,以避免图块id错乱的问题。 + +## 编辑器页面的结构 + +![image](img/editor.jpg) + +如上图,编辑器页面的结构分为三大部分。左边叫**数据区**,中央叫**地图区**,右侧叫**素材区**,竖屏状态下同时只能显示其中一个,需要经常来回切换。 + +请尤其注意中央下方的下拉框,您可以随时按下Z、X、…、句号键(字母键第三行)让数据区在这些模式之间切换。更多键鼠快捷操作请按下H键查看,这里列出一部分: +1. **Alt+0~9、0~9:**给素材区的图块绑定数字快捷键,并使用。(您也可以用中央下方的“最近/最常使用图块”和置顶来代替) +2. **WASD、或单击/长按四个箭头按钮:**滚动大地图,还可以单击“大地图”按钮观看全景。 +3. **Ctrl+W/A/S/Z/X/C/V/Y:**关闭、全选、保存、撤销、剪切、复制、粘贴、重做绘图等操作。 +4. **PageUp/PageDown或滚轮:**切换楼层。 +5. **ESC、Delete:**取消选中并保存、删除选中点的图块和事件。(需要保存时“保存地图”按钮也会变色提示) +6. **地图上单击、双击、右击:**地图选点、选中该点的素材并自动定位到素材区、弹出菜单(您可以进行出生点、机关门、上下楼的快速绑定等操作) +7. **地图上左键或右键拖动:**交换两个点的图块和事件、框选一些点供Ctrl+X/C/V剪切复制。 +8. **素材区最右侧的tileset区域左键拖动:**框选一批素材,供在地图区单击批量绘制或左键拖动平铺。(右击或触屏点击则是手动输入所需的宽高) +9. **事件编辑器中Ctrl+S/Z/X/C/V/Y、右击、双击等:**执行相应操作,如双击可以进入多行编辑/绘制预览/弹窗选文件/地图选点,地图选点时右击可以选多个点。 + +## 快速上手 + +针对红海塔作者,这里给出一个极简版的造塔流程,您可以据此造出一座没有任何事件(包括但不限于难度分歧、老人、木牌、商人和商店等)的塔: +1. 编辑勇士的出生点和初始属性: + 1. 滚轮切换到主塔0层,右击地图任意位置,绑定出生点为此点(会有一个大大的白色S字母显示出来)。 + 2. 按下B键切换到“全塔属性”,填写`core.firstData.hero`中勇士的各项初始属性,以及一些全局的数值(如四种血瓶和三种宝石的效果、破甲反击净化的比例等,注意“唯一英文标识符”name一定要修改!)。 + 3. 在数据区使用滚轮向下翻(您可以随时折叠全塔属性的几大部分),按需编辑下面的大量勾选框(主要就是状态栏的那些显示项)。 +2. 从素材区选择各种各样的非NPC图块绘制在地图上,如门、怪物、道具、楼梯、路障、箭头、踩灯、箱子等。每当选中一个素材时,数据区就进入了“图块属性”模式,您可以去填写道具的一些说明、以及修改其他一些图块的可通行性等。注意滑冰(触发器为ski)要画在背景层。如果您需要制作机关门,请简单地将机关门和守卫(不支持阻击怪和炸弹)画在地图上,再右击机关门快速绑定即可。(看到机关门左下角出现橙色小方块、守卫们左下角出现黄色小方块即说明绑定成功) +3. 如果您需要制作多个楼层,只需按下Z键将数据区切换到“地图编辑”模式,然后“新建空白地图”即可,不同楼层之间简单地通过楼梯来往返,您可以将楼梯画在地图上再右击快速绑定即可。(看到楼梯左下角出现绿色小方块即说明绑定成功)各个楼层的属性可以通过按下V键将数据区切换到“楼层属性”模式来填写,如能否使用楼传、是否为地下层、画面色调、宝石血瓶倍率等。 +4. 从素材区选择您所使用的各种怪物,在数据区填写它们的各项属性,其中“特殊属性”是通过多选框来编辑的。 +5. 游戏胜利的触发:滚轮切换到样板1层,单击地图上的公主,按下Ctrl+C复制。滚轮切换回您的boss层,(记得给boss设置不可被炸哦)单击boss身后的任何一个空格子,按下Ctrl+V粘贴即可。这样玩家触碰到公主游戏就会胜利。 + +## 控制台调试 + +![image](img/console2.jpg) + +HTML5项目都是可以进行控制台调试的,下面以edge浏览器为例介绍其部分操作: + +首先按下F12键(部分键盘没有此键或需与Fn键一起按)或Ctrl+Shift+I,打开开发人员界面。 + +可以看到它分为“元素”、“控制台”、“调试程序”、“性能”等多个部分: +1. **元素:**您可以在这里对游戏和编辑器的各HTML和css元素进行查看和临时的修改,譬如您想观察游戏在竖屏的表现,只需将窗口拉到瘦高。 +2. **性能:**您可以在这里对游戏的任何一段脚本进行性能分析,观察其中各行的执行频率和耗时,从而确定优化的方向。 +3. **调试程序:**您可以在这里查看游戏的源码,包括project文件夹的functions.js和plugin.js(脚本编辑和插件编写)以及整个libs文件夹,并进行断点调试。 +4. **控制台:**最常使用的部分,当编辑器或游戏打不开、卡死、或者不按您的预想运作时您就需要查看这里的报错信息。这里也是各种API输入的地方,譬如上图中您可以看到全部的插件函数。 + +在控制台中,我们可以输入一些命令对游戏进行调试,常见的命令有: + +- `core.status.floorId` 获得当前层的floorId。 +- `core.status.thisMap` 获得当前地图信息。例如`core.status.thisMap.blocks`可以获得当前层所有图块。 +- `core.floors` 获得所有楼层的初始信息。例如`core.floors[core.status.floorId].events`可以获得当前层所有事件。 +- `core.status.hero` (或简写为hero)获得当前勇士状态信息。例如`core.status.hero.atk`就是当前勇士的攻击力数值。 +- `core.material.enemys` 获得所有怪物信息。例如`core.material.enemys.greenSlime`就是获得绿色史莱姆的属性数据。 +- `core.material.items` 获得所有道具的信息。例如`core.material.items.pickaxe`就是获得破墙镐的信息。 +- `core.debug()` 开启调试模式;此模式下可以按住Ctrl键进行穿墙。 +- `core.updateStatusBar()` 立刻更新状态栏、地图显伤并触发自动事件。 +- `core.setStatus('atk', 1000)` 直接设置勇士的某项属性。本句等价于 `core.status.hero.atk = 1000`。 +- `core.getStatus('atk')` 返回勇士当前某项属性数值。本句等价于 `core.status.hero.atk`。 +- `core.setItem('pickaxe', 10)` 直接设置勇士某个道具的个数。这里可以需要写道具的ID。 +- `core.getItem('pickaxe', 2)` 令勇士获得两个破墙镐。 +- `core.itemCount('pickaxe')` 返回勇士某个道具的个数。 +- `core.hasItem('pickaxe')` 返回勇士是否拥有某个道具。等价于`core.itemCount('pickaxe')>0`。 +- `core.getEquip(0)` 返回0号装备类型(武器)的当前装备的itemId,不存在则返回`null` +- `core.hasEquip('sword1')` 返回某个装备当前是否处于被装备状态 +- `core.setFlag('xxx', 1)` 设置某个flag/自定义变量的值,可以设为`null`表示删除。 +- `core.getFlag('xxx', 10)` 获得某个flag/自定义变量的值;如果该项不存在(未被定义),则返回第二个参数的值。 +- `core.hasFlag('xxx')` 返回是否存在某个变量且不为0。等价于`!!core.getFlag('xxx', 0)`。 +- `core.removeFlag('xxx')` 删除某个flag/自定义变量 +- `core.insertAction(list)` 执行一段自定义事件。比如 `core.insertAction(["剧情文本"])` 将执行一个剧情文本显示事件。 +- `core.changeFloor('MT2', 'downFloor')` 立刻执行楼层切换到MT2层的下楼点位置。 +- `core.changeFloor('MT5', null, {'x': 4, 'y': 7})` 立刻切换楼层到MT5层的(4,7)点。 +- `core.getBlock(3, 5, 'MT1')` 获得当前地图上某一个块的信息。第三个参数为floorId,可省略表示当前楼层。 +- `core.getBlockId(3, 5, 'MT1')` 获得当前地图上某一个点的图块ID。第三个参数为floorId,可省略表示当前楼层。 +- `core.resetMap()` 重置当前层地图。 +- …… + +更多关于控制台调试和脚本的信息可参见[脚本](script)和[API列表](api)。 + +========================================================================================== + +[继续阅读下一章:元件说明](element) diff --git a/status.d.ts b/status.d.ts new file mode 100644 index 0000000..72fec89 --- /dev/null +++ b/status.d.ts @@ -0,0 +1,974 @@ +/** + * 怪物buff缓存 + */ +interface EnemyBuffCache { + /** + * 生命值提升量 + */ + hp_buff: number; + + /** + * 攻击提升量 + */ + atk_buff: number; + + /** + * 防御提升量 + */ + def_buff: number; + + /** + * 支援信息 + */ + guards: [number, number, string][]; +} + +interface CheckBlockStatus { + /** + * 捕捉信息 + */ + ambush: Record; + + /** + * 阻击信息 + */ + repulse: Record; + + /** + * 每点的伤害,小于等于0会不显示 + */ + damage: Record; + + /** + * 是否需要重算 + */ + needCache: boolean; + + /** + * 每点的伤害类型 + */ + type: Record>; + + /** + * 缓存信息,是每个怪物受到的光环加成 + */ + cache: Record>; + + /** + * 光环信息 + */ + halo: Record; +} + +interface DamageStatus { + /** + * v2优化下当前的偏移横坐标,单位格子 + */ + posX: number; + + /** + * v2优化下当前的偏移纵坐标,单位格子 + */ + posY: number; + + /** + * 显示的伤害信息 + */ + data: DamageStatusData[]; + + /** + * 地图伤害或其它在地图上显示的文字 + */ + extraData: DamageStatusExtraData[]; + + /** + * 不同方向伤害不同的信息 + */ + dir: DamageDirData[]; +} + +interface DamageStatusData { + /** + * 显示的文字 + */ + text: string; + + /** + * 显示横坐标,单位像素 + */ + px: number; + + /** + * 显示纵坐标,单位像素 + */ + py: number; + + /** + * 文字的颜色 + */ + color: Color; +} + +interface DamageStatusExtraData extends DamageStatusData { + /** + * 文字的不透明度 + */ + alpha: number; +} + +interface DamageDirData { + x: number; + y: number; + dir: Dir; + color: Color; +} + +interface AutomaticRouteStatus { + /** + * 勇士是否正在移动 + */ + autoHeroMove: boolean; + + /** + * 不太清楚用处 + */ + autoStep: number; + + /** + * 自动寻路中已经走过的步数 + */ + movedStep: number; + + /** + * 自动寻路的总步数 + */ + destStep: number; + + /** + * 自动寻路的目标横坐标 + */ + destX: number; + + /** + * 自动寻路的目标纵坐标 + */ + destY: number; + + /** + * 自动寻路绘图时的偏移横坐标,单位像素 + */ + offsetX: number; + + /** + * 自动寻路绘图时的偏移纵坐标,单位像素 + */ + offsetY: number; + + /** + * 自动寻路的路线 + */ + autoStepRoutes: AutoStep[]; + + /** + * 剩下的自动寻路路线 + */ + moveStepBeforeStop: AutoStep[]; + + /** + * 上一步的勇士方向 + */ + lastDirection: Dir; + + /** + * 光标界面(按下E时的界面)的光标横坐标 + */ + cursorX: number; + + /** + * 光标界面(按下E时的界面)的光标纵坐标 + */ + cursorY: number; + + /** + * 是否瞬间移动 + */ + moveDirectly: boolean; +} + +interface AutoStep { + /** + * 当前步的步数 + */ + step: number; + + /** + * 当前步走向的方向 + */ + direction: Dir; +} + +interface ReplaySaveBase { + /** + * 录像播放时,剩下的要播放的录像内容 + */ + toReplay: string[]; + + /** + * 录像播放时,录像的完整信息 + */ + totalList: string[]; + + /** + * 不太清楚用处,应该是与录像回退有关的 + */ + steps: number; +} + +interface ReplayStatus extends ReplaySaveBase { + /** + * 当前是否正在播放录像,同core.isReplaying() + */ + replaying: boolean; + + /** + * 当前录像有没有暂停 + */ + pausing: boolean; + + /** + * 当前是否正在某段动画中 + */ + animate: boolean; + + /** + * 录像播放是否失败 + */ + failed: boolean; + + /** + * 当前的录像播放速度 + */ + speed: number; + + /** + * 录像的回退信息 + */ + save: ReplaySave[]; +} + +interface ReplaySave { + /** + * 回退的存档信息 + */ + data: Save; + + /** + * 回退的录像信息 + */ + replay: ReplaySaveBase; +} + +interface TextAttribute { + /** + * 文本框的位置 + */ + position: TextPosition; + + /** + * 文本的左右对齐方式 + */ + align: 'left' | 'center' | 'right'; + + /** + * 偏移像素 + */ + offset: number; + + /** + * 标题颜色 + */ + title: RGBArray; + + /** + * 背景颜色 + */ + background: RGBArray | ImageIds; + + /** + * 文字颜色 + */ + text: RGBArray; + + /** + * 标题字体大小 + */ + titlefont: number; + + /** + * 正文字体大小 + */ + textfont: number; + + /** + * 是否加粗 + */ + bold: boolean; + + /** + * 打字机时间,每隔多少毫秒显示一个字 + */ + time: number; + + /** + * 字符间距 + */ + letterSpacing: number; + + /** + * 淡入淡出时间 + */ + animateTime: number; + + /** + * 行距 + */ + lineHeight: number; +} + +interface StatusStyle { + /** + * 游戏的边框颜色 + */ + borderColor: Color; + + /** + * 状态栏文字的颜色 + */ + statusBarColor: Color; + + /** + * 楼层切换样式,css字符串 + */ + floorChangingStyle: string; + + /** + * 全局字体 + */ + font: string; +} + +interface GlobalAttribute extends StatusStyle { + /** + * 装备栏名称 + */ + equipName: string[]; +} + +interface FloorAnimate { + /** + * 图片的目标画布 + */ + canvas: 'bg' | 'fg'; + + /** + * 图片的名称 + */ + name: ImageIds; + + /** + * 绘制横坐标 + */ + x: number; + + /** + * 绘制纵坐标 + */ + y: number; + + /** + * 裁剪横坐标 + */ + sx?: number; + + /** + * 裁剪纵坐标 + */ + sy?: number; + + /** + * 裁剪宽度 + */ + w?: number; + + /** + * 裁剪高度 + */ + h?: number; + + /** + * 绘制帧数 + */ + frame?: number; + + /** + * 图片翻转 + */ + reverse?: ':x' | ':y' | ':o'; + + /** + * 是否禁用 + */ + disable?: boolean; +} + +interface BoxAnimate { + /** + * 动画的帧数 + */ + animate: number; + + /** + * 背景的高度 + */ + bgHeight: number; + + /** + * 背景的宽度 + */ + bgWidth: number; + + /** + * 背景的左上角横坐标 + */ + bgx: number; + + /** + * 背景的左上角纵坐标 + */ + bgy: number; + + /** + * 动画图片的高度 + */ + height: 32 | 48; + + /** + * 图片信息 + */ + img: HTMLImageElement; + + /** + * 这个图块的图片在其素材图片的纵坐标 + */ + pos: number; + + /** + * 图块的横坐标 + */ + x: number; + + /** + * 图块的纵坐标 + */ + y: number; +} + +interface BigImageBoxAnimate { + /** + * 大图片的贴图信息 + */ + bigImage: HTMLImageElement; + + /** + * 贴图的朝向 + */ + face: Dir; + + /** + * 中心横坐标 + */ + centerX: number; + + /** + * 中心纵坐标 + */ + centerY: number; + + /** + * 最大宽度 + */ + max_width: number; + + /** + * 绘制到的画布 + */ + ctx: CtxRefer; +} + +interface AnimateObj { + /** + * 动画名称 + */ + name: AnimationIds; + + /** + * 动画的唯一标识符 + */ + id: number; + + /** + * 动画信息 + */ + animate: Animate; + + /** + * 中心横坐标 + */ + centerX: number; + + /** + * 中心纵坐标 + */ + centerY: number; + + /** + * 当前帧数 + */ + index: number; + + /** + * 回调函数 + */ + callback: () => void; +} + +interface ActionsPreview { + /** + * 大地图中当前是否正在拖拽 + */ + dragging: boolean; + + /** + * 大地图中是否允许拖拽 + */ + enabled: boolean; + + /** + * 大地图中当前是否已经按下了鼠标 + */ + prepareDragging: boolean; + + /** + * 当前鼠标的横坐标 + */ + px: number; + + /** + * 当前鼠标的纵坐标 + */ + py: number; +} + +interface RouteFolding { + /** + * 录像折叠信息中的勇士信息 + */ + hero: Omit, 'steps'>; + + /** + * 折叠的长度 + */ + length: number; +} + +/** + * 初始游戏状态 + */ +interface InitGameStatus { + /** + * 是否开始了游戏 + */ + played: boolean; + + /** + * 游戏是否结束 + */ + gameOver: boolean; + + /** + * 所有楼层的地图信息 + */ + maps: { + [P in FloorIds]: Floor

      ; + }; + + /** + * 背景图块 + */ + bgmaps: Record; + + /** + * 前景图块 + */ + fgmaps: Record; + + /** + * 以坐标列举的图块 + */ + mapBlockObjs: Record>; + + /** + * 伤害显示信息 + */ + damage: DamageStatus; + + /** + * 是否锁定了用户控制 + */ + lockControl: boolean; + + /** + * 勇士移动状态,每个数字干啥的自己去libs翻,这东西太复杂了,不过应该不会有人用这个东西吧( + */ + heroMoving: number; + + /** + * 勇士是否停下了 + */ + heroStop: boolean; + + /** + * 自动寻路状态 + */ + automaticRoute: DeepReadonly; + + /** + * 按键按下的时间,用于判定双击 + */ + downTime: number; + + /** + * ctrl键是否倍按下 + */ + ctrlDown: boolean; + + /** + * 当前录像信息 + */ + route: string[]; + + /** + * 当前的回放状态 + */ + replay: DeepReadonly; + + /** + * 当前的所有全局商店 + */ + shops: Record>; + + /** + * 当前的事件状态,样板最大的败笔之一,离谱到逆天 + */ + event: EventStatusOf; + + /** + * 当前的所有自动事件 + */ + autoEvents: DeepReadonly; + + /** + * 当前的全局剧情文本设置 + */ + textAttribute: TextAttribute; + + /** + * 部分全局属性,会跟随存档 + */ + globalAttribute: GlobalAttribute; + + /** + * 色调的颜色 + */ + curtainColor: Color; + + /** + * 全局动画对象 + */ + globalAnimateObjs: Block< + IdToNumber[AllIdsOf>] + >[]; + + /** + * 楼层贴图 + */ + floorAnimateObjs: FloorAnimate[]; + + /** + * 所有的BoxAnimate信息 + */ + boxAnimateObjs: (BoxAnimate | BigImageBoxAnimate)[]; + + /** + * 所有的自动元件动画 + */ + autotileAnimateObjs: Block]>[]; + + /** + * 全局动画状态,每经过一个全局动画时间便加一 + */ + globalAnimateStatus: number; + + /** + * 所有绘制的动画 + */ + animateObjs: AnimateObj[]; + + /** + * 当前难度 + */ + hard: string; + + /** + * 勇士的中心 + */ + heroCenter: Record<'px' | 'py', number>; + + /** + * 当前按下的按键 + */ + holdingKeys: number[]; + + /** + * id转数字 + */ + id2number: IdToNumber; + + /** + * 数字转图块 + */ + number2block: { + [P in keyof NumberToId]: Block

      ; + }; + + /** + * 大地图中的拖拽处理 + */ + preview: ActionsPreview; + + /** + * 录像折叠信息 + */ + routeFolding: Record<`${LocString},${FirstCharOf

      }`, RouteFolding>; +} + +/** + * 运行时的游戏状态 + */ +interface GameStatus extends InitGameStatus { + /** + * 当前层的floorId + */ + floorId: FloorIds; + + /** + * 获得当前楼层信息,等价于core.status.maps[core.status.floorId] + */ + thisMap: Floor; + + /** + * 地图伤害 + */ + checkBlock: Readonly; + + /** + * 当前勇士状态信息。例如core.status.hero.atk就是当前勇士的攻击力数值 + */ + hero: HeroStatus; +} + +interface Follower { + /** + * 跟随者的图片id + */ + name: ImageIds; +} + +interface HeroStatistics { + /** + * 击败的怪物数量 + */ + battle: number; + + /** + * 由于战斗损失的生命值 + */ + battleDamage: number; + + /** + * 当前游戏时间 + */ + currentTime: number; + + /** + * 获得的总经验值 + */ + exp: number; + + /** + * 由于地图伤害损失的生命值 + */ + extraDamage: number; + + /** + * 总共损失的生命值 + */ + hp: number; + + /** + * 由于瞬移少走的步数 + */ + ignoreSteps: number; + + /** + * 总共获得的金币数 + */ + money: number; + + /** + * 瞬移次数 + */ + moveDirectly: number; + + /** + * 中毒损失的生命值 + */ + poisonDamage: number; + + /** + * 本次游戏的开始时间 + */ + startTime: number; + + /** + * 游戏总时间 + */ + totalTime: number; +} + +/** + * 勇士状态 + */ +interface HeroStatus { + /** + * 勇士停止时及对话框中是否启用帧动画 + */ + animate: boolean; + + /** + * 勇士生命值 + */ + hp: number; + + /** + * 勇士生命回复 + */ + hpmax: number; + + /** + * 勇士的攻击 + */ + atk: number; + + /** + * 勇士的防御 + */ + def: number; + + /** + * 勇士的魔防 + */ + mdef: number; + + /** + * 勇士的等级 + */ + lv: number; + + /** + * 勇士的经验 + */ + exp: number; + + /** + * 勇士的金币 + */ + money: number; + + /** + * 勇士的额外攻击 + */ + mana: number; + + /** + * 勇士的魔法上限 + */ + manamax: number; + + /** + * 勇士的名称 + */ + name: string; + + /** + * 勇士移动过的步数 + */ + steps: number; + + /** + * 勇士的图片 + */ + image: ImageIds; + + /** + * 当前勇士的装备 + */ + equipment: ItemIdOf<'equips'>[]; + + /** + * 勇士当前的位置 + */ + loc: DiredLoc; + + /** + * 当前的变量 + */ + flags: Flags; + + /** + * 勇士的跟随者 + */ + followers: Follower[]; + + statistics: HeroStatistics; + + /** + * 勇士拥有的道具 + */ + items: { + [P in Exclude]: Record, number>; + }; + + /** + * 勇士学习的特技 + */ + special: { + num: number[]; + last: number[]; + [k: string]: any; + }; + + x?: number; + y?: number; + floorId?: FloorIds; +} diff --git a/ui-editor.md b/ui-editor.md new file mode 100644 index 0000000..d7deb6a --- /dev/null +++ b/ui-editor.md @@ -0,0 +1,864 @@ +# UI编辑器 + +## 基础操作 + +打开编辑界面后即可编辑,通过添加新操作可以向ui中添加新的内容。点击一个操作后可以选中该操作,被选中的操作将呈现黄色,可按住ctrl多选。此时点击鼠标右键,可以打开右键菜单,包括在上方插入、复制等操作。执行在上方或下方插入时,会在鼠标点击的操作上方或下方插入,执行复制或剪切时会复制或剪切所有被选中的操作,被剪切的操作将会呈现灰色。当ui编辑到一半时,可以点击保存按钮进行保存,当ui未保存时,保存按钮应当显示为黄色,点击保存后,如果保存成功,按钮会短暂变成绿色。当ui编辑完成后,可以点击预览按钮从头开始预览ui,确认符合预期后,可以点击编译按钮将操作编译为js代码,然后将该代码按照编译完成的注释操作,将代码放入插件中,在需要的地方插入即可。 + +## 注意事项 + +- 设置过渡不会立刻生效,会在下一个动画帧中生效,因此设置之后可能需要大概20~50ms的等待操作 +- 画布无法更改名称 +- 对于绘制成段文本,如果设置了打字机速度,那么两个该操作不能同时进行 +- 临时禁用只对预览有效,编译时被临时禁用的操作仍会被编译 +- 遇到问题或bug、建议等请在造塔群及时反馈 + +## 编译 + +该操作会将当前编辑器显示的所有操作编译为js代码。编译完成后,应当放到插件中一个形式为`this.xxx = function () { ui内容 }`的函数中,在需要显示该ui时,只需执行代码`core.xxx()` + +示例: + +```js +// 插件中 +this.myUi = function() { + // 请复制以下内容至插件中以使用 + // 使用案例: + /* + 插件中: + this.showMyUi = function () { + 函数内容 + } + 调用时: + core.showMyUi(); + */ + var _sprite_0 = new Sprite(100, 100, 100, 100, 200, 'game', '_sprite_0'); + _sprite_0.setCss('display: none;'); + + action_0_6(); + + function action_0_6() { + _sprite_0.setCss('display: block;'); + _sprite_0.move(100, 100); + _sprite_0.resize(100, 100); + _sprite_0.canvas.style.zIndex = '200'; + core.setAlpha(_sprite_0.context, 0.5); + core.fillEllipse(_sprite_0.context, 50, 50, 30, 50, 0, ''); + var ctx = _sprite_0.context; + ctx.moveTo(0, 20); + ctx.bezierCurveTo(10, 10, 50, 50, 50, 100); + ctx.strokeStyle = '#fff'; + ctx.lineWidth = 5; + ctx.stoke(); + _sprite_0.context.globalCompositeOperation = 'difference'; + core.fillRect(_sprite_0.context, 0, 0, 100, 100, ''); + setTimeout(action_7_8, 1000); + } + function action_7_8() { + var res = _sprite_0.canvas.style.transition; + if (res !== '') res += ', '; + res += 'left 300ms ease-out 0ms'; + _sprite_0.canvas.style.transition = res; + setTimeout(action_9_9, 50); + } + function action_9_9() { + _sprite_0.resize(100, 100, true); + } +} + +// 需要显示ui时 +core.myUi(); +``` + +## 预览 + +预览分为两种,一种是编辑时的即时预览,这种预览会显示所有的画布,并无视等待和删除,确保你能够看到所有的绘制内容,如果你想要隐藏某个操作,可以打开该操作的详细信息,然后将临时禁用勾上,这样就能隐藏该操作了。 + +第二种是点击预览按钮时发生的,这种预览会尽可能地还原编译后的执行结果,让你能够完整地预览ui。 + +## 保存 + +显而易见,该操作可以保存现在正在编辑的ui。注意,ui会保存在样板根目录下的_ui文件夹,以base64的形式进行保存。当你编辑了ui时,保存按钮会呈现黄色,说明此时你还没有保存,此时切出编辑页面会弹出提示。点击保存后,会出现短暂的保存中字样(保存快的话可能看不清),如果保存成功,会出现成功字样,并会将背景短暂地改成绿色 + +## 临时禁用 + +该操作可以让你能够禁用某个操作,让你在不删除操作的情况下看到删除之后的效果。该操作只对预览有效,这意味着编译仍会编译被禁用的操作。 + +## 编辑 + +### 创建画布 + +| 事件名称 | 创建画布 | +| --- | --- | +| 事件原形 | 创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深) | +| 功能描述 | 创建一个新的画布 | +| 输入参数 1 | 名称:画布的名称 | +| 输入参数 2 | 横坐标:画布的起点横坐标(左上角) | +| 输入参数 3 | 纵坐标:画布的起点纵坐标(左上角) | +| 输入参数 4 | 宽度:画布的宽度 | +| 输入参数 5 | 高度:画布的高度 | +| 输入参数 6 | 纵深:画布的显示优先级,数值越大优先级越高 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 创建画布 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +…… +``` + +### 等待 + +| 事件名称 | 等待 | +| --- | --- | +| 事件原形 | 等待(毫秒数) | +| 功能描述 | 等待一定时间后再执行后续操作 | +| 输入参数 1 | 毫秒数:等待的时间长度 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 等待 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +等待(毫秒数); +``` + +### 设置过渡 + +| 事件名称 | 设置过渡 | +| --- | --- | +| 事件原形 | 设置过渡(作用画布, 作用效果, 过渡时间, 过渡方式, 延迟) | +| 功能描述 | 指定画布前两个命令之间的渐变式变化 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 作用效果:可用于transition的style | +| 输入参数 3 | 过渡时间:变化的时间长度 | +| 输入参数 4 | 过渡方式:过渡方式,包括ease-out ease-in ease-in-out linear bezier-curve()等 | +| 输入参数 5 | 延迟:当指定属性变化后,多长时间后开始过渡 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +| 常见作用效果 | 说明 | +| --- | --- | +| left | x轴方向的位置 | +| top | y轴方向的位置 | +| opacity | 不透明度 | +| background-color | 背景色 | +| transform | 2D和3D变换 | +| filter | 滤镜 | +| border | 边框 | + +| 过渡方式 | 说明 | +| --- | --- | +| ease-out | 先快后慢 | +| ease-in | 先慢后快 | +| ease-in-out | 慢-快-慢 | +| linear | 线性变化(匀速) | +| bezier-curve() | 指定按照贝塞尔曲线的形式变化,1表示到达终点,0表示起点 | + +例: +```js +/* 这是UI编辑器中 设置过渡 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置过渡(作用画布, 作用效果, 过渡时间, 过渡方式, 延迟); +移动画布(名称, 横坐标, 纵坐标); +``` + +### 删除画布 + +| 事件名称 | 删除画布 | +| --- | ----------- | +| 事件原形 | 删除画布(作用画布) | +| 功能描述 | 删除一个已经存在的画布 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 画布已经存在 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 删除画布 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +删除画布(名称); +``` + +### 移动画布 + +| 事件名称 | 移动画布 | +| --- | --- | +| 事件原形 | 移动画布(作用画布, 横坐标, 纵坐标, 移动模式) | +| 功能描述 | 移动指定画布一定指定距离 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 横坐标:画布的起点横坐标(左上角) | +| 输入参数 3 | 纵坐标:画布的起点纵坐标(左上角) | +| 输入参数 4 | 移动模式:是,则为相对坐标模式,否则为绝对坐标模式 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +| 移动模式 | 说明 | +| --- | --- | +| 绝对坐标 | 横纵坐标为画布移动后左上角的坐标位置 | +| 相对坐标 | 横纵坐标为画布在横向与纵向上移动的距离值 | + +例: +```js +/* 这是UI编辑器中 移动画布 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +移动画布(名称, 横坐标, 纵坐标,false); +``` + +### 缩放画布 + +| 事件名称 | 缩放画布 | +| --- | --- | +| 事件原形 | 缩放画布(作用画布, 宽度, 高度, 修改样式) | +| 功能描述 | 调整画布至指定尺寸大小 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 宽度:画布新的宽度尺寸 | +| 输入参数 3 | 高度:画布新的高度尺寸 | +| 输入参数 4 | 修改样式:如果是,那么只会修改css的长宽,导致模糊,如果不是,那么会修改画布的长宽,清晰,但会清空已绘制内容 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 缩放画布 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +缩放画布(名称, 宽度, 高度, false); +``` + +### 旋转画布 + +| 事件名称 | 旋转画布 | +| --- | --- | +| 事件原形 | 旋转画布(作用画布, 中心横坐标, 中心纵坐标, 角度) | +| 功能描述 | 指定画布以指定的中心点旋转一定的角度 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 中心横坐标:旋转中心的横坐标 | +| 输入参数 3 | 中心纵坐标:旋转中心的纵坐标 | +| 输入参数 4 | 角度:旋转的角度,正数顺时针旋转,负数逆时针旋转 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 旋转画布 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +旋转画布(名称, 中心横坐标, 中心纵坐标, 角度); +``` + +### 擦除画布 + +| 事件名称 | 擦除画布 | +| --- | ----------- | +| 事件原形 | 擦除画布(作用画布, 横坐标, 纵坐标, 宽度, 高度) | +| 功能描述 | 擦除画布指定区域内的所有内容 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 横坐标:区域的起点横坐标(左上角) | +| 输入参数 3 | 纵坐标:区域的起点纵坐标(左上角) | +| 输入参数 4 | 宽度:区域的宽度 | +| 输入参数 5 | 高度:区域的高度 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +如果不填的话,默认擦除全部 + +例: +```js +/* 这是UI编辑器中 擦除画布 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +擦除画布(名称, 横坐标, 纵坐标, 宽度, 高度); +``` + +### 设置CSS效果 + +| 事件名称 | 设置CSS效果 | +| --- | ----------- | +| 事件原形 | 设置CSS效果(作用画布, CSS效果) | +| 功能描述 | 修改指定画布的样式 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | CSS效果:编辑框内输入自定义的CSS效果 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +| 常用CSS效果 | 说明 | +| --- | --- | +| opacity | 不透明度,一个浮点型数字,在0~1区间内 | +| background-color | 背景色,可以使用`#rgba #rrggbbaa #rgb #rrggbb rgb(r,g,b) rgba(r,g,b,a) 英文单词`共7种形式 | +| background-image | 背景图片,可使用`url(project/images/image.png)`的形式 | +| box-shadow | 元素阴影,形式为 `x偏移 y偏移 阴影大小 颜色, x偏移 y偏移 阴影大小 颜色` | +| transform | 元素的2D和3D转换,用法过多,请百度搜索或在mdn搜索 | +| filter | 元素滤镜,同上 | + +例: +```js +/* 这是UI编辑器中 设置CSS效果 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置CSS效果(名称, CSS效果); +``` + +### 设置混合方式 + +| 事件名称 | 设置混合方式 | +| --- | ----------- | +| 事件原形 | 设置混合方式(作用画布, 混合方式) | +| 功能描述 | 设置新绘制的内容与已绘制的内容间的叠加方式 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 混合方式:设置画布的混合方式 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +混合方式说明:包括`color color-burn color-dodge copy darken destination-atop destination-in destination-out destination-over difference exclusion hard-light hue lighten lighter luminosity multiply overlay saturation screen soft-light source-atop source-in source-out source-over xor`共26种,默认为source-over,其余效果请自行百度或尝试 + +例: +```js +/* 这是UI编辑器中 设置混合方式 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置混合方式(名称, 混合方式); +``` + +### 设置绘制滤镜 + +| 事件名称 | 设置绘制滤镜 | +| --- | ----------- | +| 事件原形 | 设置绘制滤镜(作用画布, 滤镜) | +| 功能描述 | 设置在绘制时的滤镜,不影响已绘制内容 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 滤镜:编辑框内输入自定义的滤镜 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +| 常见滤镜 | 说明 | +| --- | --- | +| blur | 高斯模糊,用法示例:blur(5px) | +| brightness | 亮度,100%为原始亮度,用法示例:brightness(120%) | +| contrast | 对比度,100%为原始对比度,用法示例:contrast(80%) | +| grayscale | 灰度,0%为原始灰度,设为100%将会变成黑白,用法示例:grayscale(75%) | +| opacity | 不透明度,100%为原始不透明度,设为0%将会完全透明,用法示例:opacity(80%) | + +滤镜填写示例:`blur(5px)brightness(120%)grayscale(50%)`,注意会完全覆盖之前的效果,也就是说之前的效果会全部失效 + +例: +```js +/* 这是UI编辑器中 设置绘制滤镜 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置绘制滤镜(名称, 滤镜); +``` + +### 设置阴影 + +| 事件名称 | 设置阴影 | +| --- | ----------- | +| 事件原形 | 设置阴影(作用画布, 阴影模糊, 阴影颜色, 阴影横坐标, 阴影纵坐标) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 阴影模糊:阴影的高斯模糊,与设置绘制滤镜的blur相同 | +| 输入参数 3 | 阴影颜色:与设置css效果中对background-color的描述相同 | +| 输入参数 4 | 阴影横坐标:阴影偏移绘制位置的横坐标 | +| 输入参数 5 | 阴影纵坐标:阴影偏移绘制位置的纵坐标 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 设置阴影 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置阴影(名称, 阴影模糊, 阴影颜色, 宽度, 高度); +``` + +### 设置文本属性 + +| 事件名称 | 设置文本属性 | +| --- | ----------- | +| 事件原形 | 设置文本属性(作用画布, 文本方向, 文本对齐, 文本基线) | +| 功能描述 | 设置文本方向、文本对齐、文本基线 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 文本方向:文字阅读的方向 | +| 输入参数 3 | 文本对齐:文本水平对齐的方向 | +| 输入参数 4 | 文本基线:文本竖直对齐的方向 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +| 文本方向 | 说明 | +| --- | --- | +| ltr | 从左往右 | +| rtl | 从右往左 | + +| 文本对齐 | 说明 | +| --- | --- | +| left | 左对齐 | +| center | 居中对齐 | +| right | 右对齐 | + +| 文本基线 | 说明 | +| --- | --- | +| bottom | 底部对齐 | +| middle | 居中对齐 | +| top | 顶部对齐 | + +例: +```js +/* 这是UI编辑器中 设置文本信息 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置文本属性(名称, 文本方向, 文本对齐, 文本基线); +``` + +### 设置不透明度 + +| 事件名称 | 设置不透明度 | +| --- | ----------- | +| 事件原形 | 设置不透明度(作用画布, 文本不透明度) | +| 功能描述 | 设置画布之后绘制的不透明度 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 不透明度:要设置到的不透明度 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +该操作对已绘制内容没有影响,如果想要对已绘制内容也产生影响,请使用设置css + +例: +```js +/* 这是UI编辑器中 设置不透明度 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +设置不透明度(名称, 不透明度); +``` + +### 保存画布属性 + +| 事件名称 | 保存画布属性 | +| --- | ----------- | +| 事件原形 | 保存画布属性(作用画布) | +| 功能描述 | 保存画布属性,注意之会保存之前设置的属性,如文本属性,不会保存绘制内容 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 保存画布属性 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +保存画布属性(名称); +``` + +### 回退画布属性 + +| 事件名称 | 回退画布属性 | +| --- | ----------- | +| 事件原形 | 回退画布属性(作用画布) | +| 功能描述 | 回退画布属性,注意只会回退画布的属性,如文本属性,不会回退绘制内容 | +| 输入参数 1 | 作用画布:画布的名称 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 回退画布属性 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +回退画布属性(名称); +``` + +### 绘制直线 + +| 事件名称 | 绘制直线 | +| --- | ----------- | +| 事件原形 | 绘制直线(作用画布, 起点横坐标, 起点纵坐标, 终点横坐标, 终点纵坐标, 连线宽度, 连线颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 起点横坐标: | +| 输入参数 3 | 起点纵坐标: | +| 输入参数 4 | 终点横坐标: | +| 输入参数 4 | 终点纵坐标: | +| 输入参数 7 | 连线宽度: | +| 输入参数 8 | 连线颜色: | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制直线 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制直线(名称, 起点横坐标, 起点纵坐标, 终点横坐标, 终点纵坐标, 连线宽度, 连线颜色); +``` + +### 绘制弧线 + +| 事件名称 | 绘制弧线 | +| --- | ----------- | +| 事件原形 | 绘制弧线(作用画布, 中心横坐标, 中心纵坐标, 半径, 起始弧度, 终止弧度, 是否为边框, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 横坐标 | +| 输入参数 3 | 纵坐标 | +| 输入参数 4 | 半径 | +| 输入参数 4 | 起始弧度 | +| 输入参数 4 | 终止弧度 | +| 输入参数 6 | 是否为边框 | +| 输入参数 7 | 线条宽度 | +| 输入参数 8 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制弧线 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制弧线(名称, 中心横坐标, 中心纵坐标, 半径, 起始弧度, 终止弧度, 是否为边框, 线条宽度, 颜色); +``` + +### 绘制圆 + +| 事件名称 | 绘制圆 | +| --- | ----------- | +| 事件原形 | 绘制圆(作用画布, 中心横坐标, 中心纵坐标, 半径, 是否为边框, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 横坐标 | +| 输入参数 3 | 纵坐标 | +| 输入参数 4 | 半径 | +| 输入参数 6 | 是否为边框 | +| 输入参数 7 | 线条宽度 | +| 输入参数 8 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制圆 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制圆(名称, 中心横坐标, 中心纵坐标, 半径, 是否为边框, 线条宽度, 颜色); +``` + +### 绘制矩形 + +| 事件名称 | 绘制矩形 | +| --- | ----------- | +| 事件原形 | 绘制矩形(作用画布, 横坐标, 纵坐标, 宽度, 高度, 是否为边框, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 横坐标 | +| 输入参数 3 | 纵坐标 | +| 输入参数 4 | 宽度 | +| 输入参数 5 | 高度 | +| 输入参数 6 | 是否为边框 | +| 输入参数 7 | 线条宽度 | +| 输入参数 8 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制矩形 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制矩形(名称, 横坐标, 纵坐标, 宽度, 高度, 是否为边框, 线条宽度, 颜色); +``` +### 绘制圆角矩形 + +| 事件名称 | 绘制圆角矩形 | +| --- | ----------- | +| 事件原形 | 绘制圆角矩形(作用画布, 横坐标, 纵坐标, 宽度, 高度, 圆角半径, 是否为边框, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 横坐标 | +| 输入参数 3 | 纵坐标 | +| 输入参数 4 | 宽度 | +| 输入参数 5 | 高度 | +| 输入参数 5 | 圆角半径 | +| 输入参数 6 | 是否为边框 | +| 输入参数 7 | 线条宽度 | +| 输入参数 8 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制圆角矩形 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制圆角矩形(名称, 横坐标, 纵坐标, 宽度, 高度, 圆角半径, 是否为边框, 线条宽度, 颜色); +``` + +### 绘制多边形 + +| 事件名称 | 绘制多边形 | +| --- | ----------- | +| 事件原形 | 绘制多边形(作用画布, 是否为边框, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 是否为边框 | +| 输入参数 3 | 线条宽度 | +| 输入参数 4 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制多边形 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制多边形(名称, 是否为边框, 线条宽度, 颜色); +``` + +### 绘制椭圆 + +| 事件名称 | 绘制椭圆 | +| --- | ----------- | +| 事件原形 | 绘制椭圆(作用画布, 中心横坐标, 中心纵坐标, 半长轴, 半短轴, 旋转角度, 是否为边框, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 中心横坐标 | +| 输入参数 3 | 中心纵坐标 | +| 输入参数 4 | 半长轴 | +| 输入参数 5 | 半短轴 | +| 输入参数 6 | 旋转角度 | +| 输入参数 7 | 是否为边框 | +| 输入参数 8 | 线条宽度 | +| 输入参数 9 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制椭圆 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制椭圆(名称, 中心横坐标, 中心纵坐标, 半长轴, 半短轴, 旋转角度, 是否为边框, 线条宽度, 颜色) +``` + +### 绘制箭头 + +| 事件名称 | 绘制箭头 | +| --- | ----------- | +| 事件原形 | 绘制箭头(作用画布, 起点横坐标, 起点纵坐标, 终点横坐标, 终点纵坐标, 连线宽度, 连线颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 起点横坐标 | +| 输入参数 3 | 起点纵坐标 | +| 输入参数 4 | 终点横坐标 | +| 输入参数 5 | 终点纵坐标 | +| 输入参数 6 | 连线宽度 | +| 输入参数 7 | 连线颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制箭头 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制箭头(名称, 起点横坐标, 起点纵坐标, 终点横坐标, 终点纵坐标, 连线宽度, 连线颜色); +``` + +### 绘制贝塞尔曲线 + +| 事件名称 | 绘制贝塞尔曲线 | +| --- | ----------- | +| 事件原形 | 绘制贝塞尔曲线(作用画布, 控制点1横坐标, 控制点1纵坐标, 控制点2横坐标, 控制点2纵坐标, 起点横坐标, 起点纵坐标, 终点横坐标, 终点纵坐标, 线条宽度, 颜色) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 控制点1横坐标 | +| 输入参数 3 | 控制点1纵坐标 | +| 输入参数 4 | 控制点2横坐标 | +| 输入参数 5 | 控制点2纵坐标 | +| 输入参数 6 | 起点横坐标 | +| 输入参数 7 | 起点纵坐标 | +| 输入参数 8 | 终点横坐标 | +| 输入参数 9 | 终点纵坐标 | +| 输入参数 10 | 线条宽度 | +| 输入参数 11 | 颜色 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制贝塞尔曲线 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制贝塞尔曲线(名称, 控制点1横坐标, 控制点1纵坐标, 控制点2横坐标, 控制点2纵坐标, 起点横坐标, 起点纵坐标, 终点横坐标, 终点纵坐标, 线条宽度, 颜色); +``` + +### 绘制图片 + +| 事件名称 | 绘制图片 | +| --- | ----------- | +| 事件原形 | 绘制图片(作用画布, 图片, 裁切点横坐标, 裁切点纵坐标, 裁切宽度, 裁切高度, 绘制点横坐标, 绘制点纵坐标, 绘制宽度, 绘制高度) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 图片 | +| 输入参数 3 | 裁切点横坐标 | +| 输入参数 4 | 裁切点纵坐标 | +| 输入参数 5 | 裁切宽度 | +| 输入参数 6 | 裁切高度 | +| 输入参数 7 | 绘制点横坐标 | +| 输入参数 8 | 绘制点纵坐标 | +| 输入参数 9 | 绘制宽度 | +| 输入参数 10 | 绘制高度 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制图片 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制图片(名称, 图片, 裁切点横坐标, 裁切点纵坐标, 裁切宽度, 裁切高度, 绘制点横坐标, 绘制点纵坐标, 绘制宽度, 绘制高度); +``` + +### 绘制图标 + +| 事件名称 | 绘制图标 | +| --- | ----------- | +| 事件原形 | 绘制图标(作用画布, 图标id, 横坐标, 纵坐标, 帧数) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 图标id | +| 输入参数 3 | 横坐标 | +| 输入参数 4 | 纵坐标 | +| 输入参数 5 | 宽度 | +| 输入参数 6 | 高度 | +| 输入参数 7 | 帧数:绘制图标的第几帧 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制图标 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制图标(名称, 图标id, 横坐标, 纵坐标, 帧数); +``` + +### 绘制窗口皮肤 + +| 事件名称 | 绘制窗口皮肤 | +| --- | ----------- | +| 事件原形 | 绘制窗口皮肤(作用画布, 皮肤背景, 横坐标, 纵坐标, 宽度, 高度) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 皮肤背景:需为已注册的图片 | +| 输入参数 3 | 横坐标 | +| 输入参数 4 | 纵坐标 | +| 输入参数 5 | 宽度 | +| 输入参数 6 | 高度 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制窗口皮肤 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制窗口皮肤(名称, 皮肤背景, 横坐标, 纵坐标, 宽度, 高度); +``` + +### 绘制文本 + +| 事件名称 | 绘制文本 | +| --- | ----------- | +| 事件原形 | 绘制文本(作用画布, 文字, 横坐标, 纵坐标, 添加描边, 斜体, 字体, 字体大小, 字体粗细, 字体颜色, 描边颜色, 最大宽度) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 文本 | +| 输入参数 3 | 横坐标 | +| 输入参数 4 | 纵坐标 | +| 输入参数 5 | 添加描边 | +| 输入参数 6 | 斜体 | +| 输入参数 7 | 字体 | +| 输入参数 8 | 字体大小 | +| 输入参数 9 | 字体粗细 | +| 输入参数 10 | 字体颜色 | +| 输入参数 11 | 描边颜色 | +| 输入参数 12 | 最大宽度 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制文本 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制文本(名称, 文字, 横坐标, 纵坐标, 添加描边, 斜体, 字体, 字体大小, 字体粗细, 字体颜色, 描边颜色, 最大宽度); +``` + +### 绘制成段文本 + +| 事件名称 | 绘制成段文本 | +| --- | ----------- | +| 事件原形 | 绘制成段文本(作用画布, 文本, 横坐标, 纵坐标, 宽度, 颜色, 对齐, 字体大小, 行间距, 打字机时间间隔, 字体, 字符间距, 加粗, 斜体) | +| 功能描述 | | +| 输入参数 1 | 作用画布:画布的名称 | +| 输入参数 2 | 文本 | +| 输入参数 3 | 横坐标 | +| 输入参数 4 | 纵坐标 | +| 输入参数 5 | 宽度 | +| 输入参数 6 | 颜色 | +| 输入参数 7 | 对齐 | +| 输入参数 8 | 字体大小 | +| 输入参数 9 | 行间距 | +| 输入参数 10 | 打字机时间间隔 | +| 输入参数 11 | 字体 | +| 输入参数 12 | 字符间距 | +| 输入参数 13 | 加粗 | +| 输入参数 14 | 斜体 | +| 输出参数 | 无 | +| 返回值 | 无 | +| 先决条件 | 无 | +| 调用函数 | 无 | + +例: +```js +/* 这是UI编辑器中 绘制成段文本 的参考例程 */ +创建画布(名称, 横坐标, 纵坐标, 宽度, 高度, 纵深); +绘制成段文本(名称, 文本, 横坐标, 纵坐标, 宽度, 颜色, 对齐, 字体大小, 行间距, 打字机时间间隔, 字体, 字符间距, 加粗, 斜体); +``` + +========================================================================================== + +[继续阅读下一章:API](api) \ No newline at end of file diff --git a/ui.d.ts b/ui.d.ts new file mode 100644 index 0000000..0da562c --- /dev/null +++ b/ui.d.ts @@ -0,0 +1,831 @@ +/** + * 可以设置成的画布填充描边样式 + */ +type CanvasStyle = string | CanvasGradient | CanvasPattern; + +type ImageSource = + | CanvasImageSource + | ImageIds + | `${ImageIds}${ImageReverse}` + | NameMapIn + | `${NameMapIn}${ImageReverse}`; + +interface BackgroundPosInfo { + /** + * 横坐标 + */ + px: number; + + /** + * 纵坐标 + */ + py: number; + + /** + * 是否没有位置 + */ + noPeak: boolean; + + /** + * 横坐标偏移值 + */ + xoffset: number; + + /** + * 纵坐标偏移值 + */ + yoffset: number; + + /** + * 画布,默认是ui + */ + ctx: CtxRefer; + + /** + * 箭头指向是朝上还是朝下 + */ + position: 'up' | 'bottom'; +} + +interface TextContentConfig { + left: number; + top: number; + + /** + * 最大宽度 + */ + maxWidth: number; + + /** + * 颜色,不影响\r + */ + color: Color; + + /** + * 对齐方式 + */ + align: 'left' | 'center' | 'right'; + + /** + * 字体大小 + */ + fontSize: number; + + /** + * 行高 + */ + lineHeight: number; + + /** + * 打字机间隔 + */ + time: number; + + /** + * 字体名 + */ + font: string; + + /** + * 字符间距 + */ + letterSpacing: number; + + /** + * 是否加粗 + */ + bold: boolean; + + /** + * 是否斜体 + */ + italic: boolean; +} + +interface TextContentBlock { + left: number; + top: number; + width: number; + height: number; + line: number; + marginLeft: number; + marginTop: number; +} + +interface ReturnedTextContentConfig extends TextContentConfig { + right: number; + + /** + * 默认字体 + */ + defaultFont: string; + + /** + * 当前绘制的文字索引 + */ + index: number; + + /** + * 当前颜色 + */ + currcolor: Color; + + /** + * 当前字体 + */ + currfont: string; + + /** + * 每一行的间距 + */ + lineMargin: number; + + /** + * 每一行间距的一半 + */ + topMargin: number; + + /** + * 横坐标偏移量 + */ + offsetX: number; + + /** + * 纵坐标偏移量 + */ + offsetY: number; + + /** + * 当前行数 + */ + line: number; + + /** + * 所有的文字 + */ + blocks: TextContentBlock[]; + + /** + * 是否是高清画布 + */ + isHD: boolean; + + /** + * 这一行的最大高度 + */ + lineMaxHeight: number; + + /** + * 是否是强制换行 + */ + forceChangeLine: boolean; +} + +interface TextBoxConfig { + /** + * 画布 + */ + ctx: CtxRefer; + + /** + * 对话框位置 + */ + pos: TextBoxPos; + + /** + * 是否一次性全部显示 + */ + showAll: boolean; + + /** + * 是否异步显示 + */ + async: boolean; +} + +/** + * UI窗口的绘制,如对话框、怪物手册、楼传器、存读档界面等 + */ +interface Ui { + /** + * ui数据 + */ + uidata: UiData; + + /** + * 根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。 + * 也可以传画布的context自身,则返回自己。 + */ + getContextByName(canvas: CtxRefer): CanvasRenderingContext2D | null; + + /** + * 清空某个画布图层 + * name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。(下同) + * 如果name也可以是'all',若为all则为清空所有系统画布。 + */ + clearMap( + name: CtxRefer, + x?: number, + y?: number, + w?: number, + h?: number + ): void; + + /** + * 在某个画布上绘制一段文字 + * @param text 要绘制的文本 + * @param style 绘制的样式 + * @param font 绘制的字体 + * @param maxWidth 文字整体的最大宽度,如果超过会自动缩小文字使其宽度小于这个值 + */ + fillText( + name: CtxRefer, + text: string, + x: number, + y: number, + style?: CanvasStyle, + font?: string, + maxWidth?: number + ): void; + + /** + * 根据最大宽度自动缩小字体 + * @param name 画布 + * @param text 文字 + * @param maxWidth 最大和宽度 + * @param font 字体 + */ + setFontForMaxWidth( + name: CtxRefer, + text: string, + maxWidth: number, + font?: string + ): void; + + /** + * 在某个画布上绘制一个描边文字 + * @param text 要绘制的文本 + * @param style 绘制的样式 + * @param strokeStyle 绘制的描边颜色 + * @param font 绘制的字体 + * @param lineWidth 描边的线宽 + */ + fillBoldText( + name: CtxRefer, + text: string, + x: number, + y: number, + style?: CanvasStyle, + strokeStyle?: CanvasStyle, + font?: string, + maxWidth?: number, + lineWidth?: number + ): void; + + /** + * 绘制一个矩形 + * @param style 绘制的样式 + * @param angle 旋转角度,弧度制 + */ + fillRect( + name: CtxRefer, + x: number, + y: number, + width: number, + height: number, + style?: CanvasStyle, + angle?: number + ): void; + + /** + * 绘制一个矩形的边框 + * @param style 绘制的样式 + * @param angle 旋转角度,单位弧度 + */ + strokeRect( + name: CtxRefer, + x: number, + y: number, + width: number, + height: number, + style?: CanvasStyle, + lineWidth?: number, + angle?: number + ): void; + + /** + * 在某个canvas上绘制一个圆角矩形 + */ + fillRoundRect( + name: CtxRefer, + x: number, + y: number, + width: number, + height: number, + radius: number, + style?: CanvasStyle, + angle?: number + ): void; + + /** + * 在某个canvas上绘制一个圆角矩形的边框 + */ + strokeRoundRect( + name: CtxRefer, + x: number, + y: number, + width: number, + height: number, + radius: number, + style?: CanvasStyle, + lineWidth?: number, + angle?: number + ): void; + + /** + * 在某个canvas上绘制一个多边形 + */ + fillPolygon( + name: CtxRefer, + nodes?: [x: number, y: number][], + style?: CanvasStyle + ): void; + + /** + * 在某个canvas上绘制一个多边形的边框 + */ + strokePolygon( + name: CtxRefer, + nodes?: [x: number, y: number][], + style?: CanvasStyle, + lineWidth?: number + ): void; + + /** + * 在某个canvas上绘制一个椭圆 + * @param a 横轴长度的一半 + * @param b 纵轴长度的一半 + * @param angle 旋转角度 + */ + fillEllipse( + name: CtxRefer, + x: number, + y: number, + a: number, + b: number, + angle?: number, + style?: CanvasStyle + ): void; + + /** + * 在某个canvas上绘制一个圆 + */ + fillCircle( + name: CtxRefer, + x: number, + y: number, + r: number, + style?: CanvasStyle + ): void; + + /** + * 在某个canvas上绘制一个椭圆的边框 + * @param a 横轴长度的一半 + * @param b 纵轴长度的一半 + * @param angle 旋转角度 + */ + strokeEllipse( + name: CtxRefer, + x: number, + y: number, + a: number, + b: number, + angle?: number, + style?: CanvasStyle, + lineWidth?: number + ): void; + + /** + * 在某个canvas上绘制一个圆的边框 + */ + strokeCircle( + name: CtxRefer, + x: number, + y: number, + r: any, + style?: CanvasStyle, + lineWidth?: number + ): void; + + /** + * 在某个canvas上绘制一个扇形 + */ + fillArc( + name: CtxRefer, + x: number, + y: number, + r: number, + start: number, + end: number, + style?: CanvasStyle + ): void; + + /** + * 在某个canvas上绘制一段弧 + */ + strokeArc( + name: CtxRefer, + x: number, + y: number, + r: number, + start: number, + end: number, + style?: CanvasStyle, + lineWidth?: number + ): void; + + /** + * 在某个canvas上绘制一条线 + */ + drawLine( + name: CtxRefer, + x1: number, + y1: number, + x2: number, + y2: number, + style?: CanvasStyle, + lineWidth?: number + ): void; + + /** + * 在某个canvas上绘制一个箭头 + */ + drawArrow( + name: CtxRefer, + x1: number, + y1: number, + x2: number, + y2: number, + style?: CanvasStyle, + lineWidth?: number + ): void; + + /** + * 设置某个canvas的文字字体 + */ + setFont(name: CtxRefer, font: string): void; + + /** + * 设置某个canvas的线宽度 + */ + setLineWidth(name: CtxRefer, lineWidth: number): void; + + /** + * 保存某个canvas状态 + */ + saveCanvas(name: CtxRefer): void; + + /** + * 回退某个canvas状态 + */ + loadCanvas(name: CtxRefer): void; + + /** + * 设置某个canvas的绘制不透明度 + * @returns 之前画布的不透明度 + */ + setAlpha(name: CtxRefer, alpha: number): number; + + /** + * 设置画布元素的不透明度 + */ + setOpacity(name: CtxRefer, opacity: number): void; + + /** + * 设置某个canvas的滤镜 + */ + setFilter(name: CtxRefer, filter?: string): void; + + /** + * 设置某个canvas的填充样式 + */ + setFillStyle(name: CtxRefer, style: CanvasStyle): void; + + /** + * 设置某个canvas描边样式 + */ + setStrokeStyle(name: CtxRefer, style: CanvasStyle): void; + + /** + * 设置某个canvas的文字左右对齐方式 + */ + setTextAlign(name: CtxRefer, align: CanvasTextAlign): void; + + /** + * 设置某个canvas的文字上下对齐方式 + */ + setTextBaseline(name: CtxRefer, baseline: CanvasTextBaseline): void; + + /** + * 计算某段文字的宽度,注意该函数会设置画布的字体 + */ + calWidth(name: CtxRefer, text: string, font?: string): number; + + /** + * 字符串自动换行的分割 + */ + splitLines( + name: CtxRefer, + text: string, + maxWidth?: number, + font?: string + ): string[]; + + /** + * 绘制图片 + * @param dx 绘制的横坐标 + * @param dy 绘制的纵坐标 + */ + drawImage(name: CtxRefer, image: ImageSource, dx: number, dy: number): void; + /** + * 绘制图片 + * @param dx 绘制的横坐标 + * @param dy 绘制的纵坐标 + * @param dw 绘制的宽度 + * @param dh 绘制的高度 + */ + drawImage( + name: CtxRefer, + image: ImageSource, + dx: number, + dy: number, + dw: number, + dh: number + ): void; + /** + * 绘制图片 + * @param sx 裁剪的横坐标 + * @param sy 裁剪的纵坐标 + * @param sw 裁剪的宽度 + * @param sh 裁剪的高度 + * @param dx 绘制的横坐标 + * @param dy 绘制的纵坐标 + * @param dw 绘制的宽度 + * @param dh 绘制的高度 + */ + drawImage( + name: CtxRefer, + image: ImageSource, + sx: number, + sy: number, + sw: number, + sh: number, + dx: number, + dy: number, + dw: number, + dh: number + ): void; + + /** + * 在某个canvas上绘制一个图标 + * @param frame 图标的第几帧 + */ + drawIcon( + name: CtxRefer, + id: AllIds, + x: number, + y: number, + w?: number, + h?: number, + frame?: number + ): void; + + /** + * 结束一切事件和绘制,关闭UI窗口,返回游戏进程 + */ + closePanel(): void; + + /** + * 清空UI层内容 + */ + clearUI(): void; + + /** + * 左上角绘制一段提示 + * @param text 要提示的文字内容 + * @param id 要绘制的图标ID + * @param frame 要绘制图标的第几帧 + */ + drawTip(text: string, id?: AllIds, frame?: number): void; + + /** + * 地图中间绘制一段文字 + */ + drawText(contents: string, callback?: () => void): void; + + /** + * 自绘选择光标 + */ + drawUIEventSelector( + code: number, + background: RGBArray | ImageIds, + x: number, + y: number, + w: number, + h: number, + z?: number + ): void; + + /** + * 清除一个或多个选择光标 + * @param code 要清除的选择光标,不填表示清除所有 + */ + clearUIEventSelector(code?: number | number[]): void; + + /** + * 绘制WindowSkin + * @param direction 指向箭头的方向 + */ + drawWindowSkin( + background: any, + ctx: CtxRefer, + x: number, + y: number, + w: number, + h: number, + direction?: 'up' | 'down', + px?: number, + py?: number + ): void; + + /** + * 绘制一个背景图,可绘制winskin或纯色背景;支持小箭头绘制 + */ + drawBackground( + left: string, + top: string, + right: string, + bottom: string, + posInfo?: Partial + ): void; + + /** + * 绘制一段文字到某个画布上面 + * @param ctx 要绘制到的画布 + * @param content 要绘制的内容;转义字符只允许保留 \n, \r[...], \i[...], \c[...], \d, \e + * @param config 绘制配置项 + * @returns 绘制信息 + */ + drawTextContent( + ctx: CtxRefer, + content: string, + config: Partial + ): ReturnedTextContentConfig; + + /** + * 获得某段文字的预计绘制高度 + */ + getTextContentHeight( + content: string, + config: Partial + ): number; + + /** + * 绘制一个对话框 + */ + drawTextBox(content: string, config?: TextBoxConfig): void; + + /** + * 绘制滚动字幕 + */ + drawScrollText( + content: string, + time?: number, + lineHeight?: number, + callback?: () => void + ): void; + + /** + * 文本图片化 + */ + textImage(content: string, lineHeight?: number): HTMLCanvasElement; + + /** + * 绘制一个选项界面 + */ + drawChoices( + content: string, + choices: string[], + width?: number, + ctx?: CtxRefer + ): void; + + /** + * 绘制一个确认框 + */ + drawConfirmBox( + text: string, + yesCallback?: () => void, + noCallback?: () => void, + ctx?: CtxRefer + ): void; + + /** + * 绘制等待界面 + */ + drawWaiting(text: string): void; + + /** + * 绘制分页 + */ + drawPagination(page: number, totalPage: number, y?: number): void; + + /** + * 绘制怪物手册 + */ + drawBook(index: number): void; + + /** + * 绘制楼层传送器 + */ + drawFly(page: number): void; + + /** + * 获得所有应该在道具栏显示的某个类型道具 + */ + getToolboxItems>(cls: T): ItemIdOf[]; + + /** + * 动态创建一个画布 + * @param name 画布名称,如果已存在则会直接取用当前存在的 + * @param x 横坐标 + * @param y 纵坐标 + * @param width 宽度 + * @param height 高度 + * @param zIndex 纵深 + * @param nonAntiAliasing 是否取消抗锯齿 + */ + createCanvas( + name: string, + x: number, + y: number, + width: number, + height: number, + zIndex?: number, + nonAntiAliasing?: boolean + ): CanvasRenderingContext2D; + + /** + * 重新定位一个自定义画布 + */ + relocateCanvas( + name: CtxRefer, + x: number, + y: number, + useDelta?: boolean + ): void; + + /** + * 设置一个自定义画布的旋转角度 + */ + rotateCanvas( + name: CtxRefer, + angle: number, + centerX?: number, + centerY?: number + ): void; + + /** + * 重新设置一个自定义画布的大小 + * @param styleOnly 是否只修改style,而不修改元素上的长宽,如果是true,会出现模糊现象 + * @param isTempCanvas 是否是临时画布,如果填true,会将临时画布修改为高清画布 + */ + resizeCanvas( + name: CtxRefer, + x?: number, + y?: number, + styleOnly?: boolean, + isTempCanvas?: boolean + ): void; + + /** + * 删除一个自定义画布 + */ + deleteCanvas(name: string | ((name: string) => boolean)): void; + + /** + * 清空所有的自定义画布 + */ + deleteAllCanvas(): void; + + /** + * 绘制浏览地图 + */ + _drawViewMaps(): void; + + _drawReplay(): void; + + _drawStatistics(): void; +} + +declare const ui: new () => Ui; diff --git a/ui.js b/ui.js new file mode 100644 index 0000000..1b91388 --- /dev/null +++ b/ui.js @@ -0,0 +1,3519 @@ + +/** + * ui.js:负责所有和UI界面相关的绘制 + * 包括: + * 自动寻路、怪物手册、楼传器、存读档、菜单栏、NPC对话事件、等等 + */ + +"use strict"; + +function ui() { + this._init(); +} + +// 初始化UI +ui.prototype._init = function () { + this.uidata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.ui; +} + +////////////////// 地图设置 + +ui.prototype.getContextByName = function (name) { + if (name instanceof HTMLCanvasElement) return name.getContext('2d'); + var canvas = name; + if (typeof name == 'string') { + if (core.canvas[name]) + canvas = core.canvas[name]; + else if (core.dymCanvas[name]) + canvas = core.dymCanvas[name]; + } + if (canvas && canvas.canvas) { + return canvas; + } + return null; +} + +ui.prototype._createUIEvent = function () { + if (main.mode == 'editor') return; + if (!core.dymCanvas['uievent']) { + core.createCanvas('uievent', 0, 0, core._PX_, core._PY_, 135); + } +} + +////// 清除地图 ////// +ui.prototype.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.dom.gif.innerHTML = ""; + core.removeGlobalAnimate(); + core.deleteCanvas(function (one) { return one.startsWith('_bigImage_'); }); + core.setWeather(null); + } + else { + var ctx = this.getContextByName(name); + if (ctx) { + if (x != null && y != null && width != null && height != null) { + ctx.clearRect(x, y, width, height); + } else { + if (ctx.canvas.getAttribute('isHD')) { + const width = ctx.canvas.width / core.domStyle.scale / devicePixelRatio; + const height = ctx.canvas.height / core.domStyle.scale / devicePixelRatio; + ctx.clearRect(0, 0, width, height); + } else { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + } + } + } + } +} + +ui.prototype._uievent_clearMap = function (data) { + if (main.mode != 'editor' && (data.x == null || data.y == null || data.width == null || data.height == null)) { + this.deleteCanvas('uievent'); + return; + } + this._createUIEvent(); + this.clearMap('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height)); +} + +////// 在某个canvas上绘制一段文字 ////// +ui.prototype.fillText = function (name, text, x, y, style, font, maxWidth) { + if (style) core.setFillStyle(name, style); + if (font) core.setFont(name, font); + var ctx = this.getContextByName(name); + if (!ctx) return; + text = (text + "").replace(/\\r/g, '\r'); + var originText = text.replace(/\r(\[.*\])?/g, ""); + var index = text.indexOf('\r'); + if (maxWidth != null) { + this.setFontForMaxWidth(ctx, index >= 0 ? originText : text, maxWidth); + } + if (index >= 0) { + var currentStyle = ctx.fillStyle; + var textWidth = core.calWidth(ctx, originText); + var textAlign = ctx.textAlign; + if (textAlign == 'center') x -= textWidth / 2; + else if (textAlign == 'right') x -= textWidth; + ctx.textAlign = 'left'; + text = text.replace(/\r(?!\[.*\])/g, "\r[" + currentStyle + "]"); + var colorArray = text.match(/\r\[.*?\]/g); + var textArray = text.split(/\r\[.*?\]/); + var width = 0; + for (var i = 0; i < textArray.length; i++) { + var subtext = textArray[i]; + if (colorArray[i - 1]) ctx.fillStyle = colorArray[i - 1].slice(2, -1); + ctx.fillText(subtext, x + width, y); + width += core.calWidth(ctx, subtext, x, y); + } + ctx.textAlign = textAlign; + ctx.fillStyle = currentStyle; + } else { + ctx.fillText(text, x, y); + } +} + +ui.prototype._uievent_fillText = function (data) { + this._createUIEvent(); + this.fillText('uievent', core.replaceText(data.text), core.calValue(data.x), core.calValue(data.y), data.style, data.font, data.maxWidth); +} + +////// 自适配字体大小 +ui.prototype.setFontForMaxWidth = function (name, text, maxWidth, font) { + var ctx = this.getContextByName(name); + if (font) core.setFont(name, font); + var font = ctx.font, u = /(\d+)px/.exec(font); + if (u == null) return; + for (var font_size = parseInt(u[1]); font_size >= 8; font_size--) { + ctx.font = font.replace(/(\d+)px/, font_size + "px"); + if (ctx.measureText(text).width <= maxWidth) return; + } +} + +////// 在某个canvas上绘制粗体 ////// +ui.prototype.fillBoldText = function (name, text, x, y, style, strokeStyle, 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; + ctx.lineWidth = 2; + ctx.strokeText(text, x, y); + ctx.fillStyle = style; + ctx.fillText(text, x, y); +} + +ui.prototype._uievent_fillBoldText = function (data) { + this._createUIEvent(); + this.fillBoldText('uievent', core.replaceText(data.text), core.calValue(data.x), core.calValue(data.y), data.style, data.strokeStyle, data.font); +} + +////// 在某个canvas上绘制一个矩形 ////// +ui.prototype.fillRect = function (name, x, y, width, height, style, angle) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (ctx) { + if (angle) { + ctx.save(); + ctx.translate(x + width / 2, y + height / 2); + ctx.rotate(angle); + ctx.translate(-x - width / 2, -y - height / 2); + } + ctx.fillRect(x, y, width, height); + if (angle) { + ctx.restore(); + } + } +} + +ui.prototype._uievent_fillRect = function (data) { + this._createUIEvent(); + if (data.radius) { + this.fillRoundRect('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), + core.calValue(data.radius), data.style, (core.calValue(data.angle) || 0) * Math.PI / 180); + } else { + this.fillRect('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), + data.style, (core.calValue(data.angle) || 0) * Math.PI / 180); + } +} + +////// 在某个canvas上绘制一个矩形的边框 ////// +ui.prototype.strokeRect = function (name, x, y, width, height, style, lineWidth, angle) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (ctx) { + if (angle) { + ctx.save(); + ctx.translate(x + width / 2, y + height / 2); + ctx.rotate(angle); + ctx.translate(-x - width / 2, -y - height / 2); + } + ctx.strokeRect(x, y, width, height); + if (angle) { + ctx.restore(); + } + } +} + +ui.prototype._uievent_strokeRect = function (data) { + this._createUIEvent(); + if (data.radius) { + this.strokeRoundRect('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), + core.calValue(data.radius), data.style, data.lineWidth, (core.calValue(data.angle) || 0) * Math.PI / 180); + } else { + this.strokeRect('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), + data.style, data.lineWidth, (core.calValue(data.angle) || 0) * Math.PI / 180); + } +} + +////// 在某个canvas上绘制一个圆角矩形 ////// +ui.prototype.fillRoundRect = function (name, x, y, width, height, radius, style, angle) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (ctx) { + if (angle) { + ctx.save(); + ctx.translate(x + width / 2, y + height / 2); + ctx.rotate(angle); + ctx.translate(-x - width / 2, -y - height / 2); + } + this._roundRect_buildPath(ctx, x, y, width, height, radius); + ctx.fill(); + if (angle) { + ctx.restore(); + } + } +} + +////// 在某个canvas上绘制一个圆角矩形的边框 ////// +ui.prototype.strokeRoundRect = function (name, x, y, width, height, radius, style, lineWidth, angle) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (ctx) { + if (angle) { + ctx.save(); + ctx.translate(x + width / 2, y + height / 2); + ctx.rotate(angle); + ctx.translate(-x - width / 2, -y - height / 2); + } + this._roundRect_buildPath(ctx, x, y, width, height, radius); + ctx.stroke(); + if (angle) { + ctx.restore(); + } + } +} + +ui.prototype._roundRect_buildPath = function (ctx, x, y, width, height, radius) { + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); +} + +////// 在某个canvas上绘制一个多边形 ////// +ui.prototype.fillPolygon = function (name, nodes, style) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (!ctx) return; + if (!nodes || nodes.length < 3) return; + ctx.beginPath(); + for (var i = 0; i < nodes.length; ++i) { + var x = core.calValue(nodes[i][0]), y = core.calValue(nodes[i][1]); + if (i == 0) ctx.moveTo(x, y); + else ctx.lineTo(x, y); + } + ctx.closePath(); + ctx.fill(); +} + +ui.prototype._uievent_fillPolygon = function (data) { + this._createUIEvent(); + this.fillPolygon('uievent', data.nodes, data.style); +} + +////// 在某个canvas上绘制一个多边形的边框 ////// +ui.prototype.strokePolygon = function (name, nodes, style, lineWidth) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + if (!nodes || nodes.length < 3) return; + ctx.beginPath(); + for (var i = 0; i < nodes.length; ++i) { + var x = core.calValue(nodes[i][0]), y = core.calValue(nodes[i][1]); + if (i == 0) ctx.moveTo(x, y); + else ctx.lineTo(x, y); + } + ctx.closePath(); + ctx.stroke(); +} + +ui.prototype._uievent_strokePolygon = function (data) { + this._createUIEvent(); + this.strokePolygon('uievent', data.nodes, data.style, data.lineWidth); +} + +////// 在某个canvas上绘制一个椭圆 ////// +ui.prototype.fillEllipse = function (name, x, y, a, b, angle, style) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.ellipse(x, y, a, b, angle, 0, 2 * Math.PI); + ctx.fill(); +} + +ui.prototype.fillCircle = function (name, x, y, r, style) { + return this.fillEllipse(name, x, y, r, r, 0, style); +} + +ui.prototype._uievent_fillEllipse = function (data) { + this._createUIEvent(); + this.fillEllipse('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.a), + core.calValue(data.b), (core.calValue(data.angle) || 0) * Math.PI / 180, data.style); +} + +////// 在某个canvas上绘制一个圆的边框 ////// +ui.prototype.strokeEllipse = function (name, x, y, a, b, angle, style, lineWidth) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.ellipse(x, y, a, b, angle, 0, 2 * Math.PI); + ctx.stroke(); +} + +ui.prototype.strokeCircle = function (name, x, y, r, style, lineWidth) { + return this.strokeEllipse(name, x, y, r, r, 0, style, lineWidth); +} + +ui.prototype._uievent_strokeEllipse = function (data) { + this._createUIEvent(); + this.strokeEllipse('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.a), + core.calValue(data.b), (core.calValue(data.angle) || 0) * Math.PI / 180, data.style, data.lineWidth); +} + +ui.prototype.fillArc = function (name, x, y, r, start, end, style) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.arc(x, y, r, start, end); + ctx.closePath(); + ctx.fill(); +} + +ui.prototype._uievent_fillArc = function (data) { + this._createUIEvent(); + this.fillArc('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.r), + (core.calValue(data.start) || 0) * Math.PI / 180, (core.calValue(data.end) || 0) * Math.PI / 180, data.style); +} + +ui.prototype.strokeArc = function (name, x, y, r, start, end, style, lineWidth) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.arc(x, y, r, start, end); + ctx.stroke(); +} + +ui.prototype._uievent_strokeArc = function (data) { + this._createUIEvent(); + this.strokeArc('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.r), + (core.calValue(data.start) || 0) * Math.PI / 180, (core.calValue(data.end) || 0) * Math.PI / 180, data.style, data.lineWidth); +} + +////// 在某个canvas上绘制一条线 ////// +ui.prototype.drawLine = function (name, x1, y1, x2, y2, style, lineWidth) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth != null) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); +} + +ui.prototype._uievent_drawLine = function (data) { + this._createUIEvent(); + this.drawLine('uievent', core.calValue(data.x1), core.calValue(data.y1), core.calValue(data.x2), core.calValue(data.y2), data.style, data.lineWidth); +} + +////// 在某个canvas上绘制一个箭头 ////// +ui.prototype.drawArrow = function (name, x1, y1, x2, y2, style, lineWidth) { + if (x1 == x2 && y1 == y2) return; + if (style) core.setStrokeStyle(name, style); + if (lineWidth != null) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + var head = 10; + var dx = x2 - x1, dy = y2 - y1; + var angle = Math.atan2(dy, dx); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x2 - head * Math.cos(angle - Math.PI / 6), y2 - head * Math.sin(angle - Math.PI / 6)); + ctx.moveTo(x2, y2); + ctx.lineTo(x2 - head * Math.cos(angle + Math.PI / 6), y2 - head * Math.sin(angle + Math.PI / 6)); + ctx.stroke(); +} + +ui.prototype._uievent_drawArrow = function (data) { + this._createUIEvent(); + this.drawArrow('uievent', core.calValue(data.x1), core.calValue(data.y1), core.calValue(data.x2), core.calValue(data.y2), data.style, data.lineWidth); +} + +////// 设置某个canvas的文字字体 ////// +ui.prototype.setFont = function (name, font) { + var ctx = this.getContextByName(name); + if (ctx) ctx.font = font; +} + +////// 设置某个canvas的线宽度 ////// +ui.prototype.setLineWidth = function (name, lineWidth) { + var ctx = this.getContextByName(name); + if (ctx) ctx.lineWidth = lineWidth; +} + +////// 保存某个canvas状态 ////// +ui.prototype.saveCanvas = function (name) { + var ctx = this.getContextByName(name); + if (ctx) ctx.save(); +} + +////// 加载某个canvas状态 ////// +ui.prototype.loadCanvas = function (name) { + var ctx = this.getContextByName(name); + if (ctx) ctx.restore(); +} + +////// 设置某个canvas的alpha值,并返回设置之前的alpha值 ////// +ui.prototype.setAlpha = function (name, alpha) { + var ctx = this.getContextByName(name); + if (!ctx) return null; + var previousAlpha = ctx.globalAlpha; + ctx.globalAlpha = alpha; + return previousAlpha; +} + +////// 设置某个canvas的透明度;尽量不要使用本函数,而是全部换成setAlpha实现 ////// +ui.prototype.setOpacity = function (name, opacity) { + var ctx = this.getContextByName(name); + if (ctx) ctx.canvas.style.opacity = opacity; +} + +////// 设置某个canvas的filter ////// +ui.prototype.setFilter = function (name, filter) { + var ctx = this.getContextByName(name); + if (!ctx) return; + if (!filter) ctx.filter = 'none'; + else if (typeof filter === 'string') ctx.filter = filter; + else { + var x = []; + if (filter.blur > 0) x.push('blur(' + filter.blur + 'px)'); + if (filter.hue > 0) x.push('hue-rotate(' + filter.hue + 'deg)'); + if (filter.grayscale > 0) x.push('grayscale(' + filter.grayscale + ')'); + if (filter.invert) x.push('invert(1)'); + if (filter.shadow > 0) x.push('drop-shadow(0 0 ' + filter.shadow + 'px black)'); + if (x.length == 0) ctx.filter = 'none'; + else ctx.filter = x.join(' '); + } +} + +////// 设置某个canvas的绘制属性(如颜色等) ////// +ui.prototype.setFillStyle = function (name, style) { + var ctx = this.getContextByName(name); + if (ctx) ctx.fillStyle = core.arrayToRGBA(style); +} + +////// 设置某个canvas边框属性 ////// +ui.prototype.setStrokeStyle = function (name, style) { + var ctx = this.getContextByName(name); + if (ctx) ctx.strokeStyle = core.arrayToRGBA(style); +} + +////// 设置某个canvas的对齐 ////// +ui.prototype.setTextAlign = function (name, align) { + var ctx = this.getContextByName(name); + if (ctx) ctx.textAlign = align; +} + +////// 设置某个canvas的baseline ////// +ui.prototype.setTextBaseline = function (name, baseline) { + var ctx = this.getContextByName(name); + if (ctx) ctx.textBaseline = baseline; +} + +ui.prototype._uievent_setAttribute = function (data) { + this._createUIEvent(); + if (data.font) this.setFont('uievent', data.font); + if (data.lineWidth) this.setLineWidth('uievent', data.lineWidth); + if (data.alpha != null) this.setAlpha('uievent', data.alpha); + if (data.fillStyle) this.setFillStyle('uievent', data.fillStyle); + if (data.strokeStyle) this.setStrokeStyle('uievent', data.strokeStyle); + if (data.align) this.setTextAlign('uievent', data.align); + if (data.baseline) this.setTextBaseline('uievent', data.baseline); + if (data.z != null && main.mode != 'editor') { + var z = parseInt(data.z) || 135; + core.dymCanvas.uievent.canvas.style.zIndex = z; + if (core.dymCanvas._uievent_selector) + core.dymCanvas._uievent_selector.canvas.style.zIndex = z + 1; + } +} + +ui.prototype._uievent_setFilter = function (data) { + this._createUIEvent(); + this.setFilter('uievent', data); +} + +////// 计算某段文字的宽度 ////// +ui.prototype.calWidth = function (name, text, font) { + var ctx = this.getContextByName(name); + if (ctx) { + if (font) ctx.font = font; + return ctx.measureText(text).width; + } + return 0; +} + +////// 字符串自动换行的分割 ////// +ui.prototype.splitLines = function (name, text, maxWidth, font) { + var ctx = this.getContextByName(name); + if (!ctx) return [text]; + if (font) core.setFont(name, font); + + var contents = []; + var last = 0; + for (var i = 0; i < text.length; i++) { + if (text.charAt(i) == '\n') { + contents.push(text.substring(last, i)); + last = i + 1; + } + else if (text.charAt(i) == '\\' && text.charAt(i + 1) == 'n') { + contents.push(text.substring(last, i)); + last = i + 2; + } + else { + var toAdd = text.substring(last, i + 1); + var width = core.calWidth(name, toAdd); + if (maxWidth && width > maxWidth) { + contents.push(text.substring(last, i)); + last = i; + } + } + } + contents.push(text.substring(last)); + return contents; +} + +////// 绘制一张图片 ////// +ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1, angle, reverse) { + // 检测文件名以 :x, :y, :o 结尾,表示左右翻转,上下翻转和中心翻转 + var ctx = this.getContextByName(name); + if (!ctx) return; + // var reverse = null; + if (typeof image == 'string') { + if (image.endsWith(':x') || image.endsWith(':y') || image.endsWith(':o')) { + reverse = image.charAt(image.length - 1); + image = image.substring(0, image.length - 2); + } + image = core.getMappedName(image); + image = core.material.images.images[image]; + if (!image) return; + } + + var scale = { + 'x': [-1, 1], + 'y': [1, -1], + 'o': [-1, -1] + }; + + // 只能接受2, 4, 8个参数 + if (x != null && y != null) { + if (w == null || h == null) { + // 两个参数变成四个参数 + w = image.width; + h = image.height; + } + if (x1 != null && y1 != null && w1 != null && h1 != null) { + if (!reverse && !angle) { + ctx.drawImage(image, x, y, w, h, x1, y1, w1, h1); + } else { + ctx.save(); + ctx.translate(x1 + w1 / 2, y1 + h1 / 2); + if (reverse) ctx.scale(scale[reverse][0], scale[reverse][1]); + if (angle) ctx.rotate(angle); + ctx.drawImage(image, x, y, w, h, -w1 / 2, -h1 / 2, w1, h1); + ctx.restore(); + } + return; + } + if (!reverse && !angle) { + ctx.drawImage(image, x, y, w, h); + } else { + ctx.save(); + ctx.translate(x + w / 2, y + h / 2); + if (reverse) ctx.scale(scale[reverse][0], scale[reverse][1]); + if (angle) ctx.rotate(angle); + ctx.drawImage(image, -w / 2, -h / 2, w, h); + ctx.restore(); + } + return; + } +} + +ui.prototype._uievent_drawImage = function (data) { + this._createUIEvent(); + this.drawImage('uievent', data.image + (data.reverse || ''), core.calValue(data.x), core.calValue(data.y), core.calValue(data.w), core.calValue(data.h), + core.calValue(data.x1), core.calValue(data.y1), core.calValue(data.w1), core.calValue(data.h1), (core.calValue(data.angle) || 0) * Math.PI / 180); +} + +ui.prototype.drawIcon = function (name, id, x, y, w, h, frame) { + frame = frame || 0; + var ctx = this.getContextByName(name); + if (!ctx) return; + var info = core.getBlockInfo(id); + if (!info) { + // 检查状态栏图标 + if (core.statusBar.icons[id] instanceof Image) + info = { image: core.statusBar.icons[id], posX: 0, posY: 0, height: 32 }; + else return; + } + core.drawImage(ctx, info.image, 32 * (info.posX + frame), info.height * info.posY, 32, info.height, x, y, w || 32, h || info.height); +} + +ui.prototype._uievent_drawIcon = function (data) { + this._createUIEvent(); + var id; + try { + id = core.calValue(data.id); + if (typeof id !== 'string') id = data.id; + } catch (e) { id = data.id; } + this.drawIcon('uievent', id, core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), data.frame || 0); +} + +///////////////// UI绘制 + +////// 结束一切事件和绘制,关闭UI窗口,返回游戏进程 ////// +ui.prototype.closePanel = function () { + if (core.status.hero && core.status.hero.flags) { + // 清除全部临时变量 + Object.keys(core.status.hero.flags).forEach(function (name) { + if (name.startsWith("@temp@") || /^arg\d+$/.test(name)) { + delete core.status.hero.flags[name]; + } + }); + } + this.clearUI(); + core.maps.generateGroundPattern(); + core.updateStatusBar(true); + core.unlockControl(); + core.status.event.data = null; + core.status.event.id = null; + core.status.event.selection = null; + core.status.event.ui = null; + core.status.event.interval = null; + // 清除onDownInterval + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = 'tmp'; +} + +ui.prototype.clearUI = function () { + core.status.boxAnimateObjs = []; + core.deleteCanvas("_selector"); + main.dom.next.style.display = 'none'; + main.dom.next.style.opacity = 1; + core.clearMap('ui'); + core.setAlpha('ui', 1); + core.setOpacity('ui', 1); + core.deleteCanvas('ui2'); +} + +////// 左上角绘制一段提示 ////// +ui.prototype.drawTip = function (text, id, frame) { + text = core.replaceText(text) || ""; + var realText = this._getRealContent(text); + var one = { + text: text, + textX: 21, + width: 26 + core.calWidth('data', realText, "16px Arial"), + opacity: 0.1, + stage: 1, + frame: frame || 0, + time: 0 + }; + if (id != null) { + var info = core.getBlockInfo(id); + if (info == null || !info.image || info.bigImage) { + // 检查状态栏图标 + if (core.statusBar.icons[id] instanceof Image) { + info = { image: core.statusBar.icons[id], posX: 0, posY: 0, height: 32 }; + } + else info = null; + } + if (info != null) { + one.image = info.image; + one.posX = info.posX; + one.posY = info.posY; + one.height = info.height; + one.textX += 24; + one.width += 24; + } + } + core.animateFrame.tip = one; +} + +ui.prototype._drawTip_drawOne = function (tip) { + core.setAlpha('data', tip.opacity); + core.fillRect('data', 5, 5, tip.width, 42, '#000000'); + if (tip.image) + core.drawImage('data', tip.image, (tip.posX + tip.frame) * 32, tip.posY * tip.height, 32, 32, 10, 10, 32, 32); + core.fillText('data', tip.text, tip.textX, 33, '#FFFFFF'); + core.setAlpha('data', 1); +} + +////// 地图中间绘制一段文字 ////// +ui.prototype.drawText = function (contents, callback) { + if (contents != null) return this._drawText_setContent(contents, callback); + + if (core.status.event.data.list.length == 0) { + var callback = core.status.event.data.callback; + core.ui.closePanel(false); + if (callback) callback(); + return; + } + + var data = core.status.event.data.list.shift(); + if (typeof data == 'string') data = { "text": data }; + core.ui.drawTextBox(data.text, data); +} + +ui.prototype._drawText_setContent = function (contents, callback) { + // 合并进 insertAction + if ((core.status.event && core.status.event.id == 'action') + || (!core.hasFlag('__replayText__') && core.isReplaying())) { + core.insertAction(contents, null, null, callback); + return; + } + if (!(contents instanceof Array)) contents = [contents]; + + core.status.event = { 'id': 'text', 'data': { 'list': contents, 'callback': callback } }; + core.lockControl(); + + core.waitHeroToStop(core.drawText); + return; +} + +////// 正则处理 \t[xx,yy] 问题 +ui.prototype._getTitleAndIcon = function (content) { + var title = null, image = null, icon = null, height = 32, animate = 1; + var bigImage = null, face = null; + content = content.replace(/(\t|\\t)\[(([^\],]+),)?([^\],]+)\]/g, function (s0, s1, s2, s3, s4) { + if (s4) { + if (s4 == 'hero') { + title = core.status.hero.name; + image = core.material.images.hero; + icon = 0; + var w = core.material.icons.hero.width || 32; + height = 32 * core.material.icons.hero.height / w; + } + else if (s4.endsWith(".png")) { + s4 = core.getMappedName(s4); + image = core.material.images.images[s4]; + } + else { + var blockInfo = core.getBlockInfo(s4); + if (blockInfo != null) { + if (blockInfo.name) title = blockInfo.name; + bigImage = blockInfo.bigImage; + face = blockInfo.face; + image = blockInfo.image; + icon = blockInfo.posY; + height = bigImage == null ? blockInfo.height : 32; + animate = blockInfo.animate; + } + else title = s4; + } + } + if (s3 != null) { + title = s3; + if (title == 'null') title = null; + } + return ""; + }); + return { + content: content, + title: title, + image: image, + icon: icon, + height: height, + animate: animate, + bigImage: bigImage, + face: face, + }; +} + +////// 正则处理 \b[up,xxx] 问题 +ui.prototype._getPosition = function (content) { + var pos = null, px = null, py = null, noPeak = false; + if (core.status.event.id == 'action') { + px = core.status.event.data.x; + py = core.status.event.data.y; + } + if (main.mode != 'play') { + px = editor.pos.x; + py = editor.pos.y; + } + content = content.replace("\b", "\\b") + .replace(/\\b\[(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?]/g, function (s0, s1, s2, s3) { + pos = s1; + if (s3 == 'hero' || s1 == 'hero' && !s3) { + px = core.getHeroLoc('x'); + py = core.getHeroLoc('y'); + } + else if (s3 == 'null') { + px = py = null; + } + else if (s3) { + var str = s3.split(','); + px = py = null; + if (str.length == 1) { + var follower = core.status.hero.followers[parseInt(str[0]) - 1]; + if (follower) { + px = follower.x; + py = follower.y; + } + } else { + px = parseInt(str[0]); + py = parseInt(str[1]); + noPeak = core.getBlockId(px, py) == null; + } + } + if (pos == 'hero' || pos == 'this') { + pos = py == null ? 'center' : (py > core._HALF_HEIGHT_ ? 'up' : 'down'); + } + return ""; + }); + return { content: content, position: pos, px: px, py: py, noPeak: noPeak }; +} + +////// 绘制系统选择光标 +ui.prototype._drawWindowSelector = function (background, x, y, w, h) { + w = Math.round(w), h = Math.round(h); + var ctx = core.ui.createCanvas("_selector", x, y, w, h, 165); + this._drawSelector(ctx, background, w, h); +} + +////// 自绘一个选择光标 +ui.prototype.drawUIEventSelector = function (code, background, x, y, w, h, z) { + var canvasName = '_uievent_selector_' + (code || 0); + var background = background || core.status.textAttribute.background; + if (typeof background != 'string') return; + if (main.mode == 'editor') { + this._drawSelector('uievent', background, w, h, x, y); + return; + } + z = z || (core.dymCanvas.uievent ? (parseInt(core.dymCanvas.uievent.canvas.style.zIndex) || 135) + 1 : 136); + var ctx = core.createCanvas(canvasName, x, y, w, h, z); + ctx.canvas.classList.add('_uievent_selector'); + this._drawSelector(ctx, background, w, h); +} + +ui.prototype._uievent_drawSelector = function (data) { + if (data.image == null) this.clearUIEventSelector(data.code || 0); + else this.drawUIEventSelector(data.code, data.image, core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height)); +} + +////// 清除自绘的选择光标 +ui.prototype.clearUIEventSelector = function (codes) { + if (codes == null) { + core.deleteCanvas(function (one) { return one.startsWith('_uievent_selector_'); }) + return; + } + if (codes instanceof Array) { + codes.forEach(function (code) { core.ui.clearUIEventSelector(code); }); + return; + } + core.deleteCanvas('_uievent_selector_' + (codes || 0)); +} + +ui.prototype._drawSelector = function (ctx, background, w, h, left, top) { + left = left || 0; + top = top || 0; + // back + core.drawImage(ctx, background, 130, 66, 28, 28, left + 2, top + 2, w - 4, h - 4); + // corner + core.drawImage(ctx, background, 128, 64, 2, 2, left, top, 2, 2); + core.drawImage(ctx, background, 158, 64, 2, 2, left + w - 2, top, 2, 2); + core.drawImage(ctx, background, 128, 94, 2, 2, left, top + h - 2, 2, 2); + core.drawImage(ctx, background, 158, 94, 2, 2, left + w - 2, top + h - 2, 2, 2); + // border + core.drawImage(ctx, background, 130, 64, 28, 2, left + 2, top, w - 4, 2); + core.drawImage(ctx, background, 130, 94, 28, 2, left + 2, top + h - 2, w - 4, 2); + core.drawImage(ctx, background, 128, 66, 2, 28, left, top + 2, 2, h - 4); + core.drawImage(ctx, background, 158, 66, 2, 28, left + w - 2, top + 2, 2, h - 4); +} + +////// 绘制 WindowSkin +ui.prototype.drawWindowSkin = function (background, ctx, x, y, w, h, direction, px, py) { + background = background || core.status.textAttribute.background; + // 仿RM窗口皮肤 ↓ + // 绘制背景 + core.drawImage(ctx, background, 0, 0, 128, 128, x + 2, y + 2, w - 4, h - 4); + // 绘制边框 + // 上方 + core.drawImage(ctx, background, 128, 0, 16, 16, x, y, 16, 16); + for (var dx = 0; dx < w - 64; dx += 32) { + core.drawImage(ctx, background, 144, 0, 32, 16, x + dx + 16, y, 32, 16); + core.drawImage(ctx, background, 144, 48, 32, 16, x + dx + 16, y + h - 16, 32, 16); + } + core.drawImage(ctx, background, 144, 0, w - dx - 32, 16, x + dx + 16, y, w - dx - 32, 16); + core.drawImage(ctx, background, 144, 48, w - dx - 32, 16, x + dx + 16, y + h - 16, w - dx - 32, 16); + core.drawImage(ctx, background, 176, 0, 16, 16, x + w - 16, y, 16, 16); + // 左右 + for (var dy = 0; dy < h - 64; dy += 32) { + core.drawImage(ctx, background, 128, 16, 16, 32, x, y + dy + 16, 16, 32); + core.drawImage(ctx, background, 176, 16, 16, 32, x + w - 16, y + dy + 16, 16, 32); + } + core.drawImage(ctx, background, 128, 16, 16, h - dy - 32, x, y + dy + 16, 16, h - dy - 32); + core.drawImage(ctx, background, 176, 16, 16, h - dy - 32, x + w - 16, y + dy + 16, 16, h - dy - 32); + // 下方 + core.drawImage(ctx, background, 128, 48, 16, 16, x, y + h - 16, 16, 16); + core.drawImage(ctx, background, 176, 48, 16, 16, x + w - 16, y + h - 16, 16, 16); + + // arrow + if (px != null && py != null) { + if (direction == 'up') { + core.drawImage(ctx, background, 128, 96, 32, 32, px, y + h - 3, 32, 32); + } else if (direction == 'down') { + core.drawImage(ctx, background, 160, 96, 32, 32, px, y - 29, 32, 32); + } + } + // 仿RM窗口皮肤 ↑ +} + +////// 绘制一个背景图,可绘制 winskin 或纯色背景;支持小箭头绘制 +ui.prototype.drawBackground = function (left, top, right, bottom, posInfo) { + posInfo = posInfo || {}; + var px = posInfo.px == null || posInfo.noPeak ? null : posInfo.px * 32 - core.bigmap.offsetX; + var py = posInfo.py == null || posInfo.noPeak ? null : posInfo.py * 32 - core.bigmap.offsetY; + var xoffset = posInfo.xoffset || 0, yoffset = posInfo.yoffset || 0; + var background = core.status.textAttribute.background; + + if (this._drawBackground_drawWindowSkin(background, left, top, right, bottom, posInfo.position, px, py, posInfo.ctx)) + return true; + if (typeof background == 'string') background = core.initStatus.textAttribute.background; + this._drawBackground_drawColor(background, left, top, right, bottom, posInfo.position, px, py, xoffset, yoffset, posInfo.ctx); + return false; +} + +ui.prototype._uievent_drawBackground = function (data) { + this._createUIEvent(); + var background = data.background || core.status.textAttribute.background; + var x = core.calValue(data.x), y = core.calValue(data.y), w = core.calValue(data.width), h = core.calValue(data.height); + if (typeof background == 'string') { + this.drawWindowSkin(background, 'uievent', x, y, w, h); + } + else if (background instanceof Array) { + this.fillRect('uievent', x, y, w, h, core.arrayToRGBA(background)); + this.strokeRect('uievent', x, y, w, h); + } +} + +ui.prototype._drawWindowSkin_getOpacity = function () { + return core.getFlag("__winskin_opacity__", 0.85); +} + +ui.prototype._drawBackground_drawWindowSkin = function (background, left, top, right, bottom, position, px, py, ctx) { + ctx = ctx || 'ui'; + if (typeof background == 'string' && core.material.images.images[background]) { + var image = core.material.images.images[background]; + if (image.width == 192 && image.height == 128) { + core.setAlpha(ctx, this._drawWindowSkin_getOpacity()); + this.drawWindowSkin(image, ctx, left, top, right - left, bottom - top, position, px, py); + core.setAlpha(ctx, 1); + return true; + } + } + return false; +} + +ui.prototype._drawBackground_drawColor = function (background, left, top, right, bottom, position, px, py, xoffset, yoffset, ctx) { + ctx = ctx || 'ui'; + var alpha = background[3]; + core.setAlpha(ctx, alpha); + core.setStrokeStyle(ctx, core.arrayToRGBA(core.status.globalAttribute.borderColor)); + core.setFillStyle(ctx, core.arrayToRGB(background)); + core.setLineWidth(ctx, 2); + // 绘制 + ctx = core.getContextByName(ctx); + ctx.beginPath(); + ctx.moveTo(left, top); + // 上边缘三角 + if (position == 'down' && px != null && py != null) { + ctx.lineTo(px + xoffset, top); + ctx.lineTo(px + 16, top - yoffset); + ctx.lineTo(px + 32 - xoffset, top); + } + ctx.lineTo(right, top); + ctx.lineTo(right, bottom); + // 下边缘三角 + if (position == 'up' && px != null && py != null) { + ctx.lineTo(px + 32 - xoffset, bottom); + ctx.lineTo(px + 16, bottom + yoffset); + ctx.lineTo(px + xoffset, bottom); + } + ctx.lineTo(left, bottom); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + core.setAlpha(ctx, 1); +} + +////// 计算有效文本框的宽度 +ui.prototype._calTextBoxWidth = function (ctx, content, min_width, max_width, font) { + // 无限长度自动换行 + var allLines = core.splitLines(ctx, content, null, font); + + // 如果不存在手动换行,尽量调成半行形式 + if (allLines.length == 1) { + var w = core.calWidth(ctx, allLines[0]) + 10; + if (w < min_width * 2.3) return core.clamp(w / 1.4, min_width, max_width); + if (w < max_width * 2.2) return core.clamp(w / 2.4, min_width, max_width); + return core.clamp(w / 3.4, min_width, max_width); + } + // 存在手动换行:以最长的为准 + else { + return core.clamp(allLines.reduce(function (pre, curr) { + return Math.max(pre, core.calWidth(ctx, curr) + 10); + }, 0), min_width, max_width); + } +} + +////// 处理 \i[xxx] 的问题 +ui.prototype._getDrawableIconInfo = function (id) { + if (id && id.indexOf('flag:') === 0) { + id = core.getFlag(id.substring(5), id); + } + id = core.getIdOfThis(id); + var image = null, icon = null; + ["terrains", "animates", "items", "npcs", "enemys", 'enemy48', 'npc48'].forEach(function (v) { + if (core.material.icons[v][id] != null) { + image = core.material.images[v]; + icon = core.material.icons[v][id]; + } + }); + if (image == null && id in core.statusBar.icons) { + image = core.statusBar.icons[id]; + icon = 0; + } + return [image, icon]; +} + +ui.prototype._buildFont = function (fontSize, bold, italic, font) { + var textAttribute = core.status.textAttribute || core.initStatus.textAttribute, + globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; + if (bold == null) bold = textAttribute.bold; + return (bold ? "bold " : "") + (italic ? "italic " : "") + (fontSize || textAttribute.textfont) + "px " + (font || globalAttribute.font); +} + +////// 绘制一段文字到某个画布上面 +// ctx:要绘制到的画布 +// content:要绘制的内容;转义字符目前只允许留 \n, \r[...], \i[...], \c[...], \d, \e +// config:绘制配置项,目前暂时包含如下内容(均为可选) +// left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右 +// fontSize:字体大小;lineHeight:行高;time:打字机间隔;font:字体类型;letterSpacing:字符间距 +ui.prototype.drawTextContent = function (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; + + 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; + } + tempCtx.textBaseline = 'top'; + tempCtx.font = this._buildFont(config.fontSize, config.bold, config.italic, config.font); + tempCtx.fillStyle = config.color; + config = this._drawTextContent_draw(ctx, tempCtx, content, config); + return config; +} + +ui.prototype._uievent_drawTextContent = function (data) { + this._createUIEvent(); + data.left = core.calValue(data.left); + data.top = core.calValue(data.top); + this.drawTextContent('uievent', core.replaceText(data.text), data); +} + +// 绘制的基本逻辑: +// 1. 一个个字符绘制到对应画布上(靠左对齐);这个过程中,记下来每个字对应的方块 [x, y, w, h] +// 2. 每次换行时,计算当前行的宽度,然后如果是居中或者靠右对齐,则对当前行的每个小方块增加偏移量 +// 3. 实际绘制时,从临时画布直接将一个个小方块绘制到目标画布上,一次全部绘制,或者打字机效果一个个绘制 +ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) { + // Step 1: 绘制到tempCtx上,并记录下图块信息 + while (this._drawTextContent_next(tempCtx, content, 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; + core.drawImage( + ctx, + 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 + ); + } + return true; + } + if (config.time == 0) { + while (_drawNext()); + } + else { + clearInterval(core.status.event.interval); + core.status.event.interval = setInterval(function () { + if (!_drawNext()) { + clearInterval(core.status.event.interval); + core.status.event.interval = null; + } + }, config.time); + } + + return config; +} + +ui.prototype._drawTextContent_next = function (tempCtx, content, config) { + if (config.index >= content.length) { + this._drawTextContent_newLine(tempCtx, config); + return false; + } + // get next character + var ch = content.charAt(config.index); + var code = content.charCodeAt(config.index++); + while (code >= 0xD800 && code <= 0xDBFF) { + ch += content.charAt(config.index); + code = content.charCodeAt(config.index++); + } + return this._drawTextContent_drawChar(tempCtx, content, config, ch); +} + +// 绘制下一个字符 +ui.prototype._drawTextContent_drawChar = function (tempCtx, content, config, ch) { + // 标点禁则:不能在行首的标点 + var forbidStart = "))】》>﹞>)]»›〕〉}]」}〗』" + ",。?!:;·…,.?!:;、……~&@#~&@#"; + // 标点禁则:不能在行尾的标点 + var forbidEnd = "((【《<﹝<([«‹〔〈{[「{〖『"; + + // \n, \\n + if (ch == '\n' || (ch == '\\' && content.charAt(config.index) == 'n')) { + this._drawTextContent_newLine(tempCtx, config); + if (ch == '\\') config.index++; + return this._drawTextContent_next(tempCtx, content, config); + } + // \r, \\r + if (ch == '\r' || (ch == '\\' && content.charAt(config.index) == 'r')) { + if (ch == '\\') config.index++; + return this._drawTextContent_changeColor(tempCtx, content, config); + } + if (ch == '\\') { + var c = content.charAt(config.index); + if (c == 'i') return this._drawTextContent_drawIcon(tempCtx, content, config); + if (c == 'c') return this._drawTextContent_changeFontSize(tempCtx, content, config); + if (c == 'd' || c == 'e') { + config.index++; + if (c == 'd') config.bold = !config.bold; + if (c == 'e') config.italic = !config.italic; + tempCtx.font = this._buildFont(config.currfont, config.bold, config.italic, config.font); + return true; + } + if (c == 'g') return this._drawTextContent_changeFont(tempCtx, content, config); + if (c == 'z') return this._drawTextContent_emptyChar(tempCtx, content, config); + } + // 检查是不是自动换行 + var charwidth = core.calWidth(tempCtx, ch) + config.letterSpacing; + if (config.maxWidth != null) { + if (config.offsetX + charwidth > config.maxWidth) { + // --- 当前应当换行,然而还是检查一下是否是forbidStart + if (!config.forceChangeLine && forbidStart.indexOf(ch) >= 0) { + config.forceChangeLine = true; + } else { + this._drawTextContent_newLine(tempCtx, config); + config.index -= ch.length; + return this._drawTextContent_next(tempCtx, content, config); + } + } else if (forbidEnd.indexOf(ch) >= 0 && config.index < content.length) { + // --- 当前不应该换行;但是提前检查一下是否是行尾标点 + var nextch = content.charAt(config.index); + // 确认不是手动换行 + if (nextch != '\n' && !(nextch == '\\' && content.charAt(config.index + 1) == 'n')) { + // 检查是否会换行 + var nextchwidth = core.calWidth(tempCtx, nextch) + config.letterSpacing; + if (config.offsetX + charwidth + nextchwidth > config.maxWidth) { + // 下一项会换行,因此在此处换行 + this._drawTextContent_newLine(tempCtx, config); + config.index -= ch.length; + return this._drawTextContent_next(tempCtx, content, config); + } + } + } + } + + // 输出 + var left = config.offsetX, top = config.offsetY + config.topMargin; + core.fillText(tempCtx, ch, left, top); + config.blocks.push({ + left: config.offsetX, top: config.offsetY, + width: charwidth, height: config.currfont + config.lineMargin, + line: config.line, marginLeft: 0 + }); + config.offsetX += charwidth; + return true; +} + +ui.prototype._drawTextContent_newLine = function (tempCtx, config) { + // 计算偏移量 + var width = config.offsetX, totalWidth = config.right - config.left; + var marginLeft = 0; + if (config.align == 'center') + marginLeft = (totalWidth - width) / 2; + else if (config.align == 'right') + marginLeft = totalWidth - width; + + config.blocks.forEach(function (b) { + if (b == null) return; + if (b.line == config.line) { + b.marginLeft = marginLeft; + // b.marginTop = 0; // 上对齐 + b.marginTop = (config.lineMaxHeight - b.height) / 2; // 居中对齐 + // b.marginTop = config.lineMaxHeight - b.height; // 下对齐 + } + }); + + config.offsetX = 0; + config.offsetY += config.lineMaxHeight; + config.lineMaxHeight = config.currfont + config.lineMargin; + config.line++; + config.forceChangeLine = false; +} + +ui.prototype._drawTextContent_changeColor = function (tempCtx, content, config) { + // 检查是不是 [] + var index = config.index, index2; + if (content.charAt(index) == '[' && ((index2 = content.indexOf(']', index)) >= 0)) { + // 变色 + var str = content.substring(index + 1, index2); + if (str == "") tempCtx.fillStyle = config.color; + else tempCtx.fillStyle = str; + config.index = index2 + 1; + } + else tempCtx.fillStyle = config.color; + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype._drawTextContent_changeFontSize = function (tempCtx, content, config) { + config.index++; + // 检查是不是 [] + var index = config.index, index2; + if (content.charAt(index) == '[' && ((index2 = content.indexOf(']', index)) >= 0)) { + var str = content.substring(index + 1, index2); + if (!/^\d+$/.test(str)) config.currfont = config.fontSize; + else config.currfont = parseInt(str); + config.index = index2 + 1; + } + else config.currfont = config.fontSize; + config.lineMaxHeight = Math.max(config.lineMaxHeight, config.currfont + config.lineMargin); + tempCtx.font = this._buildFont(config.currfont, config.bold, config.italic, config.font); + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype._drawTextContent_changeFont = function (tempCtx, content, config) { + config.index++; + // 检查是不是 [] + var index = config.index, index2; + if (content.charAt(index) == '[' && ((index2 = content.indexOf(']', index)) >= 0)) { + var str = content.substring(index + 1, index2); + if (str == "") config.font = config.defaultFont; + else config.font = str; + config.index = index2 + 1; + } else config.font = config.defaultFont; + tempCtx.font = this._buildFont(config.currfont, config.bold, config.italic, config.font); + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype._drawTextContent_emptyChar = function (tempCtx, content, config) { + config.index++; + var index = config.index, index2; + if (content.charAt(index) == '[' && ((index2 = content.indexOf(']', index)) >= 0)) { + var str = content.substring(index + 1, index2); + if (/^\d+$/.test(str)) { + var value = parseInt(str); + for (var i = 0; i < value; ++i) { + config.blocks.push(null); // Empty char + } + } else config.blocks.push(null); + config.index = index2 + 1; + } + else config.blocks.push(null); + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype._drawTextContent_drawIcon = function (tempCtx, content, config) { + // 绘制一个 \i 效果 + var index = config.index, index2; + if (content.charAt(config.index + 1) == '[' && ((index2 = content.indexOf(']', index + 1)) >= 0)) { + var str = core.replaceText(content.substring(index + 2, index2)); + // --- 获得图标 + var cls = core.getClsFromId(str) || ''; + var iconInfo = core.ui._getDrawableIconInfo(str), image = iconInfo[0], icon = iconInfo[1]; + if (image == null) return this._drawTextContent_next(tempCtx, content, config); + // 检查自动换行 + var width = config.currfont + 2, left = config.offsetX + 2, top = config.offsetY + config.topMargin - 1; + if (config.maxWidth != null && left + width > config.maxWidth) { + this._drawTextContent_newLine(tempCtx, config); + config.index--; + return this._drawTextContent_next(tempCtx, content, config); + } + // 绘制到画布上 + var height = 32; + if (cls.endsWith('48')) height = 48; + core.drawImage(tempCtx, image, 0, height * icon, 32, height, left, top, width, height === 48 ? width * 1.5 : width); + + config.blocks.push({ + left: left, top: config.offsetY, + width: width, height: width + config.lineMargin, + line: config.line, marginLeft: 0 + }); + + config.offsetX += width + 6; + config.index = index2 + 1; + return true; + } + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype.getTextContentHeight = function (content, config) { + return this.drawTextContent(null, content, config).offsetY; +} + +ui.prototype._getRealContent = function (content) { + return content.replace(/(\r|\\(r|c|d|e|g|z))(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "占1"); +} + +ui.prototype._animateUI = function (type, ctx, callback) { + ctx = ctx || 'ui'; + var time = core.status.textAttribute.animateTime || 0; + if (!core.status.event || !time || core.isReplaying() || (type != 'show' && type != 'hide')) { + if (callback) callback(); + return; + } + clearInterval(core.status.event.animateUI); + var opacity = 0; + if (type == 'show') { + opacity = 0; + } else if (type == 'hide') { + opacity = 1; + } + core.setOpacity(ctx, opacity); + core.dom.next.style.opacity = opacity; + core.status.event.animateUI = setInterval(function () { + if (type == 'show') opacity += 0.05; + else opacity -= 0.05; + core.setOpacity(ctx, opacity); + core.dom.next.style.opacity = opacity; + if (opacity >= 1 || opacity <= 0) { + clearInterval(core.status.event.animateUI); + delete core.status.event.animateUI; + if (callback) callback(); + } + }, time / 20); +} + +////// 绘制一个对话框 ////// +ui.prototype.drawTextBox = function (content, config) { + config = config || {}; + + this.clearUI(); + content = core.replaceText(content); + + var ctx = config.ctx || null; + if (ctx && main.mode == 'play') { + core.createCanvas(ctx, 0, 0, core._PX_, core._PY_, 141); + ctx = core.getContextByName(ctx); + } + + // Step 1: 获得标题信息和位置信息 + var textAttribute = core.status.textAttribute; + var titleInfo = this._getTitleAndIcon(content); + var posInfo = this._getPosition(titleInfo.content); + if (posInfo.position != 'up' && posInfo.position != 'down') posInfo.px = posInfo.py = null; + if (!posInfo.position) posInfo.position = textAttribute.position; + content = this._drawTextBox_drawImages(posInfo.content, config.ctx); + if (config.pos) { + delete posInfo.px; + delete posInfo.py; + posInfo.pos = config.pos; + } + posInfo.ctx = ctx; + + // Step 2: 计算对话框的矩形位置 + var hPos = this._drawTextBox_getHorizontalPosition(content, titleInfo, posInfo); + var vPos = this._drawTextBox_getVerticalPosition(content, titleInfo, posInfo, hPos.validWidth); + posInfo.xoffset = hPos.xoffset; + posInfo.yoffset = vPos.yoffset - 4; + + if (ctx && main.mode == 'play') { + ctx.canvas.setAttribute('_text_left', hPos.left); + ctx.canvas.setAttribute('_text_top', vPos.top); + } + + // Step 3: 绘制背景图 + var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom, posInfo); + var alpha = isWindowSkin ? this._drawWindowSkin_getOpacity() : textAttribute.background[3]; + + // Step 4: 绘制标题、头像、动画 + var content_top = this._drawTextBox_drawTitleAndIcon(titleInfo, hPos, vPos, alpha, config.ctx); + + // Step 5: 绘制正文 + var config = this.drawTextContent(config.ctx || 'ui', content, { + left: hPos.content_left, top: content_top, maxWidth: hPos.validWidth, + lineHeight: vPos.lineHeight, time: (config.showAll || config.async || textAttribute.time <= 0 || core.status.event.id != 'action') ? 0 : textAttribute.time + }); + + // Step 6: 绘制光标 + if (main.mode == 'play') { + main.dom.next.style.display = 'block'; + main.dom.next.style.borderRightColor = main.dom.next.style.borderBottomColor = core.arrayToRGB(textAttribute.text); + main.dom.next.style.top = (vPos.bottom - 20) * core.domStyle.scale + "px"; + var left = (hPos.left + hPos.right) / 2; + if (posInfo.position == 'up' && !posInfo.noPeak && posInfo.px != null && Math.abs(posInfo.px * 32 + 16 - left) < 50) + left = hPos.right - 64; + main.dom.next.style.left = left * core.domStyle.scale + "px"; + } + return config; +} + +ui.prototype._drawTextBox_drawImages = function (content, ctx) { + ctx = ctx || 'ui'; + return content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { + var ss = str.split(","); + // 绘制 + if (ss.length == 3) + core.drawImage(ctx, ss[0], parseFloat(ss[1]), parseFloat(ss[2])); + else if (ss.length == 5) + core.drawImage(ctx, ss[0], parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4])); + else if (ss.length >= 9) { + if (ss.length >= 10) core.setAlpha(ctx, parseFloat(ss[9])); + var angle = (parseFloat(ss[10]) || 0) * Math.PI / 180; + core.drawImage(ctx, ss[0], parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4]), + parseFloat(ss[5]), parseFloat(ss[6]), parseFloat(ss[7]), parseFloat(ss[8]), angle); + core.setAlpha(ctx, 1); + } + return ""; + }); +} + +ui.prototype._drawTextBox_getHorizontalPosition = function (content, titleInfo, posInfo) { + var ctx = posInfo.ctx || 'ui'; + var realContent = this._getRealContent(content); + var paddingLeft = 25, paddingRight = 12; + if ((posInfo.px != null && posInfo.py != null) || posInfo.pos) paddingLeft = 20; + if (titleInfo.icon != null) paddingLeft = 62; // 15 + 32 + 15 + else if (titleInfo.image) paddingLeft = 90; // 10 + 70 + 10 + var left = 7 + 3 * (core._HALF_WIDTH_ - 6), right = core._PX_ - left, + width = right - left, validWidth = width - paddingLeft - paddingRight; + // 对话框效果:改为动态计算 + if ((posInfo.px != null && posInfo.py != null) || posInfo.pos) { + var min_width = 220 - paddingLeft, max_width = validWidth; + // 无行走图或头像,则可以适当缩小min_width + if (titleInfo.image == null) min_width = 160; + if (titleInfo.title) { + min_width = core.clamp(core.calWidth(ctx, titleInfo.title, this._buildFont(core.status.textAttribute.titlefont, true)), min_width, max_width); + } + if (posInfo.pos) { + left = core.calValue(posInfo.pos[0]) || 0; + max_width = Math.max(min_width, right - left - paddingLeft - paddingRight); + } else left = null; + if (posInfo.pos && posInfo.pos[2] != null) { + width = core.calValue(posInfo.pos[2]) || 0; + min_width = validWidth = width - paddingLeft - paddingRight; + } else validWidth = 0; + if (validWidth < min_width) { + validWidth = this._calTextBoxWidth('ui', realContent, min_width, max_width, this._buildFont()); + width = validWidth + paddingLeft + paddingRight; + } + if (left == null) left = core.clamp(32 * posInfo.px + 16 - width / 2 - core.bigmap.offsetX, left, right - width); + right = left + width; + } + return { left: left, right: right, width: width, validWidth: validWidth, xoffset: 11, content_left: left + paddingLeft }; +} + +ui.prototype._drawTextBox_getVerticalPosition = function (content, titleInfo, posInfo, validWidth) { + var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; + var lineHeight = textAttribute.lineHeight || (textAttribute.textfont + 6); + var height = 45 + this.getTextContentHeight(content, { + lineHeight: lineHeight, maxWidth: validWidth + }); + if (titleInfo.title) height += textAttribute.titlefont + 5; + if (titleInfo.icon != null) { + if (titleInfo.title) height = Math.max(height, titleInfo.height + 50); + else height = Math.max(height, titleInfo.height + 30); + } + else if (titleInfo.image) + height = Math.max(height, 90); + + var yoffset = 16; + var top = parseInt((core._PY_ - height) / 2); + switch (posInfo.position) { + case 'center': top = parseInt((core._PY_ - height) / 2); break; + case 'up': + if (posInfo.px == null || posInfo.py == null) + top = 5 + textAttribute.offset; + else + top = 32 * posInfo.py - height - (titleInfo.height - 32) - yoffset - core.bigmap.offsetY; + break; + case 'down': + if (posInfo.px == null || posInfo.py == null) + top = core._PY_ - height - 5 - textAttribute.offset; + else { + top = 32 * posInfo.py + 32 + yoffset - core.bigmap.offsetY; + } + } + if (posInfo.pos) { + top = core.calValue(posInfo.pos[1]) || 0; + } + + return { top: top, height: height, bottom: top + height, yoffset: yoffset, lineHeight: lineHeight }; +} + +ui.prototype._drawTextBox_drawTitleAndIcon = function (titleInfo, hPos, vPos, alpha, ctx) { + ctx = ctx || 'ui'; + core.setTextAlign(ctx, 'left'); + var textAttribute = core.status.textAttribute; + var content_top = vPos.top + 15; + var image_top = vPos.top + 15; + if (titleInfo.title != null) { + var titlefont = textAttribute.titlefont; + content_top += titlefont + 5; + image_top = vPos.top + 40; + core.setFillStyle(ctx, core.arrayToRGB(textAttribute.title)); + core.setStrokeStyle(ctx, core.arrayToRGB(textAttribute.title)); + + // --- title也要居中或者右对齐? + var title_width = core.calWidth(ctx, titleInfo.title, this._buildFont(titlefont, true)); + var title_left = hPos.content_left; + if (textAttribute.align == 'center') + title_left = hPos.left + (hPos.width - title_width) / 2; + else if (textAttribute.align == 'right') + title_left = hPos.right - title_width - 12; + + core.fillText(ctx, titleInfo.title, title_left, vPos.top + 8 + titlefont); + } + if (titleInfo.icon != null) { + core.setAlpha(ctx, alpha); + core.strokeRect(ctx, hPos.left + 15 - 1, image_top - 1, 34, titleInfo.height + 2, null, 2); + core.setAlpha(ctx, 1); + core.status.boxAnimateObjs = []; + // --- 勇士 + if (titleInfo.image == core.material.images.hero) { + if (core.status.hero.animate) { + var direction = core.getHeroLoc('direction'); + if (direction == 'up') direction = 'down'; + core.status.boxAnimateObjs.push({ + 'bgx': hPos.left + 15, 'bgy': image_top, 'bgWidth': 32, 'bgHeight': titleInfo.height, + 'x': hPos.left + 15, 'y': image_top, 'height': titleInfo.height, 'animate': 4, + 'image': titleInfo.image, 'pos': core.material.icons.hero[direction].loc * titleInfo.height, ctx: ctx, + }) + } else { + core.clearMap(ctx, hPos.left + 15, image_top, 32, titleInfo.height); + core.fillRect(ctx, hPos.left + 15, image_top, 32, titleInfo.height, core.material.groundPattern); + core.drawImage(ctx, titleInfo.image, 0, 0, core.material.icons.hero.width || 32, core.material.icons.hero.height, + hPos.left + 15, image_top, 32, titleInfo.height); + } + } + else { + if (titleInfo.bigImage) { + core.status.boxAnimateObjs.push({ + bigImage: titleInfo.bigImage, face: titleInfo.face, centerX: hPos.left + 15 + 16, + centerY: image_top + titleInfo.height / 2, max_width: 50, ctx: ctx + }); + } else { + core.status.boxAnimateObjs.push({ + 'bgx': hPos.left + 15, 'bgy': image_top, 'bgWidth': 32, 'bgHeight': titleInfo.height, + 'x': hPos.left + 15, 'y': image_top, 'height': titleInfo.height, 'animate': titleInfo.animate, + 'image': titleInfo.image, 'pos': titleInfo.icon * titleInfo.height, ctx: ctx, + }); + } + } + core.drawBoxAnimate(); + } + if (titleInfo.image != null && titleInfo.icon == null) { // 头像图 + core.drawImage(ctx, titleInfo.image, 0, 0, titleInfo.image.width, titleInfo.image.height, + hPos.left + 10, vPos.top + 10, 70, 70); + } + return content_top; +} + +ui.prototype._createTextCanvas = function (content, lineHeight) { + var width = core._PX_, height = 30 + this.getTextContentHeight(content, { lineHeight: lineHeight }); + var ctx = document.createElement('canvas').getContext('2d'); + ctx.canvas.width = width; + ctx.canvas.height = height; + ctx.clearRect(0, 0, width, height); + return ctx; +} + +////// 绘制滚动字幕 ////// +ui.prototype.drawScrollText = function (content, time, lineHeight, callback) { + content = core.replaceText(content || ""); + lineHeight = lineHeight || 1.4; + time = time || 5000; + this.clearUI(); + var offset = core.status.textAttribute.offset || 15; + lineHeight *= core.status.textAttribute.textfont; + var ctx = this._createTextCanvas(content, lineHeight); + var obj = { align: core.status.textAttribute.align, lineHeight: lineHeight }; + if (obj.align == 'right') obj.left = core._PX_ - offset; + else if (obj.align != 'center') obj.left = offset; + this.drawTextContent(ctx, content, obj); + this._drawScrollText_animate(ctx, time, callback); +} + +ui.prototype._drawScrollText_animate = function (ctx, time, callback) { + // 开始绘制到UI上 + time /= Math.max(core.status.replay.speed, 1) + var per_pixel = 1, height = ctx.canvas.height, per_time = time * per_pixel / (core._PY_ + height); + var currH = core._PY_; + core.drawImage('ui', ctx.canvas, 0, currH); + var animate = setInterval(function () { + core.clearMap('ui'); + currH -= per_pixel; + if (currH < -height) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + return; + } + core.drawImage('ui', ctx.canvas, 0, currH); + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 文本图片化 ////// +ui.prototype.textImage = function (content, lineHeight) { + content = core.replaceText(content || ""); + lineHeight = lineHeight || 1.4; + lineHeight *= core.status.textAttribute.textfont; + var ctx = this._createTextCanvas(content, lineHeight); + this.drawTextContent(ctx, content, { align: core.status.textAttribute.align, lineHeight: lineHeight }); + return ctx.canvas; +} + +////// 绘制一个选项界面 ////// +ui.prototype.drawChoices = function (content, choices, width, ctx) { + choices = core.clone(choices || []); + + core.status.event.ui = { "text": content, "choices": choices, "width": width }; + this.clearUI(); + + content = core.replaceText(content || ""); + var titleInfo = this._getTitleAndIcon(content); + titleInfo.content = this._drawTextBox_drawImages(titleInfo.content, ctx); + var hPos = this._drawChoices_getHorizontalPosition(titleInfo, choices, width, ctx); + var vPos = this._drawChoices_getVerticalPosition(titleInfo, choices, hPos); + core.status.event.ui.offset = vPos.offset; + + var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom, { ctx: ctx }); + this._drawChoices_drawTitle(titleInfo, hPos, vPos, ctx); + this._drawChoices_drawChoices(choices, isWindowSkin, hPos, vPos, ctx); +} + +ui.prototype._drawChoices_getHorizontalPosition = function (titleInfo, choices, width, ctx) { + ctx = ctx || 'ui'; + // 宽度计算:考虑提示文字和选项的长度 + core.setFont(ctx, this._buildFont(17, true)); + var width = this._calTextBoxWidth(ctx, titleInfo.content || "", width || 246, core._PX_ - 20); + for (var i = 0; i < choices.length; i++) { + if (typeof choices[i] === 'string') + choices[i] = { "text": choices[i] }; + choices[i].text = core.replaceText(choices[i].text); + choices[i].width = core.calWidth(ctx, core.replaceText(choices[i].text)); + if (choices[i].icon != null) choices[i].width += 28; + width = Math.max(width, choices[i].width + 30); + } + var left = (core._PX_ - width) / 2, right = left + width; + var content_left = left + (titleInfo.icon == null ? 15 : 60), validWidth = right - content_left - 10; + + return { left: left, right: right, width: width, content_left: content_left, validWidth: validWidth }; +} + +ui.prototype._drawChoices_getVerticalPosition = function (titleInfo, choices, hPos) { + var length = choices.length; + var height = 32 * (length + 2), bottom = core._PY_ / 2 + height / 2; + if (length % 2 == 0) bottom += 16; + var offset = 0; + var choice_top = bottom - height + 56; + if (titleInfo.content) { + var headHeight = 0; + if (titleInfo.title) headHeight += 25; + headHeight += this.getTextContentHeight(titleInfo.content, { + lineHeight: 20, maxWidth: hPos.validWidth, fontSize: 15, bold: true + }); + height += headHeight; + if (bottom - height <= 32) { + offset = Math.floor(headHeight / 64); + bottom += 32 * offset; + choice_top += 32 * offset; + } + } + return { top: bottom - height, height: height, bottom: bottom, choice_top: choice_top, offset: offset }; +} + +ui.prototype._drawChoices_drawTitle = function (titleInfo, hPos, vPos, ctx) { + if (!titleInfo.content) return; + ctx = ctx || 'ui'; + var content_top = vPos.top + 21; + if (titleInfo.title != null) { + core.setTextAlign(ctx, 'center'); + + content_top = vPos.top + 41; + var title_offset = hPos.left + hPos.width / 2; + // 动画 + + if (titleInfo.icon != null) { + title_offset += 12; + core.strokeRect(ctx, hPos.left + 15 - 1, vPos.top + 30 - 1, 34, titleInfo.height + 2, '#DDDDDD', 2); + core.status.boxAnimateObjs = []; + if (titleInfo.bigImage) { + core.status.boxAnimateObjs.push({ + bigImage: titleInfo.bigImage, face: titleInfo.face, centerX: hPos.left + 15 + 16, + centerY: vPos.top + 30 + titleInfo.height / 2, max_width: 50, ctx: ctx + }); + } else { + core.status.boxAnimateObjs.push({ + 'bgx': hPos.left + 15, 'bgy': vPos.top + 30, 'bgWidth': 32, 'bgHeight': titleInfo.height, + 'x': hPos.left + 15, 'y': vPos.top + 30, 'height': titleInfo.height, 'animate': titleInfo.animate, + 'image': titleInfo.image, 'pos': titleInfo.icon * titleInfo.height, ctx: ctx + }); + } + core.drawBoxAnimate(); + }; + + core.fillText(ctx, titleInfo.title, title_offset, vPos.top + 27, + core.arrayToRGBA(core.status.textAttribute.title), this._buildFont(19, true)); + } + + core.setTextAlign(ctx, 'left'); + this.drawTextContent(ctx, titleInfo.content, { + left: hPos.content_left, top: content_top, maxWidth: hPos.validWidth, + fontSize: 15, lineHeight: 20, bold: true + }); +} + +ui.prototype._drawChoices_drawChoices = function (choices, isWindowSkin, hPos, vPos, ctx) { + var hasCtx = ctx != null; + ctx = ctx || 'ui'; + // 选项 + core.setTextAlign(ctx, 'center'); + core.setFont(ctx, this._buildFont(17, true)); + for (var i = 0; i < choices.length; i++) { + var color = core.arrayToRGBA(choices[i].color || core.status.textAttribute.text); + if (main.mode == 'play' && choices[i].need != null && choices[i].need != '' && !core.calValue(choices[i].need)) color = '#999999'; + core.setFillStyle(ctx, color); + var offset = core._PX_ / 2; + if (choices[i].icon) { + var iconInfo = this._getDrawableIconInfo(choices[i].icon), image = iconInfo[0], icon = iconInfo[1]; + if (image != null) { + core.drawImage(ctx, image, 0, 32 * icon, 32, 32, + core._PX_ / 2 - choices[i].width / 2, vPos.choice_top + 32 * i - 17, 22, 22); + offset += 14; + } + } + core.fillText(ctx, choices[i].text, offset, vPos.choice_top + 32 * i, color); + } + + if (choices.length > 0 && core.status.event.selection != 'none') { + core.status.event.selection = core.status.event.selection || 0; + while (core.status.event.selection < 0) core.status.event.selection += choices.length; + while (core.status.event.selection >= choices.length) core.status.event.selection -= choices.length; + var len = choices[core.status.event.selection].width; + if (isWindowSkin) { + if (hasCtx) { + this._drawSelector(ctx, core.status.textAttribute.background, + len + 10, 28, core._PX_ / 2 - len / 2 - 5, vPos.choice_top + 32 * core.status.event.selection - 20); + } else { + this._drawWindowSelector(core.status.textAttribute.background, + core._PX_ / 2 - len / 2 - 5, vPos.choice_top + 32 * core.status.event.selection - 20, len + 10, 28); + } + } + else + core.strokeRoundRect(ctx, core._PX_ / 2 - len / 2 - 5, vPos.choice_top + 32 * core.status.event.selection - 20, + len + 10, 28, 6, core.status.globalAttribute.selectColor, 2); + } +} + +////// 绘制一个确认/取消的警告页面 ////// +ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback, ctx) { + var hasCtx = ctx != null; + ctx = ctx || 'ui'; + text = core.replaceText(text || ""); + + if (main.mode == 'play') { + core.lockControl(); + + // 处理自定义事件 + if (core.status.event.id != 'action') { + core.status.event.id = 'confirmBox'; + core.status.event.ui = text; + core.status.event.data = { 'yes': yesCallback, 'no': noCallback }; + } + } + + if (core.status.event.selection != 0 && core.status.event.selection != 'none') core.status.event.selection = 1; + this.clearUI(); + + core.setFont(ctx, this._buildFont(19, true)); + var contents = text.split("\n"); + var rect = this._drawConfirmBox_getRect(contents, ctx); + var isWindowSkin = this.drawBackground(rect.left, rect.top, rect.right, rect.bottom, { ctx: ctx }); + + core.setTextAlign(ctx, 'center'); + core.setFillStyle(ctx, core.arrayToRGBA(core.status.textAttribute.text)) + for (var i in contents) { + core.fillText(ctx, contents[i], core._PX_ / 2, rect.top + 50 + i * 30); + } + + core.fillText(ctx, "确定", core._PX_ / 2 - 38, rect.bottom - 35, null, this._buildFont(17, true)); + core.fillText(ctx, "取消", core._PX_ / 2 + 38, rect.bottom - 35); + if (core.status.event.selection != 'none') { + var len = core.calWidth(ctx, "确定"); + var strokeLeft = core._PX_ / 2 + (76 * core.status.event.selection - 38) - parseInt(len / 2) - 5; + + if (isWindowSkin) { + if (hasCtx) { + this._drawSelector(ctx, core.status.textAttribute.background, + len + 10, 28, strokeLeft, rect.bottom - 35 - 20); + } else { + this._drawWindowSelector(core.status.textAttribute.background, strokeLeft, rect.bottom - 35 - 20, len + 10, 28); + } + } + else + core.strokeRoundRect(ctx, strokeLeft, rect.bottom - 35 - 20, len + 10, 28, 6, core.status.globalAttribute.selectColor, 2); + } +} + +ui.prototype._drawConfirmBox_getRect = function (contents, ctx) { + var max_width = contents.reduce(function (pre, curr) { + return Math.max(pre, core.calWidth(ctx, curr)); + }, 0); + var left = Math.min(core._PX_ / 2 - 40 - parseInt(max_width / 2), 100), right = core._PX_ - left; + var top = core._PY_ / 2 - 68 - (contents.length - 1) * 30, bottom = core._PY_ / 2 + 68; + return { top: top, left: left, bottom: bottom, right: right, width: right - left, height: bottom - top }; +} + +////// 绘制等待界面 ////// +ui.prototype.drawWaiting = function (text) { + core.lockControl(); + core.status.event.id = 'waiting'; + core.clearUI(); + text = core.replaceText(text || ""); + var text_length = core.calWidth('ui', text, this._buildFont(19, true)); + var width = Math.max(text_length + 80, 220), left = core._PX_ / 2 - parseInt(width / 2), right = left + width; + var top = core._PY_ / 2 - 48, height = 96, bottom = top + height; + this.drawBackground(left, top, right, bottom); + core.setTextAlign('ui', 'center'); + core.fillText('ui', text, core._PX_ / 2, top + 56, core.arrayToRGBA(core.status.textAttribute.text)); +} + +////// 绘制系统设置界面 ////// +ui.prototype._drawSwitchs = function () { + core.status.event.id = 'switchs'; + var choices = [ + "音效设置", + "显示设置", + /*"操作设置",*/ + "返回主菜单" + ]; + this.drawChoices(null, choices); +} + +ui.prototype._drawSwitchs_sounds = function () { + core.status.event.id = 'switchs-sounds'; + var choices = [ + "音乐: " + (core.musicStatus.bgmStatus ? "[ON]" : "[OFF]"), + "音效: " + (core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), + // 显示为 0~10 十挡 + " < 音乐音量:" + Math.round(Math.sqrt(100 * core.musicStatus.userVolume)) + " > ", + " < 音效音量:" + Math.round(Math.sqrt(100 * core.musicStatus.userVolume2)) + " > ", + "返回上一级" + ]; + this.drawChoices(null, choices); +} + +ui.prototype._drawSwitchs_display = function () { + core.status.event.id = 'switchs-display'; + var choices = [ + " < 放缩:" + Math.max(core.domStyle.scale, 1) + "x > ", + "高清画面: " + (core.flags.enableHDCanvas ? "[ON]" : "[OFF]"), + "定点怪显: " + (core.flags.enableEnemyPoint ? "[ON]" : "[OFF]"), + "怪物显伤: " + (core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"), + "临界显伤: " + (core.flags.displayCritical ? "[ON]" : "[OFF]"), + "领域显伤: " + (core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), + "领域模式: " + (core.flags.extraDamageType == 2 ? "[最简]" : core.flags.extraDamageType == 1 ? "[半透明]" : "[完整]"), + "自动放缩: " + (core.getLocalStorage('autoScale') ? "[ON]" : "[OFF]"), + "返回上一级", + ]; + this.drawChoices(null, choices); +} +/* +ui.prototype._drawSwitchs_action = function () { + core.status.event.id = 'switchs-action'; + var choices = [ + // 数值越大耗时越长 + " < 步时:" + core.values.moveSpeed + " > ", + " < 转场:" + core.values.floorChangeTime + " > ", + "血瓶绕路: " + (core.hasFlag('__potionNoRouting__') ? "[ON]" : "[OFF]"), + "单击瞬移: " + (!core.hasFlag("__noClickMove__") ? "[ON]" : "[OFF]"), + "左手模式: " + (core.flags.leftHandPrefer ? "[ON]" : "[OFF]"), + "返回上一级", + ]; + this.drawChoices(null, choices); +}*/ + +////// 绘制系统菜单栏 ////// +ui.prototype._drawSettings = function () { + core.status.event.id = 'settings'; + this.drawChoices(null, [ + "系统设置", "虚拟键盘", "浏览地图", "存档笔记", "同步存档", "游戏信息", "返回标题", "返回游戏" + ]); +} + +////// 绘制存档笔记 ////// +ui.prototype._drawNotes = function () { + core.status.event.id = 'notes'; + core.status.hero.notes = core.status.hero.notes || []; + core.lockControl(); + this.drawChoices("存档笔记允许你写入和查看任何笔记(快捷键M),你可以用做任何标记,比如Boss前的属性、开门和路线选择等。", [ + "新增存档笔记", "查看存档笔记", "编辑存档笔记", "删除存档笔记", "返回上一页" + ]); +} + +////// 绘制快捷商店选择栏 ////// +ui.prototype._drawQuickShop = function () { + core.status.event.id = 'selectShop'; + var shopList = core.status.shops, keys = core.listShopIds(); + var choices = keys.map(function (shopId) { + return { "text": shopList[shopId].textInList, "color": core.isShopVisited(shopId) ? null : "#999999" }; + }); + choices.push("返回游戏"); + this.drawChoices(null, choices); +} + +////// 绘制存档同步界面 ////// +ui.prototype._drawSyncSave = function () { + core.status.event.id = 'syncSave'; + this.drawChoices(null, [ + "同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", /*"回放和下载录像", "清空本地存档",*/ "返回主菜单" + ]); +} + +////// 绘制存档同步选择页面 ////// +ui.prototype._drawSyncSelect = function () { + core.status.event.id = 'syncSelect'; + this.drawChoices(null, [ + "同步本地所有存档", "只同步当前单存档", "返回上级菜单" + ]); +} + +////// 绘制单存档界面 ////// +ui.prototype._drawLocalSaveSelect = function () { + core.status.event.id = 'localSaveSelect'; + this.drawChoices(null, [ + "下载所有存档", "只下载当前单存档", "返回上级菜单" + ]); +} + +////// 绘制存档删除页面 ////// +/*ui.prototype._drawStorageRemove = function () { + core.status.event.id = 'storageRemove'; + this.drawChoices(null, [ + "清空全部塔的存档", "只清空当前塔的存档", "返回上级菜单" + ]); +}*/ +/* +ui.prototype._drawReplay = function () { + core.lockControl(); + core.status.event.id = 'replay'; + core.playSound('打开界面'); + this.drawChoices(null, [ + "从头回放录像", "从存档开始回放", "接续播放剩余录像", "播放存档剩余录像", "选择录像文件", "下载当前录像", "返回游戏" + ]); +}*/ + +ui.prototype._drawGameInfo = function () { + core.status.event.id = 'gameInfo'; + this.drawChoices(null, [ + /*"数据统计",*/ "查看工程", "游戏主页", /*"操作帮助", */"关于游戏", "下载离线版本", "返回主菜单" + ]); +} + +////// 绘制分页 ////// +ui.prototype.drawPagination = function (page, totalPage, y) { + // if (totalPage 1) + core.fillText('ui', '上一页', core._PX_ / 2 - 80, y * 32 + 19); + if (page < totalPage) + core.fillText('ui', '下一页', core._PX_ / 2 + 80, y * 32 + 19); +} + +////// 绘制键盘光标 ////// +ui.prototype._drawCursor = function () { + var automaticRoute = core.status.automaticRoute; + automaticRoute.cursorX = core.clamp(automaticRoute.cursorX, 0, core._WIDTH_ - 1); + automaticRoute.cursorY = core.clamp(automaticRoute.cursorY, 0, core._HEIGHT_ - 1); + core.status.event.id = 'cursor'; + core.lockControl(); + core.clearUI(); + var width = 4; + core.strokeRect('ui', 32 * automaticRoute.cursorX + width / 2, 32 * automaticRoute.cursorY + width / 2, + 32 - width, 32 - width, core.status.globalAttribute.selectColor, width); + +} + +////// 绘制怪物手册 ////// +ui.prototype.drawBook = function (index) { + var floorId = core.floorIds[(core.status.event.ui || {}).index] || core.status.floorId; + // 清除浏览地图时的光环缓存 + if (floorId != core.status.floorId && core.status.checkBlock) { + core.status.checkBlock.cache = {}; + } + var enemys = core.enemys.getCurrentEnemys(floorId); + core.clearUI(); + core.clearMap('data'); + // 生成groundPattern + core.maps.generateGroundPattern(floorId); + this._drawBook_drawBackground(); + core.setAlpha('ui', 1); + + if (enemys.length == 0) { + return this._drawBook_drawEmpty(); + } + + index = core.clamp(index, 0, enemys.length - 1); + core.status.event.data = index; + var pageinfo = this._drawBook_pageinfo(); + var perpage = pageinfo.per_page, page = parseInt(index / perpage) + 1, totalPage = Math.ceil(enemys.length / perpage); + + var start = (page - 1) * perpage; + enemys = enemys.slice(start, page * perpage); + + for (var i = 0; i < enemys.length; i++) + this._drawBook_drawOne(floorId, i, enemys[i], pageinfo, index == start + i); + + core.drawBoxAnimate(); + this.drawPagination(page, totalPage); + core.setTextAlign('ui', 'center'); + core.fillText('ui', '返回游戏', core._PX_ - 46, core._PY_ - 13, '#DDDDDD', this._buildFont(15, true)); +} + +ui.prototype._drawBook_pageinfo = function () { + var per_page = core._HALF_HEIGHT_; + var padding_top = 12; // 距离顶端像素 + var per_height = (core._PY_ - 32 - padding_top) / per_page; + return { per_page: per_page, padding_top: padding_top, per_height: per_height }; +} + +ui.prototype._drawBook_drawBackground = function () { + core.setAlpha('ui', 1); + core.setFillStyle('ui', core.material.groundPattern); + core.fillRect('ui', 0, 0, core._PX_, core._PY_); + + core.setAlpha('ui', 0.6); + core.setFillStyle('ui', '#000000'); + core.fillRect('ui', 0, 0, core._PX_, core._PY_); +} + +ui.prototype._drawBook_drawEmpty = function () { + core.setTextAlign('ui', 'center'); + core.fillText('ui', "本层无怪物", core._PX_ / 2, core._PY_ / 2 + 14, '#999999', this._buildFont(50, true)); + core.fillText('ui', '返回游戏', core._PX_ - 46, core._PY_ - 13, '#DDDDDD', this._buildFont(15, true)); +} + +ui.prototype._drawBook_drawOne = function (floorId, index, enemy, pageinfo, selected) { + var top = pageinfo.per_height * index + pageinfo.padding_top; // 最上面margin默认是12px + enemy.floorId = floorId; + // 横向规划: + // 22 + 42 = 64 是头像框 + this._drawBook_drawBox(index, enemy, top, pageinfo); + var left = 64, total_width = core._PX_ - left; + var name_width = total_width * 10 / 35; + this._drawBook_drawName(index, enemy, top, left, name_width); + this._drawBook_drawContent(index, enemy, top, left + name_width); + if (selected) + core.strokeRoundRect('ui', 10, top + 1, core._PX_ - 10 * 2, pageinfo.per_height, 10, core.status.globalAttribute.selectColor); +} + +ui.prototype._drawBook_is32x32 = function (blockInfo) { + // 判定48的怪物上半部分是否是全透明 + var height = blockInfo.height - 32; + var canvas = document.createElement('canvas'); + canvas.width = 32; canvas.height = height; + var ctx = canvas.getContext("2d"); + core.drawImage(ctx, blockInfo.image, 0, blockInfo.posY * blockInfo.height, 32, height, 0, 0, 32, height); + var url = canvas.toDataURL(); + core.clearMap(ctx); + return url == canvas.toDataURL(); +} + +ui.prototype._drawBook_drawBox = function (index, enemy, top, pageinfo) { + // 横向:22+42;纵向:10 + 42 + 10(正好居中);内部图像 32x32 + var border_top = top + (pageinfo.per_height - 42) / 2, border_left = 22; + var img_top = border_top + 5, img_left = border_left + 5; + core.strokeRect('ui', 22, border_top, 42, 42, '#DDDDDD', 2); + var blockInfo = core.getBlockInfo(enemy.id); + + // 检查大怪物 + if (blockInfo.bigImage) { + core.status.boxAnimateObjs.push({ + bigImage: blockInfo.bigImage, face: blockInfo.face, centerX: border_left + 21, centerY: border_top + 21, + max_width: 60 + }); + } + else if (blockInfo.height >= 42) { + var originEnemy = core.material.enemys[enemy.id] || {}; + // 检查上半部分是不是纯透明的;取用原始值避免重复计算 + if (originEnemy.is32x32 == null) { + originEnemy.is32x32 = this._drawBook_is32x32(blockInfo); + } + if (originEnemy.is32x32) { + core.status.boxAnimateObjs.push({ + 'bgx': border_left, 'bgy': border_top, 'bgWidth': 42, 'bgHeight': 42, + 'x': img_left, 'y': img_top, 'height': 32, 'animate': blockInfo.animate, + 'image': blockInfo.image, 'pos': blockInfo.posY * blockInfo.height + blockInfo.height - 32 + }); + } else { + var drawWidth = 42 * 32 / blockInfo.height; + core.status.boxAnimateObjs.push({ + 'bgx': border_left, 'bgy': border_top, 'bgWidth': 42, 'bgHeight': 42, + 'x': img_left - 5 + (42 - drawWidth) / 2, 'y': img_top - 5, 'dw': drawWidth, 'dh': 42, + 'height': blockInfo.height, 'animate': blockInfo.animate, + 'image': blockInfo.image, 'pos': blockInfo.posY * blockInfo.height + }); + } + } else { + core.status.boxAnimateObjs.push({ + 'bgx': border_left, 'bgy': border_top, 'bgWidth': 42, 'bgHeight': 42, + 'x': img_left, 'y': img_top, 'height': 32, 'animate': blockInfo.animate, + 'image': blockInfo.image, 'pos': blockInfo.posY * blockInfo.height + }); + } +} + +ui.prototype._drawBook_drawName = function (index, enemy, top, left, width) { + // 绘制第零列(名称和特殊属性) + // 如果需要添加自己的比如怪物的称号等,也可以在这里绘制 + core.setTextAlign('ui', 'center'); + if (enemy.specialText.length == 0) { + core.fillText('ui', enemy.name, left + width / 2, + top + 35, '#DDDDDD', this._buildFont(17, true), width); + } + else { + core.fillText('ui', enemy.name, left + width / 2, + top + 28, '#DDDDDD', this._buildFont(17, true), width); + switch (enemy.specialText.length) { + case 1: + core.fillText('ui', enemy.specialText[0], left + width / 2, + top + 50, core.arrayToRGBA((enemy.specialColor || [])[0] || '#FF6A6A'), + this._buildFont(15, true), width); + break; + case 2: + // Step 1: 计算字体 + var text = enemy.specialText[0] + " " + enemy.specialText[1]; + core.setFontForMaxWidth('ui', text, width, this._buildFont(15, true)); + // Step 2: 计算总宽度 + var totalWidth = core.calWidth('ui', text); + var leftWidth = core.calWidth('ui', enemy.specialText[0]); + var rightWidth = core.calWidth('ui', enemy.specialText[1]); + // Step 3: 绘制 + core.fillText('ui', enemy.specialText[0], left + (width + leftWidth - totalWidth) / 2, + top + 50, core.arrayToRGBA((enemy.specialColor || [])[0] || '#FF6A6A')); + core.fillText('ui', enemy.specialText[1], left + (width + totalWidth - rightWidth) / 2, + top + 50, core.arrayToRGBA((enemy.specialColor || [])[1] || '#FF6A6A')); + break; + default: + core.fillText('ui', '多属性...', left + width / 2, + top + 50, '#FF6A6A', this._buildFont(15, true), width); + } + } +} + +ui.prototype._drawBook_drawContent = function (index, enemy, top, left) { + var width = core._PX_ - left; // 9 : 8 : 8 划分三列 + this._drawBook_drawRow1(index, enemy, top, left, width, top + 20); + this._drawBook_drawRow2(index, enemy, top, left, width, top + 38); + this._drawBook_drawRow3(index, enemy, top, left, width, top + 56); +} + +ui.prototype._drawBook_drawRow1 = function (index, enemy, top, left, width, position) { + // 绘制第一行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); + var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; + core.fillText('ui', core.getStatusLabel('hp'), col1, position, '#DDDDDD', f13); + core.fillText('ui', core.formatBigNumber(enemy.hp || 0), col1 + 30, position, null, b13); + core.fillText('ui', core.getStatusLabel('atk'), col2, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.atk || 0), col2 + 30, position, null, b13); + core.fillText('ui', core.getStatusLabel('def'), col3, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.def || 0), col3 + 30, position, null, b13); +} + +ui.prototype._drawBook_drawRow2 = function (index, enemy, top, left, width, position) { + // 绘制第二行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); + var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; + // 获得第二行绘制的内容 + var second_line = []; + if (core.flags.statusBarItems.indexOf('enableMoney') >= 0) second_line.push([core.getStatusLabel('money'), core.formatBigNumber(enemy.money || 0)]); + if (core.flags.enableAddPoint) second_line.push([core.getStatusLabel('point'), core.formatBigNumber(enemy.point || 0)]); + if (core.flags.statusBarItems.indexOf('enableExp') >= 0) second_line.push([core.getStatusLabel('exp'), core.formatBigNumber(enemy.exp || 0)]); + + var damage_offset = col1 + (core._PX_ - col1) / 2 - 12; + // 第一列 + if (second_line.length > 0) { + var one = second_line.shift(); + core.fillText('ui', one[0], col1, position, '#DDDDDD', f13); + core.fillText('ui', one[1], col1 + 30, position, null, b13); + damage_offset = col2 + (core._PX_ - col2) / 2 - 12; + } + // 第二列 + if (second_line.length > 0) { + var one = second_line.shift(); + core.fillText('ui', one[0], col2, position, '#DDDDDD', f13); + core.fillText('ui', one[1], col2 + 30, position, null, b13); + damage_offset = col3 + (core._PX_ - col3) / 2 - 12; + } + // 忽略第三列,直接绘制伤害 + this._drawBook_drawDamage(index, enemy, damage_offset, position); +} + +ui.prototype._drawBook_drawRow3 = function (index, enemy, top, left, width, position) { + // 绘制第三行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); + var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; + core.fillText('ui', '临界', col1, position, '#DDDDDD', f13); + core.fillText('ui', core.formatBigNumber(enemy.critical || 0), col1 + 30, position, null, b13); + core.fillText('ui', '减伤', col2, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.criticalDamage || 0), col2 + 30, position, null, b13); + core.fillText('ui', '加防', col3, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.defDamage || 0), col3 + 30, position, null, b13); +} + +ui.prototype._drawBook_drawDamage = function (index, enemy, offset, position) { + core.setTextAlign('ui', 'center'); + var damage = enemy.damage, color = '#FFFF00'; + if (damage == null) { + damage = '无法战斗'; + color = '#FF2222'; + } + else { + if (damage >= core.status.hero.hp) color = '#FF2222'; + else if (damage >= core.status.hero.hp * 2 / 3) color = '#FF9933'; + else if (damage <= 0) color = '#11FF11'; + damage = core.formatBigNumber(damage); + if (core.enemys.hasSpecial(enemy, 19)) damage += "+"; + if (core.enemys.hasSpecial(enemy, 21)) damage += "-"; + if (core.enemys.hasSpecial(enemy, 11)) damage += "^"; + } + if (enemy.notBomb) damage += "[b]"; + core.fillText('ui', damage, offset, position, color, this._buildFont(13, true)); +} + +////// 绘制怪物属性的详细信息 ////// +ui.prototype._drawBookDetail = function (index) { + var info = this._drawBookDetail_getInfo(index), enemy = info[0]; + if (!enemy) return; + var content = info[1].join("\n"); + core.status.event.id = 'book-detail'; + core.animateFrame.tip = null; + core.clearMap('data'); + + var left = 10, width = core._PX_ - 2 * left, right = left + width; + var content_left = left + 25, validWidth = right - content_left - 13; + var height = Math.max(this.getTextContentHeight(content, { fontSize: 16, lineHeight: 24, maxWidth: validWidth }) + 58, 80), + top = (core._PY_ - height) / 2, bottom = top + height; + + core.setAlpha('data', 0.9); + core.fillRect('data', left, top, width, height, '#000000'); + core.setAlpha('data', 1); + core.strokeRect('data', left - 1, top - 1, width + 1, height + 1, + core.arrayToRGBA(core.status.globalAttribute.borderColor), 2); + core.playSound('确定'); + this._drawBookDetail_drawContent(enemy, content, { top: top, content_left: content_left, bottom: bottom, validWidth: validWidth }); +} + +ui.prototype._drawBookDetail_getInfo = function (index) { + var floorId = core.floorIds[(core.status.event.ui || {}).index] || core.status.floorId; + // 清除浏览地图时的光环缓存 + if (floorId != core.status.floorId && core.status.checkBlock) { + core.status.checkBlock.cache = {}; + } + var enemys = core.enemys.getCurrentEnemys(floorId); + if (enemys.length == 0) return []; + index = core.clamp(index, 0, enemys.length - 1); + var enemy = enemys[index], enemyId = enemy.id; + var texts = core.enemys.getSpecialHint(enemyId); + if (texts.length == 0) texts.push("该怪物无特殊属性。"); + if (enemy.description) texts.push(enemy.description + "\r"); + texts.push(""); + this._drawBookDetail_getTexts(enemy, floorId, texts); + return [enemy, texts]; +} + +ui.prototype._drawBookDetail_getTexts = function (enemy, floorId, texts) { + // --- 原始数值 + this._drawBookDetail_origin(enemy, texts); + // --- 模仿临界计算器 + this._drawBookDetail_mofang(enemy, texts); + // --- 吸血怪最低生命值 + this._drawBookDetail_vampire(enemy, floorId, texts); + // --- 仇恨伤害 + this._drawBookDetail_hatred(enemy, texts); + // --- 战斗回合数,临界表 + this._drawBookDetail_turnAndCriticals(enemy, floorId, texts); +} + +ui.prototype._drawBookDetail_origin = function (enemy, texts) { + // 怪物数值和原始值不一样时,在详细信息页显示原始数值 + var originEnemy = core.enemys._getCurrentEnemys_getEnemy(enemy.id); + var content = []; + if (enemy.locs != null && enemy.locs.length >= 0) { + texts.push("\r[#FF6A6A]\\d怪物坐标:\\d\r[]" + JSON.stringify(enemy.locs)); + } + ["hp", "atk", "def", "point", "money", "exp"].forEach(function (one) { + if (enemy[one] == null || originEnemy[one] == null) return; + if (enemy[one] != originEnemy[one]) { + content.push(core.getStatusLabel(one) + " " + originEnemy[one]); + } + }); + if (content.length > 0) { + texts.push("\r[#FF6A6A]\\d原始数值:\\d\r[]" + content.join(";")); + } +} + +ui.prototype._drawBookDetail_mofang = function (enemy, texts) { + // 模仿临界计算器 + if (core.enemys.hasSpecial(enemy.special, 10)) { + var hp = enemy.hp; + var delta = core.status.hero.atk - core.status.hero.def; + if (delta < hp && hp <= 10000 && hp > 0) { + texts.push("\r[#FF6A6A]\\d模仿临界计算器:\\d\r[](当前攻防差" + core.formatBigNumber(delta) + ")"); + var u = []; + this._drawBookDetail_mofang_getArray(hp).forEach(function (t) { + if (u.length < 20) u.push(t); + else if (Math.abs(t[0] - delta) < Math.abs(u[0][0] - delta)) { + u.shift(); + u.push(t); + } + }); + texts.push(JSON.stringify(u.map(function (v) { + return core.formatBigNumber(v[0]) + ":" + v[1]; + }))); + } + } +} + +ui.prototype._drawBookDetail_mofang_getArray = function (hp) { + var arr = []; + var last = 0, start = 0; + for (var i = 1; i < hp; i++) { + var now = parseInt((hp - 1) / i); + if (now != last) { + if (last != 0) { + arr.push([start, last + "x"]); + } + last = now; + start = i; + } + } + if (last != 0) { + arr.push([start, "1x"]); + arr.push([hp, "0"]); + } + return arr; +} + +ui.prototype._drawBookDetail_vampire = function (enemy, floorId, texts) { + if (core.enemys.hasSpecial(enemy.special, 11)) { + var damage = core.getDamage(enemy.id); + if (damage != null) { + // 二分HP + var start = 1, end = 100 * damage; + var nowHp = core.status.hero.hp; + while (start < end) { + var mid = Math.floor((start + end) / 2); + core.status.hero.hp = mid; + if (core.canBattle(enemy.id, enemy.x, enemy.y, floorId)) end = mid; + else start = mid + 1; + } + core.status.hero.hp = start; + if (core.canBattle(enemy.id)) { + texts.push("\r[#FF6A6A]\\d打死该怪物最低需要生命值:\\d\r[]" + core.formatBigNumber(start)); + } + core.status.hero.hp = nowHp; + } + } +} + +ui.prototype._drawBookDetail_hatred = function (enemy, texts) { + if (core.enemys.hasSpecial(enemy.special, 17)) { + texts.push("\r[#FF6A6A]\\d当前仇恨伤害值:\\d\r[]" + core.getFlag('hatred', 0)); + } +} + +ui.prototype._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) { + var damageInfo = core.getDamageInfo(enemy.id, null, enemy.x, enemy.y, floorId); + texts.push("\r[#FF6A6A]\\d战斗回合数:\\d\r[]" + ((damageInfo || {}).turn || 0)); + // 临界表 + var criticals = core.enemys.nextCriticals(enemy.id, 8, enemy.x, enemy.y, floorId).map(function (v) { + return core.formatBigNumber(v[0]) + ":" + core.formatBigNumber(v[1]); + }); + while (criticals[0] == '0:0') criticals.shift(); + texts.push("\r[#FF6A6A]\\d临界表:\\d\r[]" + JSON.stringify(criticals)); +} + +ui.prototype._drawBookDetail_drawContent = function (enemy, content, pos) { + // 名称 + core.setTextAlign('data', 'left'); + core.fillText('data', enemy.name, pos.content_left, pos.top + 30, core.status.globalAttribute.selectColor, this._buildFont(22, true)); + var content_top = pos.top + 44; + + this.drawTextContent('data', content, { + left: pos.content_left, top: content_top, maxWidth: pos.validWidth, + fontSize: 16, lineHeight: 24 + }); +} + +////// 绘制楼层传送器 ////// +ui.prototype.drawFly = function (page) { + core.status.event.data = page; + var floorId = core.floorIds[page]; + var title = core.status.maps[floorId].title; + core.clearMap('ui'); + core.setAlpha('ui', 0.85); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); + core.setAlpha('ui', 1); + core.setTextAlign('ui', 'center'); + core.fillText('ui', '楼层跳跃', core._PX_ / 2, 48, '#FFFFFF', this._buildFont(28, true)); + core.fillText('ui', '返回游戏', core._PX_ / 2, core._PY_ - 13, null, this._buildFont(15, true)) + core.setTextAlign('ui', 'right'); + core.fillText('ui', '浏览地图时也', core._PX_ - 10, core._PY_ - 23, '#aaaaaa', this._buildFont(10, false)); + core.fillText('ui', '可楼层跳跃!', core._PX_ - 10, core._PY_ - 11, null, this._buildFont(10, false)); + core.setTextAlign('ui', 'center'); + + var middle = core._PY_ / 2 + 39; + const lastWidth = 0.25 * core._PX_ - 16; + + // 换行 + var lines = core.splitLines('ui', title, lastWidth, this._buildFont(19, true)); + var start_y = middle - (lines.length - 1) * 11; + for (var i in lines) { + core.fillText('ui', lines[i], core._PX_ - lastWidth * 0.5, start_y, '#FFFFFF'); + start_y += 22; + } + if (core.actions._getNextFlyFloor(1) != page) { + core.fillText('ui', '▲', core._PX_ - lastWidth * 0.5, middle - 64, null, this._buildFont(17, false)); + core.fillText('ui', '▲', core._PX_ - lastWidth * 0.5, middle - 96); + core.fillText('ui', '▲', core._PX_ - lastWidth * 0.5, middle - 96 - 7); + } + if (core.actions._getNextFlyFloor(-1) != page) { + core.fillText('ui', '▼', core._PX_ - lastWidth * 0.5, middle + 64, null, this._buildFont(17, false)); + core.fillText('ui', '▼', core._PX_ - lastWidth * 0.5, middle + 96); + core.fillText('ui', '▼', core._PX_ - lastWidth * 0.5, middle + 96 + 7); + } + var size = 0.75; + core.strokeRect('ui', 16, 64, size * core._PX_, size * core._PY_, '#FFFFFF', 2); + core.drawThumbnail(floorId, null, { ctx: 'ui', x: 16, y: 64, size: size, damage: true, all: true }); +} + +////// 绘制中心对称飞行器 +ui.prototype._drawCenterFly = function () { + core.lockControl(); + core.status.event.id = 'centerFly'; + var fillstyle = 'rgba(255,0,0,0.5)'; + if (core.canUseItem('centerFly')) fillstyle = 'rgba(0,255,0,0.5)'; + var toX = core.bigmap.width - 1 - core.getHeroLoc('x'), toY = core.bigmap.height - 1 - core.getHeroLoc('y'); + this.clearUI(); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); + core.drawThumbnail(null, null, { heroLoc: core.status.hero.loc, heroIcon: core.status.hero.image, ctx: 'ui', centerX: toX, centerY: toY }); + var offsetX = core.clamp(toX - core._HALF_WIDTH_, 0, core.bigmap.width - core._WIDTH_), + offsetY = core.clamp(toY - core._HALF_HEIGHT_, 0, core.bigmap.height - core._HEIGHT_); + core.fillRect('ui', (toX - offsetX) * 32, (toY - offsetY) * 32, 32, 32, fillstyle); + core.status.event.data = { "x": toX, "y": toY, "posX": toX - offsetX, "posY": toY - offsetY }; + core.playSound('打开界面'); + core.drawTip("请确认当前" + core.material.items['centerFly'].name + "的位置", 'centerFly'); + return; +} + +////// 绘制浏览地图界面 ////// +ui.prototype._drawViewMaps = function (index, x, y) { + core.lockControl(); + core.status.event.id = 'viewMaps'; + this.clearUI(); + if (index == null) index = core.floorIds.indexOf(core.status.floorId); + //if (index == null) return this._drawViewMaps_drawHint(); + core.animateFrame.tip = null; + core.status.checkBlock.cache = {}; + var data = this._drawViewMaps_buildData(index, x, y); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); + if (index == core.floorIds.indexOf(core.status.floorId)) { + core.drawThumbnail(data.floorId, null, { heroLoc: core.status.hero.loc, heroIcon: "head1.png", damage: data.damage, ctx: 'ui', centerX: data.x, centerY: data.y, all: data.all }); + } else { + core.drawThumbnail(data.floorId, null, { damage: data.damage, ctx: 'ui', centerX: data.x, centerY: data.y, all: data.all }); + } + core.clearMap('data'); + core.setTextAlign('data', 'left'); + core.setFont('data', '16px Arial'); + var x1 = Math.round(core.status.hero.loc.x * 6.64) - 59.73 - 200; + var y1 = Math.round(core.status.hero.loc.y * 6.64) - 59.73 - 200; + var w1 = 120 + 400; + var h1 = 120 + 400; + if (flags.mapview != undefined) { + if (flags.mapview.indexOf(data.floorId) >= 0) { + if(data.floorId == core.status.floorId){ + core.strokeRect('ui', x1, y1, w1, h1, '#000000', 390); + }else{ + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); + } + } + } + // var text = core.status.maps[data.floorId].title; + // if (!data.all && (data.mw > core._WIDTH_ || data.mh > core._HEIGHT_)) + // text += " [" + (data.x - core._HALF_WIDTH_) + "," + (data.y - core._HALF_HEIGHT_) + "]"; + // if (core.markedFloorIds[data.floorId]) + // text += " (已标记)"; + // var textX = 16, textY = 18, width = textX + core.calWidth('data', text) + 16, height = 42; + // core.fillRect('data', 5, 5, width, height, 'rgba(0,0,0,0.4)'); + // core.fillText('data', text, textX + 5, textY + 15, 'rgba(255,255,255,0.6)'); +} + +ui.prototype._drawViewMaps_drawHint = function () { + core.playSound('打开界面'); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, 'rgba(0,0,0,0.7)'); + core.setTextAlign('ui', 'center'); + var stroke = function (left, top, width, height, fillStyle, lineWidth) { + core.strokeRect('ui', left + 2, top + 2, width - 4, height - 4, fillStyle, lineWidth); + } + + var perpx = core._PX_ / 5, cornerpx = perpx * 3 / 4, perpy = core._PY_ / 5, cornerpy = perpy * 3 / 4; + stroke(perpx, 0, 3 * perpx, perpy, core.status.globalAttribute.selectColor, 4); // up + stroke(0, perpy, perpx, 3 * perpy); // left + stroke(perpx, 4 * perpy, 3 * perpx, perpy); // down + stroke(4 * perpx, perpy, perpx, 3 * perpy); // right + stroke(perpx, perpy, 3 * perpx, perpy); // prev + stroke(perpx, 3 * perpy, 3 * perpx, perpy); // next + stroke(0, 0, cornerpx, cornerpy); // left top + stroke(core._PX_ - cornerpx, 0, cornerpx, cornerpy); // right top + stroke(0, core._PY_ - cornerpy, cornerpx, cornerpy); // left bottom; + + core.setTextBaseline('ui', 'middle'); + core.fillText('ui', "上移地图 [W]", core._PX_ / 2, perpy / 2, core.status.globalAttribute.selectColor, '20px Arial'); + core.fillText('ui', "下移地图 [S]", core._PX_ / 2, core._PY_ - perpy / 2); + core.fillText('ui', 'V', cornerpx / 2, cornerpy / 2); + core.fillText('ui', 'Z', core._PX_ - cornerpx / 2, cornerpy / 2); + core.fillText('ui', 'B', cornerpx / 2, core._PY_ - cornerpy / 2); + + var top = core._PY_ / 2 - 66, left = perpx / 2, right = core._PX_ - left; + var lt = ["左", "移", "地", "图", "[A]"], rt = ["右", "移", "地", "图", "[D]"]; + for (var i = 0; i < 5; ++i) { + core.fillText("ui", lt[i], left, top + 32 * i); + core.fillText("ui", rt[i], right, top + 32 * i); + } + core.fillText('ui', "前张地图 [▲ / PGUP]", core._PX_ / 2, perpy * 1.5); + core.fillText('ui', "后张地图 [▼ / PGDN]", core._PX_ / 2, core._PY_ - perpy * 1.5); + + core.fillText('ui', "退出 [ESC / ENTER]", core._PX_ / 2, core._PY_ / 2); + core.fillText('ui', "[X] 可查看" + core.material.items['book'].name + + " [G] 可使用" + core.material.items['fly'].name, core._PX_, core._PY_ + 32, null, '12px Arial'); + + core.setTextBaseline('ui', 'alphabetic'); +} + +ui.prototype._drawViewMaps_buildData = function (index, x, y) { + var damage = (core.status.event.data || {}).damage; + var all = (core.status.event.data || { all: true }).all; + if (index.damage != null) damage = index.damage; + if (index.all != null) all = index.all; + if (index.index != null) { x = index.x; y = index.y; index = index.index; } + index = core.clamp(index, 0, core.floorIds.length - 1); + if (damage == null) damage = true; // 浏览地图默认开显伤好了 + + var floorId = core.floorIds[index], mw = core.floors[floorId].width, mh = core.floors[floorId].height; + if (x == null) x = parseInt(mw / 2); + if (y == null) y = parseInt(mh / 2); + x = core.clamp(x, core._HALF_WIDTH_, mw - core._HALF_WIDTH_ - 1); + y = core.clamp(y, core._HALF_HEIGHT_, mh - core._HALF_HEIGHT_ - 1); + + core.status.event.data = { + index: index, x: x, y: y, floorId: floorId, mw: mw, mh: mh, + damage: damage, all: all + }; + return core.status.event.data; +} + +////// 绘制道具栏 ////// +ui.prototype._drawToolbox = function (index) { + var info = this._drawToolbox_getInfo(index); + this._drawToolbox_drawBackground(); + + // 绘制线 + core.setAlpha('ui', 1); + core.setStrokeStyle('ui', '#DDDDDD'); + core.canvas.ui.lineWidth = 2; + core.canvas.ui.strokeWidth = 2; + core.setTextAlign('ui', 'right'); + var line1 = core._PY_ - 306; + this._drawToolbox_drawLine(line1, "消耗道具"); + var line2 = core._PY_ - 146; + this._drawToolbox_drawLine(line2, "永久道具"); + + this._drawToolbox_drawDescription(info, line1); + + this._drawToolbox_drawContent(info, line1, info.tools, info.toolsPage, true); + this.drawPagination(info.toolsPage, info.toolsTotalPage, core._HEIGHT_ - 6); + this._drawToolbox_drawContent(info, line2, info.constants, info.constantsPage); + this.drawPagination(info.constantsPage, info.constantsTotalPage); + + core.setTextAlign('ui', 'center'); + core.fillText('ui', '[装备栏]', core._PX_ - 46, 25, '#DDDDDD', this._buildFont(15, true)); + core.fillText('ui', '返回游戏', core._PX_ - 46, core._PY_ - 13); +} + +////// 获得所有应该在道具栏显示的某个类型道具 ////// +ui.prototype.getToolboxItems = function (cls) { + if (this.uidata.getToolboxItems) { + return this.uidata.getToolboxItems(cls); + } + return Object.keys(core.status.hero.items[cls] || {}) + .filter(function (id) { return !core.material.items[id].hideInToolbox; }) + .sort(); +} + +ui.prototype._drawToolbox_getInfo = function (index) { + // 设定eventdata + if (!core.status.event.data || core.status.event.data.toolsPage == null) + core.status.event.data = { "toolsPage": 1, "constantsPage": 1, "selectId": null } + // 获取物品列表 + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + // 处理页数 + var n = core._WIDTH_ - 1; + var toolsPage = core.status.event.data.toolsPage; + var constantsPage = core.status.event.data.constantsPage; + var toolsTotalPage = Math.ceil(tools.length / n); + var constantsTotalPage = Math.ceil(constants.length / n); + // 处理index + if (index == null) + index = tools.length == 0 && constants.length > 0 ? n : 0; + core.status.event.selection = index; + // 确认选择对象 + var select, selectId; + if (index < n) { + select = index + (toolsPage - 1) * n; + if (select >= tools.length) select = Math.max(0, tools.length - 1); + selectId = tools[select]; + } + else { + select = index % n + (constantsPage - 1) * n; + if (select >= constants.length) select = Math.max(0, constants.length - 1); + selectId = constants[select]; + } + if (!core.hasItem(selectId)) selectId = null; + core.status.event.data.selectId = selectId; + return { + index: index, tools: tools, constants: constants, toolsPage: toolsPage, constantsPage: constantsPage, + toolsTotalPage: toolsTotalPage, constantsTotalPage: constantsTotalPage, selectId: selectId + }; +} + +ui.prototype._drawToolbox_drawBackground = function () { + // 绘制 + core.clearMap('ui'); + core.setAlpha('ui', 0.85); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); +} + +ui.prototype._drawToolbox_drawLine = function (yoffset, text) { + core.setFillStyle('ui', '#DDDDDD'); + core.canvas.ui.beginPath(); + core.canvas.ui.moveTo(0, yoffset); + core.canvas.ui.lineTo(core._PX_, yoffset); + core.canvas.ui.stroke(); + core.canvas.ui.beginPath(); + core.canvas.ui.moveTo(core._PX_, yoffset - 1); + core.canvas.ui.lineTo(core._PX_, yoffset - 25); + core.canvas.ui.lineTo(core._PX_ - 72, yoffset - 25); + core.canvas.ui.lineTo(core._PX_ - 102, yoffset - 1); + core.canvas.ui.fill(); + core.fillText('ui', text, core._PX_ - 5, yoffset - 6, '#333333', this._buildFont(16, true)); +} + +ui.prototype._drawToolbox_drawDescription = function (info, max_height) { + core.setTextAlign('ui', 'left'); + if (!info.selectId) return; + var item = core.material.items[info.selectId]; + var name = item.name || "未知道具"; + try { name = core.replaceText(name); } catch (e) { } + core.fillText('ui', name, 10, 32, core.status.globalAttribute.selectColor, this._buildFont(20, true)) + var text = item.text || "该道具暂无描述。"; + try { text = core.replaceText(text); } catch (e) { } + + var height = null; + for (var fontSize = 17; fontSize >= 9; fontSize -= 2) { + var config = { left: 10, top: 46, fontSize: fontSize, maxWidth: core._PX_ - 15, bold: false, color: "white" }; + height = 42 + core.getTextContentHeight(text, config); + if (height < max_height || fontSize == 9) { + core.drawTextContent('ui', text, config); + break; + } + } + if (height < max_height - 33) { + core.fillText('ui', '<继续点击该道具即可进行使用>', 10, max_height - 15, '#CCCCCC', this._buildFont(14, false)); + } +} + +ui.prototype._drawToolbox_drawContent = function (info, line, items, page, drawCount) { + var n = core._HALF_WIDTH_; + core.setTextAlign('ui', 'right'); + for (var i = 0; i < core._WIDTH_ - 1; i++) { + var item = items[(core._WIDTH_ - 1) * (page - 1) + i]; + if (!item) continue; + var yoffset = line + 54 * Math.floor(i / n) + 19; + var icon = core.material.icons.items[item], image = core.material.images.items; + core.drawImage('ui', image, 0, 32 * icon, 32, 32, 64 * (i % n) + 21, yoffset, 32, 32); + if (drawCount) + core.fillText('ui', core.itemCount(item), 64 * (i % n) + 56, yoffset + 33, '#FFFFFF', this._buildFont(14, true)); + if (info.selectId == item) + core.strokeRoundRect('ui', 64 * (i % n) + 17, yoffset - 4, 40, 40, 6, core.status.globalAttribute.selectColor); + } +} + +////// 绘制装备界面 ////// +ui.prototype._drawEquipbox = function (index) { + var info = this._drawEquipbox_getInfo(index); + this._drawToolbox_drawBackground(); + + core.setAlpha('ui', 1); + core.setStrokeStyle('ui', '#DDDDDD'); + core.canvas.ui.lineWidth = 2; + core.canvas.ui.strokeWidth = 2; + core.setTextAlign('ui', 'right'); + var line1 = core._PY_ - 306; + this._drawToolbox_drawLine(line1, "当前装备"); + var line2 = core._PY_ - 146; + this._drawToolbox_drawLine(line2 + 9, "拥有装备"); + + this._drawEquipbox_description(info, line1); + + this._drawEquipbox_drawEquiped(info, line1); + this._drawToolbox_drawContent(info, line2, info.ownEquipment, info.page, true); + this.drawPagination(info.page, info.totalPage); + + core.setTextAlign('ui', 'center'); + core.fillText('ui', '[道具栏]', core._PX_ - 46, 25, '#DDDDDD', this._buildFont(15, true)); + core.fillText('ui', '返回游戏', core._PX_ - 46, core._PY_ - 13); +} + +ui.prototype._drawEquipbox_getInfo = function (index) { + if (!core.status.event.data || core.status.event.data.page == null) + core.status.event.data = { "page": 1, "selectId": null }; + var allEquips = core.status.globalAttribute.equipName; + var equipLength = allEquips.length; + if (!core.status.hero.equipment) core.status.hero.equipment = []; + var n = core._WIDTH_ - 1; + var equipEquipment = core.status.hero.equipment; + var ownEquipment = core.getToolboxItems('equips'); + var page = core.status.event.data.page; + var totalPage = Math.ceil(ownEquipment.length / n); + // 处理index + if (index == null) { + if (equipLength > 0 && equipEquipment[0]) index = 0; + else if (ownEquipment.length > 0) index = n; + else index = 0; + } + if (index >= n && ownEquipment.length == 0) index = 0; + var selectId = null; + if (index < n) { + if (index >= equipLength) index = Math.max(0, equipLength - 1); + selectId = equipEquipment[index] || null; + } + else { + if (page == totalPage) index = Math.min(index, (ownEquipment.length + n - 1) % n + n); + selectId = ownEquipment[index - n + (page - 1) * n]; + if (!core.hasItem(selectId)) selectId = null; + } + core.status.event.selection = index; + core.status.event.data.selectId = selectId; + return { + index: index, selectId: selectId, page: page, totalPage: totalPage, allEquips: allEquips, + equipLength: equipLength, equipEquipment: equipEquipment, ownEquipment: ownEquipment + }; +} + +ui.prototype._drawEquipbox_description = function (info, max_height) { + core.setTextAlign('ui', 'left'); + if (!info.selectId) return; + var equip = core.material.items[info.selectId]; + // --- 标题 + if (!equip.equip) equip.equip = { "type": 0 }; + var equipType = equip.equip.type, equipString; + if (typeof equipType === 'string') { + equipString = equipType || "未知部位"; + equipType = core.items.getEquipTypeByName(equipType); + } + else equipString = info.allEquips[equipType] || "未知部位"; + core.fillText('ui', equip.name + "(" + equipString + ")", 10, 32, core.status.globalAttribute.selectColor, this._buildFont(20, true)) + // --- 描述 + var text = equip.text || "该装备暂无描述。"; + try { text = core.replaceText(text); } catch (e) { } + + var height = null; + for (var fontSize = 17; fontSize >= 9; fontSize -= 2) { + var config = { left: 10, top: 46, fontSize: fontSize, maxWidth: core._PX_ - 15, bold: false, color: "white" }; + height = 42 + core.getTextContentHeight(text, config); + if (height < max_height - 30 || fontSize == 9) { + core.drawTextContent('ui', text, config); + break; + } + } + // --- 变化值 + this._drawEquipbox_drawStatusChanged(info, max_height - 15, equip, equipType); +} + +ui.prototype._drawEquipbox_getStatusChanged = function (info, equip, equipType, y) { + if (info.index < core._WIDTH_ - 1) { + // 光标在装备栏上:查询卸下装备属性 + return core.compareEquipment(null, info.selectId); + } + if (equipType < 0) { + // 没有空位 + core.fillText('ui', '<当前没有该装备的空位,请先卸下装备>', 10, y, '#CCCCCC', this._buildFont(14, false)); + return null; + } + // 光标在装备上:查询装上后的属性变化 + return core.compareEquipment(info.selectId, info.equipEquipment[equipType]); +} + +ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipType) { + var compare = this._drawEquipbox_getStatusChanged(info, equip, equipType, y); + if (compare == null) return; + var obj = { drawOffset: 10, y: y }; + + // --- 变化值... + core.setFont('ui', this._buildFont(14, true)); + for (var name in core.status.hero) { + if (typeof core.status.hero[name] != 'number') continue; + var nowValue = core.getRealStatus(name); + // 查询新值 + var newValue = Math.floor((core.getStatus(name) + (compare.value[name] || 0)) + * (core.getBuff(name) * 100 + (compare.percentage[name] || 0)) / 100); + if (nowValue == newValue) continue; + var text = core.getStatusLabel(name); + this._drawEquipbox_drawStatusChanged_draw(text + " ", '#CCCCCC', obj); + var color = newValue > nowValue ? '#00FF00' : '#FF0000'; + nowValue = core.formatBigNumber(nowValue); + newValue = core.formatBigNumber(newValue); + this._drawEquipbox_drawStatusChanged_draw(nowValue + "->", '#CCCCCC', obj); + this._drawEquipbox_drawStatusChanged_draw(newValue, color, obj); + obj.drawOffset += 8; + } +} + +ui.prototype._drawEquipbox_drawStatusChanged_draw = function (text, color, obj) { + var len = core.calWidth('ui', text); + if (obj.drawOffset + len >= core._PX_) { // 换行 + obj.y += 19; + obj.drawOffset = 10; + } + core.fillText('ui', text, obj.drawOffset, obj.y, color); + obj.drawOffset += len; +} + +ui.prototype._drawEquipbox_drawEquiped = function (info, line) { + core.setTextAlign('ui', 'center'); + var per_line = core._HALF_WIDTH_ - 3, width = Math.floor(core._PX_ / (per_line + 0.25)); + // 当前装备 + for (var i = 0; i < info.equipLength; i++) { + var equipId = info.equipEquipment[i] || null; + // var offset_text = width * (i % per_line) + 56; + var offset_image = width * (i % per_line) + width * 2 / 3; + var offset_text = offset_image - (width - 32) / 2; + var y = line + 54 * Math.floor(i / per_line) + 19; + if (equipId) { + var icon = core.material.icons.items[equipId]; + core.drawImage('ui', core.material.images.items, 0, 32 * icon, 32, 32, offset_image, y, 32, 32); + } + core.fillText('ui', info.allEquips[i] || "未知", offset_text, y + 27, '#FFFFFF', this._buildFont(16, true)) + core.strokeRoundRect('ui', offset_image - 4, y - 4, 40, 40, 6, info.index == i ? core.status.globalAttribute.selectColor : "#FFFFFF"); + } +} + +////// 绘制存档/读档界面 ////// +ui.prototype._drawSLPanel = function (index, refresh) { + core.control._loadFavoriteSaves(); + if (index == null) index = 1; + if (index < 0) index = 0; + + var page = parseInt(index / 10), offset = index % 10; + var max_page = main.savePages || 30; + if (core.status.event.data && core.status.event.data.mode == 'fav') + max_page = Math.ceil((core.saves.favorite || []).length / 5); + if (page >= max_page) page = max_page - 1; + if (offset > 5) offset = 5; + if (core.status.event.data && core.status.event.data.mode == 'fav' && page == max_page - 1) { + offset = Math.min(offset, (core.saves.favorite || []).length - 5 * page); + } + + var last_page = -1; + var mode = 'all'; + if (core.status.event.data) { + last_page = core.status.event.data.page; + mode = core.status.event.data.mode; + } + core.status.event.data = { 'page': page, 'offset': offset, 'mode': mode }; + core.status.event.ui = core.status.event.ui || []; + if (refresh || page != last_page) { + core.status.event.ui = []; + this._drawSLPanel_loadSave(page, function () { + core.ui._drawSLPanel_draw(page, max_page); + }); + } + else this._drawSLPanel_draw(page, max_page); +} + +ui.prototype._drawSLPanel_draw = function (page, max_page) { + // --- 绘制背景 + this._drawSLPanel_drawBackground(); + // --- 绘制文字 + core.ui.drawPagination(page + 1, max_page); + core.setTextAlign('ui', 'center'); + var bottom = core._PY_ - 13; + core.fillText('ui', '返回游戏', core._PX_ - 48, bottom, '#DDDDDD', this._buildFont(15, true)); + + if (core.status.event.selection) + core.setFillStyle('ui', '#FF6A6A'); + if (core.status.event.id == 'save') + core.fillText('ui', '删除模式', 48, bottom); + else { + if (core.status.event.data.mode == 'all') { + core.fillText('ui', '[E]显示收藏', 52, bottom); + } else { + core.fillText('ui', '[E]显示全部', 52, bottom); + } + } + // --- 绘制记录 + this._drawSLPanel_drawRecords(); +} + +ui.prototype._drawSLPanel_drawBackground = function () { + core.clearMap('ui'); + core.setAlpha('ui', 0.85); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); // 可改成背景图 + core.setAlpha('ui', 1); +} + +ui.prototype._drawSLPanel_loadSave = function (page, callback) { + var ids = [0]; + for (var i = 1; i <= 5; ++i) { + var id = 5 * page + i; + if (core.status.event.data.mode == 'fav') + id = core.saves.favorite[id - 1]; // 因为favorite第一个不是自动存档 所以要偏移1 + ids.push(id); + } + core.getSaves(ids, function (data) { + for (var i = 1; i < ids.length; ++i) + core.status.event.ui[i] = data[i]; + core.status.event.ui[0] = data[0] == null ? null : data[0][core.saves.autosave.now - 1]; + callback(); + }); +} + +// 在以x为中心轴 y为顶坐标 的位置绘制一条宽为size的记录 cho表示是否被选中 选中会加粗 highlight表示高亮标题 ✐ +ui.prototype._drawSLPanel_drawRecord = function (title, data, x, y, size, cho, highLight) { + var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; + var strokeColor = globalAttribute.selectColor; + if (core.status.event.selection) strokeColor = '#FF6A6A'; + if (!data || !data.floorId) highLight = false; + if (data && data.__toReplay__) title = '[R]' + title; + var w = size * core._PX_, h = size * core._PY_; + core.fillText('ui', title, x, y, highLight ? globalAttribute.selectColor : '#FFFFFF', this._buildFont(17, true)); + core.strokeRect('ui', x - w / 2, y + 15, w, h, cho ? strokeColor : '#FFFFFF', cho ? 3 : 1); + if (data && data.floorId) { + core.setTextAlign('ui', "center"); + var map = core.maps.loadMap(data.maps, data.floorId); + core.extractBlocksForUI(map, data.hero.flags); + core.drawThumbnail(data.floorId, map.blocks, { + heroLoc: data.hero.loc, heroIcon: data.hero.image, flags: data.hero.flags, + ctx: 'ui', x: x - w / 2, y: y + 15, size: size, centerX: data.hero.loc.x, centerY: data.hero.loc.y, noHD: true + }); + if (core.isPlaying() && core.getFlag("hard") != data.hero.flags.hard) { + core.fillRect('ui', x - w / 2, y + 15, w, h, [0, 0, 0, 0.4]); + core.fillText('ui', data.hard, x, parseInt(y + 22 + h / 2), data.hero.flags.__hardColor__ || 'white', this._buildFont(30, true)); + } + // 绘制存档笔记 + if (data.hero.notes && data.hero.notes.length > 0) { + core.setTextAlign('ui', 'left'); + if (data.hero.notes.length >= 2) { + core.fillRect('ui', x - w / 2, y + 15, w, 28, [0, 0, 0, 0.3]); + core.fillBoldText('ui', data.hero.notes.length - 1 + ". " + data.hero.notes[data.hero.notes.length - 2].substring(0, 10), + x - w / 2 + 2, y + 15 + 12, '#FFFFFF', null, this._buildFont(10, false)); + core.fillBoldText('ui', data.hero.notes.length + ". " + data.hero.notes[data.hero.notes.length - 1].substring(0, 10), + x - w / 2 + 2, y + 15 + 24); + } else { + core.fillRect('ui', x - w / 2, y + 15, w, 16, [0, 0, 0, 0.3]); + core.fillBoldText('ui', data.hero.notes.length + ". " + data.hero.notes[data.hero.notes.length - 1].substring(0, 10), + x - w / 2 + 2, y + 15 + 12, '#FFFFFF', null, this._buildFont(10, false)); + } + } + core.setTextAlign('ui', "center"); + var v = core.formatBigNumber(data.hero.hp, true) + "/" + core.formatBigNumber(data.hero.atk, true) + "/" + core.formatBigNumber(data.hero.def, true); + var v2 = "/" + core.formatBigNumber(data.hero.mdef, true); + if (core.calWidth('ui', v + v2, this._buildFont(10, false)) <= w) v += v2; + core.fillText('ui', v, x, y + 30 + h, globalAttribute.selectColor); + core.fillText('ui', core.formatDate(new Date(data.time)), x, y + 43 + h, data.hero.flags.debug ? '#FF6A6A' : '#FFFFFF'); + } + else { + core.fillRect('ui', x - w / 2, y + 15, w, h, '#333333'); + core.fillText('ui', '空', x, parseInt(y + 22 + h / 2), '#FFFFFF', this._buildFont(30, true)); + } +} + +ui.prototype._drawSLPanel_drawRecords = function (n) { + var page = core.status.event.data.page; + var offset = core.status.event.data.offset; + var u = Math.floor(core._PX_ / 6), size = 0.3; + var name = core.status.event.id == 'save' ? "存档" : core.status.event.id == 'load' ? "读档" : "回放"; + + for (var i = 0; i < (n || 6); i++) { + var data = core.status.event.ui[i]; + var id = 5 * page + i; + var highLight = (i > 0 && core.saves.favorite.indexOf(id) >= 0) || core.status.event.data.mode == 'fav'; + var title = (highLight ? '★ ' : '☆ ') + (core.saves.favoriteName[id] || (name + id)); + if (i != 0 && core.status.event.data.mode == 'fav') { + if (!data) break; + var real_id = core.saves.favorite[id - 1]; + title = (core.saves.favoriteName[real_id] || (name + real_id)) + ' ✐'; + } + var charSize = 32; // 字体占用像素范围 + var topSpan = parseInt((core._PY_ - charSize - 2 * (charSize * 2 + size * core._PY_)) / 3); // Margin + var yTop1 = topSpan + parseInt(charSize / 2) + 8; // 文字的中心 + var yTop2 = yTop1 + charSize * 2 + size * core._PY_ + topSpan; + if (i < 3) { + this._drawSLPanel_drawRecord(i == 0 ? "自动存档" : title, data, (2 * i + 1) * u, yTop1, size, i == offset, highLight); + } + else { + this._drawSLPanel_drawRecord(title, data, (2 * i - 5) * u, yTop2, size, i == offset, highLight); + } + } +}; + +ui.prototype._drawKeyBoard = function () { + core.lockControl(); + core.status.event.id = 'keyBoard'; + core.clearUI(); + core.playSound('打开界面'); + + var offset = core._WIDTH_ % 2 == 0 ? 16 : 0; + + var width = 384, height = 320; + var left = (core._PX_ - width) / 2 + offset, right = left + width; + var top = (core._PY_ - height) / 2 + (core._HEIGHT_ % 2 == 0 ? 16 : 0), bottom = top + height; + + var isWindowSkin = this.drawBackground(left, top, right, bottom); + core.setTextAlign('ui', 'center'); + core.setFillStyle('ui', core.arrayToRGBA(core.status.textAttribute.title)); + core.fillText('ui', '虚拟键盘', core._PX_ / 2 + offset, top + 35, null, this._buildFont(22, true)); + core.setFont('ui', this._buildFont(17, false)); + core.setFillStyle('ui', core.arrayToRGBA(core.status.textAttribute.text)); + var now = core._PY_ / 2 - 89 + (core._HEIGHT_ % 2 == 0 ? 16 : 0); + + var lines = [ + ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "10", "11"], + ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Z", "X", "C", "V", "B", "N", "M"], + ["-", "=", "[", "]", "\\", ";", "'", ",", ".", "/", "`"], + ["ES", "TA", "CA", "SH", "CT", "AL", "SP", "BS", "EN", "DE"] + ]; + + lines.forEach(function (line) { + for (var i = 0; i < line.length; i++) { + core.fillText('ui', line[i], core._PX_ / 2 + 32 * (i - 5) + offset, now); + } + now += 32; + }); + + core.fillText("ui", "返回游戏", core._PX_ / 2 + 128 + offset, now - 3, '#FFFFFF', this._buildFont(15, true)); + + if (isWindowSkin) + this._drawWindowSelector(core.status.textAttribute.background, core._PX_ / 2 + 92 + offset, now - 22, 72, 27); + else + core.strokeRoundRect('ui', core._PX_ / 2 + 92 + offset, now - 22, 72, 27, 6, core.status.globalAttribute.selectColor, 2); +} + +////// 绘制状态栏 ///// +ui.prototype.drawStatusBar = function () { + this.uidata.drawStatusBar(); +} + +////// 绘制“数据统计”界面 ////// +/*ui.prototype._drawStatistics = function (floorIds) { + core.playSound('打开界面'); + var obj = this._drawStatistics_buildObj(); + if (typeof floorIds == 'string') floorIds = [floorIds]; + (floorIds || core.floorIds).forEach(function (floorId) { + core.ui._drawStatistics_floorId(floorId, obj); + }); + var statistics = core.status.hero.statistics; + core.setFlag("__replayText__", true); + core.drawText([ + this._drawStatistics_generateText(obj, "全塔", obj.total), + this._drawStatistics_generateText(obj, "当前", obj.current), + this._drawStatistics_generateText(obj, "标记(浏览地图时B键或左下角)", obj.marked), + "当前总步数:" + core.status.hero.steps + ",当前游戏时长:" + core.formatTime(statistics.currTime) + + ",总游戏时长" + core.formatTime(statistics.totalTime) + + "。\n瞬间移动次数:" + statistics.moveDirectly + ",共计少走" + statistics.ignoreSteps + "步。" + + "\n\n总计通过血瓶恢复生命值为" + core.formatBigNumber(statistics.hp) + "点。\n\n" + + "总计打死了" + statistics.battle + "个怪物,得到了" + core.formatBigNumber(statistics.money) + "金币," + core.formatBigNumber(statistics.exp) + "点经验。\n\n" + + "受到的总伤害为" + core.formatBigNumber(statistics.battleDamage + statistics.poisonDamage + statistics.extraDamage) + + ",其中战斗伤害" + core.formatBigNumber(statistics.battleDamage) + "点" + + (core.flags.statusBarItems.indexOf('enableDebuff') >= 0 ? (",中毒伤害" + core.formatBigNumber(statistics.poisonDamage) + "点") : "") + + ",领域/夹击/阻击/血网伤害" + core.formatBigNumber(statistics.extraDamage) + "点。", + "\t[说明]1. 地图数据统计的效果仅模拟当前立刻获得该道具的效果。\n2. 不会计算“不可被浏览地图”的隐藏层的数据。\n" + + "3. 不会计算任何通过事件得到的道具(显示事件、改变图块、或直接增加道具等)。\n" + + "4. 在自定义道具(例如其他宝石)后,需在脚本编辑的drawStatistics中注册,不然不会进行统计。\n" + + "5. 道具不会统计通过插入事件或useItemEvent实现的效果。\n6. 所有统计信息仅供参考,如有错误,概不负责。" + ]) + core.removeFlag("__replayText__"); +}*/ + +ui.prototype._drawStatistics_buildObj = function () { + // 数据统计要统计如下方面: + // 1. 当前全塔剩余下的怪物数量,总金币数,总经验数,总加点数 + // 2. 当前全塔剩余的黄蓝红铁门数量,和对应的钥匙数量 + // 3. 当前全塔剩余的三种宝石数量,血瓶数量,装备数量;总共增加的攻防生命值 + // 4. 当前层的上述信息 + // 5. 当前已走的步数;瞬间移动的步数,瞬间移动的次数(和少走的步数);游戏时长 + // 6. 当前已恢复的生命值;当前总伤害、战斗伤害、阻激夹域血网伤害、中毒伤害。 + var ori = this.uidata.drawStatistics(); + var ids = ori.filter(function (e) { + return e.endsWith("Door") || core.material.items[e]; + }); + var cnt = {}, cls = {}, ext = {}; + ids.forEach(function (e) { + if (e.endsWith("Door")) cls[e] = "doors"; + else cls[e] = core.material.items[e].cls; + cnt[e] = 0; + }) + var order = ["doors", "items", "tools", "constants", "equips"]; + ids.sort(function (a, b) { + var c1 = order.indexOf(cls[a]), c2 = order.indexOf(cls[b]); + if (c1 == c2) return ori.indexOf(a) - ori.indexOf(b); + return c1 - c2; + }); + var obj = { + 'monster': { + 'count': 0, 'money': 0, 'exp': 0, 'point': 0, + }, + 'count': cnt, + 'add': { + 'hp': 0, 'atk': 0, 'def': 0, 'mdef': 0 + } + }; + return { ids: ids, cls: cls, ext: ext, total: core.clone(obj), current: core.clone(obj), marked: core.clone(obj) }; +} + +ui.prototype._drawStatistics_add = function (floorId, obj, x1, x2, value) { + obj.total[x1][x2] += value || 0; + if (floorId == core.status.floorId) + obj.current[x1][x2] += value || 0; + if (core.markedFloorIds[floorId]) + obj.marked[x1][x2] += value || 0; +} + +ui.prototype._drawStatistics_floorId = function (floorId, obj) { + core.extractBlocks(floorId); + var floor = core.status.maps[floorId], blocks = floor.blocks; + // 隐藏层不给看 + if (floor.cannotViewMap && floorId != core.status.floorId) return; + blocks.forEach(function (block) { + if (block.disable) return; + var event = block.event; + if (event.cls.indexOf("enemy") == 0) { + core.ui._drawStatistics_enemy(floorId, event.id, obj); + } + else { + var id = event.id; + if (obj.total.count[id] != null) + core.ui._drawStatistics_items(floorId, floor, id, obj); + } + }) +} + +ui.prototype._drawStatistics_enemy = function (floorId, id, obj) { + var enemy = core.material.enemys[id]; + this._drawStatistics_add(floorId, obj, 'monster', 'money', enemy.money); + this._drawStatistics_add(floorId, obj, 'monster', 'exp', enemy.exp); + this._drawStatistics_add(floorId, obj, 'monster', 'point', enemy.point); + this._drawStatistics_add(floorId, obj, 'monster', 'count', 1); +} + +ui.prototype._drawStatistics_items = function (floorId, floor, id, obj) { + var hp = 0, atk = 0, def = 0, mdef = 0; + if (obj.cls[id] == 'items' && id != 'superPotion') { + var temp = core.clone(core.status.hero); + core.setFlag("__statistics__", true); + var ratio = core.status.thisMap.ratio; + core.status.thisMap.ratio = core.clone(core.status.maps[floorId].ratio); + try { eval(core.material.items[id].itemEffect); } + catch (e) { } + core.status.thisMap.ratio = ratio; + hp = core.status.hero.hp - temp.hp; + atk = core.status.hero.atk - temp.atk; + def = core.status.hero.def - temp.def; + mdef = core.status.hero.mdef - temp.mdef; + core.status.hero = temp; + window.hero = core.status.hero; + window.flags = core.status.hero.flags; + } + else if (obj.cls[id] == 'equips') { + var values = core.material.items[id].equip || {}; + atk = values.atk || 0; + def = values.def || 0; + mdef = values.mdef || 0; + } + if (id.indexOf('sword') == 0 || id.indexOf('shield') == 0 || obj.cls[id] == 'equips') { + var t = ""; + if (atk > 0) t += atk + core.getStatusLabel('atk'); + if (def > 0) t += def + core.getStatusLabel('def'); + if (mdef > 0) t += mdef + core.getStatusLabel('mdef'); + if (t != "") obj.ext[id] = t; + } + this._drawStatistics_add(floorId, obj, 'count', id, 1); + this._drawStatistics_add(floorId, obj, 'add', 'hp', hp); + this._drawStatistics_add(floorId, obj, 'add', 'atk', atk); + this._drawStatistics_add(floorId, obj, 'add', 'def', def); + this._drawStatistics_add(floorId, obj, 'add', 'mdef', mdef); +} + +ui.prototype._drawStatistics_generateText = function (obj, type, data) { + var text = type + "地图中:\n"; + text += "共有怪物" + data.monster.count + "个"; + if (core.flags.statusBarItems.indexOf('enableMoney') >= 0) text += ",总金币数" + data.monster.money; + if (core.flags.statusBarItems.indexOf('enableExp') >= 0) text += ",总经验数" + data.monster.exp; + if (core.flags.enableAddPoint) text += ",总加点数" + data.monster.point; + text += "。\n"; + + var prev = ""; + obj.ids.forEach(function (key) { + var value = data.count[key]; + if (value == 0) return; + if (obj.cls[key] != prev) { + if (prev != "") text += "。"; + text += "\n"; + } + else text += ","; + prev = obj.cls[key]; + var name = ((core.material.items[key] || (core.getBlockById(key) || {}).event) || {}).name || key; + text += name + value + "个"; + if (obj.ext[key]) + text += "(" + obj.ext[key] + ")"; + }) + if (prev != "") text += "。"; + + text += "\n"; + text += "共加生命值" + core.formatBigNumber(data.add.hp) + "点,攻击" + + core.formatBigNumber(data.add.atk) + "点,防御" + + core.formatBigNumber(data.add.def) + "点,护盾" + + core.formatBigNumber(data.add.mdef) + "点。"; + return text; +} + +////// 绘制“关于”界面 ////// +ui.prototype._drawAbout = function () { + return this.uidata.drawAbout(); +} + +////// 绘制帮助页面 ////// +ui.prototype._drawHelp = function () { + core.playSound('打开界面'); + core.clearUI(); + if (core.material.images.keyboard) { + core.status.event.id = 'help'; + core.lockControl(); + core.setAlpha('ui', 1); + core.fillRect('ui', 0, 0, core._PX_, core._PY_, '#000000'); + core.drawImage('ui', core.material.images.keyboard, 0, 0); + } + else { + core.drawText([ + "\t[键盘快捷键列表]" + + "[CTRL] 跳过对话 [Z] 转向\n" + + "[X] " + core.material.items['book'].name + " [G] " + core.material.items['fly'].name + "\n" + + "[A] 读取自动存档 [W] 撤销读取自动存档\n" + + "[S/D] 存读档页面 [SPACE] 轻按\n" + + "[V] 快捷商店 [ESC] 系统菜单\n" + + "[T] 道具页面 [Q] 装备页面\n" + + "[B] 数据统计 [H] 帮助页面\n" + + "[R] 回放录像 [E] 显示光标\n" + + "[N] 返回标题页面 [P] 游戏主页\n" + + "[O] 查看工程 [F7] 打开debug穿墙模式\n" + + "[PgUp/PgDn] 浏览地图\n" + + "[1~4] 快捷使用破炸飞和其他道具\n" + + "[Alt+0~9] 快捷换装", + "\t[鼠标操作]" + + "点状态栏中图标: 进行对应的操作\n" + + "点任意块: 寻路并移动\n" + + "点任意块并拖动: 指定寻路路线\n" + + "双击空地: 瞬间移动\n" + + "单击勇士: 转向\n" + + "双击勇士: 轻按(仅在轻按开关打开时有效)\n" + + "长按任意位置:跳过剧情对话或打开虚拟键盘" + ]); + } +} + +////// 动态canvas ////// + +////// canvas创建 ////// +ui.prototype.createCanvas = function (name, x, y, width, height, z) { + // 如果画布已存在则直接调用 + if (core.dymCanvas[name]) { + this.relocateCanvas(name, x, y); + this.resizeCanvas(name, width, height); + core.dymCanvas[name].canvas.style.zIndex = z; + return core.dymCanvas[name]; + } + var newCanvas = document.createElement("canvas"); + newCanvas.id = name; + newCanvas.style.display = 'block'; + newCanvas.setAttribute("_left", x); + newCanvas.setAttribute("_top", y); + newCanvas.setAttribute("_width", width); + newCanvas.setAttribute("_height", height); + newCanvas.style.width = width * core.domStyle.scale + 'px'; + newCanvas.style.height = height * core.domStyle.scale + 'px'; + newCanvas.style.left = x * core.domStyle.scale + 'px'; + newCanvas.style.top = y * core.domStyle.scale + 'px'; + newCanvas.style.zIndex = z; + newCanvas.style.position = 'absolute'; + newCanvas.style.pointerEvents = 'none'; + core.dymCanvas[name] = newCanvas.getContext('2d'); + core.maps._setHDCanvasSize(core.dymCanvas[name], width, height); + core.dom.gameDraw.appendChild(newCanvas); + return core.dymCanvas[name]; +} + +////// canvas重定位 ////// +ui.prototype.relocateCanvas = function (name, x, y, useDelta) { + var ctx = core.getContextByName(name); + if (!ctx) return null; + if (x != null) { + // 增量模式 + if (useDelta) { + x += parseFloat(ctx.canvas.getAttribute("_left")) || 0; + } + ctx.canvas.style.left = x * core.domStyle.scale + 'px'; + ctx.canvas.setAttribute("_left", x); + } + if (y != null) { + // 增量模式 + if (useDelta) { + y += parseFloat(ctx.canvas.getAttribute("_top")) || 0; + } + ctx.canvas.style.top = y * core.domStyle.scale + 'px'; + ctx.canvas.setAttribute("_top", y); + } + return ctx; +} + +////// canvas旋转 ////// +ui.prototype.rotateCanvas = function (name, angle, centerX, centerY) { + var ctx = core.getContextByName(name); + if (!ctx) return null; + var canvas = ctx.canvas; + angle = angle || 0; + if (centerX == null || centerY == null) { + canvas.style.transformOrigin = ''; + } else { + var left = parseFloat(canvas.getAttribute("_left")); + var top = parseFloat(canvas.getAttribute("_top")); + canvas.style.transformOrigin = (centerX - left) * core.domStyle.scale + 'px ' + (centerY - top) * core.domStyle.scale + 'px'; + } + if (angle == 0) { + canvas.style.transform = ''; + } else { + canvas.style.transform = 'rotate(' + angle + 'deg)'; + } + canvas.setAttribute('_angle', angle); +} + +////// canvas重置 ////// +ui.prototype.resizeCanvas = function (name, width, height, styleOnly) { + var ctx = core.getContextByName(name); + const canvas = ctx.canvas; + if (!ctx) return null; + if (width != null) { + if (!styleOnly && ctx.canvas.hasAttribute('isHD')) + core.maps._setHDCanvasSize(ctx, width, null); + ctx.canvas.style.width = width * core.domStyle.scale + 'px'; + canvas.setAttribute('_width', width); + } + if (height != null) { + if (!styleOnly && ctx.canvas.hasAttribute('isHD')) + core.maps._setHDCanvasSize(ctx, null, height); + ctx.canvas.style.height = height * core.domStyle.scale + 'px'; + canvas.setAttribute('_height', height); + } + return ctx; +} +////// canvas删除 ////// +ui.prototype.deleteCanvas = function (name) { + if (name instanceof Function) { + Object.keys(core.dymCanvas).forEach(function (one) { + if (name(one)) core.deleteCanvas(one); + }); + return; + } + + if (!core.dymCanvas[name]) return null; + core.dom.gameDraw.removeChild(core.dymCanvas[name].canvas); + delete core.dymCanvas[name]; +} + +////// 删除所有动态canvas ////// +ui.prototype.deleteAllCanvas = function () { + return this.deleteCanvas(function () { return true; }) +} diff --git a/util.d.ts b/util.d.ts new file mode 100644 index 0000000..593df21 --- /dev/null +++ b/util.d.ts @@ -0,0 +1,912 @@ +/** 工具类 主要用来进行一些辅助函数的计算 */ +interface Utils { + /** + * 四个方向的坐标增量 + */ + readonly scan: DeepReadonly; + + /** + * 八个方向的坐标增量 + */ + readonly scan2: DeepReadonly; + + /** + * 将一段文字中的${}(表达式)进行替换。很多情况下可以用模板字符串替代 + * @example + * // 把主角的生命值和持有的黄钥匙数量代入这句话 + * core.replaceText('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); + * @param text 模板字符串,可以使用${}计算js表达式 + * @param prefix 独立开关前缀 + * @returns 替换完毕后的字符串 + */ + replaceText(text: string, prefix?: string): string; + + /** + * 对一个表达式中的特殊规则进行替换,如status:xxx等。 + * 其中变量和全局存储会替换中文冒号,其余的不会替换 + * @example + * // 把这两个冒号表达式替换为core.getStatus('hp')和core.itemCount('yellowKey')这样的函数调用 + * core.replaceValue('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); + * @param value 模板字符串,注意独立开关不会被替换 + * @returns 替换完毕后的字符串 + */ + replaceValue(value: string): string; + + /** + * 计算一个表达式的值,支持status:xxx等的计算。 + * @example core.calValue('status:hp + status:def'); // 计算主角的生命值加防御力 + * @param value 待求值的表达式 + * @param prefix 独立开关前缀,一般可省略 + * @returns 求出的值 + */ + calValue(value: string | Function, prefix?: string): any; + + /** + * @deprecated + * 将b(可以是另一个数组)插入数组a的开头,用Array.unshift就行 + * @example core.unshift(todo, {type: 'unfollow'}); // 在事件指令数组todo的开头插入“取消所有跟随者”指令 + * @param a 原数组 + * @param b 待插入的新首项或前缀数组 + * @returns 插入完毕后的新数组,它是改变原数组a本身得到的 + */ + unshift(a: A, b: B): [...B, ...A]; + + /** + * @deprecated + * 将b(可以是另一个数组)插入数组a的末尾,用Array.push就行 + * @example core.push(todo, {type: 'unfollow'}); // 在事件指令数组todo的末尾插入“取消所有跟随者”指令 + * @param a 原数组 + * @param b 待插入的新末项或后缀数组 + * @returns 插入完毕后的新数组,它是改变原数组a本身得到的 + */ + push(a: A, b: B): [...A, ...B]; + + /** + * 解压缩一个数据,我也不知道这个解压的是什么 + * @param 要解压的内容,字符串 + */ + decompress(value: string): any; + + /** + * //@deprecated + * 设置本地存储 + * @param key 本地存储的名称 + * @param value 本地存储的值,不填代表删除 + */ + setLocalStorage(key: string, value?: any): void; + + /** + * //@deprecated + * 获得本地存储 + * @param key 获取的本地存储的名称 + * @param defaultValue 当不存在的时候的默认值 + */ + getLocalStorage(key: string, defaultValue?: T): T; + + /** + * @deprecated + * 移除本地存储 + * @param key 要移除的本地存储的值 + */ + removeLocalStorage(key: string): void; + + /** + * 异步写入localforage + * @param key 写入的键 + * @param value 写入的值 + * @param successCallback 写入成功的回调函数 + * @param errorCallback 写入出错的回调函数 + */ + setLocalForage( + key: string, + value?: any, + successCallback?: () => void, + errorCallback?: (err: Error) => void + ): void; + + /** + * 从localforage读出一段数据 + */ + getLocalForage( + key: string, + defaultValue?: T, + successCallback?: (data: T) => void, + errorCallback?: (err: Error) => void + ): void; + + /** + * 移除localforage的数据 + */ + removeLocalForage( + key: string, + successCallback?: () => void, + errorCallback?: (err: Error) => void + ): void; + + /** + * 清除localforage所有的数据 + * @param callback 清除完毕的回调函数 + */ + clearLocalForage(callback?: (err?: Error) => void): void; + + /** + * 迭代localforage的数据 + * @param iteratee 迭代器 + * @param callback 迭代完毕的回调函数 + */ + iterateLocalForage( + iteratee: (value: T, key: string, iterationNumber: number) => U, + callback?: (err: any, result: U) => void + ): void; + + /** + * 获取localforage数据的所有的键 + * @param callback 回调函数 + */ + keysLocalForage(callback?: (err: any, keys: string[]) => void): void; + + /** + * 获取localforage数据的数据量 + * @param callback 回调函数 + */ + lengthLocalForage( + callback?: (err: any, numberOfKeys: number) => void + ): void; + + /** + * @deprecated + * 设置一个全局存储,适用于global:xxx,录像播放时将忽略此函数。 + * @example core.setBlobal('一周目已通关', true); // 设置全局存储“一周目已通关”为true,方便二周目游戏中的新要素。 + * @param key 全局变量名称,支持中文 + * @param value 全局变量的新值,不填或null表示清除此全局存储 + */ + setGlobal(key: string, value?: any): void; + + /** + * @deprecated + * 读取一个全局存储,适用于global:xxx,支持录像。 + * @example if (core.getGlobal('一周目已通关', false) === true) core.getItem('dagger'); // 二周目游戏进行到此处时会获得一把屠龙匕首 + * @param key 全局变量名称,支持中文 + * @param defaultValue 可选,当此全局变量不存在或值为null、undefined时,用此值代替 + * @returns 全局变量的值 + */ + getGlobal(key: string, defaultValue?: T): T; + + /** + * 深拷贝一个对象(函数将原样返回) + * @example core.clone(core.status.hero, (name, value) => (name == 'items' || typeof value == 'number'), false); // 深拷贝主角的属性和道具 + * @param data 待拷贝对象 + * @param filter 过滤器,可选,表示data为数组或对象时拷贝哪些项或属性,true表示拷贝 + * @param recursion 过滤器是否递归,可选。true表示过滤器也被递归 + * @returns 拷贝的结果,注意函数将原样返回 + */ + clone( + data: T, + filter?: (name: string, value: any) => boolean, + recursion?: boolean + ): T; + + /** + * 深拷贝一个1D或2D的数组 + * @param data 要拷贝的数据 + */ + cloneArray(data: T): T; + + /** + * 等比例切分一张图片 + * @example core.splitImage(core.material.images.images['npc48.png'], 32, 48); // 把npc48.png切分成若干32×48px的小人 + * @param image 图片名(支持映射前的中文名)或图片对象(参见上面的例子),获取不到时返回[] + * @param width 子图的宽度,单位为像素。原图总宽度必须是其倍数,不填视为32 + * @param height 子图的高度,单位为像素。原图总高度必须是其倍数,不填视为正方形 + * @returns 子图组成的数组,在原图中呈先行后列,从左到右、从上到下排列。 + */ + splitImage( + image: NameMapIn | ImageIds | HTMLImageElement, + width?: number, + height?: number + ): HTMLImageElement[]; + + /** + * 格式化日期为字符串 + * @param date 时间,不填代表当前时间 + * @returns 格式: yyyy-mm-dd hh:mm:ss + */ + formatDate(date?: Date): string; + + /** + * 格式化日期为最简字符串 + * @param date 时间,不填代表当前时间 + * @returns 格式: yyyymmddhhmmss + */ + formatDate2(date?: Date): string; + + /** + * 格式化时间 + * @param time 时间 + * @returns 格式: hh:mm:ss + */ + formatTime(time: number): string; + + /** + * @deprecated + * 设置成两位数显示,请使用setDigits代替 + */ + setTwoDigits(x: number): string; + + /** + * 设置一个数为n位数显示 + * @param x 要设置的数 + * @param n 设置成的位数 + */ + setDigits(x: number, n: number): string; + + /** + * 格式化文件大小 + * @param size 大小,字节数 + * @returns 格式为xx.xxB KB MB + */ + formatSize(size: number): string; + + /** + * 大数字格式化,单位为10000的倍数(w,e,z,j,g),末尾四舍五入 + * @example core.formatBigNumber(123456789); // "12346w" + * @param x 原数字 + * @param onMap 显示的字符数 + * @returns 格式化结果 + */ + formatBigNumber(x: T, onMap?: number): T; + formatBigNumber(x: number | string, onMap?: number | boolean): string; + + /** + * @deprecated + * 变速移动,完全可以用mutate-animate代替 + * @param mode 缓动模式 + */ + applyEasing(mode?: EaseMode): (x: number) => number; + + /** + * 颜色数组转十六进制 + * @example core.arrayToRGB([102, 204, 255]); // "#66ccff",加载画面的宣传色 + * @param color 一行三列的数组,各元素必须为不大于255的自然数 + * @returns 该颜色的十六进制表示,使用小写字母 + */ + arrayToRGB(color: RGBArray): _RGBA; + + /** + * 颜色数组转字符串 + * @example core.arrayToRGBA([102, 204, 255]); // "rgba(102,204,255,1)" + * @param color 一行三列或一行四列的数组,前三个元素必须为不大于255的自然数。 + * 第四个元素(如果有)必须为0或不大于1的数字,第四个元素不填视为1 + * @returns 该颜色的字符串表示 + */ + arrayToRGBA(color: Color): _RGBA; + + /** + * 录像一压,其结果会被再次base64压缩 + * @example core.encodeRoute(core.status.route); // 一压当前录像 + * @param route 原始录像,自定义内容(不予压缩,原样写入)必须由0-9A-Za-z和下划线、冒号组成, + * 所以中文和数组需要用JSON.stringify预处理再base64压缩才能交由一压 + * @returns 一压的结果 + */ + encodeRoute(route: string[]): string; + + /** + * 录像解压的最后一步,即一压的逆过程 + * @example core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-| + * @param route 录像解压倒数第二步的结果,即一压的结果 + * @returns 原始录像 + */ + decodeRoute(route: string): string[]; + + /** + * 判断一个值是否不为null,undefined和NaN + * @example core.isset(0/0); // false,因为0/0等于NaN + * @param v 待测值 + * @returns false表示待测值为null、undefined、NaN或未填写,true表示为其他值 + */ + isset(v?: any): boolean; + + /** + * 判定一个数组是否为另一个数组的前缀,用于录像接续播放 + * @example core.subarray(['ad', '米库', '小精灵', '小破草', '小艾'], ['ad', '米库', '小精灵']); // ['小破草', '小艾'] + * @param a 可能的母数组,不填或比b短将返回null + * @param b 可能的前缀,不填或比a长将返回null + * @returns 如果b不是a的前缀将返回null,否则将返回a去掉此前缀后的剩余数组 + */ + subarray(a: any[], b: any[]): any[] | null; + + /** + * @deprecated + * 判定array是不是一个数组,以及element是否在该数组中。使用Array.includes代替 + * @param array 可能的数组,不为数组或不填将导致返回值为false + * @param element 待查找的元素 + * @returns 如果array为数组且具有element这项,就返回true,否则返回false + */ + inArray(array?: any, element?: any): boolean; + + /** + * 将x限定在[a,b]区间内,注意a和b可交换 + * @example core.clamp(1200, 1, 1000); // 1000 + * @param x 原始值,!x为true时x一律视为0 + * @param a 下限值,大于b将导致与b交换 + * @param b 上限值,小于a将导致与a交换 + */ + clamp(x: number, a: number, b: number): number; + + /** + * 访问浏览器cookie + */ + getCookie(name: string): string; + + /** + * @deprecated 已失效(大概 + * 填写非自绘状态栏 + * @example + * // 更新状态栏中的主角生命,使用加载画面的宣传色 + * core.setStatusBarInnerHTML('hp', core.status.hero.hp, 'color: #66CCFF'); + * @param name 状态栏项的名称,如'hp', 'atk', 'def'等。必须是core.statusBar中的一个合法项 + * @param value 要填写的内容,大数字会被格式化为至多6个字符,无中文的内容会被自动设为斜体 + * @param css 额外的css样式,可选。如更改颜色等 + */ + setStatusBarInnerHTML( + name: string, + value: string | number, + css?: string + ): void; + + /** + * 求字符串的国标码字节数,也可用于等宽字体下文本的宽度测算。请注意样板的默认字体Verdana不是等宽字体 + * @example core.strlen('无敌ad'); // 6 + * @param str 待测字符串 + * @returns 字符串的国标码字节数,每个汉字为2,每个ASCII字符为1 + */ + strlen(str: string): number; + + /** + * 计算应当转向某个方向 + * @param turn 转向的方向 + * @param direction 当前方向,不填视为当前方向 + */ + turnDirection(turn: HeroTurnDir, direction?: Dir): string; + + /** + * 通配符匹配,用于搜索图块等批量处理。 + * @example core.playSound(core.matchWildcard('*Key', itemId) ? 'item.mp3' : 'door.mp3'); // 判断捡到的是钥匙还是别的道具,从而播放不同的音效 + * @param pattern 模式串,每个星号表示任意多个(0个起)字符 + * @param string 待测串 + * @returns true表示匹配成功,false表示匹配失败 + */ + matchWildcard(pattern: string, string: string): boolean; + + /** + * 是否满足正则表达式,一般可以直接用/RegExp/.test(str)代替 + * @param pattern 正则表达式 + * @param string 要匹配的字符串 + */ + matchRegex(pattern: string, string: string): string; + + /** + * base64加密 + * @example + * core.encodeBase64('If you found this note in a small wooden box with a heart on it'); + * // "SWYgeW91IGZvdW5kIHRoaXMgbm90ZSBpbiBhIHNtYWxsIHdvb2RlbiBib3ggd2l0aCBhIGhlYXJ0IG9uIGl0" + * @param str 明文 + * @returns 密文 + */ + encodeBase64(str: string): string; + + /** + * base64解密 + * @example + * core.decodeBase64('SWYgeW91IGZvdW5kIHRoaXMgbm90ZSBpbiBhIHNtYWxsIHdvb2RlbiBib3ggd2l0aCBhIGhlYXJ0IG9uIGl0'); + * // "If you found this note in a small wooden box with a heart on it" + * @param str 密文 + * @returns 明文 + */ + decodeBase64(str: string): string; + + /** + * 不支持SL的随机数 + * @exmaple 1 + core.rand(6); // 随机生成一个小于7的正整数,模拟骰子的效果 + * @param num 填正数表示生成小于num的随机自然数,否则生成小于1的随机正数 + * @returns 随机数,即使读档也不会改变结果 + */ + rand(num?: number): number; + + /** + * 支持SL的随机数,并计入录像 + * @exmaple 1 + core.rand2(6); // 随机生成一个小于7的正整数,模拟骰子的效果 + * @param num 正整数,0或不填会被视为2147483648 + * @returns 属于 [0, num) 的随机数 + */ + rand2(num?: number): number; + + /** + * 尝试请求读取一个本地文件内容 [异步] + * @param success 成功后的回调 + * @param error 失败后的回调 + * @param accept input元素的accept属性 + * @param readType 不设置则以文本读取,否则以DataUrl形式读取 + */ + readFile( + success: (obj: any) => void, + error: () => void, + accept: string, + readType: boolean + ): void; + + /** + * 文件读取完毕后的内容处理 [异步] + * @param content 读取的内容 + */ + readFileContent(content: string): void; + + /** + * 弹窗请求下载一个文本文件 + * @example core.download('route.txt', core.status.route); // 弹窗请求下载录像 + * @param filename 文件名 + * @param content 文件内容 + */ + download(filename: string, content: string | string[]): void; + + /** + * 尝试复制一段文本到剪切板 + * @param data 赋值的东西 + */ + copy(data: string): void; + + /** + * 显示确认框,类似core.drawConfirmBox() + * @example core.myconfirm('重启游戏?', core.restart); // 弹窗询问玩家是否重启游戏 + * @param hint 弹窗的内容 + * @param yesCallback 确定后的回调函数 + * @param noCallback 取消后的回调函数 + */ + myconfirm( + hint: string, + yesCallback: () => void, + noCallback?: () => void + ): void; + + /** + * 让用户输入一段文字 + */ + myprompt( + hint: string, + value: string, + callback?: (data?: string) => void + ): void; + + /** + * @deprecated + * 动画显示某对象,有vue了,你还用这个?Transition组件和css的transition比这个强得多 + */ + showWithAnimate( + obj?: HTMLElement, + speed?: number, + callback?: () => any + ): void; + + /** + * @deprecated + * 动画使某对象消失 + */ + hideWithAnimate( + obj?: HTMLElement, + speed?: number, + callback?: () => any + ): void; + + /** + * 获得浏览器唯一的guid + */ + getGuid(): string; + + /** + * 获取一个对象的哈希值 + * @param obj 要获取的对象 + */ + hashCode(obj: any): number; + + /** + * 判定深层相等, 会逐层比较每个元素 + * @example core.same(['1', 2], ['1', 2]); // true + */ + same(a: any, b: any): boolean; + + /** + * 解压一段内容 + */ + unzip( + blobOrUrl: string | Blob, + success?: (data: any) => void, + error?: (error: string) => void, + convertToText?: boolean, + onprogress?: (loaded: number, total: number) => void + ): void; + + /** + * 发送一个HTTP请求 [异步] + * @param type 请求类型 + * @param url 目标地址 + * @param formData 如果是POST请求则为表单数据 + * @param success 成功后的回调 + * @param error 失败后的回调 + */ + http( + type: 'GET' | 'POST', + url: string, + formData?: FormData, + success?: (res: any) => void, + error?: (err: string) => void, + mimeType?: string, + responseType?: XMLHttpRequestResponseType, + onProgress?: (loaded: number, total: number) => void + ): void; +} + +declare const utils: new () => Utils; + +/** + * APP接口,使用前应先判断是否存在 + */ +interface JSInterface { + /** 强制横屏 */ + requireLandscape(): void; +} + +declare const jsinterface: JSInterface; + +interface Window { + readonly jsinterface: JSInterface; +} + +/** + * 移动的方向 + */ +type Step = Move | 'backward'; + +/** + * 坐标字符串 + */ +type LocString = `${number},${number}`; + +type _RGBA = + | `rgb(${number},${number},${number})` + | `rgba(${number},${number},${number},${number})`; + +/** + * RGBA颜色数组 + */ +type RGBArray = [number, number, number, number?]; + +/** + * 样板的颜色字符串 + */ +type Color = `#${string}` | _RGBA | RGBArray | 'transparent'; + +/** + * 四个方向 + */ +type Dir = 'up' | 'down' | 'left' | 'right'; + +/** + * 八个方向 + */ +type Dir2 = Dir | 'leftup' | 'rightup' | 'leftdown' | 'rightdown'; + +/** + * 转向的方向 + */ +type TurnDir = Dir | ':left' | ':right' | ':back'; + +/** + * 勇士转向 + */ +type HeroTurnDir = TurnDir | ':hero' | ':backhero'; + +/** + * 对话框的位置 + */ +type TextPosition = 'up' | 'center' | 'down'; + +/** + * 移动的方向 + */ +type Move = 'forward' | Dir; + +/** + * 缓动模式,不过在高级动画插件面前不堪一击( + */ +type EaseMode = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'; + +/** + * 事件执行的操作符\ + * += 增加并赋值\ + * -= 减少并赋值\ + * *= 相乘并赋值\ + * /= 相除并赋值\ + * //= 除以并取商\ + * **= 取幂\ + * %= 取余\ + * min= 取二者的最小值\ + * max= 取二者的最大值\ + * 其它的任意字符串都是赋值 + */ +type MotaOperator = + | '+=' + | '-=' + | '*=' + | '/=' + | '//=' + | '**=' + | '%=' + | 'min=' + | 'max=' + | '='; + +/** + * 位置数组 + */ +type LocArr = [x: number, y: number]; + +/** + * 位置 + */ +interface Loc { + /** + * 横坐标 + */ + x: number; + + /** + * 纵坐标 + */ + y: number; +} + +/** + * 带方向的位置 + */ +interface DiredLoc extends Loc { + /** + * 方向 + */ + direction: Dir; +} + +interface CompressedStep { + /** + * 移动方向 + */ + direction: Dir; + + /** + * 向该方向移动的步数 + */ + step: number; +} + +/** + * 四个方向的坐标增量 + */ +type Scan = { + [D in Dir]: Loc; +}; + +/** + * 八个方向的坐标增量 + */ +type Scan2 = { + [D in Dir2]: Loc; +}; + +/** + * 图片翻转 + */ +type ImageReverse = ':o' | ':x' | ':y'; + +/** + * 对话框的箭头方向 + */ +type TextBoxDir = 'up' | 'down'; + +/** + * 对话框的位置 + */ +type TextBoxPos = + | `${TextBoxDir},hero` + | `${TextBoxDir},${number},${number}` + | TextPosition; + +/** + * 画布信息 + */ +type CtxRefer = string | CanvasRenderingContext2D | HTMLCanvasElement; + +/** + * 触发器类型 + */ +type MotaTrigger = + | 'battle' + | 'pusBox' + | 'openDoor' + | 'ski' + | 'custom' + | 'getItem' + | 'changeFloor' + | 'null'; + +/** + * 切换楼层的目标坐标 + */ +type FloorChangeStair = + | 'upFloor' + | 'downFloor' + | ':symmetry' + | ':symmetry_x' + | ':symmetry_y' + | 'flyPoint'; + +/** + * 事件值的前缀 + */ +type EventValuePreffix = + | 'status' + | 'flag' + | 'item' + | 'buff' + | 'switch' + | 'temp' + | 'global'; + +interface Animate { + /** + * 动画的帧数s + */ + frame: number; + + /** + * 每帧的信息 + */ + frames: FrameObj[][]; + + /** + * 图片信息 + */ + images: HTMLImageElement[]; + + /** + * 缩放信息 + */ + ratio: number; + + /** + * 音效 + */ + se: string; +} + +type Save = DeepReadonly<{ + /** + * 存档所在的楼层id + */ + floorId: FloorIds; + + /** + * 存档的勇士信息 + */ + hero: HeroStatus; + + /** + * 难度信息 + */ + hard: number; + + /** + * 存档的地图信息,已经过压缩处理 + */ + maps: Record; + + /** + * 录像信息 + */ + route: string; + + /** + * 存档的全局变量信息 + */ + values: CoreValues; + + /** + * 游戏版本 + */ + version: string; + + /** + * 浏览器唯一guid + */ + guid: string; + + /** + * 存档时间 + */ + time: number; +}>; + +type ValueType = number | string | boolean | undefined | null | bigint | symbol; + +/** + * 深度只读一个对象,使其所有属性都只读 + */ +type DeepReadonly = { + readonly [P in keyof T]: T[P] extends ValueType ? T[P] : DeepReadonly; +}; + +/** + * 深度可选一个对象,使其所有属性都可选 + */ +type DeepPartial = { + [P in keyof T]?: T[P] extends ValueType ? T[P] : DeepPartial; +}; + +/** + * 深度必选一个对象,使其所有属性都必选 + */ +type DeepRequired = { + [P in keyof T]-?: T[P] extends ValueType ? T[P] : DeepRequired; +}; + +/** + * 使一个对象的所有属性可写 + */ +type Writable = { + -readonly [P in keyof T]: T[P]; +}; + +/** + * 深度可写一个对象,使其所有属性都可写 + */ +type DeepWritable = { + -readonly [P in keyof T]: T[P] extends ValueType + ? T[P] + : DeepWritable; +}; + +/** + * 从一个对象中选择类型是目标类型的属性 + */ +type SelectType = { + [P in keyof R as R[P] extends T ? P : never]: R[P]; +}; + +/** + * 从一个对象中选择类型是目标属性的键名 + */ +type SelectKey = keyof SelectType; + +/** + * 获取一段字符串的第一个字符 + */ +type FirstCharOf = T extends `${infer F}${infer A}` + ? F + : never; + +/** + * 非对象属性 + */ +type NonObject = number | string | boolean; + +/** + * 获取一个对象的非对象值 + */ +type NonObjectOf = SelectType; + +/** + * 以一个字符串结尾 + */ +type EndsWith = `${string}${T}`; + +type KeyExcludesUnderline = Exclude; + +type ValueOf = T[keyof T]; diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..cddf742 --- /dev/null +++ b/utils.js @@ -0,0 +1,1340 @@ + +/* +utils.js 工具类 + + */ + +"use strict"; + +function utils () { + this._init(); + this.scan = { + 'up': { 'x': 0, 'y': -1 }, + 'left': { 'x': -1, 'y': 0 }, + 'down': { 'x': 0, 'y': 1 }, + 'right': { 'x': 1, 'y': 0 } + }; + this.scan2 = { + 'up': { 'x': 0, 'y': -1 }, + 'left': { 'x': -1, 'y': 0 }, + 'down': { 'x': 0, 'y': 1 }, + 'right': { 'x': 1, 'y': 0 }, + 'leftup': { 'x': -1, 'y': -1 }, + 'leftdown': { 'x': -1, 'y': 1 }, + 'rightup': { 'x': 1, 'y': -1 }, + 'rightdown': { 'x': 1, 'y': 1 } + }; +} + +utils.prototype._init = function () { + // +} + +////// 将文字中的${和}(表达式)进行替换 ////// +utils.prototype.replaceText = function (text, prefix) { + if (typeof text != 'string') return text; + var index = text.indexOf("${"); + if (index < 0) return text; + var cnt = 0, curr = index; + while (++curr < text.length) { + if (text.charAt(curr) == '{') cnt++; + if (text.charAt(curr) == '}') cnt--; + if (cnt == 0) break; + } + if (cnt != 0) return text; + var value = core.calValue(text.substring(index + 2, curr), prefix); + if (value == null) value = ""; + return text.substring(0, index) + value + core.replaceText(text.substring(curr + 1), prefix); +} + +utils.prototype.replaceValue = function (value) { + if (typeof value == "string" && (value.indexOf(":") >= 0 || value.indexOf("flag:") >= 0 || value.indexOf('global:') >= 0)) { + if (value.indexOf('status:') >= 0) + value = value.replace(/status:([a-zA-Z0-9_]+)/g, "core.getStatus('$1')"); + if (value.indexOf('buff:') >= 0) + value = value.replace(/buff:([a-zA-Z0-9_]+)/g, "core.getBuff('$1')"); + if (value.indexOf('item:') >= 0) + value = value.replace(/item:([a-zA-Z0-9_]+)/g, "core.itemCount('$1')"); + if (value.indexOf('flag:') >= 0 || value.indexOf('flag:') >= 0) + value = value.replace(/flag[::]([a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]+)/g, "core.getFlag('$1', 0)"); + //if (value.indexOf('switch:' >= 0)) + // value = value.replace(/switch:([a-zA-Z0-9_]+)/g, "core.getFlag('" + (prefix || ":f@x@y") + "@$1', 0)"); + if (value.indexOf('global:') >= 0 || value.indexOf('global:') >= 0) + value = value.replace(/global[::]([a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]+)/g, "core.getGlobal('$1', 0)"); + if (value.indexOf('enemy:') >= 0) + value = value.replace(/enemy:([a-zA-Z0-9_]+)[\.:]([a-zA-Z0-9_]+)/g, "core.material.enemys['$1'].$2"); + if (value.indexOf('blockId:') >= 0) + value = value.replace(/blockId:(\d+),(\d+)/g, "core.getBlockId($1, $2)"); + if (value.indexOf('blockNumber:') >= 0) + value = value.replace(/blockNumber:(\d+),(\d+)/g, "core.getBlockNumber($1, $2)"); + if (value.indexOf('blockCls:') >= 0) + value = value.replace(/blockCls:(\d+),(\d+)/g, "core.getBlockCls($1, $2)"); + if (value.indexOf('equip:') >= 0) + value = value.replace(/equip:(\d)/g, "core.getEquip($1)"); + if (value.indexOf('temp:') >= 0) + value = value.replace(/temp:([a-zA-Z0-9_]+)/g, "core.getFlag('@temp@$1', 0)"); + } + return value; +} + +////// 计算表达式的值 ////// +utils.prototype.calValue = function (value, prefix) { + if (!core.isset(value)) return null; + if (typeof value === 'string') { + if (value.indexOf(':') >= 0 || value.indexOf("flag:") >= 0 || value.indexOf('global:') >= 0) { + if (value.indexOf('switch:') >= 0) + value = value.replace(/switch:([a-zA-Z0-9_]+)/g, "core.getFlag('" + (prefix || ":f@x@y") + "@$1', 0)"); + value = this.replaceValue(value); + } + return eval(value); + } + if (value instanceof Function) { + return value(); + } + return value; +} + +////// 向某个数组前插入另一个数组或元素 ////// +utils.prototype.unshift = function (a, b) { + if (!(a instanceof Array) || b == null) return; + if (b instanceof Array) { + core.clone(b).reverse().forEach(function (e) { + a.unshift(e); + }); + } + else a.unshift(b); + return a; +} + +////// 向某个数组后插入另一个数组或元素 ////// +utils.prototype.push = function (a, b) { + if (!(a instanceof Array) || b == null) return; + if (b instanceof Array) { + core.clone(b).forEach(function (e) { + a.push(e); + }); + } + else a.push(b); + return a; +} + +utils.prototype.decompress = function (value) { + try { + var output = lzw_decode(value); + if (output) return JSON.parse(output); + } + catch (e) { + } + try { + var output = LZString.decompress(value); + if (output) return JSON.parse(output); + } + catch (e) { + } + try { + return JSON.parse(value); + } + catch (e) { + console.error(e); + } + return null; +} + +////// 设置本地存储 ////// +utils.prototype.setLocalStorage = function (key, value) { + try { + if (value == null) { + this.removeLocalStorage(key); + return; + } + + var str = JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function (chr) { + return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4) + }); + localStorage.setItem(core.firstData.name + "_" + key, str); + + if (key == 'autoSave') core.saves.ids[0] = true; + else if (/^save\d+$/.test(key)) core.saves.ids[parseInt(key.substring(4))] = true; + + return true; + } + catch (e) { + console.error(e); + return false; + } +} + +////// 获得本地存储 ////// +utils.prototype.getLocalStorage = function (key, defaultValue) { + try { + var value = JSON.parse(localStorage.getItem(core.firstData.name + "_" + key)); + if (value == null) return defaultValue; + return value; + } catch (e) { + return defaultValue; + } +} + +////// 移除本地存储 ////// +utils.prototype.removeLocalStorage = function (key) { + localStorage.removeItem(core.firstData.name + "_" + key); + if (key == 'autoSave') delete core.saves.ids[0]; + else if (/^save\d+$/.test(key)) delete core.saves.ids[parseInt(key.substring(4))]; +} + +utils.prototype.setLocalForage = function (key, value, successCallback, errorCallback) { + if (value == null) { + this.removeLocalForage(key); + return; + } + + var name = core.firstData.name + "_" + key; + var str = JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function (chr) { + return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4) + }); + var callback = function (err) { + if (err) { + if (errorCallback) errorCallback(err); + } + else { + if (key == 'autoSave') core.saves.ids[0] = true; + else if (/^save\d+$/.test(key)) core.saves.ids[parseInt(key.substring(4))] = true; + if (successCallback) successCallback(); + } + } + this._setLocalForage_set(name, str, callback); +} + +utils.prototype._setLocalForage_set = function (name, str, callback) { + if (window.jsinterface && window.jsinterface.setLocalForage) { + var id = setTimeout(null); + core['__callback' + id] = callback; + core.saves.cache[name] = str; + window.jsinterface.setLocalForage(id, name, str); + } else { + var compressed = str.length > 100000 ? LZString.compress(str) : lzw_encode(str); + core.saves.cache[name] = compressed; + localforage.setItem(name, compressed, callback); + } +} + +utils.prototype.getLocalForage = function (key, defaultValue, successCallback, errorCallback) { + var name = core.firstData.name + "_" + key; + var callback = function (err, value) { + if (err) { + if (errorCallback) errorCallback(err); + } + else { + core.saves.cache[name] = value; + if (!successCallback) return; + if (value != null) { + var res = core.utils.decompress(value); + successCallback(res == null ? defaultValue : res); + return; + } + successCallback(defaultValue); + } + }; + if (core.saves.cache[name] != null) { + return callback(null, core.saves.cache[name]); + } + this._getLocalForage_get(name, callback); +} + +utils.prototype._getLocalForage_get = function (name, callback) { + if (window.jsinterface && window.jsinterface.getLocalForage) { + var id = setTimeout(null); + core['__callback' + id] = callback; + window.jsinterface.getLocalForage(id, name); + } else { + localforage.getItem(name, callback); + } +} + +utils.prototype.removeLocalForage = function (key, successCallback, errorCallback) { + var name = core.firstData.name + "_" + key; + var callback = function (err) { + if (err) { + if (errorCallback) errorCallback(err); + } + else { + if (key == 'autoSave') delete core.saves.ids[0]; + else if (/^save\d+$/.test(key)) delete core.saves.ids[parseInt(key.substring(4))]; + if (successCallback) successCallback(); + } + } + delete core.saves.cache[name]; + this._removeLocalForage_remove(name, callback); +} + +utils.prototype._removeLocalForage_remove = function (name, callback) { + if (window.jsinterface && window.jsinterface.removeLocalForage) { + var id = setTimeout(null); + core['__callback' + id] = callback; + window.jsinterface.removeLocalForage(id, name); + } else { + localforage.removeItem(name, callback); + } +} + +utils.prototype.clearLocalForage = function (callback) { + core.saves.cache = {}; + if (window.jsinterface && window.jsinterface.clearLocalForage) { + var id = setTimeout(null); + core['__callback' + id] = callback; + window.jsinterface.clearLocalForage(id); + } else { + localforage.clear(callback); + } +} + +utils.prototype.iterateLocalForage = function (iter, callback) { + if (window.jsinterface && window.jsinterface.iterateLocalForage) { + var id = setTimeout(null); + core['__iter' + id] = iter; + core['__callback' + id] = callback; + window.jsinterface.iterateLocalForage(id); + } else { + localforage.iterate(iter, callback); + } +} + +utils.prototype.keysLocalForage = function (callback) { + if (window.jsinterface && window.jsinterface.keysLocalForage) { + var id = setTimeout(null); + core['__callback' + id] = callback; + window.jsinterface.keysLocalForage(id); + } else { + localforage.keys(callback); + } +} + +utils.prototype.lengthLocalForage = function (callback) { + if (window.jsinterface && window.jsinterface.lengthLocalForage) { + var id = setTimeout(null); + core['__callback' + id] = callback; + window.jsinterface.lengthLocalForage(id); + } else { + localforage.length(callback); + } +} + +utils.prototype.setGlobal = function (key, value) { + if (core.isReplaying()) return; + core.setLocalStorage(key, value); +} + +utils.prototype.getGlobal = function (key, defaultValue) { + var value; + if (core.isReplaying()) { + // 不考虑key不一致的情况 + var action = core.status.replay.toReplay.shift(); + if (action.indexOf("input2:") == 0) { + value = JSON.parse(core.decodeBase64(action.substring(7))); + core.setFlag('__global__' + key, value); + core.status.route.push("input2:" + core.encodeBase64(JSON.stringify(value))); + } + else { + // 录像兼容性:尝试从flag和localStorage获得 + // 注意这里不再二次记录 input2: 到录像 + core.status.replay.toReplay.unshift(action); + value = core.getFlag('__global__' + key, core.getLocalStorage(key, defaultValue)); + } + } + else { + value = core.getLocalStorage(key, defaultValue); + core.setFlag('__global__' + key, value); + core.status.route.push("input2:" + core.encodeBase64(JSON.stringify(value))); + } + return value; +} + +////// 深拷贝一个对象 ////// +utils.prototype.clone = function (data, filter, recursion) { + if (!core.isset(data)) return null; + // date + if (data instanceof Date) { + var copy = new Date(); + copy.setTime(data.getTime()); + return copy; + } + // array + if (data instanceof Array) { + var copy = []; + for (var i in data) { + if (!filter || filter(i, data[i])) + copy[i] = core.clone(data[i], recursion ? filter : null, recursion); + } + return copy; + } + // 函数 + if (data instanceof Function) { + return data; + } + // object + if (data instanceof Object) { + var copy = {}; + for (var i in data) { + if (data.hasOwnProperty(i) && (!filter || filter(i, data[i]))) + copy[i] = core.clone(data[i], recursion ? filter : null, recursion); + } + return copy; + } + return data; +} + +////// 深拷贝1D/2D数组优化 ////// +utils.prototype.cloneArray = function (data) { + if (!(data instanceof Array)) return this.clone(data); + if (data[0] instanceof Array) { + return data.map(function (one) { return one.slice(); }); + } else { + return data.slice(); + } +} + +////// 裁剪图片 ////// +utils.prototype.splitImage = function (image, width, height) { + if (typeof image == "string") { + image = core.getMappedName(image); + image = core.material.images.images[image]; + } + if (!image) return []; + width = width || 32; + height = height || width; + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext("2d"); + var ans = []; + for (var j = 0; j < image.height; j += height) { + for (var i = 0; i < image.width; i += width) { + var w = Math.min(width, image.width - i), h = Math.min(height, image.height - j); + canvas.width = w; canvas.height = h; + core.drawImage(ctx, image, i, j, w, h, 0, 0, w, h); + var img = new Image(); + img.src = canvas.toDataURL("image/png"); + ans.push(img); + } + } + return ans; +} + +////// 格式化时间为字符串 ////// +utils.prototype.formatDate = function (date) { + if (!date) date = new Date(); + return "" + date.getFullYear() + "-" + core.setTwoDigits(date.getMonth() + 1) + "-" + core.setTwoDigits(date.getDate()) + " " + + core.setTwoDigits(date.getHours()) + ":" + core.setTwoDigits(date.getMinutes()) + ":" + core.setTwoDigits(date.getSeconds()); +} + +////// 格式化时间为最简字符串 ////// +utils.prototype.formatDate2 = function (date) { + if (!date) date = new Date(); + return "" + date.getFullYear() + core.setTwoDigits(date.getMonth() + 1) + core.setTwoDigits(date.getDate()) + + core.setTwoDigits(date.getHours()) + core.setTwoDigits(date.getMinutes()) + core.setTwoDigits(date.getSeconds()); +} + +utils.prototype.formatTime = function (time) { + return core.setTwoDigits(parseInt(time / 3600000)) + + ":" + core.setTwoDigits(parseInt(time / 60000) % 60) + + ":" + core.setTwoDigits(parseInt(time / 1000) % 60); +} + +////// 两位数显示 ////// +utils.prototype.setTwoDigits = function (x) { + return (parseInt(x) < 10 && parseInt(x) >= 0) ? "0" + x : x; +} + +utils.prototype.formatSize = function (size) { + if (size < 1024) return size + 'B'; + else if (size < 1024 * 1024) return (size / 1024).toFixed(2) + "KB"; + else return (size / 1024 / 1024).toFixed(2) + "MB"; +} + +utils.prototype.formatBigNumber = function (x, digits) { + if (digits === true) digits = 5; // 兼容旧版onMap参数 + if (!digits || digits < 5) digits = 6; // 连同负号、小数点和后缀字母在内的总位数,至少需为5,默认为6 + x = Math.trunc(parseFloat(x)); // 尝试识别为小数,然后向0取整 + if (x == null || !Number.isFinite(x)) return '???'; // 无法识别的数或正负无穷大,显示'???' + var units = [ // 单位及其后缀字母,可自定义,如改成千进制下的K、M、G、T、P + { "val": 1e4, "suffix": "w" }, + { "val": 1e8, "suffix": "e" }, + { "val": 1e12, "suffix": "z" }, + { "val": 1e16, "suffix": "j" }, + { "val": 1e20, "suffix": "g" }, + ]; + if (Math.abs(x) > 1e20 * Math.pow(10, digits - 2)) + return x.toExponential(0); // 绝对值过大以致于失去精度的数,直接使用科学记数法,系数只保留整数 + var sign = x < 0 ? '-' : ''; + if (sign) --digits; // 符号位单独处理,负号要占一位 + x = Math.abs(x); + + if (x < Math.pow(10, digits)) return sign + x; + + for (var i = 0; i < units.length; ++i) { + var each = units[i]; + var u = (x / each.val).toFixed(digits).substring(0, digits); + if (u.indexOf('.') < 0) continue; + u = u.substring(0, u[u.length - 2] == '.' ? u.length - 2 : u.length - 1); + return sign + u + each.suffix; + } + return sign + x.toExponential(0); +} + +////// 变速移动 ////// +utils.prototype.applyEasing = function (name) { + var list = { + "easeIn": function (t) { + return Math.pow(t, 3); + }, + "easeOut": function (t) { + return 1 - Math.pow(1 - t, 3); + }, + "easeInOut": function (t) { + // easeInOut试了一下感觉二次方效果明显点 + if (t < 0.5) return Math.pow(t, 2) * 2; + else return 1 - Math.pow(1 - t, 2) * 2; + }, + "linear": function (t) { + return t + } + } + if (name == 'random') { + var keys = Object.keys(list); + name = keys[Math.floor(Math.random() * keys.length)]; + } + return list[name] || list.linear; +} + +////// 数组转RGB ////// +utils.prototype.arrayToRGB = function (color) { + if (!(color instanceof Array)) return color; + var nowR = this.clamp(parseInt(color[0]), 0, 255), nowG = this.clamp(parseInt(color[1]), 0, 255), + nowB = this.clamp(parseInt(color[2]), 0, 255); + return "#" + ((1 << 24) + (nowR << 16) + (nowG << 8) + nowB).toString(16).slice(1); +} + +utils.prototype.arrayToRGBA = function (color) { + if (!(color instanceof Array)) return color; + if (color[3] == null) color[3] = 1; + var nowR = this.clamp(parseInt(color[0]), 0, 255), nowG = this.clamp(parseInt(color[1]), 0, 255), + nowB = this.clamp(parseInt(color[2]), 0, 255), nowA = this.clamp(parseFloat(color[3]), 0, 1); + return "rgba(" + nowR + "," + nowG + "," + nowB + "," + nowA + ")"; +} + +////// 加密路线 ////// +utils.prototype.encodeRoute = function (route) { + var ans = "", lastMove = "", cnt = 0; + + route.forEach(function (t) { + if (t == 'up' || t == 'down' || t == 'left' || t == 'right') { + if (t != lastMove && cnt > 0) { + ans += lastMove.substring(0, 1).toUpperCase(); + if (cnt > 1) ans += cnt; + cnt = 0; + } + lastMove = t; + cnt++; + } + else { + if (cnt > 0) { + ans += lastMove.substring(0, 1).toUpperCase(); + if (cnt > 1) ans += cnt; + cnt = 0; + } + ans += core.utils._encodeRoute_encodeOne(t); + } + }); + if (cnt > 0) { + ans += lastMove.substring(0, 1).toUpperCase(); + if (cnt > 1) ans += cnt; + } + return LZString.compressToBase64(ans); +} + +utils.prototype._encodeRoute_id2number = function (id) { + var number = core.maps.getNumberById(id); + return number == 0 ? id : number; +} + +utils.prototype._encodeRoute_encodeOne = function (t) { + if (t.indexOf('item:') == 0) + return "I" + this._encodeRoute_id2number(t.substring(5)) + ":"; + else if (t.indexOf('unEquip:') == 0) + return "u" + t.substring(8); + else if (t.indexOf('equip:') == 0) + return "e" + this._encodeRoute_id2number(t.substring(6)) + ":"; + else if (t.indexOf('saveEquip:') == 0) + return "s" + t.substring(10); + else if (t.indexOf('loadEquip:') == 0) + return "l" + t.substring(10); + else if (t.indexOf('fly:') == 0) + return "F" + t.substring(4) + ":"; + else if (t == 'choices:none') + return "c"; + else if (t.indexOf('choices:') == 0) + return "C" + t.substring(8); + else if (t.indexOf('shop:') == 0) + return "S" + t.substring(5) + ":"; + else if (t == 'turn') + return 'T'; + else if (t.indexOf('turn:') == 0) + return "t" + t.substring(5).substring(0, 1).toUpperCase() + ":"; + else if (t == 'getNext') + return 'G'; + else if (t == 'input:none') + return 'p'; + else if (t.indexOf('input:') == 0) + return "P" + t.substring(6); + else if (t.indexOf('input2:') == 0) + return "Q" + t.substring(7) + ":"; + else if (t == 'no') + return 'N'; + else if (t.indexOf('move:') == 0) + return "M" + t.substring(5); + else if (t.indexOf('key:') == 0) + return 'K' + t.substring(4); + else if (t.indexOf('click:') == 0) + return 'k' + t.substring(6); + else if (t.indexOf('random:') == 0) + return 'X' + t.substring(7); + return '(' + t + ')'; +} + +////// 解密路线 ////// +utils.prototype.decodeRoute = function (route) { + if (!route) return route; + + // 解压缩 + try { + var v = LZString.decompressFromBase64(route); + if (v != null && /^[-_a-zA-Z0-9+\/=:()]*$/.test(v)) { + if (v != "" || route.length < 8) + route = v; + } + } catch (e) { + } + + var decodeObj = { route: route, index: 0, ans: [] }; + while (decodeObj.index < decodeObj.route.length) { + this._decodeRoute_decodeOne(decodeObj, decodeObj.route.charAt(decodeObj.index++)); + } + return decodeObj.ans; +} + +utils.prototype._decodeRoute_getNumber = function (decodeObj, noparse) { + var num = ""; + var first = true; + while (true) { + var ch = decodeObj.route.charAt(decodeObj.index); + if (ch >= '0' && ch <= '9') num += ch; + else if (ch == '-' && first) num += ch; + else break; + first = false; + decodeObj.index++; + } + if (num.length == 0) num = "1"; + return noparse ? num : parseInt(num); +} + +utils.prototype._decodeRoute_getString = function (decodeObj) { + var str = ""; + while (decodeObj.index < decodeObj.route.length && decodeObj.route.charAt(decodeObj.index) != ':') { + str += decodeObj.route.charAt(decodeObj.index++); + } + decodeObj.index++; + return str; +} + +utils.prototype._decodeRoute_number2id = function (number) { + if (/^\d+$/.test(number)) { + var info = core.maps.blocksInfo[number]; + if (info) return info.id; + } + return number; +} + +utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { + // --- 特殊处理自定义项 + if (c == '(') { + var idx = decodeObj.route.indexOf(')', decodeObj.index); + if (idx >= 0) { + decodeObj.ans.push(decodeObj.route.substring(decodeObj.index, idx)); + decodeObj.index = idx + 1; + return; + } + } + var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't') ? + this._decodeRoute_getString(decodeObj) : this._decodeRoute_getNumber(decodeObj); + + var mp = { "U": "up", "D": "down", "L": "left", "R": "right" }; + + switch (c) { + case "U": + case "D": + case "L": + case "R": + for (var i = 0; i < nxt; i++) decodeObj.ans.push(mp[c]); + break; + case "I": + decodeObj.ans.push("item:" + this._decodeRoute_number2id(nxt)); + break; + case "u": + decodeObj.ans.push("unEquip:" + nxt); + break; + case "e": + decodeObj.ans.push("equip:" + this._decodeRoute_number2id(nxt)); + break; + case "s": + decodeObj.ans.push("saveEquip:" + nxt); + break; + case "l": + decodeObj.ans.push("loadEquip:" + nxt); + break; + case "F": + decodeObj.ans.push("fly:" + nxt); + break; + case 'c': + decodeObj.ans.push('choices:none'); + break; + case "C": + decodeObj.ans.push("choices:" + nxt); + break; + case "S": + decodeObj.ans.push("shop:" + nxt); + break; + case "T": + decodeObj.ans.push("turn"); + break; + case "t": + decodeObj.ans.push("turn:" + mp[nxt]); + break; + case "G": + decodeObj.ans.push("getNext"); + break; + case "p": + decodeObj.ans.push("input:none"); + break; + case "P": + decodeObj.ans.push("input:" + nxt); + break; + case "Q": + decodeObj.ans.push("input2:" + nxt); + break; + case "N": + decodeObj.ans.push("no"); + break; + case "M": + ++decodeObj.index; + decodeObj.ans.push("move:" + nxt + ":" + this._decodeRoute_getNumber(decodeObj)); + break; + case "K": + decodeObj.ans.push("key:" + nxt); + break; + case "k": + ++decodeObj.index; + var px = this._decodeRoute_getNumber(decodeObj); + ++decodeObj.index; + var py = this._decodeRoute_getNumber(decodeObj); + decodeObj.ans.push("click:" + nxt + ":" + px + ":" + py); + break; + case "X": + decodeObj.ans.push("random:" + nxt); + break; + } +} + +////// 判断某对象是否不为null也不为NaN ////// +utils.prototype.isset = function (val) { + return val != null && !(typeof val == 'number' && isNaN(val)); +} + +////// 获得子数组 ////// +utils.prototype.subarray = function (a, b) { + if (!(a instanceof Array) || !(b instanceof Array) || a.length < b.length) + return null; + for (var i = 0; i < b.length; ++i) { + if (a[i] != b[i]) return null; + } + return a.slice(b.length); +} + +utils.prototype.inArray = function (array, element) { + return (array instanceof Array) && array.indexOf(element) >= 0; +} + +utils.prototype.clamp = function (x, a, b) { + var min = Math.min(a, b), max = Math.max(a, b); + return Math.min(Math.max(x || 0, min), max); +} + +utils.prototype.getCookie = function (name) { + var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); + return match ? match[2] : null; +} + +////// 设置statusBar的innerHTML,会自动斜体和放缩,也可以增加自定义css ////// +utils.prototype.setStatusBarInnerHTML = function (name, value, css) { + if (!core.statusBar[name]) return; + if (typeof value == 'number') value = this.formatBigNumber(value); + var italic = /^[-a-zA-Z0-9`~!@#$%^&*()_=+\[{\]}\\|;:'",<.>\/?]*$/.test(value); + var style = 'font-style: ' + (italic ? 'italic' : 'normal') + '; '; + style += 'text-shadow: #000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0; '; + // 判定是否需要缩放 + var length = this.strlen(value) || 1; + style += 'font-size: ' + Math.min(1, 7 / length) + 'em; '; + if (css) style += css; + var _style = core.statusBar[name].getAttribute('_style'); + var _value = core.statusBar[name].getAttribute('_value'); + if (_style == style) { + if (value == _value) return; + core.statusBar[name].children[0].innerText = value; + } else { + core.statusBar[name].innerHTML = ""; + core.statusBar[name].children[0].innerText = value; + core.statusBar[name].setAttribute('_style', style); + } + core.statusBar[name].setAttribute('_value', value);; +} + +utils.prototype.strlen = function (str) { + var count = 0; + for (var i = 0, len = str.length; i < len; i++) { + count += str.charCodeAt(i) < 256 ? 1 : 2; + } + return count; +}; + +utils.prototype.turnDirection = function (turn, direction) { + direction = direction || core.getHeroLoc('direction'); + var directionList = ["left", "leftup", "up", "rightup", "right", "rightdown", "down", "leftdown"]; + if (directionList.indexOf(turn) >= 0) return turn; + if (turn == ':hero') return core.getHeroLoc('direction'); + if (turn == ':backhero') return this.turnDirection(':back', core.getHeroLoc('direction')); + if (typeof turn === 'number' && turn % 45 == 0) turn /= 45; + else { + switch (turn) { + case ':left': turn = 6; break; // turn left + case ':right': turn = 2; break; // turn right + case ':back': turn = 4; break; // turn back + default: turn = 0; break; + } + } + var index = directionList.indexOf(direction); + if (index < 0) return direction; + return directionList[(index + (turn || 0)) % directionList.length]; +} + +utils.prototype.matchWildcard = function (pattern, string) { + try { + return new RegExp('^' + pattern.split(/\*+/).map(function (s) { + return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); + }).join('.*') + '$').test(string); + } catch (e) { + return false; + } +} + +utils.prototype.matchRegex = function (pattern, string) { + try { + if (pattern.startsWith("^")) pattern = pattern.substring(1); + if (pattern.endsWith("$")) pattern = pattern.substring(0, pattern.length - 1); + return new RegExp("^" + pattern + "$").test(string); + } catch (e) { + return false; + } +} + +////// Base64加密 ////// +utils.prototype.encodeBase64 = function (str) { + return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { + return String.fromCharCode(parseInt(p1, 16)) + })) +} + +////// Base64解密 ////// +utils.prototype.decodeBase64 = function (str) { + return decodeURIComponent(atob(str).split('').map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); +} + +utils.prototype.rand = function (num) { + var rand = core.getFlag('__rand__'); + rand = this.__next_rand(rand); + core.setFlag('__rand__', rand); + var ans = rand / 2147483647; + if (num && num > 0) + return Math.floor(ans * num); + return ans; +} + +////// 生成随机数(录像方法) ////// +utils.prototype.rand2 = function (num) { + num = num || 2147483648; + num = Math.abs(num); + + var value; + if (core.isReplaying()) { + var action = core.status.replay.toReplay.shift(); + if (action.indexOf("random:") == 0) { + value = parseInt(action.substring(7)); + if (isNaN(value) || value >= num || value < 0) { + console.warn('错误!当前random:项超过范围。将重新随机生成!'); + value = Math.floor(Math.random() * num); + } + } + else { + console.warn('错误!当前需要一个random:项。将重新随机生成!'); + value = Math.floor(Math.random() * num); + } + } + else { + value = Math.floor(Math.random() * num); + } + core.status.route.push("random:" + value); + return value; +} + +utils.prototype.__init_seed = function () { + var rand = new Date().getTime() % 34834795 + 3534; + rand = this.__next_rand(rand); + rand = this.__next_rand(rand); + rand = this.__next_rand(rand); + core.setFlag('__seed__', rand); + core.setFlag('__rand__', rand); +} + +utils.prototype.__next_rand = function (_rand) { + _rand = (_rand % 127773) * 16807 - ~~(_rand / 127773) * 2836; + _rand += _rand < 0 ? 2147483647 : 0; + return _rand; +} + +////// 读取一个本地文件内容 ////// +utils.prototype.readFile = function (success, error, accept, readType) { + + core.platform.successCallback = success; + core.platform.errorCallback = error; + + if (window.jsinterface) { + window.jsinterface.readFile(); + return; + } + + // step 0: 不为http/https,直接不支持 + if (!core.platform.isOnline) { + alert("离线状态下不支持文件读取!"); + if (error) error(); + return; + } + + // Step 1: 如果不支持FileReader,直接不支持 + if (core.platform.fileReader == null) { + alert("当前浏览器不支持FileReader!"); + if (error) error(); + return; + } + + if (core.platform.fileInput == null) { + core.platform.fileInput = document.createElement("input"); + core.platform.fileInput.style.opacity = 0; + core.platform.fileInput.type = 'file'; + core.platform.fileInput.onchange = function () { + var files = core.platform.fileInput.files; + if (files.length == 0) { + if (core.platform.errorCallback) + core.platform.errorCallback(); + return; + } + if (!readType) core.platform.fileReader.readAsText(core.platform.fileInput.files[0]); + else core.platform.fileReader.readAsDataURL(core.platform.fileInput.files[0]); + core.platform.fileInput.value = ''; + } + } + core.platform.fileInput.value = ''; + if (accept) core.platform.fileInput.accept = accept; + + core.platform.fileInput.click(); +} + +////// 读取文件完毕 ////// +utils.prototype.readFileContent = function (content) { + var obj = null; + if (content.slice(0, 4) === 'data') { + if (core.platform.successCallback) + core.platform.successCallback(content); + return; + } + // 检查base64 + try { + obj = JSON.parse(LZString.decompressFromBase64(content)); + } catch (e) { } + if (!obj) { + try { + obj = JSON.parse(content); + } catch (e) { + console.error(e) + } + } + + if (obj) { + if (core.platform.successCallback) + core.platform.successCallback(obj); + return; + } + + if (core.platform.errorCallback) + core.platform.errorCallback(); +} + +////// 下载文件到本地 ////// +utils.prototype.download = function (filename, content) { + + if (window.jsinterface) { + window.jsinterface.download(filename, content); + return; + } + + // Step 0: 不为http/https,直接不支持 + if (!core.platform.isOnline) { + alert("离线状态下不支持下载操作!"); + return; + } + + // Step 1: 如果是iOS平台,直接不支持 + if (core.platform.isIOS) { + if (core.copy(content)) { + alert("iOS平台下不支持直接下载文件!\n所有应下载内容已经复制到您的剪切板,请自行创建空白文件并粘贴。"); + } + else { + alert("iOS平台下不支持下载操作!"); + } + return; + } + + // Step 2: 如果不是PC平台(Android),则只支持chrome + if (!core.platform.isPC) { + if (!core.platform.isChrome || core.platform.isQQ || core.platform.isWeChat) { // 检测chrome + if (core.copy(content)) { + alert("移动端只有Chrome浏览器支持直接下载文件!\n所有应下载内容已经复制到您的剪切板,请自行创建空白文件并粘贴。"); + } + else { + alert("该平台或浏览器暂不支持下载操作!"); + } + return; + } + } + + // Step 3: 如果是Safari浏览器,则提示并打开新窗口 + if (core.platform.isSafari) { + alert("你当前使用的是Safari浏览器,不支持直接下载文件。\n即将打开一个新窗口为应下载内容,请自行全选复制然后创建空白文件并粘贴。"); + var blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); + var href = window.URL.createObjectURL(blob); + var opened = window.open(href, "_blank"); + window.URL.revokeObjectURL(href); + return; + } + + // Step 4: 下载 + var blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); + if (window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveBlob(blob, filename); + } + else { + var href = window.URL.createObjectURL(blob); + var elem = window.document.createElement('a'); + elem.href = href; + elem.download = filename; + document.body.appendChild(elem); + elem.click(); + document.body.removeChild(elem); + window.URL.revokeObjectURL(href); + } +} + +////// 复制一段内容到剪切板 ////// +utils.prototype.copy = function (data) { + + if (window.jsinterface) { + window.jsinterface.copy(data); + return true; + } + + if (!core.platform.supportCopy) return false; + + var textArea = document.createElement("textarea"); + textArea.style.position = 'fixed'; + textArea.style.top = 0; + textArea.style.left = 0; + textArea.style.width = '2em'; + textArea.style.height = '2em'; + textArea.style.padding = 0; + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + textArea.style.background = 'transparent'; + textArea.value = data; + document.body.appendChild(textArea); + textArea.focus(); + textArea.setSelectionRange(0, textArea.value.length); + var successful = false; + try { + successful = document.execCommand('copy'); + } catch (err) { + successful = false; + } + + document.body.removeChild(textArea); + return successful; +} + +////// 显示一段confirm ////// +utils.prototype.myconfirm = function (hint, yesCallback, noCallback) { + main.dom.inputDiv.style.display = 'block'; + main.dom.inputMessage.innerHTML = hint.replace(/\n/g, '
      '); + main.dom.inputBox.style.display = 'none'; + main.dom.inputYes.blur(); + main.dom.inputNo.blur(); + core.status.holdingKeys = []; + + core.platform.successCallback = yesCallback; + core.platform.errorCallback = noCallback; +} + +////// 让用户输入一段文字 ////// +utils.prototype.myprompt = function (hint, value, callback) { + main.dom.inputDiv.style.display = 'block'; + main.dom.inputMessage.innerHTML = hint.replace(/\n/g, '
      '); + main.dom.inputBox.style.display = 'block'; + main.dom.inputBox.value = value == null ? "" : value; + main.dom.inputYes.blur(); + main.dom.inputNo.blur(); + setTimeout(function () { + main.dom.inputBox.focus(); + }); + core.status.holdingKeys = []; + + core.platform.successCallback = core.platform.errorCallback = callback; +} + +////// 动画显示某对象 ////// +utils.prototype.showWithAnimate = function (obj, speed, callback) { + obj.style.display = 'block'; + if (!speed || main.mode != 'play') { + obj.style.opacity = 1; + if (callback) callback(); + return; + } + obj.style.opacity = 0; + var opacityVal = 0; + var showAnimate = window.setInterval(function () { + opacityVal += 0.03; + obj.style.opacity = opacityVal; + if (opacityVal > 1) { + clearInterval(showAnimate); + if (callback) callback(); + } + }, speed); +} + +////// 动画使某对象消失 ////// +utils.prototype.hideWithAnimate = function (obj, speed, callback) { + if (!speed || main.mode != 'play') { + obj.style.display = 'none'; + if (callback) callback(); + return; + } + obj.style.opacity = 1; + var opacityVal = 1; + var hideAnimate = window.setInterval(function () { + opacityVal -= 0.03; + obj.style.opacity = opacityVal; + if (opacityVal < 0) { + obj.style.display = 'none'; + clearInterval(hideAnimate); + if (callback) callback(); + } + }, speed); +} + +////// 生成浏览器唯一的 guid ////// +utils.prototype.getGuid = function () { + var guid = localStorage.getItem('guid'); + if (guid != null) return guid; + guid = 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + localStorage.setItem('guid', guid); + return guid; +} + +utils.prototype.hashCode = function (obj) { + if (typeof obj == 'string') { + var hash = 0, i, chr; + if (obj.length === 0) return hash; + for (i = 0; i < obj.length; i++) { + chr = obj.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; + } + return hash; + } + return this.hashCode(JSON.stringify(obj).split("").sort().join("")); +} + +utils.prototype.same = function (a, b) { + if (a == null && b == null) return true; + if (a == null || b == null) return false; + if (a === b) return true; + if (a instanceof Array && b instanceof Array) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; i++) { + if (!this.same(a[i], b[i])) return false; + } + return true; + } + if (a instanceof Object && b instanceof Object) { + var obj = {}; + for (var i in a) obj[i] = true; + for (var i in b) obj[i] = true; + for (var i in obj) { + if (!this.same(a[i], b[i])) return false; + } + return true; + } + return false; +} + +utils.prototype.unzip = function (blobOrUrl, success, error, convertToText, onprogress) { + var _error = function (msg) { + console.error(msg); + if (error) error(msg); + } + + if (!window.zip) { + return _error("zip.js not exists!"); + } + + if (typeof blobOrUrl == 'string') { + return core.http('GET', blobOrUrl, null, function (data) { + core.unzip(data, success, error, convertToText); + }, _error, null, 'blob', onprogress); + } + + if (!(blobOrUrl instanceof Blob)) { + return _error("Should use Blob or URL as input"); + } + + zip.createReader(new zip.BlobReader(blobOrUrl), function (reader) { + reader.getEntries(function (entries) { + core.utils._unzip_readEntries(entries, function (data) { + reader.close(function () { + if (success) success(data); + }); + }, convertToText); + }); + }, _error); +} + +utils.prototype._unzip_readEntries = function (entries, success, convertToText) { + var results = {}; + if (entries == null || entries.length == 0) { + return success(results); + } + var length = entries.length; + entries.forEach(function (entry) { + entry.getData(convertToText ? new zip.TextWriter('utf8') : new zip.BlobWriter(), function (data) { + results[entry.filename] = data; + length--; + if (length == 0) { + success(results); + } + }); + }); +} + +utils.prototype.http = function (type, url, formData, success, error, mimeType, responseType, onprogress) { + var xhr = new XMLHttpRequest(); + xhr.open(type, url, true); + if (mimeType) xhr.overrideMimeType(mimeType); + if (responseType) xhr.responseType = responseType; + xhr.onload = function (e) { + if (xhr.status == 200) { + if (success) success(xhr.response); + } + else { + if (error) error("HTTP " + xhr.status); + } + }; + xhr.onprogress = function (e) { + if (e.lengthComputable) { + if (onprogress) onprogress(e.loaded, e.total); + } + } + xhr.onabort = function () { + if (error) error("Abort"); + } + xhr.ontimeout = function () { + if (error) error("Timeout"); + } + xhr.onerror = function () { + if (error) error("Error on Connection"); + } + if (formData) + xhr.send(formData); + else xhr.send(); +} + +// LZW-compress +// https://gist.github.com/revolunet/843889 +function lzw_encode (s) { + var dict = {}; + var data = (s + "").split(""); + var out = []; + var currChar; + var phrase = data[0]; + var code = 256; + for (var i = 1; i < data.length; i++) { + currChar = data[i]; + if (dict[phrase + currChar] != null) { + phrase += currChar; + } + else { + out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0)); + dict[phrase + currChar] = code; + code++; + phrase = currChar; + } + } + out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0)); + for (var i = 0; i < out.length; i++) { + out[i] = String.fromCharCode(out[i]); + } + return out.join(""); +} + +// Decompress an LZW-encoded string +function lzw_decode (s) { + var dict = {}; + var data = (s + "").split(""); + var currChar = data[0]; + var oldPhrase = currChar; + var out = [currChar]; + var code = 256; + var phrase; + for (var i = 1; i < data.length; i++) { + var currCode = data[i].charCodeAt(0); + if (currCode < 256) { + phrase = data[i]; + } + else { + phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar); + } + out.push(phrase); + currChar = phrase.charAt(0); + dict[code] = oldPhrase + currChar; + code++; + oldPhrase = phrase; + } + return out.join(""); +} diff --git a/vue.css b/vue.css new file mode 100644 index 0000000..a38b902 --- /dev/null +++ b/vue.css @@ -0,0 +1 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:19.2px;height:1.2rem;vertical-align:middle}.progress{background-color:#42b983;background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:5}.search .search-keyword,.search a:hover{color:#42b983;color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}.task-list-item{list-style-type:none}li input[type=checkbox]{margin:0 .2em .25em -1.6em;vertical-align:middle}.app-nav{left:0;margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:2}.app-nav p{margin:0}.app-nav>a{margin:0 16px;margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:#42b983;color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 16px;margin:0 1rem;padding:5px 0;position:relative}.app-nav li ul{background-color:#fff;border:1px solid #ddd;border-bottom-color:#ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:scroll;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:16px;line-height:1rem;margin:0;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.app-nav.no-badge{margin-right:25px}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner svg{color:#fff;fill:#42b983;fill:var(--theme-color,#42b983);height:80px;width:80px}.github-corner:hover .octo-arm{-webkit-animation:a .56s ease-in-out;animation:a .56s ease-in-out}main{display:block;position:relative;width:100vw;height:100%;z-index:0}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;top:0;bottom:0;left:0;position:absolute;transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out,-webkit-transform .25s ease-out;width:300px;z-index:3}.sidebar>h1{margin:0 auto 16px;margin:0 auto 1rem;font-size:24px;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar ul{margin:0;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;bottom:0;left:0;position:absolute;text-align:center;transition:opacity .3s;width:30px;width:284px;z-index:4}.sidebar-toggle .sidebar-toggle-button:hover{opacity:.4}.sidebar-toggle span{background-color:#42b983;background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;top:0;right:0;bottom:0;left:300px;position:absolute;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:800px;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:16px;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;width:20px;height:20px;text-align:center;top:14px}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:16px;padding:1rem}body.close .sidebar{-webkit-transform:translateX(-300px);transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;overflow-x:hidden}.sidebar{left:-300px;transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out,-webkit-transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:-webkit-transform .25s ease;transition:transform .25s ease;transition:transform .25s ease,-webkit-transform .25s ease}.app-nav,.github-corner{transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out,-webkit-transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto}body.close .sidebar{-webkit-transform:translateX(300px);transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px}body.close .content{-webkit-transform:translateX(300px);transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner .octo-arm{-webkit-animation:a .56s ease-in-out;animation:a .56s ease-in-out}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}}@-webkit-keyframes a{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@keyframes a{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}section.cover{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;display:none}section.cover .cover-main{-webkit-box-flex:1;-ms-flex:1;flex:1;margin:-20px 16px 0;text-align:center;z-index:1}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:24px;line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:40px;font-size:2.5rem;font-weight:300;margin:10px 0 40px;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-7px;bottom:-.4375rem;font-size:16px;font-size:1rem;position:absolute}section.cover blockquote{font-size:24px;font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-color:#42b983;border:1px solid var(--theme-color,#42b983);border-radius:2rem;box-sizing:border-box;color:#42b983;color:var(--theme-color,#42b983);display:inline-block;font-size:16.8px;font-size:1.05rem;letter-spacing:1.6px;letter-spacing:.1rem;margin-right:16px;margin-right:1rem;padding:.75em 32px;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:#42b983;background-color:var(--theme-color,#42b983);color:#fff;margin-right:0}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:#42b983;color:var(--theme-color,#42b983)}section.cover.show{display:-webkit-box;display:-ms-flexbox;display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;width:100%;height:100%}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0 6px 15px}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:32px;font-size:2rem;margin:0 0 16px;margin:0 0 1rem}.markdown-section h2{font-size:28px;font-size:1.75rem;margin:45px 0 12.8px;margin:45px 0 .8rem}.markdown-section h3{font-size:24px;font-size:1.5rem;margin:40px 0 9.6px;margin:40px 0 .6rem}.markdown-section h4{font-size:20px;font-size:1.25rem}.markdown-section h5,.markdown-section h6{font-size:16px;font-size:1rem}.markdown-section h6{color:#777}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:25.6px;line-height:1.6rem;word-spacing:.8px;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:24px;padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid #42b983;border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code{border-radius:2px;color:#e96900;font-size:12.8px;font-size:.8rem;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section code,.markdown-section pre{background-color:#f8f8f8;font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section pre{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;line-height:24px;line-height:1.5rem;margin:1.2em 0;overflow:auto;padding:0 22.4px;padding:0 1.4rem;position:relative;word-wrap:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:#42b983;color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:#42b983;color:var(--theme-color,#42b983)}.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;background-color:#f8f8f8;border-radius:2px;color:#525252;display:block;font-family:Roboto Mono,Monaco,courier,monospace;font-size:12.8px;font-size:.8rem;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;padding:2.2em 5px;white-space:inherit}.markdown-section code:after,.markdown-section code:before{letter-spacing:.8px;letter-spacing:.05rem}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:24px;min-height:1.5rem}pre:after{color:#ccc;content:attr(data-lang);font-size:9.6px;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0} \ No newline at end of file