Initial commit

This commit is contained in:
bdf1 2023-08-25 01:27:54 +10:00
commit 8cefd15107
243 changed files with 77844 additions and 0 deletions

57
_codelab/L1.md Normal file
View File

@ -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)

244
_codelab/L1_answer.md Normal file
View File

@ -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)
};
}
```

102
_codelab/L2.md Normal file
View File

@ -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)

153
_codelab/L2_answer.md Normal file
View File

@ -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;
}
}
```
激光和阻击同领域实现。

38
_codelab/L3.md Normal file
View File

@ -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`项决定。并在游戏中实际验证效果。

4
_codelab/_sidebar.md Normal file
View File

@ -0,0 +1,4 @@
- [第一章:伤害计算函数](L1)
- [第二章:特殊属性定义,表格配置](L2)
- [第三章:怪物真实属性](L3)

2
_codelab/docsify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

86
_codelab/index.html Normal file
View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="ch_ZN">
<head>
<meta charset="UTF-8">
<title>HTML5魔塔样板JS进阶</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link href="vue.css" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<xml id="toolbox" style="display:none"></xml>
<div id="blocklyArea" style="opacity: 0;z-index: -1;"><div id="blocklyDiv"></div></div>
<textarea id="codeArea" style="display:none" spellcheck="false"></textarea>
<script>
window.$docsify = {
homepage: 'L1.md',
loadSidebar: true,
name: 'HTML5魔塔样板',
repo: 'https://github.com/ckcz123/mota-js',
// basepath: '../docs/',
// Search Support
search: {
maxAge: 43200000, // 过期时间,单位毫秒,默认一天
paths: 'auto',
placeholder: {
'/': '搜索文档...',
},
noData: {
'/': '找不到结果',
},
},
// load sidebar from _sidebar.md
loadSidebar: '_sidebar',
subMaxLevel: 2,
autoHeader: true,
auto2top: true,
mergeNavbar: true,
formatUpdated: '{YYYY}-{MM}-{DD} {HH}:{mm}:{ss}',
plugins: [
/*
function(hook){
var renderScriptNode=function(str){
return str.replace(/```.*?\r?\n['"]run['"];[^]*?\r?\n```/g,function(x){
return eval(`(function(){${x.replace(/```.*?\r?\n['"]run['"];/,'').slice(0,-3)}})()`)
})
}
var renderMotaAction=function(str){
return str.replace(bg.pattern,function(x){
return bg.replaceFunc(x)
})
}
hook.beforeEach(function(content){
return renderMotaAction(renderScriptNode(
content
))
})
hook.doneEach(function(){
var map=bg.replaceToken
var node=document.querySelector('.markdown-section')
var str=node.innerHTML
for(var id in map){
str=str.replace(id,map[id])
}
node.innerHTML=str
})
}
*/
]
}
</script>
<!-- 为了保证时序用脚本加载这两个 -->
<script src="docsify.min.js"></script>
</body>
</html>

5
_codelab/index.md Normal file
View File

@ -0,0 +1,5 @@
# H5脚本进阶顺序
```
伤害计算 - 战后 - 自绘状态栏 - 其他脚本编辑如检查地图伤害 - 插件中简单的复写libs - 自己根据API写插件函数 - 使用register系列注册点击和动画等 - 啃libs的实现 - 啃编辑器
```

1
_codelab/vue.css Normal file

File diff suppressed because one or more lines are too long

10
_docs/_sidebar.md Normal file
View File

@ -0,0 +1,10 @@
- [快速上手](start)
- [元件说明](element)
- [事件编辑](event)
- [事件指令](instruction)
- [个性化](personalization)
- [脚本](script)
- [修改编辑器](editor)
- [UI编辑器](ui-editor)
- [附录API列表](api)

2415
_docs/api.md Normal file

File diff suppressed because it is too large Load Diff

55
_docs/blocksdemo.md Normal file
View File

@ -0,0 +1,55 @@
http://127.0.0.1:1055/_docs/#/blocksdemo
方便测试用
这种写法不会生成按钮
```js
'run';
return bg.parseList(['行内']);
// return bg.parse(['asdasd','df'],'event')+'<br>'+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']
```

2
_docs/docsify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

322
_docs/editor.md Normal file
View File

@ -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
<pre><code>playBgm_s
: '播放背景音乐' EvalString '开始播放秒数' Int '持续到下个本事件' Bool <b style='color:green'>'参数列表' JsonEvalString?</b> 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' : '';
<b style='color:green'>if (JsonEvalString_0) {
JsonEvalString_0 = ', "args": ' +JsonEvalString_0;
}</b>
var code = '{"type": "playBgm", "name": "'+EvalString_0+'"'+Int_0+Bool_0+<b style='color:green'>JsonEvalString_0+</b>'},\n';
return code;
*/;</code></pre>
使用了类型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到图块的部分
<pre><code> break;
case "playBgm":
this.next = MotaActionBlocks['playBgm_s'].xmlText([
data.name,data.startTime||0,data.keep||false<b style='color:green'>,data.args</b>,this.next]);
break
case "pauseBgm":</code></pre>
### 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中修改以下位置(请善用搜索来定位)
<pre><code> | drawSelector_1_s
| unknown_s
| function_s<b style='color:green'>
| meteorite_s</b>
| pass_s
;
text_0_s</code></pre>
并在分号后面添加
```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到图块的代码, 同理模仿'画面闪烁'和'强制战斗'
<pre><code> break;
case "playBgm":
this.next = MotaActionBlocks['playBgm_s'].xmlText([
data.name,data.startTime||0,data.keep||false,this.next]);
break;<b style='color:green'>
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;
</b>case "pauseBgm":</code></pre>
最后在editor_blocklyconfig.js中将其加入到工具栏中
<pre><code> '特效/声音':[<b style='color:green'>
MotaActionBlocks['meteorite_s'].xmlText(),
</b>MotaActionBlocks['sleep_s'].xmlText(),</code></pre>
==========================================================================================
[继续阅读下一章UI编辑器](ui-editor)

467
_docs/element.md Normal file
View File

@ -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回合数为勇士的攻击回合数、净化倍数为n1表示单纯无视护盾、吸血比例为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. 绘制坐标xy贴图在地图中的左上角像素坐标譬如x和y都填32则表示贴图左上角和“地图左上角格子的右下角”重合。
5. 初始禁用Y/N如果勾选了此项则此贴图初始时不显示您可以在事件中再将其显示出来。
6. 裁剪起点坐标xy和宽高wh此项规定了贴图在按帧切分前从原图中取哪一部分x和y为所取部分在原图中的左上角坐标不填视为两个0w和h为所取部分的宽高不填表示一直取到右下角
7. 帧数frame不填视为1如果填写了大于1的整数就会把上述裁剪得到的结果再从左到右等分为若干份并在实际绘制时从左到右逐帧可能还带有翻转循环绘制每帧的持续时间和其他图块一致。
* 贴图本身只具有观赏性,您仍然需要使用空气墙等手段去控制其绘制区域各个点的通行性。
* 在使用贴图来表现魔龙和章鱼这类大型怪物时需要预先准备一种32×322帧或32×484帧的行走图并注册为怪物放在地图上时指定该点的不透明度为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
* 如果玩家使用的是手机且没有连接WiFiiOS和部分浏览器无法获知网络状态将始终视为流量党那么背景音乐默认不会开启可以在标题画面点击右下角的圆形按钮来开启。
* V2.8起,这个圆形的音乐开关旁边还增加了一个放大镜按钮,您可以在标题画面连续点击它来获得一个最佳的屏幕缩放倍率。
* 发布到官网的作品还可以从<https://dl.h5mota.com/music/>远程加载背景音乐,您可以点击此链接试听和下载其他作品的背景音乐。
* 是否启用远程加载、以及启用时远程加载的根目录由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)

329
_docs/event.md Normal file
View File

@ -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. **色相:**必须为0359的整数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**冒号缩写量写作`图块IDx,y`json代码中写作`blockId:x,y`。请注意逗号始终要用英文的此值块实际直接调用的API为`core.getBlockId(x,y)`即本值块和对应的冒号缩写量只支持本层可以获知本层某个点的图块ID.
3. **图块数字:**冒号缩写量写作`图块数字x,y`json代码中写作`blockNumberx,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个装备孔中的装备IDn从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层xy点的独立开关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)

BIN
_docs/img/V2_8server.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
_docs/img/bgm.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
_docs/img/blockly.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
_docs/img/changefloor.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
_docs/img/compare.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
_docs/img/console.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
_docs/img/console1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
_docs/img/console2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
_docs/img/editor.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
_docs/img/elements.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
_docs/img/events.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
_docs/img/firstdatab.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
_docs/img/floor.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
_docs/img/flowctrl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
_docs/img/images_texty.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
_docs/img/mapsc.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
_docs/img/plugin.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
_docs/img/quickshops.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
_docs/img/server.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
_docs/img/sources.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
_docs/img/uievent.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
_docs/img/values.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
_docs/img/values_flagsb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

197
_docs/index.html Normal file
View File

@ -0,0 +1,197 @@
<!DOCTYPE html>
<html lang="ch_ZN">
<head>
<meta charset="UTF-8">
<title>HTML5魔塔样板</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link href="vue.css" rel="stylesheet">
<script>
//先下载着
/*
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://cdn.bootcss.com/docsify/4.5.5/docsify.min.js', true);
xhr.send(null);
xhr = new XMLHttpRequest();
xhr.open('GET', 'https://cdn.bootcss.com/docsify/4.5.5/plugins/search.min.js', true);
xhr.send(null);
(function(){
window.bg={replaceToken:{}}
bg.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);
});
}
bg.table=function(ths,tdss,args){
return `<table class="${((args||{}).class||[]).join(' ')}"><thead><tr>${ths.map(v=>`<th style="text-align:left">${v}</th>`).join('')}</tr></thead><tbody>${tdss.map(tds=>`<tr>${tds.map(v=>`<td style="text-align:left">${v}</td>`).join('')}</tr>`).join('')}</tbody></table>`;
}
bg.pattern=/```\s*MotaAction.*?\r?\n[^]*?\r?\n\s*```/g;
bg.replaceFunc=function(str){
var content=null;
try {
content=eval(str.split('\n').slice(1,-1).join('\n'))
} catch (ee) {
}
var match=/```\s*MotaAction(\.)?(\w+)?/.exec(str);
var blocksvg=''
if (!match[2] || match[2]=='action')blocksvg= bg.parseList(content);
else blocksvg=bg.parse(content,match[2]);
return '<p>'+blocksvg+'</p>\n\n';
}
bg.parse=function(obj,type){
MotaActionFunctions.workspace().clear();
xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event');
xml = Blockly.Xml.textToDom('<xml>'+xml_text+'</xml>');
Blockly.Xml.domToWorkspace(xml, MotaActionFunctions.workspace());
svgBlock=document.querySelector("g.blocklyBlockCanvas > g.blocklyDraggable")
svgBlock.setAttribute('transform','translate(0,0)')
tmp={width:svgBlock.getBBox().width,height:svgBlock.getBBox().height}
var id=`<span>${bg.guid()}</span>`
bg.replaceToken[id]=`<svg width="${tmp.width}px" height="${tmp.height}px">`+svgBlock.outerHTML+'</svg>'
return id;
}
bg.parseList=function(obj){
MotaActionFunctions.workspace().clear();
xml_text = MotaActionFunctions.actionParser.parseList(obj);
xml = Blockly.Xml.textToDom('<xml>'+xml_text+'</xml>');
Blockly.Xml.domToWorkspace(xml, MotaActionFunctions.workspace());
svgBlock=document.querySelector("g.blocklyBlockCanvas > g.blocklyDraggable")
svgBlock.setAttribute('transform','translate(0,0)')
tmp={width:svgBlock.getBBox().width,height:svgBlock.getBBox().height}
var id=`<span>${bg.guid()}</span>`
bg.replaceToken[id]=`<svg width="${tmp.width}px" height="${tmp.height}px">`+svgBlock.outerHTML+'</svg>'
return id;
}
})()
*/
</script>
</head>
<body>
<div id="app"></div>
<xml id="toolbox" style="display:none"></xml>
<div id="blocklyArea" style="opacity: 0;z-index: -1;"><div id="blocklyDiv"></div></div>
<textarea id="codeArea" style="display:none" spellcheck="false"></textarea>
<script>
window.$docsify = {
homepage: 'index.md',
loadSidebar: true,
name: 'HTML5魔塔样板',
repo: 'https://github.com/ckcz123/mota-js',
// basepath: '../docs/',
// Search Support
search: {
maxAge: 43200000, // 过期时间,单位毫秒,默认一天
paths: 'auto',
placeholder: {
'/': '搜索文档...',
},
noData: {
'/': '找不到结果',
},
},
// load sidebar from _sidebar.md
loadSidebar: '_sidebar',
subMaxLevel: 2,
autoHeader: true,
auto2top: true,
mergeNavbar: true,
formatUpdated: '{YYYY}-{MM}-{DD} {HH}:{mm}:{ss}',
plugins: [
/*
function(hook){
var renderScriptNode=function(str){
return str.replace(/```.*?\r?\n['"]run['"];[^]*?\r?\n```/g,function(x){
return eval(`(function(){${x.replace(/```.*?\r?\n['"]run['"];/,'').slice(0,-3)}})()`)
})
}
var renderMotaAction=function(str){
return str.replace(bg.pattern,function(x){
return bg.replaceFunc(x)
})
}
hook.beforeEach(function(content){
return renderMotaAction(renderScriptNode(
content
))
})
hook.doneEach(function(){
var map=bg.replaceToken
var node=document.querySelector('.markdown-section')
var str=node.innerHTML
for(var id in map){
str=str.replace(id,map[id])
}
node.innerHTML=str
})
}
*/
]
}
</script>
<!-- 为了保证时序用脚本加载这两个 -->
<script src="docsify.min.js"></script>
<script src="search.min.js"></script>
<!--
<script src="../_server/blockly/Converter.bundle.min.js"></script>
<script src="../_server/blockly/blockly_compressed.js"></script>
<script src="../_server/blockly/blocks_compressed.js"></script>
<script src="../_server/blockly/javascript_compressed.js"></script>
<script src="../_server/blockly/zh-hans.js"></script>
<script src='../_server/MotaActionParser.js'></script>
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
alert("图块描述文件加载失败, 请在'启动服务.exe'中打开编辑器");
return;
}
var grammerFile = xhr.responseText;
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] + 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);
</script> -->
</body>
</html>

25
_docs/index.md Normal file
View File

@ -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)

656
_docs/instruction.md Normal file
View File

@ -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层1212点处的事件”指令就能显示出二楼的小偷。
* 同理勇士接触此小偷并处理事件事件结束前执行一个“隐藏同时删除当前点事件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—965—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值初始为135z值较大的画布将覆盖较小的详见“个性化”。闪烁光标的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)

441
_docs/personalization.md Normal file
View File

@ -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-index135可以通过事件设置该值
- uiUI层用来绘制一切UI窗口如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 140)
- data数据层用来绘制一些顶层的或更新比较快的数据如左上角的提示战斗界面中数据的变化等等。 (z-index: 170)
请注意显示图片事件将自动创建一个图片层z-index是100+图片编号。
色调层的z-index是25ui层的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`和`图块IDx,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
<div class="status" id="speedCol">
<img id="img-speed">
<p class='statusLabel' id='speed'></p>
</div>
```
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`,即改用循环计算临界值,这样临界计算才不会出问题!
&nbsp;
通过上述这几种方式我们就能成功的让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)

739
_docs/script.md Normal file
View File

@ -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为当前点坐标可为nullprefix为当前点前缀
```
`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)

1
_docs/search.min.js vendored Normal file
View File

@ -0,0 +1 @@
!function(){"use strict";function e(e){var n={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"};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;r<o.length;r++)!function(n){var r=o[n],a=!1,s="",c=r.title&&r.title.trim(),l=r.body&&r.body.trim(),f=r.slug||"";if(c&&l&&(i.forEach(function(n,t){var o=new RegExp(n,"gi"),i=-1,r=-1;if(i=c&&c.search(o),r=l&&l.search(o),i<0&&r<0)a=!1;else{a=!0,r<0&&(r=0);var f=0,d=0;f=r<11?0:r-10,d=0===f?70:r+n.length+60,d>l.length&&(d=l.length);var p="..."+e(l).substring(f,d).replace(o,'<em class="search-keyword">'+n+"</em>")+"...";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")<Date.now();if(g=JSON.parse(localStorage.getItem("docsify.search.index")),a)g={};else if(!r)return;var s=r?n(i.router):e.paths,c=s.length,l=0;s.forEach(function(n){if(g[n])return l++;h.get(i.router.getFile(n)).then(function(r){g[n]=o(n,r,i.router,e.depth),c===++l&&t(e.maxAge)})})}function a(){Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 7px;\n line-height: 22px;\n font-size: 14px;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}")}function s(e,n){void 0===n&&(n="");var t='<input type="search" value="'+n+'" /><div class="results-panel"></div></div>',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+='<div class="matching-post">\n<a href="'+e.url+'"> \n<h2>'+e.title+"</h2>\n<p>"+e.content+"</p>\n</a>\n</div>"}),t.classList.add("show"),t.innerHTML=r||'<p class="empty">'+y+"</p>"}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)}();

129
_docs/start.md Normal file
View File

@ -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)

864
_docs/ui-editor.md Normal file
View File

@ -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)

1
_docs/vue.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,57 @@
MIT License
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
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.
----------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
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.
----------------------------------------------------------------
MIT License
Copyright (C) 2013 by Marijn Haverbeke and others <marijnh@gmail.com>
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.

6
_server/CodeMirror/acorn.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
_server/CodeMirror/beautify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,552 @@
/* ========== CodeMirror.css ========= */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
direction: ltr;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
/* GUTTER */
.CodeMirror-gutters {
white-space: nowrap;
}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
white-space: nowrap;
}
/* CURSOR */
.CodeMirror-cursor {
border-right: none;
width: 0;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-fat-cursor-mark {
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
@-moz-keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@-webkit-keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
/* Can style cursor different in overwrite (non-insert) mode */
.cm-tab {
display: inline-block;
text-decoration: inherit;
}
.CodeMirror-rulers {
position: absolute;
left: 0;
right: 0;
top: -50px;
bottom: -20px;
overflow: hidden;
}
.CodeMirror-ruler {
top: 0;
bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-header, .cm-strong {
font-weight: bold;
}
.cm-em {
font-style: italic;
}
.cm-link {
text-decoration: underline;
}
.cm-strikethrough {
text-decoration: line-through;
}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {
}
.CodeMirror-composing {
border-bottom: 2px solid;
}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0;
top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0;
left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0;
bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0;
bottom: 0;
}
.CodeMirror-gutters {
position: absolute;
left: 0;
top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0;
bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection {
background-color: transparent
}
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent
}
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
padding: 0.1px; /* Force widget margins to stay inside of the container */
}
.CodeMirror-widget {
}
.CodeMirror-rtl pre {
direction: rtl;
}
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre {
position: static;
}
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-crosshair {
cursor: crosshair;
}
/* Used to force a border model for a node */
.cm-force-border {
padding-right: .1px;
}
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after {
content: '';
}
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext {
background: none;
}
/* ========= show-hint.css ========= */
.CodeMirror-hints {
position: absolute;
z-index: 340;
overflow: hidden;
list-style: none;
margin: 0;
padding: 2px;
border-radius: 3px;
font-size: 90%;
font-family: monospace;
max-height: 20em;
overflow-y: auto;
}
.CodeMirror-hint {
margin: 0;
padding: 0 4px;
border-radius: 2px;
white-space: pre;
cursor: pointer;
}
/* ========= lint.css ========= */
/* The lint marker gutter */
.CodeMirror-lint-markers {
width: 16px;
}
.CodeMirror-lint-tooltip {
border-radius: 4px 4px 4px 4px;
font-family: monospace;
font-size: 10pt;
overflow: hidden;
padding: 2px 5px;
position: fixed;
white-space: pre;
white-space: pre-wrap;
z-index: 330;
max-width: 600px;
opacity: 0;
transition: opacity .4s;
-moz-transition: opacity .4s;
-webkit-transition: opacity .4s;
-o-transition: opacity .4s;
-ms-transition: opacity .4s;
}
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
background-position: left bottom;
background-repeat: repeat-x;
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
display: inline-block;
height: 16px;
width: 16px;
vertical-align: middle;
position: relative;
}
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
padding-left: 18px;
background-position: top left;
background-repeat: no-repeat;
}
.CodeMirror-lint-marker-multiple {
background-repeat: no-repeat;
background-position: right bottom;
width: 100%;
height: 100%;
}
/* ========= dialog.css ========= */
.CodeMirror-dialog {
position: absolute;
left: 0; right: 0;
background: inherit;
z-index: 15;
padding: .1em .8em;
overflow: hidden;
color: inherit;
}
.CodeMirror-dialog-top {
top: 0;
}
.CodeMirror-dialog-bottom {
bottom: 0;
}
.CodeMirror-dialog input {
border: none;
outline: none;
background: transparent;
width: 20em;
color: inherit;
font-family: monospace;
}
.CodeMirror-dialog button {
font-size: 70%;
}
/* ========= tern.css ========= */
.CodeMirror-Tern-completion {
padding-left: 22px;
position: relative;
line-height: 1.5;
}
.CodeMirror-Tern-completion:before {
position: absolute;
left: 2px;
bottom: 2px;
border-radius: 50%;
font-size: 12px;
font-weight: bold;
height: 15px;
width: 15px;
line-height: 16px;
text-align: center;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.CodeMirror-Tern-tooltip {
border-radius: 3px;
padding: 2px 5px;
font-size: 90%;
font-family: monospace;
white-space: pre-wrap;
max-width: 40em;
position: absolute;
z-index: 320;
transition: opacity 1s;
-moz-transition: opacity 1s;
-webkit-transition: opacity 1s;
-o-transition: opacity 1s;
-ms-transition: opacity 1s;
}
.CodeMirror-Tern-hint-doc {
max-width: 25em;
margin-top: -3px;
}
.CodeMirror-Tern-farg-current { text-decoration: underline; }
.CodeMirror-Tern-fhint-guess { opacity: .7; }
.CodeMirror-foldmarker {
font-family: arial;
line-height: .3;
cursor: pointer;
}
.CodeMirror-foldgutter {
width: .7em;
}
.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
cursor: pointer;
}
.CodeMirror-foldgutter-open:after {
content: "\25BE";
}
.CodeMirror-foldgutter-folded:after {
content: "\25B8";
}

4130
_server/CodeMirror/defs.js Normal file

File diff suppressed because it is too large Load Diff

1
_server/CodeMirror/jshint.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
_server/CodeMirror/tern.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4242
_server/MotaAction.g4 Normal file

File diff suppressed because it is too large Load Diff

1737
_server/MotaActionParser.js Normal file

File diff suppressed because it is too large Load Diff

273
_server/README.md Normal file
View File

