diff --git a/_docs/element.md b/_docs/element.md index 30ddded8..3b622883 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -1,6 +1,5 @@ # 元件说明 - -?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.7**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 @@ -9,29 +8,22 @@ ![生成地图](./img/sample0.png) ## 道具 - -本塔目前支持的所有道具列表在样板0层中已全部给出。当你在样板0层中拿到某个宝物时会有提示,这里不再赘述,详见拿到该道具的说明。 +样板自带的所有道具在样板0层中已全部给出。当你在样板0层中拿到某个宝物时会有提示,这里不再赘述,详见拿到该道具的说明。 大多数宝物都有默认的效果,屠龙匕首暂未定义,如有自己的需求可参见[自定义道具效果](personalization#自定义道具效果)。 拿到道具后将触发`afterGetItem`事件,有关事件的详细介绍请参见[事件](event)。 如需修改某个道具的效果,在不同区域宝石数据发生变化等问题,请参见[自定义道具效果](personalization#自定义道具效果)的说明。 - -**有关轻按,在data.js的系统变量中有定义。如果`enableGentleClick`为true,则鼠标(触摸屏)通过双击勇士,键盘通过空格可达到轻按效果,即不向前移动而获得前方物品。** - +
**有关轻按,在全塔属性的系统开关中有定义。如果勾选了 `enableGentleClick` ,则鼠标(触摸屏)通过双击勇士,键盘通过空格,手机通过虚拟数字键 7 可达到轻按效果,即不移动而获得周围物品。** ## 装备 - -如果需要让剑盾等变成装备,可以直接在`data.js`中设置`'equipment': true`即可。 - +如果需要让剑盾等变成装备,可以直接在它们的图块属性中把 `cls` 一项改为 `equips` 。
从V2.4.1开始,HTML5魔塔样板终于拥有了属于自己的装备页面。 ### 装备栏的设置,装备类型 在全塔属性中,有一个`equipName`项,其定义了本塔的所有可装备的装备栏。 - -其需要是一个不小于1且不大于6的数组,其中每一项为装备栏的名称,**建议是两个汉字**。 - +
其需要是一个长度为正数且不大于 6 ( 15×15 样板为不大于 8 )的一维数组,其中每一项为装备栏的名称,**建议是两个汉字**。
例如下面这种写法就是定义了四个装备孔,名称分别为武器、防御、首饰和魔杖。 ``` js @@ -47,9 +39,8 @@ 如果要将一个道具设置为装备,首先需要将其`cls`设为`equips`。 然后在图块属性的`equip`一项中设置装备的具体属性。该项写法如下: - -``` js -{"type": 0, "atk": 0, "def": 0, "mdef":0, "animate": "hand", "percentage": true} +``` json +{"type": 0, "atk": 0, "def": 0, "mdef": 0, "animate": "hand", "percentage": true} ``` type为该装备的类型,必填,和上面装备栏一一对应。例如,0就是武器,2就是首饰等等。 @@ -63,8 +54,7 @@ animate为该装备的攻击动画,仅对type为0时有效。具体可参见[ percentage为该装备是否按比例增加属性。 下面是几个写法例子。 - -``` js +``` json {"type": 0, "atk": 10} // 装备类型是武器,效果是攻击+10,使用默认的攻击动画 {"type": 0, "atk": 40, "animate": "sword"} // 装备类型为武器,效果是攻击+10,攻击动画是sword {"type": 1, "def": 40, "percentage": true} // 装备类型是防具,效果是防御提升40% @@ -75,8 +65,8 @@ percentage为该装备是否按比例增加属性。 所有取值全部向下取整。 -值得注意的是,如果多个装备同时按比例增加属性,使用加法计算。比如武器增加30%攻击,防具增加10%攻击,最终合起来增加的是40%而不是43%的属性。 - +值得注意的是,如果多个装备同时按比例增加属性,使用加法计算。比如武器增加30%攻击,防具增加10%攻击,最终合起来增加的是40%而不是43%的属性。
+如果某件装备需要按常数增加一种属性但按百分比增加另一种属性,请参考[插件库](https://h5mota.com/plugins/) ### 检测是否存在装备 可以使用`core.hasEquip(itemId)`来检测是否装上某个装备。 @@ -92,9 +82,7 @@ percentage为该装备是否按比例增加属性。 从V2.5.4开始,允许支持多重装备,即有若干的装备可共用若干的格子(例如永不复还那样)。 要实现这一点,上面的写法有所改变。 - -在全塔属性中的`equipName`项写法不变,不过可以写重复的装备孔名称。(但仍然最多只能写6个) - +
在全塔属性中的`equipName`项写法不变,不过可以写重复的装备孔名称。(但仍然最多只能写 6 或 8 个) ``` js "equipName": ["武器", "武器", "武器", "防具", "防具", "首饰"] ``` @@ -112,95 +100,52 @@ percentage为该装备是否按比例增加属性。 装备动画仍然会取第一个(装备类型为0)的装备的`animate`项,即使装备了多个有动画的武器。 ## 门 - -本塔支持6种门,黄蓝红绿铁花。前五种门需要有对应的钥匙打开,花门只能通过调用`openDoor`事件进行打开。 - -开门后可触发该层的`afterOpenDoor`事件,有关事件的详细介绍请参见[事件](event)。 - +样板自带6种门,黄蓝红绿铁花。前五种门需要有对应的钥匙打开,花门只能通过调用 `openDoor` 事件进行打开。
+开门后可触发该点的 `afterOpenDoor` 事件,有关事件的详细介绍请参见[事件](event)。
如果要新增自己的门,请参见[新增门和对应的钥匙](personalization#新增门和对应的钥匙)。 ## 暗墙 - -本塔支持暗墙。 - -要制作一个暗墙非常简单:在该点直接放一个普通墙壁,然后事件写“开门”,坐标为该点就行。 - -``` js +样板支持暗墙。要制作一个暗墙非常简单:在该点直接放一个普通墙壁,然后事件写“开门”,坐标为该点就行。 +``` json // 该点画一个普通的墙壁,比如`yellowWall` // 在该点的事件events中: -"x,y": [ - {"type": "openDoor"} // 直接使用开门事件,坐标可忽略表示当前点 -] +[{"type": "openDoor"}] // 直接使用开门事件,坐标可忽略表示当前点 ``` - -系统会自动调用animates中的开暗墙动画。 - -目前只有如下ID支持以这种方式开门: - +系统会自动调用animates中的开暗墙动画。样板自带如下ID支持以这种方式开门: ``` text yellowDoor, blueDoor, redDoor, greenDoor, specialDoor, steelDoor, yellowWall, blueWall, whiteWall ``` ## 怪物 - -本塔支持的怪物列表参见`project/enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。 - +样板自带的怪物列表参见 `project\enemys.js` 。其与 `images\enemys.png` 素材按顺序一一对应。
如有自己的怪物素材需求请参见[自定义素材](personalization#自定义素材)的内容。 怪物可以有特殊属性,每个怪物可以有多个自定义属性。 怪物的特殊属性所对应的数字(special)在脚本编辑中的`getSpecials`中定义,请勿对已有的属性进行修改。 - -多属性可采用数组的写法,比如`'special': [1,3]`视为同时拥有先攻和坚固属性;`'special': [5,10,14,18]`视为拥有3连击、模仿、诅咒、阻击四个属性。 - -怪物可以负伤,在全塔属性的全局变量`enableNegativeDamage`中指定。 - +
多属性可直接从图块属性勾选。怪物可以负伤,由全塔属性的系统开关 `enableNegativeDamage` 指定。
打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](event#加点事件)。 - -如果全塔属性中的enableexp为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。 - -拿到幸运金币后,打怪获得的金币将翻倍。 - -如果怪物有`"notBomb": true`,则该系列诖怪物均不可被炸。 - +
如果全塔属性中的“显示经验值”未勾选,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。
+拿到幸运金币后,打怪获得的金币将翻倍。如果怪物勾选了 `notBomb` ,则这个种类的怪物均不可被炸。
N连击怪物的special是6,且我们可以为它定义n代表实际连击数。参见样板中剑王的写法。 - -吸血怪需要给怪物设置value,代表吸血的比例。 - -可以给吸血怪添加`'add': true`来将吸血的数值加到自身上。 - -中毒怪让勇士中毒后,每步扣减的生命值由`data.js`中的values定义。 - -衰弱怪让勇士衰弱后,攻防会下降一定比例或固定数值(直到衰弱状态解除恢复);其在`data.js`中的values定义。 - +
吸血怪需要给怪物设置value,代表吸血的比例。可以给吸血怪勾选 `add` 来将吸血的数值加到自身上。
但这样做以后,红色的预估伤害往往带有误导性,这点还务请注意!
+中毒怪让勇士中毒后,每步扣减的生命值由全塔属性中的 `poisonDamage` 定义。
+衰弱怪让勇士衰弱后,攻防会下降一定比例或固定数值(直到衰弱状态解除恢复);由全塔属性中的 `weakValue` 定义。
诅咒怪将让勇士陷入诅咒状态,诅咒状态下杀怪不获得金币和经验值。 - -领域怪需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。 - -领域是十字伤害还是九宫格伤害由`zoneSquare`设定,如设置为true则为九宫格伤害,不指定或为false则为十字伤害。 - +
领域怪需要需要给怪物设置 `value` ,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发 `lose` 事件。
+领域是锯齿形状还是正方形由 `zoneSquare` 设定,如勾选则为正方形,不勾选则为锯齿形状。
领域怪还可以设置`range`选项代表该领域怪的范围,不写则默认为1。 - -**将`flag:no_zone`设置为true可以取消领域效果。** - +
**将 `flags.no_zone` 设置为 `true` 可以免疫领域效果。**
阻击怪同样需要设置value,代表阻击伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。 - -**将`flag:no_snipe`设置为true可以取消阻击效果。** - -!> 阻击怪后退的地点不能有任何事件存在,即使是已经被禁用的自定义事件! - +
**将`flags.no_snipe`设置为true可以免疫阻击效果(包括伤害和移动!)。**
+!> 阻击怪后退的地点不能有任何事件存在,即使是已经被禁用的红绿事件或重生怪!(会导致此事件意外被重新启用)
激光怪同样需要设置value,代表激光伤害的数值。 - -请注意如果吸血、领域、阻击中任何两个同时存在,则value会冲突。**因此请勿将吸血、领域、阻击或激光放置在同一个怪物身上。** - -**将`flag:no_laser`设置为true可以免疫激光效果。** - +
请注意如果吸血、领域、阻击、生命光环中任何两个同时存在,则 `value` 会冲突。**因此请勿将吸血、领域、阻击或激光放置在同一个怪物身上。退化和攻防光环同理!
** +**将 `flags.no_laser` 设置为 `true` 可以免疫激光效果。将 `flags.no_ambush` 设置为 `true` 可以免疫捕捉效果。**
退化怪需要设置'atkValue'和'defValue'表示退化的数值;也可以不设置默认为0。 - -**将`flag:no_betweenAttack`设置为true可以免疫夹击效果。** - +
**将`flags.no_betweenAttack`设置为true可以免疫夹击效果,在全塔属性中还有一个系统开关可以控制夹击伤害是否封顶至夹击怪的战斗伤害。**
固伤怪则需要设置`damage`选项,代表战前扣血数值。 如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。 @@ -226,9 +171,7 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 我们可以给`E301`和`E302`怪物属性中的`displayIdInBook`项填写为`"E300"`。 这样的话,在怪物手册中,所有的E301和E302均会被视为E300并进行合并。即使只有一只朝向左的怪物(E301),怪物手册仍然会按E300进行显示。 - -从而完美解决了同种怪物不同朝向在怪物手册的显示问题。 - +
从而完美解决了同种怪物不同朝向在怪物手册的显示问题。此做法不兼容夹击属性,漏怪检测也以映射后的怪物为准。 ### NPC的朝向问题 和怪物不同的是,NPC朝向问题更复杂一点。 @@ -238,12 +181,10 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 举个例子,假设我存在一个NPC,其向上的图块ID是N333,向下的图块ID是N334,向左的图块ID是N335,不存在向右的图块ID。 则可以在这几个图块属性中的`faceIds`中写:`{"up": "N333", "down": "N334", "left": "N335"}`。 - -当勇士从左边撞上此怪物后,将从该图块的图块属性中的faceIds中寻求`left`所对应的ID。 -如果存在定义(如N335),则会在触发对话事件前改变当前图块为N335,看起来就是在对话前进行了转向,面向勇士。 - +
当勇士从左边撞上此NPC后,将从该图块的图块属性中的 `faceIds` 中寻求 `left` 所对应的ID。 +如果存在定义(如N335),则会在触发对话事件前改变当前图块为N335,看起来就是在对话前进行了转向,面向勇士。
!> 请注意,在对话结束后朝向不会切换回来,因此如果有必要切换朝向请在事件结束前调用转变图块事件。 - +
从v2.7起,也提供了“事件转向”指令来执行这种转变图块,从而省去了手动填写ID的不便。
同理,使用移动事件让NPC在行走时,不同朝向的行走会自动调用`faceIds`中不同朝向的ID所对应的行走图,看起来就是在行走时也可以不断转向了。 从而,完美解决了NPC的朝向问题(碰触时面向勇士、行走时改变朝向)。 @@ -253,23 +194,11 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 血网的伤害数值、中毒后每步伤害数值、衰弱时暂时攻防下降的数值,都在全塔属性的values内定义。 路障同样会尽量被自动寻路绕过。 - -有关楼梯和传送门,必须在该层样板的changeFloor里指定传送点的目标。 - -floorId指定的是目标楼层的唯一标识符(ID)。 - -也可以写`"floorId": ":before"`和`"floorId": ":next"`表示上一楼和下一楼。 - -后面可以写stair到upFloor或downFloor,表示将前往目标楼层的上楼梯/下楼梯位置。你也可以写loc然后指定目标点的坐标。 - -请注意的是,如果目标楼层有多个楼梯,写stair可能会导致到达的楼梯不确定,这时候请使用loc方式来指定具体的点位置。 - -可以指定direction为up/left/right/down,指定后勇士将面向该方向。 - -可以指定time,指定后切换动画时长为指定的数值。 - -**从2.1.1开始,楼层属性中提供了`upFloor`和`downFloor`两项。如果设置此项(比如`"upFloor": [2,3]`),则写stair:upFloor或者楼传器的落点将用此点来替换楼梯位置(即类似于RM中的上箭头)。** - +
有关楼梯和传送门,必须在该点的 `changeFloor` 事件里指定传送点的目标。![生成地图](./img/changeFloorBlockly.png)
+楼层ID指定的是目标楼层的唯一标识符(ID),也可以在第一个下拉框中选择“前一楼”和“后一楼”。
+第二个下拉框可以选择传送到哪个楼梯,或选择三种对称位置,或显式指定坐标(比如目标楼层有多个楼梯时)。
+“朝向”为勇士上下楼后面对的方向,不填则不改变。“动画时间”为上下楼的黑屏时间,不填则使用玩家设定值。
“穿透性”表示鼠标或触屏寻路时勇士能否穿过此传送点,在转不开身的狭窄区域建议设为可穿透。
+**从v2.1.1开始,楼层属性中提供了 `upFloor` 和 `downFloor` 两项。如果设置此项(比如`"upFloor": [2,3]`),则写传送到哪个楼梯或者楼传器的落点将用此点来替换楼梯位置(即类似于RMXP中的上箭头)。** ## 剧情文本控制与对话框效果 在写剧情文本时,可以: @@ -312,16 +241,12 @@ floorId指定的是目标楼层的唯一标识符(ID)。 从V2.4开始,H5魔塔开始支持大地图。 大地图在创建时可以指定宽高,要求**宽和高都不得小于13(15x15版本则是不小于15),且宽高之积不超过1000**。 - -大地图一旦创建成功则不得修改宽高数值。 - +
大地图一旦创建成功则难以修改宽高数值,如确需修改,请手动打开 `floors` 文件夹中的 `JS` 文件并同时修改宽高和地图矩阵。 ## 动画和天气系统 现在我们的H5魔塔支持播放动画,也支持天气系统了。 - -要播放动画,你需要先使用“RM动画导出器”将动画导出,放在animates目录下,然后在全塔属性的animates中定义。 - -``` js +
要播放动画,你可以先使用“RM动画导出器”将动画导出,放在animates目录下,然后在全塔属性的animates中定义。 +``` json // 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 // 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 "animates": ["hand", "sword", "zone", "yongchang", "thunder"] @@ -332,18 +257,15 @@ floorId指定的是目标楼层的唯一标识符(ID)。 导出动画时可能会进行一些压缩以节省流量,因此清晰度可能不如原版。 从2.3.2开始,动画可以同时导出所用的音效。**如果导出音效,请确保将所用到的音效复制到了`sounds`目录下,并且在全塔属性中注册过。** +
确切地说,动画文件中记录着第几帧该用什么音效的文件名,您可以用记事本等文本编辑器打开动画文件并手动修改用到的音效文件名(原本可能是中文甚至日文),以和实际的文件名相匹配 动画播放时,是按照每秒20帧的速度(即50ms/帧)。 定义完毕后,我们可以调用`animate`事件来播放该动画,有关事件的详细介绍请参见[事件](event)。 !> 播放录像时,将默认忽略所有动画。 - -目前天气系统支持雨和雪和雾两种天气。 - -在每层楼的楼层属性中存在一个weather选项,表示该层楼的默认天气。 - -``` js +
目前天气系统支持雨、雪和雾三种天气。在每层楼的楼层属性中存在一个weather选项,表示该层楼的默认天气。 +``` json // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain","snow"或"fog"代表雨雪雾,第二项为1-10之间的数代表强度。 "weather": ["snow",5] ``` @@ -351,12 +273,8 @@ floorId指定的是目标楼层的唯一标识符(ID)。 我们也可以使用`setWeather`事件来设置当前天气,有关事件的详细介绍请参见[事件](event)。 ## 背景音乐 - -本塔支持BGM和SE的播放。 - -要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在全塔属性中进行定义 - -``` js +样板支持BGM和SE的播放。要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在全塔属性中进行定义 +``` json // 在此存放所有的bgm,和文件名一致。 // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 "bgms": ["bgm.mp3"] @@ -366,9 +284,7 @@ floorId指定的是目标楼层的唯一标识符(ID)。 ``` !> 音频名不能使用中文,不能带空格或特殊字符。 - -目前BGM支持主流的音乐格式,如mp3, ogg等。不支持mid格式的播放。 - +
目前BGM支持主流的音乐格式,如 `wav, mp3, ogg, flac, m4r` 等。不支持 `mid` 格式的播放。
`iOS` 系统对 `ogg` 的支持性较差,但 `ogg` 比 `mp3` 体积小,请注意权衡。
定义完毕后,我们可以调用`playBgm`/`playSound`事件来播放对应的音乐/音效,有关事件的详细介绍请参见[事件](event)。 **另外,考虑到用户的流量问题,将遵循如下规则:** @@ -382,11 +298,7 @@ floorId指定的是目标楼层的唯一标识符(ID)。 从V2.5.3开始,可以使用`loadBgm`事件来预加载一个bgm,这样到播放时无需等待,直接播放。 同时BGM将使用LRU算法增加缓存机制。默认最多缓存4个BGM(在core.js的musicStatus.cachedBgmCount控制)。 - -系统会自动释放最久未使用的BGM。 - -也可以使用`freeBgm`事件来手动释放一个无需再用的bgm。 - +
系统会自动释放最久未使用的BGM。也可以使用 `freeBgm` 脚本来手动释放一个无需再用的 `bgm` 。 ## 录像 HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回放。 @@ -404,6 +316,7 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 录像播放过程中,可以进行如下操作: +- **步进:** 暂停状态下,按 `N` 键可以单步执行录像,常用于调试。 - **暂停/播放:** 按空格可以随时暂停或播放录像。 - **加速:** 按X可以加速录像播放,最高可达6倍速。 - **减速:** 按Z可以减速录像播放,最低可达0.3倍速。 @@ -413,8 +326,7 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 - **查看手册:** 按C可以在录像播放过程中查看怪物手册。 - **浏览地图:** 按PgUp/PgDn可以在录像播放过程中浏览地图。 -如果录像出现问题,请加群539113091找小艾反馈Bug。 - +如果录像出现问题,请加 QQ 群 [959329661](https://jq.qq.com/?_wv=1027&k=5C87qeQ) 找小艾反馈Bug。 ## 操作说明 ![](img/keyboard.png) diff --git a/_docs/event.md b/_docs/event.md index 4fd92d4e..be88228b 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1,23 +1,18 @@ # 事件 - -?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * - +?> 目前版本**v2.7**,上次更新时间:* {docsify-updated} *
本章内将对样板所支持的事件进行介绍。 ## 事件的机制 - -本塔所有的事件都是依靠触发`trigger`完成的。例如,勇士碰到一个门可以触发一个事件`openDoor`,勇士碰到怪物可以触发一个事件`battle`,勇士碰到一个(上面定义的)楼层传送点可以触发一个事件`changeFloor`,勇士穿过路障可以触发一个事件`passNet`,等等。上面说的这些事件都是系统本身自带的,即类似于RMXP中的公共事件。 - +本塔所有的事件都是依靠触发器 `trigger` 完成的。例如,勇士碰到一个门可以触发一个事件 `openDoor` ,勇士碰到怪物可以触发一个事件 `battle` ,勇士碰到一个(上面定义的)楼层传送点可以触发一个事件 `changeFloor` ,等等。上面说的这些事件都是系统本身自带的,即类似于RMXP中的公共事件。
上述这些默认的事件已经存在处理机制,不需要我们操心。我们真正所需要关心的,其实只是一个自定义的事件。 - -所有事件都存在两种状态:启用和禁用。 +
所有普通事件(红)、楼层传送事件(绿)和重生怪,都存在两种状态:启用和禁用。 - 启用状态下,该事件才处于可见状态,可被触发、交互与处理。 - 禁用状态下该事件相当于不存在,不可见、不可被触发、不可交互。 -所有事件默认情况下都是启用的,除非指定了`enable: false`。 - -在事件列表中使用`type: show`和`type: hide`可以将一个禁用事件启用,或将一个启用事件给禁用。 - +所有普通事件(红)默认情况下都是启用的,除非取消了“启用”的勾选。
+在事件列表中使用“显示事件”和“隐藏事件”可以将一个禁用事件给启用,或将一个启用事件给禁用(如果不是红、绿或重生怪,则会直接永久从地图中删除)。
+因此,非常不推荐仅仅为了初始隐藏一个图块,就去给它绑定一个本身没有任何指令的普通事件。
+如有需求,请使用“转变图块”或“关门”事件 ## 关于V2.0的重要说明 在V2.0以后版本中,所有事件均可以使用blockly来进行块的可视化编辑。 @@ -25,153 +20,50 @@ 它能通过拖动、复制粘贴等方式帮助你快速生成事件列表,而不用手动打大量字符。 下述所说的都是在事件编辑器右边所展示的,该事件的代码化写法;部分增加了可视化事件编辑器的截图示意(感谢秋橙的制作)。 - -强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 - -## 自定义事件 - +
强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction` 等地方需要插入临时事件组时,还是很有必要的。 +## 普通事件 打开样板1层(`sample1.js`)有着一些介绍。下面是更为详细的说明。 - -所有自定义的事件都是如下的写法: - -``` js +
所有普通事件都是如下的开头:
+覆盖触发器(Y/N)启用(Y/N)通行状态(不改变/不可通行/可以通行)显伤(Y/N) +1. 覆盖触发器:如果该点原本是道具或怪物等有着系统触发器的图块,那么必须勾选此项,表示用此普通事件中的指令覆盖原本的行为(拾获和战斗等,但炸弹、光环和阻激夹域捕不受影响),如果是在原本的系统行为结束后执行其他行为,请使用三个 `afterXxx` 事件。 +2. 启用:如果不勾选此项,就表示此普通事件初始时是隐藏的。 +3. 通行状态:可以使用此设定来覆盖该点图块的通行性,如果不指定则以图块为准(道具可通行,怪物不可通行,其他图块由图块属性的 `canPass` 项指定) +4. 显伤:如果不勾选此项,则该点为怪物时将取消显伤。 +``` json { - "trigger": "action", // 触发的trigger, action代表自定义事件 + "trigger": "action", // 触发的trigger, action代表覆盖触发器 "enable": true, // 该事件初始状态下是否处于启用状态 "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 + {"type": "xxx", ...}, // 事件1 + {"type": "xxx", ...}, // 事件2 // ... - ] -} -``` - -我们上面提到,有很多系统已经默认的事件(例如开门、打怪等,相当于公共事件)。如果我们需要自定义一个事件,则需要`"trigger": "action"`,它表示该点是一个自定义事件。 - -!> **如果系统本身存在事件(如一个怪物),且你指定了`"trigger": "action"`,则原事件会被覆盖。** - -这种情况下一般需采用后面的afterBattle,afterOpenDoor和afterGetItem来进行事件的处理。 - -如果该点本身不存在系统事件,则`"trigger":"action"`可被省略不写: - -``` js -{ - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - "enable": true, // 该事件初始状态下是否处于启用状态 - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] -} -``` - -`"enable": true` 代表该点初始状态下是否是启用的。如果`enable`为`false`,则该点初始状态下禁用,将不会被显示和交互(比如如果该点是个怪,指定了`enable`为`false`,则该怪不会显示在地图上,也不会发生战斗)。 - -默认情况下`enable`是`true`,所以如果`enable`为`true`,该项也可以省略不写: - -``` js -{ - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] -} -``` - -`"noPass"`为该点是否可通行的标记。`true`代表该点不可通行,`false`代表该点可通行。 - -对于目前所有的素材,都存在默认的是否可通行状态。如果你在该点指定`noPass`,则原本的可通行状态会被覆盖。 - -因此,除非你想覆盖默认的可通行选项(比如将一个空地设为不可通行),否则该项可以忽略。 - -``` js -{ - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - // 除非你想覆盖系统默认的可通行状态,否则"noPass"项可以忽略 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] -} -``` - -`"data"`为实际执行的事件列表。类似于RMXP中的"脚本",也是由一系列事件顺序构成的(其中可以使用`if`和`choices`来进行条件判断或用户选择,后面会具体提到)。 - -如果大括号里只有`"data"`,则可以省略大括号和`"data"`,直接写中括号数组,换句话说,上面和下面这种写法也是等价的,可以进行一下比较: - -``` js -// 如果大括号里只有"data"项(没有"action", "enable"或"noPass"),则可以省略到只剩下中括号 -[ - // 事件1 - // 事件2 - // ... -] -``` - -这种简写方式可以极大方便地造塔者进行造塔。 - -!> **请注意:如果该点初始的`enable`为`false`,或者该点本身有系统默认事件且需要覆盖(`trigger`),或者你想覆盖该点的默认通行状态,则必须采用上面那种大括号写的方式来定义。** - -  - -`"data"`中,是由一系列的自定义事件类型组成。每个元素类似于: - -``` js -// 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 -[ - {"type": "xxx", ...}, // 事件1 - {"type": "xxx", ...}, // 事件2 - // ... - // 按顺序写事件,直到结束 + // 按顺序写事件,直到结束 ] ``` `"type"`为该自定义事件的类型;而后面的`...`则为具体的一些事件参数。 - -每次,系统都将取出数组中的下一个事件,并进行处理;直到数组中再无任何事件,才会完全结束本次自定义事件,恢复游戏状态。 - +
每次,系统都将取出数组中的下一个事件,并进行处理;直到数组中再无任何事件,或遇到“立刻结束当前事件”,才会完全结束本次普通事件,恢复游戏状态。
下面将依次对所有自定义事件类型进行介绍。 ### text:显示一段文字(剧情) 使用`{"type": "text"}`可以显示一段文字。后面`"text"`可以指定文字内容。 -``` js +``` json [ - {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件 - {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 - // ... + {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件,按空格或单击屏幕继续 + "这是第二段文字", // 显示第二个文字事件,可以简写为字符串 + // 请注意insertAction函数接收单个字符串作为参数时会优先识别为公共事件,建议改用drawText // 按顺序写事件,直到结束 ] ``` ![](img/events/1.jpg) -该项可以简写成直接的字符串的形式,即下面这种方式也是可以的: - -``` js -[ - "在界面上的一段文字",// 直接简写,和下面写法完全等价 - {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 -] -``` - -所有文字事件均可以进行简写,系统会自动转成`{"type": "text"}`的形式。 - 值得注意的是,系统会自动对文字进行换行;不过我们也可以手动加入`\n`来换行。 -``` js +``` json [ "这一段文字特别特别长,但是系统可以对它进行自动换行,因此我们无需手动换行", "这是第一行\n这是第二行\n这是第三行", @@ -181,20 +73,10 @@ ``` 我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以。 - -其一般写法是`\t[名字,ID]`,其中名字为你要显示的标题,ID为图块ID,只能为`hero`,或者NPC/怪物的图块ID。 - -如果不需要可以不写ID,则只会显示标题。 - -对于hero和怪物,也可以不写名字代表使用默认值。 - +
其一般写法是 `\t[名字,ID]` ,其中名字为你要显示的标题,ID为图块ID,也可以填 `hero` 。
如果不需要可以不写ID,则只会显示标题。对于非道具,也可以不写名字代表使用默认值。
从V2.5.2以后,新增了绘制大头像的功能。绘制大头像图的基本写法是`\t[1.png]`或者`\t[标题,1.png]`。 - -从V2.6开始,所有图块都允许只写ID,对于非怪物则仅当图块属性中设置了name才有标题(否则不显示标题)。 - -另外注意的是,名字可以用null从而只显示动画而不显示标题。 - -``` js +
从V2.6开始,所有图块都允许只写ID,对于非怪物也非道具则仅当图块属性中设置了 `name` 才有标题(否则不显示标题)。另外注意的是,名字可以用null从而只显示动画而不显示标题。 +``` json [ "一段普通文字", "\t[勇士,hero]这是一段勇士说的话", @@ -213,19 +95,17 @@ ![](img/events/2.jpg) -!> 大头像的头像图需要在全塔属性中注册,且必须是png格式,不可以用jpg或者其他格式,请自行转换。 - +!> 大头像的头像图需要在全塔属性中注册,且必须是png格式,不可以用jpg或者其他格式,请自行另存为。
除此以外,我们还能实现“对话框效果”,只要有`\b[...]`就可以。 - -- `\b[up]` 直接显示在当前点上方。同样把这里的up换成down则为下方。 - - 如果不存在当前点(如在firstArrive或eachArrive中调用),则显示在屏幕最上方(最下方) -- `\b[up,hero]` 显示在勇士上方。同样把这里的up换成down则为下方。 - - 从V2.6开始,也允许写`\b[hero]`来根据勇士位置自动决定上方还是下方 -- `\b[up,x,y]` 显示在(x,y)点的上方(下方);x和y都为整数且在0到12之间 +- `\b[up]` 直接显示在当前点上方。`up` 换成 `down` 则为下方,换成 `null` 则根据当前点在视野中的位置自动选择上下 + - 如果不存在当前点(如在`firstArrive`或`eachArrive`中调用),则显示在屏幕最上方(最下方) + - `\b[up,null]`和`\b[center]`可以无视当前点存在与否,强制显示在屏幕最上方(最下方)或中央 +- `\b[up,hero]` 显示在勇士上方。同样把这里的 `up` 换成 `down` 则为下方。 + - 从V2.6开始,也允许写`\b[hero]`来根据勇士在视野中的位置自动决定上方还是下方 +- `\b[up,x,y]` 显示在(x,y)点的上方(下方);x和y都为整数,表示该点的绝对坐标 - 从V2.6开始,也允许写`\b[null,x,y]`来根据(x,y)位置自动决定上方还是下方 -- `\b[up,x]` 显示在勇士的第x个跟随的行走图的上方(下方);也允许把up换成null来自动适配 - -``` js +- `\b[up,x]` 显示在勇士的第x个跟随的行走图的上方(下方);也允许把 `up` 换成 `down` ,`hero`或`null` 来自动适配 +``` json [ "\b[up]这段文字显示在当前点上方", "\b[down]这段文字显示在当前点上方", @@ -233,7 +113,7 @@ "\t[hero]\b[hero]这是一段勇士说的话,根据勇士位置自动适配上下", "\t[小妖精,fairy]\b[down,2,2]这是一段小妖精说的话,会显示在(2,2)点下方", "\t[null,1,3]根据坐标位置自动适配上下", - "\t[up,1]"显示在勇士第一个跟随的行走图上方", + "\t[up,1]显示在勇士第一个跟随的行走图上方", "\t[null,2]显示在勇士第二个跟随的行走图,自动适配上下", ] ``` @@ -241,10 +121,8 @@ ![](img/events/3.jpg) !> `\t[...]`必须在`\b[...]`前面!不然两者都无法正常显示。 - -还可以使用`\r[...]`来调整剧情文本的颜色。 - -``` js +
还可以使用`\r[...]`来调整剧情文本的颜色,颜色的英文名详见[w3school](https://www.w3school.com.cn/cssref/css_colornames.asp)。样板也会帮你自动补全最标准的这17个:aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, white, yellow +``` json [ "这句话是默认颜色,\r[red]将颜色变成红色,\r[blue]将颜色变成蓝色", "\r[#FF00FF]还可以使用RGB值来控制颜色,\r如果不加中括号则回到默认颜色", @@ -261,8 +139,7 @@ 从V2.6.3开始,也可以在最后加上alpha值,即`\f[img,sx,sy,sw,sh,x,y,w,h,alpha]`。 需要注意的是,这个图片是绘制在UI层上的,下一个事件执行时即会擦除;同时如果使用了\t的图标动画效果,重叠的地方也会被图标动画给覆盖掉。 - -``` js +``` json [ "\t[勇士]\b[up,hero]\f[1.png,100,100]以(100,100)为左上角绘制1.png图片", "\t[hero]\f[1.png,100,100]\f[2.png,300,300]同时绘制了两张图片", @@ -277,10 +154,9 @@ 从V2.5.5以后,也可以使用`\\i[...]`来在对话框中绘制一个图标。 这里可以使用一个合法ID(32x48图块除外),或使用一个系统图标(`core.statusBar.icons`中的内容)。 - -``` js +``` json [ - "\t[勇士]\b[up,hero]这是一个飞行器\\i[fly],这是一个破墙镐\\i[pickaxe]", + "\t[勇士]\b[up,hero]这是一个楼层传送器\\i[fly],这是一个破墙镐\\i[pickaxe]", "\t[hero]也可以使用系统图标,比如这是存档\\i[save],这是工具栏\\i[toolbox]", ] ``` @@ -288,36 +164,31 @@ ![](img/events/6.jpg) **可以在控制台中输入`core.statusBar.icons`以查看所有的系统图标定义。** - -!> 注意,在事件块中,允许只写一个反斜杠`\i`,系统会自动转义成`\\i`;但是在脚本中必须两个反斜杠都写上! - -从V2.6.3开始,也可以使用`\\c[...]`来切换当前字体,`\\d`来加粗或取消粗体,`\\e`来加斜体或取消斜体。 - -``` js +
!> 注意,在事件块中,允许只写一个反斜杠`\i`,系统会自动转义成`\\i`;但是在脚本中必须两个反斜杠都写上!`\c`、`\d`、`\e` 和 `\z` 同理
+从V2.6.3开始,也可以使用`\\c[...]`来切换当前字体,`\\d`来加粗或取消粗体,`\\e`来加斜体或取消斜体。
从V2.7开始,还可以使用`\\z[...]`来调节打字速度。 +``` json [ "这是原始字体,\\c[20]使用20号字体,\\c[10]使用10号字体", "\\c如果不加中括号则切换回原始字体。", - "\\d这是粗体\\d取消粗体,\\e加斜体\\e取消斜体" + "\\d这是粗体\\d取消粗体,\\e加斜体\\e取消斜体", + "\t[hero]\b[hero]让我想想...\\z[10]有了!" // 打出省略号后,暂停相当于打10个字的时间 ] ``` - -!> 注意,在事件块中,允许只写一个反斜杠`\c`,系统会自动转义成`\\c`;但是在脚本中必须两个反斜杠都写上!`\d`和`\e`同理。 - 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 - -``` js +``` json [ "1+2=${1+2}, 4*5+6=${4*5+6}", // 显示"1+2=3, 4*5+6=26" ] ``` -我们可以使用 `status:xxx` 代表勇士的一个属性值;`item:xxx` 代表某个道具的个数;`flag:xxx` 代表某个自定义的变量或flag值。 +我们可以使用 `status:xxx` 代表勇士的一个属性值;`item:xxx` 代表某个道具的个数;`flag:xxx` 代表某个自定义的变量或flag值;`switch:A-Z`代表某个独立开关的值。 从V2.6开始,也可以使用`global:xxx`代表全局存储(和存档无关的存储)。 -从V2.6.5开始,也可以使用`enemy:id:atk`来获得某个怪物的属性,`blockId:x,y`来获得某个点的图块ID,`equip:x`来获得某个装备孔的装备ID。 +从V2.6.5开始,也可以使用`enemy:id:xxx`来获得某个怪物的属性,`blockId:x,y`来获得某个点的图块ID,`blockCls:x,y`来获得某个点的图块类别,`equip:N`来获得某个装备孔的装备ID。 -``` js +从V2.7开始,也可以使用`temp:A-Z`来获得某个临时变量,一般在循环遍历中使用。 +``` json [ "你当前的攻击力是${status:atk}, 防御是${status:def},坐标是(${status:x},${status:y})", "你的攻防和的十倍是${10*(status:atk+status:def)}", @@ -327,13 +198,13 @@ "绿色史莱姆的攻击力是${enemy:greenSlime:atk}", "(2,3)点的图块ID是${blockId:2,3},图块类型是${blockCls:2,3}", "装备孔0的当前装备ID是${equip:0}", + "当前的临时变量A的值是${temp:A}", // blockly中还可以使用中文,如图 ] ``` ![](img/events/7.png) - -- `status:xxx` 获取勇士属性时只能使用如下几个:hp(生命值),atk(攻击力),def(防御力),mdef(护盾值),money(金币),exp(经验),x(勇士的横坐标),y(勇士的纵坐标),direction(勇士的方向)。 -- `item:xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 +- `status:xxx` 获取勇士属性时如下几个较为特殊:x(勇士的横坐标),y(勇士的纵坐标),direction(勇士的方向)。其他的单词 `xxx` 会直接解析为 `core.status.hero.xxx` 的值。 +- `item:xxx` 中的 `xxx `为道具ID。所有道具的ID定义在 `project\items.js` 中,请自行查看。例如,
`item:centerFly` 代表中心对称飞行器的个数。 - `flag:xxx` 中的xxx为一个自定义的变量/Flag(支持中文);如果没有对其进行赋值则默认值为0。 - `global:xxx` 中的xxx为一个全局存储的名称(支持中文);如果没有对其进行赋值则默认值为0。 - `enemy:xxx:yyy` 中的xxx为怪物ID;yyy为要获得的项,比如hp, atk, def等等 @@ -343,8 +214,7 @@ ### autoText:自动剧情文本 使用`{"type": "autoText"}`可以使用剧情文本。 - -``` js +``` json [ {"type": "autoText", "text": "一段自动显示的剧情文字", "time": 5000} ] @@ -359,14 +229,11 @@ time为可选项,代表该自动文本的时间。可以不指定,不指定 用户无法跳过自动剧情文本,只能等待time时间结束后自动过。 回放录像时将忽略自动剧情文本的显示。 - -!> 由于用户无法跳过自动剧情文本,因此对于大段剧情文本请自行添加“是否跳过剧情”的提示,否则可能会非常不友好。 - +
!> 由于用户无法跳过自动剧情文本,因此对于大段剧情文本请自行塞进“是否跳过剧情”的确认框分支,否则可能会非常不友好。 ### scrollText:滚动剧情文本 使用`{"type": "scrollText"}`可以使用滚动剧情文本,即将一段文字从屏幕最下方滚动到屏幕最上方。 - -``` js +``` json [ {"type": "scrollText", "text": "第一排\n第二牌\n\n空行后的一排", "time": 5000, "lineHeight": 1.4, "async": true}, ] @@ -389,8 +256,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ### setText:设置剧情文本的属性 使用`{"type": "setText"}`可以设置剧情文本的各项属性。 - -``` js +``` json [ {"type": "setText", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3], "time": 70}, {"type": "setText", "position": "up", "offset": 15, "bold": true, "titlefont": 26, "textfont": 17}, @@ -401,7 +267,9 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ![](img/events/10.jpg) -title为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示标题(名字)颜色。 默认值:`[255,215,0,1]` +title为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示标题(名字)颜色。 默认值:`[255,215,0,1]`, + +可以通过单击RGBA值后面的色块呼出调色器,进行16进制转换等复杂取色操作,其他使用RGBA的事件也是类似的。 text为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示正文颜色。 默认值:`[255,255,255,1]` @@ -423,11 +291,11 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 interval为可选项,表示文字之间的间距。单位为像素值。默认值:`0` +lineHeight为可选项,表示行距。单位为像素值。 ### tip:显示一段提示文字 `{"type": "tip"}`可以在左上角显示一段提示文字。 - -``` js +``` json [ {"type": "tip", "text": "这段话将在左上角以气泡形式显示", "icon": "book"} ] @@ -442,8 +310,7 @@ icon是可选的,如果设置则会绘制图标,其可以是一个有效的I ### comment:添加注释 使用`{"type": "comment"}`可以添加一段注释 - -``` js +``` json [ {"type": "comment", "text": "这是一段会被跳过的注释内容"} ] @@ -458,30 +325,26 @@ icon是可选的,如果设置则会绘制图标,其可以是一个有效的I `{"type": "setValue"}` 能修改勇士的某个属性、道具个数、或某个自定义变量或`Flag`的值。 其大致写法如下: - -``` js -[ - {"type": "setValue", "name": "...", "value": "..."}, // 设置一个属性、道具或自定义Flag +``` json +[ // 设置一个属性、道具、自定义Flag、独立开关、全局存储、临时变量 + {"type": "setValue", "name": "...", "operator": "...", "value": "..."}, ] ``` - -使用`setValue`需要指定`name`和`value`选项。 - -name为你要修改的属性/道具/Flag,每次只能修改一个值。写法和上面完全相同,`status:xxx` 表示勇士一个属性,`item:xxx` 表示某个道具个数,`flag:xxx` 表示某个变量或flag值。参见上面的介绍。 - -value是一个表达式,将通过这个表达式计算出的结果赋值给name。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`, `global:xxx`的写法表示勇士当前属性,道具个数,某个变量/Flag值和某个全局存储值。 - -``` js +使用 `setValue` 需要指定 `name`、`operator` 和 `value` 选项。
+`name` 为你要修改的属性/道具/Flag/独立开关/全局存储/临时变量,每次只能修改一个值。写法和 `${}` 中完全相同,`status:xxx` 表示勇士一个属性,`item:xxx` 表示某个道具个数,`flag:xxx` 表示某个变量或flag值。参见上面的介绍。
+`operator` 为运算的类型,目前支持八种,详见下面的例子和图示。
+`value` 是一个表达式,将通过这个表达式计算出的结果作为另一个运算量。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`, `global:xxx`等的写法表示勇士当前属性,道具个数,某个变量/Flag值和某个全局存储值等。 +``` json [ - {"type": "setValue", "name": "status:atk", "value": "status:atk+10" } // 攻击提高10点 - {"type": "setValue", "name": "status:money", "value": "1000" } // 将金币数设为1000(不是+1000) - {"type": "setValue", "name": "status:hp", "value": "status:hp*2" } // 生命值翻倍 - {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey+3" } // 黄钥匙个数加3 - {"type": "setValue", "name": "item:bomb", "value": "item:bomb+10" } // 炸弹个数+10 - {"type": "setValue", "name": "flag:man_times", "value": "0" } // 将变量man_times设为0 - {"type": "setValue", "name": "flag:man_times", "value": "flag:man_times+2*status:atk" } // 将变量man_times的值加上勇士的攻击数值的两倍 - {"type": "setValue", "name": "global:123", "value": "4"} // 设置全局存储233为4(任何存档都可以读取它) -] + {"type": "setValue", "name": "global:一周目已通关", "value": "true", "norefresh": true}, // 设置全局存储“一周目已通关”为true,用于二周目中触发隐藏分支 + {"type": "setValue", "name": "status:name", "operator": "+=", "value": "'之王'", "norefresh": true}, // 勇士名称结尾增加两个字“之王”,不立即刷新状态栏 + {"type": "setValue", "name": "flag:hatred", "operator": "-=", "value": "10", "norefresh": true}, // 减少10点仇恨值 + {"type": "setValue", "name": "status:hp", "operator": "*=", "value": "2", "norefresh": true}, // 生命值加倍,即圣水道具的效果 + {"type": "setValue", "name": "switch:A", "operator": "/=", "value": "10", "norefresh": true}, // 独立开关A的值缩小到原来的十分之一,可能产生小数 + {"type": "setValue", "name": "item:yellowKey", "operator": "**=", "value": "3", "norefresh": true}, // 黄钥匙的数量变为它原来值的立方 + {"type": "setValue", "name": "temp:A", "operator": "//=", "value": "3", "norefresh": true}, // 临时变量缩小到原来的三分之一,向靠近0的方向取整 + {"type": "setValue", "name": "status:atk", "operator": "%=", "value": "100"}, + ] // 攻击力对100取余数,即只保留最后两位,然后刷新状态栏 ``` ![](img/events/13.jpg) @@ -495,7 +358,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam 使用`{"type":"setEnemy"}`可以设置某个怪物的某个属性 -``` js +``` json [ {"type": "setEnemy", "id": "greenSlime", "name": "hp", "value": "1000"}, // 设置绿色史莱姆生命1000 {"type": "setEnemy", "id": "redSlime", "name": "special", "value": "[1,2]"}, // 设置红色史莱姆先攻魔攻 @@ -586,12 +449,9 @@ name必填项,代表要修改的系统开关,其是全塔属性中的flags value为必填项,只能为true或false,代表要修改到的结果。 ### show:将一个禁用事件启用 - -我们上面提到了,所有事件都必须靠其他事件驱动来完成,不存在当某个flag为true时自动执行的说法。那么,我们自然要有启用事件的写法。 - +我们上面提到了,除自动事件外,事件都必须靠其他事件驱动来完成,不存在当某个 `flag` 为 `true` 时自动执行的说法。那么,我们自然要有启用事件的写法。
使用`{"type":"show"}`可以将一个本身禁用的事件启用。 - -``` js +``` json [ {"type": "show", "loc": [3,6], "floorId": "MT1", "time": 500}, // 启用MT1层[3,6]位置事件,动画500ms {"type": "show", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 @@ -614,9 +474,7 @@ time为动画效果时间,如果指定了某个大于0的数,则会以动画 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 !> **要注意的是,调用show事件后只是让该事件从禁用状态变成启用,从不可见不可交互变成可见可交互,但本身不会去执行该点的事件。** - -### hide:将一个启用事件禁用 - +### hide:将一个启用事件禁用,或将非红非绿非重生的图块删除 `{"type":"hide"}`和show刚好相反,它会让一个已经启用的事件被禁用。 其参数和show也完全相同,loc指定事件的位置,floorId为楼层(同层可忽略),time指定的话事件会以动画效果从有到无慢慢消失,async代表是否是异步效果。 @@ -628,8 +486,7 @@ loc同样可以简单的写[x,y]表示单个点,或二维数组[[x1,y1],[x2,y2 请注意,一次性事件必须要加 `{"type":"hide"}`,尤其是例如走到某个点,触发对话或机关门(陷阱)这种,否则每次都会重复触发。 NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}`,可以不写loc选项代表当前事件,可以指定time使NPC动画消失。 - -``` js +``` json [ {"type": "hide", "loc": [3,6], "floorId": "MT1", "time": 500}, // 禁用MT1层[3,6]位置事件,动画500ms {"type": "hide", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 @@ -638,7 +495,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` {"type": "hide", "time": 500}, // 如果不指定loc选项则默认为当前点, 例如这个就是500ms消失当前对话的NPC {"type": "hide"}, // 无动画将当前事件禁用,常常适用于某个空地点(触发陷阱事件、触发机关门这种) {"type": "hide", "loc": [3,6], "time": 500, "async": true} // 可以使用异步动画效果 -] +] // 此事件实际直接调用的函数是core.removeBlock(x, y),开门打怪捡道具后也会自动调用此函数 ``` ![](img/events/20.jpg) @@ -648,8 +505,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` `{"type":"trigger"}` 会立刻触发当层另一个地点的自定义事件。 其基本写法如下: - -``` js +``` json [ {"type": "trigger", "loc": [3,6]}, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 {"type": "trigger", "loc": [3,6], "keep": true}, // 触发loc位置的事件,不结束当前事件 @@ -671,8 +527,7 @@ keep可选,如果此项为true则不会结束当前的事件列表,否则会 `{"type":"insert"}` 会插入公共事件或另一个地点的事件并执行。 其基本写法如下: - -``` js +``` json [ {"type": "insert", "name": "加点事件", "args": [10] }, // 插入公共事件:加点事件,传入参数10 {"type": "insert", "name": "毒衰咒处理", "args": [0]}, // 插入公共事件:毒衰咒处理,传入参数0 @@ -697,7 +552,8 @@ keep可选,如果此项为true则不会结束当前的事件列表,否则会 和`type:trigger`不同点如下: - `type:trigger`只能指定当前楼层且会改变当前点坐标,`type:insert`可以跨楼层且不会改变当前点坐标。 - `type:trigger`如果不设置`keep:true`则还会结束当前事件,`type:insert`而是将另一个点的事件插入到当前事件列表中执行。 -- `type:trigger`可以触发系统事件,`type:insert`只能触发该点的自定义事件或战后事件等。 +- `type:trigger`可以触发系统事件,`type:insert`只能触发该点的普通事件或 `afterXxx` 事件等。 +- `type:trigger`要求目标点启用,`type:insert`会无视目标点启用与否。 插入的事件执行完毕后,会继续执行接下来的内容。 @@ -710,33 +566,10 @@ keep可选,如果此项为true则不会结束当前的事件列表,否则会 (`flag:arg0`则会被置为公共事件名称,或者插入的点的坐标) 即可在事件中直接取用`flag:arg1`等等来获得各项参数值!。 - -### revisit:立即重启当前事件 - -revisit和trigger完全相同,只不过是立刻触发的还是本地点的事件 - -``` js -[ - {"type": "revisit"}, // 立即触发本事件,等价于 {"type": "trigger", "loc": [x,y]} - "执行revisit后,这段文字将不会再被显示" -] -``` - -![](img/events/23.jpg) - -revisit其实是trigger的简写,只不过是loc固定为当前点。 - -revisit常常使用在一些商人之类的地方,当用户购买物品后不是离开,而是立刻重新访问重新进入购买页面。 - ### exit:立刻结束当前事件 - -上面说到像商人一类,购买物品后可以立刻revisit重新访问,但是这样就相当于陷入了死循环导致无法离开。 - -可以使用`{"type":"exit"}`立刻结束事件。调用exit后,将立刻结束一切事件,清空事件列表,并返回游戏。 - +可以使用`{"type":"exit"}`立刻结束事件。调用 `exit` 后,将立刻结束一切事件,清空事件列表,并返回游戏。
例如玩家点击商人的"离开"选项,则可以调用exit返回游戏。 - -``` js +``` json [ {"type": "exit" }, // 立即结束事件并恢复游戏,一切列表中的事件都将不再被执行 "执行exit后,这段文字将不会再被显示" @@ -748,11 +581,10 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ### setBlock:设置某个图块 我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 - -``` js +``` json [ {"type": "setBlock", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层的(3,3)点变成数字233 - {"type": "setBlock", "loc": [2,1],setVa "number": 121}, // 省略floorId则默认为本层 + {"type": "setBlock", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 {"type": "setBlock", "number": 57}, // loc也可省略,默认为当前点 {"type": "setBlock", "number": "yellowDoor"}, // 从V2.6开始也允许写图块ID ] @@ -768,87 +600,48 @@ number为**要更改到的数字**,有关“数字”的定义详见参见[素 从V2.6开始,number也允许写图块的ID,将自动转成对应的数字。 +从V2.7开始,还可以指定转变的淡入动画时间,用于该点原本是空地或空气墙的场合,从而避免“仅仅为了做个淡入效果,就去创建了一个本身没有任何指令的普通事件”。
图块更改后: - 其启用/禁用状态不会发生任何改变。原来是启用还是启用,原来是禁用还是禁用。 - - 可通行状态遵循覆盖原则,即**首先取该图块的默认noPass属性,如果剧本的events中定义该点的noPass则覆盖**。 - - 触发器(trigger)亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是battle,道具是getItem,门是openDoor),如果剧本的events中定义了该点的trigger则覆盖**。 - + - 可通行状态遵循覆盖原则,即**首先取该图块的 `canPass` 属性(道具和怪物没有此属性,道具可通行,怪物不可通行),如果该点的普通事件中定义了 `noPass` 则覆盖**。 + - 触发器( `trigger` )亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是battle,道具是getItem,门是openDoor),如果该点绑定了楼层转换事件(绿)或普通事件(红)中覆盖触发器,则覆盖**。 图块更改往往与[同一个点的多事件处理](#同一个点的多事件处理)相关。 - -### hideFloorImg:隐藏楼层贴图 - -使用`{"type":"hideFloorImg"}`可以隐藏某个楼层的贴图。 - +### showFloorImg / hideFloorImg:显隐楼层贴图 +使用`{"type":"hideFloorImg"}`可以隐藏某个楼层的贴图。使用`{"type":"showFloorImg"}`可以显示楼层的贴图。
有关贴图说明请参见[使用自己的图片作为某层楼的背景/前景素材](personalization#使用自己的图片作为某层楼的背景前景素材)。 - -``` js +``` json [ - {"type": "hideFloorImg", "loc": [3,6], "floorId": "MT1"}, // 隐藏[3,6]的贴图 - {"type": "hideFloorImg", "loc": [3,6]}, // 如果是当前层,则可以省略floorId项 - {"type": "hideFloorImg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时隐藏多个贴图。 + {"type": "hideFloorImg", "loc": [32,64], "floorId": "MT1"}, // 隐藏[32,64]的贴图 + {"type": "hideFloorImg", "loc": [32,64]}, // 如果是当前层,则可以省略floorId项 + {"type": "showFloorImg", "loc": [[32,64],[256,96],[128,256]]} // 我们也可以同时显隐多个贴图。 ] ``` ![](img/events/26.jpg) - -loc为要隐藏的贴图的左上角坐标,可以简单的写[x,y]代表一个点,也可以写个二维数组[[x1,y1],[x2,y2],...]来同时显示多个点。 - -如果同时存在若干个贴图都是是该坐标为左上角,则这些贴图全部会被隐藏。 - +loc为要显隐的贴图的左上角像素坐标,可以简单的写[x,y]代表一个点,或二维数组[[x1,y1],[x2,y2],...]来同时显隐多个点。
+如果同时存在若干个贴图都是是该坐标为左上角,则这些贴图全部会被显隐。
floorId为目标点的楼层,如果是当前楼层可以忽略不写。 - -### showFloorImg:显示楼层贴图 - -使用`{"type":"showFloorImg"}`可以显示某个楼层的贴图。 - -其做法和参数,和隐藏贴图是完全一致的。 - -``` js -[ - {"type": "showFloorImg", "loc": [3,6], "floorId": "MT1"}, // 显示[3,6]的贴图 -] -``` - -### hideBgFgMap:隐藏楼层的某些背景或前景图块 - -使用`{"type":"hideBgFgMap"}`可以隐藏某个楼层的背景或前景图块。 - -从V2.4.1开始,允许绘制三层图层(背景层,事件层和前景层)。使用`hideBgFgMap`可以隐藏背景或前景层中的图块。 - -``` js +### showBgFgMap / hideBgFgMap:显隐楼层的某些背景或前景图块 +使用`{"type":"hideBgFgMap"}`可以隐藏背景或前景图块。使用`{"type":"showBgFgMap"}`可以显示背景或前景图块。
+从V2.4.1开始,允许绘制三层图层(背景层,事件层和前景层)。 +``` json [ {"type": "hideBgFgMap", "name": "bg", "loc": [3,6], "floorId": "MT1"}, // 隐藏MT1层[3,6]的背景层图块 {"type": "hideBgFgMap", "name": "bg", "loc": [3,6]}, // 如果是当前层,则可以省略floorId项 - {"type": "hideBgFgMap", "name": "fg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时隐藏多个贴图。 + {"type": "showBgFgMap", "name": "fg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时显隐多个图块。 ] ``` ![](img/events/27.jpg) name为必选的,且只能是`bg`和`fg`之一,分别代表背景图层和前景图层。 - -loc为要隐藏的贴图的左上角坐标,可以简单的写[x,y]代表一个点,也可以写个二维数组[[x1,y1],[x2,y2],...]来同时显示多个点。 - +
`loc` 为要显隐的图块的坐标,可以简单的写[x,y]代表一个点,也可以写个二维数组[[x1,y1],[x2,y2],...]来同时显示多个点。
floorId为目标点的楼层,如果是当前楼层可以忽略不写。 - -### showBgFgMap:显示楼层贴图 - -使用`{"type":"showFloorImg"}`可以显示某个楼层的贴图。 - -其做法和参数,和隐藏贴图是完全一致的。 - -``` js -[ - {"type": "showBgFgMap", "name": "bg", "loc": [3,6], "floorId": "MT1"}, // 显示MT1层[3,6]的前景层图块 -] -``` - ### setBgFgBlock:设置某个背景或前景层图块 我们可以采用 `{"type": "setBgFgBlock"}` 来改变某个背景或前景层地图块。 - -``` js +``` json [ {"type": "setBgFgBlock", "name": "bg", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层背景层的(3,3)点变成数字233 {"type": "setBgFgBlock", "name": "bg", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 @@ -869,8 +662,7 @@ loc为可选的,表示要更改地图块的坐标。如果忽略此项,则 ### setHeroIcon:更改角色行走图 使用`{"type": "setHeroIcon"}`可以更改角色行走图。 - -``` js +``` json [ {"type": "setHeroIcon", "name": "hero2.png"}, // 将勇士行走图改成hero2.png;必须在全塔属性的images中被定义过。 {"type": "setHeroIcon"}, // 如果不加name则恢复最初默认状态 @@ -881,42 +673,23 @@ loc为可选的,表示要更改地图块的坐标。如果忽略此项,则 ![](img/events/29.jpg) name是可选的,代表目标行走图的文件名。 - -!> **目标行走图必须在全塔属性的this.images中被定义过,且宽度必须是128像素(高度不限)。** - +
!> **目标行走图必须在全塔属性的this.images中被定义过,且宽度至少是128像素(高度不限)。**
如果不加name,则恢复默认的角色行走图。 如果你需要同时修改勇士的名称,可以使用`setValue`事件来修改`status:name`,但请注意value必须加单引号,不然会报错。 +如果你需要获取当前的行走图文件名,可以使用 `flags.heroIcon` ,但不要直接修改它。 ### update:立刻更新状态栏和地图显伤 - -如果你需要刷新状态栏和地图显伤,只需要简单地调用 `{"type": "update"}` 即可。 - -### hideStatusBar:隐藏状态栏 - +如果你需要刷新状态栏和地图显伤,只需要简单地调用 `{"type": "update", "doNotCheckAutoEvents": true}` 。 +### showStatusBar / hideStatusBar:显隐状态栏 使用`{"type": "hideStatusBar"}`可以隐藏状态栏。读档或重新开始游戏时,状态栏会重新显示。 - -可以添加`"toolbox": true`来不隐藏竖屏模式下的工具栏。 - -### showStatusBar:显示状态栏 - -使用`{"type": "showStatusBar"}`会重新显示状态栏。 - -### hideHero:隐藏勇士 - -使用`{"type": "hideHero"}`可以隐藏勇士。 - -### showHero:显示勇士 - -使用`{"type": "showHero"}`会重新显示勇士。 - +
可以添加`"toolbox": true`来不隐藏竖屏模式下的工具栏。使用`{"type": "showStatusBar"}`会重新显示状态栏。 +### showHero / hideHero:显隐勇士 +使用`{"type": "hideHero"}`可以隐藏勇士。使用`{"type": "showHero"}`会重新显示勇士。 ### sleep:等待多少毫秒 - -等价于RMXP中的"等待x帧",不过是以毫秒来计算。 - +等价于RPG Maker中的"等待x帧",不过是以毫秒来计算。
基本写法:`{"type": "sleep", "time": xxx}` ,其中xxx为指定的毫秒数。 - -``` js +``` json [ {"type": "sleep", "time": 1000}, // 等待1000ms "等待1000ms后才开始执行这个事件", @@ -924,17 +697,14 @@ name是可选的,代表目标行走图的文件名。 ] ``` -![](img/events/30.jpg) - -默认的等待事件可以被Ctrl跳过,下面两种情况下不可呗跳过: +![](img/events/30.jpg)
默认的等待事件可以被Ctrl跳过,下面两种情况下不可被跳过: - 加上`"noSkip": true`后 - 当前存在尚未执行完毕的异步事件。 ### battle:强制战斗 调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 - -``` js +``` json [ {"type": "battle", "id": "blackKing"}, // 强制战斗黑衣魔王 {"type": "battle", "loc": [2,3]}, // 强制战斗(2,3)点的怪物 @@ -954,8 +724,7 @@ name是可选的,代表目标行走图的文件名。 ### openDoor:开门 调用`{"type":"openDoor"}`可以打开一扇门。 - -``` js +``` json [ {"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门 {"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId @@ -971,29 +740,23 @@ loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层 如果loc所在的点既不是门也不是墙壁,则忽略本事件。 -needKey是可选的,如果设置为true则需要钥匙才能打开此门。如果没有钥匙则跳过此事件。 +needKey是可选的,如果设置为true且门在当前楼层则需要钥匙才能打开此门。如果没有钥匙则跳过此事件。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 !> needKey仅对当前楼层开门有效!跨楼层的门仍然不需要钥匙即可打开,如有需求请自行判定。 -值得注意的是,如果该点存在开门事件,执行时会**修改当前点坐标**为目标点。 - +值得注意的是,如果该点存在开门后事件,执行时会**修改当前点坐标**为目标点。 ### closeDoor:关门 从V2.6开始提供了关门事件`{"type": "closeDoor"}`,拥有关门动画和对应的音效。 - -``` js +``` json [ {"type": "closeDoor", "id": "yellowDoor", "loc": [3,6]}, // 给(3,6)点关上黄门 {"type": "closeDoor", "id": "specialDoor"}, // 不写loc则视为当前点 ] ``` - -![](img/events/33.jpg) - -id为你要关门的ID,需要是一个合法的门,系统默认只支持如下几种: - +![](img/events/33.jpg)
id为你要关门的ID,需要是一个合法的门,系统自带支持如下几种: ``` yellowDoor, blueDoor, redDoor, greenDoor, specialDoor, steelDoor, yellowWall, blueWall, whiteWall @@ -1008,40 +771,29 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 关门事件需要保证该点是空地,否则将无视此事件。 ### changeFloor:楼层切换 - -在事件中也可以对楼层进行切换。一个比较典型的例子就是TSW中,勇士在三楼的陷阱被扔到了二楼,就是一个楼层切换事件。 - +在事件中也可以对楼层进行切换。一个比较典型的例子就是51层魔塔中,勇士在三楼的陷阱被扔到了二楼,就是一个楼层切换事件。
changeFloor的事件写法大致如下。 - -``` js +``` json [ - {"type": "changeFloor", "floorId": "sample0","loc": [10, 10], "direction": "left", "time": 1000 }, + {"type": "changeFloor", "floorId": "sample0", "loc": [10, 10], "direction": "left", "time": 1000 }, //后面几项依次为楼层id,楼层位置(这两项为必填);勇士方向可选,切换时间也是可选。 ] ``` - -![](img/events/34.jpg) - -可以看到,与上面的楼梯、传送门的写法十分类似。 - -但是相比那个而言,不支持stair楼梯位置(只能写坐标),没有穿透选项。 - +![](img/events/34.jpg)
可以看到,与之前的楼梯、传送门的写法十分类似。 +但是相比那个而言,这个不支持穿透选项。
direction为可选的,指定的话将使勇士的朝向变成该方向 time为可选的,指定的话将作为楼层切换动画的时间。 **如果time指定为小于100,则视为没有楼层切换动画。** - -### changePos:当前位置切换/set勇士转向 - +### changePos:当前位置切换/勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 这时候可以用changePos。其参数和changeFloor类似,但少了floorId和time两个选项。 - -``` js +``` json [ {"type": "changePos", "loc": [10,10], "direction": "left"}, // 直接切换勇士的坐标,loc为目标地点,后面勇士换位后方向 - {"type": "changePos", "loc", [10,10]}, // 如无需指定方向则direction可省略 + {"type": "changePos", "loc": [10,10]}, // 如无需指定方向则direction可省略 {"type": "changePos", "direction": "left"} // loc也可省略,只指定direction;此时等价于当前勇士转向到某个方向。 ] ``` @@ -1051,8 +803,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 ### useItem:使用道具 调用`{"type": "useItem"}`可以使用一个道具。 - -``` js +``` json [ {"type": "useItem", "id": "pickaxe"}, // 尝试使用破 {"type": "useItem", "id": "bomb"}, // 尝试使用炸 @@ -1067,79 +818,37 @@ time为可选的,指定的话将作为楼层切换动画的时间。 如果当前不可使用该道具(如没有,或者达不到使用条件),则会进行提示并跳过本事件。 不可使用“怪物手册”(请使用【呼出怪物手册】事件)或楼层传送器(如果[覆盖楼传事件](personalization#覆盖楼传事件)则可忽视本项)。 - -### loadEquip:装上装备 - -使用`{"type": "loadEquip"}`可以装上一个装备。 - -``` js +### loadEquip / unloadEquip:装脱装备 +使用`{"type": "loadEquip"}`可以装上一个装备。使用`{"type": "unloadEquip"}`卸下某个装备孔的装备。 +``` json [ {"type": "loadEquip", "id": "sword1"}, // 尝试装上铁剑 + {"type": "unloadEquip", "pos": 0}, // 卸下装备孔0的装备 ] ``` id必填,为需要装备的ID。 使用装备时仍会检查条件(比如装备是否存在,能否装备等等)。 - -### unloadEquip:卸下装备 - -使用`{"type": "unloadEquip"}`卸下某个装备孔的装备。 - -``` js -[ - {"type": "unloadEquip", "pos": 0}, // 卸下装备孔0的装备 -] -``` - pos必填,为要卸下的装备孔编号,从0开始。 - -### openShop:打开一个全局商店 - -使用openShop可以打开一个全局商店。有关全局商店的说明可参见[全局商店](#全局商店)。 - -### disableShop:禁用一个全局商店 - -使用disableShop可以永久禁用全局商店直到再次被openShop打开为止。有关全局商店的说明可参见[全局商店](#全局商店)。 - -### follow:跟随勇士 - +### openShop / disableShop:启用或禁用一个全局商店 +使用openShop可以启用一个全局商店。使用disableShop可以永久禁用全局商店直到再次被openShop启用为止。
有关全局商店的说明可参见[全局商店](#全局商店)。 +### follow / unfollow:跟随勇士或取消跟随 使用 `{"type": "follow"}` 可以让一个npc加入跟随。 - -``` js +``` json [ - {"type": "follow", "name": "npc.png"}, // 将 npc.png 这个行走图加入跟随 - {"type": "follow", "name": "hero.png"}, // 再将另一个行走图加入跟随 + {"type": "follow", "name": "npc48.png"}, // 将 npc48.png 这个行走图加入跟随 + {"type": "follow", "name": "enemy48.png"}, // 再将另一个行走图加入跟随 + {"type": "unfollow", "name": "npc48.png"}, // 将第一个 npc48.png 行走图取消跟随 + {"type": "unfollow"} // 不填name,表示取消所有跟随 ] ``` - -name为必须的,是要加入跟随的行走图文件名。 - -name所指定的图片必须存在,在全塔属性中的images中被定义过,且是一个合法的行走图(宽为128像素,高不限。) - -### unfollow:取消跟随 - -使用 `{"type": "unfollow"}` 来取消一个跟随。 - -``` js -[ - {"type": "unfollow", "name": "npc.png"}, // 将 npc.png 这个行走图取消跟随 - {"type": "unfollow"}, // 取消所有跟随 -] -``` - -name为可选的,是要取消跟随的行走图文件名。 - -如果name指定了,则会检查所有当前正在跟随的行走图,并删除第一个文件名是name的跟随效果。 - -如果name省略,则会取消所有的跟随效果。 - +`name` 为要加入或取消跟随的行走图文件名,取消跟随时取第一个,不填则取消所有。
+`name` 所指定的图片必须存在,在全塔属性中的 `images` 中注册,且为一个合法的行走图(宽至少为128像素,高不限) ### vibrate:画面震动 使用 `{"type": "vibrate", "time": 2000, "async": true}` 可以造成画面震动效果。 - -time可以指定震动时间,默认是2000毫秒。 - +
`time` 可以指定震动时间,必须为500毫秒的倍数,且至少要1000毫秒。
async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 ### animate:显示动画 @@ -1147,8 +856,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "animate"}` 来显示一段动画。 有关动画的详细介绍可参见[动画和天气系统](element#动画和天气系统)。 - -``` js +``` json [ {"type": "animate", "name": "yongchang", "loc": [1,3]}, // 在(1,3)显示“咏唱魔法”动画 {"type": "animate", "name": "zone", "loc": "hero"}, // 在勇士位置显示“领域”动画 @@ -1160,20 +868,17 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ![](img/events/37.jpg) name为动画名,**请确保动画在全塔属性中的animates中被定义过。** - -loc为动画的位置,可以是`[x,y]`表示在(x,y)点显示,也可以是字符串`"hero"`表示在勇士点显示。 - -loc可忽略,如果忽略则显示为事件当前点。 +
loc为动画的位置,可以是`[x,y]`表示在(x,y)点显示,也可以是字符串`"hero"`表示跟随勇士显示(就像踩到血网时)。
+loc可忽略,如果忽略则视为事件当前点。 如果async指定为true,则不会等待动画绘制完毕,立刻执行下个事件。 否则,在动画播放结束后才会继续执行下一个事件。 - +
从V2.7开始,增加了一个勾选项表示该坐标是绝对坐标还是在视野中的相对坐标,譬如13×13样板中,勾选此项并填写坐标 `[6,6]` 就能无视大地图,强制在视野正中心播放动画。 ### showImage:显示图片 我们可以使用 `{"type": "showImage"}` 来显示一张图片。 - -``` js +``` json [ {"type": "showImage", "code": 1, "image": "bg.jpg", "loc": [231,297], "opacity": 1, "time" : 0}, // 在(231,297)显示bg.jpg {"type": "showImage", "code": 12, "image": "1.png", "loc": [209,267], "opacity": 0.5, "time" : 1000}, // 在(209,267)渐变显示1.png,渐变时间为1000毫秒,完成时不透明度为0.5,这张图片将遮盖上一张 @@ -1186,6 +891,8 @@ loc可忽略,如果忽略则显示为事件当前点。 code为图片编号,如果两张图片重叠,编号较大会覆盖编号较小的。该值需要在1~50之间。 +图片编号的真正意义,请参阅[个性化](personalization)。 + image为图片名。**请确保图片在全塔属性中的images中被定义过。** sloc为可选项;如果设置了则是个2或4元组,代表裁剪原始图片的左上角像素位置和宽高。 @@ -1198,11 +905,10 @@ time为渐变时间,默认值为0,即不渐变直接显示。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 -### showTextImage:显示文本化图片 +### showTextImage:显示图片化文本 我们可以使用 `{"type": "showTextImage"}` 以图片的方式显示文本。 - -``` js +``` json [ {"type": "showTextImage", "code": 1, "text": "第一排\n第二排\n\n空行后的一排", "loc": [231,297], "opacity": 1, "time" : 0}, // 在(231,297)显示"第一排\n第二排\n\n空行后的一排" ] @@ -1210,7 +916,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 code为图片编号,如果两张图片重叠,编号较大会覆盖编号较小的。该值需要在1~50之间。 -text为要显示的文本。默认行宽为416。 +text为要显示的文本。默认行宽为416(15×15样板为480)。 loc为图片左上角坐标,以像素为单位进行计算。 @@ -1227,8 +933,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ### hideImage:清除图片 我们可以使用 `{"type": "hideImage"}` 来清除一张图片。 - -``` js +``` json [ {"type": "hideImage", "code": 1, "time" : 0}, // 使1号图片消失 {"type": "hideImage", "code": 12, "time" : 1000}, // 使12号图片渐变消失,时间为1000毫秒 @@ -1244,8 +949,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ### showGif:显示动图 我们可以使用 `{"type": "showGif"}` 来显示一张图片。 - -``` js +``` json [ {"type": "showGif", "name": "timg.gif", "loc": [231,297]}, // 在(231,297)显示一张动图 {"type": "showGif"} // 如果不指定name则清除所有动图。 @@ -1261,8 +965,7 @@ loc为动图左上角坐标,以像素为单位进行计算。 ### moveImage:图片移动 我们可以使用 `{"type": "moveImage"}` 来造成图片移动,淡入淡出等效果。 - -``` js +``` json [ {"type": "moveImage", "code": 1, "to": [22,333], "opacity": 1, "time": 1000}, // 将1号图片移动到(22,333),动画时间为1000ms {"type": "moveImage", "code": 12, "opacity": 0.5, "time": 500}, // 将二号图片的透明度变为0.5,动画时间500ms @@ -1285,8 +988,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ### setCurtain:更改画面色调 我们可以使用 `{"type": "setCurtain"}` 来更改画面色调。 - -``` js +``` json [ {"type": "setCurtain", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 {"type": "setCurtain", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 @@ -1306,11 +1008,11 @@ time为可选的,如果指定,则会作为更改画面色调的时间。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 +从V2.7开始,色调、天气和bgm的更改都提供了“持续到下个本事件”勾选项,勾选此项后,设置的结果将分别计入 `flags.__color__ , flags.__weather__ , flags.__bgm__` ,也会计入存档,并无视楼层切换带来的色调、天气和bgm变化,直到再次遇到本事件。但您也可以随时手动干预或清除这三个 `flag` ### screenFlash:画面闪烁 我们可以使用 `{"type": "screenFlash"}` 来进行画面闪烁。 - -``` js +``` json [ {"type": "screenFlash", "color": [255,255,255,0.6], "time": 500, "times": 1}, // 闪光为白色,不透明度0.6,动画时间1000毫秒 {"type": "screenFlash", "color": [255,0,0,1], "time": 100, "times": 2, "async": true}, // 闪光为红色,强度最大,动画时间100毫秒,闪烁两次且异步执行 @@ -1330,58 +1032,51 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ### setWeather:更改天气 我们可以使用 `{"type": "setWeather"}` 来更改天气。 - -``` js +``` json [ {"type": "setWeather", "name": "rain", "level": 6}, // 更改为雨天,强度为6级 {"type": "setWeather", "name": "snow", "level": 3}, // 更改为雪天,强度为3级 {"type": "setWeather"} // 更改回晴天 ] ``` - -name为天气选项。目前只支持`rain`和`snow`,即雨天和雪天。 - -从V2.5.3开始,也支持雾天`fog`。 +name为天气名。起初只支持 `rain` 和 `snow` ,即雨天和雪天。从V2.5.3开始,也支持雾天 `fog` level为天气的强度等级,在1-10之间。1级为最弱,10级为最强。 如果想改回晴天则直接不加任何参数。 - -!> 使用setWeather更改的天气在切换地图后会被目标地图的默认天气覆盖。 - ### move:让某个NPC/怪物移动 如果我们需要移动某个NPC或怪物,可以使用`{"type": "move"}`。 下面是该事件常见的写法: - -``` js +``` json [ - {"type": "move", "time": 750, "loc": [x,y], "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),loc为位置可选,steps为移动数组 + {"type": "move", "time": 750, "loc": [x,y], "steps": [ + // 动画效果,time为移动和淡出速度(比如这里每750ms一步),loc为位置可选,steps为移动数组 "right", "right", "down" // 向右两格,向下一格 ], "keep": true, "async":true }, // keep可选,如果为true则不消失,否则渐变消失;async可选,如果为true则异步执行。 ] ``` ![](img/events/41.jpg) - -time选项必须指定,为每移动一步所需要用到的时间。 +
time选项必须指定,为每移动一步和淡出所需要用到的时间。 loc为需要移动的事件位置。可以省略,如果省略则移动本事件。 steps为一个数组,其每一项是`up, down, left, right`之一,表示这一步应该朝哪个方向走。 +对于有 `faceIds` 的NPC,也支持 `forward, backward` ,表示前进和后退 + keep为一个可选项,代表该事件移动完毕后是否消失。如果该项指定了并为true,则移动完毕后将不消失,否则以动画效果消失。 值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用该点的hide事件。 换句话说,当move事件被调用后,该点本身的事件将被禁用。 -如果指定了`"keep": true`,则相当于会在目标地点触发一个`setBlock`事件;如需能继续对话交互请在目标地点再写事件。 - +如果指定了 `"keep": true` ,则相当于会在目标地点触发一个 `setBlock` 和一个 `show` 事件;
如需能继续对话交互请在目标地点再写事件。
+阻击属性的移动效果就是这样,推箱子和阻击类似但目标点只 `setBlock` 不 `show` ,请自行注意可能的隐患。
如果想让move后的NPC/怪物仍然可以被交互,需采用如下的写法: - -``` js +``` json "4,3": [ // [4,3]是一个NPC,比如小偷 {"type": "move", "time": 750, "steps": [ // 向上移动两格,每步750毫秒 "up", "up" @@ -1406,8 +1101,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 如果我们需要移动勇士,可以使用`{"type": "moveHero"}`。 下面是该事件常见的写法: - -``` js +``` json "x,y": [ // 实际执行的事件列表 {"type": "moveHero", "time": 750, "async": true, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组 "down", "right", "forward", "backward" // 向下、右、前、后各走一步 @@ -1422,20 +1116,27 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 - +### moveAction:移动一步 +如果我们需要像事件外一样移动一步,可以使用`{"type": "moveAction"}`。 +下面是该事件常见的写法: +``` json +"x,y": [ // 实际执行的事件列表 + {"type": "moveAction", "time": 750, "direction": "forward"} +] +``` +`time` 可选,为移动的用时,单位是毫秒,不填则取玩家设置值。`direction` 可选,为移动的方向,不填则取 `forward`
+用这种办法移动时会检查通行性,也会正常触发阻激夹域捕和地形效果,如果不可通行则会去撞击(如开门打怪) ### jump:让某个NPC/怪物跳跃 如果我们需要移动某个NPC或怪物,可以使用`{"type": "jump"}`。 下面是该事件常见的写法: - -``` js +``` json [ {"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "keep": true, "async": true}, ] ``` - -from为需要跳跃的事件位置。可以省略,如果省略则移动本事件。 +from为需要跳跃的事件起点。可以省略,如果省略则跳跃本事件。 to为要跳跃到的坐标。可以省略,如果省略则跳跃到当前坐标。 @@ -1443,7 +1144,8 @@ time选项必须指定,为全程跳跃所需要用到的时间。 keep为一个可选项,同上代表该跳跃完毕后是否不消失。如果该项指定了并为true,则跳跃完毕后不会消失,否则以动画效果消失。 -如果指定了`"keep": true`,则相当于会在目标地点触发一个`setBlock`事件;如需能继续对话交互请在目标地点再写事件。 +如果指定了`"keep": true`,则相当于会在目标地点触发一个 `setBlock` 和一个 `show` 事件;
+和移动一样,如需能继续对话交互请在目标地点再写事件。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 @@ -1452,8 +1154,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 如果我们需要跳跃勇士,可以使用`{"type": "jumpHero"}`。 下面是该事件常见的写法: - -``` js +``` json [ {"type": "jump", "loc": [3,6], "time": 750, "async": true}, ] @@ -1470,13 +1171,13 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用playBgm可以播放一个背景音乐。 使用方法:`{"type": "playBgm", "name": "bgm.mp3"}` - -值得注意的是,额外添加进文件的背景音乐,需在main.js中this.bgms里加载它。 - -目前支持mp3/ogg/wav等多种格式的音乐播放。 +
音乐文件放在 `project\sounds` 文件夹,文件名在全塔属性中注册,不得使用中文。
+目前支持 `mp3/ogg/wav/flac/m4r` 等多种格式的音乐播放,不支持 `mid` 格式。 从V2.6.3开始,还提供了keep项。如果此项为真,则会记录该bgm,并且持续到下次调用本事件位置(楼层切换不改变bgm,读档也有效)。 +从V2.7开始,还提供了“从第几秒开始”的参数,可加以利用。 + 有关BGM播放的详细说明参见[背景音乐](element#背景音乐) ### pauseBgm:暂停背景音乐 @@ -1487,6 +1188,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用`{"type": "resumeBgm"}`可以恢复背景音乐的播放。 +从V2.7开始,还提供了一个勾选项,您可以选择从上次暂停的位置继续播放。 ### loadBgm:预加载一个背景音乐 使用loadBgm可以预加载一个背景音乐。 @@ -1506,9 +1208,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用playSound可以立刻播放一个音效。 使用方法:`{"type": "playSound", "name": "item.mp3"}` - -值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。 - +
音乐文件放在 `project\sounds` 文件夹,文件名在全塔属性中注册,不得使用中文。
从V2.6开始,也可以加`"stop": true`来停止之前正在播放的音效。 ### stopSound:停止所有音效 @@ -1524,27 +1224,23 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用方法: `{"type": "setVolume", "value": 90, "time": 500, "async": true}` ![](img/events/43.jpg) - -value为音量大小,在0到100之间,默认为100。设置后,BGM将使用该音量进行播放。SE的音量大小不会发生改变。 +
`value` 为音量大小,在0到100之间(玩家设置值为这个值的平方根),默认为100。设置后,BGM将使用该音量进行播放。SE的音量大小不会发生改变。 可以设置time为音量渐变时间。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 ### win:获得胜利 - -`{"type": "win", "reason": "xxx"}` 将会直接调用events.js中的win函数,并将reason作为结局传入。 - +`{"type": "win", "reason": "xxx"}` 将会直接调用脚本编辑中的 `win` 函数,并将 `reason` 作为结局传入。
该事件会显示获胜页面,并重新游戏。 可以增加`"norank": 1`来表示该结局不计入榜单。 !> 如果`reason`不为空,则会以reason作为获胜的结局! +从V2.6.6起,也提供了一个“不结束游戏”勾选项(但录像依然会停止录制),您可以勾选它然后做一些结局演出。 ### lose:游戏失败 - -`{"type": "lose", "reason": "xxx"}` 将会直接调用`events.js`中的lose函数,并将reason作为参数传入。 - +`{"type": "lose", "reason": "xxx"}` 将会直接调用脚本编辑中的lose函数,并将reason作为参数传入。
该事件会显示失败页面,并重新开始游戏。 ### restart:直接回到标题界面 @@ -1576,8 +1272,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ### input:接受用户输入数字 使用`{"type": "input"}`可以接受用户的输入的数字。 - -``` js +``` json [ {"type": "input", "text": "请输入一个数"}, // 显示一个弹窗让用户输入数字 "你刚刚输入的数是${flag:input}" // 输入结果将被赋值为flag:input @@ -1589,16 +1284,13 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 text为提示文字,可以在这里给输入提示文字。这里同样可以使用${ }来计算表达式的值。 当执行input事件时,将显示一个弹窗,并提示用户输入一个内容。 - -!> 该事件只能接受非负整数输入,所有非法的输入将全部变成`0`。例如用户在输入框内输入“你好”或者-3,都将实际得到0。 - +
!> 该事件只能接受非负整数输入,所有非法的输入将全部变成 `0` 。例如用户输入“你好”或者 `-3` ,都将实际得到 `0` 。
输入得到的结果将被赋值给flag:input,可以供后续if来进行判断。 ### input2:接受用户输入文本 类似于input事件,使用`{"type": "input2"}`可以接受用户的输入的文本。 - -``` js +``` json [ {"type": "input2", "text": "请输入你的ID"}, // 显示一个弹窗让用户输入文本 "你好,${flag:input},欢迎来到本塔" // 输入结果将被赋值为flag:input @@ -1616,12 +1308,8 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 输入得到的结果也将被赋值给flag:input,可以供后续使用。 ### if:条件判断 - -使用`{"type": "if"}`可以对条件进行判断,根据判断结果将会选择不同的分支执行。 - -其大致写法如下: - -``` js +使用`{"type": "if"}`可以对条件进行判断,根据判断结果将会选择不同的分支执行。其大致写法如下: +``` json [ {"type": "if", "condition": "...", // 测试某个条件 "true": [ // 条件成立则执行true里面的事件 @@ -1633,16 +1321,14 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 }, ] ``` - -我们可以在condition中给出一个表达式(能将`status:xxx, item:xxx, flag:xxx`来作为参数),并进行判断是否成立。 +我们可以在condition中给出一个表达式(能将`status:xxx, item:xxx, flag:xxx`等来作为参数),并进行判断是否成立。 如果条件成立,则将继续执行`"true"`中的列表事件内容。 如果条件不成立,则将继续执行`"false"`中的列表事件内容。 例如下面这个例子,每次将检查你的攻击力是否大于500,不是的场合将给你的攻击力加100点。 - -``` js +``` json [ {"type": "if", "condition": "status:atk>500", // 判断攻击力是否大于500 "true": [ // 条件成立则执行true里面的事件 @@ -1651,11 +1337,11 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 ], "false": [ // 条件不成立则执行false里的事件 "你当前攻击力为${status:atk}, 不足500!\n给你增加100点攻击力!", - {"type": "setValue", "name": "status:atk", "operator": "+=", "value": "100"}, // 攻击力加100, 接着会执行revisit事件 - ] + {"type": "setValue", "name": "status:atk", "operator": "+=", "value": "100"}, + ] // 攻击力加100, 接着会执行trigger事件 }, - {"type", "revisit"}, // 立刻重启本事件, 直到攻击力大于500后结束 -] + {"type": "trigger", "loc": ["core.nextX()","core.nextY()"]} +] // 立刻重新触发勇士面前的本事件, 直到攻击力大于500后结束 ``` ![](img/events/46.jpg) @@ -1663,8 +1349,9 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 需要额外注意的几点: - 给定的表达式(condition)一般需要返回true或false。 +- `0、null、undefined、NaN`、空字符串,也会被理解为 `false` - `flag:xxx` 可取用一个自定义变量或flag。如果从未设置过该flag,则其值默认为0。 -- 即使成功失败的场合不执行事件,对应的true或false数组也需要存在,不过简单的留空就好。 +- 即使条件成立的场合不执行事件,对应的 `true` 数组也需要存在,不过简单的留空就好。 - if可以不断进行嵌套,一层套一层;如成立的场合再进行另一个if判断等。 - if语句内的内容执行完毕后将接着其后面的语句继续执行。 @@ -1674,20 +1361,19 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 使用`{"type": "switch"}`可以比较判别值和不同分支的条件,根据判断结果选择不同的分支执行。 其大致写法如下: - -``` js +``` json [ {"type": "switch", "condition": "...", // 计算某个表达式 "caseList": [ {"case": "a", "action": [// 若表达式的值等于a则执行该处事件 - ], + ]}, {"case": "b", "nobreak": true, "action": [// 若表达式的值等于b则执行该处事件,不跳出 - ], + ]}, {"case": "default", "action": [ // 没有条件成立则执行该处里的事件 - ] + ]} ] }, ] @@ -1699,11 +1385,10 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 如果没有符合的值,则将执行`default`中的列表事件内容。 -nobreak是可选的,如果设置,则在当前条件满足并插入事件后,不跳出多重分歧,而是继续判定下一个条件。 +nobreak是可选的,如果设置,则在当前条件满足并插入事件后,不跳出多重分歧,而是无视接下来的一切条件,挨个插入事件,直到插入完一组没有nobreak的事件,或抵达多重分歧的最底部。 例如下面这个例子,将检查当前游戏难度并赠送不同属性。 - -``` js +``` json [ {"type": "switch", "condition": "flag:hard", // 判断当前游戏难度 "caseList": [ @@ -1711,15 +1396,15 @@ nobreak是可选的,如果设置,则在当前条件满足并插入事件后 "当前为简单难度,赠送100点攻防!", {"type": "setValue", "name": "status:atk", "value": "status:atk+100"}, {"type": "setValue", "name": "status:def", "value": "status:def+100"}, - ], + ]}, {"case": "1", "action": [// 若表达式的值等于1则执行该处事件 "当前为普通难度,赠送50点攻防!", {"type": "setValue", "name": "status:atk", "value": "status:atk+50"}, {"type": "setValue", "name": "status:def", "value": "status:def+50"}, - ], + ]}, {"case": "default", "action": [ // 其他难度下不赠送属性 - ] + ]} ] }, ] @@ -1736,20 +1421,19 @@ nobreak是可选的,如果设置,则在当前条件满足并插入事件后 - switch语句内的内容执行完毕后将接着其后面的语句继续执行。 另外由于`case`中的内容是会被计算的,因此如下写法也是合法的 - -```js +```json [ {"type": "switch", "condition": "true", // 条件:某一项为真时 "caseList": [ {"case": "flag:a==1", "action": [ // 如果 flag:a == 1 "走进了 flag:a==1 分支!" - ], - {"case": "flag:b>=3", "action": [ // 如果 flag:b >= 3 - "走进了 flag:b>=3 分支!" - ], - {"case": "default", "action": [ // 上述两条均布成立 + ]}, + {"case": "flag:b>=3", "action": [ // 如果 flag:a != 1 && flag:b >= 3 + "走进了 flag:a!=1 && flag:b>=3 分支!" + ]}, + {"case": "default", "action": [ // 上述两条均不成立 "上述两条均不成立" - ] + ]} ] }, ] @@ -1763,11 +1447,10 @@ choices是一个很麻烦的事件,它将弹出一个列表供用户进行选 当用户做出了不同的选择,可以有着不同的分支处理。 -其完全类似于RMXP中的"显示选择项","XX的场合",只不过同样是需要使用数组来定义。 +其完全类似于RPG Maker中的"显示选择项","XX的场合",只不过同样是需要使用数组来定义。 其大致写法如下: - -``` js +``` json [ {"type": "choices", "text": "...", // 提示文字 "choices": [ @@ -1797,13 +1480,13 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 icon是可选的,如果设置则会在选项前绘制图标,其可以是一个有效的ID,或者`core.statusBar.icons`中的系统图标。 -选项可以有任意多个,但一般不要超过6个,否则屏幕可能塞不下。 +选项可以有任意多个(没有提示信息时最多为13或15个),但一般不要超过6个,否则屏幕可能塞不下。 +从V2.6.3开始,每个子选项允许附加一个“出现条件”,从而做出类似“怒气值满才显示开大招按钮”的效果。 ### confirm:显示确认框 `{"type": "confirm"}`将提供一个确认框供用户选择,其基本写法如下: - -```js +```json [ {"type": "confirm", "text": "...", // 提示文字 "default": false, // 是否默认选中【确定】 @@ -1825,13 +1508,11 @@ default可选,如果为true则显示选择项时默认选中【确定】,否 yes和no均为必填项,即用户点击确认或取消后执行的事件。 +从V2.7开始,“选择项”、“确认框”和后面要讲的“等待用户操作”事件,都允许提供一个限制时间,效果是如果玩家在若干毫秒内不做出选择,就视为什么都没选,并继续后面的事件。 +
您可以使用这个功能去制作如《新新魔塔2》那样的QTE战斗。 ### while:前置条件循环 - -从2.2.1样板开始,我们提供了循环处理(while事件)。 - -其大致写法如下: - -``` js +从V2.2.1开始,我们提供了循环处理( `while` 事件)。其大致写法如下: +``` json [ {"type": "while", "condition": "...", // 循环测试某个条件 "data": [ // 条件成立则执行data里面的事件 @@ -1840,16 +1521,9 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 }, ] ``` - -我们可以在condition中给出一个表达式(能将`status:xxx, item:xxx, flag:xxx`来作为参数),并进行判断是否成立。 - -如果条件成立,则将执行`"data"`中的列表事件内容。 - -该事件列表执行完毕后,将继续测试`"condition"`,如果还为true则重新进行执行data内容。 - -下面是一个输出1到10之间的数字,每隔1秒显示一个的例子。 - -``` js +它和 `if` 事件非常相似,最大的区别是,每次 `data` 事件列表执行完毕后,都将再次测试 `condition` , +
如果还为 `true` ,则将再次执行 `data` 的内容。下面是一个输出1到10之间的数字,每隔1秒显示一个的例子。 +``` json [ {"type":"while", "condition": "flag:i<=10", // 循环处理;注意flag未设置则默认为0 "data":[ @@ -1864,26 +1538,47 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ![](img/events/49.jpg) ### dowhile:后置条件循环 - -`type:dowhile`可以制作一个后置条件循环。 - -其写法与参数和`type:while`完全一致,不过与其不同的是,会先执行一次事件列表,再对条件进行判定,就和C/C++中的 `do {...} while (...);` 语法一样。 - +`type:dowhile`可以制作一个后置条件循环。其写法与参数和 `type:while` 完全一致,不过与其不同的是,
+会先执行一次事件列表(即首次执行是无条件的),再对条件进行判定,和脚本中的 `do {...} while (...);` 一样。 ![](img/events/50.jpg) +### for:计数循环 +从V2.7开始,`type:for` 可以制作一个计数循环,其基本写法为: +``` json +[ + {"type": "for", "name": "temp:A-Z", "from": "...", "to": "...", "step": "...", "data": [ + // 可以在这里引用临时变量A-Z,循环执行一些内容 + ]}, +] +``` +其中 `name` 为循环的计数器,只能使用临时变量 `A-Z` ,`from` 和 `to` 为两个表达式,表示计数器的初始值和边界值 -### break:跳出循环 +`step` 为每轮循环结束后计数器的增量,也允许填表达式甚至负数,填负数的话 `from` 当然就要比 `to` 更大或相等了。 -使用 `{"type": "break"}` 可以跳出当前循环。 +当计数器以正步长越过(即大于)边界值时,循环终止;当计数器以负步长越过(即小于)边界值时,循环也终止。 -!> 如果break事件不在任何循环中被执行,则和exit等价,即会立刻结束当前事件! +下面是13×13小地图中,模拟大黄门钥匙的使用效果: +![大黄门钥匙](./img/bigKey.png) +### forEach:数组迭代 +从V2.7开始,`type: forEach` 可以循环迭代一个数组,逐项读取其每个元素,其基本写法为: +``` json +[ + {"type": "forEach", "name": "temp:A-Z", "list": [..., ..., ..., ...], "data": [ + // 可以在这里引用临时变量A-Z,循环执行一些内容 + ]}, +] +``` +和 `for` 一样只能使用临时变量,实际执行时,每轮循环会从 `list` 的开头移除第一项,代入临时变量,直到 `list` 变空。 -### continue:继续执行当前循环 - -使用 `{"type": "continue"}` 可以继续执行当前循环。 - -上面的输出例子也可以这么写: - -``` js +要注意的是,`list` 中的字符串不会被自动解释为那些带冒号的缩写量,你需要手动调用 `core.calValue()` 去求值: +![数组迭代](./img/forEach.png) +这个事件会让勇士说出“我的`status:atk`属性为`10`”这样的句子。 +### break:跳出当前循环或公共事件 +使用 `{"type": "break"}` 可以跳出当前循环或公共事件。
+跳出公共事件的原理是,公共事件实际上是作为一个“条件为 `false` 的后置条件循环”去执行的。
+!> 如果break事件不在任何循环或公共事件中被执行,则和exit等价,即会立刻结束当前事件! +### continue:提前结束本轮循环 +使用 `{"type": "continue"}` 可以提前结束本轮循环。上面的输出例子也可以这么写: +``` json [ {"type":"while", "condition": "true", // 循环处理;永远为真 "data":[ @@ -1891,7 +1586,7 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 "${flag:i}", // 输出i {"type": "sleep","time":1000}, // 等待1秒 {"type": "if", "condition": "flag:i<10", // 测试i是否小于10 - "true": [{"type": "continue"}], // 是的,则继续循环 + "true": [{"type": "continue"}], // 是,提前结束本轮循环,避免被后面的break跳出 "false": [] }, {"type": "break"}, // 跳出循环 @@ -1901,9 +1596,7 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ``` ![](img/events/51.jpg) - -!> 如果continue事件不在任何循环中被执行,则和exit等价,即会立刻结束当前事件! - +
!> 如果continue事件不在任何循环或公共事件中被执行,则和exit等价,即会立刻结束当前事件! ### wait:等待用户操作 使用 `{"type": "wait"}` 可以等待用户进行操作(如点击、按键等)。 @@ -1914,8 +1607,7 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 下面是一个while事件和wait合并使用的例子,这个例子将不断接收用户的点击或按键行为,并输出该信息。 如果用户按下了ESC或者点击了屏幕正中心,则退出循环。 - -``` js +``` json [ {"type": "while", "condition": "true", // 永久循环 "data": [ @@ -1944,12 +1636,11 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ![](img/events/52.jpg) 从V2.6.6开始,也允许直接在`type:wait`中增加`data`项判定按键或点击坐标。 - -```js +```json [ {"type": "wait", "data": [ - {"case": "keyboard", "keycode": 13, "action": [ - {"type": "comment", "text": "当按下回车(keycode=13)时执行此事件"}, + {"case": "keyboard", "keycode": "13,32", "action": [ + {"type": "comment", "text": "按下回车(keycode=13)或空格(keycode=32)时执行此事件"}, ]}, {"case": "mouse", "px": [0,32], "py": [0,32], "action": [ {"type": "comment", "text": "当点击地图左上角时执行此事件"}, @@ -1966,8 +1657,7 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 action为如果满足该条件时应该执行的事件列表。 - - +从V2.7开始,提供了“限制时间”,如果玩家在若干毫秒内不做出响应,就会跳过这些场合,直接继续执行后面的事件。 ### waitAsync:等待所有异步事件执行完毕 上面有很多很多的异步事件(也就是执行时不等待执行完毕)。 @@ -1994,8 +1684,7 @@ action为如果满足该条件时应该执行的事件列表。 UI绘制事件。 `{"type": "clearMap"}`可以清除`uievent`画布的内容。 - -```js +```json [ {"type": "clearMap", "x": 0, "y": 0, "width": "flag:width", "height": 416}, // 清除画布的一部分 {"type": "clearMap"}, // 清空并删除画布 @@ -2011,8 +1700,7 @@ x, y, width, height均可选,表示要清除的坐标和长宽。也可以使 UI绘制事件。 此项可以设置`uievent`画布的各项属性。 - -```js +```json [ {"type": "setAttribute", "font": "17px Verdana", "fillStyle": [255,0,0,1]}, ] @@ -2025,7 +1713,7 @@ UI绘制事件。 - `lineWidth`:线宽度,必须是正整数 - `alpha`:不透明度,必须是0到1之间的浮点数 - `align`:对齐方式,只能是`left`, `center`, `right`,分别代表左对齐,居中和右对齐 -- `baseline`:基准线,只能是`top`, `middle`, `alphabetic`, `bottom`,分别代表顶部,居中,标准值和底部。 +- `baseline`:基准线,只能是`top`, `middle`, `alphabetic`, `bottom`, `ideographic`, `hanging`,分别代表顶部,居中,标准值、底部、中文和悬挂。 - `z`:画布的z值,必须是正整数。初始创建时画布的z值是135。请注意,闪烁光标所在画布的z值永远比该画布大1。 ### fillText:绘制文本 @@ -2033,10 +1721,9 @@ UI绘制事件。 UI绘制事件。 此项可以绘制一行文本。 - -```js +```json [ - {"type": "fillText", "text":"要绘制的文本", "x": 10, "y": 20, "maxWidth": 50} + {"type": "fillText", "text": "要绘制的文本", "x": 10, "y": 20, "maxWidth": 50} ] ``` @@ -2055,10 +1742,9 @@ maxWidth可选,如果设置且不为0,则代表要绘制的最大宽度; UI绘制事件。 此项可以绘制一行描边文本。 - -```js +```json [ - {"type": "fillText", "text":"要绘制的描边文本", "x": 10, "y": 20, "style": [255,0,0,1]} + {"type": "fillText", "text":"要绘制的描边文本", "x": 10, "y": 20, "style": [255,0,0,1]} ] ``` @@ -2070,15 +1756,15 @@ style可选,如果设置需要是三元组RGB或四元组RBGA,代表绘制 font可选,如果设置则是要绘制的字体。 +从V2.7开始,可以选择描边的颜色,初始为黑色。描边原理是先用描边色绘制4份一样的偏移文本,再以文本色覆盖。 ### drawTextContent:绘制多行文本 UI绘制事件。 此项可以绘制多行文本。 - -```js +```json [ - {"type": "drawTextContent", "text":"要绘制的多行文本", "left": 10, "top": 20, "maxWidth": 100} + {"type": "drawTextContent", "text": "要绘制的多行文本", "left": 10, "top": 20, "maxWidth": 100} ] ``` @@ -2099,12 +1785,9 @@ fontSize可选,为字体大小,如果不设置则使用剧情文本设置中 lineHeight可选,绘制的行距值,如果不设置则使用fontSize*1.3(即1.3倍行距)。 此项不支持字体样式的设置,使用的是全塔属性中的全局字体;如有需要请使用“设置全局属性”事件来设置字体样式。 - -### fillRect:绘制矩形 - -UI绘制事件。此项可以绘制一个矩形。 - -```js +### fillRect:绘制实心矩形 +UI绘制事件。此项可以绘制一个实心矩形。 +```json [ {"type": "fillRect", "x": 100, "y": 100, "width": 120, "height": 120, "style": [255,0,0,1]} ] @@ -2113,12 +1796,9 @@ UI绘制事件。此项可以绘制一个矩形。 x, y, width, height必填,为要绘制的起点坐标和宽高;也可以用`flag:xxx`。 color可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 - -### strokeRect:绘制矩形边框 - -UI绘制事件。此项可以绘制一个矩形边框。 - -```js +### strokeRect:绘制空心矩形 +UI绘制事件。此项可以绘制一个空心矩形。 +```json [ {"type": "strokeRect", "x": 100, "y": 100, "width": 120, "height": 120, "style": [255,0,0,1], "lineWidth": 4} ] @@ -2131,10 +1811,8 @@ style可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 lineWidth可选,表示边框的线宽。 ### drawLine:绘制线段 - -UI绘制事件。此事件可以绘制一个函数。 - -```js +UI绘制事件。此事件可以绘制一条直线段,循环使用的话您甚至可以画出带渐变色的复杂曲线。 +```json [ {"type": "drawLine", "x1": 0, "y1": 0, "x2": "flag:x", "y2": 200, "style": [255,0,0,1]} ] @@ -2146,18 +1824,17 @@ style可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 lineWidth可选,表示边框的线宽。 +下面是一个循环绘制直线段形成抛物线的例子:
+`for (var x = 0; x < 20; ++x) core.drawLine('ui', x * 20, x * x, (x + 1) * 20, (x + 1) ** 2)` ### drawArrow:绘制箭头 UI绘制事件。此事件可以绘制一个箭头。 参数和写法与`drawLine`完全一致,只不过是会多画一个箭头标记。 - -### fillPolygon:绘制多边形 - +### fillPolygon:绘制实心多边形 UI绘制事件。此事件可以绘制一个多边形。 - -```js -[ +```json +[ // 实际绘制原理是把顶点依次用直线段连起来,因此请自行注意顶点的顺序 {"type": "fillPolygon", "nodes": [[0,0],[0,100],[100,0]], "style": [255,0,0,1]} ] ``` @@ -2165,18 +1842,13 @@ UI绘制事件。此事件可以绘制一个多边形。 nodes必填,为一个二维数组,其中每一项都是多边形一个顶点坐标。(与显示/隐藏事件写法相同) style可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 - -### strokePolygon:绘制多边形边框 - +### strokePolygon:绘制空心多边形 UI绘制事件。此事件可以绘制一个多边形边框。 参数列表和`fillPolygon`基本相同,不过多了一个`lineWidth`表示的绘制线宽。 - -### fillCircle:绘制圆 - -UI绘制事件。此项可以绘制一个圆。 - -```js +### fillCircle:绘制实心圆盘 +UI绘制事件。此项可以绘制一个圆 +```json [ {"type": "fillCircle", "x": 100, "y": 100, "r": 10, "style": [255,0,0,1]} ] @@ -2185,18 +1857,20 @@ UI绘制事件。此项可以绘制一个圆。 x, y, r必填,为要绘制的圆心和半径;也可以用`flag:xxx`。 color可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 - -### strokeCircle:绘制圆边框 - +### strokeCircle:绘制空心圆环 UI绘制事件。此项可以绘制一个圆边框。 参数列表和`fillCircle`基本相同,不过多了一个`lineWidth`表示的绘制线宽。 +如果您需要绘制椭圆,可以使用参数方程。譬如: +``` js +for(var x = 0; x < 6.28; x += .01) + core.drawLine('ui', 208 + 200 * Math.cos(x), 208 + 100 * Math.sin(x), 208 + 200 * Math.cos(x + .01), 208 + 100 * Math.sin(x + .01)) +``` ### drawImage:绘制图片 UI绘制事件。此事件可以绘制一个图片。 - -```js +```json [ {"type": "drawImage", "image": "bg.jpg", "x": 0, "y": 0}, // 在(0,0)绘制bg.jpg {"type": "drawImage", "image": "bg.jpg", "x": 0, "y": 0, "w": 100, "h": 100}, // 在(0,0)绘制bg.jpg,且放缩到100x100 @@ -2219,8 +1893,7 @@ http://www.w3school.com.cn/html5/canvas_drawimage.asp ### drawIcon:绘制图标 UI绘制事件。此事件可以绘制一个图标。 - -```js +```json [ {"type": "drawIcon", "id": "yellowKey", "x": 100, "y": 100}, // 在(100,100)绘制黄钥匙 ] @@ -2230,11 +1903,11 @@ id必填,为要绘制的图标ID。可以是一个注册过的图标ID,也 x, y必填,为要绘制的左上角坐标。width和height可选,如果设置则会将图标放缩成对应的宽高。 +从V2.7开始,对于多帧图块,允许指定绘制哪一帧,默认绘制第 `0` 帧。 ### drawBackground:绘制背景图 UI绘制事件。此事件可以绘制一个背景图。 - -```js +```json [ {"type": "drawBackground", "background": "winskin.png", "x": 0, "y": 0, "width": 100, "height": 100}, ] @@ -2249,32 +1922,27 @@ x, y, width, height必填,分别为要绘制的起点坐标和长宽。 ### drawSelector:绘制闪烁光标 UI绘制事件。此事件可以绘制闪烁光标。 - -```js +```json [ - {"type": "drawSelector", "image": "winskin.png", "x": 0, "y": 0, "width": 100, "height": 100, "clear": true}, - {"type": "drawSelector"} // 清除闪烁光标 + {"type": "drawSelector", "image": "winskin.png", "code": 1, "x": 0, "y": 0, "width": 100, "height": 100}, + {"type": "drawSelector", "code": 1} // 清除1号闪烁光标 ] ``` image为要绘制的WindowSkin图片名;如果不填则视为“清除闪烁光标”。 -x, y, width, height分别为要绘制的起点坐标和长宽。clear可选,如果为true则在绘制前清空已有光标。 - -请注意,同时只会有一个闪烁光标存在,如果创建多个则后者会替换前者。 - -闪烁光标将会一直存在即使事件流结束;请使用本事件并不填`image`来清除闪烁光标。 +`x, y, width, height` 分别为要绘制的起点坐标和长宽。`code` 为要绘制或清除的光标编号,必须为正整数。 +闪烁光标将会一直存在即使事件流结束;请使用本事件并只填 `code` 来清除闪烁光标。 ### function: 自定义JS脚本 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 - -``` js +``` json [ - {"type": "function", "function": function(){ // 执行一段js脚本 + {"type": "function", "function": "function(){ // 执行一段js脚本 // 这里写js代码 - alert(core.getStatus("atk")); // 弹窗显示勇士的攻击力 - }}, + alert(core.getStatus('atk')); // 弹窗显示勇士的攻击力 + }"}, ] ``` @@ -2295,7 +1963,7 @@ var toFloor = core.floorIds[core.rand(core.floorIds.length)]; // 随机一个楼 var toX = core.rand(13), toY = core.rand(13); // 随机一个点 core.insertAction([ {"type": "changeFloor", "floorId": toFloor, "loc": [toX, toY]} // 插入一个changeFloor事件,并在该脚本结束后执行。 -]) +]) // 你会注意到此时事件参数允许使用var型变量,这也是insertAction的强大之处 // 请勿直接调用 core.changeFloor(toFloor, ...),这个代码是异步的,会导致事件处理和录像出问题! ``` @@ -2343,16 +2011,13 @@ core.insertAction([ 我们可以发现,就目前而且,每个点的事件是和该点进行绑定,并以该点坐标作为唯一索引来查询。 而有时候,我们往往需要在同一个点存在多个不同的事件。这涉及到同一个点的多事件处理。 - -我们可以依靠两来实现。**`setBlock`事件**和**if+flag的条件判断**。 - +
我们可以依靠两种指令来实现。**`setBlock` 事件**和 **`if+flag` 的条件判断**。
下面以几个具体例子来进行详细说明。 ### 打怪掉宝 我们注意到怪物和道具都是系统默认事件,因此无需写events,而是直接在afterBattle中setBlock即可。 - -``` js +``` json "afterBattle": { "x,y": [ {"type": "setBlock", "number": 21} // 变成黄钥匙。注意是当前点因此可省略floorId和loc @@ -2367,8 +2032,7 @@ core.insertAction([ 注意到events中不覆盖trigger,则还是怪物时,存在系统trigger因此会战斗并触发afterBattle;变成NPC后没有系统trigger因此会触发自定义事件(楼层转换)。 请注意打死怪物时默认会禁用该点,因此替换后需要手动进行show来启用。 - -``` js +``` json "events": { "x,y": [ {"type": "changeFloor", "loc": [0,0], "floorId": "MT1"} @@ -2391,8 +2055,7 @@ core.insertAction([ 因此需要我们需要覆盖系统trigger(getItem),并覆盖noPass。 通过if来判断有没有获得圣水,没有则触发圣水(生命x2)然后变成墙,否则不执行。 - -``` js +``` json "events": { "x,y": { "trigger": "action", // 覆盖系统trigger,默认的getItem不会执行 @@ -2401,13 +2064,13 @@ core.insertAction([ {"type": "if", "condition": "flag:hasSuperPotion", // 条件判断:是否喝过圣水 "true": [], // 喝过了,不执行 "false": [ - {"type":"setValue", "name":"status:hp", "value":"status:hp*2"}, // 生命翻倍 - {"type":"setBlock", "number": 1}, // 将该点变成墙 - {"type":"setValue", "name":"flag:hasSuperPotion", "value": "true"} // 标记已经喝过了 - ] + {"type":"setValue", "name":"status:hp", "value":"status:hp*2"}, + {"type":"setBlock", "number": 1}, // 生命翻倍,将该点变成墙 + {"type":"setValue", "name":"flag:hasSuperPotion", "value": "true"} + ] // 标记已经喝过了。你会发现这种写法并不涉及圣水道具本身,它只是个素材 } ] - ] + } } ``` @@ -2416,23 +2079,22 @@ core.insertAction([ 总之,记住如下两点: - 可以使用setBlock来更改一个图块。 - - 可通行状态遵循覆盖原则,即**首先取该图块的默认noPass属性,如果剧本的events中定义该点的noPass则覆盖**。 - - 触发器(trigger)亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是battle,道具是getItem,门是openDoor),如果剧本的events中定义了该点的trigger则覆盖**。 + - 可通行状态遵循覆盖原则,即**首先取该图块的默认 `canPass` 属性,如果该点的普通事件中定义该点的 `noPass` 则覆盖**。 + - 触发器(trigger)亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是 `battle` ,道具是 `getItem` ,门是 `openDoor` ),如果该点绑定了楼层转换事件(绿)或普通事件(红)中覆盖触发器则覆盖**。 - 可以通过if语句和flag来控制自定义事件具体走向哪个分支。 - - 如果弄不清楚系统trigger和自定义事件等的区别,也可以全部覆盖为自定义事件,然后通过type:battle,type:openDoor等来具体进行控制。 + - 如果弄不清楚系统trigger和覆盖触发器的区别,也可以全部覆盖为自定义事件,然后通过type:battle,type:openDoor等来具体进行控制。 + - 如果弄不清楚通行性,也可以全部覆盖为不可通行,并在本该通行的场合用事件让勇士前进。 + - 在上述两种覆盖的情形下,请善用“图块ID:x,y”和“图块类别:x,y”这两个缩写量去判断该图块是什么。 - 多事件处理时请不要使用`changeFloor`那一项,而是使用`events`或者`afterXXX`来处理。 - -## 并行事件处理 - -从V2.4.3后,H5样板开始支持并行事件处理。 - +## 并行脚本处理 +从V2.4.3后,H5样板开始支持并行脚本处理。
在脚本编辑里面提供了一个parallelDo函数,这个函数可以用来做并行处理内容。 从V2.5.2开始,每层楼的楼层属性中也增加了一个parallelDo选项,可以在里面写任何脚本代码。该部分代码仅在人物在该楼层时才会被反复执行。 ``` js "parallelDo": function (timestamp) { - // 并行事件处理,可以在这里写任何需要并行处理的脚本或事件 + // 并行脚本处理,可以在这里写任何需要并行处理的脚本或事件 // 该函数将被系统反复执行,每次执行间隔视浏览器或设备性能而定,一般约为16.6ms一次 // 参数timestamp为“从游戏资源加载完毕到当前函数执行时”的时间差,以毫秒为单位 @@ -2449,7 +2111,7 @@ core.insertAction([ } // 下面是一个并行事件开门的样例 - /* + /* V2.6.4起,提供了自动事件,不再推荐用并行脚本去做这样的处理 // 如果某个flag为真 if (core.hasFlag("xxx")) { // 千万别忘了将该flag清空!否则下次仍然会执行这段代码。 @@ -2463,8 +2125,7 @@ core.insertAction([ */ } ``` - -该函数将被系统反复执行,执行间隔试浏览器或设备性能而定,一般约为16.6ms一次。 +该函数将被系统反复执行,执行间隔试浏览器或设备性能而定,一般约为16.6ms一次(60fps)。 此函数有个参数timestamp,为**从游戏资源加载完毕到当前函数执行时**的时间差,以毫秒为单位。可以使用此参数来制作一些时间相关内容或者特效等。 @@ -2472,7 +2133,7 @@ core.insertAction([ !> 判定flag后千万别忘了将该flag清空!否则下次仍然会执行这段代码。 -每层楼的并行事件处理类似,只有角色在当前楼层时才会反复执行当前楼层中parallelDo部分的代码。 +每层楼的并行脚本处理类似,只有角色在当前楼层时才会反复执行当前楼层中parallelDo部分的代码。 下面是一个打怪开门的样例:(假设每打一个怪的战后事件把`flag:door`+1) @@ -2489,10 +2150,7 @@ if (core.getFlag("door",0)==2) { ``` ## 加点事件 - -打败怪物后可以进行加点。 - -要启用加点,首先需要在`data.js`中将`enableAddPoint`置为true。 +打败怪物后可以进行加点。要启用加点,首先需要在全塔属性中勾选“启用战后加点”。 如果要对某个怪物进行加点操作,则首先需要修改该怪物的`point`数值,代表怪物本身的加点数值。 @@ -2511,65 +2169,41 @@ if (core.flags.enableAddPoint && point > 0) { 我们可以采用上面的choices方式来给出一个商店。这样的商店确实可以有效地进行操作,但是却是"非全局"的,换句话说,只有在碰到NPC的时候才能触发商店事件。 我们可以定义"全局商店",其可以直接被快捷栏中的"快捷商店"进行调用。换句话说,我们可以定义快捷商店,让用户在任意楼层都能快速使用商店。 - -全局商店定义在`data.js`中,找到shops一项。 - +
全局商店定义在全塔属性中,找到shops一项。
从V2.2以后,全局商店也可以使用图块进行编辑,但仍需知道每一项的使用。 - -``` js -"shops": [ // 定义全局商店(即快捷商店) - { - "id": "moneyShop1", // 商店唯一ID - "name": "贪婪之神", // 商店名称(标题) - "icon": "blueShop", // 商店图标,blueShop为蓝色商店,pinkShop为粉色商店 - "textInList": "1F金币商店", // 在快捷商店栏中显示的名称 - "use": "money", // 商店所要使用的。只能是"money"或"exp"。 - "commonTimes": true, // 是否使用全局次数 - "mustEnable": false, // 如果未开启则不显示在状态栏中 - "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times(访问次数)作为参数计算。 - "text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字。可以使用${need}表示上面的need值。 - "choices": [ // 商店的选项 - // effect可以对status,item和flag进行操作;必须是X+=Y的形式,其中Y可以是一个表达式 - {"text": "生命+800", "effect": "status:hp+=800"}, // 生命+800 - {"text": "攻击+4", "need": 30, "effect": "status:atk+=4"}, // 规定具体的数值 - {"text": "防御+2,护盾+4", "effect": "status:def+=2;status:mdef+=4"}, // 多个效果用分号分开 - ] - } -], +``` json +"shops": [{ + "id": "shop1", + "text": "\t[贪婪之神,blueShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", + "textInList": "1F金币商店", + "mustEnable": false, + "disablePreview": false, + "choices": [ + {"text": "生命+800", "need": "status:money>=20+2*flag:shop1", "action": [ + {"type": "setValue", "name": "status:money", "operator": "-=", "value": "20+2*flag:shop1"}, // 从V2.7起,全局商店由自带插件实现。标准商店中需要手动扣减金币和增加访问次数 + {"type": "setValue", "name": "flag:shop1", "operator": "+=", "value": "1"}, + {"type": "setValue", "name": "status:hp", "operator": "+=", "value": "800"}]}]}] ``` - -全局商店全部定义在`data.js`中的shops一项里。商店以数组形式存放,每一个商店都是其中的一个对象。 - - id 为商店的唯一标识符(ID),请确保任何两个商店的id都不相同 -- name 为商店的名称(打开商店后的标题) -- icon 为商店的图标,在icons.js的npcs中定义。如woman可代表一个商人。 +- text 为商店所说的话。可以使用\t效果和${}表达式求值。 - textInList 为其在快捷商店栏中显示的名称,如"3楼金币商店"等 -- use 为消耗的类型,是金币(money)还是经验(exp)。 -- commonTimes 是否使用全局次数;如果为true则可以多个快捷商店共享相同的次数 - mustEnable 是否必须是只在开启状态才在列表显示;如果此项为true则未开启的快捷商店不予显示 -- need 是一个表达式,计算商店所需要用到的数值。 - - 可以将times作为参数,times为该商店已经访问过的次数,第一次访问时times是0。 - - 如果对于每个选项都需要不同的数值,这里设为"-1";可参见下面经验商店的例子。 -- text 为商店所说的话。可以用${need}表示需要的数值。 +- disablePreview 是否禁止预览,如果此项为true则不能预览 - choices 为商店的各个选项,是一个list,每一项是一个选项 - - text为显示文字。请注意这里不支持 ${} 的表达式计算。 - - effect 为该选项的效果;effect必须是 `status:xxx+=yyy`, `item:xxx+=yyy`或`flag:xxx+=yyy`的形式。即中间必须是+=符号。 - - 如有多个effect(例如升级全属性提升),使用分号分开。 + - text 为显示文字。同样支持 ${} 的表达式计算,预览时、或未满足购买条件的项则会显示为灰色。 + - need 和 condition 分别为该选项的购买条件和出现条件,“出现条件”的含义和“显示选择项”事件中一致。 + - action 为该选项的效果;从V2.7开始,选项效果支持任意类型的事件,当然也就不再支持长按连续购买。 -像这样定义了全局商店后,即可在快捷栏中看到。 +像这样定义了全局商店后,即可在V键(从V2.7开始,不再支持K键)菜单或非自绘状态栏点击金币图标时看到。 请注意,快捷商店默认是不可被使用的。直到至少调用一次自定义事件中的 `{"type": "openShop"}` 打开商店后,才能真正在快捷栏中被使用。 - -``` js +``` json // 事件列表 [ // 打开商店前,你也可以添加自己的剧情 // 例如,通过if来事件来判断是不是第一次访问商店,是的则显示一段文字(类似宿命的华音那样) - {"type": "openShop", "id": "moneyShop1"}, // 这里的id要和data.js中你定义的商店ID完全一致 - // 调用openShop事件后,所有当前事件都会被结束(同exit事件),然后打开一个全局商店 - - // 如果需要禁用商店,则需要调用disableShop事件 - {"type": "disableShop", "id": "moneyShop1"} + {"type": "openShop", "id": "moneyShop1"}, // 这里的id要和全塔属性中你定义的商店ID完全一致 + {"type": "disableShop", "id": "moneyShop1"} // 如果需要禁用商店,则需要调用disableShop事件 ], ``` @@ -2579,9 +2213,10 @@ if (core.flags.enableAddPoint && point > 0) { 另外需要注意的一点就是,每层楼都有一个 canUseQuickShop 选项。如果该选项置为false则无法在该层使用快捷商店。 -**从V2.6开始,也提出了“公共事件化的全局商店”,即打开使用全局商店实际上是执行一个公共事件。** +从V2.7开始,`canUseQuickShop` 函数被挪动到了插件编写中,且对三种全局商店都有效,您也可以加以利用。 -```js +**从V2.6开始,也提出了“公共事件化的全局商店”,即打开使用全局商店实际上是执行一个公共事件。** +```json "shops": [ // 定义公共事件化的全局商店 { @@ -2602,20 +2237,19 @@ if (core.flags.enableAddPoint && point > 0) { `args`可选,为向该公共事件传递的参数,参见[type:insert](#insert:插入公共事件或另一个地点的事件并执行)的说明。 +从V2.6.4开始,自带了道具商店插件,您可以自由设置道具的价格、出售和回收价、进货数量、出现条件等。 ## 系统引发的自定义事件 +我们知道,所有普通事件都是需要定义在`"x,y"`处,并且得让用户经过或撞上才能触发的。 -我们知道,所有自定义事件都是需要定义在`"x,y"`处,并且得让用户经过或撞上才能触发的。 +但是有一系列的事件,例如战斗、获取道具、开门等,是系统已经预先设定好的事件,我们如果将其覆盖触发器,原本的战斗等事件就会被覆盖。 -但是有一系列的事件,例如战斗、获取道具、开门等,是系统已经预先设定好的事件,我们不能将其覆盖为自定义事件,否则原本的战斗等事件会被覆盖。 - -为了解决此问题,在每层的剧本中引入了三个元素:`afterBattle`, `afterGetItem`, `afterOpenDoor`。 +为了解决此问题,在每个点的事件中引入了三个元素:`afterBattle`, `afterGetItem`, `afterOpenDoor`。 - 当某个战斗结束后,将执行`afterBattle`中,对应位置的事件。 -- 当获取某个道具后,将执行`afterGetItem`中,对应位置的事件。 +- 当获取某个道具后,将执行`afterGetItem`中,对应位置的事件。您还可以指定该事件能否被轻按触发。 - 当开了某个门后,将执行`afterOpenDoor`中,对应位置的事件。 例如,下面就是一个典型的杀怪开门的例子。每当杀死一个守卫机关门的怪物,将检查是否满足打开机关门的条件。如果是,则开启机关门。 - -``` js +``` json "afterBattle": { // 战斗后可能触发的事件列表 "9,6": [ // 初级卫兵1 {"type": "setValue", "name": "flag:door", "value": "flag:door+1"}, // 将"door"这个自定义flag加一 @@ -2634,8 +2268,8 @@ if (core.flags.enableAddPoint && point > 0) { ], "false": [] // 如果条件不成立则无事件触发 }, - ], -}, + ], // 从V2.6.4起,提供了右击快速绑定机关门的操作,绑定以后,战后事件只负责累加杀怪数, +}, // 而判定杀怪数是否足够、以及开门的操作则由机关门处的自动事件执行 ``` ![](img/events/60.jpg) @@ -2656,13 +2290,11 @@ if (core.flags.enableAddPoint && point > 0) { 默认情况下,拾取冰面上道具后将停止滑冰行为。如果要继续滑冰,请在`afterGetItem`中插入公共事件:滑冰事件。打怪和开门同理。 -!> 滑冰图块的数字是167,请勿修改此数字! - +!> 滑冰图块的默认触发器是 `ski` ,您可以将别的图块也改为这个默认触发器并画在背景层,从而做出五颜六色的冰面。 ## 推箱子事件 +关于推箱子,存在三种状态:花(flower),箱子(box)和已经推到花的箱子(boxed)。 -关于推箱子,存在三种状态:花(168),箱子(169)和已经推到花的箱子(170)。 - -!> 推箱子的前方不允许存在任何事件(花除外),包括已经禁用的自定义事件。 +!> 推箱子的前方不允许存在任何事件(花除外),包括已经禁用的红绿事件或者重生怪。(会导致箱子消失) 推完箱子后将触发脚本编辑中的afterPushBox函数,你可以在这里进行开门判断。 @@ -2687,13 +2319,12 @@ if (core.flags.enableAddPoint && point > 0) { 有时候光战后事件`afterBattle`是不够的,我们可能还需要战前剧情,例如Boss战之前和Boss进行一段对话。 -要使用战前剧情,首先你需要覆盖该店的系统默认事件,改成你自己的自定义事件,然后在战前剧情后调用`{"type": "battle"}`强制战斗。 +要使用战前剧情,你需要在该点创建普通事件并覆盖触发器,然后在战前剧情后调用`{"type": "battle"}`强制战斗。 -值得注意的是,使用这种自定义事件来覆盖系统的默认战斗事件时,可以增加`displayDamage`代表该点是否需要显伤。此项可省略,默认为有显伤。 - -``` js +顺便一说,普通事件中还提供了是否显伤的勾选框,您可以不勾选,从而使该怪物在地图上不显示预估伤害。 +``` json { - "trigger": "action", // 覆盖该点触发器,变成自定义事件 + "trigger": "action", // 覆盖触发器 "data": [ // 该点的自定义事件列表 // ... 战前剧情 {"type": "battle", "id": "xxx"}, // 强制战斗 @@ -2710,11 +2341,10 @@ if (core.flags.enableAddPoint && point > 0) { 本塔也支持经验升级,即用户杀怪获得经验后,可以到达某些数值自动进阶,全面提升属性。 -要经验升级,你需要先在`data.js`中的全局变量中启用。你需要将`enableExp`启用经验,且`enableLevelUp`启用进阶。同时你也可以将`enableLv`置为true以在状态栏中显示当前等级(境界)。 +要经验升级,你需要在全塔属性中同时勾选“显示等级称号”、“显示经验值”、“开启自动进阶”。 -同时,你还需要在`data.js`中的`levelUp`来定义每一个进阶所需要的经验值,以及进阶时的效果。 - -``` js +同时,你还需要在全塔属性中的 `levelUp` 来定义每一个进阶所需要的经验值,以及进阶时的效果。 +``` json "levelUp": [ // 经验升级所需要的数值,是一个数组 {"need": "0", "title": "", "action": []}, // 第一项为初始等级,仅title生效 // 每一个里面可以含有四个参数 need, title, clear, action @@ -2742,53 +2372,32 @@ if (core.flags.enableAddPoint && point > 0) { - `action` 为本次等级要执行的事件流。 ## 开始游戏与难度分歧 +游戏开始时将调用全塔属性中的 `startText` 事件,我们可以修改它的内容来对于不同难度分别设置初始属性。 -游戏开始时将调用`events.js`中的`startGame`函数。 - -它将显示全塔属性中的startText内容(可以修改成自己的),提供战斗动画开启选择,设置初始福利,并正式开始游戏。 - -我们可以修改脚本编辑`setInitData`函数来对于不同难度分别设置初始属性。 - -其参数hard分为对应全塔属性中levelChooseButtons中的第二项,分别对应不同的难度,并会在游戏中传输,在状态栏显示。 - -针对不同的难度,我们可以设置一些难度分歧。 - -``` js +其中 `core.status.hard` 为全塔属性“难度分歧”二维数组的第二列(四个单词),会以红色显示在状态栏一角。 +``` json ////// 不同难度分别设置初始属性 ////// -"setInitData": function (hard) { - if (hard=='Easy') { // 简单难度 - core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度 - // 可以在此设置一些初始福利,比如设置初始生命值可以调用: - // core.setStatus('hp', 10000); - // 赠送一把黄钥匙可以调用 - // core.setItem('yellowKey', 1); - } - if (hard=='Normal') { // 普通难度 - core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度 - } - if (hard=='Hard') { // 困难难度 - core.setFlag('hard', 3); // 可以用flag:hard来获得当前难度 - } -} -``` - -难度会赋值给`flag:hard`,即我们可以在上述if语句的condition中进行判断。 - -请不要在任何事件中修改对`flag:hard`进行任何赋值(修改它),不然可能会造成一些不可知的后果。 - -如果是在游戏中根据不同的难度分析来控制地图,比如简单难度删除绿钥匙,则可以在该楼层的`firstArrive`(首次到达楼层)中进行条件判定。 - -```js -// 该楼层的firstArrive -[ - {"type": "if", "condition": "flag:hard==1", // 条件判定flag:hard是否为1 - "true": [{"type":"hide","loc":[0,0]}], // 如果是,则隐藏某点事件 - "false": [] - } +"startText": [ // 根据难度分歧设置<变量:hard>并给其他初始值 + {"type": "switch", "condition": "core.status.hard", "caseList": [ + {"case": "'Easy'", "action": [ // 可以在这里修改初始道具或属性,比如赠送黄钥匙等 + {"type": "setValue", "name": "flag:hard", "value": "1"}, + {"type": "hide", "loc": [[6,6]], "floorId": "MT10"} // 简单难度删除主塔10层中央的图块 + ]}, + {"case": "'Normal'", "action": [ + {"type": "setValue", "name": "flag:hard", "value": "2"} + ]}, + {"case": "'Hard'", "action": [ + {"type": "setValue", "name": "flag:hard", "value": "3"} + ]}, + {"case": "'Hell'", "action": [ + {"type": "setValue", "name": "flag:hard", "value": "4"} + ]} + ]}, // 初始剧情 + "Hi,欢迎来到 HTML5 魔塔样板!\n本样板由艾之葵制作,可以让你在不会写任何代码\n的情况下也能做出属于自己的H5魔塔!", + "这是游戏开始时的剧情。\n你可以在这里写上自己的内容。\n赶快来试一试吧!" ] ``` - -![](img/events/59.jpg) +`flag:hard` 为计榜难度系数,即在线游戏排行榜中只统计此值最大且有人通关的难度。游戏过程中请勿二次修改它。 ========================================================================================== diff --git a/_docs/img/bigKey.png b/_docs/img/bigKey.png new file mode 100644 index 00000000..384d53b8 Binary files /dev/null and b/_docs/img/bigKey.png differ diff --git a/_docs/img/changefloorBlockly.png b/_docs/img/changefloorBlockly.png new file mode 100644 index 00000000..558bb804 Binary files /dev/null and b/_docs/img/changefloorBlockly.png differ diff --git a/_docs/img/events/13.jpg b/_docs/img/events/13.jpg index af81a7f0..075b0814 100644 Binary files a/_docs/img/events/13.jpg and b/_docs/img/events/13.jpg differ diff --git a/_docs/img/events/26.jpg b/_docs/img/events/26.jpg index 6eaca21b..f2b11f44 100644 Binary files a/_docs/img/events/26.jpg and b/_docs/img/events/26.jpg differ diff --git a/_docs/img/events/27.jpg b/_docs/img/events/27.jpg index 6c5b3a25..021b48e6 100644 Binary files a/_docs/img/events/27.jpg and b/_docs/img/events/27.jpg differ diff --git a/_docs/img/events/46.jpg b/_docs/img/events/46.jpg index 1eea4e37..0fff3f91 100644 Binary files a/_docs/img/events/46.jpg and b/_docs/img/events/46.jpg differ diff --git a/_docs/img/events/48.jpg b/_docs/img/events/48.jpg index 2086b1c3..4a0d31a8 100644 Binary files a/_docs/img/events/48.jpg and b/_docs/img/events/48.jpg differ diff --git a/_docs/img/events/7.png b/_docs/img/events/7.png index a2f60686..80b15c87 100644 Binary files a/_docs/img/events/7.png and b/_docs/img/events/7.png differ diff --git a/_docs/img/forEach.png b/_docs/img/forEach.png new file mode 100644 index 00000000..0b9ed39d Binary files /dev/null and b/_docs/img/forEach.png differ diff --git a/_docs/img/keyboard.png b/_docs/img/keyboard.png index c2b9d241..9f2b057a 100644 Binary files a/_docs/img/keyboard.png and b/_docs/img/keyboard.png differ diff --git a/_docs/img/server.png b/_docs/img/server.png index eb905427..74488f31 100644 Binary files a/_docs/img/server.png and b/_docs/img/server.png differ diff --git a/_docs/index.md b/_docs/index.md index d3943d2c..31bacae7 100644 --- a/_docs/index.md +++ b/_docs/index.md @@ -1,18 +1,17 @@ # HTML5 魔塔样板说明文档 +?> 目前版本**v2.7**,上次更新时间:* {docsify-updated} * +
当您打开这份帮助文档的瞬间,相信您一定是抱着幼年时的游戏开发梦想前来的。
+众所周知,即时制游戏的开发要比非即时游戏难上许多,像素级游戏的开发又要比网格地图游戏难上许多。
在非即时的网格地图游戏(譬如策略战棋)中,有一类游戏叫做“固定数值RPG”,或者简称“魔塔”。
这是一种基于运筹学的数学优化建模游戏,虽然小众,却不失有自己的圈子。 -?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * - -众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 +在当下,魔塔的趋势是向移动端发展,网络上也常常能见到“求手机魔塔”的提问。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔(譬如《魔塔盛宴》和一些登陆了TapTap的魔塔,以及最新的由RPG Maker MV制作的《盖伦排位记》等)虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 但是,现在我们有了HTML5。 HTML5的画布(canvas)以及它被Android/iOS内置浏览器所支持的特性,可以让我们做出真正意义上的全平台覆盖的魔塔。 -然而,一般而言使用非RMXP制作魔塔往往需要一定的编程技术,HTML5魔塔自然也不例外。但是,为了能让大家更加注重于“做塔”本身,而不用考虑做塔以外的各种脚本问题,我特意制作了这样一部HTML5的魔塔样板。 +然而,一般而言使用非RPG Maker MV制作魔塔往往需要一定的编程技术,HTML5魔塔自然也不例外。但是,为了能让大家更加注重于“做塔”本身,而不用考虑做塔以外的各种脚本问题,@艾之葵(GitHub ckcz123)特意制作了这样一部HTML5的魔塔样板。 +> 这个魔塔样板,可以让你在完全不懂任何编程语言的情况下,做出自己的H5魔塔。不会代码?没关系!只要你想做,就能做出来! -> 这个魔塔样板,可以让你在完全不懂任何编程的情况下,做出自己的H5魔塔。不会代码?没关系!只要你想做,就能做出来! - -继续查看文档的详细介绍,让你学会如何使用这一个样板来制作属于自己的HTML5魔塔。 - -本说明文档配有B站视频教程,对照查看效果更佳哦:[https://www.bilibili.com/video/av32781473/](https://www.bilibili.com/video/av32781473/)。 +继续查看文档的详细介绍,让你学会如何使用这一个样板来制作属于自己的HTML5魔塔,或者……任何非即时的网格地图游戏。
+本说明文档配有较为过时的B站视频教程,可供参考:[https://www.bilibili.com/video/av32781473/](https://www.bilibili.com/video/av32781473/)。 ========================================================================================== diff --git a/_docs/personalization.md b/_docs/personalization.md index ec6f5863..e49bde8a 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -1,6 +1,5 @@ # 个性化 - -?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.7**,上次更新时间:* {docsify-updated} * 有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 @@ -69,13 +68,6 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); - `terrains.png` 为所有地形的图标。 系统会读取`icon.js`文件,并获取每个ID对应的图标所在的位置。 - -### 使用预定义的素材 - -在images目录的“默认素材”下给定了若干预定义的自定义素材。 - -如果你需要某个素材已经存在,则可以直接将其覆盖images目录下的同名文件,就能看到效果。 - ### 背景和前景图层 从V2.4.1开始,样板允许多个图层叠加,最多支持背景层、事件层和前景层三个图层。 @@ -246,15 +238,14 @@ ID必须由数字字母下划线组成,数字在1000以内,且均不能和 额外素材可以使用“tileset贴图”的方式进行绘制,一次绘制一个矩形区域。 +“辅助工具”中提供了“额外素材合并”,如果使用此功能,请不要对额外素材进行基于ID、索引和数字的判定和读写等操作,如确有此需求,可以创建一些玩家不可达也不可预览的隐藏样板层,然后用等量代换的办法去从样板层取用。 ## 自定义道具效果 本节中将继续介绍如何自己编辑一个道具的效果。 -道具效果的具体实现都在`items.js`中。 - +道具效果的具体实现都在 `project\items.js` 中。 ### 即捡即用类道具(cls: items) - -对于即捡即用类道具,如宝石、血瓶、剑盾等,我们可以简单地修改`data.js`中的value一栏即可。 +对于即捡即用类道具,如宝石、血瓶等,我们可以简单地修改全塔属性中的 `values` 一栏即可。 如果你想要同种宝石在不同层效果不同的话,可以进行如下操作: @@ -281,11 +272,14 @@ core.status.hero.atk += core.values.redJewel + 2*ratio 具体过程比较复杂,需要一定的JS能力,在这里就不多说了,有需求可以找`艾之葵`进行了解。 +从V2.6.6起,道具的“图块属性”中,提供了 `useItemEvent` 项,您可以用事件而不是脚本去书写道具的使用效果(对 `cls: items` 也有效但会打断寻路),参见样板的黄宝石和生命魔杖。 + +如果弄不清楚 `canUseItem` ,也可以干脆统一填写为 `"true"`,先斩后奏,并在使用效果中再行判定,如果发现其实使用失败了,就悄咪咪返还给勇士一个该道具,参见样板的破墙镐和炸弹。 ### 实战!拿到神圣盾后免疫吸血、领域、夹击效果 1. 在itemEffect中修改拿到神圣盾时的效果,标记一个自定义Flag。 ``` js -core.status.hero.def += core.values.shield5 * ratio; +core.status.hero.def += 100; core.setFlag("shield5", true); // 增加一个自定义Flag:已经拿到神圣盾 ``` 2. 免疫吸血效果:在脚本编辑的getDamageInfo中,编辑成如果存在神圣盾标记,吸血伤害为0。 @@ -310,31 +304,24 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { // ... 下略 ``` 3. 免疫领域、夹击、阻击效果:在2.4.1之后,可以直接将flag:no_zone设为true来免疫领域效果,其他几个同理。 -``` js -// 写在获得道具后事件 -[ +``` json +[ // 写在获得道具后事件 // 设置不同的flag可以分别无视对应的阻激夹域效果 {"type": "setValue", "name": "flag:no_zone", "value": "true"}, // 免疫领域 {"type": "setValue", "name": "flag:no_snipe", "value": "true"}, // 免疫阻击 {"type": "setValue", "name": "flag:no_laser", "value": "true"}, // 免疫激光 {"type": "setValue", "name": "flag:no_betweenAttack", "value": "true"}, // 免疫夹击 + {"type": "setValue", "name": "flag:no_ambush", "value": "true"}, // 免疫捕捉 + {"type": "setValue", "name": "item:shoes", "value": "1"} // 免疫路障 ] ``` 4. 如果有更高的需求,例如想让吸血效果变成一半,则还是在上面这些地方进行对应的修改即可。 ## 新增门和对应的钥匙 - -如果要新增一个门和对应的钥匙,需要进行如下几步: - -1. 在terrains.png中添加新的门的素材,并在地图编辑器中注册门的ID。该ID必须是以`Door`结尾,例如`abcDoor`。 -2. 在animates.png中添加开门的四格动画,然后直接打开icons.js文件,在animates下直接添加ID和索引信息,例如`'abcDoor': 34`。 -3. 在items.png中添加钥匙的素材,并在地图编辑器中注册钥匙的ID。该ID必须是和门对应且以`Key`结尾,例如`abcKey`。 -4. 该道具的cls应为`tools`,可以自行写道具描述,最下面几项均留`null`即可。 - -!> **请勿在animates中对门的动画素材进行注册!而是请直接打开icons.js文件并添加ID和索引信息!!!** - -!> terrains和animates的门ID必须完全一致,且以`Door`结尾;所对应的钥匙ID应当是把`Door`换成`Key`,这样才能对应的上! - +如果要新增一个门和对应的钥匙,只需要进行如下三步: +1. 在 `project\images\animates.png` 中添加开门的四帧动画并注册,其中第一帧也作为未开启的静止门。 +2. 在图块属性中将其默认触发器改为 `openDoor` ,动画帧数填 `1` 表示门未开启时静止在第一帧。 +3. 在 `doorInfo` 中填写需要哪些钥匙(可以填任何消耗类道具)各多少把,开关门分别播放什么音效等。 ## 覆盖楼传事件 对于特殊的塔,我们可以考虑修改楼传事件来完成一些特殊的要求,比如镜子可以按楼传来切换表里。 @@ -354,23 +341,25 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { 如果你对现有的怪物不满意,想自行添加怪物属性也是可以的。具体参见脚本编辑的getSpecials。 -你需自己指定一个special数字,修改属性名和属性提示文字。后两者可以直接写字符串,或写个函数传入怪物。 +你需自己指定一个special数字,修改属性名和属性提示文字。提示文字可以直接写字符串,或写个函数传入怪物。 + +如果要改动怪物在手册中的显示数值(也会被用于伤害计算,如模仿),请修改下面的 `getEnemyInfo` 函数。 如果要修改伤害计算公式,请修改下面的getDamageInfo函数。请注意,如果无法战斗,该函数必须返回`null`。 !> 如果改动了伤害计算公式,可能导致临界计算崩掉,因此建议将全塔属性中的`useLoop`置为true。 -对于毒衰弱怪物的战斗后结算在脚本编辑中的afterBattle函数中。 - -对于领域、夹击、阻击怪物的检查在`control.js`中的checkBlock函数中。 +对于毒衰咒、自爆、退化怪物和加点、仇恨值累加等战斗后结算在脚本编辑中的 `afterBattle` 函数中。 +对于领域、夹击、阻击、激光、捕捉怪物的检查在脚本编辑中的 `updateCheckBlock` 函数中。 ## 自定义快捷键 如果需要绑定某个快捷键为处理一段事件,也是可行的。 要修改按键,我们可以在脚本编辑的`onKeyUp`进行处理: -比如,我们设置一个快捷键进行绑定,比如`Y`,其keycode是89。(有关每个键的keycode搜一下就能得到) +我们设置一个快捷键进行绑定,比如 `Y`,其 `keycode` 是 `89` 。
+(大键盘数字键 `0-9` 的 `keycode` 为 `48-57, A-Z` 键的 `keycode` 为 `65-90` ,其他键的 `keycode` 搜一下就能得到) 然后在脚本编辑的`onKeyUp`函数的`switch`中进行处理。 @@ -387,8 +376,7 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 break; ``` - -强烈建议所有新增的自定义快捷键均给个对应的永久道具可点击,以方便手机端的行为。 +强烈建议所有新增的自定义非数字快捷键均给个对应的永久道具可点击,以方便手机端的行为。 使用`core.status.route.push("key:"+keyCode)`可以将这次按键记录在录像中。 @@ -457,7 +445,7 @@ this.myfunc = function(x) { 可以在脚本编辑的onKeyUp中定义每个快捷键的使用效果,比如使用道具或释放技能等。 -默认值下,1使用破,2使用炸,3使用飞,4使用其他存在的道具,5-8未定义。可以相应修改成自己的效果。 +默认值下,1使用破,2使用炸,3使用飞,4使用其他存在的道具,5读取上一个自动存档,6读取下一个自动存档,7轻按,8未定义。可以相应修改成自己的效果。 也可以替换icons.png中的对应图标,以及修改main.js中`main.statusBar.image.btn1~8`中的onclick事件来自定义按钮和对应按键。 @@ -475,6 +463,7 @@ this.myfunc = function(x) { 具体可详见脚本编辑的`drawStatusBar`函数。 +自绘状态栏开启后,金币图标将失去打开快捷商店的功能,您可以修改脚本编辑的 `onStatusBarCLick` 函数来适配。 ## 自定义状态栏的显示项 在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - statusLeftBackground)等等。 @@ -534,13 +523,9 @@ core.statusBar.speed.innerHTML = core.getStatus('speed'); // 设置其显示内容为flag:speed值,无需额外进行定义。 core.statusBar.speed.innerHTML = core.getFlag('speed', 0); ``` - +总的来说不建议这样做,因为 `main.js` 和几个 `html` 文件不在 `project` 文件夹,会导致随样板更新迁移接档变得困难。 ## 技能塔的支持 - -要支持技能塔,可能需要如下几个方面: - -从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。 - +从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。要支持技能塔,可能需要如下几个方面: - 魔力(和上限)的添加;技能的定义 - 状态栏的显示 - 技能的触发(按键与录像问题) @@ -548,11 +533,9 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); ### 魔力的定义添加;技能的定义 -从V2.5开始,提供了status:mana选项,可以直接代表当前魔力值。 +从V2.5开始,提供了 `status:mana` 选项,可以直接代表当前魔力值。可以在全塔属性勾选来启用它。 -如果要启用,需要开启全塔属性的enableMana选项。 - -如果需要魔力上限,则可以使用flag:manaMax来表示当前的魔力最大值。 +如果需要魔力上限,则可以使用 `status:manaMax` 来表示当前的魔力最大值,负数表示没有上限。 同时,我们可以使用flag:skill表示当前开启的技能编号,flag:skillName表示当前开启的技能名称。 @@ -581,8 +564,7 @@ core.statusBar.skill.innerHTML = core.getFlag('skillName', '无'); ### 技能的触发 #### 使用道具作为技能 - -由于手机端按键十分不方便,虚拟键盘不好用,因此强烈推荐**给每个技能设置一个道具图标,在道具栏点击使用!** +由于手机端按字母键不方便,虚拟键盘不好用,因此强烈推荐**给每个字母键技能设置一个道具,在道具栏点击使用!** 下面是个很简单的例子,要制作一个技能"二倍斩"。 @@ -616,19 +598,17 @@ else { // 关闭技能 在技能的道具定义完毕后,再将该道具绑定到一个快捷键上。有关绑定按键请参见[自定义快捷键](#自定义快捷键)。 -下面是一个很简单的例子,当勇士按下W后,触发我们上面定义的二倍斩技能。 - +下面是一个很简单的例子,当勇士按下 `F` 后,触发我们上面定义的二倍斩技能。 ``` js -case 87: // W:开启技能“二倍斩” +case 70: // F:开启技能“二倍斩” // 是否拥有“二倍斩”这个技能道具 if (core.hasItem('skill1')) { - core.status.route.push("key:87"); + core.status.route.push("key:70"); core.useItem('skill1', true); } break; ``` - -在勇士处于停止的条件下,按下W键时,判断技能的道具是否存在,如果存在再使用它。 +在勇士处于停止的条件下,按下 `F` 键时,判断技能的道具是否存在,如果存在再使用它。 !> 由于现在手机端存在拓展键盘,也强烈建议直接覆盖1-8的使用效果,这样手机端使用也非常方便。 diff --git a/_docs/start.md b/_docs/start.md index 880bfd08..69b6fed1 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -1,37 +1,34 @@ # 快速上手 - -?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * - +?> 目前版本**v2.7**,上次更新时间:* {docsify-updated} *
在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! ## 前置需求 - -你需要有满足如下条件才能进行制作: - -- Windows 8以上操作系统;Windows 7需要安装.Net Framework 4.0。(能打开同目录下的“启动服务.exe”即可) - - Mac系统则需OS X 10.7及以上,能正确打开启动服务(Mac版).app即可。 - - 安卓手机端需要下载最新版的HTML5安卓造塔器。 -- Chrome浏览器。其他浏览器可能会导致本地服务器产生闪退等现象。 -- 强烈推荐安装一个很好的文本编辑器:VSCode。在某些需要直接修改文件的场合,可能会非常重要。 - -上述软件均可以在群文件的常用软件中找到下载安装包。 +你需要满足如下条件才能进行制作: +- Windows 8 以上操作系统;Windows 7 需要安装 .Net Framework 4.0。(能打开同目录下的“启动服务.exe”即可) + - 其他电脑则需安装 [python 3.8.2](https://www.python.org/getit/) 或更高版本,能打开server.py即可( Windows 也可以这样做)。 + - 安卓手机端需要下载最新版的 [HTML5安卓造塔器](https://h5mota.com/games/_client/H5motaMaker.apk)。 +- [Chrome浏览器](https://www.google.cn/chrome/)。其他浏览器可能会导致本地服务器产生闪退等现象。 +- 强烈推荐安装一个很好的文本编辑器:[VSCode](https://code.visualstudio.com/download)。在某些需要直接修改文件的场合,可能会非常重要。 只要满足了上述条件,你就可以开始做自己的塔啦! ## 启动HTTP服务 - -在根目录下有一个“启动服务.exe”,运行之。(Mac版本则双击运行“启动服务(Mac版).app”,手机端则使用造塔APP。) - +与编辑器闭源的 RPG Maker MV 不同,本样板对文件的绝大部分修改是通过网页编辑器经由本地 HTTP 服务完成的,这样做也有助于编辑器跨平台并最大限度地复用运行时代码,还可以让玩家在在线游戏时查看游戏工程。
+在根目录下有一个“启动服务.exe”,运行之。(非 Windows 电脑则需使用命令行运行 server.py,手机端则使用造塔APP。) ![启动服务](img/server.png) - -* “启动游戏”按钮将打开一个网页,你能在里面看到现在游戏的效果。 -* “地图编辑器”允许你以可视化的方式进行编辑地图。 +* “启动游戏”按钮将打开一个网页(您也可以手动在浏览器中键入 127.0.0.1:1055/index.html ,同时开启多个启动服务则1056、1057顺延),你能在里面看到现在游戏的效果。 +* “地图编辑器”允许你以可视化的方式进行编辑游戏(这个名称为历史遗留,两年前它真的只能编辑地图)。您也可以手动在浏览器中键入 127.0.0.1:1055/editor.html +* 以下为 Windows 专用的一些辅助工具,由C#编写: * “便捷PS工具”能让你很方便的对自定义素材进行添加。参见[自定义素材](personalization#自定义素材)。 * “地图生成器”能让你从已有的截图(如RMXP项目)中立刻生成可被本样板识别的地图数据。 -* “怪物数据导出”能让你从RMXP中导出怪物数据而被H5魔塔使用。 -* “RM动画导出器”能让你从RMXP中导出动画而被H5魔塔使用。 -* “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。 +* “怪物数据导出”能让你从RMXP(RPG Maker XP 1.03,下同)中导出怪物数据而被HTML5魔塔使用。 +* “RM动画导出”能让你从RMXP中导出动画而被HTML5魔塔使用。 +* “JS代码压缩”能对JS代码和音像素材进行压缩,从而减少IO请求数和文件大小。 +* “额外素材合并”能将用到的tileset素材重新拼成更少的图片,并更新对应的地图矩阵。 * “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。 +* “动画编辑器”能让你编辑已有的animate动画文件,或自己利用图片制作全新的动画。 +* “[怪物数据生成机](https://pan.baidu.com/s/1w-JMg-ZPRvbsqbbtOwORGA#list/path=%2F +)”能够借助AI生成适当强度的怪物,此工具需要手动下载。 !> **整个造塔过程中,启动服务必须全程处于开启状态!切不可手滑关闭,否则做的都是无用功!** @@ -48,15 +45,10 @@ ![绘制地图](./img/drawmap.jpg) 如果提示“该素材未被定义”或有红色问号框,请参见[素材注册](#素材注册)。 - -绘制地图时可以右键弹出菜单,移动图块和事件。 - -从V2.4.2开始,可以使用`Alt+0~9`对一个图块素材快速保存,`Ctrl+0~9`来快速选用。 - +
绘制地图时可以右键弹出菜单,复制、交换、删除图块、选中素材、更改出生点或快速创建常用事件。
从V2.4.2开始,可以使用 `Alt+0~9` 对一个图块素材快速保存,大键盘 `0~9` 来快速选用。
+从V2.6.6开始,最近使用的图块也会直接显示在地图下方,您可以随时选取或清空它们。 ### 从RMXP导入已有的地图 - -!> 注:现在已经不推荐此方法,如需从RM刻塔请使用 [RM转H5刻塔器使用教程](https://www.bilibili.com/video/av43125840) 进行操作。 - +!> 注:现在已经不推荐此方法,如需从RMXP复刻魔塔请观看 [RM转H5刻塔器使用教程](https://www.bilibili.com/video/av43125840) 进行操作。
如果我们想复刻一个现有的,已经被RMXP所制作的塔,也有很便捷的方式,那就是用到我们的“地图生成器”。 首先,我们打开RMXP和对应的项目,可以看到它的地图。 @@ -64,9 +56,7 @@ ![绘制地图](./img/rmxp2.png) 我们打开地图编辑器,创建一个地图,宽高需要和RM中的地图一致。 - -之后,我们打开Windows自带的“截图工具”,并将整个地图有效区域截图下来,并将其复制到剪切板。 - +
之后,我们打开 Windows 10 自带的“截图工具”(非 Windows 10 请使用 Print Screen 键全屏截图后粘贴到“画图”中裁剪,或使用微信/QQ等自带的截图功能),并将整个地图有效区域截图下来,复制到剪切板。 ![绘制地图](./img/rmxp3.png) 截图时请注意:**只截取有效游戏空间内数据,并且有效空间内的范围必须是创建的地图的大小(至少为13x13)。** @@ -78,8 +68,7 @@ 然后点击“复制地图”,即可将地图数据复制到剪切板,然后再地图编辑器中切换到“地图编辑”,并在左边的框进行粘贴。 !> **地图生成器默认只支持已被定义的素材。如果有自定义素材需求(例如原版的1层小塔那种素材),请先[导入并注册素材](#素材注册)后再进行操作。** - -!> **请确保截图范围为你创建的地图大小,并且保证每个位置的像素都是32x32。** +!> **请确保截图范围为你创建的地图大小,并且保证每个位置的像素都是32x32(譬如48×48的RPG Maker MV就需要缩小到2/3比例再截图)。** !> **如果宽度超过13,地图生成器将无法显示完全,但是仍然可以粘贴到地图编辑器中进行修改。** @@ -90,9 +79,7 @@ ## 编辑属性 当地图绘制完毕后,我们可能需要进行属性的编辑。属性编辑有四类:楼层属性、怪物属性、道具属性、全塔属性。 - -### 楼层属性 - +### 楼层属性(快捷键V) 我们切换到楼层属性,并进行一项项的编辑。将鼠标放在中间有具体的每一项注释,这里不再细说。 ![楼层属性](./img/floor.png) @@ -110,13 +97,9 @@ 同理,点击道具可以对道具属性进行编辑。 目前大多数道具都已有默认效果。更多信息可参见[元件说明:道具](element#道具)。 - -### 全塔属性 - +### 全塔属性(快捷键B) 我们切换到全塔属性,并进行一项项的编辑。同样鼠标放在中间可以查看注释。 - -我们可以在这里定义一些全局的属性,比如开始剧情、宝石血瓶剑盾数值,破甲反击净化的比例,以及一些系统开关比如是否启用加点,是否允许负伤害等等。 - +
我们可以在这里定义一些全局的属性,比如开始剧情、宝石血瓶数值,破甲反击净化的比例,以及一些系统开关比如是否启用加点,是否允许负伤害等等。
!> **注:name作为游戏的唯一标识符必须进行修改,否则可能会导致存档等出现问题。**   @@ -142,9 +125,7 @@ 从上至下有若干项,分别代表该点的一些事件。有关事件的详细信息请参见[事件](event)。 如果一个点存在事件,则在地图编辑器中该点的左下角会有一个小方框表示。 - -红色为自定义事件,绿色为楼层传送事件,黄色为战后事件,青色为道具获得后的事件,粉色为开门后的事件。 - +
红色为普通事件,橙色为自动事件,黄色为战后事件,绿色为楼层转换,青色为拾获道具后事件,靛色为不可通行性(不是事件),粉(紫)色为开门后事件。 ## 自定义素材的添加与注册 有关素材的更多详细信息,包括素材的机制等内容,请参见[自定义素材](personalization#自定义素材)。 @@ -154,15 +135,10 @@ ### 新素材的添加 如果我们需要新增加一些素材,比如样板中没有的怪物等。 - -我们可以从启动服务打开编辑PS工具,然后选择对应的图片进行添加。 - +
我们可以从启动服务打开便捷PS工具,然后选择对应的图片进行添加。 ![便捷PS工具](img/ps.png) - -在左边选择你要导入到的位置,比如怪物就是enemy.png,道具就是items.png等等,再在右边导入你的新素材图片。 - -之后就可以简单的使用Ctrl+C和Ctrl+V来复制粘贴了。 - +在左边选择你要导入到的文件,比如怪物就是enemy.png,道具就是items.png等等,再在右边导入你的新素材图片。
+之后就可以简单的使用 C 和 V 来复制粘贴了。
保存后,刷新地图编辑器即可生效。 ### 素材注册 @@ -172,15 +148,11 @@ ![素材注册](./img/register.png) 出现这种情况,是因为我们没有将该素材定义到游戏中,也无法进行使用。 - -要解决这个问题,只需要在左边的图块属性中定义新的图块ID和图块数字即可。ID必须由数字字母下划线组成,数字在1000以内,且均不能和已有的进行重复。 - +
要解决这个问题,只需要在左边的图块属性中定义新的图块ID和图块数字即可。ID必须由数字字母下划线组成,ID不允许使用纯数字。图块数字在1000以内,ID和图块数字均不能和已有的进行重复。
有关图块的ID和数字定义请参见[素材的机制](personalization#素材的机制)。 之后刷新编辑器即可。 - -也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 - +
也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字,随后强烈建议手动修改ID)。
素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 ### 额外素材 @@ -194,11 +166,8 @@ 有关额外素材的更多说明参见[额外素材](personalization#额外素材) ## 控制台调试 - -HTML5的塔都是可以进行控制台调试的。 - -当我们使用Chrome进入游戏后,可以按 `Ctrl+Shift+I` ,并找到 `Console` 控制台。 - +HTML5游戏都是可以进行控制台调试的。
+当我们使用Chrome进入游戏后,可以按 `Ctrl+Shift+I` (F12),并找到 `Console` 控制台。 ![控制台](./img/console.png) 在控制台中,我们可以输入一些命令对游戏进行调试,常见的命令有: @@ -209,21 +178,21 @@ HTML5的塔都是可以进行控制台调试的。 - `core.status.hero` 获得当前勇士状态信息。例如`core.status.hero.atk`就是当前勇士的攻击力数值。 - `core.material.enemys` 获得所有怪物信息。例如`core.material.enemys.greenSlime`就是获得绿色史莱姆的属性数据。 - `core.material.items` 获得所有道具的信息。例如`core.material.items.pickaxe`就是获得破墙镐的信息。 -- `core.debug()` 开启调试模式;此模式下可以按住Ctrl键进行穿墙。 -- `core.updateStatusBar()` 立刻更新状态栏和地图显伤。 +- `flags.debug = true` 开启调试模式;此模式下可以按住 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.hasItem('pickaxe')` 返回勇士是否拥有某个道具。等价于`core.itemCount('pickaxe') > 0`。 - `core.getEquip(0)` 返回0号装备类型(武器)的当前装备的itemId,不存在则返回null - `core.hasEquip('sword1')` 返回某个装备当前是否处于被装备状态 - `core.setFlag('xxx', 1)` 设置某个flag/自定义变量的值。 - `core.getFlag('xxx', 10)` 获得某个flag/自定义变量的值;如果该项不存在(未被定义),则返回第二个参数的值。 -- `core.hasFlag('xxx')` 返回是否存在某个变量且不为0。等价于`core.getFlag('xxx', 0)!=0`。 -- `core.removeFlag('xxx')` 删除某个flag/自定义变量 -- `core.insertAction(list)` 执行一段自定义事件。比如 `core.insertAction(["剧情文本"])` 将执行一个剧情文本显示事件。 +- `core.hasFlag('xxx')` 返回是否存在某个变量且不为六种广义 `false` 。等价于 `!!core.getFlag('xxx')` 。 +- `core.setFlag('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,可省略表示当前楼层。 @@ -234,16 +203,16 @@ HTML5的塔都是可以进行控制台调试的。 更多API和详细参数介绍可参见[API列表](api)。 ## 编辑器的基本操作 - +- **H** 查看所有快捷键 - **Alt+0~9, 0~9** 保存和读取当前选中图块 - **W/A/S/D** 移动大地图 - **Ctrl+Z** 撤销上次绘图 - **Ctrl+Y** 重做上次绘图 -- **PgUp/PgDn** 切换楼层 +- **PgUp/PgDn,或滚轮** 切换楼层 - **Ctrl+S** 保存事件编辑器/脚本编辑器 - **地图上单击** 选中该点 - **地图上双击** 选中该点图块 -- **地图上右键** 弹出菜单栏,包括选中、复制、清除等操作 +- **地图上右键** 弹出菜单栏,包括选中、复制、交换、清除、更改出生点、快速绑定等操作 - **事件编辑器中Ctrl+C, Ctrl+X, 右键等** 执行相应操作 ## 报错处理 @@ -273,10 +242,7 @@ HTML5的塔都是可以进行控制台调试的。 ![删除ID](./img/floorid.png) 请注意,手动删除楼层时一定要确保“初始楼层ID”是一个有效且存在的floorId! - -  - -如有任何无法解决的问题,欢迎加群**539113091**寻求帮助。 +
如有任何无法解决的问题,欢迎加 QQ 群 [959329661](https://jq.qq.com/?_wv=1027&k=5C87qeQ) 寻求帮助。 ========================================================================================== diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index ff7e86d7..f6ff166b 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -34,7 +34,7 @@ autoEvent_m /* autoEvent_m -tooltip : ????? +tooltip : 自动事件 helpUrl : https://h5mota.com/games/template/_docs/#/event default : ["flag:__door__==2",0,true,false,false,null] var code = { @@ -102,78 +102,52 @@ var code = ' \n'; return code; */; -shopcommonevent - : '公共事件版商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? '执行的公共事件 id' EvalString '参数列表' JsonEvalString? - -/* shopcommonevent -tooltip : 全局商店, 执行一个公共事件 -helpUrl : https://h5mota.com/games/template/_docs/#/ -default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] -if (JsonEvalString_0) { - if (!(JSON.parse(JsonEvalString_0) instanceof Array)) - throw new Error('参数列表必须是个有效的数组!'); -} -var code = { - 'id': IdString_0, - 'textInList': EvalString_0, - 'mustEnable': Bool_0, - 'commonEvent': EvalString_1 -} -if (JsonEvalString_0) code.args = JSON.parse(JsonEvalString_0); -code=JSON.stringify(code,null,2)+',\n'; -return code; -*/; - shopsub - : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '未开启状态则不显示在列表中' Bool BGNL? Newline '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND + : '商店 id' IdString '标题' EvalString? '图像' IdString? BGNL? Newline '文字' EvalString? BGNL? Newline '快捷名称' EvalString '未开启不显示' Bool '不可预览' Bool BGNL? Newline shopChoices+ BEND /* shopsub -tooltip : 全局商店,消耗填-1表示每个选项的消耗不同,正数表示消耗数值 +tooltip : 全局商店 helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 -default : ["shop1","贪婪之神","blueShop","1F金币商店",false,false,null,"20+10*times*(times+1)","勇敢的武士啊, 给我${need}金币就可以:"] +default : ["shop1","贪婪之神","blueShop","勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:","金币商店",false,false] +var title=''; +if (EvalString_0==''){ + if (IdString_1=='') title=''; + else title='\t['+IdString_1+']'; +} else { + if (IdString_1=='')title='\t['+EvalString_0+']'; + else title='\t['+EvalString_0+','+IdString_1+']'; +} var code = { 'id': IdString_0, - 'name': EvalString_0, - 'icon': IdString_1, - 'textInList': EvalString_1, - 'commonTimes': Bool_0, - 'mustEnable': Bool_1, - 'use': ShopUse_List_0, - 'need': EvalString_2, - 'text': EvalString_3, + 'text': title+EvalString_1, + 'textInList': EvalString_2, + 'mustEnable': Bool_0, + 'disablePreview': Bool_1, 'choices': 'choices_asdfefw' } -code=JSON.stringify(code,null,2).split('"choices_asdfefw"').join('[\n'+shopChoices_0+']\n')+',\n'; +code=JSON.stringify(code,null,2).split('"choices_asdfefw"').join('[\n'+shopChoices_0+']')+',\n'; return code; */; shopChoices - : '商店选项' EvalString '消耗' EvalString? BGNL? Newline shopEffect+ + : '商店选项' EvalString '使用条件' EvalString BGNL? Newline '图标' IdString? '颜色' ColorString? Colour '出现条件' EvalString? BGNL? Newline action+ BEND /* shopChoices tooltip : 商店选项,商店消耗是-1时,这里的消耗对应各自选项的消耗,商店消耗不是-1时这里的消耗不填 helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 -default : ["攻击+1",""] +default : ["攻击+1","status:money>=20+2*flag:shop1","","","rgba(255,255,255,1)",""] colour : this.subColor -EvalString_1 = EvalString_1 && (', "need": "'+EvalString_1+'"'); -var code = '{"text": "'+EvalString_0+'"'+EvalString_1+', "effect": "'+shopEffect_0.slice(2,-1)+'"},\n'; -return code; -*/; - -shopEffect - : idString_e '+=' expression - - -/* shopEffect -colour : this.subColor -var code = idString_e_0+'+='+expression_0+';' +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +EvalString_2 = EvalString_2 && (', "condition": "'+EvalString_2+'"') +IdString_0 = IdString_0? (', "icon": "'+IdString_0+'"'):''; +var code = '{"text": "'+EvalString_0+'", "need": "'+EvalString_1+'"'+IdString_0+ColorString_0+EvalString_2+', "action": [\n'+action_0+']},\n'; return code; */; shopitem - : '道具商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? Newline shopItemChoices+ BEND + : '道具商店 id' IdString '快捷名称' EvalString '未开启不显示' Bool BGNL? Newline shopItemChoices+ BEND /* shopitem @@ -187,7 +161,7 @@ var code = { 'mustEnable': Bool_0, 'choices': 'choices_aqwedsa' } -code=JSON.stringify(code,null,2).split('"choices_aqwedsa"').join('[\n'+shopItemChoices_0+']\n')+',\n'; +code=JSON.stringify(code,null,2).split('"choices_aqwedsa"').join('[\n'+shopItemChoices_0+']')+',\n'; return code; */; @@ -210,6 +184,28 @@ var code = '{"id": "' + IdString_0 + '"' + IntString_0 + EvalString_0 + EvalStri return code; */; +shopcommonevent + : '公共事件商店 id' IdString '快捷名称' EvalString '未开启不显示' Bool BGNL? '执行的公共事件名' EvalString '参数列表' JsonEvalString? + +/* shopcommonevent +tooltip : 全局商店, 执行一个公共事件 +helpUrl : https://h5mota.com/games/template/_docs/#/ +default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); +} +var code = { + 'id': IdString_0, + 'textInList': EvalString_0, + 'mustEnable': Bool_0, + 'commonEvent': EvalString_1 +} +if (JsonEvalString_0) code.args = JSON.parse(JsonEvalString_0); +code=JSON.stringify(code,null,2)+',\n'; +return code; +*/; + //afterBattle 事件编辑器入口之一 afterBattle_m : '战斗结束后' BGNL? Newline action+ BEND @@ -224,14 +220,17 @@ return code; //afterGetItem 事件编辑器入口之一 afterGetItem_m - : '获取道具后' BGNL? Newline action+ BEND + : '获取道具后' '轻按时不触发' Bool BGNL? Newline action+ BEND /* afterGetItem_m tooltip : 系统引发的自定义事件 helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 -var code = '[\n'+action_0+']\n'; -return code; +if (Bool_0) { + return '{"disableOnGentleClick": true, "data": [\n'+action_0+']\n}'; +} else { + return '[\n'+action_0+']\n'; +} */; //afterOpenDoor 事件编辑器入口之一 @@ -284,6 +283,7 @@ if (Floor_List_0!='floorId') toFloorId = Floor_List_0; var loc = ', "loc": ['+Number_0+', '+Number_1+']'; if (Stair_List_0===':now') loc = ''; else if (Stair_List_0!=='loc')loc = ', "stair": "'+Stair_List_0+'"'; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; if (IgnoreChangeFloor_List_0!='null') { @@ -340,9 +340,9 @@ action | trigger_s | insert_1_s | insert_2_s - | revisit_s | exit_s | setBlock_s + | turnBlock_s | showFloorImg_s | hideFloorImg_s | showBgFgMap_s @@ -362,8 +362,7 @@ action | openDoor_s | closeDoor_s | changeFloor_s - | changePos_0_s - | changePos_1_s + | changePos_s | setViewport_s | moveViewport_s | useItem_s @@ -380,8 +379,7 @@ action | hideImage_s | showTextImage_s | moveImage_s - | showGif_0_s - | showGif_1_s + | showGif_s | setCurtain_0_s | setCurtain_1_s | screenFlash_s @@ -700,13 +698,13 @@ return code; */; hide_s - : '隐藏事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' IntString? '不等待执行完毕' Bool? Newline + : '隐藏事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '同时删除' Bool '动画时间' IntString? '不等待执行完毕' Bool? Newline /* hide_s -tooltip : hide: 将一个启用事件禁用,所有参数均可不填,代表禁用事件自身,xy可用逗号分隔表示多个点 +tooltip : hide: 隐藏事件,同时可删除 helpUrl : https://h5mota.com/games/template/_docs/#/event?id=hide%EF%BC%9A%E5%B0%86%E4%B8%80%E4%B8%AA%E5%90%AF%E7%94%A8%E4%BA%8B%E4%BB%B6%E7%A6%81%E7%94%A8 -default : ["","","","",false] +default : ["","","",true,"",false] colour : this.mapColor var floorstr = ''; if (EvalString_0 && EvalString_1) { @@ -727,8 +725,9 @@ if (EvalString_0 && EvalString_1) { } IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; -Bool_0 = Bool_0 ?', "async": true':''; -var code = '{"type": "hide"'+floorstr+IdString_0+''+IntString_0+Bool_0+'},\n'; +Bool_0 = Bool_0 ?', "remove": true':''; +Bool_1 = Bool_1 ?', "async": true':''; +var code = '{"type": "hide"'+floorstr+IdString_0+Bool_0+IntString_0+Bool_1+'},\n'; return code; */; @@ -786,18 +785,6 @@ var code = '{"type": "insert", "loc": ['+PosString_0+','+PosString_1+']'+Event_L return code; */; -revisit_s - : '重启当前事件' Newline - - -/* revisit_s -tooltip : revisit: 立即重启当前事件 -helpUrl : https://h5mota.com/games/template/_docs/#/event?id=revisit%EF%BC%9A%E7%AB%8B%E5%8D%B3%E9%87%8D%E5%90%AF%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 -colour : this.eventColor -var code = '{"type": "revisit"},\n'; -return code; -*/; - exit_s : '立刻结束当前事件' Newline @@ -841,6 +828,39 @@ var code = '{"type": "setBlock", "number": "'+EvalString_0+'"'+floorstr+IdString return code; */; +turnBlock_s + : '事件转向' DirectionEx_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* turnBlock_s +tooltip : turnBlock:事件转向;自动检索faceIds +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setblock%EF%BC%9A%E8%AE%BE%E7%BD%AE%E6%9F%90%E4%B8%AA%E5%9B%BE%E5%9D%97 +colour : this.mapColor +default : [null,"","",""] +var floorstr = ''; +if (EvalString_0 && EvalString_1) { + var pattern1 = MotaActionFunctions.pattern.id; + if(pattern1.test(EvalString_0) || pattern1.test(EvalString_1)){ + EvalString_0=MotaActionFunctions.PosString_pre(EvalString_0); + EvalString_1=MotaActionFunctions.PosString_pre(EvalString_1); + EvalString_0=[EvalString_0,EvalString_1] + } else { + var pattern2 = /^([+-]?\d+)(,[+-]?\d+)*$/; + if(!pattern2.test(EvalString_0) || !pattern2.test(EvalString_1))throw new Error('坐标格式错误,请右键点击帮助查看格式'); + EvalString_0=EvalString_0.split(','); + EvalString_1=EvalString_1.split(','); + if(EvalString_0.length!==EvalString_1.length)throw new Error('坐标格式错误,请右键点击帮助查看格式'); + for(var ii=0;ii * { diff --git a/_server/css/editor_mode_mobile.css b/_server/css/editor_mode_mobile.css index e59fbd3e..423a85ba 100644 --- a/_server/css/editor_mode_mobile.css +++ b/_server/css/editor_mode_mobile.css @@ -213,6 +213,8 @@ div.etableInputDiv { div.checkboxSet { position: relative !important; + max-height: 250px; + overflow: auto; } .etableInputDiv > * { diff --git a/_server/editor.js b/_server/editor.js index 2b1d1f12..ab9abb3b 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -629,7 +629,7 @@ editor.prototype.buildMark = function(){ } } -editor.prototype.setSelectBoxFromInfo=function(thisevent){ +editor.prototype.setSelectBoxFromInfo=function(thisevent, scrollTo){ var pos={x: 0, y: 0, images: "terrains"}; var ysize = 32; if(thisevent==0){ @@ -646,6 +646,10 @@ editor.prototype.setSelectBoxFromInfo=function(thisevent){ } if(pos.x == 0) pos.y+=2; } + if (!editor.isMobile && scrollTo) { + editor.dom.iconLib.scrollLeft = pos.x * 32 - editor.dom.iconLib.offsetWidth / 2; + editor.dom.iconLib.scrollTop = pos.y * ysize - editor.dom.iconLib.offsetHeight / 2; + } editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; editor.dom.dataSelection.style.height = ysize - 6 + 'px'; diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 3bd99924..a11391a8 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -38,16 +38,16 @@ editor_blockly = function () { },'autoEvent'), MotaActionBlocks['changeFloor_m'].xmlText(), MotaActionFunctions.actionParser.parse([{ - "id": "moneyShop1", - "name": "贪婪之神", - "icon": "blueShop", - "textInList": "1F金币商店", - "use": "money", - "need": "20+10*times*(times+1)", - "text": "勇敢的武士啊,给我\\\${need}金币就可以:", + "id": "shop1", + "text": "\t[贪婪之神,blueShop]勇敢的武士啊, 给我\${20+2*flag:shop1}金币就可以:", + "textInList": "1F金币商店", "choices": [ - {"text": "生命+800", "effect": "status:hp+=800"}, - {"text": "攻击+4", "effect": "status:atk+=4"}, + {"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", @@ -83,8 +83,7 @@ editor_blockly = function () { MotaActionBlocks['hideImage_s'].xmlText(), MotaActionBlocks['showTextImage_s'].xmlText(), MotaActionBlocks['moveImage_s'].xmlText(), - MotaActionBlocks['showGif_0_s'].xmlText(), - MotaActionBlocks['showGif_1_s'].xmlText(), + MotaActionBlocks['showGif_s'].xmlText(), MotaActionBlocks['tip_s'].xmlText(), MotaActionBlocks['win_s'].xmlText(), MotaActionBlocks['lose_s'].xmlText(), @@ -111,8 +110,7 @@ editor_blockly = function () { MotaActionBlocks['moveHero_s'].xmlText(), MotaActionBlocks['jumpHero_s'].xmlText(), MotaActionBlocks['changeFloor_s'].xmlText(), - MotaActionBlocks['changePos_0_s'].xmlText(), - MotaActionBlocks['changePos_1_s'].xmlText(), + MotaActionBlocks['changePos_s'].xmlText(), MotaActionBlocks['battle_s'].xmlText(), MotaActionBlocks['useItem_s'].xmlText(), MotaActionBlocks['loadEquip_s'].xmlText(), @@ -130,6 +128,7 @@ editor_blockly = function () { MotaActionBlocks['show_s'].xmlText(), MotaActionBlocks['hide_s'].xmlText(), MotaActionBlocks['setBlock_s'].xmlText(), + MotaActionBlocks['turnBlock_s'].xmlText(), MotaActionBlocks['move_s'].xmlText(), MotaActionBlocks['jump_s'].xmlText(), MotaActionBlocks['showBgFgMap_s'].xmlText(), @@ -145,13 +144,12 @@ editor_blockly = function () { {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, {"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]}, ]}), - MotaActionFunctions.actionParser.parseList({"type": "for", "name": "switch:A", "from": "0", "to": "12", "step": "1", "data": []}), - MotaActionFunctions.actionParser.parseList({"type": "forEach", "name": "switch:A", "list": ["status:atk","status:def"], "data": []}), + 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['revisit_s'].xmlText(), MotaActionBlocks['exit_s'].xmlText(), MotaActionBlocks['trigger_s'].xmlText(), MotaActionBlocks['insert_1_s'].xmlText(), @@ -222,6 +220,7 @@ editor_blockly = function () { ]), MotaActionBlocks['expression_arithmetic_0'].xmlText(), MotaActionBlocks['evFlag_e'].xmlText(), + MotaActionBlocks['evTemp_e'].xmlText(), MotaActionBlocks['negate_e'].xmlText(), MotaActionBlocks['bool_e'].xmlText(), MotaActionBlocks['idString_e'].xmlText(), @@ -243,30 +242,30 @@ editor_blockly = function () { }), '', MotaActionFunctions.actionParser.parse([ - {"type": "choices", "text": "\\t[老人,man]少年,你需要钥匙吗?\\n我这里有大把的!", - "choices": [ - {"text": "黄钥匙(\\\${9+flag:shop_times}金币)", "color": [255,255,0,1], "action": [ - {"type": "if", "condition": "status:money>=9+flag:shop_times", - "true": [ - {"type": "setValue", "name": "status:money", "operator": "-=", "value": "9+flag:shop_times"}, - {"type": "setValue", "name": "item:yellowKey", "operator": "+=", "value": "1"}, - ], - "false": [ - "\\t[老人,man]你的金钱不足!", - {"type": "revisit"} - ] - } - ]}, - {"text": "蓝钥匙(\\\${18+2*flag:shop_times}金币)", "color": [0,0,255,1], "action": [ - ]}, - {"text": "离开", "action": [ - {"type": "exit"} - ]} - ] - }, - {"type": "setValue", "name": "flag:shop_times", "operator": "+=", "value": "1"}, - {"type": "revisit"} - ], 'event'), + {"type": "while", "condition": "true", "data": [ + {"type": "choices", "text": "\\t[老人,man]少年,你需要钥匙吗?\\n我这里有大把的!", + "choices": [ + {"text": "黄钥匙(\\\${9+flag:shop_times}金币)", "color": [255,255,0,1], "action": [ + {"type": "if", "condition": "status:money>=9+flag:shop_times", + "true": [ + {"type": "setValue", "name": "status:money", "operator": "-=", "value": "9+flag:shop_times"}, + {"type": "setValue", "name": "item:yellowKey", "operator": "+=", "value": "1"}, + ], + "false": [ + "\\t[老人,man]你的金钱不足!", + {"type": "continue"} + ] + } + ]}, + {"text": "蓝钥匙(\\\${18+2*flag:shop_times}金币)", "color": [0,0,255,1], "action": [ + ]}, + {"text": "离开", "action": [ + {"type": "break"} + ]} + ] + }, + {"type": "setValue", "name": "flag:shop_times", "operator": "+=", "value": "1"} + ]}], 'event'), '', MotaActionFunctions.actionParser.parse({ "trigger": "action", @@ -674,7 +673,7 @@ function omitedcheckUpdateFunction(event) { } } } - if (one.async && one.type != 'animate') hasAsync = true; + if (one.async && one.type != 'animate' && one.type != 'function') hasAsync = true; if (one.type == 'waitAsync') hasAsync = false; } return hasAsync; @@ -728,7 +727,7 @@ function omitedcheckUpdateFunction(event) { 'choices_s': 'EvalString_0', 'showTextImage_s': 'EvalString_0', 'function_s': 'RawEvalString_0', - 'shopsub': 'EvalString_3', + 'shopsub': 'EvalString_1', 'confirm_s': 'EvalString_0', 'drawTextContent_s': 'EvalString_0', } @@ -751,12 +750,12 @@ function omitedcheckUpdateFunction(event) { 'hide_s', 'setValue_s', 'if_s', + 'while_s', 'battle_s', 'openDoor_s', 'choices_s', 'setText_s', 'exit_s', - 'revisit_s', 'sleep_s', 'setBlock_s', 'insert_1_s' @@ -828,14 +827,15 @@ function omitedcheckUpdateFunction(event) { var selectPointBlocks = { "changeFloor_m": ["Number_0", "Number_1", "IdString_0", true], "jumpHero_s": ["PosString_0", "PosString_1"], - "changeFloor_s": ["PosString_0", "PosString_1", "IdString_0", true], - "changePos_0_s": ["PosString_0", "PosString_1"], + "changeFloor_s": ["Number_0", "Number_1", "IdString_0", true], + "changePos_s": ["PosString_0", "PosString_1"], "battle_1_s": ["PosString_0", "PosString_1"], "openDoor_s": ["PosString_0", "PosString_1", "IdString_0"], "closeDoor_s": ["PosString_0", "PosString_1"], "show_s": ["EvalString_0", "EvalString_1", "IdString_0"], "hide_s": ["EvalString_0", "EvalString_1", "IdString_0"], "setBlock_s": ["EvalString_1", "EvalString_2", "IdString_0"], + "turnBlock_s": ["EvalString_1", "EvalString_2", "IdString_0"], "move_s": ["PosString_0", "PosString_1"], "jump_s": ["PosString_2", "PosString_3"], // 跳跃暂时只考虑终点 "showBgFgMap_s": ["EvalString_0", "EvalString_1", "IdString_0"], @@ -876,16 +876,18 @@ function omitedcheckUpdateFunction(event) { block.setFieldValue(xv+"", arr[0]); block.setFieldValue(yv+"", arr[1]); } - if (block.type == 'changeFloor_m') { + if (block.type == 'changeFloor_m' || block.type == 'changeFloor_s') { block.setFieldValue("floorId", "Floor_List_0"); block.setFieldValue("loc", "Stair_List_0"); } }); } - editor_blockly.getAutoCompletions = function (content) { + editor_blockly.getAutoCompletions = function (content, type, name) { // --- content为当前框中输入内容;将返回一个列表,为后续所有可补全内容 + // console.log(type, name); + // 检查 status:xxx,item:xxx和flag:xxx var index = Math.max(content.lastIndexOf(":"), content.lastIndexOf(":")); if (index >= 0) { @@ -977,6 +979,114 @@ function omitedcheckUpdateFunction(event) { }).sort(); } + var allIds = core.getAllIconIds(); + var allIconIds = allIds.concat(Object.keys(core.statusBar.icons).filter(function (x) { + return core.statusBar.icons[x] instanceof Image; + })); + var allImages = Object.keys(core.material.images.images); + var allEnemys = Object.keys(core.material.enemys); + var allItems = Object.keys(core.material.items); + var allAnimates = Object.keys(core.material.animates); + var allBgms = Object.keys(core.material.bgms); + var allSounds = Object.keys(core.material.sounds); + var allShops = Object.keys(core.status.shops); + var allFloorIds = core.floorIds; + var allColors = ["aqua(青色)", "black(黑色)", "blue(蓝色)", "fuchsia(品红色)", "gray(灰色)", "green(深绿色)", "lime(绿色)", + "maroon(深红色)", "navy(深蓝色)", "gold(金色)", "olive(黄褐色)", "orange(橙色)", "purple(品红色)", + "red(红色)", "silver(淡灰色)", "teal(深青色)", "white(白色)", "yellow(黄色)"]; + var filter = function (list, content) { + return list.filter(function (one) { + return one != content && one.startsWith(content); + }).sort(); + } + + // 对任意图块提供补全 + if ((type == 'text_1_s' && name == 'EvalString_1') || (type == 'autoText_s' && name == 'EvalString_1') + || (type == 'choices_s' && name == 'IdString_0') || (type == 'choicesContext' && name == 'IdString_0') + || (type == 'closeDoor_s' && name == 'IdString_0') || (type == 'setBlock_s' && name == 'EvalString_0') + || (type == 'setBgFgBlock_s' && name == 'EvalString_0') || (type == 'drawIcon_s' && name == 'IdString_0') + || (type == 'shopsub' && name == 'IdString_1') || (type == 'shopChoices' && name == 'IdString_0')) { + return filter(allIds, content); + } + + // 对怪物ID提供补全 + if ((type == 'idString_3_e' || type == 'battle_s' || type == 'setEnemy_s') && name == 'IdString_0') { + return filter(allEnemys, content); + } + + // 对道具ID进行补全 + if ((type == 'useItem_s' || type == 'loadEquip_s') && name == 'IdString_0') { + return filter(allItems, content); + } + + // 对图片名进行补全 + if ((type == 'showImage_s' || type == 'showImage_1_s' || type == 'showGif_s' || type == 'setHeroIcon_s' + || type == 'follow_s' || type == 'unfollow_s' || type == 'drawImage_s' || type == 'drawImage_1_s') && name == 'EvalString_0') { + return filter(allImages, content); + } + + // 对动画进行补全 + if (type == 'animate_s' && name == 'IdString_0') { + return filter(allAnimates, content); + } + + // 对音乐进行补全 + if ((type == 'playBgm_s' || type == 'loadBgm_s' || type == 'freeBgm_s') && name == 'EvalString_0') { + return filter(allBgms, content); + } + + // 对音效进行补全 + if (type == 'playSound_s' && name == 'EvalString_0') { + return filter(allSounds, content); + } + + // 对全局商店进行补全 + if ((type == 'openShop_s' || type == 'disableShop_s') && name == 'IdString_0') { + return filter(allShops, content); + } + + // 对楼层名进行补全 + if ((type == 'setFloor_s' || type == 'show_s' || type == 'hide_s' || type == 'insert_2_s' + || type == 'setBlock_s' || type == 'turnBlock_s' || type == 'showFloorImg_s' || type == 'hideFloorImg_s' + || type == 'showBgFgMap_s' || type == 'hideBgFgMap_s' || type == 'setBgFgBlock_s' + || type == 'openDoor_s' || type == 'changeFloor_m') && name == "IdString_0") { + return filter(allFloorIds, content); + } + + // 对\f进行自动补全 + index = Math.max(content.lastIndexOf("\f["), content.lastIndexOf("\\f[")); + if (index >= 0) { + if (content.charAt(index) == '\\') index++; + var after = content.substring(index + 2); + if (after.indexOf(",") < 0 && after.indexOf("]") < 0) { + return filter(allImages, after); + } + } + + // 对\\i进行补全 + index = content.lastIndexOf("\\i["); + if (index >= 0) { + var after = content.substring(index + 3); + if (after.indexOf("]") < 0) { + return filter(allIconIds, after); + } + } + + // 对\r进行补全 + index = Math.max(content.lastIndexOf("\r["), content.lastIndexOf("\\r[")); + if (index >= 0) { + if (content.charAt(index) == '\\') index++; + var after = content.substring(index + 2); + if (after.indexOf("]") < 0) { + return filter(allColors, after); + } + } + + // 对\进行补全! + if (content.charAt(content.length - 1) == '\\') { + return ["n(换行)", "f(立绘)", "r(变色)", "i(图标)", "z(暂停打字)", "t(标题图标)", "b(对话框)", "c(字体大小)", "d(粗体)", "e(斜体)"]; + } + return []; } @@ -1079,11 +1189,13 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { // --- awesomplete var awesomplete = new Awesomplete(htmlInput, { - minChars: pb.type == "idString_3_e" ? 1 : 2, + minChars: 1, maxItems: 12, autoFirst: true, replace: function (text) { text = text.toString(); + var index = text.indexOf("("); + if (index >= 0) text = text.substring(0, index); var value = this.input.value, index = this.input.selectionEnd; if (index == null) index = value.length; if (index < awesomplete.prefix.length) index = awesomplete.prefix.length; @@ -1124,7 +1236,7 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { if (index == null) index = value.length; value = value.substring(0, index); // cal prefix - awesomplete.prefix = ""; + awesomplete.prefix = value; for (var i = index - 1; i>=0; i--) { var c = value.charAt(i); if (!/^[a-zA-Z0-9_\u4E00-\u9FCC]$/.test(c)) { @@ -1133,13 +1245,7 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { } } - var list = editor_blockly.getAutoCompletions(value); - if (pb.type == "idString_3_e") { - list = list.concat(Object.keys(core.material.enemys).filter(function (one) { - return one != value && one.startsWith(value); - })); - list.sort(); - } + var list = editor_blockly.getAutoCompletions(value, pb.type, self.name); awesomplete.list = list; awesomplete.ul.style.marginLeft = getCaretCoordinates(htmlInput, htmlInput.selectionStart).left - diff --git a/_server/editor_mappanel.js b/_server/editor_mappanel.js index f810e748..996a66ea 100644 --- a/_server/editor_mappanel.js +++ b/_server/editor_mappanel.js @@ -48,7 +48,7 @@ editor_mappanel_wrapper = function (editor) { if (editor.uivalues.bindSpecialDoor.loc != null) return; var loc = editor.uifunctions.eToLoc(e); var pos = editor.uifunctions.locToPos(loc, true); - editor.setSelectBoxFromInfo(editor[editor.layerMod][pos.y][pos.x]); + editor.setSelectBoxFromInfo(editor[editor.layerMod][pos.y][pos.x], true); return; } @@ -514,7 +514,7 @@ editor_mappanel_wrapper = function (editor) { editor.uifunctions.hideMidMenu(); e.stopPropagation(); var thisevent = editor[editor.layerMod][editor.pos.y][editor.pos.x]; - editor.setSelectBoxFromInfo(thisevent); + editor.setSelectBoxFromInfo(thisevent, true); } /** diff --git a/_server/editor_mode.js b/_server/editor_mode.js index 7cd86e40..4bf207ad 100644 --- a/_server/editor_mode.js +++ b/_server/editor_mode.js @@ -18,7 +18,7 @@ editor_mode = function (editor) { this._ids = {} this.dom = {} this.actionList = []; - this.mode = ''; + this.mode = 'tower'; // 初始默认显示全塔属性 this.info = {}; this.appendPic = {}; this.doubleClickMode = 'change'; @@ -176,6 +176,7 @@ editor_mode = function (editor) { printe(err); throw Error(err); } + var notExist = null; thiseval.map(function (v) { var name = v.indexOf('.') < 0 ? (v+'.png') : v; if (data.indexOf(name) < 0) notExist = name; diff --git a/_server/editor_table.js b/_server/editor_table.js index 350136f2..c9d0ce31 100644 --- a/_server/editor_table.js +++ b/_server/editor_table.js @@ -43,7 +43,7 @@ editor_table_wrapper = function (editor) { return /* html */`
${content.join('')}
\n`; } editor_table.prototype.checkboxSetMember = function (value,key,prefixString) { - return /* html */`${prefixString}\n`; + return /* html */`${prefixString}\n`; } editor_table.prototype.editGrid = function (showComment, showEdit) { var list = []; @@ -79,7 +79,11 @@ editor_table_wrapper = function (editor) { var value=[] for (var i in inputs) { if (inputs[i].nodeName == 'INPUT') { - if (inputs[i].checked) value.push(inputs[i].getAttribute('key')); + if (inputs[i].checked) { + var one = inputs[i].getAttribute('key'); + if (inputs[i].getAttribute('ctype') == 'number') one = parseFloat(one); + value.push(one); + } } } thiseval = value; @@ -188,6 +192,7 @@ editor_table_wrapper = function (editor) { // 当cobj的参数为函数时,代入args算出值 for (var key in cobj) { if (key === '_data') continue; + if (key === '_transform' || key == '_onconfirm') cobj[key] = cobj[key].toString(); if (cobj[key] instanceof Function) cobj[key] = cobj[key](args); } pvobj[ii] = vobj = args.vobj; @@ -344,31 +349,6 @@ editor_table_wrapper = function (editor) { } } - /** - * 当“cannotOut / cannotIn”的表格值变化时 - */ - editor_table.prototype.onCannotInOutChange = function (guid, obj, commentObj, thisTr, inputs, field, cobj, modeNode) { - editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); - var value = []; - var directions = ['up', 'down', 'left', 'right']; - var index = 0; - for (var i in inputs) { - if (inputs[i].nodeName == 'INPUT') { - if (inputs[i].checked) value.push(directions[index]); - index++; - } - } - if (value.length == 0) thiseval = null; - else thiseval = value; - if (editor.table.checkRange(cobj, thiseval)) { - editor_mode.addAction(['change', field, thiseval]); - editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 - } else { - printe(field + ' : 输入的值不合要求,请鼠标放置在注释上查看说明'); - } - - } - /** * 当"显示完整注释"被按下时 */ @@ -384,8 +364,10 @@ editor_table_wrapper = function (editor) { var tr = button.parentNode.parentNode; var guid = tr.getAttribute('id'); var cobj = JSON.parse(tr.children[1].getAttribute('cobj')); + var input = tr.children[2].children[0].children[0]; if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string }); + if (cobj._type === 'material') editor.table.selectMaterial(input, cobj); } /** @@ -399,6 +381,7 @@ editor_table_wrapper = function (editor) { if (editor_mode.doubleClickMode === 'change') { if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string }); + if (cobj._type === 'material') editor.table.selectMaterial(input, cobj); } else if (editor_mode.doubleClickMode === 'add') { editor_mode.doubleClickMode = 'change'; editor.table.addfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) @@ -408,6 +391,17 @@ editor_table_wrapper = function (editor) { } } + editor_table.prototype.selectMaterial = function (input, cobj) { + editor.uievent.selectMaterial(input.value, cobj._docs || cobj._data || '请选择素材', cobj._directory, function (one) { + if (!/^[-A-Za-z0-9_.]+$/.test(one)) return null; + if (cobj._transform) return eval("("+cobj._transform+")(one)"); + return one; + }, function (data) { + input.value = JSON.stringify(cobj._onconfirm ? eval("("+cobj._onconfirm+")(JSON.parse(input.value), data)") : data); + input.onchange(); + }) + } + /** * 删除表格项 */ diff --git a/_server/editor_ui.js b/_server/editor_ui.js index d2764df8..47e26bdb 100644 --- a/_server/editor_ui.js +++ b/_server/editor_ui.js @@ -468,16 +468,7 @@ editor_ui_wrapper = function (editor) { uievent.elements.canvas = document.getElementById('uievent'); uievent.elements.usedFlags = document.getElementById('uieventUsedFlags'); uievent.elements.usedFlagList = document.getElementById('uieventUsedFlagList'); - - uievent.confirm = function () { - var callback = uievent.values.callback, floorId = uievent.values.floorId, - x = uievent.values.x, y = uievent.values.y; - uievent.close(); - if (callback) { - callback(floorId, x, y); - } - } - uievent.elements.yes.onclick = uievent.confirm; + uievent.elements.materialList = document.getElementById('uieventMaterialList'); uievent.close = function () { uievent.isOpen = false; @@ -534,7 +525,6 @@ editor_ui_wrapper = function (editor) { uievent.selectPoint = function (floorId, x, y, hideFloor, callback) { uievent.values.hideFloor = hideFloor; - uievent.values.callback = callback; uievent.values.size = editor.isMobile ? window.innerWidth / core.__SIZE__ : 32; uievent.elements.selectPointBox.style.width = (uievent.values.size - 6) + "px"; uievent.elements.selectPointBox.style.height = (uievent.values.size - 6) + "px"; @@ -545,12 +535,20 @@ editor_ui_wrapper = function (editor) { uievent.elements.selectPoint.style.display = 'block'; uievent.elements.yes.style.display = 'inline'; uievent.elements.selectBackground.style.display = 'none'; - uievent.elements.selectFloor.style.display = hideFloor ? 'none' : 'inline'; + // uievent.elements.selectFloor.style.display = hideFloor ? 'none' : 'inline'; + uievent.elements.selectFloor.style.display = 'inline'; uievent.elements.selectPointBox.style.display = 'block'; uievent.elements.canvas.style.display = 'block'; uievent.elements.usedFlags.style.display = 'none'; uievent.elements.usedFlagList.style.display = 'none'; uievent.elements.body.style.overflow = "hidden"; + uievent.elements.yes.onclick = function () { + var floorId = uievent.values.floorId, x = uievent.values.x, y = uievent.values.y; + uievent.close(); + if (callback) { + callback(floorId, x, y); + } + } // Append children var floors = ""; @@ -646,7 +644,8 @@ editor_ui_wrapper = function (editor) { })(); uievent.elements.div.onmousewheel = function (e) { - if (uievent.mode != 'selectPoint' || uievent.values.hideFloor) return; + // if (uievent.mode != 'selectPoint' || uievent.values.hideFloor) return; + if (uievent.mode != 'selectPoint') return; var index = core.floorIds.indexOf(uievent.values.floorId); try { if (e.wheelDelta) @@ -743,6 +742,60 @@ editor_ui_wrapper = function (editor) { return list; } + // ------ 素材选择框 ------ // + uievent.selectMaterial = function (value, title, directory, transform, callback) { + fs.readdir(directory, function (err, data) { + if (err) { + printe(directory + '不存在!'); + throw (directory + '不存在!'); + } + if (!(data instanceof Array)) { + printe('没有可显示的内容') + return; + } + value = value || []; + data = (transform ? data.map(transform) : data).filter(function (one) {return one;}).sort(); + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectMaterial'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.selectBackground.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.usedFlags.style.display = 'none'; + uievent.elements.usedFlagList.style.display = 'none'; + uievent.elements.materialList.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var list = Array.from(document.getElementsByClassName('materialCheckbox')).filter(function (one) { + return one.checked; + }).map(function (one) {return one.getAttribute('key'); }); + uievent.close(); + if (callback) callback(list); + } + + // 显示每一项内容 + var html = "

"; + html += "
"; + data.forEach(function (one) { + html += `= 0? 'checked' : ''} /> ${one}
`; + }); + html += "

"; + uievent.elements.materialList.innerHTML = html; + }); + } + + uievent._selectAll = function (checked) { + Array.from(document.getElementsByClassName('materialCheckbox')).forEach(function (one) { + one.checked = checked; + }) + } + editor.constructor.prototype.uievent=uievent; } \ No newline at end of file diff --git a/_server/table/comment.js b/_server/table/comment.js index 3129e109..c55edea4 100644 --- a/_server/table/comment.js +++ b/_server/table/comment.js @@ -162,10 +162,22 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { }, "special": { "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null || thiseval instanceof Array || (thiseval==~~thiseval && thiseval>=0)", - "_docs": "特殊属性", - "_data": "特殊属性\n\n0:无,1:先攻,2:魔攻,3:坚固,4:2连击,\n5:3连击,6:n连击,7:破甲,8:反击,9:净化,\n10:模仿,11:吸血,12:中毒,13:衰弱,14:诅咒,\n15:领域,16:夹击,17:仇恨,18:阻击,19:自爆,\n20:无敌,21:退化,22:固伤,23:重生,24:激光,25:光环\n26:支援,27:捕捉\n多个属性例如用[1,4,11]表示先攻2连击吸血" + "_type": "checkboxSet", + "_checkboxSet":function(){ + var array=functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys.getSpecials() + var b=[],c=[]; + for (var index = 0; index < array.length; index++) { + b.push(array[index][0]) + var name = array[index][1]; + if (name instanceof Function) name = name({}); + c.push((index%2==0&&index>0?'
':'')+name+'') + } + return { + "prefix":c, + "key":b + } + }, + "_data": "特殊属性" }, "value": { "_leaf": true, @@ -228,7 +240,7 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { } } }, - "enemys_template": { 'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'exp': 0, 'point': 0, 'special': 0 }, + "enemys_template": { 'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'exp': 0, 'point': 0, 'special': [] }, // --------------------------- 【图块属性】相关的表格配置 --------------------------- // @@ -266,9 +278,8 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "values": [ "null", "openDoor", - "passNet", - "changeLight", "pushBox", + "ski", "custom" ] }, diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js index f2e96178..d92b6349 100644 --- a/_server/table/data.comment.js +++ b/_server/table/data.comment.js @@ -19,36 +19,75 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { }, "images": { "_leaf": true, - "_type": "textarea", + "_type": "material", "_range": "editor.mode.checkImages(thiseval)", + "_directory": "./project/images/", + "_transform": function (one) { + if (one.endsWith('.png') || one.endsWith('.jpg') || one.endsWith('.jpeg') || end.endsWith('.gif')) + return one; + return null; + }, "_docs": "本塔使用图片", - "_data": "在此存放所有可能使用的图片(tilesets除外) \n图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 \n 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 \n 依次向后添加" + "_data": "在此存放所有可能使用的图片(tilesets除外) \n图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 \n 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 \n 依次向后添加", }, "tilesets": { "_leaf": true, - "_type": "textarea", + "_type": "material", "_range": "editor.mode.checkImages(thiseval)", + "_directory": "./project/images/", + "_transform": function (one) { + if (one.endsWith('.png')) + return one; + return null; + }, + "_onconfirm": function (previous, current) { + // 额外素材是有序的,因此根据之前的内容进行排序,然后在之后追加新的 + previous = previous || []; + return current.sort(function (a, b) { + var i = previous.indexOf(a); + var j = previous.indexOf(b); + if (i >= 0 && j >= 0) return i - j; + return j >= 0 ? 1 : -1; + }); + }, "_docs": "本塔额外素材", "_data": "在此存放额外素材的图片名, \n可以自定导入任意张素材图片,无需PS,无需注册,即可直接在游戏中使用 \n 形式如[\"1.png\", \"2.png\"] ,将需要的素材图片放在images目录下 \n 素材的宽高必须都是32的倍数,且图片上的总图块数不超过1000(即最多有1000个32*32的图块在该图片上)" }, "animates": { "_leaf": true, - "_type": "textarea", + "_type": "material", "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/animates/", + "_transform": function (one) { + if (one.endsWith(".animate")) return one.substring(0, one.lastIndexOf('.')); + return null; + }, "_docs": "本塔使用动画", "_data": "在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" 根据需求自行添加" }, "bgms": { "_leaf": true, - "_type": "textarea", + "_type": "material", "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/sounds/", + "_transform": function (one) { + if (one.endsWith('.mp3') || one.endsWith('.wmv') || one.endsWith('.ogg') || end.endsWith('.wav')) + return one; + return null; + }, "_docs": "本塔使用音乐", "_data": "在此存放所有的bgm,和文件名一致。 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" }, "sounds": { "_leaf": true, - "_type": "textarea", + "_type": "material", "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/sounds/", + "_transform": function (one) { + if (one.endsWith('.mp3') || one.endsWith('.wmv') || one.endsWith('.ogg') || end.endsWith('.wav')) + return one; + return null; + }, "_docs": "本塔使用音效", "_data": "在此存放所有的SE,和文件名一致 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" }, @@ -529,29 +568,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_docs": "楼传平面模式", "_data": "传送器平面塔模式;此模式下楼层传送器将飞到上次离开该楼层的位置。" }, - "pickaxeFourDirections": { - "_leaf": true, - "_type": "checkbox", - "_docs": "四向破墙镐", - "_data": "使用破墙镐是否四个方向都破坏;如果false则只破坏面前的墙壁" - }, - "bombFourDirections": { - "_leaf": true, - "_type": "checkbox", - "_docs": "四向炸弹", - "_data": "使用炸弹是否四个方向都会炸;如果false则只炸面前的怪物(即和圣锤等价)" - }, - "snowFourDirections": { - "_leaf": true, - "_type": "checkbox", - "_docs": "四向破冰稿", - "_data": "使用冰冻徽章是否四个方向都会消除熔岩;如果false则只消除面前的熔岩" - }, - "bigKeyIsBox": { - "_leaf": true, - "_type": "checkbox", - "_data": "如果此项为true,则视为钥匙盒,红黄蓝钥匙+1;若为false,则视为大黄门钥匙" - }, "steelDoorWithoutKey": { "_leaf": true, "_type": "checkbox", @@ -564,12 +580,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_docs": "首次道具提示", "_data": "首次获得道具是否提示" }, - "equipment": { - "_leaf": true, - "_type": "checkbox", - "_docs": "剑盾装备", - "_data": "剑和盾是否作为装备。如果此项为true,则作为装备,需要在装备栏使用,否则将直接加属性。" - }, "equipboxButton": { "_leaf": true, "_type": "checkbox", @@ -667,12 +677,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_docs": "允许瞬移", "_data": "是否允许瞬间移动" }, - "enableDisabledShop": { - "_leaf": true, - "_type": "checkbox", - "_docs": "商店预览", - "_data": "是否允许查看未开启状态的快捷商店内容;如果此项为真,则对于未开启状态的商店允许查看其内容(但不能购买)" - }, "disableShopOnDamage": { "_leaf": true, "_type": "checkbox", diff --git a/_server/table/functions.comment.js b/_server/table/functions.comment.js index d8fd7de3..b0ab8a39 100644 --- a/_server/table/functions.comment.js +++ b/_server/table/functions.comment.js @@ -70,29 +70,11 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_lint": true, "_data": "道具后脚本" }, - "afterChangeLight": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "改变亮灯后" - }, "afterPushBox": { "_leaf": true, "_type": "textarea", "_lint": true, "_data": "推箱子后" - }, - "afterPassNet": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "特殊地形后" - }, - "canUseQuickShop": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "能否快捷商店" } } }, diff --git a/_server/table/plugins.comment.js b/_server/table/plugins.comment.js index ec02f2ff..f012cdc1 100644 --- a/_server/table/plugins.comment.js +++ b/_server/table/plugins.comment.js @@ -14,13 +14,19 @@ var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_range": "typeof(thiseval)=='string'", "_data": "自定义插件" }, + "shop": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string'", + "_data": "全局商店" + }, "drawLight": { "_leaf": true, "_type": "textarea", "_range": "typeof(thiseval)=='string' || thiseval==null", "_data": "灯光效果" }, - "drawItemShop": { + "itemShop": { "_leaf": true, "_type": "textarea", "_range": "typeof(thiseval)=='string' || thiseval==null", diff --git a/editor-mobile.html b/editor-mobile.html index 784302b5..909ea08a 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -565,6 +565,7 @@
+
diff --git a/editor.html b/editor.html index d82f8236..f6d81925 100644 --- a/editor.html +++ b/editor.html @@ -546,7 +546,8 @@
- + +
diff --git a/libs/actions.js b/libs/actions.js index de3809c0..0a9ed824 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -260,9 +260,6 @@ actions.prototype._sys_keyDown_lockControl = function (keyCode) { case 'replayRemain': this._keyDownSL(keyCode); break; - case 'shop': - this._keyDownShop(keyCode); - break; case 'selectShop': case 'switchs': case 'settings': @@ -346,9 +343,6 @@ actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) { case 'viewMaps': this._keyUpViewMaps(keyCode); break; - case 'shop': - this._keyUpShop(keyCode); - break; case 'selectShop': this._keyUpQuickShop(keyCode); break; @@ -476,7 +470,6 @@ actions.prototype._sys_onmove_choices = function (x, y) { switch (core.status.event.id) { case 'action': if (core.status.event.data.type != 'choices') break; - case 'shop': case 'selectShop': case 'switchs': case 'settings': @@ -488,6 +481,9 @@ actions.prototype._sys_onmove_choices = function (x, y) { case 'gameInfo': this._onMoveChoices(x, y); return true; + case 'confirmBox': + this._onMoveConfirmBox(x, y); + return true; default: break; } @@ -614,9 +610,6 @@ actions.prototype._sys_onclick_lockControl = function (x, y) { case 'settings': this._clickSettings(x, y); break; - case 'shop': - this._clickShop(x, y); - break; case 'selectShop': this._clickQuickShop(x, y); break; @@ -792,10 +785,6 @@ actions.prototype._sys_longClick_lockControl = function (x, y) { return true; } } - // 长按商店连续购买 - if (core.status.event.id == 'shop' && x >= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { - return this._clickShop(x, y); - } // 长按可以跳过等待事件 if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' && !core.status.event.data.current.noSkip) { @@ -886,7 +875,7 @@ actions.prototype._clickCenterFly = function (x, y) { core.useItem('centerFly'); } else { - core.drawTip('当前不能使用中心对称飞行器'); + core.drawTip('当前不能使用' + core.material.items['centerFly'].name); } } } @@ -898,7 +887,7 @@ actions.prototype._keyUpCenterFly = function (keycode) { core.useItem('centerFly'); } else { - core.drawTip('当前不能使用中心对称飞行器'); + core.drawTip('当前不能使用' + core.material.items['centerFly'].name); } } } @@ -939,6 +928,22 @@ actions.prototype._keyUpConfirmBox = function (keycode) { } } +////// 鼠标在确认框上移动时 ////// +actions.prototype._onMoveConfirmBox = function (x, y) { + if (y == this.HSIZE + 1) { + if (x == this.HSIZE - 2 || x == this.HSIZE - 1) { + core.status.event.selection = 0; + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + return; + } + if (x == this.HSIZE + 2 || x == this.HSIZE + 1) { + core.status.event.selection = 1; + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + return; + } + } +} + ////// 自定义事件时的点击操作 ////// actions.prototype._clickAction = function (x, y) { if (core.status.event.data.type == 'text') { @@ -1280,61 +1285,28 @@ actions.prototype._keyUpViewMaps = function (keycode) { return; } -////// 商店界面时的点击操作 ////// -actions.prototype._clickShop = function (x, y) { - var shop = core.status.event.data.shop; - var choices = shop.choices; - if (x >= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { - var topIndex = this.HSIZE - parseInt(choices.length / 2) + (core.status.event.ui.offset || 0); - if (y >= topIndex && y < topIndex + choices.length) { - return core.events._useShop(shop, y - topIndex); - } - // 离开 - else if (y == topIndex + choices.length) { - core.events._exitShop(); - } - else return false; - } - return true; -} - -actions.prototype._keyDownShop = function (keycode) { - // 商店界面长按空格连续购买 - if (keycode == 32 && core.status.event.selection != core.status.event.data.shop.choices.length) { - this._selectChoices(core.status.event.data.shop.choices.length + 1, keycode, this._clickShop); - return; - } - this._keyDownChoices(keycode); -} - -////// 商店界面时,放开某个键的操作 ////// -actions.prototype._keyUpShop = function (keycode) { - if (keycode == 27 || keycode == 88) { - core.events._exitShop(); - return; - } - if (keycode != 32 || core.status.event.selection == core.status.event.data.shop.choices.length) { - this._selectChoices(core.status.event.data.shop.choices.length + 1, keycode, this._clickShop); - return; - } - return; -} - ////// 快捷商店界面时的点击操作 ////// actions.prototype._clickQuickShop = function (x, y) { - var keys = Object.keys(core.status.shops).filter(function (shopId) { - return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable - }); + var shopIds = core.listShopIds(); if (x >= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { - var topIndex = this.HSIZE - parseInt(keys.length / 2) + (core.status.event.ui.offset || 0); - if (y >= topIndex && y < topIndex + keys.length) { - core.events.openShop(keys[y - topIndex], true); - if (core.status.event.id == 'shop') - core.status.event.data.fromList = true; + var topIndex = this.HSIZE - parseInt(shopIds.length / 2) + (core.status.event.ui.offset || 0); + if (y >= topIndex && y < topIndex + shopIds.length) { + var shopId = shopIds[y - topIndex]; + if (!core.canOpenShop(shopId)) { + core.drawTip('当前项尚未开启'); + return; + } + var message = core.canUseQuickShop(shopId); + if (message == null) { + // core.ui.closePanel(); + core.openShop(shopIds[y - topIndex], false); + } else { + core.drawTip(message); + } } // 离开 - else if (y == topIndex + keys.length) + else if (y == topIndex + shopIds.length) core.ui.closePanel(); return; } @@ -1347,10 +1319,7 @@ actions.prototype._keyUpQuickShop = function (keycode) { core.ui.closePanel(); return; } - var keys = Object.keys(core.status.shops).filter(function (shopId) { - return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable - }); - this._selectChoices(keys.length + 1, keycode, this._clickQuickShop); + this._selectChoices(core.listShopIds().length + 1, keycode, this._clickQuickShop); return; } diff --git a/libs/control.js b/libs/control.js index a0a3d27a..e7ba51c1 100644 --- a/libs/control.js +++ b/libs/control.js @@ -656,10 +656,9 @@ control.prototype._moveAction_moving = function (callback) { // 执行该点事件 if (!hasTrigger) core.events._trigger(nowx, nowy); - core.updateStatusBar(); // 检查该点是否是滑冰 - if (core.getBgNumber() == 167) { + if (core.onSki()) { core.insertAction("滑冰事件", null, null, null, true); } @@ -745,7 +744,7 @@ control.prototype.waitHeroToStop = function(callback) { core.setHeroLoc('direction', lastDirection); core.drawHero(); callback(); - }, 30); + }, core.status.replay.speed == 24 ? 0 : 30); } } @@ -757,8 +756,7 @@ control.prototype.turnHero = function(direction) { core.status.route.push("turn:"+direction); return; } - var dirs = {'up':'right','right':'down','down':'left','left':'up'}; - core.setHeroLoc('direction', dirs[core.getHeroLoc('direction')]); + core.setHeroLoc('direction', core.turnDirection(':right')); core.drawHero(); core.status.route.push("turn"); } @@ -991,7 +989,8 @@ control.prototype.checkBlock = function () { var damage = core.status.checkBlock.damage[loc]; if (damage) { core.status.hero.hp -= damage; - core.drawTip("受到"+(core.status.checkBlock.type[loc]||"伤害")+damage+"点"); + var text = (Object.keys(core.status.checkBlock.type[loc] || {}).join(",")) || "伤害"; + core.drawTip("受到"+text+damage+"点"); core.drawHeroAnimate("zone"); this._checkBlock_disableQuickShop(); core.status.hero.statistics.extraDamage += damage; @@ -1000,6 +999,8 @@ control.prototype.checkBlock = function () { core.updateStatusBar(); core.events.lose(); return; + } else { + core.updateStatusBar(); } } this._checkBlock_snipe(core.status.checkBlock.snipe[loc]); @@ -1009,9 +1010,9 @@ control.prototype.checkBlock = function () { control.prototype._checkBlock_disableQuickShop = function () { // 禁用快捷商店 if (core.flags.disableShopOnDamage) { - for (var shopId in core.status.shops) { - core.status.shops[shopId].visited = false; - } + Object.keys(core.status.shops).forEach(function (shopId) { + core.setShopVisited(shopId, false); + }); } } @@ -1020,7 +1021,7 @@ control.prototype._checkBlock_snipe = function (snipe) { if (!snipe || snipe.length == 0) return; var actions = []; snipe.forEach(function (t) { - actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 500, "keep": true, "async": true}); + actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 250, "keep": true, "async": true}); }); actions.push({"type": "waitAsync"}); core.insertAction(actions); @@ -1032,12 +1033,14 @@ control.prototype._checkBlock_ambush = function (ambush) { // 捕捉效果 var actions = []; ambush.forEach(function (t) { - actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 500, "keep": false, "async":true}); + actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 250, "keep": false, "async":true}); }); actions.push({"type": "waitAsync"}); // 强制战斗 ambush.forEach(function (t) { - actions.push({"type": "battle", "id": t[2]}); + actions.push({"type": "function", "function": "function() { "+ + "core.battle('" + t[2] + "', " + t[0]+ "," + t[1] + ", true, core.doAction); "+ + "}", "async": true}); }); core.insertAction(actions); } @@ -1265,7 +1268,7 @@ control.prototype.bookReplay = function () { if (core.isMoving() || core.status.replay.animate || (core.status.event.id && core.status.event.id != 'viewMaps')) return core.drawTip("请等待当前事件的处理结束"); - if (!core.hasItem('book')) return core.drawTip('你没有怪物手册'); + if (!core.hasItem('book')) return core.drawTip('你没有'+core.material.items['book'].name); // 从“浏览地图”页面打开 if (core.status.event.id=='viewMaps') @@ -1430,7 +1433,7 @@ control.prototype._replayAction_item = function (action) { if (action.indexOf("item:")!=0) return false; var itemId = action.substring(5); if (!core.canUseItem(itemId)) return false; - if (core.material.items[itemId].hideInReplay) { + if (core.material.items[itemId].hideInReplay || core.status.replay.speed == 24) { core.useItem(itemId, false, core.replay); return true; } @@ -1463,7 +1466,7 @@ control.prototype._replayAction_equip = function (action) { var index = ownEquipment.indexOf(equipId), per = core.__SIZE__-1; if (index<0) return false; core.status.route.push(action); - if (core.material.items[equipId].hideInReplay) { + if (core.material.items[equipId].hideInReplay || core.status.replay.speed == 24) { core.loadEquip(equipId, core.replay); return true; } @@ -1483,6 +1486,10 @@ control.prototype._replayAction_unEquip = function (action) { if (!core.isset(equipType)) return false; core.ui.drawEquipbox(equipType); core.status.route.push(action); + if (core.status.replay.speed == 24) { + core.unloadEquip(equipType, core.replay); + return true; + } setTimeout(function () { core.ui.closePanel(); core.unloadEquip(equipType, core.replay); @@ -1496,6 +1503,11 @@ control.prototype._replayAction_fly = function (action) { var toIndex=core.floorIds.indexOf(floorId); if (!core.canUseItem('fly')) return false; core.ui.drawFly(toIndex); + if (core.status.replay.speed == 24) { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + return true; + } setTimeout(function () { if (!core.flyTo(floorId, core.replay)) core.control._replay_error(action); @@ -1505,36 +1517,13 @@ control.prototype._replayAction_fly = function (action) { control.prototype._replayAction_shop = function (action) { if (action.indexOf("shop:")!=0) return false; - var sps=action.substring(5).split(":"); - var shopId=sps[0], selections=sps[1].split(""); - if (selections.length == 0) return false; - var shop=core.status.shops[shopId]; - if (!shop || !shop.visited) return false; - // --- 判定commonEvent或item - if (shop.commonEvent || shop.item) { - core.openShop(shopId, false); - setTimeout(core.replay); + var shopId = action.substring(5); + if (core.canUseQuickShop(shopId) != null || !core.canOpenShop(shopId)) { + this._replay_error(shopId); return true; } - var choices = shop.choices; - core.status.event.selection = parseInt(selections.shift()); - core.events.openShop(shopId, false); - var topIndex = core.__HALF_SIZE__ - parseInt(choices.length / 2) + (core.status.event.ui.offset || 0); - var shopInterval = setInterval(function () { - if (!core.actions._clickShop(core.__HALF_SIZE__, topIndex+core.status.event.selection)) { - clearInterval(shopInterval); - core.control._replay_error(action); - return; - } - if (selections.length==0) { - clearInterval(shopInterval); - core.actions._clickShop(core.__HALF_SIZE__, topIndex+choices.length); - core.replay(); - return; - } - core.status.event.selection = parseInt(selections.shift()); - core.events.openShop(shopId, false); - }, core.control.__replay_getTimeout()); + core.openShop(shopId, false); + core.replay(); return true; } @@ -1542,14 +1531,14 @@ control.prototype._replayAction_turn = function (action) { if (action != 'turn' && action.indexOf('turn:') != 0) return false; if (action == 'turn') core.turnHero(); else core.turnHero(action.substring(5)); - setTimeout(core.replay); + core.replay(); return true; } control.prototype._replayAction_getNext = function (action) { if (action != "getNext") return false; if (!core.getNextItem()) return false; - setTimeout(core.replay); + core.replay(); return true; } @@ -1566,6 +1555,11 @@ control.prototype._replayAction_moveDirectly = function (action) { var x=parseInt(pos[0]), y=parseInt(pos[1]); var nowx=core.getHeroLoc('x'), nowy=core.getHeroLoc('y'); if (!core.moveDirectly(x, y)) return false; + if (core.status.replay.speed == 24) { + core.replay(); + return true; + } + core.ui.drawArrow('ui', 32*nowx+16-core.bigmap.offsetX, 32*nowy+16-core.bigmap.offsetY, 32*x+16-core.bigmap.offsetX, 32*y+16-core.bigmap.offsetY, '#FF0000', 3); setTimeout(function () { @@ -1578,7 +1572,7 @@ control.prototype._replayAction_moveDirectly = function (action) { control.prototype._replayAction_key = function (action) { if (action.indexOf("key:") != 0) return false; core.actions.keyUp(parseInt(action.substring(4)), false, true); - setTimeout(core.replay); + core.replay(); return true; } diff --git a/libs/core.js b/libs/core.js index 2654c2e1..09c5d5f4 100644 --- a/libs/core.js +++ b/libs/core.js @@ -239,6 +239,10 @@ core.prototype._init_flags = function () { core.values = core.clone(core.data.values); core.firstData = core.clone(core.data.firstData); this._init_sys_flags(); + + // 让你总是拼错! + window.on = true; + window.off = false; core.dom.versionLabel.innerText = core.firstData.version; core.dom.logoLabel.innerText = core.firstData.title; @@ -277,7 +281,6 @@ core.prototype._init_flags = function () { // 初始化怪物、道具等 core.material.enemys = core.enemys.getEnemys(); core.material.items = core.items.getItems(); - core.items._resetItems(); core.material.icons = core.icons.getIcons(); } diff --git a/libs/enemys.js b/libs/enemys.js index 4c82a6fa..b4478955 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -86,7 +86,7 @@ enemys.prototype.getSpecialHint = function (enemy, special) { var hints = []; for (var i = 0; i < specials.length; i++) { if (this.hasSpecial(enemy, specials[i][0])) - hints.push(this._calSpecialContent(enemy, specials[i][1]) + ":" + this._calSpecialContent(enemy, specials[i][2])); + hints.push("\r[#FF6A6A]\\d"+this._calSpecialContent(enemy, specials[i][1]) + ":\\d\r[]" + this._calSpecialContent(enemy, specials[i][2])); } return hints; } @@ -94,7 +94,7 @@ enemys.prototype.getSpecialHint = function (enemy, special) { if (specials == null) return ""; for (var i = 0; i < specials.length; i++) { if (special == specials[i][0]) - return this._calSpecialContent(enemy, specials[i][1]) + ":" + this._calSpecialContent(enemy, specials[i][2]); + return "\r[#FF6A6A]\\d"+this._calSpecialContent(enemy, specials[i][1]) + ":\\d\r[]" + this._calSpecialContent(enemy, specials[i][2]); } return ""; } diff --git a/libs/events.js b/libs/events.js index 7462c7da..ea1a1316 100644 --- a/libs/events.js +++ b/libs/events.js @@ -317,7 +317,7 @@ events.prototype._trigger = function (x, y) { eval(block.event.script); } catch (e) { main.log(e); } - if (block.event.trigger) { + if (block.event.trigger && block.event.trigger != 'null') { var noPass = block.event.noPass, trigger = block.event.trigger; if (noPass) core.clearAutomaticRouteNode(x, y); @@ -402,7 +402,16 @@ events.prototype.openDoor = function (x, y, needKey, callback) { }); return; } - this._openDoor_animate(id, x, y, callback); + if (core.status.replay.speed == 24) { + core.status.replay.animate = true; + core.removeBlock(x, y); + setTimeout(function () { + core.status.replay.animate = false; + core.events.afterOpenDoor(id, x, y, callback); + }); + } else { + this._openDoor_animate(id, x, y, callback); + } } events.prototype._openDoor_check = function (id, x, y, needKey) { @@ -477,16 +486,16 @@ events.prototype.afterOpenDoor = function (doorId, x, y, callback) { } events.prototype._sys_getItem = function (data, callback) { - this.getItem(data.event.id, 1, data.x, data.y, callback); + this.getItem(data.event.id, 1, data.x, data.y, false, callback); } ////// 获得某个物品 ////// -events.prototype.getItem = function (id, num, x, y, callback) { +events.prototype.getItem = function (id, num, x, y, isGentleClick, callback) { if (num == null) num = 1; num = num || 1; var itemCls = core.material.items[id].cls; - core.items.getItemEffect(id, num); core.removeBlock(x, y); + core.items.getItemEffect(id, num); var text = '获得 ' + core.material.items[id].name; if (num > 1) text += "x" + num; if (itemCls === 'items' && num == 1) text += core.items.getItemEffectTip(id); @@ -510,14 +519,11 @@ events.prototype.getItem = function (id, num, x, y, callback) { itemHint.push(id); } - - core.updateStatusBar(); - - this.afterGetItem(id, x, y, callback); + this.afterGetItem(id, x, y, isGentleClick, callback); } -events.prototype.afterGetItem = function (id, x, y, callback) { - this.eventdata.afterGetItem(id, x, y, callback); +events.prototype.afterGetItem = function (id, x, y, isGentleClick, callback) { + this.eventdata.afterGetItem(id, x, y, isGentleClick, callback); } ////// 获得面前的物品(轻按) ////// @@ -545,7 +551,7 @@ events.prototype._getNextItem = function (direction, noRoute) { var nx = core.getHeroLoc('x') + core.utils.scan[direction].x; var ny = core.getHeroLoc('y') + core.utils.scan[direction].y; if (!noRoute) core.status.route.push("getNext"); - this.getItem(core.getBlockId(nx, ny), 1, nx, ny); + this.getItem(core.getBlockId(nx, ny), 1, nx, ny, true); return true; } @@ -593,6 +599,8 @@ events.prototype._changeFloor_getInfo = function (floorId, stair, heroLoc, time) var index = core.floorIds.indexOf(core.status.floorId); if (index < core.floorIds.length - 1) floorId = core.floorIds[index + 1]; else floorId = core.status.floorId; + } else if (floorId == ':now') { + floorId = core.status.floorId; } if (!core.status.maps[floorId]) { main.log("不存在的楼层:" + floorId); @@ -702,33 +710,6 @@ events.prototype.visitFloor = function (floorId) { core.getFlag("__visited__")[floorId] = true; } -events.prototype._sys_passNet = function (data, callback) { - this.passNet(data); - if (callback) callback(); -} - -////// 经过一个路障 ////// -events.prototype.passNet = function (data) { - if (!core.hasItem('shoes')) { - // 血网 lavaNet 移动到 checkBlock 中处理 - if (data.event.id == 'poisonNet') { // 毒网 - core.insertAction({"type":"insert","name":"毒衰咒处理","args":[0]}); - } - else if (data.event.id == 'weakNet') { // 衰网 - core.insertAction({"type":"insert","name":"毒衰咒处理","args":[1]}); - } - else if (data.event.id == 'curseNet') { // 咒网 - core.insertAction({"type":"insert","name":"毒衰咒处理","args":[2]}); - } - } - this.afterPassNet(data.x, data.y, data.event.id); - core.updateStatusBar(); -} - -events.prototype.afterPassNet = function (x, y, id) { - if (this.eventdata.afterPassNet) this.eventdata.afterPassNet(x, y, id); -} - events.prototype._sys_pushBox = function (data, callback) { this.pushBox(data); if (callback) callback(); @@ -743,7 +724,15 @@ events.prototype.pushBox = function (data) { nx = data.x + core.utils.scan[direction].x, ny = data.y + core.utils.scan[direction].y; // 检测能否推上去 - if (!core.canMoveHero() || !core.canMoveHero(data.x, data.y, direction)) return; + if (!core.canMoveHero()) return; + var canGoDeadZone = core.flags.canGoDeadZone; + core.flags.canGoDeadZone = false; + if (!core.canMoveHero(data.x, data.y, direction)) { + core.flags.canGoDeadZone = canGoDeadZone; + return; + } + core.flags.canGoDeadZone = canGoDeadZone; + var nextId = core.getBlockId(nx, ny); if (nextId != null && nextId != 'flower') return; @@ -753,8 +742,6 @@ events.prototype.pushBox = function (data) { core.removeBlock(data.x, data.y); else core.setBlock(168, data.x, data.y); - - core.updateStatusBar(); this._pushBox_moveHero(direction); } @@ -780,27 +767,22 @@ events.prototype.afterPushBox = function () { return this.eventdata.afterPushBox(); } -events.prototype._sys_changeLight = function (data, callback) { - core.events.changeLight(data.event.id, data.x, data.y); - if (callback) callback(); -} - -////// 改变亮灯(感叹号)的事件 ////// -events.prototype.changeLight = function (id, x, y) { - if (id != null && id != 'light') return; - core.setBlock(core.getNumberById('darkLight'), x, y); - return this.eventdata.afterChangeLight(x, y); -} - events.prototype._sys_ski = function (data, callback) { core.insertAction(["V2.6后,请将滑冰放在背景层!"], data.x, data.y, callback); } +/// 当前是否在冰上 +events.prototype.onSki = function (number) { + if (number == null) number = core.getBgNumber(); + var block = core.getBlockByNumber(number); + return block && block.event && block.event.trigger == 'ski'; +} + events.prototype._sys_action = function (data, callback) { var ev = core.clone(data.event.data), ex = data.x, ey = data.y; // 检查是否需要改变朝向 if (ex == core.nextX() && ey == core.nextY()) { - var dir = core.reverseDirection(); + var dir = core.turnDirection(":back"); var id = data.event.id, toId = (data.event.faceIds || {})[dir]; if (toId && id != toId) { var number = core.getNumberById(toId); @@ -1269,14 +1251,12 @@ events.prototype._action_show = function (data, x, y, prefix) { events.prototype._action_hide = function (data, x, y, prefix) { data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); if (data.time > 0 && data.floorId == core.status.floorId) { - data.loc.forEach(function (t) { - core.hideBlock(t[0], t[1], data.floorId); - }); - this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, 'hide', data.time); + this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, data.remove ? 'remove' : 'hide', data.time); } else { data.loc.forEach(function (t) { - core.removeBlock(t[0], t[1], data.floorId) + if (data.remove) core.removeBlock(t[0], t[1], data.floorId); + else core.hideBlock(t[0], t[1], data.floorId); }); core.doAction(); } @@ -1290,6 +1270,14 @@ events.prototype._action_setBlock = function (data, x, y, prefix) { core.doAction(); } +events.prototype._action_turnBlock = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.loc.forEach(function (t) { + core.turnBlock(data.number, t[0], t[1], data.floorId); + }); + core.doAction(); +} + events.prototype._action_showFloorImg = function (data, x, y, prefix) { core.maps.showFloorImage(this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); } @@ -1383,7 +1371,7 @@ events.prototype._action_changeFloor = function (data, x, y, prefix) { events.prototype._action_changePos = function (data, x, y, prefix) { core.clearMap('hero'); if (data.x == null && data.y == null && data.direction) { - core.setHeroLoc('direction', data.direction, true); + core.setHeroLoc('direction', core.turnDirection(data.direction), true); core.drawHero(); return core.doAction(); } @@ -1391,7 +1379,7 @@ events.prototype._action_changePos = function (data, x, y, prefix) { var loc = this.__action_getHeroLoc(data.loc, prefix); core.setHeroLoc('x', loc[0]); core.setHeroLoc('y', loc[1]); - if (data.direction) core.setHeroLoc('direction', data.direction); + if (data.direction) core.setHeroLoc('direction', core.turnDirection(data.direction)); core.drawHero(); core.doAction(); } @@ -1502,16 +1490,13 @@ events.prototype._action_unloadEquip = function (data, x, y, prefix) { } events.prototype._action_openShop = function (data, x, y, prefix) { - core.status.shops[data.id].visited = true; - this.setEvents([]); - if (!core.isReplaying()) - this.openShop(data.id); - if (core.status.event.id == 'action') - core.doAction(); + core.setShopVisited(data.id, true); + if (data.open) core.openShop(data.id, true); + core.doAction(); } events.prototype._action_disableShop = function (data, x, y, prefix) { - this.disableQuickShop(data.id); + core.setShopVisited(data.id, false); core.doAction(); } @@ -1820,9 +1805,9 @@ events.prototype._precompile_confirm = function (data) { } events.prototype._action_for = function (data, x, y, prefix) { - // Only support switch:A - if (!/^switch:[A-Z]$/.test(data.name)) { - core.insertAction(['循环遍历事件只支持独立开关!']); + // Only support temp:A + if (!/^temp:[A-Z]$/.test(data.name)) { + core.insertAction('循环遍历事件只支持临时变量!'); return core.doAction(); } var from = core.calValue(data.from); @@ -1830,22 +1815,22 @@ events.prototype._action_for = function (data, x, y, prefix) { core.insertAction('循环遍历事件要求【起始点】仅能是数字!'); return core.doAction(); } - this._setValue_setSwitch(data.name, from, prefix); - var toName = '__for@to@' + prefix + '@' + data.name.substring(7) + '__'; - var stepName = '__for@step@' + prefix + '@' + data.name.substring(7) + '__'; + var letter = data.name.substring(5); + core.setFlag('@temp@' + letter, from); + var toName = '@temp@for-to@' + letter; + var stepName = '@temp@for-step@' + letter; core.setFlag(toName, data.to); core.setFlag(stepName, data.step); var condition = "(function () {"+ - "var clearAndReturn = function (v) { if (!v) { core.removeFlag('"+toName+"'); core.removeFlag('"+stepName+"'); } return v; };"+ "var to = core.calValue(core.getFlag('" + toName + "'));"+ "var step = core.calValue(core.getFlag('" + stepName + "'));"+ - "if (typeof step != 'number' || typeof to != 'number') return clearAndReturn(false);"+ - "if (step == 0) return clearAndReturn(true);"+ - "var currentValue = core.calValue('switch:'+'" + data.name.substring(7) + "', '"+prefix+"');"+ + "if (typeof step != 'number' || typeof to != 'number') return false;"+ + "if (step == 0) return true;"+ + "var currentValue = core.getFlag('@temp@" + letter + "');"+ "currentValue += step;"+ - "core.events._setValue_setSwitch('switch:'+'" + data.name.substring(7) + "', currentValue, '"+prefix+"');"+ - "if (step > 0) { return clearAndReturn(currentValue <= to); }"+ - "else { return clearAndReturn(currentValue >= to); }"+ + "core.setFlag('@temp@" + letter + "', currentValue);"+ + "if (step > 0) { return currentValue <= to; }"+ + "else { return currentValue >= to; }"+ "})()"; return this._action_dowhile({"condition": condition, "data": data.data}, x, y, prefix); } @@ -1859,17 +1844,17 @@ events.prototype._precompile_for = function (data) { } events.prototype._action_forEach = function (data, x, y, prefix) { - // Only support switch:A - if (!/^switch:[A-Z]$/.test(data.name)) { - core.insertAction(['循环遍历事件只支持独立开关!']); + // Only support temp:A + if (!/^temp:[A-Z]$/.test(data.name)) { + core.insertAction(['循环遍历事件只支持临时变量!']); return core.doAction(); } - var listName = '__forEach@' + prefix + '@' + data.name.substring(7) + '__'; + var listName = '@temp@forEach@' + data.name.substring(5); core.setFlag(listName, core.clone(data.list)); var condition = "(function () {" + "var list = core.getFlag('"+listName+"', []);"+ - "if (list.length == 0) { core.removeFlag('" + listName + "'); return false; }"+ - "core.events._setValue_setSwitch('switch:'+'" + data.name.substring(7) + "', list.shift(), '"+prefix+"');"+ + "if (list.length == 0) return false;"+ + "core.setFlag('@temp@'+'" + data.name.substring(5) + "', list.shift());"+ "return true;"+ "})()"; return this._action_while({"condition": condition, "data": data.data}, x, y, prefix); @@ -1949,7 +1934,7 @@ events.prototype._action_function = function (data, x, y, prefix) { } events.prototype._action_update = function (data, x, y, prefix) { - core.updateStatusBar(); + core.updateStatusBar(data.doNotCheckAutoEvents); core.doAction(); } @@ -2086,13 +2071,6 @@ events.prototype._action_waitAsync = function (data, x, y, prefix) { }, 50); } -events.prototype._action_revisit = function (data, x, y, prefix) { - var block = core.getBlock(x, y); - if (block != null && block.block.event.trigger == 'action') - this.setEvents(block.block.event.data); - core.doAction(); -} - events.prototype._action_callBook = function (data, x, y, prefix) { if (core.isReplaying() || !core.hasItem('book')) { core.doAction(); @@ -2296,14 +2274,14 @@ events.prototype.useFly = function (fromUserAction) { if (core.isReplaying()) return; if (!this._checkStatus('fly', fromUserAction, true)) return; if (core.flags.flyNearStair && !core.nearStair()) { - core.drawTip("只有在楼梯边才能使用传送器"); + core.drawTip("只有在楼梯边才能使用" + core.material.items['fly'].name); core.unLockControl(); core.status.event.data = null; core.status.event.id = null; return; } if (!core.canUseItem('fly')) { - core.drawTip("楼层传送器好像失效了"); + core.drawTip(core.material.items['fly'].name + "好像失效了"); core.unLockControl(); core.status.event.data = null; core.status.event.id = null; @@ -2343,8 +2321,17 @@ events.prototype.openQuickShop = function (fromUserAction) { // --- 如果只有一个商店,则直接打开之 if (Object.keys(core.status.shops).length == 1) { var shopId = Object.keys(core.status.shops)[0]; - if (core.status.event.id != null || !this._checkStatus('shop', false)) return; - core.events.openShop(shopId, true); + if (core.status.event.id != null) return; + if (!core.canOpenShop(shopId)) { + core.drawTip("当前无法打开快捷商店!"); + return; + } + var message = core.canUseQuickShop(shopId); + if (message != null) { + core.drawTip(message); + return; + } + core.openShop(shopId, false); return; } @@ -2452,6 +2439,7 @@ events.prototype.setValue = function (name, operator, value, prefix) { this._setValue_setItem(name, value); this._setValue_setFlag(name, value); this._setValue_setSwitch(name, value, prefix); + this._setValue_setTemp(name, value, prefix); this._setValue_setGlobal(name, value); } @@ -2477,6 +2465,11 @@ events.prototype._setValue_setSwitch = function (name, value, prefix) { core.setFlag((prefix || ":f@x@y") + "@" + name.substring(7), value); } +events.prototype._setValue_setTemp = function (name, value) { + if (name.indexOf("temp:") !== 0) return; + core.setFlag("@temp@" + name.substring(5), value); +} + events.prototype._setValue_setGlobal = function (name, value) { if (name.indexOf("global:") !== 0) return; core.setGlobal(name.substring(7), value); @@ -2487,16 +2480,6 @@ events.prototype.addValue = function (name, value, prefix) { this.setValue(name, '+=', value, prefix); } -////// 执行一个表达式的effect操作 ////// -events.prototype.doEffect = function (effect, need, times) { - effect.split(";").forEach(function (expression) { - var arr = expression.split("+="); - if (arr.length != 2) return; - var name=arr[0], value=core.calValue(arr[1], null, need, times); - core.addValue(name, value); - }); -} - ////// 设置一个怪物属性 ////// events.prototype.setEnemy = function (id, name, value, prefix) { if (!core.hasFlag('enemyInfo')) { @@ -2864,87 +2847,6 @@ events.prototype._jumpHero_finished = function (animate, ex, ey, callback) { if (callback) callback(); } -////// 打开一个全局商店 ////// -events.prototype.openShop = function (shopId, needVisited) { - var shop = core.status.shops[shopId]; - shop.times = shop.times || 0; - if (shop.commonTimes) shop.times = core.getFlag('commonTimes', 0); - var reason = core.events.canUseQuickShop(shop.id); - if (reason != null) return core.drawTip(reason); - - if (needVisited && !shop.visited) { - if (!core.flags.enableDisabledShop || shop.commonEvent || shop.item) { - if (shop.times == 0) core.drawTip("该项尚未开启"); - else core.drawTip("该项已失效"); - core.ui.closePanel(); - return; - } - else { - core.drawTip("该商店尚未开启,只能浏览不可使用"); - } - } - else shop.visited = true; - - if (shop.item) { - core.status.route.push("shop:" + shopId + ":0"); - if (core.openItemShop) { - core.openItemShop(shopId); - } else { - core.insertAction("道具商店插件不存在!请检查是否存在该插件!"); - } - return; - } else if (shop.commonEvent) { - core.status.route.push("shop:"+shopId+":0"); - core.insertAction({"type": "insert", "name": shop.commonEvent, "args": shop.args}); - return; - } - core.ui.drawShop(shopId); -} - -events.prototype._useShop = function (shop, index) { - if (!shop.visited) { - core.drawTip(shop.times ? "该商店已失效" : "该商店尚未开启"); - return false; - } - var use = shop.use, choice = shop.choices[index]; - var times = shop.times, need = core.calValue(choice.need || shop.need, null, null, times); - if (need > core.getStatus(use)) { - core.drawTip("你的" + (use == 'money' ? "金币" : "经验") + "不足"); - return false; - } - core.status.event.selection = index; - core.status.event.data.actions.push(index); - core.setStatus(use, core.getStatus(use) - need); - core.doEffect(choice.effect, need, times); - core.updateStatusBar(); - shop.times++; - if (shop.commonTimes) core.setFlag('commonTimes', shop.times); - this.openShop(shop.id); - return true; -} - -events.prototype._exitShop = function () { - if (core.status.event.data.actions.length > 0) { - core.status.route.push("shop:" + core.status.event.data.id + ":" + core.status.event.data.actions.join("")); - } - core.status.event.data.actions = []; - core.status.boxAnimateObjs = []; - if (core.status.event.data.fromList) - core.ui.drawQuickShop(); - else - core.ui.closePanel(); -} - -////// 禁用一个全局商店 ////// -events.prototype.disableQuickShop = function (shopId) { - core.status.shops[shopId].visited = false; -} - -////// 能否使用快捷商店 ////// -events.prototype.canUseQuickShop = function (shopId) { - return this.eventdata.canUseQuickShop(shopId); -} - ////// 设置角色行走图 ////// events.prototype.setHeroIcon = function (name, noDraw) { name = core.getMappedName(name); diff --git a/libs/icons.js b/libs/icons.js index 45051561..a21780f3 100644 --- a/libs/icons.js +++ b/libs/icons.js @@ -27,6 +27,15 @@ icons.prototype.getClsFromId = function (id) { return null; } +icons.prototype.getAllIconIds = function () { + if (this.allIconIds) return this.allIconIds; + this.allIconIds = []; + for (var type in this.icons) { + this.allIconIds = this.allIconIds.concat(Object.keys(this.icons[type])); + } + return this.allIconIds; +} + icons.prototype._getAnimateFrames = function (cls, useOriginValue) { if (cls == 'enemys' || cls == 'npcs') { return 2; diff --git a/libs/items.js b/libs/items.js index 46121db2..41c9d61c 100644 --- a/libs/items.js +++ b/libs/items.js @@ -26,35 +26,6 @@ items.prototype.getItems = function () { return core.clone(this.items); } -items.prototype._resetItems = function () { - // 只有运行时才能执行此函数! - if (main.mode != 'play') return; - - // 根据flag来对道具进行修改 - if (core.flags.bigKeyIsBox) { - core.material.items.bigKey.cls = 'items'; - core.material.items.bigKey.name = '钥匙盒'; - } - if (core.flags.pickaxeFourDirections) - core.material.items.pickaxe.text = "可以破坏勇士四周的墙"; - if (core.flags.bombFourDirections) - core.material.items.bomb.text = "可以炸掉勇士四周的怪物"; - if (core.flags.snowFourDirections) - core.material.items.snow.text = "可以将四周的熔岩变成平地"; - if (core.flags.equipment) { - core.material.items.sword1.cls = 'equips'; - core.material.items.sword2.cls = 'equips'; - core.material.items.sword3.cls = 'equips'; - core.material.items.sword4.cls = 'equips'; - core.material.items.sword5.cls = 'equips'; - core.material.items.shield1.cls = 'equips'; - core.material.items.shield2.cls = 'equips'; - core.material.items.shield3.cls = 'equips'; - core.material.items.shield4.cls = 'equips'; - core.material.items.shield5.cls = 'equips'; - } -} - ////// “即捡即用类”道具的使用效果 ////// items.prototype.getItemEffect = function (itemId, itemNum) { var itemCls = core.material.items[itemId].cls; @@ -81,6 +52,7 @@ items.prototype.getItemEffect = function (itemId, itemNum) { main.log(e); } } + core.updateStatusBar(); } else { core.addItem(itemId, itemNum); diff --git a/libs/maps.js b/libs/maps.js index 2d095e38..72debb41 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -516,7 +516,7 @@ maps.prototype._canMoveHero_checkCannotInOut = function (number, name, direction } return false; } - if (name == 'cannotIn') direction = core.reverseDirection(direction); + if (name == 'cannotIn') direction = core.turnDirection(":back", direction); return core.inArray((this.getBlockByNumber(number).event || {})[name], direction); } @@ -596,7 +596,7 @@ maps.prototype._canMoveDirectly_bfs = function (sx, sy, locs, number, ans) { if (!core.inArray(canMoveArray[x][y], direction)) continue; var nx = x + core.utils.scan[direction].x, ny = y + core.utils.scan[direction].y, nindex = nx + "," + ny; if (visited[nindex]) continue; - if (bgMap[ny][nx] == 167) continue; + if (core.onSki(bgMap[ny][nx])) continue; if (!this._canMoveDirectly_checkNextPoint(blocksObj, nx, ny)) continue; visited[nindex] = visited[now] + 1; // if (nx == ex && ny == ey) return visited[nindex]; @@ -1464,19 +1464,35 @@ maps.prototype.hideBlock = function (x, y, floorId) { core.clearMap('event2', x * 32, y * 32 + 32 - height, 32, height - 32); } - block.block.disable = true; + core.hideBlockByIndex(block.index, floorId); core.updateStatusBar(); } -////// 将某个块从启用变成禁用状态 ////// -maps.prototype.removeBlock = function (x, y, floorId) { +////// 根据图块的索引来隐藏图块 ////// +maps.prototype.hideBlockByIndex = function (index, floorId) { floorId = floorId || core.status.floorId; if (!floorId) return; - var block = core.getBlock(x, y, floorId, true); - if (block == null) return; // 不存在 + var blocks = core.status.maps[floorId].blocks, block = blocks[index]; + block.disable = true; +} - var index = block.index; +////// 一次性隐藏多个block ////// +maps.prototype.hideBlockByIndexes = function (indexes, floorId) { + indexes.sort(function (a, b) { + return b - a; + }).forEach(function (index) { + core.hideBlockByIndex(index, floorId); + }); +} + +////// 删除某个图块 ////// +maps.prototype.removeBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return false; + + var block = core.getBlock(x, y, floorId, true); + if (block == null) return false; // 不存在 // 删除动画,清除地图 if (floorId == core.status.floorId) { @@ -1487,9 +1503,9 @@ maps.prototype.removeBlock = function (x, y, floorId) { core.clearMap('event2', x * 32, y * 32 + 32 - height, 32, height - 32); } - // 删除Index - core.removeBlockByIndex(index, floorId); + core.removeBlockByIndex(block.index, floorId); core.updateStatusBar(); + return true; } ////// 根据block的索引(尽可能)删除该块 ////// @@ -1498,13 +1514,7 @@ maps.prototype.removeBlockByIndex = function (index, floorId) { if (!floorId) return; var blocks = core.status.maps[floorId].blocks, block = blocks[index]; - - if (this.canRemoveBlock(block, floorId)) { // 能否彻底删除该图块 - blocks.splice(index, 1); - } - else { - block.disable = true; - } + blocks.splice(index, 1); } ////// 一次性删除多个block ////// @@ -1516,19 +1526,6 @@ maps.prototype.removeBlockByIndexes = function (indexes, floorId) { }); } -////// 能否彻底从地图中删除一个图块 ////// -maps.prototype.canRemoveBlock = function (block, floorId) { - var x = block.x, y = block.y; - // 检查该点是否存在事件 - if (core.floors[floorId].events[x + "," + y] || core.floors[floorId].changeFloor[x + "," + y]) - return false; - // 检查是否存在重生 - if (block.event && block.event.cls.indexOf('enemy') == 0 && core.hasSpecial(block.event.id, 23)) - return false; - - return true; -} - ////// 显示前景/背景地图 ////// maps.prototype.showBgFgMap = function (name, loc, floorId, callback) { this._triggerBgFgMap('show', name, loc, floorId, callback); @@ -1611,7 +1608,7 @@ maps.prototype.setBlock = function (number, x, y, floorId) { var block = this.initBlock(x, y, number, true, core.floors[floorId]); if (block.id == 0 && !block.event.trigger) { - // 转变图块为0且该点无事件,视为隐藏 + // 转变图块为0且该点无事件,视为删除 core.removeBlock(x, y, floorId); return; } @@ -1641,6 +1638,26 @@ maps.prototype.setBlock = function (number, x, y, floorId) { } } +////// 事件转向 ////// +maps.prototype.turnBlock = function (direction, x, y, floorId) { + var id = core.getBlockId(x, y, floorId, true); + var blockInfo = core.getBlockInfo(id); + if (blockInfo == null) return; + var faceIds = blockInfo.faceIds || {}; + var currDirection = null; + for (var dir in core.utils.scan) { + if (faceIds[dir] == id) { + currDirection = dir; + } + } + if (currDirection == null) return; + var nextDirection = core.turnDirection(direction, currDirection); + var nextId = faceIds[nextDirection]; + if (nextId != null && nextId != id) { + this.setBlock(nextId, x, y, floorId); + } +} + ////// 将地图中所有某个图块替换成另一个图块 ////// maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) { floorId = floorId || core.status.floorId; @@ -1778,6 +1795,7 @@ maps.prototype._getAndRemoveBlock = function (x, y) { ////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// maps.prototype.moveBlock = function (x, y, steps, time, keep, callback) { + if (core.status.replay.speed == 24) time = 1; time = time || 500; var blockArr = this._getAndRemoveBlock(x, y); if (blockArr == null) { @@ -1939,24 +1957,24 @@ maps.prototype._moveJumpBlock_finished = function (blockInfo, canvases, info, an ////// 显示/隐藏某个块时的动画效果 ////// maps.prototype.animateBlock = function (loc, type, time, callback) { - var isHide = type == 'hide'; + if (core.status.replay.speed == 24) time = 1; if (typeof loc[0] == 'number' && typeof loc[1] == 'number') loc = [loc]; // --- 检测所有是0的点 - var list = this._animateBlock_getList(loc); + var list = this._animateBlock_getList(loc, type); if (list.length == 0) { if (callback) callback(); return; } - this._animateBlock_drawList(list, isHide ? 1 : 0); + this._animateBlock_drawList(list, type != 'show' ? 1 : 0); time /= Math.max(core.status.replay.speed, 1) - this._animateBlock_doAnimate(loc, list, isHide, 10 / time, callback); + this._animateBlock_doAnimate(loc, list, type, 10 / time, callback); } -maps.prototype._animateBlock_doAnimate = function (loc, list, isHide, delta, callback) { - var opacity = isHide ? 1 : 0; +maps.prototype._animateBlock_doAnimate = function (loc, list, type, delta, callback) { + var opacity = type != 'show' ? 1 : 0; var animate = setInterval(function () { - opacity += isHide ? -delta : delta; + opacity += type != 'show' ? -delta : delta; core.maps._animateBlock_drawList(list, opacity); if (opacity >= 1 || opacity <= 0) { delete core.animateFrame.asyncId[animate]; @@ -1966,8 +1984,9 @@ maps.prototype._animateBlock_doAnimate = function (loc, list, isHide, delta, cal core.maps._deleteDetachedBlock(t.canvases); }); loc.forEach(function (t) { - if (isHide) core.removeBlock(t[0], t[1]); - else core.showBlock(t[0], t[1]); + if (type == 'show') core.showBlock(t[0], t[1]); + else if (type == 'hide') core.hideBlock(t[0], t[1]); + else if (type == 'remove') core.removeBlock(t[0], t[1]); }); if (callback) callback(); } @@ -1976,7 +1995,7 @@ maps.prototype._animateBlock_doAnimate = function (loc, list, isHide, delta, cal core.animateFrame.asyncId[animate] = true; } -maps.prototype._animateBlock_getList = function (loc) { +maps.prototype._animateBlock_getList = function (loc, type) { var list = []; loc.forEach(function (t) { var block = core.getBlock(t[0], t[1], null, true); @@ -1988,6 +2007,16 @@ maps.prototype._animateBlock_getList = function (loc) { list.push({ 'x': t[0], 'y': t[1] }); return; } + // 该点是否已经被启用/删除 + if ((type == 'show' && !block.disable) || ((type == 'hide' || type == 'remove') && block.disable)) { + list.push({ 'x': t[0], 'y': t[1] }); + return; + } + + if (type == 'hide' || type == 'remove') { + core.hideBlock(t[0], t[1]); // 暂时先隐藏 + } + var canvases = core.maps._initDetachedBlock(blockInfo, t[0], t[1], block.event.displayDamage !== false); list.push({ diff --git a/libs/ui.js b/libs/ui.js index 965d4b80..8050ec13 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -360,23 +360,56 @@ ui.prototype.splitLines = function (name, text, maxWidth, font) { if (!ctx) return [text]; if (font) core.setFont(name, font); + // 不能在行首的标点 + var forbidStart = "))】》>﹞>)]»›〕〉}]」}〗』" + ",。?!:;·…,.?!:;、……~&@#~&@#"; + // 不能在行尾的标点 + var forbidEnd = "((【《<﹝<([«‹〔〈{[「{〖『"; + var contents = []; var last = 0; + var forceChangeLine = false; // 是否强制换行,避免多个连续forbidStart存在 for (var i = 0; i < text.length; i++) { if (text.charAt(i) == '\n') { contents.push(text.substring(last, i)); last = i + 1; + forceChangeLine = false; } else if (text.charAt(i) == '\\' && text.charAt(i + 1) == 'n') { contents.push(text.substring(last, i)); last = i + 2; + forceChangeLine = false; } else { + var curr = text.charAt(i); var toAdd = text.substring(last, i + 1); var width = core.calWidth(name, toAdd); if (maxWidth && width > maxWidth) { + // --- 当前应当换行,然而还是检查一下是否是forbidStart + if (!forceChangeLine && forbidStart.indexOf(curr) >= 0) { + forceChangeLine = true; + continue; + } contents.push(text.substring(last, i)); last = i; + forceChangeLine = false; + } else { + // --- 当前不应该换行;但是提前检查一下是否是行尾标点 + var curr = text.charAt(i); + if (forbidEnd.indexOf(curr) >= 0 && i < text.length -1) { + // 检查是否是行尾 + var nextcurr = text.charAt(i+1); + // 确认不是手动换行 + if (nextcurr != '\n' && !(nextcurr == '\\' && text.charAt(i+2) == 'n')) { + var toAdd = text.substring(last, i+2); + var width = core.calWidth(name, toAdd); + if (maxWidth && width > maxWidth) { + // 下一项会换行,因此在此处换行 + contents.push(text.substring(last, i)); + last = i; + forceChangeLine = false; + } + } + } } } } @@ -443,6 +476,14 @@ ui.prototype._uievent_drawIcon = function (data) { ////// 结束一切事件和绘制,关闭UI窗口,返回游戏进程 ////// ui.prototype.closePanel = function () { + if (core.status.hero && core.status.hero.flags) { + // 清除全部临时变量 + Object.keys(core.status.hero.flags).forEach(function (name) { + if (name.startsWith("@temp@")) { + delete core.status.hero.flags[name]; + } + }); + } this.clearUI(); core.maps.generateGroundPattern(); core.updateStatusBar(true); @@ -652,6 +693,14 @@ ui.prototype._uievent_drawSelector = function (data) { this._drawSelector(ctx, background, w, h); } +ui.prototype._clearUIEventSelector = function (codes) { + if (codes instanceof Array) { + codes.forEach(function (code) { core.ui._clearUIEventSelector(code); }); + return; + } + core.deleteCanvas('_uievent_selector_' + (codes || 0)); +} + ui.prototype._drawSelector = function (ctx, background, w, h, left, top) { left = left || 0; top = top || 0; @@ -1546,11 +1595,9 @@ ui.prototype.drawSettings = function () { ////// 绘制快捷商店选择栏 ////// ui.prototype.drawQuickShop = function () { core.status.event.id = 'selectShop'; - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { - return shopList[shopId].visited || !shopList[shopId].mustEnable - }); + var shopList = core.status.shops, keys = core.listShopIds(); var choices = keys.map(function (shopId) { - return {"text": shopList[shopId].textInList, "color": shopList[shopId].visited?null:"#999999"}; + return {"text": shopList[shopId].textInList, "color": core.isShopVisited(shopId) ? null : "#999999"}; }); choices.push("返回游戏"); this.drawChoices(null, choices); @@ -1828,8 +1875,8 @@ ui.prototype.drawBookDetail = function (index) { var left = 10, width = this.PIXEL - 2 * left, right = left + width; var content_left = left + 25, validWidth = right - content_left - 13; - var contents = core.splitLines("data", content, validWidth, this._buildFont(16, false)); - var height = Math.max(24 * contents.length + 55, 80), top = (this.PIXEL - height) / 2, bottom = top + height; + var height = Math.max(this.getTextContentHeight(content, {fontSize: 16, lineHeight: 24, maxWidth: validWidth}) + 58, 80), + top = (this.PIXEL - height) / 2, bottom = top + height; core.setAlpha('data', 0.9); core.fillRect('data', left, top, width, height, '#000000'); @@ -1837,7 +1884,7 @@ ui.prototype.drawBookDetail = function (index) { core.strokeRect('data', left - 1, top - 1, width + 1, height + 1, core.status.globalAttribute.borderColor, 2); - this._drawBookDetail_drawContent(enemy, contents, {top: top, content_left: content_left, bottom: bottom}); + this._drawBookDetail_drawContent(enemy, content, {top: top, content_left: content_left, bottom: bottom, validWidth: validWidth}); } ui.prototype._drawBookDetail_getInfo = function (index) { @@ -1870,7 +1917,7 @@ ui.prototype._drawBookDetail_mofang = function (enemy, texts) { var hp = enemy.hp; var delta = core.status.hero.atk - core.status.hero.def; if (delta0) { - texts.push("模仿临界计算器:(当前攻防差"+core.formatBigNumber(delta)+")"); + texts.push("\r[#FF6A6A]\\d模仿临界计算器:\\d\r[](当前攻防差"+core.formatBigNumber(delta)+")"); var u = []; this._drawBookDetail_mofang_getArray(hp).forEach(function (t) { if (u.length < 20) u.push(t); @@ -1921,7 +1968,7 @@ ui.prototype._drawBookDetail_vampire = function (enemy, texts) { } core.status.hero.hp = start; if (core.canBattle(enemy.id)) { - texts.push("打死该怪物最低需要生命值:"+core.formatBigNumber(start)); + texts.push("\r[#FF6A6A]\\d打死该怪物最低需要生命值:\\d\r[]"+core.formatBigNumber(start)); } core.status.hero.hp = nowHp; } @@ -1930,19 +1977,19 @@ ui.prototype._drawBookDetail_vampire = function (enemy, texts) { ui.prototype._drawBookDetail_hatred = function (enemy, texts) { if (core.enemys.hasSpecial(enemy.special, 17)) { - texts.push("当前仇恨伤害值:"+core.getFlag('hatred', 0)); + texts.push("\r[#FF6A6A]\\d当前仇恨伤害值:\\d\r[]"+core.getFlag('hatred', 0)); } } ui.prototype._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) { var damageInfo = core.getDamageInfo(enemy, null, null, null, floorId); - texts.push("战斗回合数:"+((damageInfo||{}).turn||0)); + texts.push("\r[#FF6A6A]\\d战斗回合数:\\d\r[]"+((damageInfo||{}).turn||0)); // 临界表 var criticals = core.enemys.nextCriticals(enemy, 8, null, null, floorId).map(function (v) { return core.formatBigNumber(v[0])+":"+core.formatBigNumber(v[1]); }); while (criticals[0]=='0:0') criticals.shift(); - texts.push("临界表:"+JSON.stringify(criticals)); + texts.push("\r[#FF6A6A]\\d临界表:\\d\r[]"+JSON.stringify(criticals)); var prevInfo = core.getDamageInfo(enemy, {atk: core.status.hero.atk-1}, null, null, floorId); if (prevInfo != null && damageInfo != null) { if (damageInfo.damage != null) damageInfo = damageInfo.damage; @@ -1953,26 +2000,14 @@ ui.prototype._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) } } -ui.prototype._drawBookDetail_drawContent = function (enemy, contents, pos) { +ui.prototype._drawBookDetail_drawContent = function (enemy, content, pos) { // 名称 core.setTextAlign('data', 'left'); core.fillText('data', enemy.name, pos.content_left, pos.top + 30, '#FFD700', this._buildFont(22, true)); - var content_top = pos.top + 57; + var content_top = pos.top + 44; - for (var i=0;i=0) { - var x1 = text.substring(0, index+1); - core.fillText('data', x1, pos.content_left, content_top, '#FF6A6A', this._buildFont(16, true)); - var len=core.calWidth('data', x1); - core.fillText('data', text.substring(index+1), pos.content_left+len, content_top, '#FFFFFF', this._buildFont(16, false)); - } - else { - core.fillText('data', contents[i], pos.content_left, content_top, '#FFFFFF', this._buildFont(16, false)); - } - content_top+=24; - } + this.drawTextContent('data', content, {left: pos.content_left, top: content_top, maxWidth: pos.validWidth, + fontSize: 16, lineHeight: 24}); } ////// 绘制楼层传送器 ////// @@ -2028,37 +2063,10 @@ ui.prototype.drawCenterFly = function () { offsetY = core.clamp(toY - core.__HALF_SIZE__, 0, core.bigmap.height - core.__SIZE__); core.fillRect('ui', (toX - offsetX) * 32, (toY - offsetY) * 32, 32, 32, fillstyle); core.status.event.data = {"x": toX, "y": toY, "posX": toX - offsetX, "posY": toY - offsetY}; - core.drawTip("请确认当前中心对称飞行器的位置"); + core.drawTip("请确认当前"+core.material.items['centerFly'].name+"的位置"); return; } -////// 绘制全局商店 -ui.prototype.drawShop = function (shopId) { - var shop = core.status.shops[shopId]; - var actions = [], fromList = (core.status.event.data||{}).fromList, selection = core.status.event.selection; - if (core.status.event.data && core.status.event.data.actions) actions=core.status.event.data.actions; - - core.ui.closePanel(); - core.lockControl(); - core.status.event.id = 'shop'; - core.status.event.data = {'id': shopId, 'shop': shop, 'actions': actions, 'fromList': fromList}; - core.status.event.selection = selection; - - var times = shop.times, need=core.calValue(shop.need, null, null, times); - var content = "\t["+shop.name+","+shop.icon+"]" + core.replaceText(shop.text, null, need, times); - var use = shop.use=='exp'?'经验':'金币'; - var choices = []; - for (var i=0;i= 0) value = value.replace(/global:([a-zA-Z0-9_\u4E00-\u9FCC]+)/g, "core.getGlobal('$1', 0)"); if (value.indexOf('enemy:')>=0) - value = value.replace(/enemy:([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)/g, "core.material.enemys['$1'].$2"); + value = value.replace(/enemy:([a-zA-Z0-9_]+)[\.:]([a-zA-Z0-9_]+)/g, "core.material.enemys['$1'].$2"); if (value.indexOf('blockId:')>=0) value = value.replace(/blockId:(\d+),(\d+)/g, "core.getBlockId($1, $2)"); if (value.indexOf('blockCls:')>=0) value = value.replace(/blockCls:(\d+),(\d+)/g, "core.getBlockCls($1, $2)"); if (value.indexOf('equip:')>=0) value = value.replace(/equip:(\d)/g, "core.getEquip($1)"); + if (value.indexOf('temp:')>=0) + value = value.replace(/temp:([a-zA-Z0-9_]+)/g, "core.getFlag('@temp@$1', 0)"); } return value; } ////// 计算表达式的值 ////// -utils.prototype.calValue = function (value, prefix, need, times) { +utils.prototype.calValue = function (value, prefix) { if (!core.isset(value)) return null; if (typeof value === 'string') { if (value.indexOf(':') >= 0) { @@ -594,7 +596,7 @@ utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { decodeObj.ans.push("choices:" + nxt); break; case "S": - decodeObj.ans.push("shop:" + nxt + ":" + this._decodeRoute_getNumber(decodeObj, true)); + decodeObj.ans.push("shop:" + nxt); break; case "T": decodeObj.ans.push("turn"); @@ -689,9 +691,19 @@ utils.prototype.strlen = function (str) { return count; }; -utils.prototype.reverseDirection = function (direction) { +utils.prototype.turnDirection = function (turn, direction) { direction = direction || core.getHeroLoc('direction'); - return {"left":"right","right":"left","down":"up","up":"down"}[direction] || direction; + var directionList = ["left", "up", "right", "down"]; + if (directionList.indexOf(turn) >= 0) return turn; + switch (turn) { + case ':left': turn = 3; break; // turn left + case ':right': turn = 1; break; // turn right + case ':back': turn = 2; break; // turn back + default: turn = 0; break; + } + var index = directionList.indexOf(direction); + if (index < 0) return direction; + return directionList[(index + (turn || 0)) % 4]; } utils.prototype.matchWildcard = function (pattern, string) { diff --git a/main.js b/main.js index 74da7a29..e6206b82 100644 --- a/main.js +++ b/main.js @@ -597,7 +597,7 @@ main.statusBar.image.keyboard.onclick = function (e) { main.core.openKeyBoard(true); } -////// 点击状态栏中的快捷商店键盘时 ////// +////// 点击状态栏中的快捷商店时 ////// main.statusBar.image.shop.onclick = function (e) { e.stopPropagation(); @@ -610,7 +610,7 @@ main.statusBar.image.shop.onclick = function (e) { main.core.openQuickShop(true); } -////// 点击金币时也可以开启虚拟键盘 ////// +////// 点击金币时也可以开启快捷商店 ////// main.statusBar.image.money.onclick = function (e) { e.stopPropagation(); diff --git a/project/data.js b/project/data.js index d048aad3..49445eab 100644 --- a/project/data.js +++ b/project/data.js @@ -299,37 +299,100 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = ], "shops": [ { - "id": "moneyShop1", - "name": "贪婪之神", - "icon": "blueShop", + "id": "shop1", + "text": "\t[贪婪之神,blueShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", "textInList": "1F金币商店", - "commonTimes": false, "mustEnable": false, - "use": "money", - "need": "20+10*times*(times+1)", - "text": "勇敢的武士啊,给我${need}金币就可以:", + "disablePreview": false, "choices": [ { "text": "生命+800", - "effect": "status:hp+=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" + } + ] + }, + { + "text": "攻击+4", + "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:atk", + "operator": "+=", + "value": "4" + } + ] } ] }, { - "id": "expShop1", - "name": "经验之神", - "icon": "pinkShop", + "id": "shop2", + "text": "\t[贪婪之神,pinkShop]勇敢的武士啊, 给我一定经验就可以:", "textInList": "1F经验商店", - "commonTimes": false, "mustEnable": false, - "use": "exp", - "need": "-1", - "text": "勇敢的武士啊,给我若干经验就可以:", + "disablePreview": true, "choices": [ { - "text": "等级+1", - "need": "100", - "effect": "status:hp+=1000" + "text": "等级+1(100经验)", + "need": "status:exp>=100", + "action": [ + { + "type": "setValue", + "name": "status:exp", + "operator": "-=", + "value": "100" + }, + { + "type": "setValue", + "name": "status:lv", + "operator": "+=", + "value": "1" + }, + { + "type": "setValue", + "name": "status:hp", + "operator": "+=", + "value": "1000" + } + ] } ] }, @@ -342,12 +405,13 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = { "id": "yellowKey", "number": 10, - "money": 10 + "money": "10", + "sell": "5" } ] }, { - "id": "keyShop1", + "id": "keyShop", "textInList": "回收钥匙商店", "mustEnable": false, "commonEvent": "回收钥匙商店" @@ -423,13 +487,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = ], "flyNearStair": true, "flyRecordPosition": false, - "pickaxeFourDirections": false, - "bombFourDirections": false, - "snowFourDirections": false, - "bigKeyIsBox": false, "steelDoorWithoutKey": false, "itemFirstText": false, - "equipment": false, "equipboxButton": false, "enableAddPoint": false, "enableNegativeDamage": false, @@ -446,7 +505,6 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "ignoreChangeFloor": true, "canGoDeadZone": false, "enableMoveDirectly": true, - "enableDisabledShop": true, "disableShopOnDamage": false, "blurFg": false } diff --git a/project/enemys.js b/project/enemys.js index 9a203006..e2024616 100644 --- a/project/enemys.js +++ b/project/enemys.js @@ -1,67 +1,67 @@ -var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = +var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = { "greenSlime": {"name":"绿头怪","hp":100,"atk":120,"def":0,"money":1,"exp":1,"point":0,"special":[1,5,7,8]}, "redSlime": {"name":"红头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[16,18],"value":10}, - "blackSlime": {"name":"青头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "blackSlime": {"name":"青头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, "slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[1,9]}, - "bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":1}, - "bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "redBat": {"name":"红蝙蝠","hp":100,"atk":120,"def":0,"money":5,"exp":0,"point":0,"special":4}, - "vampire": {"name":"冥灵魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "skeletonSoilder": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "ghostSkeleton": {"name":"冥队长","hp":100,"atk":120,"def":0,"money":8,"exp":0,"point":0,"special":7}, - "zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "rock": {"name":"石头人","hp":100,"atk":120,"def":0,"money":4,"exp":0,"point":0,"special":3}, + "bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1]}, + "bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "redBat": {"name":"红蝙蝠","hp":100,"atk":120,"def":0,"money":5,"exp":0,"point":0,"special":[4]}, + "vampire": {"name":"冥灵魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "skeletonSoilder": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "ghostSkeleton": {"name":"冥队长","hp":100,"atk":120,"def":0,"money":8,"exp":0,"point":0,"special":[7]}, + "zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "rock": {"name":"石头人","hp":100,"atk":120,"def":0,"money":4,"exp":0,"point":0,"special":[3]}, "slimeMan": {"name":"影子战士","hp":100,"atk":0,"def":0,"money":11,"exp":0,"point":0,"special":[10,21],"atkValue":2,"defValue":3}, - "bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":2}, - "redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":15,"value":100,"range":2}, - "redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":15,"value":200,"zoneSquare":true}, - "yellowGuard": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":0}, - "blueGuard": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "redGuard": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, + "bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[2]}, + "redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":[15],"value":100,"range":2}, + "redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":[15],"value":200,"zoneSquare":true}, + "yellowGuard": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[]}, + "blueGuard": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "redGuard": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, "swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"special":[5,23]}, - "soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "redKnight": {"name":"红骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"special":0,"notBomb":true}, - "yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"special":8}, - "goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "poisonSkeleton": {"name":"紫骷髅","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"special":13}, - "steelRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"special":18,"value":20}, - "skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "skeletonWizard": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "redSkeletonCaption": {"name":"骷髅武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "badHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "demon": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "whiteKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":16}, - "blackMagician": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"special":11,"value":0.3333333333333333,"add":true,"notBomb":true}, - "silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"special":14}, - "swordEmperor": {"name":"剑圣","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "whiteHornSlime": {"name":"尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "badPrincess": {"name":"痛苦魔女","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "badFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "grayPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "redSwordsman": {"name":"剑王","hp":100,"atk":120,"def":0,"money":7,"exp":0,"point":0,"special":6,"n":8}, - "whiteGhost": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"special":12}, - "magicDragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "darkFairy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0}, - "steelGuard": {"name":"铁守卫","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":18,"value":20}, + "soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "redKnight": {"name":"红骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"special":[],"notBomb":true}, + "yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"special":[8]}, + "goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "poisonSkeleton": {"name":"紫骷髅","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"special":[13]}, + "steelRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"special":[18,23],"value":20}, + "skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "skeletonWizard": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "redSkeletonCaption": {"name":"骷髅武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "badHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "demon": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "whiteKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":[16]}, + "blackMagician": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"special":[11],"value":0.3333333333333333,"add":true,"notBomb":true}, + "silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"special":[14]}, + "swordEmperor": {"name":"剑圣","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "whiteHornSlime": {"name":"尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "badPrincess": {"name":"痛苦魔女","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "badFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "grayPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "redSwordsman": {"name":"剑王","hp":100,"atk":120,"def":0,"money":7,"exp":0,"point":0,"special":[6],"n":8}, + "whiteGhost": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"special":[12]}, + "magicDragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "darkFairy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}, + "steelGuard": {"name":"铁守卫","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20}, "evilBat": {"name":"邪恶蝙蝠","hp":1000,"atk":1,"def":0,"money":0,"exp":0,"point":0,"special":[2,3]} } \ No newline at end of file diff --git a/project/events.js b/project/events.js index d98dcfda..b6aea258 100644 --- a/project/events.js +++ b/project/events.js @@ -163,7 +163,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = }, { "type": "if", - "condition": "core.getBgNumber() == 167", + "condition": "core.onSki()", "true": [ { "type": "if", @@ -244,7 +244,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "回收钥匙商店": [ { "type": "comment", - "text": "此事件在全局商店中被引用了(全局商店keyShop1)" + "text": "此事件在全局商店中被引用了(全局商店keyShop)" }, { "type": "comment", diff --git a/project/floors/MT0.js b/project/floors/MT0.js index afcc0832..8e98ca33 100644 --- a/project/floors/MT0.js +++ b/project/floors/MT0.js @@ -1,15 +1,15 @@ main.floors.MT0= { -"floorId": "MT0", -"title": "主塔 0 层", -"name": "0", -"canFlyTo": true, -"canUseQuickShop": true, -"cannotViewMap": false, -"defaultGround": "ground", -"images": [], -"item_ratio": 1, -"map": [ + "floorId": "MT0", + "title": "主塔 0 层", + "name": "0", + "canFlyTo": true, + "canUseQuickShop": true, + "cannotViewMap": false, + "defaultGround": "ground", + "images": [], + "item_ratio": 1, + "map": [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -24,18 +24,21 @@ main.floors.MT0= [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ], -"firstArrive": [], -"parallelDo": "", -"events": {}, -"changeFloor": {}, -"afterBattle": {}, -"afterGetItem": {}, -"afterOpenDoor": {}, -"cannotMove": {}, -"bgmap": [ + "firstArrive": [], + "parallelDo": "", + "events": {}, + "changeFloor": {}, + "afterBattle": {}, + "afterGetItem": {}, + "afterOpenDoor": {}, + "cannotMove": {}, + "bgmap": [ ], -"fgmap": [ + "fgmap": [ ], + "width": 13, + "height": 13, + "autoEvent": {} } \ No newline at end of file diff --git a/project/floors/sample0.js b/project/floors/sample0.js index 8ed705e3..4af03e43 100644 --- a/project/floors/sample0.js +++ b/project/floors/sample0.js @@ -1,15 +1,15 @@ main.floors.sample0= { -"floorId": "sample0", -"title": "样板 0 层", -"name": "0", -"canFlyTo": true, -"canUseQuickShop": true, -"defaultGround": "ground", -"images": [], -"bgm": "bgm.mp3", -"item_ratio": 1, -"map": [ + "floorId": "sample0", + "title": "样板 0 层", + "name": "0", + "canFlyTo": true, + "canUseQuickShop": true, + "defaultGround": "ground", + "images": [], + "bgm": "bgm.mp3", + "item_ratio": 1, + "map": [ [ 0, 0,220, 0, 0, 20, 87, 3, 65, 64, 44, 43, 42], [ 0,246, 0,246, 0, 20, 0, 3, 58, 59, 60, 61, 41], [219, 0, 0, 0,219, 20, 0, 3, 57, 26, 62, 63, 40], @@ -24,199 +24,202 @@ main.floors.sample0= [ 87, 11, 12, 13, 14, 4, 4, 2, 2, 2,122, 2, 2], [ 88, 89, 90, 91, 92, 93, 94, 2, 81, 82, 83, 84, 86] ], -"firstArrive": [ - { - "type": "setText", - "background": "winskin.png", - "time": 0 - }, - "\t[样板提示]首次到达某层可以触发 firstArrive 事件,该事件可类似于RMXP中的“自动执行脚本”。\n\n本事件支持一切的事件类型,常常用来触发对话,例如:", - "\t[hero]\b[up,hero]我是谁?我从哪来?我又要到哪去?", - "\t[仙子,fairy]你问我...?我也不知道啊...", - "本层主要对道具、门、怪物等进行介绍,有关事件的各种信息在下一层会有更为详细的说明。" -], -"events": { - "10,9": [ - "\t[老人,man]这些是本样板支持的所有的道具。\n\n道具分为四类:items, constants, tools,equips。\nitems 为即捡即用类道具,例如宝石、血瓶、剑盾等。\nconstants 为永久道具,例如怪物手册、楼层传送器、幸运金币等。\ntools 为消耗类道具,例如破墙镐、炸弹、中心对称飞行器等。\nequips 为装备,例如剑盾等。", - "\t[老人,man]\b[up]有关道具效果,定义在items.js中。\n目前大多数道具已有默认行为,如有自定义的需求则需在items.js中修改代码。", - "\t[老人,man]\b[up]拾取道具结束后可触发 afterGetItem 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。", + "firstArrive": [ { - "type": "hide", - "time": 500 - } + "type": "setText", + "background": "winskin.png", + "time": 0 + }, + "\t[样板提示]首次到达某层可以触发 firstArrive 事件,该事件可类似于RMXP中的“自动执行脚本”。\n\n本事件支持一切的事件类型,常常用来触发对话,例如:", + "\t[hero]\b[up,hero]我是谁?我从哪来?我又要到哪去?", + "\t[仙子,fairy]你问我...?我也不知道啊...", + "本层主要对道具、门、怪物等进行介绍,有关事件的各种信息在下一层会有更为详细的说明。" ], - "10,11": [ - "\t[老人,woman]这些是门,需要对应的钥匙打开。\n机关门必须使用特殊的开法。", - "\t[老人,woman]开门后可触发 afterOpenDoor 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。", - { - "type": "hide", - "time": 500 - } - ], - "2,10": [ - "\t[少女,npc0]这些是路障、楼梯、传送门。", - "\t[少女,npc0]血网的伤害数值、中毒后每步伤害数值、衰弱时攻防下降的数值,都在 data.js 内定义。\n\n路障同样会尽量被自动寻路绕过。", - "\t[少女,npc0]楼梯和传送门需要在changeFloor中定义目标楼层和位置,可参见样板里已有的的写法。", - { - "type": "hide", - "time": 500 - } - ], - "2,8": [ - "\t[老人,magician]这些都是各种各样的怪物,所有怪物的数据都在enemys.js中设置。", - "\t[老人,magician]这批怪物分别为:普通、先攻、魔攻、坚固、2连击、3连击、4连击、破甲、反击、净化。", - "\t[老人,magician]打败怪物后可触发 afterBattle 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。", - { - "type": "hide", - "time": 500 - } - ], - "2,5": [ - "\t[老人,magician]模仿、吸血、中毒、衰弱、诅咒。\n\n请注意吸血怪需要设置value为吸血数值,可参见样板中黑暗大法师的写法。", - { - "type": "hide", - "time": 500 - } - ], - "2,3": [ - "\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。", - "\t[老人,magician]夹击和领域同时发生时先计算领域,再夹击。\n自动寻路同样会尽量绕过你设置的这些点。", - { - "type": "hide", - "time": 500 - } - ], - "12,10": { - "enable": false, - "data": [ - "\t[仙子,fairy]只有楼上启用事件后,才能看到我并可以和我对话来触发事件。", + "events": { + "10,9": [ + "\t[老人,man]这些是本样板支持的所有的道具。\n\n道具分为四类:items, constants, tools,equips。\nitems 为即捡即用类道具,例如宝石、血瓶、剑盾等。\nconstants 为永久道具,例如怪物手册、楼层传送器、幸运金币等。\ntools 为消耗类道具,例如破墙镐、炸弹、中心对称飞行器等。\nequips 为装备,例如剑盾等。", + "\t[老人,man]\b[up]有关道具效果,定义在items.js中。\n目前大多数道具已有默认行为,如有自定义的需求则需在items.js中修改代码。", + "\t[老人,man]\b[up]拾取道具结束后可触发 afterGetItem 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。", { "type": "hide", "time": 500 } - ] - } -}, -"changeFloor": { - "6,0": { - "floorId": "sample1", - "stair": "downFloor" + ], + "10,11": [ + "\t[老人,woman]这些是门,需要对应的钥匙打开。\n机关门必须使用特殊的开法。", + "\t[老人,woman]开门后可触发 afterOpenDoor 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。", + { + "type": "hide", + "time": 500 + } + ], + "2,10": [ + "\t[少女,npc0]这些是路障、楼梯、传送门。", + "\t[少女,npc0]血网的伤害数值、中毒后每步伤害数值、衰弱时攻防下降的数值,都在 data.js 内定义。\n\n路障同样会尽量被自动寻路绕过。", + "\t[少女,npc0]楼梯和传送门需要在changeFloor中定义目标楼层和位置,可参见样板里已有的的写法。", + { + "type": "hide", + "time": 500 + } + ], + "2,8": [ + "\t[老人,magician]这些都是各种各样的怪物,所有怪物的数据都在enemys.js中设置。", + "\t[老人,magician]这批怪物分别为:普通、先攻、魔攻、坚固、2连击、3连击、4连击、破甲、反击、净化。", + "\t[老人,magician]打败怪物后可触发 afterBattle 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。", + { + "type": "hide", + "time": 500 + } + ], + "2,5": [ + "\t[老人,magician]模仿、吸血、中毒、衰弱、诅咒。\n\n请注意吸血怪需要设置value为吸血数值,可参见样板中黑暗大法师的写法。", + { + "type": "hide", + "time": 500 + } + ], + "2,3": [ + "\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。", + "\t[老人,magician]夹击和领域同时发生时先计算领域,再夹击。\n自动寻路同样会尽量绕过你设置的这些点。", + { + "type": "hide", + "time": 500 + } + ], + "12,10": { + "enable": false, + "data": [ + "\t[仙子,fairy]只有楼上启用事件后,才能看到我并可以和我对话来触发事件。", + { + "type": "hide", + "time": 500 + } + ] + } }, - "0,11": { - "floorId": "sample0", - "loc": [ - 0, - 12 + "changeFloor": { + "6,0": { + "floorId": "sample1", + "stair": "downFloor" + }, + "0,11": { + "floorId": "sample0", + "loc": [ + 0, + 12 + ] + }, + "0,12": { + "floorId": "sample0", + "stair": "upFloor" + }, + "1,12": { + "floorId": "sample0", + "loc": [ + 1, + 12 + ] + }, + "2,12": { + "floorId": "sample0", + "loc": [ + 2, + 12 + ] + }, + "3,12": { + "floorId": "sample0", + "loc": [ + 6, + 1 + ], + "direction": "up" + }, + "4,12": { + "floorId": "sample0", + "loc": [ + 0, + 9 + ], + "direction": "left", + "time": 1000 + }, + "5,12": { + "floorId": "sample0", + "loc": [ + 6, + 10 + ], + "time": 0, + "portalWithoutTrigger": false + }, + "6,12": { + "floorId": "sample0", + "loc": [ + 10, + 10 + ], + "direction": "left", + "time": 1000 + } + }, + "afterBattle": { + "2,6": [ + "\t[ghostSkeleton]不可能,你怎么可能打败我!\n(一个打败怪物触发的事件)" ] }, - "0,12": { - "floorId": "sample0", - "stair": "upFloor" - }, - "1,12": { - "floorId": "sample0", - "loc": [ - 1, - 12 + "afterGetItem": { + "11,8": [ + "由于状态栏放不下,绿钥匙和铁门钥匙均视为tools,放入工具栏中。\n碰到绿门和铁门仍然会自动使用开门。" + ], + "8,6": [ + "由于吸血和夹击等的存在,血瓶默认自动被绕路。\n你可以修改data.js中的系统Flag来设置这一项。" + ], + "8,7": [ + "如需修改消耗品的效果,请前往 data.js ,找到并修改values内对应的具体数值即可。\n如果有更高级的需求(如每个区域宝石数值变化),详见doc文档内的做法说明。" + ], + "9,5": [ + "每层楼的 canFlyTo 决定了该楼层能否被飞到。\n\n不能被飞到的楼层也无法使用楼层传送器。", + "飞行的楼层顺序由 main.js 中 floorIds 加载顺序所决定。\n\n是否必须在楼梯边使用楼传器由 data.js 中的系统Flag所决定。" + ], + "10,5": [ + "破墙镐是破面前的墙壁还是四个方向的墙壁,由data.js中的系统Flag所决定。" + ], + "8,4": [ + "炸弹是只能炸面前的怪物还是四个方向的怪物,由data.js中的系统Flag所决定。\n如只能炸前方怪物则和上面的圣锤等价。\n不能被炸的怪物在enemys中可以定义,可参见样板里黑衣魔王和黑暗大法师的写法。" + ], + "10,4": [ + "“上楼”和“下楼”的目标层由 main.js 的 floorIds顺序所决定。" + ], + "9,2": [ + "该道具默认是大黄门钥匙,如需改为钥匙盒直接修改 data.js 中的系统Flag即可。" + ], + "10,2": [ + "屠龙匕首目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。" + ], + "12,7": [ + "在 data.js 的系统Flag中设置是否启用装备栏。\n如果不启用则装备会直接增加属性。" + ], + "12,6": [ + "在 data.js 的系统Flag中设置是否启用装备栏按钮。\n如果启用则装备栏按钮会替代楼传按钮。" + ], + "12,5": [ + "装备的种类由全塔属性中的equipName决定,type的值就是该类型在equipName中的位次,例如默认情况下equiptype为0代表武器,同时只有type为0的装备的animate属性生效" ] }, - "2,12": { - "floorId": "sample0", - "loc": [ - 2, - 12 + "afterOpenDoor": { + "11,12": [ + "你开了一个绿门,触发了一个afterOpenDoor事件" ] }, - "3,12": { - "floorId": "sample0", - "loc": [ - 6, - 1 - ], - "direction": "up" - }, - "4,12": { - "floorId": "sample0", - "loc": [ - 0, - 9 - ], - "direction": "left", - "time": 1000 - }, - "5,12": { - "floorId": "sample0", - "loc": [ - 6, - 10 - ], - "time": 0, - "portalWithoutTrigger": false - }, - "6,12": { - "floorId": "sample0", - "loc": [ - 10, - 10 - ], - "direction": "left", - "time": 1000 - } -}, -"afterBattle": { - "2,6": [ - "\t[ghostSkeleton]不可能,你怎么可能打败我!\n(一个打败怪物触发的事件)" - ] -}, -"afterGetItem": { - "11,8": [ - "由于状态栏放不下,绿钥匙和铁门钥匙均视为tools,放入工具栏中。\n碰到绿门和铁门仍然会自动使用开门。" - ], - "8,6": [ - "由于吸血和夹击等的存在,血瓶默认自动被绕路。\n你可以修改data.js中的系统Flag来设置这一项。" - ], - "8,7": [ - "如需修改消耗品的效果,请前往 data.js ,找到并修改values内对应的具体数值即可。\n如果有更高级的需求(如每个区域宝石数值变化),详见doc文档内的做法说明。" - ], - "9,5": [ - "每层楼的 canFlyTo 决定了该楼层能否被飞到。\n\n不能被飞到的楼层也无法使用楼层传送器。", - "飞行的楼层顺序由 main.js 中 floorIds 加载顺序所决定。\n\n是否必须在楼梯边使用楼传器由 data.js 中的系统Flag所决定。" - ], - "10,5": [ - "破墙镐是破面前的墙壁还是四个方向的墙壁,由data.js中的系统Flag所决定。" - ], - "8,4": [ - "炸弹是只能炸面前的怪物还是四个方向的怪物,由data.js中的系统Flag所决定。\n如只能炸前方怪物则和上面的圣锤等价。\n不能被炸的怪物在enemys中可以定义,可参见样板里黑衣魔王和黑暗大法师的写法。" - ], - "10,4": [ - "“上楼”和“下楼”的目标层由 main.js 的 floorIds顺序所决定。" - ], - "9,2": [ - "该道具默认是大黄门钥匙,如需改为钥匙盒直接修改 data.js 中的系统Flag即可。" - ], - "10,2": [ - "屠龙匕首目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。" - ], - "12,7": [ - "在 data.js 的系统Flag中设置是否启用装备栏。\n如果不启用则装备会直接增加属性。" - ], - "12,6": [ - "在 data.js 的系统Flag中设置是否启用装备栏按钮。\n如果启用则装备栏按钮会替代楼传按钮。" - ], - "12,5": [ - "装备的种类由全塔属性中的equipName决定,type的值就是该类型在equipName中的位次,例如默认情况下equiptype为0代表武器,同时只有type为0的装备的animate属性生效" - ] -}, -"afterOpenDoor": { - "11,12": [ - "你开了一个绿门,触发了一个afterOpenDoor事件" - ] -}, -"cannotMove": {}, -"bgmap": [ + "cannotMove": {}, + "bgmap": [ ], -"fgmap": [ + "fgmap": [ ], + "width": 13, + "height": 13, + "autoEvent": {} } \ No newline at end of file diff --git a/project/floors/sample1.js b/project/floors/sample1.js index 5149304d..9c769745 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -1,25 +1,25 @@ main.floors.sample1= { -"floorId": "sample1", -"title": "样板 1 层", -"name": "1", -"canFlyTo": true, -"canUseQuickShop": true, -"defaultGround": "grass", -"images": [ - [ - 0, - 0, - "bg.jpg", - 0 - ] -], -"weather": [ - "snow", - 6 -], -"item_ratio": 1, -"map": [ + "floorId": "sample1", + "title": "样板 1 层", + "name": "1", + "canFlyTo": true, + "canUseQuickShop": true, + "defaultGround": "grass", + "images": [ + [ + 0, + 0, + "bg.jpg", + 0 + ] + ], + "weather": [ + "snow", + 6 + ], + "item_ratio": 1, + "map": [ [ 7,131, 8,152, 9,130, 10,152,166,165,132,165,166], [ 0, 0, 0, 0, 0, 0, 0,152,165,164, 0,162,165], [152,152,152,152,121,152,152,152, 0, 0,229, 0, 0], @@ -34,609 +34,617 @@ main.floors.sample1= [ 1, 0,123, 1, 0, 20,124, 0,121, 0,122, 0,126], [ 1, 0, 0, 1, 88, 20, 86, 0, 0, 0, 0, 0, 0] ], -"firstArrive": [], -"events": { - "4,10": [ - "\t[样板提示]本层楼将会对各类事件进行介绍。", - "左边是一个仿50层的陷阱做法,上方是商店、快捷商店的使用方法,右上是一个典型的杀怪开门的例子,右下是各类可能的NPC事件。", - "本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nanimate: 显示动画\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)", - "openShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nshowImage: 显示图片\nsetFg: 更改画面色调\nsetWeather: 更改天气\nmove: 移动事件效果\nmoveHero: 移动勇士效果\nplayBgm: 播放某个背景音乐\npauseBgm: 暂停背景音乐\nresumeBgm: 恢复背景音乐的播放\nplaySound: 播放某个音频", - "if: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nwin: 获得胜利(游戏通关)\nlose: 游戏失败\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nrevisit: 立刻结束事件并重新触发\nfunction: 自定义JS脚本\n\n更多支持的事件还在编写中,欢迎您宝贵的意见。", - "有关各事件的样例,可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后,需要调用{\"type\": \"hide\"}该事件才不会再次出现。", - { - "type": "hide" - } - ], - "1,5": { - "enable": false, - "data": [] - }, - "1,6": { - "enable": false, - "data": [] - }, - "0,7": { - "enable": false, - "data": [] - }, - "2,7": { - "enable": false, - "data": [] - }, - "1,8": { - "enable": false, - "data": [] - }, - "1,7": [ - { - "type": "show", - "loc": [ - 1, - 5 - ], - "time": 1500 + "firstArrive": [], + "events": { + "4,10": [ + "\t[样板提示]本层楼将会对各类事件进行介绍。", + "左边是一个仿50层的陷阱做法,上方是商店、快捷商店的使用方法,右上是一个典型的杀怪开门的例子,右下是各类可能的NPC事件。", + "本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nanimate: 显示动画\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)", + "openShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nshowImage: 显示图片\nsetFg: 更改画面色调\nsetWeather: 更改天气\nmove: 移动事件效果\nmoveHero: 移动勇士效果\nplayBgm: 播放某个背景音乐\npauseBgm: 暂停背景音乐\nresumeBgm: 恢复背景音乐的播放\nplaySound: 播放某个音频", + "if: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nwin: 获得胜利(游戏通关)\nlose: 游戏失败\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nfunction: 自定义JS脚本\n\n更多支持的事件还在编写中,欢迎您宝贵的意见。", + "有关各事件的样例,可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后,需要调用{\"type\": \"hide\"}该事件才不会再次出现。", + { + "type": "hide" + } + ], + "1,5": { + "enable": false, + "data": [] }, - { - "type": "sleep", - "time": 500 + "1,6": { + "enable": false, + "data": [] }, - "\t[redKing]欢迎来到魔塔,你是第一百位挑战者。\n若你能打败我所有的手下,我就与你一对一的决斗。\n现在你必须接受我的安排。", - { - "type": "show", - "loc": [ - [ + "0,7": { + "enable": false, + "data": [] + }, + "2,7": { + "enable": false, + "data": [] + }, + "1,8": { + "enable": false, + "data": [] + }, + "1,7": [ + { + "type": "show", + "loc": [ 1, - 6 + 5 ], - [ - 0, - 7 - ], - [ - 1, - 8 - ], - [ - 2, - 7 - ] - ], - "time": 500 - }, - "\t[hero]什么?", - { - "type": "playSound", - "name": "attack.mp3" - }, - { - "type": "setValue", - "name": "status:atk", - "operator": "/=", - "value": "10" - }, - { - "type": "setValue", - "name": "status:def", - "operator": "/=", - "value": "10" - }, - { - "type": "hide", - "loc": [ - [ - 1, - 6 - ], - [ - 0, - 7 - ], - [ - 2, - 7 - ], - [ - 1, - 8 - ] - ] - }, - { - "type": "hide", - "loc": [ - 1, - 5 - ], - "time": 500 - }, - { - "type": "hide" - }, - { - "type": "setCurtain", - "color": [ - 0, - 0, - 0 - ], - "time": 1250 - }, - { - "type": "sleep", - "time": 700 - }, - { - "type": "changeFloor", - "floorId": "sample1", - "loc": [ - 1, - 11 - ], - "direction": "right", - "time": 1000 - }, - { - "type": "trigger", - "loc": [ - 2, - 11 - ] - } - ], - "2,11": [ - "\t[杰克,thief]喂!醒醒!快醒醒!", - { - "type": "setCurtain", - "time": 1500 - }, - "\t[hero]额,我这是在什么地方?", - "\t[杰克,thief]你被魔王抓了起来扔进了监狱,和我关在了一起,但是幸运的是我在昨天刚刚挖好一条越狱的暗道!", - { - "type": "openDoor", - "loc": [ - 3, - 11 - ] - }, - { - "type": "sleep", - "time": 300 - }, - "\t[杰克,thief]我先走了,祝你好运!", - { - "type": "move", - "time": 750, - "steps": [ - "right", - "right", - "down" - ] - }, - "上面是个move事件,可以对NPC等进行移动。\n详见样板中小偷事件的写法。", - "\t[hero]怎么跑的这么快..." - ], - "4,2": [ - "\t[老人,man]本塔的商店有两类,全局商店和非全局商店。\n\n所谓非全局商店,就类似于右下角那个卖钥匙的老人一样,一定要碰到才能触发事件。\n\n而全局商店,则能在快捷商店中直接使用。", - "\t[老人,man]要注册一个全局商店,你需要在 data.js 中,找到 shops,并在内添加你的商店信息。", - "\t[老人,man]商店信息添加后,可以在需要的事件处调用{\"type\": \"openShop\"}来打开你添加的全局商店。", - "\t[老人,man]在上面的例子里,左边是一个仿50层的金币商店,右边是一个仿24层的经验商店。\n\n商店被访问后即可在快捷商店中进行使用。", - "\t[老人,man]如果你需要在某层暂时禁用快捷商店,可以在data.js中设置cannotUseQuickShop。\n如果需要永久禁用商店,请使用{\"type\":\"disableShop\"}", - { - "type": "hide", - "time": 500 - } - ], - "1,0": [ - { - "type": "openShop", - "id": "moneyShop1" - } - ], - "5,0": [ - { - "type": "openShop", - "id": "expShop1" - } - ], - "7,7": [ - "\t[老人,man]这是一个典型的杀怪开门、强制战斗事件。", - { - "type": "hide" - } - ], - "8,7": { - "enable": false, - "data": [] - }, - "9,7": [ - { - "type": "show", - "loc": [ - 8, - 7 - ] - }, - { - "type": "hide" - } - ], - "10,4": [ - "\t[blackKing]你终于还是来了。", - "\t[hero]放开我们的公主!", - "\t[blackKing]如果我不愿意呢?", - "\t[hero]无需多说,拔剑吧!", - { - "type": "battle", - "id": "blackKing" - }, - { - "type": "hide", - "loc": [ - 10, - 2 - ] - }, - { - "type": "openDoor", - "loc": [ - 8, - 7 - ] - }, - "\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了,请好好对她。", - { - "type": "hide" - } - ], - "10,0": [ - "\t[hero]公主,我来救你了~", - "\t[公主,princess]快救我出去!我受够这里了!", - "\t[hero]公主别怕,我们走吧~", - { - "type": "win", - "reason": "救出公主" - } - ], - "6,12": { - "enable": false, - "data": [] - }, - "6,11": [ - "\t[仙子,fairy]通过调用 {\"type\": \"show\"} 可以使隐藏的事件显示出来。\n比如我下面这个机关门。", - { - "type": "show", - "loc": [ - 6, - 12 - ] - }, - "\t[仙子,fairy]通过调用 {\"type\": \"openDoor\"} 可以无需钥匙打开一扇门或暗墙。", - { - "type": "openDoor", - "loc": [ - 6, - 12 - ] - }, - "\t[仙子,fairy]同时,也可以对其它层进行操作,比如楼下的机关门,现在已经为你打开了。", - { - "type": "openDoor", - "loc": [ - 11, - 10 - ], - "floorId": "sample0" - }, - "\t[仙子,fairy]如果 show 或 hide 指定了 time 参数,则以动画效果显示,指定的参数作为消失时间(毫秒)来计算。", - "\t[仙子,fairy]现在到楼下来找我吧~", - { - "type": "show", - "loc": [ - 12, - 10 - ], - "floorId": "sample0" - }, - { - "type": "hide", - "time": 500 - } - ], - "8,11": [ - { - "type": "setValue", - "name": "flag:man_times", - "operator": "+=", - "value": "1" - }, - "\t[老人,man]在文字中使用$+{}可以计算并显示一个表达式的结果。\n", - "\t[老人,man]例如:\n你的当前攻击力是${status:atk},防御力是${status:def}。\n攻防和的十倍是${10*(status:atk+status:def)},攻防之积是${status:atk*status:def}。\n你有${item:yellowKey}把黄钥匙,${item:blueKey}把蓝钥匙,${item:redKey}把红钥匙。\n你有${item:pickaxe}个破,${item:bomb}个炸,${item:centerFly}个飞。\n这是你第${flag:man_times}次和我对话。", - "\t[老人,man]同时,你也可以通过{\"type\": \"setValue\"}来设置一个勇士的属性、道具,或某个Flag。", - "\t[老人,man]例如:\n现在我将让你的攻防提升50%,再将攻防和的十倍加到生命值上。", - { - "type": "setValue", - "name": "status:atk", - "operator": "*=", - "value": "1.5" - }, - { - "type": "setValue", - "name": "status:def", - "operator": "*=", - "value": "1.5" - }, - { - "type": "setValue", - "name": "status:hp", - "operator": "+=", - "value": "10*(status:atk+status:def)" - }, - "\t[老人,man]再送你500金币,1000经验,1破2炸3飞!", - { - "type": "setValue", - "name": "status:money", - "operator": "+=", - "value": "500" - }, - { - "type": "setValue", - "name": "status:exp", - "operator": "+=", - "value": "1000" - }, - { - "type": "setValue", - "name": "item:pickaxe", - "operator": "+=", - "value": "1" - }, - { - "type": "setValue", - "name": "item:bomb", - "operator": "+=", - "value": "2" - }, - { - "type": "setValue", - "name": "item:centerFly", - "operator": "+=", - "value": "3" - }, - "\t[老人,man]status:xxx 代表勇士的某个属性。\n其中xxx可取hp, atk, def, mdef, money,exp这几项。\n\nitem:xxx 代表勇士的某个道具的个数。\nxxx为道具ID,具体可参见items.js中的定义。\n\nflag:xxx 代表某个自定义Flag或变量。\nxxx为Flag/变量名,可以自行定义,由字母、数字和下划线组成。\n未定义过而直接取用的Flag默认值为false。", - "\t[老人,man]你现在可以重新和我进行对话,进一步看到属性值的改变。" - ], - "10,11": [ - { - "type": "if", - "condition": "flag:woman_times==0", - "true": [ - "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", - "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", - "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" - ], - "false": [ - { - "type": "if", - "condition": "flag:woman_times==8", - "true": [ - "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", - "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", - { - "type": "setValue", - "name": "item:bigKey", - "operator": "+=", - "value": "1" - }, - "\t[老人,woman]我先走了,拜拜~", - { - "type": "hide", - "time": 500 - }, - { - "type": "exit" - } + "time": 1500 + }, + { + "type": "sleep", + "time": 500 + }, + "\t[redKing]欢迎来到魔塔,你是第一百位挑战者。\n若你能打败我所有的手下,我就与你一对一的决斗。\n现在你必须接受我的安排。", + { + "type": "show", + "loc": [ + [ + 1, + 6 ], - "false": [ - { - "type": "choices", - "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", - "choices": [ - { - "text": "黄钥匙(${9+flag:woman_times}金币)", - "action": [ - { - "type": "if", - "condition": "status:money>=9+flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "operator": "-=", - "value": "9+flag:woman_times" - }, - { - "type": "setValue", - "name": "item:yellowKey", - "operator": "+=", - "value": "1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "蓝钥匙(${18+2*flag:woman_times}金币)", - "action": [ - { - "type": "if", - "condition": "status:money>=18+2*flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "operator": "-=", - "value": "18+2*flag:woman_times" - }, - { - "type": "setValue", - "name": "item:blueKey", - "operator": "+=", - "value": "1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "红钥匙(${36+4*flag:woman_times}金币)", - "action": [ - { - "type": "if", - "condition": "status:money>=36+4*flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "operator": "-=", - "value": "36+4*flag:woman_times" - }, - { - "type": "setValue", - "name": "item:redKey", - "operator": "+=", - "value": "1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "离开", - "action": [ - { - "type": "exit" - } - ] - } - ] - } + [ + 0, + 7 + ], + [ + 1, + 8 + ], + [ + 2, + 7 ] - } - ] + ], + "time": 500 + }, + "\t[hero]什么?", + { + "type": "playSound", + "name": "attack.mp3" + }, + { + "type": "setValue", + "name": "status:atk", + "operator": "/=", + "value": "10" + }, + { + "type": "setValue", + "name": "status:def", + "operator": "/=", + "value": "10" + }, + { + "type": "hide", + "loc": [ + [ + 1, + 6 + ], + [ + 0, + 7 + ], + [ + 2, + 7 + ], + [ + 1, + 8 + ] + ] + }, + { + "type": "hide", + "loc": [ + 1, + 5 + ], + "time": 500 + }, + { + "type": "hide" + }, + { + "type": "setCurtain", + "color": [ + 0, + 0, + 0 + ], + "time": 1250 + }, + { + "type": "sleep", + "time": 700 + }, + { + "type": "changeFloor", + "floorId": "sample1", + "loc": [ + 1, + 11 + ], + "direction": "right", + "time": 1000 + }, + { + "type": "trigger", + "loc": [ + 2, + 11 + ] + } + ], + "2,11": [ + "\t[杰克,thief]喂!醒醒!快醒醒!", + { + "type": "setCurtain", + "time": 1500 + }, + "\t[hero]额,我这是在什么地方?", + "\t[杰克,thief]你被魔王抓了起来扔进了监狱,和我关在了一起,但是幸运的是我在昨天刚刚挖好一条越狱的暗道!", + { + "type": "openDoor", + "loc": [ + 3, + 11 + ] + }, + { + "type": "sleep", + "time": 300 + }, + "\t[杰克,thief]我先走了,祝你好运!", + { + "type": "move", + "time": 750, + "steps": [ + "right", + "right", + "down" + ] + }, + "上面是个move事件,可以对NPC等进行移动。\n详见样板中小偷事件的写法。", + "\t[hero]怎么跑的这么快..." + ], + "4,2": [ + "\t[老人,man]本塔的商店有两类,全局商店和非全局商店。\n\n所谓非全局商店,就类似于右下角那个卖钥匙的老人一样,一定要碰到才能触发事件。\n\n而全局商店,则能在快捷商店中直接使用。", + "\t[老人,man]要注册一个全局商店,你需要在 data.js 中,找到 shops,并在内添加你的商店信息。", + "\t[老人,man]商店信息添加后,可以在需要的事件处调用{\"type\": \"openShop\"}来打开你添加的全局商店。", + "\t[老人,man]在上面的例子里,左边是一个仿50层的金币商店,右边是一个仿24层的经验商店。\n\n商店被访问后即可在快捷商店中进行使用。", + "\t[老人,man]如果你需要在某层暂时禁用快捷商店,可以在data.js中设置cannotUseQuickShop。\n如果需要永久禁用商店,请使用{\"type\":\"disableShop\"}", + { + "type": "hide", + "time": 500 + } + ], + "1,0": [ + { + "type": "openShop", + "id": "shop1", + "open": true + } + ], + "5,0": [ + { + "type": "openShop", + "id": "shop2", + "open": true + } + ], + "7,7": [ + "\t[老人,man]这是一个典型的杀怪开门、强制战斗事件。", + { + "type": "hide" + } + ], + "8,7": { + "enable": false, + "data": [] }, - { - "type": "setValue", - "name": "flag:woman_times", - "operator": "+=", - "value": "1" + "9,7": [ + { + "type": "show", + "loc": [ + 8, + 7 + ] + }, + { + "type": "hide" + } + ], + "10,4": [ + "\t[blackKing]你终于还是来了。", + "\t[hero]放开我们的公主!", + "\t[blackKing]如果我不愿意呢?", + "\t[hero]无需多说,拔剑吧!", + { + "type": "battle", + "id": "blackKing" + }, + { + "type": "hide", + "loc": [ + 10, + 2 + ] + }, + { + "type": "openDoor", + "loc": [ + 8, + 7 + ] + }, + "\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了,请好好对她。", + { + "type": "hide" + } + ], + "10,0": [ + "\t[hero]公主,我来救你了~", + "\t[公主,princess]快救我出去!我受够这里了!", + "\t[hero]公主别怕,我们走吧~", + { + "type": "win", + "reason": "救出公主" + } + ], + "6,12": { + "enable": false, + "data": [] }, - { - "type": "revisit" - } - ], - "12,11": [ - "\t[老人,womanMagician]使用 {\"type\":\"function\"} 可以写自定义的JS脚本。\n本塔支持的所有主要API会在doc文档内给出。", - "\t[老人,womanMagician]例如这个例子:即将弹出一个输入窗口,然后会将你的输入结果直接加到你的攻击力上。", - { - "type": "input", - "text": "请输入你要加攻击力的数值:" - }, - { - "type": "if", - "condition": "flag:input>0", - "true": [ - { - "type": "setValue", - "name": "status:atk", - "operator": "+=", - "value": "flag:input" - }, - { - "type": "tip", - "text": "操作成功,攻击+${flag:input}" - }, - "操作成功,攻击+${flag:input}" - ], - "false": [] - }, - "\t[老人,womanMagician]具体可参见样板中本事件的写法。" - ], -}, -"changeFloor": { - "4,12": { - "floorId": "sample0", - "loc": [ - 6, - 0 + "6,11": [ + "\t[仙子,fairy]通过调用 {\"type\": \"show\"} 可以使隐藏的事件显示出来。\n比如我下面这个机关门。", + { + "type": "show", + "loc": [ + 6, + 12 + ] + }, + "\t[仙子,fairy]通过调用 {\"type\": \"openDoor\"} 可以无需钥匙打开一扇门或暗墙。", + { + "type": "openDoor", + "loc": [ + 6, + 12 + ] + }, + "\t[仙子,fairy]同时,也可以对其它层进行操作,比如楼下的机关门,现在已经为你打开了。", + { + "type": "openDoor", + "loc": [ + 11, + 10 + ], + "floorId": "sample0" + }, + "\t[仙子,fairy]如果 show 或 hide 指定了 time 参数,则以动画效果显示,指定的参数作为消失时间(毫秒)来计算。", + "\t[仙子,fairy]现在到楼下来找我吧~", + { + "type": "show", + "loc": [ + 12, + 10 + ], + "floorId": "sample0" + }, + { + "type": "hide", + "time": 500 + } + ], + "8,11": [ + { + "type": "setValue", + "name": "flag:man_times", + "operator": "+=", + "value": "1" + }, + "\t[老人,man]在文字中使用$+{}可以计算并显示一个表达式的结果。\n", + "\t[老人,man]例如:\n你的当前攻击力是${status:atk},防御力是${status:def}。\n攻防和的十倍是${10*(status:atk+status:def)},攻防之积是${status:atk*status:def}。\n你有${item:yellowKey}把黄钥匙,${item:blueKey}把蓝钥匙,${item:redKey}把红钥匙。\n你有${item:pickaxe}个破,${item:bomb}个炸,${item:centerFly}个飞。\n这是你第${flag:man_times}次和我对话。", + "\t[老人,man]同时,你也可以通过{\"type\": \"setValue\"}来设置一个勇士的属性、道具,或某个Flag。", + "\t[老人,man]例如:\n现在我将让你的攻防提升50%,再将攻防和的十倍加到生命值上。", + { + "type": "setValue", + "name": "status:atk", + "operator": "*=", + "value": "1.5" + }, + { + "type": "setValue", + "name": "status:def", + "operator": "*=", + "value": "1.5" + }, + { + "type": "setValue", + "name": "status:hp", + "operator": "+=", + "value": "10*(status:atk+status:def)" + }, + "\t[老人,man]再送你500金币,1000经验,1破2炸3飞!", + { + "type": "setValue", + "name": "status:money", + "operator": "+=", + "value": "500" + }, + { + "type": "setValue", + "name": "status:exp", + "operator": "+=", + "value": "1000" + }, + { + "type": "setValue", + "name": "item:pickaxe", + "operator": "+=", + "value": "1" + }, + { + "type": "setValue", + "name": "item:bomb", + "operator": "+=", + "value": "2" + }, + { + "type": "setValue", + "name": "item:centerFly", + "operator": "+=", + "value": "3" + }, + "\t[老人,man]status:xxx 代表勇士的某个属性。\n其中xxx可取hp, atk, def, mdef, money,exp这几项。\n\nitem:xxx 代表勇士的某个道具的个数。\nxxx为道具ID,具体可参见items.js中的定义。\n\nflag:xxx 代表某个自定义Flag或变量。\nxxx为Flag/变量名,可以自行定义,由字母、数字和下划线组成。\n未定义过而直接取用的Flag默认值为false。", + "\t[老人,man]你现在可以重新和我进行对话,进一步看到属性值的改变。" + ], + "10,11": [ + { + "type": "while", + "condition": "true", + "data": [ + { + "type": "switch", + "condition": "flag:woman_times", + "caseList": [ + { + "case": "0", + "action": [ + "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", + "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", + "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" + ] + }, + { + "case": "8", + "action": [ + "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", + "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", + { + "type": "setValue", + "name": "item:bigKey", + "operator": "+=", + "value": "1" + }, + "\t[老人,woman]我先走了,拜拜~", + { + "type": "hide", + "time": 500 + }, + { + "type": "exit" + } + ] + }, + { + "case": "default", + "action": [ + { + "type": "choices", + "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", + "choices": [ + { + "text": "黄钥匙(${9+flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=9+flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "operator": "-=", + "value": "9+flag:woman_times" + }, + { + "type": "setValue", + "name": "item:yellowKey", + "operator": "+=", + "value": "1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!" + ] + } + ] + }, + { + "text": "蓝钥匙(${18+2*flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=18+2*flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "operator": "-=", + "value": "18+2*flag:woman_times" + }, + { + "type": "setValue", + "name": "item:blueKey", + "operator": "+=", + "value": "1" + }, + { + "type": "continue" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!" + ] + } + ] + }, + { + "text": "红钥匙(${36+4*flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=36+4*flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "operator": "-=", + "value": "36+4*flag:woman_times" + }, + { + "type": "setValue", + "name": "item:redKey", + "operator": "+=", + "value": "1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "continue" + } + ] + } + ] + }, + { + "text": "离开", + "action": [ + { + "type": "exit" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "setValue", + "name": "flag:woman_times", + "operator": "+=", + "value": "1" + } + ] + } + ], + "12,11": [ + "\t[老人,womanMagician]使用 {\"type\":\"function\"} 可以写自定义的JS脚本。\n本塔支持的所有主要API会在doc文档内给出。", + "\t[老人,womanMagician]例如这个例子:即将弹出一个输入窗口,然后会将你的输入结果直接加到你的攻击力上。", + { + "type": "input", + "text": "请输入你要加攻击力的数值:" + }, + { + "type": "if", + "condition": "flag:input>0", + "true": [ + { + "type": "setValue", + "name": "status:atk", + "operator": "+=", + "value": "flag:input" + }, + { + "type": "tip", + "text": "操作成功,攻击+${flag:input}" + }, + "操作成功,攻击+${flag:input}" + ], + "false": [] + }, + "\t[老人,womanMagician]具体可参见样板中本事件的写法。" ] }, - "5,5": { - "floorId": "sample2", - "stair": "downFloor", - "direction": "up" - }, - "10,12": null -}, -"afterBattle": { - "9,6": [ - { - "type": "setValue", - "name": "flag:door", - "operator": "+=", - "value": "1" - } - ], - "11,6": [ - { - "type": "setValue", - "name": "flag:door", - "operator": "+=", - "value": "1" - } - ] -}, -"afterGetItem": {}, -"afterOpenDoor": {}, -"cannotMove": {}, -"bgmap": [ - -], -"fgmap": [ - -], -"autoEvent": { - "10,5": { - "0": { - "condition": "flag:door==2", - "currentFloor": true, - "priority": 0, - "delayExecute": false, - "multiExecute": false, - "data": [ - { - "type": "openDoor" - } - + "changeFloor": { + "4,12": { + "floorId": "sample0", + "loc": [ + 6, + 0 ] + }, + "5,5": { + "floorId": "sample2", + "stair": "downFloor", + "direction": "up" + }, + "10,12": null + }, + "afterBattle": { + "9,6": [ + { + "type": "setValue", + "name": "flag:door", + "operator": "+=", + "value": "1" + } + ], + "11,6": [ + { + "type": "setValue", + "name": "flag:door", + "operator": "+=", + "value": "1" + } + ] + }, + "afterGetItem": {}, + "afterOpenDoor": {}, + "cannotMove": {}, + "bgmap": [ + +], + "fgmap": [ + +], + "autoEvent": { + "10,5": { + "0": { + "condition": "flag:door==2", + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + { + "type": "openDoor" + } + ] + } } - } -} + }, + "width": 13, + "height": 13 } \ No newline at end of file diff --git a/project/floors/sample2.js b/project/floors/sample2.js index 7ae07530..a118ee6d 100644 --- a/project/floors/sample2.js +++ b/project/floors/sample2.js @@ -1,15 +1,15 @@ main.floors.sample2= { -"floorId": "sample2", -"title": "样板 2 层", -"name": "2", -"canFlyTo": true, -"canUseQuickShop": true, -"cannotViewMap": false, -"defaultGround": "ground", -"images": [], -"item_ratio": 1, -"map": [ + "floorId": "sample2", + "title": "样板 2 层", + "name": "2", + "canFlyTo": true, + "canUseQuickShop": true, + "cannotViewMap": false, + "defaultGround": "ground", + "images": [], + "item_ratio": 1, + "map": [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 1,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -37,36 +37,37 @@ main.floors.sample2= [ 0, 0, 0, 0, 0,202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,202, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ], -"width": 26, -"height": 26, -"firstArrive": [], -"events": { - "3,2": [ - "123" - ], - "12,12": [ - "234" - ] -}, -"changeFloor": { - "6,10": { - "floorId": "sample1", - "stair": "upFloor" + "width": 26, + "height": 26, + "firstArrive": [], + "events": { + "3,2": [ + "123" + ], + "12,12": [ + "234" + ] }, - "7,12": { - "floorId": "sample3", - "stair": "downFloor" - } -}, -"afterBattle": {}, -"afterGetItem": {}, -"afterOpenDoor": {}, -"cannotMove": {}, -"upFloor": null, -"bgmap": [ + "changeFloor": { + "6,10": { + "floorId": "sample1", + "stair": "upFloor" + }, + "7,12": { + "floorId": "sample3", + "stair": "downFloor" + } + }, + "afterBattle": {}, + "afterGetItem": {}, + "afterOpenDoor": {}, + "cannotMove": {}, + "upFloor": null, + "bgmap": [ ], -"fgmap": [ + "fgmap": [ ], + "autoEvent": {} } \ No newline at end of file diff --git a/project/floors/sample3.js b/project/floors/sample3.js index e115ba5f..e0c28451 100644 --- a/project/floors/sample3.js +++ b/project/floors/sample3.js @@ -1,25 +1,25 @@ main.floors.sample3= { -"floorId": "sample3", -"title": "主塔 40 层", -"name": "40", -"canFlyTo": false, -"canUseQuickShop": true, -"defaultGround": "snowGround", -"images": [], -"color": [ - 255, - 0, - 0, - 0.3 -], -"weather": [ - "rain", - 10 -], -"bgm": "bgm.mp3", -"item_ratio": 1, -"map": [ + "floorId": "sample3", + "title": "主塔 40 层", + "name": "40", + "canFlyTo": false, + "canUseQuickShop": true, + "defaultGround": "snowGround", + "images": [], + "color": [ + 255, + 0, + 0, + 0.3 + ], + "weather": [ + "rain", + 10 + ], + "bgm": "bgm.mp3", + "item_ratio": 1, + "map": [ [ 5, 5, 5, 5, 5, 5, 87, 5, 5, 5, 5, 5, 5], [ 5, 4, 4, 4, 4, 1, 0, 1, 4, 4, 4, 4, 5], [ 5, 4, 4, 4, 4, 1, 85, 1, 4, 4, 4, 4, 5], @@ -34,972 +34,975 @@ main.floors.sample3= [ 5, 4, 4, 4, 4, 4, 85, 4, 4, 4, 4, 4, 5], [ 5, 5, 5, 5, 5, 5, 88, 5, 5, 5, 5, 5, 5] ], -"firstArrive": [ - "\t[实战!]本楼将尝试复刻《宿命的旋律》40F剧情。" -], -"events": { - "6,11": { - "enable": false, - "data": [] - }, - "6,10": [ - { - "type": "playSound", - "name": "door.mp3" - }, - { - "type": "show", - "loc": [ - 6, - 11 - ] - }, - { - "type": "hide" - }, - { - "type": "trigger", - "loc": [ - 6, - 7 - ] - } + "firstArrive": [ + "\t[实战!]本楼将尝试复刻《宿命的旋律》40F剧情。" ], - "6,7": [ - { - "type": "playSound", - "name": "item.mp3" + "events": { + "6,11": { + "enable": false, + "data": [] }, - "\t[hero]杰克,你究竟是什么人?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[杰克,thief]……", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]我们……是朋友对吧?\n是朋友就应该相互信任对吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[杰克,thief]……事到如今也没有什么好隐瞒的了。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[杰克,thief]没错,我就是这一切的背后主谋。", - { - "type": "move", - "steps": [ - { - "direction": "up", - "value": 3 - } - ], - "time": 1000 - }, - { - "type": "show", - "loc": [ - 6, - 4 - ], - "time": 1000 - }, - { - "type": "sleep", - "time": 500 - }, - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]我的真名为——黑暗大法师,第四区域的头目。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]呵呵,不知道为什么,我竟然对事情走到现在这一步毫不感觉意外。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]以杰克的名义利用了你这么久,真是抱歉啊。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]真正的杰克现在在哪里?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]盗贼杰克这个人类从未存在过,他只是我用来接近你的一副皮囊而已。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……这样啊,呵呵。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]为什么你看上去丝毫不生气?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]多亏了鬼帝,我现在的脾气好得连我自己都害怕。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]说起来我还得好好感谢你呢,如果没有杰克……你的帮助,我早就死在第一区域了。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]不论你的目的如何,你的所作所为都是对我有利的。不是吗?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]能够如此淡定的面对背叛,看来跟五年前相比,你确实成长了很多啊。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]五年前?……黑暗大法师,在这之前,我们好像素未谋面吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]五年前那场屠城你应该这一生都不会忘记吧。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]很不巧,那场屠城的主谋,也是我。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]这么说,击中我双亲的那道紫色闪电,也就是你释放的吧……", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]你的双亲?这种事情我怎么可能会记得?\n你难道在踩死蚂蚁的时候还会一只只记下他们的样子吗?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]老 子 要 你 的 命", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]你应该对我心怀感激才对,如果不是那时的我看出了你隐藏的稀有勇者体质,你绝对不可能活到今天。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]在暗中动手脚让你通过勇者选拔的人也是我,我一直一直在暗中引导你走到今天这一步。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]是我救赎了一无是处的你。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]为什么只有我一个人活了下来!!!!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]为什么偏偏是我!!!!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]我刚才不是说过了吗?因为我看出了你有稀有勇者体质啊。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]你刚刚跟鬼帝交过手,应该已经很清楚这稀有勇者体质意味着什么了吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……就因为我有这种体质,就不得不背负如此残酷的宿命吗?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]愚蠢!这意味着只要我对你加以引导跟培养,你就能成为这世间实力最强的存在!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……所以,你究竟想利用我干什么?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]我利用你干的事情,你不是已经完成了吗?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……你说什么?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]不知不觉间,你已经在我的指引下跟鬼帝正面交手并且杀掉了他啊。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]就连我跟鬼帝的对决……也是被你安排好了的?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]你们两个一个是人类勇者,一个是魔物勇者,迟早会有交手的一天。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]我只不过是操纵了一系列的连锁事件让这一天提早了数十年到来而已。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……你这样做对谁有好处?他可是你们魔物世界的救世主啊。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]一个惧怕征战,爱好和平的懦夫,也配叫救世主?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]获得了力量,却只会被动挨打而不主动向人类世界出击,龟缩在第二区域惶惶度日,他根本就不配拥有稀有勇者体质。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]为了不让这种人霸占着积累多年的庞大灵魂能量无作为,我设计让你杀掉了他。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]你没有辜负我的期待,成功战胜了那个废物,现在你体内累积的灵魂能量……也就是魔力,已经达到了能跟魔王匹敌的地步。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……是吗?现在的我能与魔王匹敌?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]不止如此,你现在的力量之强就算是统治世界也是绰绰有余!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]怎么样?要不要加入我的麾下,跟随我去征战人类世界?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]能与魔王匹敌的话,也就是说。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]我 现 在 对 付 你 这 种 杂 碎 也 绰 绰 有 余 吧 ?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]……什么?!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]等一下!别冲动!你先等我把这利害关系理一理——", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]你给老子闭嘴。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]老子什么都不想听。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]老子现在想做的事情只有一件——", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]剁掉你的头,把它放回我双亲的墓前。", - { - "type": "update" - } - ], - "6,4": { - "enable": false, - "data": [] - }, - "5,4": { - "enable": false, - "data": [] - }, - "7,4": { - "enable": false, - "data": [] - }, - "5,5": { - "enable": false, - "data": [] - }, - "7,5": { - "enable": false, - "data": [] - }, - "6,3": { - "trigger": "action", - "enable": false, - "data": [ - "\t[blackMagician]听不进去人话的蠢货,就要用疼痛来管教!", - { - "type": "changePos", - "direction": "up" - }, + "6,10": [ { "type": "playSound", - "name": "item.mp3" + "name": "door.mp3" }, - "\t[blackMagician]出来吧!禁忌——紫电凶杀阵!", - { - "type": "show", - "loc": [ - [ - 4, - 3 - ], - [ - 4, - 6 - ], - [ - 8, - 6 - ], - [ - 8, - 3 - ] - ], - "time": 500 - }, - { - "type": "sleep", - "time": 500 - }, - "\t[blackMagician]感受绝望吧!冥顽不化的蠢货!", - { - "type": "animate", - "name": "yongchang", - "loc": [ - 4, - 3 - ] - }, - { - "type": "animate", - "name": "yongchang", - "loc": [ - 4, - 6 - ] - }, - { - "type": "animate", - "name": "yongchang", - "loc": [ - 8, - 6 - ] - }, - { - "type": "animate", - "name": "yongchang", - "loc": [ - 8, - 3 - ] - }, - { - "type": "sleep", - "time": 200 - }, - { - "type": "playSound", - "name": "attack.mp3" - }, - { - "type": "animate", - "name": "thunder", - "loc": "hero" - }, - { - "type": "sleep", - "time": 200 - }, - "\t[hero]唔……!!(吐血)", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]我的魔力可是充足的很啊!我会一直折磨到你屈服于我为止!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]人类!好好感受吧!当初你们施加于我的痛苦!如今我要百倍奉还!", { "type": "show", "loc": [ 6, - 6 - ], - "time": 1000 + 11 + ] }, { - "type": "sleep", - "time": 700 + "type": "hide" }, { "type": "trigger", "loc": [ 6, - 6 + 7 ] } - ] - }, - "4,3": { - "trigger": "action", - "displayDamage": false, - "enable": false, - "data": [] - }, - "8,3": { - "trigger": "action", - "displayDamage": false, - "enable": false, - "data": [] - }, - "4,6": { - "trigger": "action", - "displayDamage": false, - "enable": false, - "data": [] - }, - "8,6": { - "trigger": "action", - "displayDamage": false, - "enable": false, - "data": [] - }, - "6,6": { - "enable": false, - "data": [ + ], + "6,7": [ { "type": "playSound", "name": "item.mp3" }, - "\t[hero]…妖精…小姐……是你吗?", + "\t[hero]杰克,你究竟是什么人?", { "type": "playSound", "name": "item.mp3" }, - "\t[小妖精,fairy]不要绝望,也不要悲伤。", + "\t[杰克,thief]……", { "type": "playSound", "name": "item.mp3" }, - "\t[小妖精,fairy]你从来都不是独自一人在前进。", + "\t[hero]我们……是朋友对吧?\n是朋友就应该相互信任对吧?", { "type": "playSound", "name": "item.mp3" }, - "\t[小妖精,fairy]咱一直,一直都在注视着你。", + "\t[杰克,thief]……事到如今也没有什么好隐瞒的了。", { "type": "playSound", "name": "item.mp3" }, - "\t[小妖精,fairy]耍小聪明的你、笨笨的你呆呆的你、胆小的你、勇敢的你帅气的你……全部全部都是你。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]所以放心吧,无论发生什么,咱都会陪伴在你身边的。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]因为你要是离开我的话,立刻就会死掉吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]…妖精…小姐……其实一直以来,我都非常感激你……", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]笨蛋!都这种时候了就不要作出像是临终遗言的发言了啊!!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]喂!那边穿衣品味差到极点的黑暗大法师,别左顾右盼说的就是你!你应该知道咱的身份吧?\n还不速速退下!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]可恶…多管闲事的妖精族…明明只要再让他承受一点疼痛来瓦解他的意志力,我的计划就成功了!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]哼哼哼~抱歉哦,这个笨蛋的意志力可不像你想象的那么薄弱哦!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]不甘心!我不甘心!妖精公主又如何!\n只要是阻挡我的,不管是谁我都要铲除!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]终于露出狐狸尾巴了,其实咱早就看出你有谋反的念头。你的计划就是拉拢这家伙入伙然后推翻魔王对魔塔的统治对吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]呵呵呵……那个昏庸的魔王,掌握着那么庞大的魔物军队却只知道固守魔塔,而不主动侵略人类世界扩张领土!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]我实在是看不过眼,所以我才决定把这个具备稀有勇者体质的家伙培养成新一任魔王!\n来让这个世界的势力重新洗牌!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]你觉得一个满脑子想着回家种田的废柴勇者会成为改变世界的魔王?你晃晃脑袋试试,是不是能听到大海的声音?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]恼人至极的妖精族!呵呵呵……我干脆一不做二不休,连你也一块收拾了吧!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]别小瞧咱!咱好歹也是妖精族里实力数一数二的存在!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]只会耍嘴皮子的恼人苍蝇!我倒要看看一块焦炭会不会说话!\n——招雷弹!!", - { - "type": "animate", - "name": "yongchang", - "loc": [ - 4, - 3 - ] - }, - { - "type": "animate", - "name": "yongchang", - "loc": [ - 4, - 6 - ] - }, - { - "type": "animate", - "name": "yongchang", - "loc": [ - 8, - 6 - ] - }, - { - "type": "animate", - "name": "yongchang", - "loc": [ - 8, - 3 - ] - }, - { - "type": "playSound", - "name": "attack.mp3" - }, - { - "type": "animate", - "name": "thunder", - "loc": [ - 6, - 6 - ] - }, - { - "type": "sleep", - "time": 500 - }, - "\t[小妖精,fairy]切,这点伤痛跟他刚才经历的身心地狱相比根本就不算什么。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]哼!翅膀都被烧焦了还要嘴硬?你难不成真以为我不会对你动真格?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]……你这混蛋!给我离她远点!!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]!…你现在受了很严重的致命伤,乱动什么?\n乖。别怕,这里有咱顶着!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]对了,咱再问你一遍,你是很珍惜自己性命的对吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]!…等等…妖精小姐,你不会是……?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]喂,黑暗大法师,你作为魔塔里最博学多识的蠢货,应该对咱妖精族的特殊能力再清楚不过吧?", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]什么?!难不成你是想!!不可能……\n就为了一个渺小的人类,不可理喻!!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]哼哼哼!你害怕的表情可真美味!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]不过比起这个,咱更期待你吃到“妖精自灭冲击”之后的死状哦!~", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[blackMagician]不!!不应该是这样的!我完美的计划竟然会被一只小小的妖精破坏!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[hero]不要!……千万不要!……为了我这种人……唔!", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]笨蛋,动都动不了了就不要强撑着站起来了啊。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]真是的,都到最后一刻了,你这家伙好歹也让咱省点心吧。", - { - "type": "playSound", - "name": "item.mp3" - }, - "\t[小妖精,fairy]那么,再见了……我的勇者大人。", + "\t[杰克,thief]没错,我就是这一切的背后主谋。", { "type": "move", - "time": 700, "steps": [ { "direction": "up", "value": 3 } - ] - }, - { - "type": "playSound", - "name": "attack.mp3" - }, - { - "type": "sleep", - "time": 200 - }, - "\t[blackMagician]不可能!!!!!", - { - "type": "hide", - "loc": [ - 6, - 3 - ] - }, - { - "type": "hide", - "loc": [ - 4, - 3 - ] - }, - { - "type": "hide", - "loc": [ - 4, - 6 - ] - }, - { - "type": "hide", - "loc": [ - 8, - 6 - ] - }, - { - "type": "hide", - "loc": [ - 8, - 3 - ] - }, - { - "type": "changeFloor", - "floorId": "sample3", - "loc": [ - 6, - 6 ], - "direction": "up", "time": 1000 }, { "type": "show", "loc": [ 6, - 5 - ] + 4 + ], + "time": 1000 }, { "type": "sleep", - "time": 200 + "time": 500 }, { "type": "playSound", "name": "item.mp3" }, - { - "type": "sleep", - "time": 200 - }, - "\t[hero]…妖精…小姐……", + "\t[blackMagician]我的真名为——黑暗大法师,第四区域的头目。", { "type": "playSound", "name": "item.mp3" }, - "\t[hero]……妖精小姐!", + "\t[hero]呵呵,不知道为什么,我竟然对事情走到现在这一步毫不感觉意外。", { "type": "playSound", "name": "item.mp3" }, - "\t[hero]是梦吗?……不对,为什么我在流泪?", + "\t[blackMagician]以杰克的名义利用了你这么久,真是抱歉啊。", { "type": "playSound", "name": "item.mp3" }, - "\t[hero]这颗漂亮的宝石是……?", + "\t[hero]真正的杰克现在在哪里?", { "type": "playSound", "name": "item.mp3" }, - "\t[hero]我全都想起来了……妖精小姐为了我……\n牺牲了自己的性命。", + "\t[blackMagician]盗贼杰克这个人类从未存在过,他只是我用来接近你的一副皮囊而已。", { "type": "playSound", "name": "item.mp3" }, - "\t[hero]在这颗宝石上,我能感受到你的温度……\n熟悉而又令人安心,这就是你最后留给我的东西吗……", + "\t[hero]……这样啊,呵呵。", { "type": "playSound", "name": "item.mp3" }, - "\t[hero]好温暖……", + "\t[blackMagician]为什么你看上去丝毫不生气?", { - "type": "setValue", - "name": "item:yellowJewel", - "value": "1" + "type": "playSound", + "name": "item.mp3" }, + "\t[hero]多亏了鬼帝,我现在的脾气好得连我自己都害怕。", { - "type": "hide", - "loc": [ - 6, - 5 - ] + "type": "playSound", + "name": "item.mp3" }, + "\t[hero]说起来我还得好好感谢你呢,如果没有杰克……你的帮助,我早就死在第一区域了。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]不论你的目的如何,你的所作所为都是对我有利的。不是吗?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]能够如此淡定的面对背叛,看来跟五年前相比,你确实成长了很多啊。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]五年前?……黑暗大法师,在这之前,我们好像素未谋面吧?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]五年前那场屠城你应该这一生都不会忘记吧。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]很不巧,那场屠城的主谋,也是我。", { "type": "playSound", "name": "item.mp3" }, "\t[hero]……", { - "type": "openDoor", + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]这么说,击中我双亲的那道紫色闪电,也就是你释放的吧……", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]你的双亲?这种事情我怎么可能会记得?\n你难道在踩死蚂蚁的时候还会一只只记下他们的样子吗?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]老 子 要 你 的 命", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]你应该对我心怀感激才对,如果不是那时的我看出了你隐藏的稀有勇者体质,你绝对不可能活到今天。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]在暗中动手脚让你通过勇者选拔的人也是我,我一直一直在暗中引导你走到今天这一步。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]是我救赎了一无是处的你。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]为什么只有我一个人活了下来!!!!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]为什么偏偏是我!!!!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]我刚才不是说过了吗?因为我看出了你有稀有勇者体质啊。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]你刚刚跟鬼帝交过手,应该已经很清楚这稀有勇者体质意味着什么了吧?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……就因为我有这种体质,就不得不背负如此残酷的宿命吗?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]愚蠢!这意味着只要我对你加以引导跟培养,你就能成为这世间实力最强的存在!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……所以,你究竟想利用我干什么?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]我利用你干的事情,你不是已经完成了吗?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……你说什么?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]不知不觉间,你已经在我的指引下跟鬼帝正面交手并且杀掉了他啊。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]就连我跟鬼帝的对决……也是被你安排好了的?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]你们两个一个是人类勇者,一个是魔物勇者,迟早会有交手的一天。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]我只不过是操纵了一系列的连锁事件让这一天提早了数十年到来而已。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……你这样做对谁有好处?他可是你们魔物世界的救世主啊。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]一个惧怕征战,爱好和平的懦夫,也配叫救世主?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]获得了力量,却只会被动挨打而不主动向人类世界出击,龟缩在第二区域惶惶度日,他根本就不配拥有稀有勇者体质。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]为了不让这种人霸占着积累多年的庞大灵魂能量无作为,我设计让你杀掉了他。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]你没有辜负我的期待,成功战胜了那个废物,现在你体内累积的灵魂能量……也就是魔力,已经达到了能跟魔王匹敌的地步。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……是吗?现在的我能与魔王匹敌?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]不止如此,你现在的力量之强就算是统治世界也是绰绰有余!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]怎么样?要不要加入我的麾下,跟随我去征战人类世界?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]能与魔王匹敌的话,也就是说。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]我 现 在 对 付 你 这 种 杂 碎 也 绰 绰 有 余 吧 ?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]……什么?!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]等一下!别冲动!你先等我把这利害关系理一理——", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]你给老子闭嘴。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]老子什么都不想听。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]老子现在想做的事情只有一件——", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]剁掉你的头,把它放回我双亲的墓前。", + { + "type": "update" + } + ], + "6,4": { + "enable": false, + "data": [] + }, + "5,4": { + "enable": false, + "data": [] + }, + "7,4": { + "enable": false, + "data": [] + }, + "5,5": { + "enable": false, + "data": [] + }, + "7,5": { + "enable": false, + "data": [] + }, + "6,3": { + "trigger": "action", + "enable": false, + "data": [ + "\t[blackMagician]听不进去人话的蠢货,就要用疼痛来管教!", + { + "type": "changePos", + "direction": "up" + }, + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]出来吧!禁忌——紫电凶杀阵!", + { + "type": "show", + "loc": [ + [ + 4, + 3 + ], + [ + 4, + 6 + ], + [ + 8, + 6 + ], + [ + 8, + 3 + ] + ], + "time": 500 + }, + { + "type": "sleep", + "time": 500 + }, + "\t[blackMagician]感受绝望吧!冥顽不化的蠢货!", + { + "type": "animate", + "name": "yongchang", + "loc": [ + 4, + 3 + ] + }, + { + "type": "animate", + "name": "yongchang", + "loc": [ + 4, + 6 + ] + }, + { + "type": "animate", + "name": "yongchang", + "loc": [ + 8, + 6 + ] + }, + { + "type": "animate", + "name": "yongchang", + "loc": [ + 8, + 3 + ] + }, + { + "type": "sleep", + "time": 200 + }, + { + "type": "playSound", + "name": "attack.mp3" + }, + { + "type": "animate", + "name": "thunder", + "loc": "hero" + }, + { + "type": "sleep", + "time": 200 + }, + "\t[hero]唔……!!(吐血)", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]我的魔力可是充足的很啊!我会一直折磨到你屈服于我为止!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]人类!好好感受吧!当初你们施加于我的痛苦!如今我要百倍奉还!", + { + "type": "show", + "loc": [ + 6, + 6 + ], + "time": 1000 + }, + { + "type": "sleep", + "time": 700 + }, + { + "type": "trigger", + "loc": [ + 6, + 6 + ] + } + ] + }, + "4,3": { + "trigger": "action", + "displayDamage": false, + "enable": false, + "data": [] + }, + "8,3": { + "trigger": "action", + "displayDamage": false, + "enable": false, + "data": [] + }, + "4,6": { + "trigger": "action", + "displayDamage": false, + "enable": false, + "data": [] + }, + "8,6": { + "trigger": "action", + "displayDamage": false, + "enable": false, + "data": [] + }, + "6,6": { + "enable": false, + "data": [ + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]…妖精…小姐……是你吗?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]不要绝望,也不要悲伤。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]你从来都不是独自一人在前进。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]咱一直,一直都在注视着你。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]耍小聪明的你、笨笨的你呆呆的你、胆小的你、勇敢的你帅气的你……全部全部都是你。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]所以放心吧,无论发生什么,咱都会陪伴在你身边的。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]因为你要是离开我的话,立刻就会死掉吧?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]…妖精…小姐……其实一直以来,我都非常感激你……", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]笨蛋!都这种时候了就不要作出像是临终遗言的发言了啊!!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]喂!那边穿衣品味差到极点的黑暗大法师,别左顾右盼说的就是你!你应该知道咱的身份吧?\n还不速速退下!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]可恶…多管闲事的妖精族…明明只要再让他承受一点疼痛来瓦解他的意志力,我的计划就成功了!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]哼哼哼~抱歉哦,这个笨蛋的意志力可不像你想象的那么薄弱哦!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]不甘心!我不甘心!妖精公主又如何!\n只要是阻挡我的,不管是谁我都要铲除!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]终于露出狐狸尾巴了,其实咱早就看出你有谋反的念头。你的计划就是拉拢这家伙入伙然后推翻魔王对魔塔的统治对吧?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]呵呵呵……那个昏庸的魔王,掌握着那么庞大的魔物军队却只知道固守魔塔,而不主动侵略人类世界扩张领土!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]我实在是看不过眼,所以我才决定把这个具备稀有勇者体质的家伙培养成新一任魔王!\n来让这个世界的势力重新洗牌!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]你觉得一个满脑子想着回家种田的废柴勇者会成为改变世界的魔王?你晃晃脑袋试试,是不是能听到大海的声音?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]恼人至极的妖精族!呵呵呵……我干脆一不做二不休,连你也一块收拾了吧!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]别小瞧咱!咱好歹也是妖精族里实力数一数二的存在!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]只会耍嘴皮子的恼人苍蝇!我倒要看看一块焦炭会不会说话!\n——招雷弹!!", + { + "type": "animate", + "name": "yongchang", + "loc": [ + 4, + 3 + ] + }, + { + "type": "animate", + "name": "yongchang", + "loc": [ + 4, + 6 + ] + }, + { + "type": "animate", + "name": "yongchang", + "loc": [ + 8, + 6 + ] + }, + { + "type": "animate", + "name": "yongchang", + "loc": [ + 8, + 3 + ] + }, + { + "type": "playSound", + "name": "attack.mp3" + }, + { + "type": "animate", + "name": "thunder", + "loc": [ + 6, + 6 + ] + }, + { + "type": "sleep", + "time": 500 + }, + "\t[小妖精,fairy]切,这点伤痛跟他刚才经历的身心地狱相比根本就不算什么。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]哼!翅膀都被烧焦了还要嘴硬?你难不成真以为我不会对你动真格?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……你这混蛋!给我离她远点!!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]!…你现在受了很严重的致命伤,乱动什么?\n乖。别怕,这里有咱顶着!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]对了,咱再问你一遍,你是很珍惜自己性命的对吧?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]!…等等…妖精小姐,你不会是……?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]喂,黑暗大法师,你作为魔塔里最博学多识的蠢货,应该对咱妖精族的特殊能力再清楚不过吧?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]什么?!难不成你是想!!不可能……\n就为了一个渺小的人类,不可理喻!!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]哼哼哼!你害怕的表情可真美味!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]不过比起这个,咱更期待你吃到“妖精自灭冲击”之后的死状哦!~", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[blackMagician]不!!不应该是这样的!我完美的计划竟然会被一只小小的妖精破坏!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]不要!……千万不要!……为了我这种人……唔!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]笨蛋,动都动不了了就不要强撑着站起来了啊。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]真是的,都到最后一刻了,你这家伙好歹也让咱省点心吧。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[小妖精,fairy]那么,再见了……我的勇者大人。", + { + "type": "move", + "time": 700, + "steps": [ + { + "direction": "up", + "value": 3 + } + ] + }, + { + "type": "playSound", + "name": "attack.mp3" + }, + { + "type": "sleep", + "time": 200 + }, + "\t[blackMagician]不可能!!!!!", + { + "type": "hide", + "loc": [ + 6, + 3 + ] + }, + { + "type": "hide", + "loc": [ + 4, + 3 + ] + }, + { + "type": "hide", + "loc": [ + 4, + 6 + ] + }, + { + "type": "hide", + "loc": [ + 8, + 6 + ] + }, + { + "type": "hide", + "loc": [ + 8, + 3 + ] + }, + { + "type": "changeFloor", + "floorId": "sample3", + "loc": [ + 6, + 6 + ], + "direction": "up", + "time": 1000 + }, + { + "type": "show", + "loc": [ + 6, + 5 + ] + }, + { + "type": "sleep", + "time": 200 + }, + { + "type": "playSound", + "name": "item.mp3" + }, + { + "type": "sleep", + "time": 200 + }, + "\t[hero]…妖精…小姐……", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……妖精小姐!", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]是梦吗?……不对,为什么我在流泪?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]这颗漂亮的宝石是……?", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]我全都想起来了……妖精小姐为了我……\n牺牲了自己的性命。", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]在这颗宝石上,我能感受到你的温度……\n熟悉而又令人安心,这就是你最后留给我的东西吗……", + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]好温暖……", + { + "type": "setValue", + "name": "item:yellowJewel", + "value": "1" + }, + { + "type": "hide", + "loc": [ + 6, + 5 + ] + }, + { + "type": "playSound", + "name": "item.mp3" + }, + "\t[hero]……", + { + "type": "openDoor", + "loc": [ + 6, + 2 + ] + }, + { + "type": "openDoor", + "loc": [ + 6, + 11 + ] + } + ] + }, + "6,5": { + "enable": false, + "data": [] + } + }, + "changeFloor": { + "6,0": { + "floorId": "sample3", + "stair": "upFloor" + }, + "6,12": { + "floorId": "sample2", + "stair": "upFloor" + } + }, + "afterBattle": { + "6,4": [ + "\t[blackMagician]天真!你以为这样就能战胜我吗?", + { + "type": "show", "loc": [ - 6, - 2 - ] + 7, + 5 + ], + "time": 500 }, { - "type": "openDoor", + "type": "update" + } + ], + "7,5": [ + "\t[blackMagician]你打败的不过是我众多分身中的其中一个而已。", + { + "type": "show", + "loc": [ + 5, + 4 + ], + "time": 500 + }, + { + "type": "update" + } + ], + "5,4": [ + "\t[blackMagician]你的身体已经伤痕累累了,可我还留有着九成多的魔力。", + { + "type": "show", + "loc": [ + 5, + 5 + ], + "time": 500 + }, + { + "type": "update" + } + ], + "5,5": [ + "\t[blackMagician]顽固的家伙!放弃抵抗吧!", + { + "type": "show", + "loc": [ + 7, + 4 + ], + "time": 500 + }, + { + "type": "update" + } + ], + "7,4": [ + "\t[blackMagician]哈哈哈哈!我的灵魂远比你想象的强大!\n我即是永恒!", + { + "type": "show", "loc": [ 6, - 11 + 3 + ], + "time": 500 + }, + { + "type": "trigger", + "loc": [ + 6, + 3 ] } ] }, - "6,5": { - "enable": false, - "data": [] - } -}, -"changeFloor": { - "6,0": { - "floorId": "sample3", - "stair": "upFloor" - }, - "6,12": { - "floorId": "sample2", - "stair": "upFloor" - } -}, -"afterBattle": { - "6,4": [ - "\t[blackMagician]天真!你以为这样就能战胜我吗?", - { - "type": "show", - "loc": [ - 7, - 5 - ], - "time": 500 - }, - { - "type": "update" - } - ], - "7,5": [ - "\t[blackMagician]你打败的不过是我众多分身中的其中一个而已。", - { - "type": "show", - "loc": [ - 5, - 4 - ], - "time": 500 - }, - { - "type": "update" - } - ], - "5,4": [ - "\t[blackMagician]你的身体已经伤痕累累了,可我还留有着九成多的魔力。", - { - "type": "show", - "loc": [ - 5, - 5 - ], - "time": 500 - }, - { - "type": "update" - } - ], - "5,5": [ - "\t[blackMagician]顽固的家伙!放弃抵抗吧!", - { - "type": "show", - "loc": [ - 7, - 4 - ], - "time": 500 - }, - { - "type": "update" - } - ], - "7,4": [ - "\t[blackMagician]哈哈哈哈!我的灵魂远比你想象的强大!\n我即是永恒!", - { - "type": "show", - "loc": [ - 6, - 3 - ], - "time": 500 - }, - { - "type": "trigger", - "loc": [ - 6, - 3 - ] - } - ] -}, -"afterGetItem": {}, -"afterOpenDoor": {}, -"cannotMove": {}, -"bgmap": [ + "afterGetItem": {}, + "afterOpenDoor": {}, + "cannotMove": {}, + "bgmap": [ ], -"fgmap": [ + "fgmap": [ ], + "width": 13, + "height": 13, + "autoEvent": {} } \ No newline at end of file diff --git a/project/functions.js b/project/functions.js index 195e0a0b..cf38a8c7 100644 --- a/project/functions.js +++ b/project/functions.js @@ -29,7 +29,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 初始化怪物和道具 core.material.enemys = core.enemys.getEnemys(); core.material.items = core.items.getItems(); - core.items._resetItems(); // 初始化全局数值和全局开关 core.values = core.clone(core.data.values); for (var key in values || {}) @@ -112,6 +111,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 重置画布尺寸 core.maps.resizeMap(floorId); // 设置勇士的位置 + heroLoc.direction = core.turnDirection(heroLoc.direction); core.status.hero.loc = heroLoc; // 检查重生怪并重置 if (!fromLoad) { @@ -273,7 +273,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 删除该块 var guards = []; // 支援 if (x != null && y != null) { - core.removeBlock(x, y); + // 检查是否是重生怪物;如果是则仅隐藏不删除 + if (core.hasSpecial(enemy.special, 23)) { + core.hideBlock(x, y); + } else { + core.removeBlock(x, y); + } guards = core.getFlag("__guards__" + x + "_" + y, []); core.removeFlag("__guards__" + x + "_" + y); } @@ -381,6 +386,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 开一个门后触发的事件 var todo = []; + // 检查该点的获得开门后事件。 var event = core.floors[core.status.floorId].afterOpenDoor[x + "," + y]; if (event) core.unshift(todo, event); @@ -393,21 +399,22 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (callback) callback(); }, - "afterGetItem": function (itemId, x, y, callback) { + "afterGetItem": function (itemId, x, y, isGentleClick, callback) { // 获得一个道具后触发的事件 + // itemId:获得的道具ID;x和y是该道具所在的坐标 + // isGentleClick:是否是轻按触发的 core.playSound('item.mp3'); var todo = []; + // 检查该点的获得道具后事件。 var event = core.floors[core.status.floorId].afterGetItem[x + "," + y]; - if (event) core.unshift(todo, event); + if (event && (event instanceof Array || !isGentleClick || !event.disableOnGentleClick)) { + core.unshift(todo, event); + } if (todo.length > 0) core.insertAction(todo, x, y); if (callback) callback(); -}, - "afterChangeLight": function(x,y) { - // 改变亮灯之后,可以触发的事件 - }, "afterPushBox": function () { // 推箱子后的事件 @@ -422,25 +429,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = */ } }, - "afterPassNet": function (x, y, id) { - // 经过特殊地形后的事件;x和y为当前坐标,id为当前的图块id - - // 这是个一次性血网的例子 - // if (id == 'lavaNet') core.removeBlock(x, y); - -}, - "canUseQuickShop": function(shopId) { - // 当前能否使用某个快捷商店 - // shopId:快捷商店ID - // 如果返回一个字符串,表示不能,字符串为不能使用的提示 - // 返回null代表可以使用 - - // 检查当前楼层的canUseQuickShop选项是否为false - if (core.status.thisMap.canUseQuickShop === false) - return '当前楼层不能使用快捷商店。'; - - return null; -} }, "enemys": { "getSpecials": function () { @@ -453,7 +441,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = [3, "坚固", "勇士每回合最多只能对怪物造成1点伤害"], [4, "2连击", "怪物每回合攻击2次"], [5, "3连击", "怪物每回合攻击3次"], - [6, function (enemy) { return (enemy.n || 4) + "连击"; }, function (enemy) { return "怪物每回合攻击" + (enemy.n || 4) + "次"; }], + [6, function (enemy) { return (enemy.n || '') + "连击"; }, function (enemy) { return "怪物每回合攻击" + (enemy.n || 4) + "次"; }], [7, "破甲", "战斗前,怪物附加角色防御的" + Math.floor(100 * core.values.breakArmor || 0) + "%作为伤害"], [8, "反击", "战斗时,怪物每回合附加角色攻击的" + Math.floor(100 * core.values.counterAttack || 0) + "%作为伤害,无视角色防御"], [9, "净化", "战斗前,怪物附加勇士护盾的" + core.values.purify + "倍作为伤害"], @@ -834,22 +822,14 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = break; case 49: // 快捷键1: 破 if (core.hasItem('pickaxe')) { - if (core.canUseItem('pickaxe')) { - core.status.route.push("key:49"); // 将按键记在录像中 - core.useItem('pickaxe', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 - } else { - core.drawTip('当前不能使用破墙镐'); - } + core.status.route.push("key:49"); // 将按键记在录像中 + core.useItem('pickaxe', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 } break; case 50: // 快捷键2: 炸 if (core.hasItem('bomb')) { - if (core.canUseItem('bomb')) { - core.status.route.push("key:50"); // 将按键记在录像中 - core.useItem('bomb', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 - } else { - core.drawTip('当前不能使用炸弹'); - } + core.status.route.push("key:50"); // 将按键记在录像中 + core.useItem('bomb', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 } break; case 51: // 快捷键3: 飞 @@ -943,17 +923,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = 'maps': core.maps.saveMap(), 'route': core.encodeRoute(core.status.route), 'values': values, - 'shops': {}, 'version': core.firstData.version, "time": new Date().getTime() }; - // 设置商店次数 - for (var shopId in core.status.shops) { - data.shops[shopId] = { - 'times': core.status.shops[shopId].times || 0, - 'visited': core.status.shops[shopId].visited || false - }; - } return data; }, @@ -963,13 +935,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 重置游戏和路线 core.resetGame(data.hero, data.hard, data.floorId, core.maps.loadMap(data.maps), data.values); core.status.route = core.decodeRoute(data.route); - // 加载商店信息 - for (var shopId in core.status.shops) { - if (data.shops[shopId]) { - core.status.shops[shopId].times = data.shops[shopId].times; - core.status.shops[shopId].visited = data.shops[shopId].visited; - } - } // 文字属性,全局属性 core.status.textAttribute = core.getFlag('textAttribute', core.status.textAttribute); var toAttribute = core.getFlag('globalAttribute', core.status.globalAttribute); @@ -1103,10 +1068,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = id = block.event.id, enemy = core.material.enemys[id]; + type[loc] = type[loc] || {}; + // 血网 - if (id == 'lavaNet' && block.event.trigger == 'passNet' && !core.hasItem('shoes')) { + if (id == 'lavaNet' && !core.hasItem('shoes')) { damage[loc] = (damage[loc] || 0) + core.values.lavaDamage; - type[loc] = "血网伤害"; + type[loc]["血网伤害"] = true; } // 领域 @@ -1128,7 +1095,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 如果是十字领域,则还需要满足 |dx|+|dy|<=range if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); - type[currloc] = "领域伤害"; + type[currloc] = type[currloc] || {}; + type[currloc]["领域伤害"] = true; } } } @@ -1142,9 +1110,10 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = currloc = nx + "," + ny; if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); - type[currloc] = "阻击伤害"; + type[currloc] = type[currloc] || {}; + type[currloc]["阻击伤害"] = true; - var rdir = core.reverseDirection(dir); + var rdir = core.turnDirection(":back", dir); // 检查下一个点是否存在事件(从而判定是否移动) var rnx = x + core.utils.scan[rdir].x, rny = y + core.utils.scan[rdir].y; @@ -1163,14 +1132,16 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = var currloc = nx + "," + y; if (nx != x) { damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); - type[currloc] = "激光伤害"; + type[currloc] = type[currloc] || {}; + type[currloc]["激光伤害"] = true; } } for (var ny = 0; ny < height; ny++) { var currloc = x + "," + ny; if (ny != y) { damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); - type[currloc] = "激光伤害"; + type[currloc] = type[currloc] || {}; + type[currloc]["激光伤害"] = true; } } } @@ -1230,7 +1201,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } if (value > 0) { damage[loc] = (damage[loc] || 0) + value; - type[loc] = "夹击伤害"; + type[loc] = type[loc] || {}; + type[loc]["夹击伤害"] = true; } } } @@ -1269,9 +1241,18 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.status.hero.hp = 0; core.updateStatusBar(); core.events.lose(); - return; + } else { + core.updateStatusBar(); } } + + // 从v2.7开始,每一步行走不会再刷新状态栏。 + // 如果有特殊要求(如每走一步都加buff之类),可手动取消注释下面这一句: + // core.updateStatusBar(true); + + // 检查自动事件 + core.checkAutoEvents(); + // 如需强行终止行走可以在这里条件判定: // core.stopAutomaticRoute(); }, diff --git a/project/items.js b/project/items.js index 1244844e..0a68a3f0 100644 --- a/project/items.js +++ b/project/items.js @@ -370,11 +370,11 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "book": "core.ui.drawBook(0);", "fly": "core.ui.drawFly(core.floorIds.indexOf(core.status.floorId));", "earthquake": "(function () {\n\tvar indexes = [];\n\tfor (var index in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[index];\n\t\tif (!block.disable && block.event.canBreak) {\n\t\t\tindexes.push(index);\n\t\t}\n\t}\n\tcore.removeBlockByIndexes(indexes);\n\tcore.drawMap(core.status.floorId, function () {\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t});\n})();", - "pickaxe": "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.block.disable) return false;\n\t\treturn block.block.event.canBreak;\n\t};\n\n\tcore.playSound('pickaxe.mp3');\n\tif (core.flags.pickaxeFourDirections) {\n\t\t// 四方向破\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (canBreak(nx, ny)) {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// 仅破当前\n\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t}\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", + "pickaxe": "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.block.disable) return false;\n\t\treturn block.block.event.canBreak;\n\t};\n\n\tvar success = false;\n\tvar pickaxeFourDirections = false; // 是否四方向破;如果是将其改成true\n\tif (pickaxeFourDirections) {\n\t\t// 四方向破\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (canBreak(nx, ny)) {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// 仅破当前\n\t\tif (canBreak(core.nextX(), core.nextY())) {\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\tif (success) {\n\t\tcore.playSound('pickaxe.mp3');\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t} else {\n\t\t// 无法使用\n\t\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n})();", "icePickaxe": "(function () {\n\tcore.removeBlock(core.nextX(), core.nextY());\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", - "snow": "(function () {\n\tif (core.flags.snowFourDirections) {\n\t\t// 四方向雪花\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (core.getBlockId(nx, ny) == 'lava') {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t}\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", + "snow": "(function () {\n\tvar success = false;\n\n\tvar snowFourDirections = false; // 是否四方向雪花;如果是将其改成true\n\tif (snowFourDirections) {\n\t\t// 四方向雪花\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (core.getBlockId(nx, ny) == 'lava') {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (core.getBlockId(core.nextX(), core.nextY()) == 'lava') {\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\tif (success) {\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t} else {\n\t\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n})();", "bigKey": "(function () {\n\tvar actions = core.searchBlock(\"yellowDoor\").map(function (block) {\n\t\treturn { \"type\": \"openDoor\", \"loc\": [block.x, block.y], \"async\": true };\n\t});\n\tactions.push({ \"type\": \"waitAsync\" });\n\tactions.push({ \"type\": \"tip\", \"text\": core.material.items[itemId].name + \"使用成功\" });\n\tcore.insertAction(actions);\n})();", - "bomb": "(function () {\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.block.disable || block.block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tcore.playSound('bomb.mp3');\n\tvar bombList = []; // 炸掉的怪物坐标列表\n\tif (core.flags.bombFourDirections) {\n\t\t// 四方向炸\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (canBomb(nx, ny)) {\n\t\t\t\tbombList.push([nx, ny]);\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// 仅炸当前\n\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\tbombList.push([core.nextX(), core.nextY()]);\n\t}\n\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t// 炸弹后事件\n\t// 这是一个使用炸弹也能开门的例子\n\t/*\n\tif (core.status.floorId=='xxx' && core.terrainExists(x0,y0,'specialDoor') // 某个楼层,该机关门存在\n\t\t&& !core.enemyExists(x1,y1) && !core.enemyExists(x2,y2)) // 且守门的怪物都不存在\n\t{\n\t\tcore.insertAction([ // 插入事件\n\t\t\t{\"type\": \"openDoor\", \"loc\": [x0,y0]} // 开门\n\t\t])\n\t}\n\t*/\n})();", + "bomb": "(function () {\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.block.disable || block.block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tvar bombList = []; // 炸掉的怪物坐标列表\n\tvar bombFourDirections = false; // 是否四方向可炸;如果是将其改成true。\n\tif (bombFourDirections) {\n\t\t// 四方向炸\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (canBomb(nx, ny)) {\n\t\t\t\tbombList.push([nx, ny]);\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// 仅炸当前\n\t\tif (canBomb(core.nextX(), core.nextY())) {\n\t\t\tbombList.push([core.nextX(), core.nextY()]);\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t}\n\t}\n\n\tif (bombList.length > 0) {\n\t\tcore.playSound('bomb.mp3');\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t} else {\n\t\tcore.drawTip('当前无法使用' + core.material.items[itemId].name);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n\n\t// 炸弹后事件\n\t// 这是一个使用炸弹也能开门的例子\n\t/*\n\tif (core.status.floorId=='xxx' && core.terrainExists(x0,y0,'specialDoor') // 某个楼层,该机关门存在\n\t\t&& !core.enemyExists(x1,y1) && !core.enemyExists(x2,y2)) // 且守门的怪物都不存在\n\t{\n\t\tcore.insertAction([ // 插入事件\n\t\t\t{\"type\": \"openDoor\", \"loc\": [x0,y0]} // 开门\n\t\t])\n\t}\n\t*/\n})();", "centerFly": "core.playSound('centerFly.mp3');\ncore.clearMap('hero');\ncore.setHeroLoc('x', core.bigmap.width - 1 - core.getHeroLoc('x'));\ncore.setHeroLoc('y', core.bigmap.height - 1 - core.getHeroLoc('y'));\ncore.drawHero();\ncore.drawTip(core.material.items[itemId].name + '使用成功');", "upFly": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) + 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();", "downFly": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) - 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();", @@ -397,14 +397,14 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "canUseItemEffect": { "book": "true", "fly": "(function () {\n\treturn core.status.maps[core.status.floorId].canFlyTo;\n})();", - "pickaxe": "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.block.disable) return false;\n\t\treturn block.block.event.canBreak;\n\t};\n\n\tif (core.flags.pickaxeFourDirections) {\n\t\t// 四方向破\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tif (canBreak(core.getHeroLoc('x') + delta.x, core.getHeroLoc('y') + delta.y)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} else {\n\t\t// 仅破当前\n\t\treturn canBreak(core.nextX(), core.nextY());\n\t}\n})();", + "pickaxe": "true", "icePickaxe": "(function () {\n\treturn core.getBlockId(core.nextX(), core.nextY()) == 'ice';\n})();", - "bomb": "(function () {\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.block.disable || block.block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tif (core.flags.bombFourDirections) {\n\t\t// 四方向炸\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tif (canBomb(core.getHeroLoc('x') + delta.x, core.getHeroLoc('y') + delta.y)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} else {\n\t\t// 仅炸当前\n\t\treturn canBomb(core.nextX(), core.nextY());\n\t}\n})();", + "bomb": "true", "earthquake": "(function () {\n\treturn core.status.thisMap.blocks.filter(function (block) {\n\t\treturn !block.disable && block.event.canBreak;\n\t}).length > 0;\n})();", "centerFly": "(function () {\n\tvar toX = core.bigmap.width - 1 - core.getHeroLoc('x'),\n\t\ttoY = core.bigmap.height - 1 - core.getHeroLoc('y');\n\tvar id = core.getBlockId(toX, toY);\n\treturn id == null;\n})();", "upFly": "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index < core.floorIds.length - 1) {\n\t\tvar toId = core.floorIds[index + 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();", "downFly": "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index > 0) {\n\t\tvar toId = core.floorIds[index - 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();", - "snow": "(function () {\n\tif (core.flags.snowFourDirections) {\n\t\t// 四方向雪花\n\t\tfor (var direction in core.utils.scan) {\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tif (core.getBlockId(core.getHeroLoc('x') + delta.x, core.getHeroLoc('y') + delta.y) == 'lava') {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} else {\n\t\treturn core.getBlockId(core.nextX(), core.nextY()) == 'lava';\n\t}\n})();", + "snow": "true", "bigKey": "(function () {\n\treturn core.searchBlock('yellowDoor').length > 0;\n})();", "poisonWine": "core.hasFlag('poison');", "weakWine": "core.hasFlag('weak');", diff --git a/project/maps.js b/project/maps.js index 4077be1f..b21de5b9 100644 --- a/project/maps.js +++ b/project/maps.js @@ -3,17 +3,17 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "1": {"cls":"animates","id":"yellowWall","canBreak":true,"animate":1,"doorInfo":[null,"door.mp3","door.mp3"]}, "2": {"cls":"animates","id":"whiteWall","canBreak":true,"animate":1,"doorInfo":[null,"door.mp3","door.mp3"]}, "3": {"cls":"animates","id":"blueWall","canBreak":true,"animate":1,"doorInfo":[null,"door.mp3","door.mp3"]}, - "4": {"cls":"animates","id":"star"}, - "5": {"cls":"animates","id":"lava"}, + "4": {"cls":"animates","id":"star","name":"星空"}, + "5": {"cls":"animates","id":"lava","name":"岩浆"}, "6": {"cls":"terrains","id":"ice"}, "7": {"cls":"terrains","id":"blueShopLeft"}, "8": {"cls":"terrains","id":"blueShopRight"}, "9": {"cls":"terrains","id":"pinkShopLeft"}, "10": {"cls":"terrains","id":"pinkShopRight"}, - "11": {"cls":"animates","id":"lavaNet","canPass":true,"trigger":"passNet"}, - "12": {"cls":"animates","id":"poisonNet","canPass":true,"trigger":"passNet"}, - "13": {"cls":"animates","id":"weakNet","canPass":true,"trigger":"passNet"}, - "14": {"cls":"animates","id":"curseNet","canPass":true,"trigger":"passNet"}, + "11": {"cls":"animates","id":"lavaNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 血网的伤害效果移动到 checkBlock 中处理\n\n\t// 如果要做一次性血网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();","name":"血网"}, + "12": {"cls":"animates","id":"poisonNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 直接插入公共事件进行毒处理\n\tif (!core.hasItem('shoes')) {\n\t\tcore.insertAction({ \"type\": \"insert\", \"name\": \"毒衰咒处理\", \"args\": [0] });\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"毒网"}, + "13": {"cls":"animates","id":"weakNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 直接插入公共事件进行衰处理\n\tif (!core.hasItem('shoes')) {\n\t\tcore.insertAction({ \"type\": \"insert\", \"name\": \"毒衰咒处理\", \"args\": [1] });\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"衰网"}, + "14": {"cls":"animates","id":"curseNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 直接插入公共事件进行咒处理\n\tif (!core.hasItem('shoes')) {\n\t\tcore.insertAction({ \"type\": \"insert\", \"name\": \"毒衰咒处理\", \"args\": [2] });\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"咒网"}, "15": {"cls":"animates","id":"blueWater"}, "16": {"cls":"animates","id":"water"}, "20": {"cls":"autotile","id":"autotile"}, @@ -68,12 +68,12 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "71": {"cls":"items","id":"shield0"}, "72": {"cls":"items","id":"skill1"}, "73": {"cls":"items","id":"wand"}, - "81": {"cls":"animates","id":"yellowDoor","trigger":"openDoor","animate":1,"doorInfo":[{"yellowKey":1},"door.mp3","door.mp3"]}, - "82": {"cls":"animates","id":"blueDoor","trigger":"openDoor","animate":1,"doorInfo":[{"blueKey":1},"door.mp3","door.mp3"]}, - "83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":[{"redKey":1},"door.mp3","door.mp3"]}, - "84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":[{"greenKey":1},"door.mp3","door.mp3"]}, - "85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":[null,"door.mp3","door.mp3"]}, - "86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":[{"steelKey":1},"door.mp3","door.mp3"]}, + "81": {"cls":"animates","id":"yellowDoor","trigger":"openDoor","animate":1,"doorInfo":[{"yellowKey":1},"door.mp3","door.mp3"],"name":"黄门"}, + "82": {"cls":"animates","id":"blueDoor","trigger":"openDoor","animate":1,"doorInfo":[{"blueKey":1},"door.mp3","door.mp3"],"name":"蓝门"}, + "83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":[{"redKey":1},"door.mp3","door.mp3"],"name":"红门"}, + "84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":[{"greenKey":1},"door.mp3","door.mp3"],"name":"绿门"}, + "85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":[null,"door.mp3","door.mp3"],"name":"机关门"}, + "86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":[{"steelKey":1},"door.mp3","door.mp3"],"name":"铁门"}, "87": {"cls":"terrains","id":"upFloor","canPass":true}, "88": {"cls":"terrains","id":"downFloor","canPass":true}, "89": {"cls":"animates","id":"portal","canPass":true}, @@ -109,7 +109,7 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "162": {"cls":"terrains","id":"arrowDown","canPass":true,"cannotOut":["left","right","up"],"cannotIn":["down"]}, "163": {"cls":"terrains","id":"arrowLeft","canPass":true,"cannotOut":["up","down","right"],"cannotIn":["left"]}, "164": {"cls":"terrains","id":"arrowRight","canPass":true,"cannotOut":["up","down","left"],"cannotIn":["right"]}, - "165": {"cls":"terrains","id":"light","trigger":"changeLight","canPass":true}, + "165": {"cls":"terrains","id":"light","trigger":"null","canPass":true,"script":"(function () {\n\tcore.setBlock(core.getNumberById('darkLight'), core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();"}, "166": {"cls":"terrains","id":"darkLight"}, "167": {"cls":"terrains","id":"ski","trigger":"ski","canPass":true}, "168": {"cls":"terrains","id":"flower","canPass":true}, diff --git a/project/plugins.js b/project/plugins.js index 3f25e0f6..b8a4776e 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -39,7 +39,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = // core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。 // core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 // core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 - // core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,且40%后才开始衰减。 + // core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,40%后才开始衰减。 this.drawLight = function (name, color, lights, lightDec) { // 清空色调层;也可以修改成其它层比如animate/weather层,或者用自己创建的canvas @@ -84,6 +84,142 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = ctx.globalCompositeOperation = 'source-over'; // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); } +}, + "shop": function () { + // 【全局商店】相关的功能 + // + // 打开一个全局商店 + // shopId:要打开的商店id;noRoute:是否不计入录像 + this.openShop = function (shopId, noRoute) { + var shop = core.status.shops[shopId]; + // Step 1: 检查能否打开此商店 + if (!this.canOpenShop(shopId)) { + core.drawTip("该商店尚未开启"); + return false; + } + + // Step 2: (如有必要)记录打开商店的脚本事件 + if (!noRoute) { + core.status.route.push("shop:" + shopId); + } + + // Step 3: 检查道具商店 or 公共事件 + if (shop.item) { + if (core.openItemShop) { + core.openItemShop(shopId); + } else { + core.insertAction("道具商店插件不存在!请检查是否存在该插件!"); + } + return; + } + if (shop.commonEvent) { + core.insertAction({ "type": "insert", "name": shop.commonEvent, "args": shop.args }); + return; + } + + // Step 4: 执行标准公共商店 + core.insertAction(this._convertShop(shop)); + return true; + } + + ////// 将一个全局商店转变成可预览的公共事件 ////// + this._convertShop = function (shop) { + return [{ + "type": "while", + "condition": "true", + "data": [ + // 检测能否访问该商店 + { + "type": "if", + "condition": "core.isShopVisited('" + shop.id + "')", + "true": [ + // 可以访问,直接插入执行效果 + { "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', false) }" }, + ], + "false": [ + // 不能访问的情况下:检测能否预览 + { + "type": "if", + "condition": shop.disablePreview, + "true": [ + // 不可预览,提示并退出 + "当前无法访问该商店!", + { "type": "break" }, + ], + "false": [ + // 可以预览:将商店全部内容进行替换 + { "type": "tip", "text": "当前处于预览模式,不可购买" }, + { "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', true) }" }, + ] + } + ] + } + ] + }]; + } + + this._convertShop_replaceChoices = function (shopId, previewMode) { + var shop = core.status.shops[shopId]; + var choices = (shop.choices || []).filter(function (choice) { + if (choice.condition == null || choice.condition == '') return true; + try { return core.calValue(choice.condition); } catch (e) { return true; } + }).map(function (choice) { + var ableToBuy = core.calValue(choice.need); + return { + "text": choice.text, + "icon": choice.icon, + "color": ableToBuy && !previewMode ? choice.color : [153, 153, 153, 1], + "action": ableToBuy && !previewMode ? choice.action : [ + { "type": "tip", "text": previewMode ? "预览模式下不可购买" : "购买条件不足" } + ] + }; + }).concat({ "text": "离开", "action": [{ "type": "break" }] }); + core.insertAction({ "type": "choices", "text": shop.text, "choices": choices }); + } + + /// 是否访问过某个快捷商店 + this.isShopVisited = function (id) { + if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {}); + var shops = core.getFlag("__shops__"); + if (!shops[id]) shops[id] = {}; + return shops[id].visited; + } + + /// 当前应当显示的快捷商店列表 + this.listShopIds = function () { + return Object.keys(core.status.shops).filter(function (id) { + return core.isShopVisited(id) || !core.status.shops[id].mustEnable; + }); + } + + /// 是否能够打开某个商店 + this.canOpenShop = function (id) { + if (this.isShopVisited(id)) return true; + var shop = core.status.shops[id]; + if (shop.item || shop.commonEvent || shop.mustEnable) return false; + return true; + } + + /// 启用或禁用某个快捷商店 + this.setShopVisited = function (id, visited) { + if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {}); + var shops = core.getFlag("__shops__"); + if (!shops[id]) shops[id] = {}; + if (visited) shops[id].visited = true; + else delete shops[id].visited; + } + + /// 能否使用快捷商店 + this.canUseQuickShop = function (id) { + // 如果返回一个字符串,表示不能,字符串为不能使用的提示 + // 返回null代表可以使用 + + // 检查当前楼层的canUseQuickShop选项是否为false + if (core.status.thisMap.canUseQuickShop === false) + return '当前楼层不能使用快捷商店。'; + return null; + } + }, "itemShop": function () { // 道具商店相关的插件 @@ -97,6 +233,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = var totalPage = 0; var totalMoney = 0; var list = []; + var shopInfo = null; // 商店信息 + var choices = []; // 商店选项 var bigFont = core.ui._buildFont(20, false), middleFont = core.ui._buildFont(18, false); @@ -107,16 +245,15 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = // Step 1: 背景和固定的几个文字 core.ui._createUIEvent(); core.clearMap('uievent'); - core.ui._uievent_drawSelector({ "code": 1 }); - core.ui._uievent_drawSelector({ "code": 2 }); + core.ui._clearUIEventSelector([1, 2]); core.setTextAlign('uievent', 'left'); core.setTextBaseline('uievent', 'top'); core.fillRect('uievent', 0, 0, 416, 416, 'black'); - core.ui._uievent_drawBackground({ background: 'winskin.png', x: 0, y: 0, width: 416, height: 56 }); - core.ui._uievent_drawBackground({ background: 'winskin.png', x: 0, y: 56, width: 312, height: 56 }); - core.ui._uievent_drawBackground({ background: 'winskin.png', x: 0, y: 112, width: 312, height: 304 }); - core.ui._uievent_drawBackground({ background: 'winskin.png', x: 312, y: 56, width: 104, height: 56 }); - core.ui._uievent_drawBackground({ background: 'winskin.png', x: 312, y: 112, width: 104, height: 304 }); + core.drawWindowSkin('winskin.png', 'uievent', 0, 0, 416, 56); + core.drawWindowSkin('winskin.png', 'uievent', 0, 56, 312, 56); + core.drawWindowSkin('winskin.png', 'uievent', 0, 112, 312, 304); + core.drawWindowSkin('winskin.png', 'uievent', 312, 56, 104, 56); + core.drawWindowSkin('winskin.png', 'uievent', 312, 112, 104, 304); core.setFillStyle('uievent', 'white'); core.setStrokeStyle('uievent', 'white'); core.fillText("uievent", "购买", 32, 74, 'white', bigFont); @@ -143,9 +280,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } // Step 2:获得列表并展示 - var choices = core.status.shops[shopId].choices; list = choices.filter(function (one) { - if (one.condition != null) { + if (one.condition != null && one.condition != '') { try { if (!core.calValue(one.condition)) return false; } catch (e) {} } return (type == 0 && one.money != null) || (type == 1 && one.sell != null); @@ -369,6 +505,10 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = page = 0; selectItem = null; selectCount = 0; + shopInfo = flags.__shops__[shopId]; + if (shopInfo.choices == null) shopInfo.choices = core.clone(core.status.shops[shopId].choices); + choices = shopInfo.choices; + core.insertAction([{ "type": "while", "condition": "true", @@ -378,44 +518,13 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { "type": "function", "function": "function() { core.performItemShopAction(); }" } ] }, - { "type": "function", "function": "function () { " + - "core.deleteCanvas('uievent'); " + - "core.ui._uievent_drawSelector({ \"code\": 1 }); " + - "core.ui._uievent_drawSelector({ \"code\": 2 }); " + - "}" } + { + "type": "function", + "function": "function () { core.deleteCanvas('uievent'); core.ui._clearUIEventSelector([1, 2]); }" + } ]); } - // Write item number to save - core.control.saveData = function () { - var data = this.controldata.saveData(); - for (var shopId in core.status.shops) { - if (core.status.shops[shopId].item) { - data.shops[shopId].choices = core.status.shops[shopId].choices.map(function (t) { - return { - number: t.number, - money_count: t.money_count || 0, - sell_count: t.sell_count || 0 - } - }); - } - } - return data; - } - - core.control.loadData = function (data, callback) { - this.controldata.loadData(data, callback); - for (var shopId in data.shops) { - if (data.shops[shopId].choices) { - for (var i = 0; i < data.shops[shopId].choices.length; ++i) { - core.status.shops[shopId].choices[i].number = data.shops[shopId].choices[i].number; - core.status.shops[shopId].choices[i].money_count = data.shops[shopId].choices[i].money_count; - core.status.shops[shopId].choices[i].sell_count = data.shops[shopId].choices[i].sell_count; - } - } - } - } - }, "smoothCamera": function () { diff --git a/styles.css b/styles.css index 255fd81b..dec14381 100644 --- a/styles.css +++ b/styles.css @@ -295,22 +295,27 @@ p#name { #bg { z-index: 10; + image-rendering: pixelated; } #event { z-index: 30; + image-rendering: pixelated; } #hero { z-index: 40; + image-rendering: pixelated; } #event2 { z-index: 50; + image-rendering: pixelated; } #fg { z-index: 60; + image-rendering: pixelated; } #damage { diff --git a/v2.x-final更新.txt b/v2.x-final更新.txt index 337fc4d2..f996dd5a 100644 --- a/v2.x-final更新.txt +++ b/v2.x-final更新.txt @@ -3,7 +3,7 @@ (已完成!) 3. 请求修复(如果还没修复)core.drawTip()不能使用系统图标和clear参数的bug,建议增加一个参数来表示多帧图块绘制第几帧 (已完成!) 3.1 (“UI绘制”类事件中的“绘制图标”同理) (已完成!\b[up,null]可实现此效果) 4. 建议显示文章的\b[]提供top和bottom这两种新写法来实现在顶部或底部显示对话框,目前只有startText、firstArrive、eachArrive、useItemEvent和公共事件等没有当前点的事件中才能用up和down来临时实现这种效果,否则就得设置剧情文本的属性,这在频繁来回切换时是致命的,就像《无上之光》RMXP版地上40层一样 -5. 建议优化core.splitLines()来避免把单词打断和触犯标点禁则,如点号和右标号禁止用于行首,左标号禁止用于行尾 +(已完成!) 5. 建议优化core.splitLines()来避免把单词打断和触犯标点禁则,如点号和右标号禁止用于行首,左标号禁止用于行尾 (已完成!) 6. core.drawAnimate()和对应的事件,建议增加一个boolean型参数来表示坐标是绝对坐标(即当前的实现)还是在视野中的相对坐标(以13*13为例相对坐标总是在0~12之间),以实现形如“在视野正中心播放某某动画”的需求(小地图可以写6,6,大地图怎么办) (已完成!) 7. 建议把core.playBgm(bgm, startTime)的startTime参数在事件中也提供出来,以用于一些演出(比如适当的剧情处直接从高潮开始)。同理可以让“暂停背景音乐”记录下当前播放到了第几秒,并在“恢复背景音乐”中自动从这个秒数恢复。作者还可以手动配合音量渐变效果 (已完成!) 8. 系统设置菜单中“音量”和“步时”的点击区并不是根据文本长度自动适配的,这导致作者修改文本(包括但不限于翻译)后玩家难以找到准确的点击区,建议优化 @@ -12,14 +12,16 @@ (已完成!) 11. 建议修复勇士后退时跟随者的鬼畜行为,并推出一套能够对跟随者位置和朝向进行读写的API甚至事件,且允许这些信息被计入存档,以实现一些演出效果甚至游戏要素,如新新2用公主占位防冰块 (已完成!) 12. “设置怪物属性”事件的下拉框目前只支持一部分,另一部分只能通过手敲json再解析来得到,希望提供完整支持 13. 建议给core.moveAction(callback)提供对应的事件,用于在事件中让勇士像事件外一样移动,从而正常触发跑毒和阻激夹域捕等 -14. core.openShop()和对应的事件,建议在用于公共事件时简单地退化为“插入公共事件”而不要打断当前事件,把决定权交给作者 +(已完成!) 14. core.openShop()和对应的事件,建议在用于公共事件时简单地退化为“插入公共事件”而不要打断当前事件,把决定权交给作者 (已完成!) 15. “等待用户操作”虽然提供了场合块但还不支持将多个按键的场合合并(比如空格、回车和C键一般会被作者予以合并,执行内容如果只是大致相同也值得合并,在块内由作者根据flag再行分歧),建议支持一下 (已完成!) 16. “绘制描边文本”事件建议加一个“描边颜色”参数,目前只能描黑边 (已完成!) 17. “设置画布属性”的基准线建议增加'hanging'和'ideographic'这两种模式 (已完成!) 18. 建议修复“绘制多行文本”时行距比字号大不太多会导致各行下缘丢失的bug(加大行距又会导致第一行的纵坐标难以估计),此bug在道具商店和1.3倍行距英文看的很明显 -19.(来自群友)建议给第一代全局商店的子选项像“显示选择项”一样提供图标、颜色和出现条件(注意长按的适配) +(已完成!) 19.(来自群友)建议给第一代全局商店的子选项像“显示选择项”一样提供图标、颜色和出现条件(注意长按的适配) (已完成!) 20. 很多事件对应的脚本有默认参数,但在事件中省略参数却会变成0,建议修复 +------------- + (已完成!) defaultGround全局可用 (已完成!) 对floorIds和图片进行检测 (已完成!) 删除绘图模式 @@ -27,18 +29,28 @@ (已完成!) cannotIn / cannotOut使用选项框 (已完成!) noPass->canPass改成选择框 初始化&读档优化:不一次读取全部楼层并创建 -绿钥匙进状态栏 +(已完成!) 绿钥匙进状态栏 (已完成!) 图块ID不可全数字 -怪物详细信息富文本化 +(已完成!) 怪物详细信息富文本化 (已完成!) for和forEach事件 -转向:顺时针/逆时针/反向 -事件转向 +(已完成!) 转向:顺时针/逆时针/反向 +(已完成!) 事件转向 +(已完成!) 修复core.removeBlock隐藏和删除问题 (已完成!) 合并数值操作事件 (已完成!) fromLoad,聚集问题 (已完成!) 注释优化 -状态栏显示项的优化 -动画/音乐/音效自动补全 +(已完成!) 状态栏显示项的优化 +(已完成!) 动画/音乐/音效自动补全 +(已完成!) 重构全局商店! +(已完成!) 读档时色调数据丢失 +圆角边框 +像素高分辨率问题 +(已完成!) 道具效果优化,删除部分道具相关的开关 +(已完成!) 素材列表选择 +油漆桶,动态更改地图大小 +地图拉框选择复制剪切删除 +------------- (不处理) 0. 部分文案的修改,如“地图编辑器”(启动服务.exe中)、“enemys”、“snipe” (造塔工具相关都给鹿神) 1. 便捷PS工具希望加入自定义单位宽高功能,希望把“白底转透明”增强为“纯色底转透明”(弹窗由作者输入RGB或RGBA值)。 @@ -55,7 +67,7 @@ (不处理) 12. (来自鹿神和君浪)坚固属性建议按照描述来,去修改turn或hero_per_damage而不是mon_def(rmxp魔塔的坚固属性是通过一个全局量去指定hero_per_damage的,默认为1)以免和模仿(或者说仿防)冲突 (不处理) 13. 破甲、反击、净化倍率、仇恨增量和是否减半,建议允许使用怪物的属性去覆盖全塔属性中的值,如不同的破甲怪破甲倍率不同 (已完成!) 14. (来自小艾)仇恨伤害和固伤合并进总伤害以免和负伤抵消 -(不处理) 15. 单点阻激夹域和血网伤害同时存在多种时,气泡提示的伤害类型存在覆盖现象,建议优化 +(已完成!) 15. 单点阻激夹域和血网伤害同时存在多种时,气泡提示的伤害类型存在覆盖现象,建议优化 (已完成!) 16. (来自小艾)建议修复瞬移判定无视图快属性script项的bug (已完成!) 17. 建议修复(如果还没修复)core.drawTip()不能使用系统图标和clear参数的bug,并建议增加一个参数来表示多帧图块使用第几帧(UI绘制事件中的绘制图标同理),增加另一个参数来表示32*48图块绘制上2/3(当前实现,和手册一致)还是下2/3 (不处理) 18. (来自群友)推箱子、阻击、捕捉、重生、炸锤和afterXxx事件的系列问题... @@ -64,8 +76,8 @@ (不处理) 21. (来自群友)建议大幅缩减表格的长度,譬如把勾选项和单个数值类紧凑摆放 (已完成!) 22. betweenAttackMax在被四个怪同时夹击时(比如上下楼后或通过事件使勇士和其中一只怪重合了)目前是以上下方向的怪为准,建议在表格注释中予以强调,更合理的做法是在两种怪的战损中取max或min (不处理) 23. displayIdInBook属性不能用于夹击QAQ,同时建议给core.getBlockId()追加一个“如果是怪物,是否按displayIdInBook进行映射”的参数。此外此属性会导致core.hasEnemyLeft()在指定id时失真(不一定是坏事) -(不处理) 24. core.hasEnemyLeft()不应该调用core.getCurrentEnemys(),因为后者是用于手册的,它为了按伤害升序排列而调用了core.getDamageInfo(),而core.getDamageInfo()又会调用core.getEnemyInfo(),但作者完全可能在core.getDamageInfo()或core.getEnemyInfo()中使用core.hasEnemyLeft()判断特定id的怪物是否存在从而实现一些类似光环的效果(如协同),这样就会造成死递归 -(不处理) 25. 建议新增一个类似“勇士转向”的事件指令————“图块转向”,用于NPC甚至怪物(比如基于索引/数字对4取余),毕竟用转变图块的话不够通用 +(已完成!) 24. core.hasEnemyLeft()不应该调用core.getCurrentEnemys(),因为后者是用于手册的,它为了按伤害升序排列而调用了core.getDamageInfo(),而core.getDamageInfo()又会调用core.getEnemyInfo(),但作者完全可能在core.getDamageInfo()或core.getEnemyInfo()中使用core.hasEnemyLeft()判断特定id的怪物是否存在从而实现一些类似光环的效果(如协同),这样就会造成死递归 +(已完成!) 25. 建议新增一个类似“勇士转向”的事件指令————“图块转向”,用于NPC甚至怪物(比如基于索引/数字对4取余),毕竟用转变图块的话不够通用 (已完成!) 26. “勇士转向”会导致跟随者聚集,这不利于演出,建议改成像core.turnHero()一样不聚集跟随者 (已完成!) 27. 勇士后退时跟随者很鬼畜,建议优化(每步聚集算是一种简陋的办法) (不处理) 28. 勇士的移动帧只有2,建议改为允许作者指定 @@ -83,7 +95,7 @@ (已修复!) 38.2 移除圣锤道具的定义 (不处理) 39. 建议把样板自带的大部分tools类道具的useItemEffect用useItemEvent重写,以方便作者参照学习(我能说266都几个月了好多新作者和老作者压根不知道useItemEvent的存在吗) (已修复!) 40. 希望显示文章的\b提供top和bottom这两种新写法来实现在顶部或底部显示对话框,目前只有firstArrive和eachArrive等没有当前点的事件中才能用up和down来临时实现这种效果,否则就得设置剧情文本的属性,这在频繁来回切换时是致命的,就像《无上之光》RMXP版地上40层一样 -41. 建议优化core.splitLines()来避免出现标点禁则(如点号和右标号不能用于行首,左标号不能用于行尾),同时也能更好地支持字母文字语言如英语(日语韩语等同样是方块字的倒不要紧) +(已完成!) 41. 建议优化core.splitLines()来避免出现标点禁则(如点号和右标号不能用于行首,左标号不能用于行尾),同时也能更好地支持字母文字语言如英语(日语韩语等同样是方块字的倒不要紧) (已修复!) 42. core.drawAnimate()和对应的事件指令,建议增加一个参数来表示坐标是绝对坐标(即当前的实现)还是在视野中的相对坐标(以13*13为例相对坐标总是在0~12之间),毕竟有些动画按相对坐标绘制才是作者的演出需求。 (不处理) 43. (来自鹿神)希望给animate文件支持多音效、画面闪烁和图块闪烁 (已修复!) 44. 希望给“播放背景音乐”事件像core.playBgm()函数一样添加“从第几秒开始”的参数,以用于一些演出(比如适当的剧情处直接从高潮开始)。同理可以让“暂停背景音乐”记录下当前播放到了第几秒,并在“恢复背景音乐”中自动从这个秒数恢复。作者还可以手动配合音量渐变效果 @@ -95,18 +107,18 @@ (不处理) 50. 建议给录像播放时的N键提供一个对应的移动端操作,以方便移动端作者 (已完成!) 51. 希望给“显示选择项”、“显示确认框”、“等待用户操作”这三个事件添加一个“若多少毫秒内不响应则触发的分支”,用来实现一些QTE,录像中可以记录“choice: timeout”、“confirm: timeout”和“wait: timeout”来表示触发了超时分支 52. 希望给“转变图块”加一个淡入时间参数(显隐和转变图层块也希望加个淡入淡出时间参数,图层块也希望提供移动和跳跃事件),专门用于原本是空地或空气墙的情况。目前要实现这样的效果必须使用红点,然而红点有着“推箱子、阻击、捕捉、重生、炸锤和afterXxx事件”的系列问题,且会导致core.removeBlock()(隐藏事件)退化为core.hideBlock() -53. 希望“移动事件”的移速和淡出速度拆开成两个参数,目前这样捆绑在一起不太自由 -(不处理) 54. 建议把图块属性的noPass改为勾选框而不是下拉框,因为null一定表示不可通行(道具根本没有noPass) +(不处理) 53. 希望“移动事件”的移速和淡出速度拆开成两个参数,目前这样捆绑在一起不太自由 +(已完成) 54. 建议把图块属性的noPass改为勾选框而不是下拉框,因为null一定表示不可通行(道具根本没有noPass) (不处理,可以转变图块) 55. “移动事件”和“跳跃事件”会导致本来勾选了不显伤的怪物突然有了显伤,非常难看,建议优化 (不处理) 56. 希望推出一套能够对跟随者位置和坐标进行读写的API甚至事件指令,并允许这些信息被计入存档,以实现一些演出甚至游戏性效果,如新新2用公主占位防冰块 (不处理) 57. 希望\t[hero]和\t[xxx,hero]能像图块一样绘制原地抖动的4帧全局动画而不是不动,希望能使用勇士的当前朝向而不是一直朝下,希望能使用\t[hero1]和\t[xxx,hero1]这样的写法来使用跟随者的行走图作为头像 (文档相关给秋橙) 58. 建议在在线文档中给\r支持的颜色单词做一下枚举并写出对应的RGB值 (不处理) 59. 在标题画面调用core.hideStartAnimate(core.startGame)会立即开始游戏并跳过难度选择,可能造成非作者预期的效果,有办法优化吗? (已完成!) 60. “设置怪物属性”事件的下拉框目前只支持一部分,另一部分只能通过手敲json再解析来得到,希望提供完整支持 -61. 在事件编辑器中进行地图选点时,有些事件指令虽然只能用于同楼层,但实际生效时勇士可能已经不在当前正在编辑的楼层(比如在一个楼层切换事件指令后)了,所以希望地图选点始终提供楼层下拉表 +(已完成!) 61. 在事件编辑器中进行地图选点时,有些事件指令虽然只能用于同楼层,但实际生效时勇士可能已经不在当前正在编辑的楼层(比如在一个楼层切换事件指令后)了,所以希望地图选点始终提供楼层下拉表 (文档相关给秋橙) 62. 希望在线文档介绍一下楼层和startText的独立开关的flag表示 63. 希望给core.moveAction(callback)函数提供一个对应的事件指令,用于在事件流处理中让勇士进行一些有游戏性意义(即非纯演出)的移动。如果做不到的话希望参照本文的46号issue作为原生脚本勾选async的例子讲解如何使用 -64. “打开全局商店”事件指令建议在用于公共事件版商店时简单地退化为“插入公共事件”而不要打断当前事件流,由作者在这个公共事件结尾去决定“立刻结束当前事件”还是不结束 +(已完成!) 64. “打开全局商店”事件指令建议在用于公共事件版商店时简单地退化为“插入公共事件”而不要打断当前事件流,由作者在这个公共事件结尾去决定“立刻结束当前事件”还是不结束 (已修复!) 65. “显隐贴图”的参数应为像素坐标而不是网格坐标,请修正在线文档的描述。同时建议加个淡入淡出时间参数,还建议贴图能像图片一样有透明度和被移动 (不处理) 66. 读档建议换个和floor.mp3不一样的音效 (不处理) 67. “画面震动”事件希望不要光左右晃,至少做成QQ窗口抖动的效果吧w @@ -118,7 +130,7 @@ 73. 画弧既然有现成的API,那建议也作为UI绘制事件提供。甚至希望提供画椭圆功能(长短轴水平或铅直的那种)参数为中心坐标和长短轴长度 (已完成!) 74. “设置画布属性”的基准线建议增加'hanging'和'ideographic'这两种模式 (已完成!) 75. 建议修复“绘制多行文本”时行距比字号大不太多会导致各行下缘丢失的bug(加大行距又会导致第一行的纵坐标难以估计),此bug在道具商店和1.3倍行距英文看的很明显 -76. (来自群友)建议给第一代全局商店像“显示选择项”一样提供图标、颜色和出现条件 +(已完成!) 76. (来自群友)建议给第一代全局商店像“显示选择项”一样提供图标、颜色和出现条件 (已完成!增加正则匹配) 77. 建议给core.searchBlock()新增问号通配符(匹配一个任意字符) (已完成!) 78. 建议移除core.setInitData()并将其内容写在startText以方便作者仿照修改 (不处理;有跟随情况下处理很麻烦) 79. 建议给core.drawHero()添加纵横两个偏移参数(目前只有一个),以用于做一些斜向移动的演出