@ -0,0 +1,273 @@
# editor
直接使用游戏运行时(之后简称core)的代码来绘制游戏画面, 借助fs.js来实现浏览器编辑文件. 通过表格编辑数据, blockly图块编辑事件, code mirror编辑文本的可视化魔塔编辑器.
![](img_md/view1.png)
左侧数据区, 中间地图区, 右侧素材区
![](img_md/view2.png)
事件编辑器
![](img_md/view3.png)
脚本编辑器
> 此文件是editor的结构说明, 不是使用文档
## 组成
本目录下所有文件,以及`../editor.html`,`../editor-mobile.html`和`../启动服务.exe`,`../server.py`是地图编辑器的所有组件.
### 父目录
+ editor(-mobile).html
编辑器的[入口页面](http://127.0.0.1:1055/editor.html)
以`display:none`的形式引入了core的`index.html`的`dom`,修改了原来的`.gameCanvas #ui #data`等的名字以避免冲突
+ 启动服务.exe [源码](http://github.com/ckcz123/mota-js-server/)
为fs.js提供后端支持, 同时集成了一些实用工具
+ server.py
非windows平台中为fs.js提供后端支持
### core
游戏运行时中部分代码根据`main.mod=='editor'`进行了调整
+ 通过`main.init('editor')`加载数据
+ `editor`模式关闭了部分动画
+ `core.drawMap`中`editor`模式下不再画图,而是生成画图的函数提+ 供给`editor`
+ `editor`模式下`GlobalAnimate`可以独立的选择是否播放
+ `core.playBgm`和`core.playSound`中非`play`模式不再播放声音
+ `core.show`和`core.hide`中非`play`模式不再进行动画而是立刻+ 完成并执行回调
+ `editor`模式不执行`core.resize`
### fs.js
依照[issue#31](https://github.com/ckcz123/mota-js/issues/13)的约定, 模仿node的fs模块提供如下api,与`启动服务.exe`,`server.py`配合为js提供文件读写功能, 是编辑器成立的前提
``` js
fs.readFile('file.in','utf-8',callback)
//读文本文件
//callback:function(err, data)
//data:字符串
fs.readFile('file.in','base64',callback)
//读二进制文件
//callback:function(err, data)
//data:base64字符串
fs.writeFile('file.out', data ,'utf-8', callback)
//写文本文件
//callback:function(err)
//data:字符串
fs.writeFile('file.out', data ,'base64', callback)
//写二进制文件
//callback:function(err)
//data:base64字符串
fs.readdir(path, callback)
//callback:function(err, data)
//path:支持"/"做分隔符
//data:[filename1,filename2,..] filename是字符串,只包含文件不包含目录
//所有参数不允许缺省
```
### editor_multi.js
用[CodeMirror](https://github.com/codemirror/CodeMirror) 实现有高亮的多行文本编辑
编辑选定`id_`的文本域
``` js
editor_multi.import(id_,{lint:true})
```
编辑blockly方块的特定域
``` js
editor_multi.multiLineEdit(value,b,f,{lint:true},callback)
```
配置表格
``` js
editor_multi.editCommentJs(mod)
```
### MotaAction.g4
通过[antlr-blockly](https://github.com/zhaouv/antlr-blockly)的语法定义core中各事件对应的方块.
借助google的[blockly](https://github.com/google/blockly)来实现事件的可视化编辑.
入口方块以`_m`结尾
一般语句写在`action`中, 以`_s`结尾
### editor_blockly.js
把选定`id_`的事件用blockly编辑
``` js
editor_blockly.import(id_,{type:'event'});
```
把文本区域的代码转换成图块
``` js
editor_blockly.parse();
```
`initscript中`的`toolboxObj`定义了侧边栏中显示的图块
自定义`Blockly.FieldColour.prototype.createWidget_`修改了颜色选择器的行为
自定义`Blockly.FieldTextInput.prototype.showInlineEditor_`添加了自动补全
### editor_mode.js
用于切换数据区
加载数据
```javascript
editor.mode.loc();
editor.mode.enemyitem();
editor.mode.floor();
editor.mode.tower();
editor.mode.functions();
```
切换模式
```javascript
editor.mode.onmode('');//清空
editor.mode.onmode('save');//保存
editor.mode.onmode('nextChange');//下次onmode时前端进行切换
editor.mode.onmode('loc');
editor.mode.onmode('enemyitem');
editor.mode.onmode('floor');
editor.mode.onmode('tower');
editor.mode.onmode('functions');
editor.mode.onmode('map');
editor.mode.onmode('appendpic');
```
在`onmode('save')`时,改动才会保存到文件,涉及到图片的改动需要刷新页面使得`editor`能看到
数据区一些通用的函数也定义在这里
### editor_table.js
处理表格的生成, 及其响应的事件
其接受来自../project/\*.js的数据`obj`和来自table/\*.comment.js的注释`commentObj`
commentObj的结构如示例
``` js
{
"_type": "object",
"_data": {
"events": {
"_type": "object",
"_data": {
"resetGame": {
"_leaf": true,
"_type": "textarea",
"_lint": true,
"_data": "重置整个游戏"
},
"setInitData": {
"_leaf": true,
"_type": "textarea",
"_lint": true,
"_data": "设置初始属性"
},
```
一层正常数据, 一层`_`开头的结构说明, 忽略`_`层的话与obj同结构
通过
``` js
editor.table.objToTable(obj, commentObj)
editor.table.objToTr
editor.table.objToTd
```
遍历这两个对象来生成表格, 叶节点根据`_type`渲染成对应的dom
表格的值变化`onchange`, 双击`dblclickfunc`, 删除`deletefunc`, 添加`addfunc`也定义在此文件
### editor_mappanel.js
与地图区相关的功能
+ 画地图 线/矩形/tileset
+ 通过地图选中事件或素材
+ 右键菜单
+ 切换楼层
+ 大地图移动可视窗口
### editor_materialpanel.js
与素材区相关的功能
+ 选中
+ 展开/折叠
### editor_datapanel.js
与数据区相关的功能 (且与表格无关的功能)
+ 地图编辑
- 创建新地图
- 批量创建
+ 地图选点
+ 图块属性
- 注册素材
- 修改id
+ 楼层属性
- 修改楼层id
+ 全塔属性
+ 脚本编辑
+ 追加素材
- 选择导入的区域
- 导入图片
- 改色相
- 选中图片中的格子
- 确认追加
+ 公共事件
+ 插件编写
### editor_ui.js
ui事件中没有具体到前三个区中的函数
+ 响应点击
+ 快捷键
+ 显示帮助
+ UI预览 & 地图选点相关
### editor_util.js
一些通用的函数
+ 生成id
+ HTML转义
+ 像素处理
+ base64的encode/decode
+ 检查值是否为空
### editor_listen.js
界面与功能的绑定
### editor_file.js
包装fs.js, 把数据读写到对应的文件
### editor_game.js
游戏数据的处理
此部分的重构未完成, 实际上是由editor_file.js和editor_file_unsorted.js来做的
### editor.js
初始化加整合各模块
现状是还放了一些游戏数据有关的函数未挪到editor_game, 以及部分和入口页面生成有关的函数

File diff suppressed because one or more lines are too long

177
_server/blockly/LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2011
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

13
_server/blockly/NOTICE.md Normal file
View File

@ -0,0 +1,13 @@
# NOTICE
files
`blockly_compressed.js`
`blocks_compressed.js`
`javascript_compressed.js`
`zh-hans.js`
`media/*`
copyed from
https://github.com/google/blockly.git
`Converter.bundle.min.js`
copyed from
https://github.com/zhaouv/antlr-blockly.git

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
// Do not edit this file; automatically generated by gulp.
'use strict';
Blockly.Blocks.colour={};Blockly.Constants={};Blockly.Constants.Colour={};Blockly.Constants.Colour.HUE=20;
Blockly.defineBlocksWithJsonArray([{type:"colour_picker",message0:"%1",args0:[{type:"field_colour",name:"COLOUR",colour:"#ff0000"}],output:"Colour",helpUrl:"%{BKY_COLOUR_PICKER_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_PICKER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"colour_random",message0:"%{BKY_COLOUR_RANDOM_TITLE}",output:"Colour",helpUrl:"%{BKY_COLOUR_RANDOM_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RANDOM_TOOLTIP}"},{type:"colour_rgb",message0:"%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3",
args0:[{type:"input_value",name:"RED",check:"Number",align:"RIGHT"},{type:"input_value",name:"GREEN",check:"Number",align:"RIGHT"},{type:"input_value",name:"BLUE",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_RGB_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RGB_TOOLTIP}"},{type:"colour_blend",message0:"%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} %1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3",args0:[{type:"input_value",name:"COLOUR1",check:"Colour",
align:"RIGHT"},{type:"input_value",name:"COLOUR2",check:"Colour",align:"RIGHT"},{type:"input_value",name:"RATIO",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_BLEND_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_BLEND_TOOLTIP}"}]);Blockly.Blocks.lists={};Blockly.Constants.Lists={};Blockly.Constants.Lists.HUE=260;
Blockly.defineBlocksWithJsonArray([{type:"lists_create_empty",message0:"%{BKY_LISTS_CREATE_EMPTY_TITLE}",output:"Array",style:"list_blocks",tooltip:"%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}",helpUrl:"%{BKY_LISTS_CREATE_EMPTY_HELPURL}"},{type:"lists_repeat",message0:"%{BKY_LISTS_REPEAT_TITLE}",args0:[{type:"input_value",name:"ITEM"},{type:"input_value",name:"NUM",check:"Number"}],output:"Array",style:"list_blocks",tooltip:"%{BKY_LISTS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_LISTS_REPEAT_HELPURL}"},{type:"lists_reverse",
message0:"%{BKY_LISTS_REVERSE_MESSAGE0}",args0:[{type:"input_value",name:"LIST",check:"Array"}],output:"Array",inputsInline:!0,style:"list_blocks",tooltip:"%{BKY_LISTS_REVERSE_TOOLTIP}",helpUrl:"%{BKY_LISTS_REVERSE_HELPURL}"},{type:"lists_isEmpty",message0:"%{BKY_LISTS_ISEMPTY_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",style:"list_blocks",tooltip:"%{BKY_LISTS_ISEMPTY_TOOLTIP}",helpUrl:"%{BKY_LISTS_ISEMPTY_HELPURL}"},{type:"lists_length",message0:"%{BKY_LISTS_LENGTH_TITLE}",
args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",style:"list_blocks",tooltip:"%{BKY_LISTS_LENGTH_TOOLTIP}",helpUrl:"%{BKY_LISTS_LENGTH_HELPURL}"}]);
Blockly.Blocks.lists_create_with={init:function(){this.setHelpUrl(Blockly.Msg.LISTS_CREATE_WITH_HELPURL);this.setStyle("list_blocks");this.itemCount_=3;this.updateShape_();this.setOutput(!0,"Array");this.setMutator(new Blockly.Mutator(["lists_create_with_item"]));this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"),
10);this.updateShape_()},decompose:function(a){var b=a.newBlock("lists_create_with_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d<this.itemCount_;d++){var e=a.newBlock("lists_create_with_item");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}return b},compose:function(a){var b=a.getInputTargetBlock("STACK");for(a=[];b;)a.push(b.valueConnection_),b=b.nextConnection&&b.nextConnection.targetBlock();for(b=0;b<this.itemCount_;b++){var c=this.getInput("ADD"+b).connection.targetConnection;
c&&-1==a.indexOf(c)&&c.disconnect()}this.itemCount_=a.length;this.updateShape_();for(b=0;b<this.itemCount_;b++)Blockly.Mutator.reconnect(a[b],this,"ADD"+b)},saveConnections:function(a){a=a.getInputTargetBlock("STACK");for(var b=0;a;){var c=this.getInput("ADD"+b);a.valueConnection_=c&&c.connection.targetConnection;b++;a=a.nextConnection&&a.nextConnection.targetBlock()}},updateShape_:function(){this.itemCount_&&this.getInput("EMPTY")?this.removeInput("EMPTY"):this.itemCount_||this.getInput("EMPTY")||
this.appendDummyInput("EMPTY").appendField(Blockly.Msg.LISTS_CREATE_EMPTY_TITLE);for(var a=0;a<this.itemCount_;a++)if(!this.getInput("ADD"+a)){var b=this.appendValueInput("ADD"+a).setAlign(Blockly.ALIGN_RIGHT);0==a&&b.appendField(Blockly.Msg.LISTS_CREATE_WITH_INPUT_WITH)}for(;this.getInput("ADD"+a);)this.removeInput("ADD"+a),a++}};
Blockly.Blocks.lists_create_with_container={init:function(){this.setStyle("list_blocks");this.appendDummyInput().appendField(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD);this.appendStatementInput("STACK");this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP);this.contextMenu=!1}};
Blockly.Blocks.lists_create_with_item={init:function(){this.setStyle("list_blocks");this.appendDummyInput().appendField(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TITLE);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);this.contextMenu=!1}};
Blockly.Blocks.lists_indexOf={init:function(){var a=[[Blockly.Msg.LISTS_INDEX_OF_FIRST,"FIRST"],[Blockly.Msg.LISTS_INDEX_OF_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_INDEX_OF_HELPURL);this.setStyle("list_blocks");this.setOutput(!0,"Number");this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST);this.appendValueInput("FIND").appendField(new Blockly.FieldDropdown(a),"END");this.setInputsInline(!0);var b=this;this.setTooltip(function(){return Blockly.Msg.LISTS_INDEX_OF_TOOLTIP.replace("%1",
b.workspace.options.oneBasedIndex?"0":"-1")})}};
Blockly.Blocks.lists_getIndex={init:function(){var a=[[Blockly.Msg.LISTS_GET_INDEX_GET,"GET"],[Blockly.Msg.LISTS_GET_INDEX_GET_REMOVE,"GET_REMOVE"],[Blockly.Msg.LISTS_GET_INDEX_REMOVE,"REMOVE"]];this.WHERE_OPTIONS=[[Blockly.Msg.LISTS_GET_INDEX_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_INDEX_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_INDEX_FIRST,"FIRST"],[Blockly.Msg.LISTS_GET_INDEX_LAST,"LAST"],[Blockly.Msg.LISTS_GET_INDEX_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.LISTS_GET_INDEX_HELPURL);this.setStyle("list_blocks");
a=new Blockly.FieldDropdown(a,function(a){a="REMOVE"==a;this.getSourceBlock().updateStatement_(a)});this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_GET_INDEX_INPUT_IN_LIST);this.appendDummyInput().appendField(a,"MODE").appendField("","SPACE");this.appendDummyInput("AT");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.LISTS_GET_INDEX_TAIL);this.setInputsInline(!0);this.setOutput(!0);this.updateAt_(!0);var b=this;this.setTooltip(function(){var a=
b.getFieldValue("MODE"),d=b.getFieldValue("WHERE"),e="";switch(a+" "+d){case "GET FROM_START":case "GET FROM_END":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM;break;case "GET FIRST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST;break;case "GET LAST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST;break;case "GET RANDOM":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM;break;case "GET_REMOVE FROM_START":case "GET_REMOVE FROM_END":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM;break;case "GET_REMOVE FIRST":e=
Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST;break;case "GET_REMOVE LAST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST;break;case "GET_REMOVE RANDOM":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM;break;case "REMOVE FROM_START":case "REMOVE FROM_END":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM;break;case "REMOVE FIRST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST;break;case "REMOVE LAST":e=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST;break;case "REMOVE RANDOM":e=
Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM}if("FROM_START"==d||"FROM_END"==d)e+=" "+("FROM_START"==d?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP).replace("%1",b.workspace.options.oneBasedIndex?"#1":"#0");return e})},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("statement",!this.outputConnection);var b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){var b=
"true"==a.getAttribute("statement");this.updateStatement_(b);a="false"!=a.getAttribute("at");this.updateAt_(a)},updateStatement_:function(a){a!=!this.outputConnection&&(this.unplug(!0,!0),a?(this.setOutput(!1),this.setPreviousStatement(!0),this.setNextStatement(!0)):(this.setPreviousStatement(!1),this.setNextStatement(!1),this.setOutput(!0)))},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&
this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var c="FROM_START"==b||"FROM_END"==b;if(c!=a){var e=this.getSourceBlock();e.updateAt_(c);e.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.moveInputBefore("TAIL",null)}};
Blockly.Blocks.lists_setIndex={init:function(){var a=[[Blockly.Msg.LISTS_SET_INDEX_SET,"SET"],[Blockly.Msg.LISTS_SET_INDEX_INSERT,"INSERT"]];this.WHERE_OPTIONS=[[Blockly.Msg.LISTS_GET_INDEX_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_INDEX_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_INDEX_FIRST,"FIRST"],[Blockly.Msg.LISTS_GET_INDEX_LAST,"LAST"],[Blockly.Msg.LISTS_GET_INDEX_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.LISTS_SET_INDEX_HELPURL);this.setStyle("list_blocks");this.appendValueInput("LIST").setCheck("Array").appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
this.appendDummyInput().appendField(new Blockly.FieldDropdown(a),"MODE").appendField("","SPACE");this.appendDummyInput("AT");this.appendValueInput("TO").appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_TO);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setTooltip(Blockly.Msg.LISTS_SET_INDEX_TOOLTIP);this.updateAt_(!0);var b=this;this.setTooltip(function(){var a=b.getFieldValue("MODE"),d=b.getFieldValue("WHERE"),e="";switch(a+" "+d){case "SET FROM_START":case "SET FROM_END":e=
Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM;break;case "SET FIRST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST;break;case "SET LAST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST;break;case "SET RANDOM":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM;break;case "INSERT FROM_START":case "INSERT FROM_END":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM;break;case "INSERT FIRST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST;break;case "INSERT LAST":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST;
break;case "INSERT RANDOM":e=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM}if("FROM_START"==d||"FROM_END"==d)e+=" "+Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP.replace("%1",b.workspace.options.oneBasedIndex?"#1":"#0");return e})},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");
this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var c="FROM_START"==b||"FROM_END"==b;if(c!=a){var e=this.getSourceBlock();e.updateAt_(c);e.setFieldValue(b,"WHERE");return null}});this.moveInputBefore("AT","TO");this.getInput("ORDINAL")&&this.moveInputBefore("ORDINAL",
"TO");this.getInput("AT").appendField(b,"WHERE")}};
Blockly.Blocks.lists_getSublist={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_SUBLIST_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_SUBLIST_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_GET_SUBLIST_HELPURL);this.setStyle("list_blocks");
this.appendValueInput("LIST").setCheck("Array").appendField(Blockly.Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST);this.appendDummyInput("AT1");this.appendDummyInput("AT2");Blockly.Msg.LISTS_GET_SUBLIST_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.LISTS_GET_SUBLIST_TAIL);this.setInputsInline(!0);this.setOutput(!0,"Array");this.updateAt_(1,!0);this.updateAt_(2,!0);this.setTooltip(Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),
b=this.getInput("AT1").type==Blockly.INPUT_VALUE;a.setAttribute("at1",b);b=this.getInput("AT2").type==Blockly.INPUT_VALUE;a.setAttribute("at2",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("at1");a="true"==a.getAttribute("at2");this.updateAt_(1,b);this.updateAt_(2,a)},updateAt_:function(a,b){this.removeInput("AT"+a);this.removeInput("ORDINAL"+a,!0);b?(this.appendValueInput("AT"+a).setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL"+a).appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
this.appendDummyInput("AT"+a);var c=new Blockly.FieldDropdown(this["WHERE_OPTIONS_"+a],function(c){var e="FROM_START"==c||"FROM_END"==c;if(e!=b){var d=this.getSourceBlock();d.updateAt_(a,e);d.setFieldValue(c,"WHERE"+a);return null}});this.getInput("AT"+a).appendField(c,"WHERE"+a);1==a&&(this.moveInputBefore("AT1","AT2"),this.getInput("ORDINAL1")&&this.moveInputBefore("ORDINAL1","AT2"));Blockly.Msg.LISTS_GET_SUBLIST_TAIL&&this.moveInputBefore("TAIL",null)}};
Blockly.Blocks.lists_sort={init:function(){this.jsonInit({message0:Blockly.Msg.LISTS_SORT_TITLE,args0:[{type:"field_dropdown",name:"TYPE",options:[[Blockly.Msg.LISTS_SORT_TYPE_NUMERIC,"NUMERIC"],[Blockly.Msg.LISTS_SORT_TYPE_TEXT,"TEXT"],[Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE,"IGNORE_CASE"]]},{type:"field_dropdown",name:"DIRECTION",options:[[Blockly.Msg.LISTS_SORT_ORDER_ASCENDING,"1"],[Blockly.Msg.LISTS_SORT_ORDER_DESCENDING,"-1"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Array",style:"list_blocks",
tooltip:Blockly.Msg.LISTS_SORT_TOOLTIP,helpUrl:Blockly.Msg.LISTS_SORT_HELPURL})}};
Blockly.Blocks.lists_split={init:function(){var a=this,b=new Blockly.FieldDropdown([[Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT,"SPLIT"],[Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST,"JOIN"]],function(b){a.updateType_(b)});this.setHelpUrl(Blockly.Msg.LISTS_SPLIT_HELPURL);this.setStyle("list_blocks");this.appendValueInput("INPUT").setCheck("String").appendField(b,"MODE");this.appendValueInput("DELIM").setCheck("String").appendField(Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER);this.setInputsInline(!0);this.setOutput(!0,
"Array");this.setTooltip(function(){var b=a.getFieldValue("MODE");if("SPLIT"==b)return Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT;if("JOIN"==b)return Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN;throw Error("Unknown mode: "+b);})},updateType_:function(a){if(this.getFieldValue("MODE")!=a){var b=this.getInput("INPUT").connection;b.setShadowDom(null);var c=b.targetBlock();c&&(b.disconnect(),c.isShadow()?c.dispose():this.bumpNeighbours())}"SPLIT"==a?(this.outputConnection.setCheck("Array"),this.getInput("INPUT").setCheck("String")):
(this.outputConnection.setCheck("String"),this.getInput("INPUT").setCheck("Array"))},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("mode",this.getFieldValue("MODE"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("mode"))}};Blockly.Blocks.logic={};Blockly.Constants.Logic={};Blockly.Constants.Logic.HUE=210;
Blockly.defineBlocksWithJsonArray([{type:"logic_boolean",message0:"%1",args0:[{type:"field_dropdown",name:"BOOL",options:[["%{BKY_LOGIC_BOOLEAN_TRUE}","TRUE"],["%{BKY_LOGIC_BOOLEAN_FALSE}","FALSE"]]}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_BOOLEAN_TOOLTIP}",helpUrl:"%{BKY_LOGIC_BOOLEAN_HELPURL}"},{type:"controls_if",message0:"%{BKY_CONTROLS_IF_MSG_IF} %1",args0:[{type:"input_value",name:"IF0",check:"Boolean"}],message1:"%{BKY_CONTROLS_IF_MSG_THEN} %1",args1:[{type:"input_statement",
name:"DO0"}],previousStatement:null,nextStatement:null,style:"logic_blocks",helpUrl:"%{BKY_CONTROLS_IF_HELPURL}",mutator:"controls_if_mutator",extensions:["controls_if_tooltip"]},{type:"controls_ifelse",message0:"%{BKY_CONTROLS_IF_MSG_IF} %1",args0:[{type:"input_value",name:"IF0",check:"Boolean"}],message1:"%{BKY_CONTROLS_IF_MSG_THEN} %1",args1:[{type:"input_statement",name:"DO0"}],message2:"%{BKY_CONTROLS_IF_MSG_ELSE} %1",args2:[{type:"input_statement",name:"ELSE"}],previousStatement:null,nextStatement:null,
style:"logic_blocks",tooltip:"%{BKYCONTROLS_IF_TOOLTIP_2}",helpUrl:"%{BKY_CONTROLS_IF_HELPURL}",extensions:["controls_if_tooltip"]},{type:"logic_compare",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A"},{type:"field_dropdown",name:"OP",options:[["=","EQ"],["\u2260","NEQ"],["\u200f<","LT"],["\u200f\u2264","LTE"],["\u200f>","GT"],["\u200f\u2265","GTE"]]},{type:"input_value",name:"B"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_COMPARE_HELPURL}",extensions:["logic_compare",
"logic_op_tooltip"]},{type:"logic_operation",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Boolean"},{type:"field_dropdown",name:"OP",options:[["%{BKY_LOGIC_OPERATION_AND}","AND"],["%{BKY_LOGIC_OPERATION_OR}","OR"]]},{type:"input_value",name:"B",check:"Boolean"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_OPERATION_HELPURL}",extensions:["logic_op_tooltip"]},{type:"logic_negate",message0:"%{BKY_LOGIC_NEGATE_TITLE}",args0:[{type:"input_value",name:"BOOL",
check:"Boolean"}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_NEGATE_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NEGATE_HELPURL}"},{type:"logic_null",message0:"%{BKY_LOGIC_NULL}",output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_NULL_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NULL_HELPURL}"},{type:"logic_ternary",message0:"%{BKY_LOGIC_TERNARY_CONDITION} %1",args0:[{type:"input_value",name:"IF",check:"Boolean"}],message1:"%{BKY_LOGIC_TERNARY_IF_TRUE} %1",args1:[{type:"input_value",name:"THEN"}],message2:"%{BKY_LOGIC_TERNARY_IF_FALSE} %1",
args2:[{type:"input_value",name:"ELSE"}],output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_TERNARY_TOOLTIP}",helpUrl:"%{BKY_LOGIC_TERNARY_HELPURL}",extensions:["logic_ternary"]}]);
Blockly.defineBlocksWithJsonArray([{type:"controls_if_if",message0:"%{BKY_CONTROLS_IF_IF_TITLE_IF}",nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_IF_TOOLTIP}"},{type:"controls_if_elseif",message0:"%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",previousStatement:null,nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"},{type:"controls_if_else",message0:"%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",previousStatement:null,
enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"}]);Blockly.Constants.Logic.TOOLTIPS_BY_OP={EQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}",NEQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}",LT:"%{BKY_LOGIC_COMPARE_TOOLTIP_LT}",LTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}",GT:"%{BKY_LOGIC_COMPARE_TOOLTIP_GT}",GTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}",AND:"%{BKY_LOGIC_OPERATION_TOOLTIP_AND}",OR:"%{BKY_LOGIC_OPERATION_TOOLTIP_OR}"};
Blockly.Extensions.register("logic_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Logic.TOOLTIPS_BY_OP));
Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN={elseifCount_:0,elseCount_:0,suppressPrefixSuffix:!0,mutationToDom:function(){if(!this.elseifCount_&&!this.elseCount_)return null;var a=Blockly.utils.xml.createElement("mutation");this.elseifCount_&&a.setAttribute("elseif",this.elseifCount_);this.elseCount_&&a.setAttribute("else",1);return a},domToMutation:function(a){this.elseifCount_=parseInt(a.getAttribute("elseif"),10)||0;this.elseCount_=parseInt(a.getAttribute("else"),10)||0;this.rebuildShape_()},
decompose:function(a){var b=a.newBlock("controls_if_if");b.initSvg();for(var c=b.nextConnection,d=1;d<=this.elseifCount_;d++){var e=a.newBlock("controls_if_elseif");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}this.elseCount_&&(a=a.newBlock("controls_if_else"),a.initSvg(),c.connect(a.previousConnection));return b},compose:function(a){a=a.nextConnection.targetBlock();this.elseCount_=this.elseifCount_=0;for(var b=[null],c=[null],d=null;a;){switch(a.type){case "controls_if_elseif":this.elseifCount_++;
b.push(a.valueConnection_);c.push(a.statementConnection_);break;case "controls_if_else":this.elseCount_++;d=a.statementConnection_;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}this.updateShape_();this.reconnectChildBlocks_(b,c,d)},saveConnections:function(a){a=a.nextConnection.targetBlock();for(var b=1;a;){switch(a.type){case "controls_if_elseif":var c=this.getInput("IF"+b),d=this.getInput("DO"+b);a.valueConnection_=c&&c.connection.targetConnection;
a.statementConnection_=d&&d.connection.targetConnection;b++;break;case "controls_if_else":d=this.getInput("ELSE");a.statementConnection_=d&&d.connection.targetConnection;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}},rebuildShape_:function(){var a=[null],b=[null],c=null;this.getInput("ELSE")&&(c=this.getInput("ELSE").connection.targetConnection);for(var d=1;this.getInput("IF"+d);){var e=this.getInput("IF"+d),f=this.getInput("DO"+
d);a.push(e.connection.targetConnection);b.push(f.connection.targetConnection);d++}this.updateShape_();this.reconnectChildBlocks_(a,b,c)},updateShape_:function(){this.getInput("ELSE")&&this.removeInput("ELSE");for(var a=1;this.getInput("IF"+a);)this.removeInput("IF"+a),this.removeInput("DO"+a),a++;for(a=1;a<=this.elseifCount_;a++)this.appendValueInput("IF"+a).setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSEIF),this.appendStatementInput("DO"+a).appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN);
this.elseCount_&&this.appendStatementInput("ELSE").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSE)},reconnectChildBlocks_:function(a,b,c){for(var d=1;d<=this.elseifCount_;d++)Blockly.Mutator.reconnect(a[d],this,"IF"+d),Blockly.Mutator.reconnect(b[d],this,"DO"+d);Blockly.Mutator.reconnect(c,this,"ELSE")}};Blockly.Extensions.registerMutator("controls_if_mutator",Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN,null,["controls_if_elseif","controls_if_else"]);
Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION=function(){this.setTooltip(function(){if(this.elseifCount_||this.elseCount_){if(!this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_2;if(this.elseifCount_&&!this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_3;if(this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_4}else return Blockly.Msg.CONTROLS_IF_TOOLTIP_1;return""}.bind(this))};Blockly.Extensions.register("controls_if_tooltip",Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION);
Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN={onchange:function(a){this.prevBlocks_||(this.prevBlocks_=[null,null]);var b=this.getInputTargetBlock("A"),c=this.getInputTargetBlock("B");b&&c&&!b.outputConnection.checkType(c.outputConnection)&&(Blockly.Events.setGroup(a.group),a=this.prevBlocks_[0],a!==b&&(b.unplug(),!a||a.isDisposed()||a.isShadow()||this.getInput("A").connection.connect(a.outputConnection)),b=this.prevBlocks_[1],b!==c&&(c.unplug(),!b||b.isDisposed()||b.isShadow()||this.getInput("B").connection.connect(b.outputConnection)),
this.bumpNeighbours(),Blockly.Events.setGroup(!1));this.prevBlocks_[0]=this.getInputTargetBlock("A");this.prevBlocks_[1]=this.getInputTargetBlock("B")}};Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION=function(){this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN)};Blockly.Extensions.register("logic_compare",Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION);
Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN={prevParentConnection_:null,onchange:function(a){var b=this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),d=this.outputConnection.targetConnection;if((b||c)&&d)for(var e=0;2>e;e++){var f=1==e?b:c;f&&!f.outputConnection.checkType(d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours()):(f.unplug(),f.bumpNeighbours()),Blockly.Events.setGroup(!1))}this.prevParentConnection_=
d}};Blockly.Extensions.registerMixin("logic_ternary",Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120;
Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"field_number",name:"TIMES",value:10,
min:0,precision:1}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_whileUntil",message0:"%1 %2",args0:[{type:"field_dropdown",name:"MODE",options:[["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}","WHILE"],["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}","UNTIL"]]},{type:"input_value",name:"BOOL",check:"Boolean"}],
message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",extensions:["controls_whileUntil_tooltip"]},{type:"controls_for",message0:"%{BKY_CONTROLS_FOR_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"FROM",check:"Number",align:"RIGHT"},{type:"input_value",name:"TO",check:"Number",align:"RIGHT"},{type:"input_value",name:"BY",
check:"Number",align:"RIGHT"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],inputsInline:!0,previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOR_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_for_tooltip"]},{type:"controls_forEach",message0:"%{BKY_CONTROLS_FOREACH_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"LIST",check:"Array"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOREACH_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_forEach_tooltip"]},{type:"controls_flow_statements",message0:"%1",args0:[{type:"field_dropdown",name:"FLOW",options:[["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}","BREAK"],["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}","CONTINUE"]]}],previousStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}",
extensions:["controls_flow_tooltip","controls_flow_in_loop_check"]}]);Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS={WHILE:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}",UNTIL:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}"};Blockly.Extensions.register("controls_whileUntil_tooltip",Blockly.Extensions.buildTooltipForDropdown("MODE",Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS={BREAK:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}",CONTINUE:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}"};
Blockly.Extensions.register("controls_flow_tooltip",Blockly.Extensions.buildTooltipForDropdown("FLOW",Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS));
Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getField("VAR").getVariable(),c=b.name;if(!this.isCollapsed()&&null!=c){var d={enabled:!0};d.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c);b=Blockly.Variables.generateVariableFieldDom(b);c=Blockly.utils.xml.createElement("block");c.setAttribute("type","variables_get");c.appendChild(b);d.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(d)}}}};
Blockly.Extensions.registerMixin("contextMenu_newGetVariableBlock",Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);Blockly.Extensions.register("controls_for_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOR_TOOLTIP}","VAR"));Blockly.Extensions.register("controls_forEach_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOREACH_TOOLTIP}","VAR"));
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN={LOOP_TYPES:["controls_repeat","controls_repeat_ext","controls_forEach","controls_for","controls_whileUntil"],suppressPrefixSuffix:!0,getSurroundLoop:function(a){do{if(-1!=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.indexOf(a.type))return a;a=a.getSurroundParent()}while(a);return null},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()&&a.type==Blockly.Events.BLOCK_MOVE&&a.blockId==this.id){var b=
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(this);this.setWarningText(b?null:Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);if(!this.isInFlyout){var c=Blockly.Events.getGroup();Blockly.Events.setGroup(a.group);this.setEnabled(b);Blockly.Events.setGroup(c)}}}};Blockly.Extensions.registerMixin("controls_flow_in_loop_check",Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);Blockly.Blocks.math={};Blockly.Constants.Math={};Blockly.Constants.Math.HUE=230;
Blockly.defineBlocksWithJsonArray([{type:"math_number",message0:"%1",args0:[{type:"field_number",name:"NUM",value:0}],output:"Number",helpUrl:"%{BKY_MATH_NUMBER_HELPURL}",style:"math_blocks",tooltip:"%{BKY_MATH_NUMBER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"math_arithmetic",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Number"},{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ADDITION_SYMBOL}","ADD"],["%{BKY_MATH_SUBTRACTION_SYMBOL}","MINUS"],["%{BKY_MATH_MULTIPLICATION_SYMBOL}",
"MULTIPLY"],["%{BKY_MATH_DIVISION_SYMBOL}","DIVIDE"],["%{BKY_MATH_POWER_SYMBOL}","POWER"]]},{type:"input_value",name:"B",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ARITHMETIC_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_single",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_SINGLE_OP_ROOT}","ROOT"],["%{BKY_MATH_SINGLE_OP_ABSOLUTE}","ABS"],["-","NEG"],["ln","LN"],["log10","LOG10"],["e^","EXP"],["10^","POW10"]]},
{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_SINGLE_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_trig",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_TRIG_SIN}","SIN"],["%{BKY_MATH_TRIG_COS}","COS"],["%{BKY_MATH_TRIG_TAN}","TAN"],["%{BKY_MATH_TRIG_ASIN}","ASIN"],["%{BKY_MATH_TRIG_ACOS}","ACOS"],["%{BKY_MATH_TRIG_ATAN}","ATAN"]]},{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",
helpUrl:"%{BKY_MATH_TRIG_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_constant",message0:"%1",args0:[{type:"field_dropdown",name:"CONSTANT",options:[["\u03c0","PI"],["e","E"],["\u03c6","GOLDEN_RATIO"],["sqrt(2)","SQRT2"],["sqrt(\u00bd)","SQRT1_2"],["\u221e","INFINITY"]]}],output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTANT_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTANT_HELPURL}"},{type:"math_number_property",message0:"%1 %2",args0:[{type:"input_value",name:"NUMBER_TO_CHECK",check:"Number"},
{type:"field_dropdown",name:"PROPERTY",options:[["%{BKY_MATH_IS_EVEN}","EVEN"],["%{BKY_MATH_IS_ODD}","ODD"],["%{BKY_MATH_IS_PRIME}","PRIME"],["%{BKY_MATH_IS_WHOLE}","WHOLE"],["%{BKY_MATH_IS_POSITIVE}","POSITIVE"],["%{BKY_MATH_IS_NEGATIVE}","NEGATIVE"],["%{BKY_MATH_IS_DIVISIBLE_BY}","DIVISIBLE_BY"]]}],inputsInline:!0,output:"Boolean",style:"math_blocks",tooltip:"%{BKY_MATH_IS_TOOLTIP}",mutator:"math_is_divisibleby_mutator"},{type:"math_change",message0:"%{BKY_MATH_CHANGE_TITLE}",args0:[{type:"field_variable",
name:"VAR",variable:"%{BKY_MATH_CHANGE_TITLE_ITEM}"},{type:"input_value",name:"DELTA",check:"Number"}],previousStatement:null,nextStatement:null,style:"variable_blocks",helpUrl:"%{BKY_MATH_CHANGE_HELPURL}",extensions:["math_change_tooltip"]},{type:"math_round",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ROUND_OPERATOR_ROUND}","ROUND"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}","ROUNDUP"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}","ROUNDDOWN"]]},{type:"input_value",name:"NUM",
check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ROUND_HELPURL}",tooltip:"%{BKY_MATH_ROUND_TOOLTIP}"},{type:"math_on_list",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ONLIST_OPERATOR_SUM}","SUM"],["%{BKY_MATH_ONLIST_OPERATOR_MIN}","MIN"],["%{BKY_MATH_ONLIST_OPERATOR_MAX}","MAX"],["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}","AVERAGE"],["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}","MEDIAN"],["%{BKY_MATH_ONLIST_OPERATOR_MODE}","MODE"],["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}",
"STD_DEV"],["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}","RANDOM"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ONLIST_HELPURL}",mutator:"math_modes_of_list_mutator",extensions:["math_op_tooltip"]},{type:"math_modulo",message0:"%{BKY_MATH_MODULO_TITLE}",args0:[{type:"input_value",name:"DIVIDEND",check:"Number"},{type:"input_value",name:"DIVISOR",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_MODULO_TOOLTIP}",
helpUrl:"%{BKY_MATH_MODULO_HELPURL}"},{type:"math_constrain",message0:"%{BKY_MATH_CONSTRAIN_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"Number"},{type:"input_value",name:"LOW",check:"Number"},{type:"input_value",name:"HIGH",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTRAIN_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTRAIN_HELPURL}"},{type:"math_random_int",message0:"%{BKY_MATH_RANDOM_INT_TITLE}",args0:[{type:"input_value",name:"FROM",check:"Number"},
{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_INT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_INT_HELPURL}"},{type:"math_random_float",message0:"%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_FLOAT_HELPURL}"},{type:"math_atan2",message0:"%{BKY_MATH_ATAN2_TITLE}",args0:[{type:"input_value",name:"X",check:"Number"},{type:"input_value",
name:"Y",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_ATAN2_TOOLTIP}",helpUrl:"%{BKY_MATH_ATAN2_HELPURL}"}]);
Blockly.Constants.Math.TOOLTIPS_BY_OP={ADD:"%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}",MINUS:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}",MULTIPLY:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MULTIPLY}",DIVIDE:"%{BKY_MATH_ARITHMETIC_TOOLTIP_DIVIDE}",POWER:"%{BKY_MATH_ARITHMETIC_TOOLTIP_POWER}",ROOT:"%{BKY_MATH_SINGLE_TOOLTIP_ROOT}",ABS:"%{BKY_MATH_SINGLE_TOOLTIP_ABS}",NEG:"%{BKY_MATH_SINGLE_TOOLTIP_NEG}",LN:"%{BKY_MATH_SINGLE_TOOLTIP_LN}",LOG10:"%{BKY_MATH_SINGLE_TOOLTIP_LOG10}",EXP:"%{BKY_MATH_SINGLE_TOOLTIP_EXP}",POW10:"%{BKY_MATH_SINGLE_TOOLTIP_POW10}",
SIN:"%{BKY_MATH_TRIG_TOOLTIP_SIN}",COS:"%{BKY_MATH_TRIG_TOOLTIP_COS}",TAN:"%{BKY_MATH_TRIG_TOOLTIP_TAN}",ASIN:"%{BKY_MATH_TRIG_TOOLTIP_ASIN}",ACOS:"%{BKY_MATH_TRIG_TOOLTIP_ACOS}",ATAN:"%{BKY_MATH_TRIG_TOOLTIP_ATAN}",SUM:"%{BKY_MATH_ONLIST_TOOLTIP_SUM}",MIN:"%{BKY_MATH_ONLIST_TOOLTIP_MIN}",MAX:"%{BKY_MATH_ONLIST_TOOLTIP_MAX}",AVERAGE:"%{BKY_MATH_ONLIST_TOOLTIP_AVERAGE}",MEDIAN:"%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}",MODE:"%{BKY_MATH_ONLIST_TOOLTIP_MODE}",STD_DEV:"%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}",RANDOM:"%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}"};
Blockly.Extensions.register("math_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Math.TOOLTIPS_BY_OP));
Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),b="DIVISIBLE_BY"==this.getFieldValue("PROPERTY");a.setAttribute("divisor_input",b);return a},domToMutation:function(a){a="true"==a.getAttribute("divisor_input");this.updateShape_(a)},updateShape_:function(a){var b=this.getInput("DIVISOR");a?b||this.appendValueInput("DIVISOR").setCheck("Number"):b&&this.removeInput("DIVISOR")}};
Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION=function(){this.getField("PROPERTY").setValidator(function(a){a="DIVISIBLE_BY"==a;this.getSourceBlock().updateShape_(a)})};Blockly.Extensions.registerMutator("math_is_divisibleby_mutator",Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN,Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION);Blockly.Extensions.register("math_change_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_MATH_CHANGE_TOOLTIP}","VAR"));
Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN={updateType_:function(a){"MODE"==a?this.outputConnection.setCheck("Array"):this.outputConnection.setCheck("Number")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("op",this.getFieldValue("OP"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("op"))}};Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION=function(){this.getField("OP").setValidator(function(a){this.updateType_(a)}.bind(this))};
Blockly.Extensions.registerMutator("math_modes_of_list_mutator",Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN,Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION);Blockly.Blocks.procedures={};
Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput("",Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);
this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&this.moveInputBefore("STACK","RETURN")):this.removeInput("STACK",!0),this.hasStatements_=
a)},updateParams_:function(){var a="";this.arguments_.length&&(a=Blockly.Msg.PROCEDURES_BEFORE_PARAMS+" "+this.arguments_.join(", "));Blockly.Events.disable();try{this.setFieldValue(a,"PARAMS")}finally{Blockly.Events.enable()}},mutationToDom:function(a){var b=Blockly.utils.xml.createElement("mutation");a&&b.setAttribute("name",this.getFieldValue("NAME"));for(var c=0;c<this.argumentVarModels_.length;c++){var d=Blockly.utils.xml.createElement("arg"),e=this.argumentVarModels_[c];d.setAttribute("name",
e.name);d.setAttribute("varid",e.getId());a&&this.paramIds_&&d.setAttribute("paramId",this.paramIds_[c]);b.appendChild(d)}this.hasStatements_||b.setAttribute("statements","false");return b},domToMutation:function(a){this.arguments_=[];this.argumentVarModels_=[];for(var b=0,c;c=a.childNodes[b];b++)if("arg"==c.nodeName.toLowerCase()){var d=c.getAttribute("name");c=c.getAttribute("varid")||c.getAttribute("varId");this.arguments_.push(d);c=Blockly.Variables.getOrCreateVariablePackage(this.workspace,c,
d,"");null!=c?this.argumentVarModels_.push(c):console.log("Failed to create a variable with name "+d+", ignoring.")}this.updateParams_();Blockly.Procedures.mutateCallers(this);this.setStatements_("false"!==a.getAttribute("statements"))},decompose:function(a){var b=Blockly.utils.xml.createElement("block");b.setAttribute("type","procedures_mutatorcontainer");var c=Blockly.utils.xml.createElement("statement");c.setAttribute("name","STACK");b.appendChild(c);for(var d=0;d<this.arguments_.length;d++){var e=
Blockly.utils.xml.createElement("block");e.setAttribute("type","procedures_mutatorarg");var f=Blockly.utils.xml.createElement("field");f.setAttribute("name","NAME");var g=Blockly.utils.xml.createTextNode(this.arguments_[d]);f.appendChild(g);e.appendChild(f);f=Blockly.utils.xml.createElement("next");e.appendChild(f);c.appendChild(e);c=f}a=Blockly.Xml.domToBlock(b,a);"procedures_defreturn"==this.type?a.setFieldValue(this.hasStatements_,"STATEMENTS"):a.removeInput("STATEMENT_INPUT");Blockly.Procedures.mutateCallers(this);
return a},compose:function(a){this.arguments_=[];this.paramIds_=[];this.argumentVarModels_=[];for(var b=a.getInputTargetBlock("STACK");b;){var c=b.getFieldValue("NAME");this.arguments_.push(c);c=this.workspace.getVariable(c,"");this.argumentVarModels_.push(c);this.paramIds_.push(b.id);b=b.nextConnection&&b.nextConnection.targetBlock()}this.updateParams_();Blockly.Procedures.mutateCallers(this);a=a.getFieldValue("STATEMENTS");if(null!==a&&(a="TRUE"==a,this.hasStatements_!=a))if(a)this.setStatements_(!0),
Blockly.Mutator.reconnect(this.statementConnection_,this,"STACK"),this.statementConnection_=null;else{a=this.getInput("STACK").connection;if(this.statementConnection_=a.targetConnection)a=a.targetBlock(),a.unplug(),a.bumpNeighbours();this.setStatements_(!1)}},getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!1]},getVars:function(){return this.arguments_},getVarModels:function(){return this.argumentVarModels_},renameVarById:function(a,b){var c=this.workspace.getVariableById(a);
if(""==c.type){c=c.name;b=this.workspace.getVariableById(b);for(var d=!1,e=0;e<this.argumentVarModels_.length;e++)this.argumentVarModels_[e].getId()==a&&(this.arguments_[e]=b.name,this.argumentVarModels_[e]=b,d=!0);d&&(this.displayRenamedVar_(c,b.name),Blockly.Procedures.mutateCallers(this))}},updateVarName:function(a){for(var b=a.name,c=!1,d=0;d<this.argumentVarModels_.length;d++)if(this.argumentVarModels_[d].getId()==a.getId()){var e=this.arguments_[d];this.arguments_[d]=b;c=!0}c&&(this.displayRenamedVar_(e,
b),Blockly.Procedures.mutateCallers(this))},displayRenamedVar_:function(a,b){this.updateParams_();if(this.mutator&&this.mutator.isVisible())for(var c=this.mutator.workspace_.getAllBlocks(!1),d=0,e;e=c[d];d++)"procedures_mutatorarg"==e.type&&Blockly.Names.equals(a,e.getFieldValue("NAME"))&&e.setFieldValue(b,"NAME")},customContextMenu:function(a){if(!this.isInFlyout){var b={enabled:!0},c=this.getFieldValue("NAME");b.text=Blockly.Msg.PROCEDURES_CREATE_DO.replace("%1",c);var d=Blockly.utils.xml.createElement("mutation");
d.setAttribute("name",c);for(c=0;c<this.arguments_.length;c++){var e=Blockly.utils.xml.createElement("arg");e.setAttribute("name",this.arguments_[c]);d.appendChild(e)}c=Blockly.utils.xml.createElement("block");c.setAttribute("type",this.callType_);c.appendChild(d);b.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(b);if(!this.isCollapsed())for(c=0;c<this.argumentVarModels_.length;c++)b={enabled:!0},d=this.argumentVarModels_[c],b.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",d.name),
d=Blockly.Variables.generateVariableFieldDom(d),e=Blockly.utils.xml.createElement("block"),e.setAttribute("type","variables_get"),e.appendChild(d),b.callback=Blockly.ContextMenu.callbackFactory(this,e),a.push(b)}},callType_:"procedures_callnoreturn"};
Blockly.Blocks.procedures_defreturn={init:function(){var a=new Blockly.FieldTextInput("",Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.appendValueInput("RETURN").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&
this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:Blockly.Blocks.procedures_defnoreturn.setStatements_,updateParams_:Blockly.Blocks.procedures_defnoreturn.updateParams_,
mutationToDom:Blockly.Blocks.procedures_defnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_defnoreturn.domToMutation,decompose:Blockly.Blocks.procedures_defnoreturn.decompose,compose:Blockly.Blocks.procedures_defnoreturn.compose,getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!0]},getVars:Blockly.Blocks.procedures_defnoreturn.getVars,getVarModels:Blockly.Blocks.procedures_defnoreturn.getVarModels,renameVarById:Blockly.Blocks.procedures_defnoreturn.renameVarById,
updateVarName:Blockly.Blocks.procedures_defnoreturn.updateVarName,displayRenamedVar_:Blockly.Blocks.procedures_defnoreturn.displayRenamedVar_,customContextMenu:Blockly.Blocks.procedures_defnoreturn.customContextMenu,callType_:"procedures_callreturn"};
Blockly.Blocks.procedures_mutatorcontainer={init:function(){this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE);this.appendStatementInput("STACK");this.appendDummyInput("STATEMENT_INPUT").appendField(Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS).appendField(new Blockly.FieldCheckbox("TRUE"),"STATEMENTS");this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP);this.contextMenu=!1}};
Blockly.Blocks.procedures_mutatorarg={init:function(){var a=new Blockly.FieldTextInput(Blockly.Procedures.DEFAULT_ARG,this.validator_);a.oldShowEditorFn_=a.showEditor_;a.showEditor_=function(){this.createdVariables_=[];this.oldShowEditorFn_()};this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORARG_TITLE).appendField(a,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP);this.contextMenu=
!1;a.onFinishEditing_=this.deleteIntermediateVars_;a.createdVariables_=[];a.onFinishEditing_("x")},validator_:function(a){var b=this.getSourceBlock(),c=Blockly.Mutator.findParentWs(b.workspace);a=a.replace(/[\s\xa0]+/g," ").replace(/^ | $/g,"");if(!a)return null;for(var d=(b.workspace.targetWorkspace||b.workspace).getAllBlocks(!1),e=a.toLowerCase(),f=0;f<d.length;f++)if(d[f].id!=this.getSourceBlock().id){var g=d[f].getFieldValue("NAME");if(g&&g.toLowerCase()==e)return null}if(b.isInFlyout)return a;
(b=c.getVariable(a,""))&&b.name!=a&&c.renameVariableById(b.getId(),a);b||(b=c.createVariable(a,""))&&this.createdVariables_&&this.createdVariables_.push(b);return a},deleteIntermediateVars_:function(a){var b=Blockly.Mutator.findParentWs(this.getSourceBlock().workspace);if(b)for(var c=0;c<this.createdVariables_.length;c++){var d=this.createdVariables_[c];d.name!=a&&b.deleteVariableById(d.getId())}}};
Blockly.Blocks.procedures_callnoreturn={init:function(){this.appendDummyInput("TOPROW").appendField(this.id,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.quarkConnections_={};this.quarkIds_=null;this.previousEnabledState_=!0},getProcedureCall:function(){return this.getFieldValue("NAME")},renameProcedure:function(a,b){Blockly.Names.equals(a,
this.getProcedureCall())&&(this.setFieldValue(b,"NAME"),this.setTooltip((this.outputConnection?Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP:Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace("%1",b)))},setProcedureParameters_:function(a,b){var c=Blockly.Procedures.getDefinition(this.getProcedureCall(),this.workspace),d=c&&c.mutator&&c.mutator.isVisible();d||(this.quarkConnections_={},this.quarkIds_=null);if(b)if(a.join("\n")==this.arguments_.join("\n"))this.quarkIds_=b;else{if(b.length!=a.length)throw RangeError("paramNames and paramIds must be the same length.");
this.setCollapsed(!1);this.quarkIds_||(this.quarkConnections_={},this.quarkIds_=[]);c=this.rendered;this.rendered=!1;for(var e=0;e<this.arguments_.length;e++){var f=this.getInput("ARG"+e);f&&(f=f.connection.targetConnection,this.quarkConnections_[this.quarkIds_[e]]=f,d&&f&&-1==b.indexOf(this.quarkIds_[e])&&(f.disconnect(),f.getSourceBlock().bumpNeighbours()))}this.arguments_=[].concat(a);this.argumentVarModels_=[];for(e=0;e<this.arguments_.length;e++)a=Blockly.Variables.getOrCreateVariablePackage(this.workspace,
null,this.arguments_[e],""),this.argumentVarModels_.push(a);this.updateShape_();if(this.quarkIds_=b)for(e=0;e<this.arguments_.length;e++)b=this.quarkIds_[e],b in this.quarkConnections_&&(f=this.quarkConnections_[b],Blockly.Mutator.reconnect(f,this,"ARG"+e)||delete this.quarkConnections_[b]);(this.rendered=c)&&this.render()}},updateShape_:function(){for(var a=0;a<this.arguments_.length;a++){var b=this.getField("ARGNAME"+a);if(b){Blockly.Events.disable();try{b.setValue(this.arguments_[a])}finally{Blockly.Events.enable()}}else b=
new Blockly.FieldLabel(this.arguments_[a]),this.appendValueInput("ARG"+a).setAlign(Blockly.ALIGN_RIGHT).appendField(b,"ARGNAME"+a).init()}for(;this.getInput("ARG"+a);)this.removeInput("ARG"+a),a++;if(a=this.getInput("TOPROW"))this.arguments_.length?this.getField("WITH")||(a.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS,"WITH"),a.init()):this.getField("WITH")&&a.removeField("WITH")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("name",this.getProcedureCall());
for(var b=0;b<this.arguments_.length;b++){var c=Blockly.utils.xml.createElement("arg");c.setAttribute("name",this.arguments_[b]);a.appendChild(c)}return a},domToMutation:function(a){var b=a.getAttribute("name");this.renameProcedure(this.getProcedureCall(),b);b=[];for(var c=[],d=0,e;e=a.childNodes[d];d++)"arg"==e.nodeName.toLowerCase()&&(b.push(e.getAttribute("name")),c.push(e.getAttribute("paramId")));this.setProcedureParameters_(b,c)},getVarModels:function(){return this.argumentVarModels_},onchange:function(a){if(this.workspace&&
!this.workspace.isFlyout&&a.recordUndo)if(a.type==Blockly.Events.BLOCK_CREATE&&-1!=a.ids.indexOf(this.id)){var b=this.getProcedureCall();b=Blockly.Procedures.getDefinition(b,this.workspace);!b||b.type==this.defType_&&JSON.stringify(b.arguments_)==JSON.stringify(this.arguments_)||(b=null);if(!b){Blockly.Events.setGroup(a.group);a=Blockly.utils.xml.createElement("xml");b=Blockly.utils.xml.createElement("block");b.setAttribute("type",this.defType_);var c=this.getRelativeToSurfaceXY(),d=c.y+2*Blockly.SNAP_RADIUS;
b.setAttribute("x",c.x+Blockly.SNAP_RADIUS*(this.RTL?-1:1));b.setAttribute("y",d);c=this.mutationToDom();b.appendChild(c);c=Blockly.utils.xml.createElement("field");c.setAttribute("name","NAME");c.appendChild(Blockly.utils.xml.createTextNode(this.getProcedureCall()));b.appendChild(c);a.appendChild(b);Blockly.Xml.domToWorkspace(a,this.workspace);Blockly.Events.setGroup(!1)}}else a.type==Blockly.Events.BLOCK_DELETE?(b=this.getProcedureCall(),b=Blockly.Procedures.getDefinition(b,this.workspace),b||(Blockly.Events.setGroup(a.group),
this.dispose(!0),Blockly.Events.setGroup(!1))):a.type==Blockly.Events.CHANGE&&"disabled"==a.element&&(b=this.getProcedureCall(),(b=Blockly.Procedures.getDefinition(b,this.workspace))&&b.id==a.blockId&&((b=Blockly.Events.getGroup())&&console.log("Saw an existing group while responding to a definition change"),Blockly.Events.setGroup(a.group),a.newValue?(this.previousEnabledState_=this.isEnabled(),this.setEnabled(!1)):this.setEnabled(this.previousEnabledState_),Blockly.Events.setGroup(b)))},customContextMenu:function(a){if(this.workspace.isMovable()){var b=
{enabled:!0};b.text=Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;var c=this.getProcedureCall(),d=this.workspace;b.callback=function(){var a=Blockly.Procedures.getDefinition(c,d);a&&(d.centerOnBlock(a.id),a.select())};a.push(b)}},defType_:"procedures_defnoreturn"};
Blockly.Blocks.procedures_callreturn={init:function(){this.appendDummyInput("TOPROW").appendField("","NAME");this.setOutput(!0);this.setStyle("procedure_blocks");this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);this.arguments_=[];this.quarkConnections_={};this.quarkIds_=null;this.previousEnabledState_=!0},getProcedureCall:Blockly.Blocks.procedures_callnoreturn.getProcedureCall,renameProcedure:Blockly.Blocks.procedures_callnoreturn.renameProcedure,setProcedureParameters_:Blockly.Blocks.procedures_callnoreturn.setProcedureParameters_,
updateShape_:Blockly.Blocks.procedures_callnoreturn.updateShape_,mutationToDom:Blockly.Blocks.procedures_callnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_callnoreturn.domToMutation,getVarModels:Blockly.Blocks.procedures_callnoreturn.getVarModels,onchange:Blockly.Blocks.procedures_callnoreturn.onchange,customContextMenu:Blockly.Blocks.procedures_callnoreturn.customContextMenu,defType_:"procedures_defreturn"};
Blockly.Blocks.procedures_ifreturn={init:function(){this.appendValueInput("CONDITION").setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_IFRETURN_HELPURL);this.hasReturnValue_=!0},mutationToDom:function(){var a=
Blockly.utils.xml.createElement("mutation");a.setAttribute("value",Number(this.hasReturnValue_));return a},domToMutation:function(a){this.hasReturnValue_=1==a.getAttribute("value");this.hasReturnValue_||(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN))},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()){a=!1;var b=this;do{if(-1!=this.FUNCTION_TYPES.indexOf(b.type)){a=!0;break}b=b.getSurroundParent()}while(b);
a?("procedures_defnoreturn"==b.type&&this.hasReturnValue_?(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!1):"procedures_defreturn"!=b.type||this.hasReturnValue_||(this.removeInput("VALUE"),this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!0),this.setWarningText(null),this.isInFlyout||this.setEnabled(!0)):(this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING),
this.isInFlyout||this.getInheritedDisabled()||this.setEnabled(!1))}},FUNCTION_TYPES:["procedures_defnoreturn","procedures_defreturn"]};Blockly.Blocks.texts={};Blockly.Constants.Text={};Blockly.Constants.Text.HUE=160;
Blockly.defineBlocksWithJsonArray([{type:"text",message0:"%1",args0:[{type:"field_input",name:"TEXT",text:""}],output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_TEXT_HELPURL}",tooltip:"%{BKY_TEXT_TEXT_TOOLTIP}",extensions:["text_quotes","parent_tooltip_when_inline"]},{type:"text_multiline",message0:"%1 %2",args0:[{type:"field_image",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpPU2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjmRIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z8873zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5lujnYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiDMYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYXfwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4zwA5X2Z9AYnQrEAAAAASUVORK5CYII=",width:12,
height:17,alt:"\u00b6"},{type:"field_multilinetext",name:"TEXT",text:""}],output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_TEXT_HELPURL}",tooltip:"%{BKY_TEXT_TEXT_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"text_join",message0:"",output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_JOIN_HELPURL}",tooltip:"%{BKY_TEXT_JOIN_TOOLTIP}",mutator:"text_join_mutator"},{type:"text_create_join_container",message0:"%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2",args0:[{type:"input_dummy"},
{type:"input_statement",name:"STACK"}],style:"text_blocks",tooltip:"%{BKY_TEXT_CREATE_JOIN_TOOLTIP}",enableContextMenu:!1},{type:"text_create_join_item",message0:"%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}",previousStatement:null,nextStatement:null,style:"text_blocks",tooltip:"%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}",enableContextMenu:!1},{type:"text_append",message0:"%{BKY_TEXT_APPEND_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_TEXT_APPEND_VARIABLE}"},{type:"input_value",name:"TEXT"}],
previousStatement:null,nextStatement:null,style:"text_blocks",extensions:["text_append_tooltip"]},{type:"text_length",message0:"%{BKY_TEXT_LENGTH_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",style:"text_blocks",tooltip:"%{BKY_TEXT_LENGTH_TOOLTIP}",helpUrl:"%{BKY_TEXT_LENGTH_HELPURL}"},{type:"text_isEmpty",message0:"%{BKY_TEXT_ISEMPTY_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",style:"text_blocks",tooltip:"%{BKY_TEXT_ISEMPTY_TOOLTIP}",
helpUrl:"%{BKY_TEXT_ISEMPTY_HELPURL}"},{type:"text_indexOf",message0:"%{BKY_TEXT_INDEXOF_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"String"},{type:"field_dropdown",name:"END",options:[["%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}","FIRST"],["%{BKY_TEXT_INDEXOF_OPERATOR_LAST}","LAST"]]},{type:"input_value",name:"FIND",check:"String"}],output:"Number",style:"text_blocks",helpUrl:"%{BKY_TEXT_INDEXOF_HELPURL}",inputsInline:!0,extensions:["text_indexOf_tooltip"]},{type:"text_charAt",message0:"%{BKY_TEXT_CHARAT_TITLE}",
args0:[{type:"input_value",name:"VALUE",check:"String"},{type:"field_dropdown",name:"WHERE",options:[["%{BKY_TEXT_CHARAT_FROM_START}","FROM_START"],["%{BKY_TEXT_CHARAT_FROM_END}","FROM_END"],["%{BKY_TEXT_CHARAT_FIRST}","FIRST"],["%{BKY_TEXT_CHARAT_LAST}","LAST"],["%{BKY_TEXT_CHARAT_RANDOM}","RANDOM"]]}],output:"String",style:"text_blocks",helpUrl:"%{BKY_TEXT_CHARAT_HELPURL}",inputsInline:!0,mutator:"text_charAt_mutator"}]);
Blockly.Blocks.text_getSubstring={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.TEXT_GET_SUBSTRING_HELPURL);this.setStyle("text_blocks");
this.appendValueInput("STRING").setCheck("String").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT);this.appendDummyInput("AT1");this.appendDummyInput("AT2");Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL);this.setInputsInline(!0);this.setOutput(!0,"String");this.updateAt_(1,!0);this.updateAt_(2,!0);this.setTooltip(Blockly.Msg.TEXT_GET_SUBSTRING_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),
b=this.getInput("AT1").type==Blockly.INPUT_VALUE;a.setAttribute("at1",b);b=this.getInput("AT2").type==Blockly.INPUT_VALUE;a.setAttribute("at2",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("at1");a="true"==a.getAttribute("at2");this.updateAt_(1,b);this.updateAt_(2,a)},updateAt_:function(a,b){this.removeInput("AT"+a);this.removeInput("ORDINAL"+a,!0);b?(this.appendValueInput("AT"+a).setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL"+a).appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
this.appendDummyInput("AT"+a);2==a&&Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL));var c=new Blockly.FieldDropdown(this["WHERE_OPTIONS_"+a],function(c){var e="FROM_START"==c||"FROM_END"==c;if(e!=b){var d=this.getSourceBlock();d.updateAt_(a,e);d.setFieldValue(c,"WHERE"+a);return null}});this.getInput("AT"+a).appendField(c,"WHERE"+a);1==a&&(this.moveInputBefore("AT1","AT2"),this.getInput("ORDINAL1")&&
this.moveInputBefore("ORDINAL1","AT2"))}};Blockly.Blocks.text_changeCase={init:function(){var a=[[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_UPPERCASE,"UPPERCASE"],[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_LOWERCASE,"LOWERCASE"],[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_TITLECASE,"TITLECASE"]];this.setHelpUrl(Blockly.Msg.TEXT_CHANGECASE_HELPURL);this.setStyle("text_blocks");this.appendValueInput("TEXT").setCheck("String").appendField(new Blockly.FieldDropdown(a),"CASE");this.setOutput(!0,"String");this.setTooltip(Blockly.Msg.TEXT_CHANGECASE_TOOLTIP)}};
Blockly.Blocks.text_trim={init:function(){var a=[[Blockly.Msg.TEXT_TRIM_OPERATOR_BOTH,"BOTH"],[Blockly.Msg.TEXT_TRIM_OPERATOR_LEFT,"LEFT"],[Blockly.Msg.TEXT_TRIM_OPERATOR_RIGHT,"RIGHT"]];this.setHelpUrl(Blockly.Msg.TEXT_TRIM_HELPURL);this.setStyle("text_blocks");this.appendValueInput("TEXT").setCheck("String").appendField(new Blockly.FieldDropdown(a),"MODE");this.setOutput(!0,"String");this.setTooltip(Blockly.Msg.TEXT_TRIM_TOOLTIP)}};
Blockly.Blocks.text_print={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_PRINT_TITLE,args0:[{type:"input_value",name:"TEXT"}],previousStatement:null,nextStatement:null,style:"text_blocks",tooltip:Blockly.Msg.TEXT_PRINT_TOOLTIP,helpUrl:Blockly.Msg.TEXT_PRINT_HELPURL})}};
Blockly.Blocks.text_prompt_ext={init:function(){var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]];this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setStyle("text_blocks");var b=this;a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendValueInput("TEXT").appendField(a,"TYPE");this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},
updateType_:function(a){this.outputConnection.setCheck("NUMBER"==a?"Number":"String")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("type",this.getFieldValue("TYPE"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("type"))}};
Blockly.Blocks.text_prompt={init:function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]],b=this;this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setStyle("text_blocks");a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendDummyInput().appendField(a,"TYPE").appendField(this.newQuote_(!0)).appendField(new Blockly.FieldTextInput(""),"TEXT").appendField(this.newQuote_(!1));
this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},updateType_:Blockly.Blocks.text_prompt_ext.updateType_,mutationToDom:Blockly.Blocks.text_prompt_ext.mutationToDom,domToMutation:Blockly.Blocks.text_prompt_ext.domToMutation};
Blockly.Blocks.text_count={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_COUNT_MESSAGE0,args0:[{type:"input_value",name:"SUB",check:"String"},{type:"input_value",name:"TEXT",check:"String"}],output:"Number",inputsInline:!0,style:"text_blocks",tooltip:Blockly.Msg.TEXT_COUNT_TOOLTIP,helpUrl:Blockly.Msg.TEXT_COUNT_HELPURL})}};
Blockly.Blocks.text_replace={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_REPLACE_MESSAGE0,args0:[{type:"input_value",name:"FROM",check:"String"},{type:"input_value",name:"TO",check:"String"},{type:"input_value",name:"TEXT",check:"String"}],output:"String",inputsInline:!0,style:"text_blocks",tooltip:Blockly.Msg.TEXT_REPLACE_TOOLTIP,helpUrl:Blockly.Msg.TEXT_REPLACE_HELPURL})}};
Blockly.Blocks.text_reverse={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_REVERSE_MESSAGE0,args0:[{type:"input_value",name:"TEXT",check:"String"}],output:"String",inputsInline:!0,style:"text_blocks",tooltip:Blockly.Msg.TEXT_REVERSE_TOOLTIP,helpUrl:Blockly.Msg.TEXT_REVERSE_HELPURL})}};
Blockly.Constants.Text.QUOTE_IMAGE_MIXIN={QUOTE_IMAGE_LEFT_DATAURI:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAAn0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMfz9AylsaRRgGzvZAAAAAElFTkSuQmCC",QUOTE_IMAGE_RIGHT_DATAURI:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAAqUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhggONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvBO3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5AoslLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==",
QUOTE_IMAGE_WIDTH:12,QUOTE_IMAGE_HEIGHT:12,quoteField_:function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)if(a==e.name){c.insertFieldAt(d,this.newQuote_(!0));c.insertFieldAt(d+2,this.newQuote_(!1));return}console.warn('field named "'+a+'" not found in '+this.toDevString())},newQuote_:function(a){a=this.RTL?!a:a;return new Blockly.FieldImage(a?this.QUOTE_IMAGE_LEFT_DATAURI:this.QUOTE_IMAGE_RIGHT_DATAURI,this.QUOTE_IMAGE_WIDTH,this.QUOTE_IMAGE_HEIGHT,a?"\u201c":"\u201d")}};
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.quoteField_("TEXT")};
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"),10);this.updateShape_()},decompose:function(a){var b=a.newBlock("text_create_join_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d<this.itemCount_;d++){var e=a.newBlock("text_create_join_item");e.initSvg();c.connect(e.previousConnection);
c=e.nextConnection}return b},compose:function(a){var b=a.getInputTargetBlock("STACK");for(a=[];b;)a.push(b.valueConnection_),b=b.nextConnection&&b.nextConnection.targetBlock();for(b=0;b<this.itemCount_;b++){var c=this.getInput("ADD"+b).connection.targetConnection;c&&-1==a.indexOf(c)&&c.disconnect()}this.itemCount_=a.length;this.updateShape_();for(b=0;b<this.itemCount_;b++)Blockly.Mutator.reconnect(a[b],this,"ADD"+b)},saveConnections:function(a){a=a.getInputTargetBlock("STACK");for(var b=0;a;){var c=
this.getInput("ADD"+b);a.valueConnection_=c&&c.connection.targetConnection;b++;a=a.nextConnection&&a.nextConnection.targetBlock()}},updateShape_:function(){this.itemCount_&&this.getInput("EMPTY")?this.removeInput("EMPTY"):this.itemCount_||this.getInput("EMPTY")||this.appendDummyInput("EMPTY").appendField(this.newQuote_(!0)).appendField(this.newQuote_(!1));for(var a=0;a<this.itemCount_;a++)if(!this.getInput("ADD"+a)){var b=this.appendValueInput("ADD"+a).setAlign(Blockly.ALIGN_RIGHT);0==a&&b.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH)}for(;this.getInput("ADD"+
a);)this.removeInput("ADD"+a),a++}};Blockly.Constants.Text.TEXT_JOIN_EXTENSION=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.itemCount_=2;this.updateShape_();this.setMutator(new Blockly.Mutator(["text_create_join_item"]))};Blockly.Extensions.register("text_append_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_TEXT_APPEND_TOOLTIP}","VAR"));
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION=function(){var a=this;this.setTooltip(function(){return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace("%1",a.workspace.options.oneBasedIndex?"0":"-1")})};
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("at",!!this.isAt_);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT",!0);this.removeInput("ORDINAL",!0);a&&(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX));Blockly.Msg.TEXT_CHARAT_TAIL&&
(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_CHARAT_TAIL));this.isAt_=a}};
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION=function(){this.getField("WHERE").setValidator(function(a){a="FROM_START"==a||"FROM_END"==a;a!=this.isAt_&&this.getSourceBlock().updateAt_(a)});this.updateAt_(!0);var a=this;this.setTooltip(function(){var b=a.getFieldValue("WHERE"),c=Blockly.Msg.TEXT_CHARAT_TOOLTIP;("FROM_START"==b||"FROM_END"==b)&&(b="FROM_START"==b?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP)&&(c+=" "+b.replace("%1",a.workspace.options.oneBasedIndex?
"#1":"#0"));return c})};Blockly.Extensions.register("text_indexOf_tooltip",Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION);Blockly.Extensions.register("text_quotes",Blockly.Constants.Text.TEXT_QUOTES_EXTENSION);Blockly.Extensions.registerMutator("text_join_mutator",Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN,Blockly.Constants.Text.TEXT_JOIN_EXTENSION);Blockly.Extensions.registerMutator("text_charAt_mutator",Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN,Blockly.Constants.Text.TEXT_CHARAT_EXTENSION);Blockly.Blocks.variables={};Blockly.Constants.Variables={};Blockly.Constants.Variables.HUE=330;
Blockly.defineBlocksWithJsonArray([{type:"variables_get",message0:"%1",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"}],output:null,style:"variable_blocks",helpUrl:"%{BKY_VARIABLES_GET_HELPURL}",tooltip:"%{BKY_VARIABLES_GET_TOOLTIP}",extensions:["contextMenu_variableSetterGetter"]},{type:"variables_set",message0:"%{BKY_VARIABLES_SET}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"},{type:"input_value",name:"VALUE"}],previousStatement:null,
nextStatement:null,style:"variable_blocks",tooltip:"%{BKY_VARIABLES_SET_TOOLTIP}",helpUrl:"%{BKY_VARIABLES_SET_HELPURL}",extensions:["contextMenu_variableSetterGetter"]}]);
Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){if("variables_get"==this.type)var b="variables_set",c=Blockly.Msg.VARIABLES_GET_CREATE_SET;else b="variables_get",c=Blockly.Msg.VARIABLES_SET_CREATE_GET;var d={enabled:0<this.workspace.remainingCapacity()},e=this.getField("VAR").getText();d.text=c.replace("%1",e);c=Blockly.utils.xml.createElement("field");c.setAttribute("name","VAR");c.appendChild(Blockly.utils.xml.createTextNode(e));
e=Blockly.utils.xml.createElement("block");e.setAttribute("type",b);e.appendChild(c);d.callback=Blockly.ContextMenu.callbackFactory(this,e);a.push(d)}else if("variables_get"==this.type||"variables_get_reporter"==this.type)b={text:Blockly.Msg.RENAME_VARIABLE,enabled:!0,callback:Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)},e=this.getField("VAR").getText(),d={text:Blockly.Msg.DELETE_VARIABLE.replace("%1",e),enabled:!0,callback:Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)},
a.unshift(b),a.unshift(d)}};Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();Blockly.Variables.renameVariable(b,c)}};Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();b.deleteVariableById(c.getId());b.refreshToolboxSelection()}};Blockly.Extensions.registerMixin("contextMenu_variableSetterGetter",Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);Blockly.Constants.VariablesDynamic={};Blockly.Constants.VariablesDynamic.HUE=310;
Blockly.defineBlocksWithJsonArray([{type:"variables_get_dynamic",message0:"%1",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"}],output:null,style:"variable_dynamic_blocks",helpUrl:"%{BKY_VARIABLES_GET_HELPURL}",tooltip:"%{BKY_VARIABLES_GET_TOOLTIP}",extensions:["contextMenu_variableDynamicSetterGetter"]},{type:"variables_set_dynamic",message0:"%{BKY_VARIABLES_SET}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_VARIABLES_DEFAULT_NAME}"},{type:"input_value",
name:"VALUE"}],previousStatement:null,nextStatement:null,style:"variable_dynamic_blocks",tooltip:"%{BKY_VARIABLES_SET_TOOLTIP}",helpUrl:"%{BKY_VARIABLES_SET_HELPURL}",extensions:["contextMenu_variableDynamicSetterGetter"]}]);
Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getFieldValue("VAR");var c=this.workspace.getVariableById(b).type;if("variables_get_dynamic"==this.type){b="variables_set_dynamic";var d=Blockly.Msg.VARIABLES_GET_CREATE_SET}else b="variables_get_dynamic",d=Blockly.Msg.VARIABLES_SET_CREATE_GET;var e={enabled:0<this.workspace.remainingCapacity()},f=this.getField("VAR").getText();e.text=d.replace("%1",f);
d=Blockly.utils.xml.createElement("field");d.setAttribute("name","VAR");d.setAttribute("variabletype",c);d.appendChild(Blockly.utils.xml.createTextNode(f));f=Blockly.utils.xml.createElement("block");f.setAttribute("type",b);f.appendChild(d);e.callback=Blockly.ContextMenu.callbackFactory(this,f);a.push(e)}else if("variables_get_dynamic"==this.type||"variables_get_reporter_dynamic"==this.type)b={text:Blockly.Msg.RENAME_VARIABLE,enabled:!0,callback:Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)},
f=this.getField("VAR").getText(),e={text:Blockly.Msg.DELETE_VARIABLE.replace("%1",f),enabled:!0,callback:Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)},a.unshift(b),a.unshift(e)},onchange:function(a){a=this.getFieldValue("VAR");a=Blockly.Variables.getVariable(this.workspace,a);"variables_get_dynamic"==this.type?this.outputConnection.setCheck(a.type):this.getInput("VALUE").connection.setCheck(a.type)}};
Blockly.Constants.VariablesDynamic.RENAME_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();Blockly.Variables.renameVariable(b,c)}};Blockly.Constants.VariablesDynamic.DELETE_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();b.deleteVariableById(c.getId());b.refreshToolboxSelection()}};Blockly.Extensions.registerMixin("contextMenu_variableDynamicSetterGetter",Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);

View File

@ -0,0 +1,102 @@
// Do not edit this file; automatically generated by gulp.
'use strict';
Blockly.JavaScript=new Blockly.Generator("JavaScript");Blockly.JavaScript.addReservedWords("break,case,catch,class,const,continue,debugger,default,delete,do,else,export,extends,finally,for,function,if,import,in,instanceof,new,return,super,switch,this,throw,try,typeof,var,void,while,with,yield,enum,implements,interface,let,package,private,protected,public,static,await,null,true,false,arguments,"+Object.getOwnPropertyNames(Blockly.utils.global).join(","));
Blockly.JavaScript.ORDER_ATOMIC=0;Blockly.JavaScript.ORDER_NEW=1.1;Blockly.JavaScript.ORDER_MEMBER=1.2;Blockly.JavaScript.ORDER_FUNCTION_CALL=2;Blockly.JavaScript.ORDER_INCREMENT=3;Blockly.JavaScript.ORDER_DECREMENT=3;Blockly.JavaScript.ORDER_BITWISE_NOT=4.1;Blockly.JavaScript.ORDER_UNARY_PLUS=4.2;Blockly.JavaScript.ORDER_UNARY_NEGATION=4.3;Blockly.JavaScript.ORDER_LOGICAL_NOT=4.4;Blockly.JavaScript.ORDER_TYPEOF=4.5;Blockly.JavaScript.ORDER_VOID=4.6;Blockly.JavaScript.ORDER_DELETE=4.7;
Blockly.JavaScript.ORDER_AWAIT=4.8;Blockly.JavaScript.ORDER_EXPONENTIATION=5;Blockly.JavaScript.ORDER_MULTIPLICATION=5.1;Blockly.JavaScript.ORDER_DIVISION=5.2;Blockly.JavaScript.ORDER_MODULUS=5.3;Blockly.JavaScript.ORDER_SUBTRACTION=6.1;Blockly.JavaScript.ORDER_ADDITION=6.2;Blockly.JavaScript.ORDER_BITWISE_SHIFT=7;Blockly.JavaScript.ORDER_RELATIONAL=8;Blockly.JavaScript.ORDER_IN=8;Blockly.JavaScript.ORDER_INSTANCEOF=8;Blockly.JavaScript.ORDER_EQUALITY=9;Blockly.JavaScript.ORDER_BITWISE_AND=10;
Blockly.JavaScript.ORDER_BITWISE_XOR=11;Blockly.JavaScript.ORDER_BITWISE_OR=12;Blockly.JavaScript.ORDER_LOGICAL_AND=13;Blockly.JavaScript.ORDER_LOGICAL_OR=14;Blockly.JavaScript.ORDER_CONDITIONAL=15;Blockly.JavaScript.ORDER_ASSIGNMENT=16;Blockly.JavaScript.ORDER_YIELD=17;Blockly.JavaScript.ORDER_COMMA=18;Blockly.JavaScript.ORDER_NONE=99;
Blockly.JavaScript.ORDER_OVERRIDES=[[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_LOGICAL_NOT,Blockly.JavaScript.ORDER_LOGICAL_NOT],[Blockly.JavaScript.ORDER_MULTIPLICATION,Blockly.JavaScript.ORDER_MULTIPLICATION],[Blockly.JavaScript.ORDER_ADDITION,
Blockly.JavaScript.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR,Blockly.JavaScript.ORDER_LOGICAL_OR]];
Blockly.JavaScript.init=function(a){Blockly.JavaScript.definitions_=Object.create(null);Blockly.JavaScript.functionNames_=Object.create(null);Blockly.JavaScript.variableDB_?Blockly.JavaScript.variableDB_.reset():Blockly.JavaScript.variableDB_=new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);Blockly.JavaScript.variableDB_.setVariableMap(a.getVariableMap());for(var b=[],c=Blockly.Variables.allDeveloperVariables(a),d=0;d<c.length;d++)b.push(Blockly.JavaScript.variableDB_.getName(c[d],Blockly.Names.DEVELOPER_VARIABLE_TYPE));
a=Blockly.Variables.allUsedVarModels(a);for(d=0;d<a.length;d++)b.push(Blockly.JavaScript.variableDB_.getName(a[d].getId(),Blockly.VARIABLE_CATEGORY_NAME));b.length&&(Blockly.JavaScript.definitions_.variables="var "+b.join(", ")+";")};
Blockly.JavaScript.finish=function(a){var b=[],c;for(c in Blockly.JavaScript.definitions_)b.push(Blockly.JavaScript.definitions_[c]);delete Blockly.JavaScript.definitions_;delete Blockly.JavaScript.functionNames_;Blockly.JavaScript.variableDB_.reset();return b.join("\n\n")+"\n\n\n"+a};Blockly.JavaScript.scrubNakedValue=function(a){return a+";\n"};Blockly.JavaScript.quote_=function(a){a=a.replace(/\\/g,"\\\\").replace(/\n/g,"\\\n").replace(/'/g,"\\'");return"'"+a+"'"};
Blockly.JavaScript.multiline_quote_=function(a){return a.split(/\n/g).map(Blockly.JavaScript.quote_).join(" + '\\n' +\n")};
Blockly.JavaScript.scrub_=function(a,b,c){var d="";if(!a.outputConnection||!a.outputConnection.targetConnection){var e=a.getCommentText();e&&(e=Blockly.utils.string.wrap(e,Blockly.JavaScript.COMMENT_WRAP-3),d+=Blockly.JavaScript.prefixLines(e+"\n","// "));for(var f=0;f<a.inputList.length;f++)a.inputList[f].type==Blockly.INPUT_VALUE&&(e=a.inputList[f].connection.targetBlock())&&(e=Blockly.JavaScript.allNestedComments(e))&&(d+=Blockly.JavaScript.prefixLines(e,"// "))}a=a.nextConnection&&a.nextConnection.targetBlock();
c=c?"":Blockly.JavaScript.blockToCode(a);return d+b+c};
Blockly.JavaScript.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.JavaScript.ORDER_NONE;a.workspace.options.oneBasedIndex&&c--;var f=a.workspace.options.oneBasedIndex?"1":"0";a=0<c?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_ADDITION)||f:0>c?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_SUBTRACTION)||f:d?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_UNARY_NEGATION)||f:Blockly.JavaScript.valueToCode(a,b,e)||f;if(Blockly.isNumber(a))a=Number(a)+c,d&&
(a=-a);else{if(0<c){a=a+" + "+c;var g=Blockly.JavaScript.ORDER_ADDITION}else 0>c&&(a=a+" - "+-c,g=Blockly.JavaScript.ORDER_SUBTRACTION);d&&(a=c?"-("+a+")":"-"+a,g=Blockly.JavaScript.ORDER_UNARY_NEGATION);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.JavaScript.colour={};Blockly.JavaScript.colour_picker=function(a){return[Blockly.JavaScript.quote_(a.getFieldValue("COLOUR")),Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.colour_random=function(a){return[Blockly.JavaScript.provideFunction_("colourRandom",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"() {"," var num = Math.floor(Math.random() * Math.pow(2, 24));"," return '#' + ('00000' + num.toString(16)).substr(-6);","}"])+"()",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.colour_rgb=function(a){var b=Blockly.JavaScript.valueToCode(a,"RED",Blockly.JavaScript.ORDER_COMMA)||0,c=Blockly.JavaScript.valueToCode(a,"GREEN",Blockly.JavaScript.ORDER_COMMA)||0;a=Blockly.JavaScript.valueToCode(a,"BLUE",Blockly.JavaScript.ORDER_COMMA)||0;return[Blockly.JavaScript.provideFunction_("colourRgb",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(r, g, b) {"," r = Math.max(Math.min(Number(r), 100), 0) * 2.55;"," g = Math.max(Math.min(Number(g), 100), 0) * 2.55;",
" b = Math.max(Math.min(Number(b), 100), 0) * 2.55;"," r = ('0' + (Math.round(r) || 0).toString(16)).slice(-2);"," g = ('0' + (Math.round(g) || 0).toString(16)).slice(-2);"," b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2);"," return '#' + r + g + b;","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.colour_blend=function(a){var b=Blockly.JavaScript.valueToCode(a,"COLOUR1",Blockly.JavaScript.ORDER_COMMA)||"'#000000'",c=Blockly.JavaScript.valueToCode(a,"COLOUR2",Blockly.JavaScript.ORDER_COMMA)||"'#000000'";a=Blockly.JavaScript.valueToCode(a,"RATIO",Blockly.JavaScript.ORDER_COMMA)||.5;return[Blockly.JavaScript.provideFunction_("colourBlend",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(c1, c2, ratio) {"," ratio = Math.max(Math.min(Number(ratio), 1), 0);"," var r1 = parseInt(c1.substring(1, 3), 16);",
" var g1 = parseInt(c1.substring(3, 5), 16);"," var b1 = parseInt(c1.substring(5, 7), 16);"," var r2 = parseInt(c2.substring(1, 3), 16);"," var g2 = parseInt(c2.substring(3, 5), 16);"," var b2 = parseInt(c2.substring(5, 7), 16);"," var r = Math.round(r1 * (1 - ratio) + r2 * ratio);"," var g = Math.round(g1 * (1 - ratio) + g2 * ratio);"," var b = Math.round(b1 * (1 - ratio) + b2 * ratio);"," r = ('0' + (r || 0).toString(16)).slice(-2);"," g = ('0' + (g || 0).toString(16)).slice(-2);"," b = ('0' + (b || 0).toString(16)).slice(-2);",
" return '#' + r + g + b;","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.lists={};Blockly.JavaScript.lists_create_empty=function(a){return["[]",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"null";return["["+b.join(", ")+"]",Blockly.JavaScript.ORDER_ATOMIC]};
Blockly.JavaScript.lists_repeat=function(a){var b=Blockly.JavaScript.provideFunction_("listsRepeat",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(value, n) {"," var array = [];"," for (var i = 0; i < n; i++) {"," array[i] = value;"," }"," return array;","}"]),c=Blockly.JavaScript.valueToCode(a,"ITEM",Blockly.JavaScript.ORDER_COMMA)||"null";a=Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_COMMA)||"0";return[b+"("+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_length=function(a){return[(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+".length",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.lists_isEmpty=function(a){return["!"+(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+".length",Blockly.JavaScript.ORDER_LOGICAL_NOT]};
Blockly.JavaScript.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''";b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_getIndex=function(a){var b=a.getFieldValue("MODE")||"GET",c=a.getFieldValue("WHERE")||"FROM_START",d=Blockly.JavaScript.valueToCode(a,"VALUE","RANDOM"==c?Blockly.JavaScript.ORDER_COMMA:Blockly.JavaScript.ORDER_MEMBER)||"[]";switch(c){case "FIRST":if("GET"==b)return[d+"[0]",Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".shift()",Blockly.JavaScript.ORDER_MEMBER];if("REMOVE"==b)return d+".shift();\n";break;case "LAST":if("GET"==b)return[d+".slice(-1)[0]",Blockly.JavaScript.ORDER_MEMBER];
if("GET_REMOVE"==b)return[d+".pop()",Blockly.JavaScript.ORDER_MEMBER];if("REMOVE"==b)return d+".pop();\n";break;case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT");if("GET"==b)return[d+"["+a+"]",Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".splice("+a+", 1)[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".splice("+a+", 1);\n";break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT",1,!0);if("GET"==b)return[d+".slice("+a+")[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];
if("GET_REMOVE"==b)return[d+".splice("+a+", 1)[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".splice("+a+", 1);";break;case "RANDOM":d=Blockly.JavaScript.provideFunction_("listsGetRandomItem",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(list, remove) {"," var x = Math.floor(Math.random() * list.length);"," if (remove) {"," return list.splice(x, 1)[0];"," } else {"," return list[x];"," }","}"])+"("+d+", "+("GET"!=b)+")";if("GET"==b||"GET_REMOVE"==b)return[d,
Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+";\n"}throw Error("Unhandled combination (lists_getIndex).");};
Blockly.JavaScript.lists_setIndex=function(a){function b(){if(c.match(/^\w+$/))return"";var a=Blockly.JavaScript.variableDB_.getDistinctName("tmpList",Blockly.VARIABLE_CATEGORY_NAME),b="var "+a+" = "+c+";\n";c=a;return b}var c=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_MEMBER)||"[]",d=a.getFieldValue("MODE")||"GET",e=a.getFieldValue("WHERE")||"FROM_START",f=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_ASSIGNMENT)||"null";switch(e){case "FIRST":if("SET"==d)return c+
"[0] = "+f+";\n";if("INSERT"==d)return c+".unshift("+f+");\n";break;case "LAST":if("SET"==d)return a=b(),a+(c+"["+c+".length - 1] = "+f+";\n");if("INSERT"==d)return c+".push("+f+");\n";break;case "FROM_START":e=Blockly.JavaScript.getAdjusted(a,"AT");if("SET"==d)return c+"["+e+"] = "+f+";\n";if("INSERT"==d)return c+".splice("+e+", 0, "+f+");\n";break;case "FROM_END":e=Blockly.JavaScript.getAdjusted(a,"AT",1,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b();if("SET"==d)return a+(c+"["+c+".length - "+e+
"] = "+f+";\n");if("INSERT"==d)return a+(c+".splice("+c+".length - "+e+", 0, "+f+");\n");break;case "RANDOM":a=b();e=Blockly.JavaScript.variableDB_.getDistinctName("tmpX",Blockly.VARIABLE_CATEGORY_NAME);a+="var "+e+" = Math.floor(Math.random() * "+c+".length);\n";if("SET"==d)return a+(c+"["+e+"] = "+f+";\n");if("INSERT"==d)return a+(c+".splice("+e+", 0, "+f+");\n")}throw Error("Unhandled combination (lists_setIndex).");};
Blockly.JavaScript.lists.getIndex_=function(a,b,c){return"FIRST"==b?"0":"FROM_END"==b?a+".length - 1 - "+c:"LAST"==b?a+".length - 1":c};
Blockly.JavaScript.lists_getSublist=function(a){var b=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_MEMBER)||"[]",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"==c&&"LAST"==d)b+=".slice(0)";else if(b.match(/^\w+$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var e=Blockly.JavaScript.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.JavaScript.getAdjusted(a,"AT1",1,!1,Blockly.JavaScript.ORDER_SUBTRACTION);e=b+".length - "+e;break;case "FIRST":e=
"0";break;default:throw Error("Unhandled option (lists_getSublist).");}switch(d){case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT2",1);break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT2",0,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b+".length - "+a;break;case "LAST":a=b+".length";break;default:throw Error("Unhandled option (lists_getSublist).");}b=b+".slice("+e+", "+a+")"}else{e=Blockly.JavaScript.getAdjusted(a,"AT1");a=Blockly.JavaScript.getAdjusted(a,"AT2");var f=Blockly.JavaScript.lists.getIndex_,
g={FIRST:"First",LAST:"Last",FROM_START:"FromStart",FROM_END:"FromEnd"};b=Blockly.JavaScript.provideFunction_("subsequence"+g[c]+g[d],["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(sequence"+("FROM_END"==c||"FROM_START"==c?", at1":"")+("FROM_END"==d||"FROM_START"==d?", at2":"")+") {"," var start = "+f("sequence",c,"at1")+";"," var end = "+f("sequence",d,"at2")+" + 1;"," return sequence.slice(start, end);","}"])+"("+b+("FROM_END"==c||"FROM_START"==c?", "+e:"")+("FROM_END"==d||"FROM_START"==
d?", "+a:"")+")"}return[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_sort=function(a){var b=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"[]",c="1"===a.getFieldValue("DIRECTION")?1:-1;a=a.getFieldValue("TYPE");var d=Blockly.JavaScript.provideFunction_("listsGetSortCompare",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(type, direction) {"," var compareFuncs = {",' "NUMERIC": function(a, b) {'," return Number(a) - Number(b); },",' "TEXT": function(a, b) {'," return a.toString() > b.toString() ? 1 : -1; },",
' "IGNORE_CASE": function(a, b) {'," return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; },"," };"," var compare = compareFuncs[type];"," return function(a, b) { return compare(a, b) * direction; }","}"]);return[b+".slice().sort("+d+'("'+a+'", '+c+"))",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_split=function(a){var b=Blockly.JavaScript.valueToCode(a,"INPUT",Blockly.JavaScript.ORDER_MEMBER),c=Blockly.JavaScript.valueToCode(a,"DELIM",Blockly.JavaScript.ORDER_NONE)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="split";else if("JOIN"==a)b||(b="[]"),a="join";else throw Error("Unknown mode: "+a);return[b+"."+a+"("+c+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_reverse=function(a){return[(Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"[]")+".slice().reverse()",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.logic={};
Blockly.JavaScript.controls_if=function(a){var b=0,c="";Blockly.JavaScript.STATEMENT_PREFIX&&(c+=Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_PREFIX,a));do{var d=Blockly.JavaScript.valueToCode(a,"IF"+b,Blockly.JavaScript.ORDER_NONE)||"false";var e=Blockly.JavaScript.statementToCode(a,"DO"+b);Blockly.JavaScript.STATEMENT_SUFFIX&&(e=Blockly.JavaScript.prefixLines(Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX,a),Blockly.JavaScript.INDENT)+e);c+=(0<b?" else ":"")+"if ("+
d+") {\n"+e+"}";++b}while(a.getInput("IF"+b));if(a.getInput("ELSE")||Blockly.JavaScript.STATEMENT_SUFFIX)e=Blockly.JavaScript.statementToCode(a,"ELSE"),Blockly.JavaScript.STATEMENT_SUFFIX&&(e=Blockly.JavaScript.prefixLines(Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX,a),Blockly.JavaScript.INDENT)+e),c+=" else {\n"+e+"}";return c+"\n"};Blockly.JavaScript.controls_ifelse=Blockly.JavaScript.controls_if;
Blockly.JavaScript.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.JavaScript.ORDER_EQUALITY:Blockly.JavaScript.ORDER_RELATIONAL,d=Blockly.JavaScript.valueToCode(a,"A",c)||"0";a=Blockly.JavaScript.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]};
Blockly.JavaScript.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"&&":"||",c="&&"==b?Blockly.JavaScript.ORDER_LOGICAL_AND:Blockly.JavaScript.ORDER_LOGICAL_OR,d=Blockly.JavaScript.valueToCode(a,"A",c);a=Blockly.JavaScript.valueToCode(a,"B",c);if(d||a){var e="&&"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};
Blockly.JavaScript.logic_negate=function(a){var b=Blockly.JavaScript.ORDER_LOGICAL_NOT;return["!"+(Blockly.JavaScript.valueToCode(a,"BOOL",b)||"true"),b]};Blockly.JavaScript.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"true":"false",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.logic_null=function(a){return["null",Blockly.JavaScript.ORDER_ATOMIC]};
Blockly.JavaScript.logic_ternary=function(a){var b=Blockly.JavaScript.valueToCode(a,"IF",Blockly.JavaScript.ORDER_CONDITIONAL)||"false",c=Blockly.JavaScript.valueToCode(a,"THEN",Blockly.JavaScript.ORDER_CONDITIONAL)||"null";a=Blockly.JavaScript.valueToCode(a,"ELSE",Blockly.JavaScript.ORDER_CONDITIONAL)||"null";return[b+" ? "+c+" : "+a,Blockly.JavaScript.ORDER_CONDITIONAL]};Blockly.JavaScript.loops={};
Blockly.JavaScript.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(Number(a.getFieldValue("TIMES"))):Blockly.JavaScript.valueToCode(a,"TIMES",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0",c=Blockly.JavaScript.statementToCode(a,"DO");c=Blockly.JavaScript.addLoopTrap(c,a);a="";var d=Blockly.JavaScript.variableDB_.getDistinctName("count",Blockly.VARIABLE_CATEGORY_NAME),e=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(e=Blockly.JavaScript.variableDB_.getDistinctName("repeat_end",Blockly.VARIABLE_CATEGORY_NAME),
a+="var "+e+" = "+b+";\n");return a+("for (var "+d+" = 0; "+d+" < "+e+"; "+d+"++) {\n"+c+"}\n")};Blockly.JavaScript.controls_repeat=Blockly.JavaScript.controls_repeat_ext;
Blockly.JavaScript.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.JavaScript.valueToCode(a,"BOOL",b?Blockly.JavaScript.ORDER_LOGICAL_NOT:Blockly.JavaScript.ORDER_NONE)||"false",d=Blockly.JavaScript.statementToCode(a,"DO");d=Blockly.JavaScript.addLoopTrap(d,a);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"};
Blockly.JavaScript.controls_for=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),c=Blockly.JavaScript.valueToCode(a,"FROM",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0",d=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0",e=Blockly.JavaScript.valueToCode(a,"BY",Blockly.JavaScript.ORDER_ASSIGNMENT)||"1",f=Blockly.JavaScript.statementToCode(a,"DO");f=Blockly.JavaScript.addLoopTrap(f,a);if(Blockly.isNumber(c)&&Blockly.isNumber(d)&&
Blockly.isNumber(e)){var g=Number(c)<=Number(d);a="for ("+b+" = "+c+"; "+b+(g?" <= ":" >= ")+d+"; "+b;b=Math.abs(Number(e));a=(1==b?a+(g?"++":"--"):a+((g?" += ":" -= ")+b))+(") {\n"+f+"}\n")}else a="",g=c,c.match(/^\w+$/)||Blockly.isNumber(c)||(g=Blockly.JavaScript.variableDB_.getDistinctName(b+"_start",Blockly.VARIABLE_CATEGORY_NAME),a+="var "+g+" = "+c+";\n"),c=d,d.match(/^\w+$/)||Blockly.isNumber(d)||(c=Blockly.JavaScript.variableDB_.getDistinctName(b+"_end",Blockly.VARIABLE_CATEGORY_NAME),a+=
"var "+c+" = "+d+";\n"),d=Blockly.JavaScript.variableDB_.getDistinctName(b+"_inc",Blockly.VARIABLE_CATEGORY_NAME),a+="var "+d+" = ",a=Blockly.isNumber(e)?a+(Math.abs(e)+";\n"):a+("Math.abs("+e+");\n"),a=a+("if ("+g+" > "+c+") {\n")+(Blockly.JavaScript.INDENT+d+" = -"+d+";\n"),a+="}\n",a+="for ("+b+" = "+g+"; "+d+" >= 0 ? "+b+" <= "+c+" : "+b+" >= "+c+"; "+b+" += "+d+") {\n"+f+"}\n";return a};
Blockly.JavaScript.controls_forEach=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),c=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_ASSIGNMENT)||"[]",d=Blockly.JavaScript.statementToCode(a,"DO");d=Blockly.JavaScript.addLoopTrap(d,a);a="";var e=c;c.match(/^\w+$/)||(e=Blockly.JavaScript.variableDB_.getDistinctName(b+"_list",Blockly.VARIABLE_CATEGORY_NAME),a+="var "+e+" = "+c+";\n");c=Blockly.JavaScript.variableDB_.getDistinctName(b+
"_index",Blockly.VARIABLE_CATEGORY_NAME);d=Blockly.JavaScript.INDENT+b+" = "+e+"["+c+"];\n"+d;return a+("for (var "+c+" in "+e+") {\n"+d+"}\n")};
Blockly.JavaScript.controls_flow_statements=function(a){var b="";Blockly.JavaScript.STATEMENT_PREFIX&&(b+=Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_PREFIX,a));Blockly.JavaScript.STATEMENT_SUFFIX&&(b+=Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX,a));if(Blockly.JavaScript.STATEMENT_PREFIX){var c=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(a);c&&!c.suppressPrefixSuffix&&(b+=Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_PREFIX,
c))}switch(a.getFieldValue("FLOW")){case "BREAK":return b+"break;\n";case "CONTINUE":return b+"continue;\n"}throw Error("Unknown flow statement.");};Blockly.JavaScript.math={};Blockly.JavaScript.math_number=function(a){a=Number(a.getFieldValue("NUM"));return[a,0<=a?Blockly.JavaScript.ORDER_ATOMIC:Blockly.JavaScript.ORDER_UNARY_NEGATION]};
Blockly.JavaScript.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.JavaScript.ORDER_ADDITION],MINUS:[" - ",Blockly.JavaScript.ORDER_SUBTRACTION],MULTIPLY:[" * ",Blockly.JavaScript.ORDER_MULTIPLICATION],DIVIDE:[" / ",Blockly.JavaScript.ORDER_DIVISION],POWER:[null,Blockly.JavaScript.ORDER_COMMA]}[a.getFieldValue("OP")],c=b[0];b=b[1];var d=Blockly.JavaScript.valueToCode(a,"A",b)||"0";a=Blockly.JavaScript.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:["Math.pow("+d+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.math_single=function(a){var b=a.getFieldValue("OP");if("NEG"==b)return a=Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_UNARY_NEGATION)||"0","-"==a[0]&&(a=" "+a),["-"+a,Blockly.JavaScript.ORDER_UNARY_NEGATION];a="SIN"==b||"COS"==b||"TAN"==b?Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_DIVISION)||"0":Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_NONE)||"0";switch(b){case "ABS":var c="Math.abs("+a+")";break;case "ROOT":c="Math.sqrt("+
a+")";break;case "LN":c="Math.log("+a+")";break;case "EXP":c="Math.exp("+a+")";break;case "POW10":c="Math.pow(10,"+a+")";break;case "ROUND":c="Math.round("+a+")";break;case "ROUNDUP":c="Math.ceil("+a+")";break;case "ROUNDDOWN":c="Math.floor("+a+")";break;case "SIN":c="Math.sin("+a+" / 180 * Math.PI)";break;case "COS":c="Math.cos("+a+" / 180 * Math.PI)";break;case "TAN":c="Math.tan("+a+" / 180 * Math.PI)"}if(c)return[c,Blockly.JavaScript.ORDER_FUNCTION_CALL];switch(b){case "LOG10":c="Math.log("+a+
") / Math.log(10)";break;case "ASIN":c="Math.asin("+a+") / Math.PI * 180";break;case "ACOS":c="Math.acos("+a+") / Math.PI * 180";break;case "ATAN":c="Math.atan("+a+") / Math.PI * 180";break;default:throw Error("Unknown math operator: "+b);}return[c,Blockly.JavaScript.ORDER_DIVISION]};
Blockly.JavaScript.math_constant=function(a){return{PI:["Math.PI",Blockly.JavaScript.ORDER_MEMBER],E:["Math.E",Blockly.JavaScript.ORDER_MEMBER],GOLDEN_RATIO:["(1 + Math.sqrt(5)) / 2",Blockly.JavaScript.ORDER_DIVISION],SQRT2:["Math.SQRT2",Blockly.JavaScript.ORDER_MEMBER],SQRT1_2:["Math.SQRT1_2",Blockly.JavaScript.ORDER_MEMBER],INFINITY:["Infinity",Blockly.JavaScript.ORDER_ATOMIC]}[a.getFieldValue("CONSTANT")]};
Blockly.JavaScript.math_number_property=function(a){var b=Blockly.JavaScript.valueToCode(a,"NUMBER_TO_CHECK",Blockly.JavaScript.ORDER_MODULUS)||"0",c=a.getFieldValue("PROPERTY");if("PRIME"==c)return[Blockly.JavaScript.provideFunction_("mathIsPrime",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(n) {"," // https://en.wikipedia.org/wiki/Primality_test#Naive_methods"," if (n == 2 || n == 3) {"," return true;"," }"," // False if n is NaN, negative, is 1, or not whole."," // And false if n is divisible by 2 or 3.",
" if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 || n % 3 == 0) {"," return false;"," }"," // Check all the numbers of form 6k +/- 1, up to sqrt(n)."," for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {"," if (n % (x - 1) == 0 || n % (x + 1) == 0) {"," return false;"," }"," }"," return true;","}"])+"("+b+")",Blockly.JavaScript.ORDER_FUNCTION_CALL];switch(c){case "EVEN":var d=b+" % 2 == 0";break;case "ODD":d=b+" % 2 == 1";break;case "WHOLE":d=b+" % 1 == 0";break;case "POSITIVE":d=
b+" > 0";break;case "NEGATIVE":d=b+" < 0";break;case "DIVISIBLE_BY":a=Blockly.JavaScript.valueToCode(a,"DIVISOR",Blockly.JavaScript.ORDER_MODULUS)||"0",d=b+" % "+a+" == 0"}return[d,Blockly.JavaScript.ORDER_EQUALITY]};
Blockly.JavaScript.math_change=function(a){var b=Blockly.JavaScript.valueToCode(a,"DELTA",Blockly.JavaScript.ORDER_ADDITION)||"0";a=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME);return a+" = (typeof "+a+" == 'number' ? "+a+" : 0) + "+b+";\n"};Blockly.JavaScript.math_round=Blockly.JavaScript.math_single;Blockly.JavaScript.math_trig=Blockly.JavaScript.math_single;
Blockly.JavaScript.math_on_list=function(a){var b=a.getFieldValue("OP");switch(b){case "SUM":a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_MEMBER)||"[]";a+=".reduce(function(x, y) {return x + y;})";break;case "MIN":a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_COMMA)||"[]";a="Math.min.apply(null, "+a+")";break;case "MAX":a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_COMMA)||"[]";a="Math.max.apply(null, "+a+")";break;case "AVERAGE":b=Blockly.JavaScript.provideFunction_("mathMean",
["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(myList) {"," return myList.reduce(function(x, y) {return x + y;}) / myList.length;","}"]);a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "MEDIAN":b=Blockly.JavaScript.provideFunction_("mathMedian",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(myList) {"," var localList = myList.filter(function (x) {return typeof x == 'number';});"," if (!localList.length) return null;",
" localList.sort(function(a, b) {return b - a;});"," if (localList.length % 2 == 0) {"," return (localList[localList.length / 2 - 1] + localList[localList.length / 2]) / 2;"," } else {"," return localList[(localList.length - 1) / 2];"," }","}"]);a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "MODE":b=Blockly.JavaScript.provideFunction_("mathModes",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(values) {"," var modes = [];",
" var counts = [];"," var maxCount = 0;"," for (var i = 0; i < values.length; i++) {"," var value = values[i];"," var found = false;"," var thisCount;"," for (var j = 0; j < counts.length; j++) {"," if (counts[j][0] === value) {"," thisCount = ++counts[j][1];"," found = true;"," break;"," }"," }"," if (!found) {"," counts.push([value, 1]);"," thisCount = 1;"," }"," maxCount = Math.max(thisCount, maxCount);"," }"," for (var j = 0; j < counts.length; j++) {",
" if (counts[j][1] == maxCount) {"," modes.push(counts[j][0]);"," }"," }"," return modes;","}"]);a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "STD_DEV":b=Blockly.JavaScript.provideFunction_("mathStandardDeviation",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(numbers) {"," var n = numbers.length;"," if (!n) return null;"," var mean = numbers.reduce(function(x, y) {return x + y;}) / n;"," var variance = 0;",
" for (var j = 0; j < n; j++) {"," variance += Math.pow(numbers[j] - mean, 2);"," }"," variance = variance / n;"," return Math.sqrt(variance);","}"]);a=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "RANDOM":b=Blockly.JavaScript.provideFunction_("mathRandomList",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(list) {"," var x = Math.floor(Math.random() * list.length);"," return list[x];","}"]);a=Blockly.JavaScript.valueToCode(a,
"LIST",Blockly.JavaScript.ORDER_NONE)||"[]";a=b+"("+a+")";break;default:throw Error("Unknown operator: "+b);}return[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.math_modulo=function(a){var b=Blockly.JavaScript.valueToCode(a,"DIVIDEND",Blockly.JavaScript.ORDER_MODULUS)||"0";a=Blockly.JavaScript.valueToCode(a,"DIVISOR",Blockly.JavaScript.ORDER_MODULUS)||"0";return[b+" % "+a,Blockly.JavaScript.ORDER_MODULUS]};
Blockly.JavaScript.math_constrain=function(a){var b=Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_COMMA)||"0",c=Blockly.JavaScript.valueToCode(a,"LOW",Blockly.JavaScript.ORDER_COMMA)||"0";a=Blockly.JavaScript.valueToCode(a,"HIGH",Blockly.JavaScript.ORDER_COMMA)||"Infinity";return["Math.min(Math.max("+b+", "+c+"), "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.math_random_int=function(a){var b=Blockly.JavaScript.valueToCode(a,"FROM",Blockly.JavaScript.ORDER_COMMA)||"0";a=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_COMMA)||"0";return[Blockly.JavaScript.provideFunction_("mathRandomInt",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(a, b) {"," if (a > b) {"," // Swap a and b to ensure a is smaller."," var c = a;"," a = b;"," b = c;"," }"," return Math.floor(Math.random() * (b - a + 1) + a);",
"}"])+"("+b+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.math_random_float=function(a){return["Math.random()",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.math_atan2=function(a){var b=Blockly.JavaScript.valueToCode(a,"X",Blockly.JavaScript.ORDER_COMMA)||"0";return["Math.atan2("+(Blockly.JavaScript.valueToCode(a,"Y",Blockly.JavaScript.ORDER_COMMA)||"0")+", "+b+") / Math.PI * 180",Blockly.JavaScript.ORDER_DIVISION]};Blockly.JavaScript.procedures={};
Blockly.JavaScript.procedures_defreturn=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("NAME"),Blockly.PROCEDURE_CATEGORY_NAME),c="";Blockly.JavaScript.STATEMENT_PREFIX&&(c+=Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_PREFIX,a));Blockly.JavaScript.STATEMENT_SUFFIX&&(c+=Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX,a));c&&(c=Blockly.JavaScript.prefixLines(c,Blockly.JavaScript.INDENT));var d="";Blockly.JavaScript.INFINITE_LOOP_TRAP&&(d=Blockly.JavaScript.prefixLines(Blockly.JavaScript.injectId(Blockly.JavaScript.INFINITE_LOOP_TRAP,
a),Blockly.JavaScript.INDENT));var e=Blockly.JavaScript.statementToCode(a,"STACK"),f=Blockly.JavaScript.valueToCode(a,"RETURN",Blockly.JavaScript.ORDER_NONE)||"",g="";e&&f&&(g=c);f&&(f=Blockly.JavaScript.INDENT+"return "+f+";\n");for(var k=[],h=0;h<a.arguments_.length;h++)k[h]=Blockly.JavaScript.variableDB_.getName(a.arguments_[h],Blockly.VARIABLE_CATEGORY_NAME);c="function "+b+"("+k.join(", ")+") {\n"+c+d+e+g+f+"}";c=Blockly.JavaScript.scrub_(a,c);Blockly.JavaScript.definitions_["%"+b]=c;return null};
Blockly.JavaScript.procedures_defnoreturn=Blockly.JavaScript.procedures_defreturn;Blockly.JavaScript.procedures_callreturn=function(a){for(var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("NAME"),Blockly.PROCEDURE_CATEGORY_NAME),c=[],d=0;d<a.arguments_.length;d++)c[d]=Blockly.JavaScript.valueToCode(a,"ARG"+d,Blockly.JavaScript.ORDER_COMMA)||"null";return[b+"("+c.join(", ")+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.procedures_callnoreturn=function(a){return Blockly.JavaScript.procedures_callreturn(a)[0]+";\n"};
Blockly.JavaScript.procedures_ifreturn=function(a){var b="if ("+(Blockly.JavaScript.valueToCode(a,"CONDITION",Blockly.JavaScript.ORDER_NONE)||"false")+") {\n";Blockly.JavaScript.STATEMENT_SUFFIX&&(b+=Blockly.JavaScript.prefixLines(Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX,a),Blockly.JavaScript.INDENT));a.hasReturnValue_?(a=Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_NONE)||"null",b+=Blockly.JavaScript.INDENT+"return "+a+";\n"):b+=Blockly.JavaScript.INDENT+
"return;\n";return b+"}\n"};Blockly.JavaScript.texts={};Blockly.JavaScript.text=function(a){return[Blockly.JavaScript.quote_(a.getFieldValue("TEXT")),Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.text_multiline=function(a){a=Blockly.JavaScript.multiline_quote_(a.getFieldValue("TEXT"));a.includes("\n")&&(a="("+a+")");return[a,Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.text.forceString_=function(a){return Blockly.JavaScript.text.forceString_.strRegExp.test(a)?a:"String("+a+")"};
Blockly.JavaScript.text.forceString_.strRegExp=/^\s*'([^']|\\')*'\s*$/;
Blockly.JavaScript.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.JavaScript.ORDER_ATOMIC];case 1:return a=Blockly.JavaScript.valueToCode(a,"ADD0",Blockly.JavaScript.ORDER_NONE)||"''",a=Blockly.JavaScript.text.forceString_(a),[a,Blockly.JavaScript.ORDER_FUNCTION_CALL];case 2:var b=Blockly.JavaScript.valueToCode(a,"ADD0",Blockly.JavaScript.ORDER_NONE)||"''";a=Blockly.JavaScript.valueToCode(a,"ADD1",Blockly.JavaScript.ORDER_NONE)||"''";a=Blockly.JavaScript.text.forceString_(b)+
" + "+Blockly.JavaScript.text.forceString_(a);return[a,Blockly.JavaScript.ORDER_ADDITION];default:b=Array(a.itemCount_);for(var c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"''";a="["+b.join(",")+"].join('')";return[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]}};
Blockly.JavaScript.text_append=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME);a=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''";return b+" += "+Blockly.JavaScript.text.forceString_(a)+";\n"};Blockly.JavaScript.text_length=function(a){return[(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"''")+".length",Blockly.JavaScript.ORDER_MEMBER]};
Blockly.JavaScript.text_isEmpty=function(a){return["!"+(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+".length",Blockly.JavaScript.ORDER_LOGICAL_NOT]};
Blockly.JavaScript.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''";b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.text_charAt=function(a){var b=a.getFieldValue("WHERE")||"FROM_START",c=Blockly.JavaScript.valueToCode(a,"VALUE","RANDOM"==b?Blockly.JavaScript.ORDER_NONE:Blockly.JavaScript.ORDER_MEMBER)||"''";switch(b){case "FIRST":return[c+".charAt(0)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "LAST":return[c+".slice(-1)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "FROM_START":return a=Blockly.JavaScript.getAdjusted(a,"AT"),[c+".charAt("+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "FROM_END":return a=
Blockly.JavaScript.getAdjusted(a,"AT",1,!0),[c+".slice("+a+").charAt(0)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "RANDOM":return[Blockly.JavaScript.provideFunction_("textRandomLetter",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(text) {"," var x = Math.floor(Math.random() * text.length);"," return text[x];","}"])+"("+c+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]}throw Error("Unhandled option (text_charAt).");};
Blockly.JavaScript.text.getIndex_=function(a,b,c){return"FIRST"==b?"0":"FROM_END"==b?a+".length - 1 - "+c:"LAST"==b?a+".length - 1":c};
Blockly.JavaScript.text_getSubstring=function(a){var b=Blockly.JavaScript.valueToCode(a,"STRING",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"''",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"!=c||"LAST"!=d)if(b.match(/^'?\w+'?$/)||"FROM_END"!=c&&"LAST"!=c&&"FROM_END"!=d&&"LAST"!=d){switch(c){case "FROM_START":var e=Blockly.JavaScript.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.JavaScript.getAdjusted(a,"AT1",1,!1,Blockly.JavaScript.ORDER_SUBTRACTION);e=b+".length - "+e;break;
case "FIRST":e="0";break;default:throw Error("Unhandled option (text_getSubstring).");}switch(d){case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT2",1);break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT2",0,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b+".length - "+a;break;case "LAST":a=b+".length";break;default:throw Error("Unhandled option (text_getSubstring).");}b=b+".slice("+e+", "+a+")"}else{e=Blockly.JavaScript.getAdjusted(a,"AT1");a=Blockly.JavaScript.getAdjusted(a,"AT2");var f=
Blockly.JavaScript.text.getIndex_,g={FIRST:"First",LAST:"Last",FROM_START:"FromStart",FROM_END:"FromEnd"};b=Blockly.JavaScript.provideFunction_("subsequence"+g[c]+g[d],["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(sequence"+("FROM_END"==c||"FROM_START"==c?", at1":"")+("FROM_END"==d||"FROM_START"==d?", at2":"")+") {"," var start = "+f("sequence",c,"at1")+";"," var end = "+f("sequence",d,"at2")+" + 1;"," return sequence.slice(start, end);","}"])+"("+b+("FROM_END"==c||"FROM_START"==
c?", "+e:"")+("FROM_END"==d||"FROM_START"==d?", "+a:"")+")"}return[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.text_changeCase=function(a){var b={UPPERCASE:".toUpperCase()",LOWERCASE:".toLowerCase()",TITLECASE:null}[a.getFieldValue("CASE")];a=Blockly.JavaScript.valueToCode(a,"TEXT",b?Blockly.JavaScript.ORDER_MEMBER:Blockly.JavaScript.ORDER_NONE)||"''";return[b?a+b:Blockly.JavaScript.provideFunction_("textToTitleCase",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(str) {"," return str.replace(/\\S+/g,"," function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();});",
"}"])+"("+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_trim=function(a){var b={LEFT:".replace(/^[\\s\\xa0]+/, '')",RIGHT:".replace(/[\\s\\xa0]+$/, '')",BOTH:".trim()"}[a.getFieldValue("MODE")];return[(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''")+b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_print=function(a){return"window.alert("+(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''")+");\n"};
Blockly.JavaScript.text_prompt_ext=function(a){var b="window.prompt("+(a.getField("TEXT")?Blockly.JavaScript.quote_(a.getFieldValue("TEXT")):Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''")+")";"NUMBER"==a.getFieldValue("TYPE")&&(b="Number("+b+")");return[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_prompt=Blockly.JavaScript.text_prompt_ext;
Blockly.JavaScript.text_count=function(a){var b=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''";a=Blockly.JavaScript.valueToCode(a,"SUB",Blockly.JavaScript.ORDER_NONE)||"''";return[Blockly.JavaScript.provideFunction_("textCount",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(haystack, needle) {"," if (needle.length === 0) {"," return haystack.length + 1;"," } else {"," return haystack.split(needle).length - 1;"," }","}"])+"("+b+", "+a+")",Blockly.JavaScript.ORDER_SUBTRACTION]};
Blockly.JavaScript.text_replace=function(a){var b=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''",c=Blockly.JavaScript.valueToCode(a,"FROM",Blockly.JavaScript.ORDER_NONE)||"''";a=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_NONE)||"''";return[Blockly.JavaScript.provideFunction_("textReplace",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(haystack, needle, replacement) {",' needle = needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g,"\\\\$1")',
' .replace(/\\x08/g,"\\\\x08");'," return haystack.replace(new RegExp(needle, 'g'), replacement);","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.text_reverse=function(a){return[(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''")+".split('').reverse().join('')",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.variables={};Blockly.JavaScript.variables_get=function(a){return[Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.variables_set=function(a){var b=Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0";return Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME)+" = "+b+";\n"};Blockly.JavaScript.variablesDynamic={};Blockly.JavaScript.variables_get_dynamic=Blockly.JavaScript.variables_get;Blockly.JavaScript.variables_set_dynamic=Blockly.JavaScript.variables_set;

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="12.71" height="8.79" viewBox="0 0 12.71 8.79"><title>dropdown-arrow</title><g opacity="0.1"><path d="M12.71,2.44A2.41,2.41,0,0,1,12,4.16L8.08,8.08a2.45,2.45,0,0,1-3.45,0L0.72,4.16A2.42,2.42,0,0,1,0,2.44,2.48,2.48,0,0,1,.71.71C1,0.47,1.43,0,6.36,0S11.75,0.46,12,.71A2.44,2.44,0,0,1,12.71,2.44Z" fill="#231f20"/></g><path d="M6.36,7.79a1.43,1.43,0,0,1-1-.42L1.42,3.45a1.44,1.44,0,0,1,0-2c0.56-.56,9.31-0.56,9.87,0a1.44,1.44,0,0,1,0,2L7.37,7.37A1.43,1.43,0,0,1,6.36,7.79Z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="96px" height="124px">
<style type="text/css">
#background {
fill: none;
}
.arrows {
fill: #000;
stroke: none;
}
.selected>.arrows {
fill: #fff;
}
.checkmark {
fill: #000;
font-family: sans-serif;
font-size: 10pt;
text-anchor: middle;
}
.trash {
fill: #888;
}
.zoom {
fill: none;
stroke: #888;
stroke-width: 2;
stroke-linecap: round;
}
.zoom>.center {
fill: #888;
stroke-width: 0;
}
</style>
<rect id="background" width="96" height="124" x="0" y="0" />
<g>
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
</g>
<g class="selected" transform="translate(0, 16)">
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
</g>
<text class="checkmark" x="55.5" y="28">&#10003;</text>
<g class="trash">
<path d="M 2,41 v 6 h 42 v -6 h -10.5 l -3,-3 h -15 l -3,3 z" />
<rect width="36" height="20" x="5" y="50" />
<rect width="36" height="42" x="5" y="50" rx="4" ry="4" />
</g>
<g class="zoom">
<circle r="11.5" cx="16" cy="108" />
<circle r="4.3" cx="16" cy="108" class="center" />
<path d="m 28,108 h3" />
<path d="m 1,108 h3" />
<path d="m 16,120 v3" />
<path d="m 16,93 v3" />
</g>
<g class="zoom">
<circle r="15" cx="48" cy="108" />
<path d="m 48,101.6 v12.8" />
<path d="m 41.6,108 h12.8" />
</g>
<g class="zoom">
<circle r="15" cx="80" cy="108" />
<path d="m 73.6,108 h12.8" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

434
_server/blockly/zh-hans.js Normal file
View File

@ -0,0 +1,434 @@
// This file was automatically generated. Do not modify.
'use strict';
Blockly.Msg["ADD_COMMENT"] = "添加注释";
Blockly.Msg["CANNOT_DELETE_VARIABLE_PROCEDURE"] = "不能删除变量“%1”因为它是函数“%2”定义的一部分";
Blockly.Msg["CHANGE_VALUE_TITLE"] = "更改值:";
Blockly.Msg["CLEAN_UP"] = "整理块";
Blockly.Msg["COLLAPSED_WARNINGS_WARNING"] = "已收起的信息块内包含警告。";
Blockly.Msg["COLLAPSE_ALL"] = "折叠块";
Blockly.Msg["COLLAPSE_BLOCK"] = "折叠块";
Blockly.Msg["COLOUR_BLEND_COLOUR1"] = "颜色1";
Blockly.Msg["COLOUR_BLEND_COLOUR2"] = "颜色2";
Blockly.Msg["COLOUR_BLEND_HELPURL"] = "https://meyerweb.com/eric/tools/color-blend/#:::rgbp"; // untranslated
Blockly.Msg["COLOUR_BLEND_RATIO"] = "比例";
Blockly.Msg["COLOUR_BLEND_TITLE"] = "混合";
Blockly.Msg["COLOUR_BLEND_TOOLTIP"] = "把两种颜色以一个给定的比例(0.0-1.0)进行混合。";
Blockly.Msg["COLOUR_PICKER_HELPURL"] = "https://zh.wikipedia.org/wiki/颜色";
Blockly.Msg["COLOUR_PICKER_TOOLTIP"] = "从调色板中选择一种颜色。";
Blockly.Msg["COLOUR_RANDOM_HELPURL"] = "http://randomcolour.com"; // untranslated
Blockly.Msg["COLOUR_RANDOM_TITLE"] = "随机颜色";
Blockly.Msg["COLOUR_RANDOM_TOOLTIP"] = "随机选择一种颜色。";
Blockly.Msg["COLOUR_RGB_BLUE"] = "蓝色";
Blockly.Msg["COLOUR_RGB_GREEN"] = "绿色";
Blockly.Msg["COLOUR_RGB_HELPURL"] = "https://www.december.com/html/spec/colorpercompact.html"; // untranslated
Blockly.Msg["COLOUR_RGB_RED"] = "红色";
Blockly.Msg["COLOUR_RGB_TITLE"] = "颜色";
Blockly.Msg["COLOUR_RGB_TOOLTIP"] = "通过指定红色、绿色和蓝色的量创建一种颜色。所有的值必须在0和100之间。";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#loop-termination-blocks"; // untranslated
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK"] = "跳出循环";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE"] = "继续下一轮循环";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK"] = "跳出包含它的循环。";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE"] = "跳过本轮循环的剩余部分,并继进行续下一轮迭代。";
Blockly.Msg["CONTROLS_FLOW_STATEMENTS_WARNING"] = "警告:这个块只能在循环内使用。";
Blockly.Msg["CONTROLS_FOREACH_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#for-each"; // untranslated
Blockly.Msg["CONTROLS_FOREACH_TITLE"] = "为列表 %2 里的每一项 %1";
Blockly.Msg["CONTROLS_FOREACH_TOOLTIP"] = "遍历列表中的每一项,将变量“%1”设为所选项并执行一些语句。";
Blockly.Msg["CONTROLS_FOR_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#count-with"; // untranslated
Blockly.Msg["CONTROLS_FOR_TITLE"] = "变量 %1 从 %2 数到 %3 每次增加 %4";
Blockly.Msg["CONTROLS_FOR_TOOLTIP"] = "用变量%1记录从开始数值到终止数值之间的数值数值按指定间隔增加并执行指定的块。";
Blockly.Msg["CONTROLS_IF_ELSEIF_TOOLTIP"] = "在这个if语句块中增加一个条件。";
Blockly.Msg["CONTROLS_IF_ELSE_TOOLTIP"] = "在这个if语句块中添加一个最终的包括所有其余情况的条件。";
Blockly.Msg["CONTROLS_IF_HELPURL"] = "https://github.com/google/blockly/wiki/IfElse"; // untranslated
Blockly.Msg["CONTROLS_IF_IF_TOOLTIP"] = "增加、删除或重新排列各节来重新配置这个if语句块。";
Blockly.Msg["CONTROLS_IF_MSG_ELSE"] = "否则";
Blockly.Msg["CONTROLS_IF_MSG_ELSEIF"] = "否则如果";
Blockly.Msg["CONTROLS_IF_MSG_IF"] = "如果";
Blockly.Msg["CONTROLS_IF_TOOLTIP_1"] = "如果值为真,执行一些语句。";
Blockly.Msg["CONTROLS_IF_TOOLTIP_2"] = "如果值为真,则执行第一块语句。否则,则执行第二块语句。";
Blockly.Msg["CONTROLS_IF_TOOLTIP_3"] = "如果第一个值为真,则执行第一块的语句。否则,如果第二个值为真,则执行第二块的语句。";
Blockly.Msg["CONTROLS_IF_TOOLTIP_4"] = "如果第一个值为真,则执行第一块对语句。否则,如果第二个值为真,则执行语句的第二块。如果没有值为真,则执行最后一块的语句。";
Blockly.Msg["CONTROLS_REPEAT_HELPURL"] = "https://zh.wikipedia.org/wiki/For循环";
Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"] = "执行";
Blockly.Msg["CONTROLS_REPEAT_TITLE"] = "重复 %1 次";
Blockly.Msg["CONTROLS_REPEAT_TOOLTIP"] = "多次执行一些语句。";
Blockly.Msg["CONTROLS_WHILEUNTIL_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#repeat"; // untranslated
Blockly.Msg["CONTROLS_WHILEUNTIL_OPERATOR_UNTIL"] = "重复直到条件满足";
Blockly.Msg["CONTROLS_WHILEUNTIL_OPERATOR_WHILE"] = "当条件满足时重复";
Blockly.Msg["CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL"] = "只要值为假,执行一些语句。";
Blockly.Msg["CONTROLS_WHILEUNTIL_TOOLTIP_WHILE"] = "只要值为真,执行一些语句。";
Blockly.Msg["DELETE_ALL_BLOCKS"] = "删除所有 %1 个块吗?";
Blockly.Msg["DELETE_BLOCK"] = "删除块";
Blockly.Msg["DELETE_VARIABLE"] = "删除变量“%1”";
Blockly.Msg["DELETE_VARIABLE_CONFIRMATION"] = "要删除对变量“%2”的%1个引用吗";
Blockly.Msg["DELETE_X_BLOCKS"] = "删除 %1 个块";
Blockly.Msg["DISABLE_BLOCK"] = "禁用块";
Blockly.Msg["DUPLICATE_BLOCK"] = "复制";
Blockly.Msg["DUPLICATE_COMMENT"] = "复制注释";
Blockly.Msg["ENABLE_BLOCK"] = "启用块";
Blockly.Msg["EXPAND_ALL"] = "展开块";
Blockly.Msg["EXPAND_BLOCK"] = "展开块";
Blockly.Msg["EXTERNAL_INPUTS"] = "外部输入";
Blockly.Msg["HELP"] = "帮助";
Blockly.Msg["INLINE_INPUTS"] = "单行输入";
Blockly.Msg["IOS_CANCEL"] = "取消";
Blockly.Msg["IOS_ERROR"] = "错误";
Blockly.Msg["IOS_OK"] = "确定";
Blockly.Msg["IOS_PROCEDURES_ADD_INPUT"] = "+添加输入";
Blockly.Msg["IOS_PROCEDURES_ALLOW_STATEMENTS"] = "允许的语句";
Blockly.Msg["IOS_PROCEDURES_DUPLICATE_INPUTS_ERROR"] = "这个函数有多个输入。";
Blockly.Msg["IOS_PROCEDURES_INPUTS"] = "输入";
Blockly.Msg["IOS_VARIABLES_ADD_BUTTON"] = "添加";
Blockly.Msg["IOS_VARIABLES_ADD_VARIABLE"] = "+添加变量";
Blockly.Msg["IOS_VARIABLES_DELETE_BUTTON"] = "删除";
Blockly.Msg["IOS_VARIABLES_EMPTY_NAME_ERROR"] = "你不能使用空白的变量名。";
Blockly.Msg["IOS_VARIABLES_RENAME_BUTTON"] = "重命名";
Blockly.Msg["IOS_VARIABLES_VARIABLE_NAME"] = "变量名";
Blockly.Msg["LISTS_CREATE_EMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-empty-list";
Blockly.Msg["LISTS_CREATE_EMPTY_TITLE"] = "创建空列表";
Blockly.Msg["LISTS_CREATE_EMPTY_TOOLTIP"] = "返回一个列表,长度为 0不包含任何数据记录";
Blockly.Msg["LISTS_CREATE_WITH_CONTAINER_TITLE_ADD"] = "列表";
Blockly.Msg["LISTS_CREATE_WITH_CONTAINER_TOOLTIP"] = "增加、删除或重新排列各部分以此重新配置这个列表块。";
Blockly.Msg["LISTS_CREATE_WITH_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-list-with";
Blockly.Msg["LISTS_CREATE_WITH_INPUT_WITH"] = "建立列表从";
Blockly.Msg["LISTS_CREATE_WITH_ITEM_TOOLTIP"] = "将一个项添加到列表中。";
Blockly.Msg["LISTS_CREATE_WITH_TOOLTIP"] = "建立一个具有任意数量项目的列表。";
Blockly.Msg["LISTS_GET_INDEX_FIRST"] = "第一个";
Blockly.Msg["LISTS_GET_INDEX_FROM_END"] = "倒数第#";
Blockly.Msg["LISTS_GET_INDEX_FROM_START"] = "#";
Blockly.Msg["LISTS_GET_INDEX_GET"] = "取得";
Blockly.Msg["LISTS_GET_INDEX_GET_REMOVE"] = "取得并移除";
Blockly.Msg["LISTS_GET_INDEX_LAST"] = "最后一个";
Blockly.Msg["LISTS_GET_INDEX_RANDOM"] = "随机的";
Blockly.Msg["LISTS_GET_INDEX_REMOVE"] = "移除";
Blockly.Msg["LISTS_GET_INDEX_TAIL"] = "-";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_FIRST"] = "返回列表中的第一项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_FROM"] = "返回在列表中的指定位置的项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_LAST"] = "返回列表中的最后一项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_RANDOM"] = "返回列表中的随机一项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST"] = "移除并返回列表中的第一项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM"] = "移除并返回列表中的指定位置的项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST"] = "移除并返回列表中的最后一项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM"] = "移除并返回列表中的随机一项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST"] = "移除列表中的第一项";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM"] = "移除在列表中的指定位置的项。";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST"] = "移除列表中的最后一项";
Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM"] = "删除列表中的随机一项。";
Blockly.Msg["LISTS_GET_SUBLIST_END_FROM_END"] = "到倒数第#";
Blockly.Msg["LISTS_GET_SUBLIST_END_FROM_START"] = "到#";
Blockly.Msg["LISTS_GET_SUBLIST_END_LAST"] = "到最后";
Blockly.Msg["LISTS_GET_SUBLIST_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#getting-a-sublist"; // untranslated
Blockly.Msg["LISTS_GET_SUBLIST_START_FIRST"] = "获取子列表从第一个";
Blockly.Msg["LISTS_GET_SUBLIST_START_FROM_END"] = "获取子列表从最后一个#";
Blockly.Msg["LISTS_GET_SUBLIST_START_FROM_START"] = "获取子列表从#";
Blockly.Msg["LISTS_GET_SUBLIST_TAIL"] = "-";
Blockly.Msg["LISTS_GET_SUBLIST_TOOLTIP"] = "复制列表中指定的部分。";
Blockly.Msg["LISTS_INDEX_FROM_END_TOOLTIP"] = "%1是最后一项。";
Blockly.Msg["LISTS_INDEX_FROM_START_TOOLTIP"] = "%1是第一项。";
Blockly.Msg["LISTS_INDEX_OF_FIRST"] = "寻找第一次出现的项";
Blockly.Msg["LISTS_INDEX_OF_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated
Blockly.Msg["LISTS_INDEX_OF_LAST"] = "寻找最后一次出现的项";
Blockly.Msg["LISTS_INDEX_OF_TOOLTIP"] = "返回在列表中的第一/最后一个匹配项的索引值。如果找不到项目则返回%1。";
Blockly.Msg["LISTS_INLIST"] = "在列表中";
Blockly.Msg["LISTS_ISEMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#is-empty"; // untranslated
Blockly.Msg["LISTS_ISEMPTY_TITLE"] = "%1是空的";
Blockly.Msg["LISTS_ISEMPTY_TOOLTIP"] = "如果改列表为空,则返回真。";
Blockly.Msg["LISTS_LENGTH_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#length-of"; // untranslated
Blockly.Msg["LISTS_LENGTH_TITLE"] = "%1的长度";
Blockly.Msg["LISTS_LENGTH_TOOLTIP"] = "返回列表的长度。";
Blockly.Msg["LISTS_REPEAT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-list-with"; // untranslated
Blockly.Msg["LISTS_REPEAT_TITLE"] = "建立列表使用项 %1 重复 %2 次";
Blockly.Msg["LISTS_REPEAT_TOOLTIP"] = "建立包含指定重复次数的值的列表。";
Blockly.Msg["LISTS_REVERSE_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#reversing-a-list";
Blockly.Msg["LISTS_REVERSE_MESSAGE0"] = "倒转%1";
Blockly.Msg["LISTS_REVERSE_TOOLTIP"] = "倒转一个列表的拷贝。";
Blockly.Msg["LISTS_SET_INDEX_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#in-list--set"; // untranslated
Blockly.Msg["LISTS_SET_INDEX_INPUT_TO"] = "值为";
Blockly.Msg["LISTS_SET_INDEX_INSERT"] = "插入在";
Blockly.Msg["LISTS_SET_INDEX_SET"] = "设置";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST"] = "在列表的起始处添加该项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_FROM"] = "插入在列表中指定位置的项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_LAST"] = "在列表的末尾处添加该项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM"] = "在列表的随机位置插入该项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_FIRST"] = "设置列表中的第一项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_FROM"] = "设置在列表中指定位置的项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_LAST"] = "设置列表中的最后一项。";
Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_RANDOM"] = "设置列表中的随机一项。";
Blockly.Msg["LISTS_SORT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#sorting-a-list";
Blockly.Msg["LISTS_SORT_ORDER_ASCENDING"] = "升序";
Blockly.Msg["LISTS_SORT_ORDER_DESCENDING"] = "降序";
Blockly.Msg["LISTS_SORT_TITLE"] = "排序%1 %2 %3";
Blockly.Msg["LISTS_SORT_TOOLTIP"] = "排序一个列表的拷贝。";
Blockly.Msg["LISTS_SORT_TYPE_IGNORECASE"] = "按字母排序,忽略大小写";
Blockly.Msg["LISTS_SORT_TYPE_NUMERIC"] = "按数字排序";
Blockly.Msg["LISTS_SORT_TYPE_TEXT"] = "按字母排序";
Blockly.Msg["LISTS_SPLIT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists";
Blockly.Msg["LISTS_SPLIT_LIST_FROM_TEXT"] = "从文本制作列表";
Blockly.Msg["LISTS_SPLIT_TEXT_FROM_LIST"] = "从列表拆出文本";
Blockly.Msg["LISTS_SPLIT_TOOLTIP_JOIN"] = "加入文本列表至一个文本,由分隔符分隔。";
Blockly.Msg["LISTS_SPLIT_TOOLTIP_SPLIT"] = "拆分文本到文本列表,按每个分隔符拆分。";
Blockly.Msg["LISTS_SPLIT_WITH_DELIMITER"] = "用分隔符";
Blockly.Msg["LOGIC_BOOLEAN_FALSE"] = "假";
Blockly.Msg["LOGIC_BOOLEAN_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#values"; // untranslated
Blockly.Msg["LOGIC_BOOLEAN_TOOLTIP"] = "返回真或假。";
Blockly.Msg["LOGIC_BOOLEAN_TRUE"] = "真";
Blockly.Msg["LOGIC_COMPARE_HELPURL"] = "https://zh.wikipedia.org/wiki/不等";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_EQ"] = "如果两个输入结果相等,则返回真。";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_GT"] = "如果第一个输入结果比第二个大,则返回真。";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_GTE"] = "如果第一个输入结果大于或等于第二个输入结果,则返回真。";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_LT"] = "如果第一个输入结果比第二个小,则返回真。";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_LTE"] = "如果第一个输入结果小于或等于第二个输入结果,则返回真。";
Blockly.Msg["LOGIC_COMPARE_TOOLTIP_NEQ"] = "如果两个输入结果不相等,则返回真。";
Blockly.Msg["LOGIC_NEGATE_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#not";
Blockly.Msg["LOGIC_NEGATE_TITLE"] = "非%1";
Blockly.Msg["LOGIC_NEGATE_TOOLTIP"] = "如果输入结果为假,则返回真;如果输入结果为真,则返回假。";
Blockly.Msg["LOGIC_NULL"] = "空";
Blockly.Msg["LOGIC_NULL_HELPURL"] = "https://en.wikipedia.org/wiki/Nullable_type"; // untranslated
Blockly.Msg["LOGIC_NULL_TOOLTIP"] = "返回空值。";
Blockly.Msg["LOGIC_OPERATION_AND"] = "并且";
Blockly.Msg["LOGIC_OPERATION_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#logical-operations"; // untranslated
Blockly.Msg["LOGIC_OPERATION_OR"] = "或";
Blockly.Msg["LOGIC_OPERATION_TOOLTIP_AND"] = "如果两个输入结果都为真,则返回真。";
Blockly.Msg["LOGIC_OPERATION_TOOLTIP_OR"] = "如果至少有一个输入结果为真,则返回真。";
Blockly.Msg["LOGIC_TERNARY_CONDITION"] = "断言";
Blockly.Msg["LOGIC_TERNARY_HELPURL"] = "https://zh.wikipedia.org/wiki/条件运算符";
Blockly.Msg["LOGIC_TERNARY_IF_FALSE"] = "如果为假";
Blockly.Msg["LOGIC_TERNARY_IF_TRUE"] = "如果为真";
Blockly.Msg["LOGIC_TERNARY_TOOLTIP"] = "检查“断言”里的条件语句。如果条件为真,则返回“如果为真”的值,否则,则返回“如果为假”的值。";
Blockly.Msg["MATH_ADDITION_SYMBOL"] = "+"; // untranslated
Blockly.Msg["MATH_ARITHMETIC_HELPURL"] = "https://zh.wikipedia.org/wiki/算术";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_ADD"] = "返回两个数值的和。";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_DIVIDE"] = "返回两个数值的商。";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_MINUS"] = "返回两个数值的差值。";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_MULTIPLY"] = "返回两个数值的乘积。";
Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_POWER"] = "返回以第一个数值为底数,以第二个数值为幂的结果。";
Blockly.Msg["MATH_ATAN2_HELPURL"] = "https://zh.wikipedia.org/wiki/反正切2";
Blockly.Msg["MATH_ATAN2_TITLE"] = "点(x:%1,y:%2)的方位角";
Blockly.Msg["MATH_ATAN2_TOOLTIP"] = "返回点XY的反正切值范围为-180到180度。";
Blockly.Msg["MATH_CHANGE_HELPURL"] = "https://zh.wikipedia.org/wiki/加法";
Blockly.Msg["MATH_CHANGE_TITLE"] = "将 %1 增加 %2";
Blockly.Msg["MATH_CHANGE_TOOLTIP"] = "为变量“%1”增加一个数值。";
Blockly.Msg["MATH_CONSTANT_HELPURL"] = "https://zh.wikipedia.org/wiki/数学常数";
Blockly.Msg["MATH_CONSTANT_TOOLTIP"] = "返回一个常见常量:π (3.141…)、e (2.718…)、φ (1.618…)、平方根 (1.414…)、开平方根 (0.707…)或∞ (无限大)。";
Blockly.Msg["MATH_CONSTRAIN_HELPURL"] = "https://en.wikipedia.org/wiki/Clamping_(graphics)"; // untranslated
Blockly.Msg["MATH_CONSTRAIN_TITLE"] = "将 %1 限制在 最低 %2 到最高 %3 之间";
Blockly.Msg["MATH_CONSTRAIN_TOOLTIP"] = "将一个数值限制在两个指定的数值范围(含边界)之间。";
Blockly.Msg["MATH_DIVISION_SYMBOL"] = "÷"; // untranslated
Blockly.Msg["MATH_IS_DIVISIBLE_BY"] = "可被整除";
Blockly.Msg["MATH_IS_EVEN"] = "是偶数";
Blockly.Msg["MATH_IS_NEGATIVE"] = "是负数";
Blockly.Msg["MATH_IS_ODD"] = "是奇数";
Blockly.Msg["MATH_IS_POSITIVE"] = "是正数";
Blockly.Msg["MATH_IS_PRIME"] = "是质数";
Blockly.Msg["MATH_IS_TOOLTIP"] = "检查一个数值是否是偶数、奇数、质数、自然数、正数、负数或者是否能被某数整除。返回真或假。";
Blockly.Msg["MATH_IS_WHOLE"] = "是整数";
Blockly.Msg["MATH_MODULO_HELPURL"] = "https://zh.wikipedia.org/wiki/模除";
Blockly.Msg["MATH_MODULO_TITLE"] = "取 %1 ÷ %2 的余数";
Blockly.Msg["MATH_MODULO_TOOLTIP"] = "返回这两个数字相除后的余数。";
Blockly.Msg["MATH_MULTIPLICATION_SYMBOL"] = "×"; // untranslated
Blockly.Msg["MATH_NUMBER_HELPURL"] = "https://zh.wikipedia.org/wiki/数";
Blockly.Msg["MATH_NUMBER_TOOLTIP"] = "一个数值。";
Blockly.Msg["MATH_ONLIST_HELPURL"] = ""; // untranslated
Blockly.Msg["MATH_ONLIST_OPERATOR_AVERAGE"] = "列表平均值";
Blockly.Msg["MATH_ONLIST_OPERATOR_MAX"] = "列表最大值";
Blockly.Msg["MATH_ONLIST_OPERATOR_MEDIAN"] = "列表中位数";
Blockly.Msg["MATH_ONLIST_OPERATOR_MIN"] = "列表最小值";
Blockly.Msg["MATH_ONLIST_OPERATOR_MODE"] = "列表中的众数";
Blockly.Msg["MATH_ONLIST_OPERATOR_RANDOM"] = "列表随机项";
Blockly.Msg["MATH_ONLIST_OPERATOR_STD_DEV"] = "列表的标准差";
Blockly.Msg["MATH_ONLIST_OPERATOR_SUM"] = "列表中数值的和";
Blockly.Msg["MATH_ONLIST_TOOLTIP_AVERAGE"] = "返回列表中的数值的平均值。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MAX"] = "返回列表中最大值。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MEDIAN"] = "返回列表中数值的中位数。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MIN"] = "返回列表中最小值。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_MODE"] = "返回列表中的出现次数最多的项的列表。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_RANDOM"] = "从列表中返回一个随机的元素。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_STD_DEV"] = "返回列表的标准差。";
Blockly.Msg["MATH_ONLIST_TOOLTIP_SUM"] = "返回列表中的所有数值的和。";
Blockly.Msg["MATH_POWER_SYMBOL"] = "^"; // untranslated
Blockly.Msg["MATH_RANDOM_FLOAT_HELPURL"] = "https://zh.wikipedia.org/wiki/随机数生成器";
Blockly.Msg["MATH_RANDOM_FLOAT_TITLE_RANDOM"] = "随机小数";
Blockly.Msg["MATH_RANDOM_FLOAT_TOOLTIP"] = "返回一个介于0.0到1.0之间(含边界)的随机数。";
Blockly.Msg["MATH_RANDOM_INT_HELPURL"] = "https://zh.wikipedia.org/wiki/随机数生成器";
Blockly.Msg["MATH_RANDOM_INT_TITLE"] = "从 %1 到 %2 范围内的随机整数";
Blockly.Msg["MATH_RANDOM_INT_TOOLTIP"] = "返回一个限制在两个指定数值的范围(含边界)之间的随机整数。";
Blockly.Msg["MATH_ROUND_HELPURL"] = "https://zh.wikipedia.org/wiki/数值修约";
Blockly.Msg["MATH_ROUND_OPERATOR_ROUND"] = "四舍五入";
Blockly.Msg["MATH_ROUND_OPERATOR_ROUNDDOWN"] = "向下舍入";
Blockly.Msg["MATH_ROUND_OPERATOR_ROUNDUP"] = "向上舍入";
Blockly.Msg["MATH_ROUND_TOOLTIP"] = "数字向上或向下舍入。";
Blockly.Msg["MATH_SINGLE_HELPURL"] = "https://zh.wikipedia.org/wiki/平方根";
Blockly.Msg["MATH_SINGLE_OP_ABSOLUTE"] = "绝对值";
Blockly.Msg["MATH_SINGLE_OP_ROOT"] = "平方根";
Blockly.Msg["MATH_SINGLE_TOOLTIP_ABS"] = "返回一个数值的绝对值。";
Blockly.Msg["MATH_SINGLE_TOOLTIP_EXP"] = "返回一个数值的e次幂。";
Blockly.Msg["MATH_SINGLE_TOOLTIP_LN"] = "返回一个数值的自然对数。";
Blockly.Msg["MATH_SINGLE_TOOLTIP_LOG10"] = "返回一个数值的以10为底的对数。";
Blockly.Msg["MATH_SINGLE_TOOLTIP_NEG"] = "返回一个数值的相反数。";
Blockly.Msg["MATH_SINGLE_TOOLTIP_POW10"] = "返回一个数值的10次幂。";
Blockly.Msg["MATH_SINGLE_TOOLTIP_ROOT"] = "返回一个数的平方根。";
Blockly.Msg["MATH_SUBTRACTION_SYMBOL"] = "-"; // untranslated
Blockly.Msg["MATH_TRIG_ACOS"] = "acos"; // untranslated
Blockly.Msg["MATH_TRIG_ASIN"] = "asin"; // untranslated
Blockly.Msg["MATH_TRIG_ATAN"] = "atan"; // untranslated
Blockly.Msg["MATH_TRIG_COS"] = "cos"; // untranslated
Blockly.Msg["MATH_TRIG_HELPURL"] = "https://zh.wikipedia.org/wiki/三角函数";
Blockly.Msg["MATH_TRIG_SIN"] = "sin"; // untranslated
Blockly.Msg["MATH_TRIG_TAN"] = "tan"; // untranslated
Blockly.Msg["MATH_TRIG_TOOLTIP_ACOS"] = "返回一个数值的反余弦值。";
Blockly.Msg["MATH_TRIG_TOOLTIP_ASIN"] = "返回一个数值的反正弦值。";
Blockly.Msg["MATH_TRIG_TOOLTIP_ATAN"] = "返回一个数值的反正切值。";
Blockly.Msg["MATH_TRIG_TOOLTIP_COS"] = "返回指定角度的余弦值(非弧度)。";
Blockly.Msg["MATH_TRIG_TOOLTIP_SIN"] = "返回指定角度的正弦值(非弧度)。";
Blockly.Msg["MATH_TRIG_TOOLTIP_TAN"] = "返回指定角度的正切值(非弧度)。";
Blockly.Msg["NEW_COLOUR_VARIABLE"] = "创建颜色变量...";
Blockly.Msg["NEW_NUMBER_VARIABLE"] = "创建数字变量...";
Blockly.Msg["NEW_STRING_VARIABLE"] = "创建字符串变量...";
Blockly.Msg["NEW_VARIABLE"] = "创建变量...";
Blockly.Msg["NEW_VARIABLE_TITLE"] = "新变量的名称:";
Blockly.Msg["NEW_VARIABLE_TYPE_TITLE"] = "新变量的类型:";
Blockly.Msg["ORDINAL_NUMBER_SUFFIX"] = "-";
Blockly.Msg["PROCEDURES_ALLOW_STATEMENTS"] = "允许声明";
Blockly.Msg["PROCEDURES_BEFORE_PARAMS"] = "与:";
Blockly.Msg["PROCEDURES_CALLNORETURN_HELPURL"] = "https://zh.wikipedia.org/wiki/子程序";
Blockly.Msg["PROCEDURES_CALLNORETURN_TOOLTIP"] = "运行用户定义的函数“%1”。";
Blockly.Msg["PROCEDURES_CALLRETURN_HELPURL"] = "https://zh.wikipedia.org/wiki/子程序";
Blockly.Msg["PROCEDURES_CALLRETURN_TOOLTIP"] = "运行用户定义的函数“%1”并使用它的输出值。";
Blockly.Msg["PROCEDURES_CALL_BEFORE_PARAMS"] = "与:";
Blockly.Msg["PROCEDURES_CREATE_DO"] = "创建“%1”";
Blockly.Msg["PROCEDURES_DEFNORETURN_COMMENT"] = "描述该功能...";
Blockly.Msg["PROCEDURES_DEFNORETURN_DO"] = "-";
Blockly.Msg["PROCEDURES_DEFNORETURN_HELPURL"] = "https://zh.wikipedia.org/wiki/子程序";
Blockly.Msg["PROCEDURES_DEFNORETURN_PROCEDURE"] = "做点什么";
Blockly.Msg["PROCEDURES_DEFNORETURN_TITLE"] = "至";
Blockly.Msg["PROCEDURES_DEFNORETURN_TOOLTIP"] = "创建一个不带输出值的函数。";
Blockly.Msg["PROCEDURES_DEFRETURN_HELPURL"] = "https://zh.wikipedia.org/wiki/子程序";
Blockly.Msg["PROCEDURES_DEFRETURN_RETURN"] = "返回";
Blockly.Msg["PROCEDURES_DEFRETURN_TOOLTIP"] = "创建一个有输出值的函数。";
Blockly.Msg["PROCEDURES_DEF_DUPLICATE_WARNING"] = "警告:此函数具有重复参数。";
Blockly.Msg["PROCEDURES_HIGHLIGHT_DEF"] = "突出显示函数定义";
Blockly.Msg["PROCEDURES_IFRETURN_HELPURL"] = "http://c2.com/cgi/wiki?GuardClause";
Blockly.Msg["PROCEDURES_IFRETURN_TOOLTIP"] = "如果值为真,则返回第二个值。";
Blockly.Msg["PROCEDURES_IFRETURN_WARNING"] = "警告:这个块只能在函数内部使用。";
Blockly.Msg["PROCEDURES_MUTATORARG_TITLE"] = "输入名称:";
Blockly.Msg["PROCEDURES_MUTATORARG_TOOLTIP"] = "添加函数输入。";
Blockly.Msg["PROCEDURES_MUTATORCONTAINER_TITLE"] = "输入";
Blockly.Msg["PROCEDURES_MUTATORCONTAINER_TOOLTIP"] = "添加、移除或重新排此函数的输入。";
Blockly.Msg["REDO"] = "重做";
Blockly.Msg["REMOVE_COMMENT"] = "删除注释";
Blockly.Msg["RENAME_VARIABLE"] = "重命名变量...";
Blockly.Msg["RENAME_VARIABLE_TITLE"] = "将所有“%1”变量重命名为:";
Blockly.Msg["TEXT_APPEND_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-modification"; // untranslated
Blockly.Msg["TEXT_APPEND_TITLE"] = "向%1附加文本%2";
Blockly.Msg["TEXT_APPEND_TOOLTIP"] = "将一些文本追加到变量“%1”里。";
Blockly.Msg["TEXT_CHANGECASE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#adjusting-text-case"; // untranslated
Blockly.Msg["TEXT_CHANGECASE_OPERATOR_LOWERCASE"] = "转为小写";
Blockly.Msg["TEXT_CHANGECASE_OPERATOR_TITLECASE"] = "转为首字母大写";
Blockly.Msg["TEXT_CHANGECASE_OPERATOR_UPPERCASE"] = "转为大写";
Blockly.Msg["TEXT_CHANGECASE_TOOLTIP"] = "用不同的大小写模式复制并返回这段文字。";
Blockly.Msg["TEXT_CHARAT_FIRST"] = "寻找第一个字母";
Blockly.Msg["TEXT_CHARAT_FROM_END"] = "获取字符从倒数#";
Blockly.Msg["TEXT_CHARAT_FROM_START"] = "获取字符从#";
Blockly.Msg["TEXT_CHARAT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#extracting-text"; // untranslated
Blockly.Msg["TEXT_CHARAT_LAST"] = "寻找最后一个字母";
Blockly.Msg["TEXT_CHARAT_RANDOM"] = "寻找随机的字母";
Blockly.Msg["TEXT_CHARAT_TAIL"] = "-";
Blockly.Msg["TEXT_CHARAT_TITLE"] = "在文本%1 里 %2";
Blockly.Msg["TEXT_CHARAT_TOOLTIP"] = "返回位于指定位置的字母。";
Blockly.Msg["TEXT_COUNT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#counting-substrings";
Blockly.Msg["TEXT_COUNT_MESSAGE0"] = "计算%1在%2里出现的次数";
Blockly.Msg["TEXT_COUNT_TOOLTIP"] = "计算在一段文本中,某个部分文本重复出现了多少次。";
Blockly.Msg["TEXT_CREATE_JOIN_ITEM_TOOLTIP"] = "将一个项添加到文本中。";
Blockly.Msg["TEXT_CREATE_JOIN_TITLE_JOIN"] = "加入";
Blockly.Msg["TEXT_CREATE_JOIN_TOOLTIP"] = "添加、移除或重新排列各节来重新配置这个文本块。";
Blockly.Msg["TEXT_GET_SUBSTRING_END_FROM_END"] = "到最后一个字符#";
Blockly.Msg["TEXT_GET_SUBSTRING_END_FROM_START"] = "至字符#";
Blockly.Msg["TEXT_GET_SUBSTRING_END_LAST"] = "到最后一个字符";
Blockly.Msg["TEXT_GET_SUBSTRING_HELPURL"] = "https://github.com/google/blockly/wiki/Text#extracting-a-region-of-text"; // untranslated
Blockly.Msg["TEXT_GET_SUBSTRING_INPUT_IN_TEXT"] = "从文本";
Blockly.Msg["TEXT_GET_SUBSTRING_START_FIRST"] = "取得子串自第一个字符";
Blockly.Msg["TEXT_GET_SUBSTRING_START_FROM_END"] = "取得子串自倒数#";
Blockly.Msg["TEXT_GET_SUBSTRING_START_FROM_START"] = "取得子串自#";
Blockly.Msg["TEXT_GET_SUBSTRING_TAIL"] = "-";
Blockly.Msg["TEXT_GET_SUBSTRING_TOOLTIP"] = "返回文本中指定的一部分。";
Blockly.Msg["TEXT_INDEXOF_HELPURL"] = "https://github.com/google/blockly/wiki/Text#finding-text"; // untranslated
Blockly.Msg["TEXT_INDEXOF_OPERATOR_FIRST"] = "寻找第一次出现的文本";
Blockly.Msg["TEXT_INDEXOF_OPERATOR_LAST"] = "寻找最后一次出现的文本";
Blockly.Msg["TEXT_INDEXOF_TITLE"] = "在文本 %1 里 %2 %3";
Blockly.Msg["TEXT_INDEXOF_TOOLTIP"] = "返回第一个文本段在第二个文本段中的第一/最后一个匹配项的起始位置。如果未找到,则返回%1。";
Blockly.Msg["TEXT_ISEMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; // untranslated
Blockly.Msg["TEXT_ISEMPTY_TITLE"] = "%1是空的";
Blockly.Msg["TEXT_ISEMPTY_TOOLTIP"] = "如果给定的文本为空,则返回真。";
Blockly.Msg["TEXT_JOIN_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-creation"; // untranslated
Blockly.Msg["TEXT_JOIN_TITLE_CREATEWITH"] = "建立文本从";
Blockly.Msg["TEXT_JOIN_TOOLTIP"] = "通过串起任意数量的项以建立一段文本。";
Blockly.Msg["TEXT_LENGTH_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-modification"; // untranslated
Blockly.Msg["TEXT_LENGTH_TITLE"] = "%1的长度";
Blockly.Msg["TEXT_LENGTH_TOOLTIP"] = "返回给定文本的字母数(包括空格)。";
Blockly.Msg["TEXT_PRINT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#printing-text"; // untranslated
Blockly.Msg["TEXT_PRINT_TITLE"] = "输出%1";
Blockly.Msg["TEXT_PRINT_TOOLTIP"] = "输出指定的文字、数字或其他值。";
Blockly.Msg["TEXT_PROMPT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#getting-input-from-the-user"; // untranslated
Blockly.Msg["TEXT_PROMPT_TOOLTIP_NUMBER"] = "要求用户输入数字。";
Blockly.Msg["TEXT_PROMPT_TOOLTIP_TEXT"] = "要求用户输入一些文本。";
Blockly.Msg["TEXT_PROMPT_TYPE_NUMBER"] = "要求输入数字,并显示提示消息";
Blockly.Msg["TEXT_PROMPT_TYPE_TEXT"] = "要求输入文本,并显示提示消息";
Blockly.Msg["TEXT_REPLACE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#replacing-substrings";
Blockly.Msg["TEXT_REPLACE_MESSAGE0"] = "在%3中将%1替换为%2";
Blockly.Msg["TEXT_REPLACE_TOOLTIP"] = "在一段文本中,将出现过的某部分文本都替换掉。";
Blockly.Msg["TEXT_REVERSE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#reversing-text";
Blockly.Msg["TEXT_REVERSE_MESSAGE0"] = "翻转文本%1";
Blockly.Msg["TEXT_REVERSE_TOOLTIP"] = "将文本中各个字符的顺序倒转。";
Blockly.Msg["TEXT_TEXT_HELPURL"] = "https://zh.wikipedia.org/wiki/字符串";
Blockly.Msg["TEXT_TEXT_TOOLTIP"] = "一个字、词语或一行文本。";
Blockly.Msg["TEXT_TRIM_HELPURL"] = "https://github.com/google/blockly/wiki/Text#trimming-removing-spaces"; // untranslated
Blockly.Msg["TEXT_TRIM_OPERATOR_BOTH"] = "消除其两侧的空白";
Blockly.Msg["TEXT_TRIM_OPERATOR_LEFT"] = "消除其左侧的空白";
Blockly.Msg["TEXT_TRIM_OPERATOR_RIGHT"] = "消除其右侧的空白";
Blockly.Msg["TEXT_TRIM_TOOLTIP"] = "从某一端或同时从两端删除多余的空白,并返回这段文字的一个副本。";
Blockly.Msg["TODAY"] = "今天";
Blockly.Msg["UNDO"] = "撤销";
Blockly.Msg["UNNAMED_KEY"] = "匿名";
Blockly.Msg["VARIABLES_DEFAULT_NAME"] = "项目";
Blockly.Msg["VARIABLES_GET_CREATE_SET"] = "创建“设定%1”";
Blockly.Msg["VARIABLES_GET_HELPURL"] = "https://github.com/google/blockly/wiki/Variables#get"; // untranslated
Blockly.Msg["VARIABLES_GET_TOOLTIP"] = "返回此变量的值。";
Blockly.Msg["VARIABLES_SET"] = "赋值 %1 为 %2";
Blockly.Msg["VARIABLES_SET_CREATE_GET"] = "创建“获得%1”";
Blockly.Msg["VARIABLES_SET_HELPURL"] = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated
Blockly.Msg["VARIABLES_SET_TOOLTIP"] = "设置此变量,以使它和输入值相等。";
Blockly.Msg["VARIABLE_ALREADY_EXISTS"] = "名字叫“%1”的变量已经存在了。";
Blockly.Msg["VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE"] = "名字叫“%1”的变量已经有了另一个类型“%2”。";
Blockly.Msg["WORKSPACE_ARIA_LABEL"] = "Blockly工作区";
Blockly.Msg["WORKSPACE_COMMENT_DEFAULT_TEXT"] = "说点什么...";
Blockly.Msg["CONTROLS_FOREACH_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["CONTROLS_FOR_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["CONTROLS_IF_ELSEIF_TITLE_ELSEIF"] = Blockly.Msg["CONTROLS_IF_MSG_ELSEIF"];
Blockly.Msg["CONTROLS_IF_ELSE_TITLE_ELSE"] = Blockly.Msg["CONTROLS_IF_MSG_ELSE"];
Blockly.Msg["CONTROLS_IF_IF_TITLE_IF"] = Blockly.Msg["CONTROLS_IF_MSG_IF"];
Blockly.Msg["CONTROLS_IF_MSG_THEN"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["CONTROLS_WHILEUNTIL_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"];
Blockly.Msg["LISTS_CREATE_WITH_ITEM_TITLE"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["LISTS_GET_INDEX_HELPURL"] = Blockly.Msg["LISTS_INDEX_OF_HELPURL"];
Blockly.Msg["LISTS_GET_INDEX_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["LISTS_GET_SUBLIST_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["LISTS_INDEX_OF_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["LISTS_SET_INDEX_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"];
Blockly.Msg["MATH_CHANGE_TITLE_ITEM"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["PROCEDURES_DEFRETURN_COMMENT"] = Blockly.Msg["PROCEDURES_DEFNORETURN_COMMENT"];
Blockly.Msg["PROCEDURES_DEFRETURN_DO"] = Blockly.Msg["PROCEDURES_DEFNORETURN_DO"];
Blockly.Msg["PROCEDURES_DEFRETURN_PROCEDURE"] = Blockly.Msg["PROCEDURES_DEFNORETURN_PROCEDURE"];
Blockly.Msg["PROCEDURES_DEFRETURN_TITLE"] = Blockly.Msg["PROCEDURES_DEFNORETURN_TITLE"];
Blockly.Msg["TEXT_APPEND_VARIABLE"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["TEXT_CREATE_JOIN_ITEM_TITLE_ITEM"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"];
Blockly.Msg["MATH_HUE"] = "230";
Blockly.Msg["LOOPS_HUE"] = "120";
Blockly.Msg["LISTS_HUE"] = "260";
Blockly.Msg["LOGIC_HUE"] = "210";
Blockly.Msg["VARIABLES_HUE"] = "330";
Blockly.Msg["TEXTS_HUE"] = "160";
Blockly.Msg["PROCEDURES_HUE"] = "290";
Blockly.Msg["COLOUR_HUE"] = "20";
Blockly.Msg["VARIABLES_DYNAMIC_HUE"] = "310";

1
_server/config.json Normal file
View File

@ -0,0 +1 @@
{"viewportLoc":[0,0],"lastUsed":[{"idnum":1,"id":"yellowWall","images":"animates","y":10,"recent":1673440212744,"frequent":2},{"idnum":211,"id":"skeletonCaptain","images":"enemys","y":10,"recent":1673440202872,"frequent":1}],"editor_multi.fontSize":14,"editorLastFloorId":"sample1"}

BIN
_server/css/FiraCode.ttf Normal file

Binary file not shown.

710
_server/css/editor.css Normal file
View File

@ -0,0 +1,710 @@
/** editor **/
:root {
--pixel: 416px;
}
html, body, div, img {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
font-family: Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;;
}
/* ::-webkit-scrollbar {
width: 5px;
} */
.main {
max-width: 100%;
min-height: 500px;
margin: 0 auto;
}
.cp-app {
z-index: 250;
}
#colorPanel {
position: fixed;
width: max-content;
height: 205px;
z-index: 240;
padding: 4px 6px;
margin-top: 6px;
box-sizing: border-box;
}
#colorPicker {
margin: 2px 0;
border-radius: 3px;
width: 104px;
}
#left, #mid, #mid2, #right {
border-radius: 2px;
box-sizing: border-box;
}
#left {
position: absolute;
left: 5px;
top: 10px;
width: 435px;
height: 630px;
}
#mapEditArea {
position: absolute;
width: 100%;
height: 400px;
left: 0;
top: 0;
/* padding: 10px 5px; */
box-sizing: border-box;
}
#pout {
display: block;
width: 410px;
height: 380px;
box-sizing: border-box;
margin-left: 22px;
margin-top: 23px;
line-height: 20px;
font-size: 12.3px;
font-family: 'Lucida Console', Monaco, monospace;
white-space: pre;
border-radius: 2px;
overflow: auto;
}
#editTip {
position: absolute;
width: 100%;
left: 10px;
top: 430px;
}
#editBtns {
position: absolute;
width: 100%;
left: 10px;
top: 465px;
}
#newMaps {
position: absolute;
left: 10px;
top: 505px;
}
#newFloors {
position: absolute;
width: 100%;
left: 10px;
top: 530px;
}
#mapEditArea p {
margin: 10px;
display: block;
width: 70%;
line-height: 20px;
text-align: left;
font-size: 14px;
}
#editTip .btn {
float: right;
margin-right: 20px;
margin-top: 5px;
}
#mid {
position: absolute;
left: 448px;
top: 10px;
width: calc(24px + var(--pixel));
height: calc(422px + var(--pixel) / 2);
}
#mid2 {
position: absolute;
left: 448px;
top: calc(442px + var(--pixel) / 2);
width: calc(24px + var(--pixel));
bottom: 10px;
}
#mapEdit {
overflow: hidden;
}
.map {
position: absolute;
left: 20px;
top: 21px;
width: var(--pixel);
height: var(--pixel);
}
#lastUsedDiv {
height: auto;
bottom: 0;
margin-top: 20px;
overflow-x: hidden;
overflow-y: auto;
}
#mid .tools {
position: absolute;
width: calc(var(--pixel) + 9px);
height: calc(388px - var(--pixel) / 2);
left: 0;
bottom: 0;
padding: 10px 5px;
margin-left: 8px;;
box-sizing: border-box;
}
#tip {
float: right;
width: 48%;
height: 95%;
padding: 5px 10px 10px 10px;
margin-right: 0;
box-sizing: border-box;
border-radius: 2px;
font-size: 15px;
line-height: 18px;
}
#tip p {
margin-top: 10px;
margin-bottom: 10px;
}
.files {
width: 50%;
height: 120px;
/* padding: 10px; */
margin-top: 15px;
}
#bgSelect {
width: 50%;
height: 100px;
margin-top: 10px;
}
#bgSelect span {
/* display: block; */
font-size: 14px;
line-height: 30px;
}
#printOut {
margin-top: 10px;
height: 20px;
}
#right {
position: absolute;
left: calc(484px + var(--pixel));
top: 10px;
right: 0;
bottom: 0;
/* border: 1px solid rgb(238, 13, 13); */
}
#iconLib {
position: absolute;
right: 0;
bottom: 0;
left: 5px;
top: 5px;
overflow: auto;
}
#iconImages {
z-index: 0;
overflow: hidden;
}
#iconImages img {
position: absolute;
}
.gameCanvas {
position: absolute;
}
#dataSelection, .appendSelection {
position: absolute;
/* top:0;
left:320px; */
z-index: 75;
width: 26px;
height: 26px;
margin: 3px 0 0 3px;
padding: 0;
/* display: none; */
box-sizing: border-box;
}
#iconExpandBtn {
position: absolute;
left: 20px;
bottom: 30px;
font-size: 15px;
padding: 6px;
display: none;
}
.warnText {
font-weight: 700;
font-size: 14px;
line-height: 1.2em;
}
.successText {
line-height: 1.2em;
}
#mapColMark, #mapRowMark {
cursor: crosshair;
}
table.col {
position: relative;
text-align: center;
border-collapse: collapse;
}
#arrColMark td {
width: 16px;
}
#arrColMark {
top: 2px;
left: 36px;
width: 385px;
height: 16px;
font-size: 13px;
}
#mapColMark {
top: 2px;
left: 19px;
width: var(--pixel);
height: 16px;
font-size: 13px;
}
#mapColMark td {
width: 29px;
}
#mapColMark td:hover .colBlock {
position: absolute;
top: 19px;
height: var(--pixel);
width: 32px;
z-index: 100;
}
table.row {
position: relative;
text-align: right;
vertical-align: middle;
border-collapse: collapse;
}
#arrRowMark {
top: 5px;
left: 2px;
width: 16px;
height: 262px;
font-size: 12px;
}
#mapRowMark {
top: 1px;
left: 2px;
width: 16px;
height: var(--pixel);
font-size: 12px;
}
#mapRowMark td {
height: 29px;
}
#mapRowMark td:hover .rowBlock {
position: absolute;
left: 18px;
height: 32px;
width: var(--pixel);
z-index: 100;
}
#menuDiv * { margin: 0; padding: 0; }
#midMenu{
border-style: solid;
border-width: 1px;
cursor: default;
font: normal 13px Arial, sans-serif;
margin: 0;
outline: none;
padding: 4px 0;
position: absolute;
overflow-y: auto;
overflow-x: hidden;
max-height: 100%;
z-index: 101;
border-radius: 4px;
}
#midMenu .menuitem{
font: normal 13px Arial, sans-serif;
list-style: none;
margin: 0;
padding: 4px 7em 4px 28px;
white-space: nowrap;
/* padding-left: 12px; */
/* padding-right: 20px; */
}
#midMenu .menuitem:hover{
border-style: dotted;
border-width: 1px 0;
padding-bottom: 3px;
padding-top: 3px;
}
#midMenu .menuitem-content{
font: normal 13px Arial, sans-serif;
}
#searchBlock {
width: 100px;
border-radius: 10px;
outline: none;
padding-left: 20px;
height: 14px;
}
.searchLogo {
width: 15px;
height: 15px;
position: absolute;
left: 6px;
top: 6px;
background-image:url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGJhc2VQcm9maWxlPSJmdWxsIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpldj0iaHR0cDovL3d3dy53My5vcmcvMjAwMS94bWwtZXZlbnRzIj4KPGc%2BCgk8cG9seWdvbiBmaWxsPSIjNjY2IiBwb2ludHM9IjkuMjA3LDYuMTI2IDcuNzkzLDcuNTQxIDExLjc5MywxMS41NDEgMTMuMjA3LDEwLjEyNiIgLz4KCTxwYXRoIGZpbGw9IiM2NjYiIGQ9Ik01LjkxNywyYzEuNjA4LDAsMi45MTcsMS4zMDgsMi45MTcsMi45MTdTNy41MjUsNy44MzMsNS45MTcsNy44MzNTMyw2LjUyNSwzLDQuOTE3UzQuMzA4LDIsNS45MTcsMgoJCSBNNS45MTcsMEMzLjIwMSwwLDEsMi4yMDEsMSw0LjkxN3MyLjIwMSw0LjkxNyw0LjkxNyw0LjkxN3M0LjkxNy0yLjIwMSw0LjkxNy00LjkxN0MxMC44MzMsMi4yMDEsOC42MzIsMCw1LjkxNywwTDUuOTE3LDB6IiAvPgo8L2c%2BCjwvc3ZnPgo%3D');
}
#uieventDiv {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 2000
}
#uieventDialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -55%);
width: 560px;
}
#uieventHead {
margin: 10px 20px;
}
#uieventTitle {
font-weight: bold;
}
#uieventNo {
float: right;
}
#uieventYes {
display: none;
float: right;
margin-right: 15px;
}
#uieventBody {
width: 540px;
height: 540px;
position: relative;
margin-left: 10px;
margin-bottom: 5px;
overflow: hidden;
}
#uievent {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
#selectPoint {
display: none;
margin-left: 10px;
margin-bottom: 10px;
}
#selectPointFloor {
margin-right: 10px;
}
#selectPointButtons {
display: inline;
}
#selectPointBox {
position: absolute;
z-index: 75;
width: 26px;
height: 26px;
margin: 3px 0 0 3px;
padding: 0;
/* display: none; */
box-sizing: border-box;
}
/** editor_mode **/
.leftTab {
border-radius: 2px;
box-sizing: border-box;
overflow: auto;
position: absolute;
bottom: 0;
}
.leftTab > * {
margin: 2.5px 5px;
}
.leftTab > :first-child {
margin-top: 5px;
}
.leftTab > :last-child {
margin-bottom: 5px;
}
.leftTab {
left: 5px;
top: 10px;
width: 435px;
}
.leftTab .leftTabHeader {
position: fixed;
top: 15px;
left: 15px;
z-index: 20;
}
.leftTab .leftTabContent {
padding-top: 50px;
}
#appendPicSelection span {
position: absolute;
font-size: 11px;
}
#left6 {
left: 5px;
top: 5px;
z-index: 200;
position: fixed;
width: 100%;
height: 100%;
overflow: hidden;
}
#left6 #blocklyArea {
float: left;
width: 60%;
height: 95%;
}
#left6 #blocklyDiv {
position: relative;
}
#left6 .CodeMirror {
float: left;
height: 95%;
width: 35%;
}
#left6 #codeArea {
width: 99.5%;
height: 15.4em;
overflow: y; /* resize:none; */
}
#left7 {
/* height: 440px; width: 375px;float:left; */
left: 5px;
top: 5px;
z-index: 200;
position: fixed;
width: 100%;
height: 100%;
}
#left7 .CodeMirror {
/* border-top: 1px solid black;
border-bottom: 1px solid black; */
font-size: 14px;
height: 95%;
width: 95%;
}
.etable table, .etable table td {
cursor: auto;
}
/* copy from github-css https://github.com/sindresorhus/github-markdown-css */
.etable table {
border-spacing: 0;
border-collapse: collapse;
}
.etable table {
margin-top: 0;
margin-bottom: 16px;
}
.etable table {
display: block;
width: 100%;
overflow: auto;
word-break: break-all;
}
.etable table th {
font-weight: 600;
}
.etable table th,
.etable table td {
padding: 5px;
}
/* copy end --------------------------------------------- */
.etable tr {
width: 100%
}
.etable tr > :nth-child(1) {
width: 24%
}
.etable tr > :nth-child(2) {
width: 19%
}
.etable tr > :nth-child(3) {
width: 32%;
}
.etable tr > :nth-child(4) {
width: 25%;
padding: 0;
}
.etable table {
overflow: visible;
}
.etable tr:not(:first-child) > :nth-child(3) {
margin: 0;
padding: 0;
height: 100%;
position: relative;
}
.etable tr > :nth-child(4) {
text-align: center;
}
div.etableInputDiv {
position: absolute;
padding: 5px 0 0 5px;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
div.checkboxSet {
position: relative !important;
max-height: 250px;
overflow: auto;
}
.etableInputDiv > * {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
border: none;
background: transparent;
}
.etableInputDiv .checkbox {
width: 16px;
height: 16px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -8px;
margin-top: -8px;
}
.etableInputDiv .checkboxSetMember {
width: 16px;
height: 16px;
display: inline-block;
}
.etableInputDiv textarea {
resize: none;
}
.etableInputDiv textarea:hover {
margin: -5px;
}
@font-face {
font-family: code;
src: url(./FiraCode.ttf);
}
.main .CodeMirror {
font-family: code, 微软雅黑, 黑体, 新宋体, Verdana;
}

View File

@ -0,0 +1,587 @@
/** 配色文件:此文件包含了整个编辑器的全部配色 **/
/** ======== 编辑器主界面相关 ======== **/
/** 全局属性 **/
body {
background-color: #F5F5F5;
}
/** 可自行仿照添加更多的全局属性...... */
/** 颜色选择器 */
#colorPanel {
background-color: #F5F5F5;
}
/** 所有的阴影效果(颜色选择器、数据区、地图区、素材区等) */
#colorPanel, #left, #mid, #mid2, #right, .leftTab {
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
/** 地图编辑的输入框 */
#pout {
border: 1px solid #ddd;
}
/** 中间的消息提示框 */
#tip {
border: 1px solid #ccc;
}
/** 素材区背景 */
#iconLib, #mapEdit {
background-color: #F5F5F5;
}
/** 选中素材区时的方框 */
#dataSelection, .appendSelection, #selectPointBox {
background-color: rgba(255, 255, 255, 0.0);
border: 1px solid #000;
box-shadow: 0 0 0 2px #fff, 0 0 0 3px #000;
}
/** 错误消息(如保存失败) */
.warnText {
color: #D00;
}
/** 提示消息(如告知当前图块信息) */
.infoText {
color: #39F;
}
/** 成功消息(如保存成功) */
.successText {
color: #098;
}
/** 数据库的表格相关 */
table, td {
border: 1px solid #fff;
color: #fff;
}
.coltd, .rowtd {
border: 0.230vw solid #fff;
color: #fff;
}
/** 横向标尺背景色 */
table.col td, div.col .coltd {
background-color: #398;
}
/** 横向标尺选中时的背景色 */
#mapColMark td:hover .colBlock, #mapColMark .coltd:hover .colBlock {
background-color: rgba(85, 204, 187, 0.5);
}
/** 纵向标尺背景色 */
table.row td, div.row .rowtd .rowtext {
background-color: #379;
}
/** 纵向标尺选中时的背景色 */
#mapRowMark td:hover .rowBlock, #mapRowMark .rowtd:hover .rowBlock {
background-color: rgba(102, 170, 204, 0.5);
}
/** 右键菜单栏 */
#midMenu{
color: #000;
background-color: #fff;
border-color: #ccc #666 #666 #ccc;
}
#mid .tools {
border-top: 1px solid #ccc;
}
/** 右键菜单栏的当前选中项 */
#midMenu .menuitem:hover{
background-color: #cef;
border-color: #bde;
}
/** 禁用的输入方框如ID的背景色 */
textarea[disabled] {
color: #999999;
}
/** 搜索事件块的输入框 */
#searchBlock {
background-color: #eeeef4;
}
/** 打开浮层(变量搜索等)时页面背景 */
#uieventDiv {
background: rgba(128,128,128,0.6);
}
/** 浮层的对话框 */
#uieventDialog {
background: white;
}
/** 追加素材时的编号标记 */
#appendPicSelection span {
-webkit-text-stroke: 1px red;
text-shadow: black 1px 0, black 0 1px, black -1px 0, black 0 -1px;
}
.leftTab {
background-color: #F5F5F5;
}
/** 整个事件编辑器 */
#left6 {
background-color: rgb(245, 245, 245);
}
/** 事件编辑器右边的文本框 */
#left6 .CodeMirror {
border: 1px solid #eee;
}
/** 脚本编辑器 */
#left7 {
background-color: rgb(245, 245, 245);
}
#left7 .CodeMirror {
border: 1px solid #eee;
}
/** 数据区的表格 */
.etable table, .etable table td {
color: #000;
}
/** 数据区每行效果2n代表偶数行以此类推 */
.etable table tr {
background-color: #fff;
border-top: 1px solid #ccccd4;
}
.etable table tr:nth-child(2n) {
background-color: #fafafa;
}
/** 数据区表格每列效果 */
.etable table th, .etable table td {
border: 1px solid #ddd;
}
/** 鼠标悬停在数据区表格上的边框效果 */
.etable tr:not(:first-child) > :nth-child(3):hover {
border: 1px solid rgb(87, 198, 232);
box-shadow: 0px 0px 3px rgb(87, 198, 232);
}
.etable tr:not(:first-child) > :nth-child(2):hover,
.etable tr:not(:first-child) > :nth-child(1):hover {
border: 1px solid rgb(87, 232, 198);
box-shadow: 0px 0px 3px rgb(87, 232, 198);
}
/** 表格编辑的按钮颜色 */
.editorTableEditBtn {
background-color: #ddf8ff;
border-color: #bbc9cc;
color: #445560;
}
.editorTableFoldBtn {
background-color: #fff4bb;
border-color: #d4ccbb;
color: #605544;
}
/** 保存地图的按钮颜色 */
#saveFloor.highlight {
background-color: #ffd700;
border-color: #aa9944;
color: #430;
}
/** 解析按钮颜色 */
#blocklyParse.highlight {
background-color: #ffd700;
border-color: #aa9944;
color: #430;
}
/** 大地图按钮颜色 */
#bigmapBtn.highlight {
background-color: #ffd700;
border-color: #aa9944;
color: #430;
}
.popCheckboxItem {
color: black;
}
table.col td.highlight {
background-color: #77ddcc;
}
table.row td.highlight {
background-color: #88bbdd;
}
/** ======== blockly 相关 ======== */
/*魔改*/
.blocklyTreeLabel {
color: #0f0f0f !important;
}
.blocklyTreeLabel:hover {
/*background-color: #888 !important;*/
color: #0f0f0f !important;
}
.blocklyTreeSelected {
background-color: #5bf !important;
color: #fff !important;
}
.blocklyTreeRow {
color: #0f0f0f !important;
}
.blocklyTreeRow:hover {
background-color: #eee !important;
color: #0f0f0f !important;
}
/*魔改*/
/** 如果需要blockly深色模式请有选择性视情况取消注释下面几条具体功能自行研究 */
/*
.blocklyToolboxDiv {
background-color: #ddd !important;
color: black;
}
.blocklySvg {
background-color: #000;
}
.blocklyNonEditableText>rect, .blocklyEditableText>rect {
fill: #000;
fill-opacity: .4;
}
.blocklyNonEditableText>text, .blocklyEditableText>text {
fill: #fff;
}
input.blocklyHtmlInput {
color: white;
background-color: black;
}
*/
/** ======== Blockly中自动补全相关 ======== */
.awesomplete > ul {
background: #fff;
background: hsla(0,0%,100%,.9);
background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8));
border: 1px solid rgba(0,0,0,.3);
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
}
.awesomplete > ul:before {
background: white;
}
.awesomplete > ul > li:hover {
background: #cef/*hsl(200, 40%, 80%)*/;
color: black;
}
.awesomplete > ul > li[aria-selected="true"] {
background: #39f/*hsl(205, 40%, 40%)*/;
color: white;
}
.awesomplete mark {
background: #cef;
}
.awesomplete li:hover mark {
background: #39f;
color: white;
}
.awesomplete li[aria-selected="true"] mark {
background: #06c;
color: inherit;
}
/** ======== 脚本编辑相关 ======== **/
/* 很多我也不知道是干嘛的,自行根据名字猜每一项作用吧 */
.CodeMirror {
color: black;
background: white;
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white;
}
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
}
.CodeMirror-linenumber {
color: #999;
}
.CodeMirror-guttermarker {
color: black;
}
.CodeMirror-guttermarker-subtle {
color: #999;
}
.CodeMirror-cursor {
border-left: 1px solid black;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
background: #7e7;
}
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
}
.cm-animate-fat-cursor {
background-color: #7e7;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {
color: blue;
}
.cm-s-default .cm-quote {
color: #090;
}
.cm-negative {
color: #d44;
}
.cm-positive {
color: #292;
}
.cm-s-default .cm-keyword {
color: #819;
}
.cm-s-default .cm-atom {
color: #219;
}
.cm-s-default .cm-number {
color: #275;
}
.cm-s-default .cm-def {
color: #00f;
}
.cm-s-default .cm-variable-2 {
color: #05a;
}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #085;
}
.cm-s-default .cm-comment {
color: #960;
}
.cm-s-default .cm-string {
color: #a11;
}
.cm-s-default .cm-string-2 {
color: #f50;
}
.cm-s-default .cm-meta {
color: #555;
}
.cm-s-default .cm-qualifier {
color: #555;
}
.cm-s-default .cm-builtin {
color: #30a;
}
.cm-s-default .cm-bracket {
color: #997;
}
.cm-s-default .cm-tag {
color: #170;
}
.cm-s-default .cm-attribute {
color: #00c;
}
.cm-s-default .cm-hr {
color: #999;
}
.cm-s-default .cm-link {
color: #00c;
}
.cm-s-default .cm-error {
color: #f00;
}
.cm-invalidchar {
color: #f00;
}
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {
color: #0b0;
}
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #a22;
}
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, .3);
}
.CodeMirror-activeline-background {
background: #e8f2ff;
}
.CodeMirror-selected {
background: #d9d9d9;
}
.CodeMirror-focused .CodeMirror-selected {
background: #d7d4f0;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #d7d4f0;
}
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, .4);
}
/* 代码补全提示相关 */
.cm-matchhighlight {
background-color: #dedede
}
.CodeMirror-hints {
border: 1px solid silver;
background: white;
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
}
.CodeMirror-hint {
color: black;
}
li.CodeMirror-hint-active {
background: #08f;
color: white;
}
/* 代码语法检查相关相关 */
.CodeMirror-lint-tooltip {
background-color: #ffd;
border: 1px solid black;
color: black;
}
.CodeMirror-lint-mark-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==");
}
.CodeMirror-lint-mark-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-multiple {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
}
/* 代码补全的对话框(提供搜索功能)相关 */
.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
}
.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
}
/* 搜索项的背景色 */
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
.cm-matchhighlight {background-color: lightgreen}
.CodeMirror-selection-highlight-scrollbar {background-color: yellowgreen}
.CodeMirror-search-hint {
color: #888;
}
/* 增强的代码补全系统相关 */
/* 变量类型 */
.CodeMirror-Tern-completion:before {
color: white;
}
.CodeMirror-Tern-completion-unknown:before {
content: "?";
background: #4bb;
}
.CodeMirror-Tern-completion-object:before {
content: "O";
background: #77c;
}
.CodeMirror-Tern-completion-fn:before {
content: "F";
background: #7c7;
}
.CodeMirror-Tern-completion-array:before {
content: "A";
background: #c66;
}
.CodeMirror-Tern-completion-number:before {
content: "1";
background: #999;
}
.CodeMirror-Tern-completion-string:before {
content: "S";
background: #999;
}
.CodeMirror-Tern-completion-bool:before {
content: "B";
background: #999;
}
.CodeMirror-Tern-completion-guess {
color: #999;
}
/* 额外提示框(如文档) */
.CodeMirror-Tern-tooltip {
border: 1px solid silver;
color: #444;
background-color: white;
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
}
.CodeMirror-Tern-fname { color: black; }
.CodeMirror-Tern-farg { color: #70a; }
.CodeMirror-Tern-type { color: #07c; }
.CodeMirror-foldmarker {
color: blue;
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
}

View File

@ -0,0 +1,756 @@
/** 配色文件:此文件包含了整个编辑器的全部配色 **/
/** ======== 编辑器主界面相关 ======== **/
/** 全局属性 **/
body {
background-color: #242424;
color: #bbb;
filter: brightness(0.825);
}
input {
background-color: #404048;
color: #c0c0c0;
}
button {
background-color: #404048;
border-color: #aaa;
color: #c0c0c0;
}
/** 可自行仿照添加更多的全局属性...... */
/** 颜色选择器 */
#colorPanel {
background-color: #333;
}
/** 所有的阴影效果(颜色选择器、数据区、地图区、素材区等) */
#colorPanel, #left, #mid, #mid2, #right, .leftTab {
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
/** 地图编辑的输入框 */
#pout {
border: 1px solid #ddd;
}
/** 中间的消息提示框 */
#tip {
border: 1px solid #ccc;
}
/** 素材区背景 */
#iconLib, #mapEdit {
background-color: #242424;
}
/** 选中素材区时的方框 */
#dataSelection, .appendSelection, #selectPointBox {
background-color: rgba(255, 255, 255, 0.0);
border: 1px solid #aaa;
box-shadow: 0 0 0 2px #ddd, 0 0 0 3px #aaa;
}
/** 错误消息(如保存失败) */
.warnText {
color: #e44;
}
/** 提示消息(如告知当前图块信息) */
.infoText {
color: #7bd;
}
/** 成功消息(如保存成功) */
.successText {
color: #8ca;
}
/** 数据库的表格相关 */
table, td {
border: 1px solid #777;
color: #777;
}
.coltd, .rowtd {
border: 0.230vw solid #777;
color: #777;
}
/** 横向标尺背景色 */
table.col td, div.col .coltd {
background-color: #287;
border-color: #ccc;
color: #ccc;
}
/** 横向标尺选中时的背景色 */
#mapColMark td:hover .colBlock, #mapColMark .coltd:hover .colBlock {
background-color: rgba(68, 187, 170, 0.33);
}
/** 纵向标尺背景色 */
table.row td, div.row .rowtd .rowtext {
background-color: #268;
border-color: #ccc;
color: #ccc;
}
/** 纵向标尺选中时的背景色 */
#mapRowMark td:hover .rowBlock, #mapRowMark .rowtd:hover .rowBlock {
background-color: rgba(85, 153, 187, 0.33);
}
/** 右键菜单栏 */
#midMenu{
color: #bbb;/*文字颜色*/
background-color: #33333c;
border-color: #888 #666 #666 #888;
}
#mid .tools {
border-top: 1px solid #555;
}
/** 右键菜单栏的当前选中项 */
#midMenu .menuitem:hover{
background-color: #247;
border-color: #469;
color: #fff;
}
/*===============================自己加的东西======================================*/
select {
background-color: #33333c;
}
option {
background-color: #33333c;
}
a {
color: #7bd;
}
textarea {
color: #bbb;
}
::-webkit-scrollbar-track
{
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
border-radius: 8px;
background-color: #242424;
}
::-webkit-scrollbar
{
width: 8px;
height: 10px;
background-color: #242424;
}
::-webkit-scrollbar-thumb
{
border-radius: 8px;
-webkit-box-shadow: inset 0 0 3px rgba(0,0,0,.3);
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: #bbb;
}
/** 禁用的输入方框如ID的背景色 */
textarea[disabled] {
color: #888;
}
select {
color: #ccc;
background-color: #33333c;
}
option {
filter: brightness(0.8);
background-color: #292930;
color: #9a9a9a;
}
option:checked {
filter: brightness(0.8);
background-color: #555/*#358*/;
color: #ddd;
}
.gameCanvas, img {
filter: brightness(0.85);
}
/** 搜索事件块的输入框 */
#searchBlock {
background-color: #bbb;
color: #444;
}
/** 打开浮层(变量搜索等)时页面背景 */
#uieventDiv {
background: rgba(85,85,85,0.67);
}
/** 浮层的对话框 */
#uieventDialog {
background: #101010;
color: #ccc;
}
/** 追加素材时的编号标记 */
#appendPicSelection span {
-webkit-text-stroke: 1px gold;
text-shadow: black 1px 0, black 0 1px, black -1px 0, black 0 -1px;
}
.leftTab {
background-color: #242424;
}
/** 整个事件编辑器 */
#left6 {
background-color: rgb(36, 36, 36);
}
/** 事件编辑器右边的文本框 */
#left6 .CodeMirror {
/*border: 1px solid #eee;*/
border: 1px solid #666;
background-color: #242424;
/*color: #ccc;*/
}
/** 脚本编辑器 */
#left7 {
background-color: rgb(36, 36, 36);
}
#left7 .CodeMirror {
border: 1px solid #666;
}
/** 数据区的表格 */
.etable table, .etable table td {
color: #bbb;
}
/** 数据区每行效果2n代表偶数行以此类推 */
.etable table tr {
background-color: #3c3c3c;
border-top: 1px solid #ccccd4;
}
.etable table tr:nth-child(2n) {
background-color: #343434;
}
/** 数据区表格每列效果 */
.etable table th, .etable table td {
border: 1px solid #777;
}
/** 鼠标悬停在数据区表格上的边框效果 */
.etable tr:not(:first-child) > :nth-child(3):hover {
border: 1px solid #468;
box-shadow: 0px 0px 3px #468;
}
.etable tr:not(:first-child) > :nth-child(2):hover,
.etable tr:not(:first-child) > :nth-child(1):hover {
border: 1px solid #375;
box-shadow: 0px 0px 3px #375;
}
/** 表格编辑的按钮颜色 */
.editorTableEditBtn {
background-color: #389;
border-color: #bde;/*8cf和fd8*/
color: #cef;
}
.editorTableFoldBtn {
background-color: #a83;
border-color: #edb;
color: #fec;
filter: brightness(0.95);
}
/** 保存地图的按钮颜色 */
#saveFloor {
background-color: #234;/*33333c*/
border-color: #abc;
color: #cdf;
}
#saveFloor.highlight {
background-color: #871;
border-color: #eda;
color: #fff4d0;
}
/** 解析按钮颜色 */
#blocklyParse {
background-color: #404048;
color: #c0c0c0;
}
#blocklyParse.highlight {
background-color: #871;
border-color: #eda;
color: #fff4d0;
}
#bigmapBtn {
background-color: #234;/*33333c*/
border-color: #abc;
color: #cdf;
}
#bigmapBtn.highlight {
background-color: #871;
border-color: #eda;
color: #fff4d0;
}
.popCheckboxItem {
color: #bbbbbb;
}
table.col td.highlight {
background-color: #66ccbb;
}
table.row td.highlight {
background-color: #77aacc;
}
/** ======== blockly 相关 ======== */
/**
blockly图块的的黑暗程度用opacity表示0到1之间
0表示表示纯亮色1表示纯黑色
*/
/** 如果需要blockly深色模式请有选择性视情况取消注释下面几条具体功能自行研究 */
/*somethingSOMETHING*/
/*.blocklyTreeLabel {
color: #ccc;
}*/
.blocklyTreeLabel:hover {
/*background-color: #888 !important;*/
color: #fff !important;
}
.blocklyTreeSelected {
background-color: #358 !important;
color: #fff !important;
}
/*.blocklyTreeSelected .blocklyTreeLabel {
background-color: #358 !important;
background: #358 !important;
color: #fff !important;
}*/
.blocklyTreeRow {
/*background-color: #888 !important;*/
color: #ccc !important;
}
.blocklyTreeRow:hover {
background-color: #666 !important;
color: #fff !important;
}
/*
.blocklyFlyout {
background-color: rgba(0,0,0,1);
}
*/
.blocklyFlyoutBackground {
fill: #666;
fill-opacity: 0.67;
}
/*somethingSOMETHING*/
.blocklyToolboxDiv {
background-color: #2a2a2a !important;
border: 1px solid #666;
color: black;
}
.blocklySvg {
background-color: #242424;
}
.blocklyNonEditableText>rect, .blocklyEditableText>rect {
fill: #000;
fill-opacity: .3;
}
.blocklyNonEditableText>text, .blocklyEditableText>text {
fill: #fff;
}
input.blocklyHtmlInput {
color: white;
background-color: black;
}
.blocklyFieldRect:not(.blocklyColourFieldRect) {
fill: #242424 !important;
}
.blocklyEditableText > .blocklyText {
fill: #eee !important;
}
.blocklyHtmlInput {
background: rgba(36,36,36,0.6);
color: #eee;
}
.blocklyDropDownDiv {
background-color: #33333c;
border: 1px solid rgba(0,0,0,.3);
border-color: #666;
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
}
.blocklyDropDownDiv .goog-menuitem-content {
color: #eee;
}
.blocklyDropDownDiv .goog-menuitem-highlight {
background: #247/*hsl(200, 40%, 80%)*/;
color: #eee;
}
.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,
.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon,
.blocklyDropDownDiv .goog-option-selected .goog-menuitem-checkbox,
.blocklyDropDownDiv .goog-option-selected .goog-menuitem-icon {
background: url(../blockly/media/sprites_white.png) no-repeat -48px -16px;
}
/** ======== Blockly中自动补全相关 ======== */
.awesomplete > ul {
background: #33333c;
/*background: hsla(0,0%,100%,.9);
background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8));*/
border: 1px solid rgba(0,0,0,.3);
border-color: #666;
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
}
.awesomplete > ul:before {
background: white;
}
.awesomplete > ul > li:hover {
background: #247/*hsl(200, 40%, 80%)*/;
color: #fff;
}
.awesomplete > ul > li[aria-selected="true"] {
background: hsl(205, 40%, 40%);
color: white;
}
.awesomplete mark {
background: /*hsl(65, 100%, 50%);*/#278;
color: #cef;
}
.awesomplete li:hover mark {
background: /*hsl(68, 100%, 41%)*/#1a9;
color: #fff;
}
.awesomplete li[aria-selected="true"] mark {
background: /*hsl(86, 100%, 21%)*/#468;
color: inherit;
}
/** ======== 脚本编辑相关 ======== **/
/* 很多我也不知道是干嘛的,自行根据名字猜每一项作用吧 */
.CodeMirror {
color: #ddd;/*杨子默:我也不知道他是干啥用的*/
background: #242424;
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: #242424;
}
.CodeMirror-gutters {
border-right: 1px solid #666;
background-color: #2a2a2a;
}
.CodeMirror-linenumber {
color: #999;
}
.CodeMirror-guttermarker {
color: black;
}
.CodeMirror-guttermarker-subtle {
color: #999;
}
.CodeMirror-cursor {
border-left: 1px solid #f8f8f0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
background: #7e7;
}
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
}
.cm-animate-fat-cursor {
background-color: #7e7;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {
color: #a8f;
}
.cm-s-default .cm-quote {
color: #695;
}
.cm-negative {
color: #f55;
}
.cm-positive {
color: #5f5;
}
.cm-s-default .cm-keyword {
color: #d7a;
}
.cm-s-default .cm-atom {
color: #80c0ff;
}
.cm-s-default .cm-number {
color: #a0e0c0;
}
.cm-s-default .cm-def {
color: #db7;
}
.cm-s-default .cm-variable-2 {
color: #8cd;
}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #8cd;
}
.cm-s-default .cm-comment {
color: #7a6;
}
.cm-s-default .cm-string {
color: #db7;
}
.cm-s-default .cm-string-2 {
color: #db7/*something?*/;
}
.cm-s-default .cm-meta {
color: #33333c;
}
.cm-s-default .cm-qualifier {
color: #33333c;
}
.cm-s-default .cm-builtin {
color: #66d9ef;
}
.cm-s-default .cm-bracket {
color: #f8f8f2;
}
.cm-s-default .cm-tag {
color: #f55;
}
.cm-s-default .cm-attribute {
color: #ab6;
}
.cm-s-default .cm-hr {
color: #999;
}
.cm-s-default .cm-link {
color: #a8f;
}
.cm-s-default .cm-error {
background: #f55;
color: #fff;
}
/*somethingsomething*/
.cm-s-default .cm-matchhighlight {
background: #57a;
color: #eef8ff;
}
/*somethingsomething*/
.cm-invalidchar {
color: #f55;
}
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {
color: #5f5;
}
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #f55;
}
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, .3);
}
.CodeMirror-activeline-background {
background: #247;
}
.CodeMirror-selected {
background: #358;
}
.CodeMirror-focused .CodeMirror-selected {
background: #358;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #247;
}
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #247;
}
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, .4);
}
/* 代码补全提示相关 */
.cm-matchhighlight {
background-color: #247;
}
.CodeMirror-hints {
border: 1px solid #888;
background: #33333c;
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
}
.CodeMirror-hint {
color: #ccc;
}
li.CodeMirror-hint-active {
background: #247;
color: #fff;
}
/* 代码语法检查相关相关 */
.CodeMirror-lint-tooltip {
background-color: #247;
border: 1px solid #135;
color: #fff;
}
.CodeMirror-lint-mark-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==");
}
.CodeMirror-lint-mark-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-multiple {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
}
/* 代码补全的对话框(提供搜索功能)相关 */
.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
}
.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
}
/* 搜索项的背景色 */
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
.cm-matchhighlight {background-color: lightgreen}
.CodeMirror-selection-highlight-scrollbar {background-color: yellowgreen}
.CodeMirror-search-hint {
color: #888;
}
/* 增强的代码补全系统相关 */
/* 变量类型 */
.CodeMirror-Tern-completion:before {
color: white;
}
.CodeMirror-Tern-completion-unknown:before {
content: "?";
background: #4bb;
}
.CodeMirror-Tern-completion-object:before {
content: "O";
background: #77c;
}
.CodeMirror-Tern-completion-fn:before {
content: "F";
background: #7c7;
}
.CodeMirror-Tern-completion-array:before {
content: "A";
background: #c66;
}
.CodeMirror-Tern-completion-number:before {
content: "1";
background: #999;
}
.CodeMirror-Tern-completion-string:before {
content: "S";
background: #999;
}
.CodeMirror-Tern-completion-bool:before {
content: "B";
background: #999;
}
.CodeMirror-Tern-completion-guess {
color: #999;
}
/* 额外提示框(如文档) */
.CodeMirror-Tern-tooltip {
border: 1px solid #aaa;
color: #ccc;
background-color: #33333c;
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
}
.CodeMirror-Tern-fname { color: #ddd; }
.CodeMirror-Tern-farg { color: #d7a; }
.CodeMirror-Tern-type { color: #db7; }
.CodeMirror-foldmarker {
color: #ddd;
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
}

View File

@ -0,0 +1,720 @@
/** editor **/
:root {
--size: 13;
}
html{
font-size: 4vw;
}
input, textarea, select, button {
font-size: 1rem;
}
html, body, div, img {
margin: 0;
padding: 0;
}
body {
font-family: Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;;
}
/* ::-webkit-scrollbar {
width: 5px;
} */
.main {
max-width: 100%;
min-height: 500px;
margin: 0 auto;
}
#left, #mid, #right {
/* border-radius: 2px;
box-sizing: border-box;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); */
width: 100vw;
height: 100vw;
}
#left {
position: absolute;
}
#mapEditArea {
position: absolute;
width: 100%;
height: 70%;
left: 0;
top: 0;
/* padding: 10px 5px; */
box-sizing: border-box;
display: none;
}
#pout {
display: block;
width: 95vw;
height: 62vw;
box-sizing: border-box;
margin-left: 5vw;
margin-top: 23px;
line-height: 5vw;
font-size: 12px;
font-family: 'Lucida Console', Monaco, monospace;
white-space: pre;
border-radius: 2px;
overflow: auto;
}
#mapEditArea p {
margin: 10px;
display: block;
width: 70%;
line-height: 20px;
text-align: left;
font-size: 14px;
}
#editTip {
position: absolute;
width: 95vw;
left: 4vw;
top: 4vw;
}
#editTip .btn {
float: right;
margin-right: 20px;
margin-top: 5px;
}
#editBtns {
position: absolute;
width: 95vw;
left: 4vw;
top: 20vw;
}
#newMaps {
position: absolute;
left: 4vw;
top: 35vw;
}
#newFloors {
position: absolute;
width: 95vw;
left: 4vw;
top: 43vw;
}
#mid {
position: absolute;
}
#mid2 {
display: none;
}
#mapEdit {
overflow: hidden;
}
.map {
position: absolute;
left: 4vw;
top: 4vw;
width: 96vw;
height: 96vw;
}
.map canvas{
width: 96vw;
height: 96vw;
}
#mid .tools {
position: absolute;
width: 425px;
height: 180px;
left: 0;
bottom: 0;
padding: 10px 5px;
margin-left: 8px;;
box-sizing: border-box;
}
#tip {
float: right;
width: 45%;
/* height: 95%; */
min-height: 9rem;
padding: 5px 10px 10px 10px;
margin-right: 0;
box-sizing: border-box;
border-radius: 2px;
font-size: 1rem;
line-height: 1.2rem;
}
#tip p{
margin: 0.5rem
}
.files {
width: 50%;
height: 120px;
/* padding: 10px; */
margin-top: 15px;
}
#bgSelect {
width: 50%;
height: 100px;
margin-top: 10px;
}
#bgSelect span {
/* display: block; */
font-size: 14px;
line-height: 30px;
}
#printOut {
margin-top: 10px;
height: 20px;
}
#right {
position: absolute;
/* border: 1px solid rgb(238, 13, 13); */
}
#iconLib {
position: absolute;
width: 100vw;
height: 100vw;
top: 5px;
overflow: auto;
}
#iconImages {
z-index: 0;
overflow: hidden;
}
#iconImages img {
position: absolute;
}
.gameCanvas {
position: absolute;
}
#dataSelection, .appendSelection {
position: absolute;
/* top:0;
left:320px; */
z-index: 75;
width: 26px;
height: 26px;
margin: 3px 0 0 3px;
padding: 0;
/* display: none; */
box-sizing: border-box;
}
#iconExpandBtn {
position: absolute;
left: 20px;
bottom: 30px;
font-size: 15px;
padding: 6px;
display: none;
}
.warnText {
font-weight: 700;
font-size: 14px;
line-height: 1.2em;
}
.successText {
line-height: 1.2em;
}
td, .coltd, .rowtd {
cursor: crosshair;
}
table.col, div.col {
position: relative;
text-align: center;
border-collapse: collapse;
}
#arrColMark td {
width: 16px;
}
#arrColMark {
top: 2px;
left: 5vw;
width: 95vw;
height: 16px;
font-size: 13px;
display: none;
}
#mapColMark {
position: absolute;
top: 0;
left: 4vw;
width: 96vw;
height: 4vw;
font-size: 3vw;
}
#mapColMark .coltd,
#mapColMark .coltd .coltext {
position: absolute;
width: calc(93vw / var(--size));
height: 4vw;
line-height: 4vw;
padding: 0;
border-bottom-width: 0px;
border-top-width: 0px;
border-left-width: calc(1.5vw / var(--size));
border-right-width: calc(1.5vw / var(--size));
}
#mapColMark .coltd:hover .colBlock {
position: absolute;
top: 4vw;
left:0;
height: 96vw;
width: calc(96vw / var(--size));
z-index: 100;
}
table.row,div.row,
table.row,div.row .rowtext {
width:4vw;
position: relative;
text-align: right;
vertical-align: middle;
border-collapse: collapse;
}
#arrRowMark {
top: 5px;
left: 0;
width: 4vw;
height: 62vw;
font-size: 12px;
display: none;
}
#mapRowMark {
position: absolute;
top: 4vw;
left: 0;
width: 4vw;
height: 96vw;
font-size: 3vw;
}
#mapRowMark .rowtd{
position: absolute;
}
#mapRowMark .rowtd,
#mapRowMark .rowtd .rowtext{
height: calc(93vw / var(--size));
line-height: calc(93vw / var(--size));
padding: 0;
border-left-width: 0px;
border-right-width: 0px;
border-top-width: calc(1.5vw / var(--size));
border-bottom-width: calc(1.5vw / var(--size));
}
#mapRowMark .rowtd:hover .rowBlock {
position: absolute;
left: 4vw;
top: 0;
height: calc(96vw / var(--size));
width: 96vw;
z-index: 100;
}
#menuDiv * { margin: 0; padding: 0; }
#midMenu{
border-style: solid;
border-width: 1px;
cursor: default;
font: normal 2.5vw Arial, sans-serif;
margin: 0;
outline: none;
padding: 4px 0;
overflow-y: auto;
overflow-x: hidden;
max-height: 100%;
max-width: 40%;
z-index: 101;
border-radius: 4px;
}
#midMenu .menuitem{
font: normal 2.5vw Arial, sans-serif;
list-style: none;
margin: 0;
padding: 4px 7em 4px 4px;
white-space: nowrap;
/* padding-left: 12px; */
/* padding-right: 20px; */
}
#midMenu .menuitem:hover{
border-style: dotted;
border-width: 1px 0;
padding-bottom: 3px;
padding-top: 3px;
}
#midMenu .menuitem-content{
font: normal 2.5vw Arial, sans-serif;
}
#down{
position: absolute;
left:0;
top:100vw;
bottom: 0;
width: 100vw;
}
#colorPanel {
position: fixed;
width: max-content;
height: 205px;
z-index: 240;
padding: 4px 6px;
margin-top: 6px;
box-sizing: border-box;
}
#colorPicker {
margin: 2px 0;
border-radius: 3px;
width: 90px;
}
#uieventDiv {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 2000
}
#uieventDialog {
position: fixed;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100vw;
}
#uieventHead {
margin: 10px 20px;
}
#uieventTitle {
font-weight: bold;
}
#uieventNo {
float: right;
}
#uieventYes {
display: none;
float: right;
margin-right: 15px;
}
#uieventBody {
width: 100vw;
height: 100vw;
position: relative;
margin-left: 0;
margin-bottom: 5px;
overflow: hidden;
}
#uievent {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
#selectPoint {
display: none;
margin-left: 10px;
margin-bottom: 10px;
}
#selectPointFloor {
margin-right: 10px;
}
#selectPointButtons {
display: inline;
}
#selectPointBox {
position: absolute;
z-index: 75;
width: 26px;
height: 26px;
margin: 3px 0 0 3px;
padding: 0;
/* display: none; */
box-sizing: border-box;
}
/** editor_mode **/
.leftTab {
width: 100vw;
height: 100vw;
}
.leftTab {
overflow: auto;
position: absolute;
height: 100vw;
}
.leftTab > * {
margin: 2.5px 5px;
}
.leftTab > :first-child {
margin-top: 5px;
}
.leftTab > :last-child {
margin-bottom: 5px;
}
.leftTab {
left: 0;
top: 0;
width: 100vw;
}
.leftTab .leftTabHeader {
position: fixed;
top: 15px;
left: 15px;
}
.leftTab .leftTabContent {
padding-top: 50px;
}
#appendPicSelection span {
position: absolute;
font-size: 11px;
}
#left6 {
left: 0;
top: 0;
z-index: 200;
position: fixed;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
#left6 h3{
font-size: 6vw;
margin: 1.77vw 0;
}
#left6 #blocklyArea {
width: 100vw;
height: 100vw;
}
#left6 #blocklyDiv {
position: relative;
}
#left6 .CodeMirror {
height: 38vw;
width: 100%;
}
#left6 #codeArea {
width: 99.5%;
height: 15.4em;
overflow: y; /* resize:none; */
}
#left6 #blocklyDiv .blocklyToolboxDiv{
width:6vw;
}
#left6 #blocklyDiv .blocklyTreeLabel{
margin-left:-4vw;
}
#left7 {
/* height: 440px; width: 375px;float:left; */
left: 0;
top: 0;
z-index: 200;
position: fixed;
width: 100%;
height: 100%;
}
#left7 .CodeMirror {
font-size: 14px;
height: 95%;
width: 95%;
}
.etable table,
.etable table td {
cursor: auto;
}
/* copy from github-css https://github.com/sindresorhus/github-markdown-css */
.etable table {
border-spacing: 0;
border-collapse: collapse;
}
.etable table {
margin-top: 0;
margin-bottom: 16px;
}
.etable table {
display: block;
width: 100%;
overflow: auto;
word-break: break-all;
}
.etable table th {
font-weight: 600;
}
.etable table th,
.etable table td {
padding: 5px;
}
/* copy end --------------------------------------------- */
.etable tr {
width: 100%
}
.etable tr > :nth-child(1) {
width: 20%
}
.etable tr > :nth-child(2) {
width: 20%
}
.etable tr > :nth-child(3) {
width: 38%;
}
.etable tr > :nth-child(4) {
width: 22%;
text-align: center;
}
.etable table {
overflow: visible;
}
.etable tr:not(:first-child) > :nth-child(3) {
margin: 0;
padding: 0;
height: 100%;
position: relative;
}
div.etableInputDiv {
position: absolute;
padding: 5px 0 0 5px;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
div.checkboxSet {
position: relative !important;
max-height: 250px;
overflow: auto;
}
.etableInputDiv > * {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
border: none;
background: transparent;
}
.etableInputDiv .checkbox {
width: 16px;
height: 16px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -8px;
margin-top: -8px;
}
.etableInputDiv .checkboxSetMember {
width: 16px;
height: 16px;
display: inline-block;
}
.etableInputDiv textarea {
resize: none;
}
.etableInputDiv textarea:hover {
margin: -5px;
}
@font-face {
font-family: Fira;
src: url(./FiraCode.ttf);
}
.main .CodeMirror {
font-family: Fira, Menlo, Consolas, 'Courier New', Courier, monospace;
}

1054
_server/editor.js Normal file

File diff suppressed because it is too large Load Diff

1213
_server/editor_blockly.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 = {
'入口方块':[
'<label text="入口方块会根据当前类型在此数组中筛选,具体控制在editor_blockly.entranceCategoryCallback中"></label>',
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(),
],
'常见事件模板':[
'<label text="检测音乐如果没有开启则系统提示开启"></label>',
MotaActionFunctions.actionParser.parseList({"type": "if", "condition": "!core.musicStatus.bgmStatus",
"true": [
"\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳"
],
"false": []
}),
'<label text="仿新新魔塔一次性商人"></label>',
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'),
'<label text="全地图选中一个点"></label>',
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'),
'<label text="多阶段Boss战斗"></label>',
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'),
],
'最近使用事件':[
'<label text="此处只是占位符,实际定义在editor_blockly.searchBlockCategoryCallback中"></label>',
]
}
var toolboxgap = '<sep gap="5"></sep>'
//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('<block type="'+eventType+'">').exec(blockText)){
var block = Blockly.Xml.textToDom('<xml>'+blockText+'</xml>').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 = '<xml>' +
MotaActionBlocks[labels[i]].xmlText() +
'</xml>';
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转义过一次了,所以这里要覆盖掉以避免在注释中出现&lt;等
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('</'+inputType+'>');
}
if(comment){
xmlText.push('<comment>');
xmlText.push(comment);
xmlText.push('</comment>');
}
var next = inputs[rule.args.length];
if (next) {//next
xmlText.push('<next>');
xmlText.push(next);
xmlText.push('</next>');
}
xmlText.push('</'+blocktext+'>');
return xmlText.join('');
}
})();
// end mark sfergsvae
}).toString().split('// start mark sfergsvae')[1].split('// end mark sfergsvae')[0]

Some files were not shown because too many files have changed in this diff Show More