From 023d913ea77b97cc32f362318af73f625043cb79 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Tue, 28 Feb 2023 17:49:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=BB=93=E6=9E=84=E6=94=B9?= =?UTF-8?q?=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierignore | 3 +- package.json | 3 +- public/_server/CodeMirror/defs.js | 8369 ++++++++++++----------- public/_server/MotaAction.g4 | 2 +- public/_server/table/plugins.comment.js | 68 +- public/libs/actions.js | 10 +- public/libs/control.js | 6 +- public/libs/core.js | 26 +- public/libs/events.js | 12 +- public/libs/ui.js | 4 +- public/project/data.js | 20 +- public/project/floors/MT0.js | 2 +- public/project/floors/MT16.js | 4 +- public/project/floors/MT21.js | 2 +- public/project/floors/MT31.js | 2 +- public/project/floors/MT32.js | 2 +- public/project/floors/MT35.js | 4 +- public/project/floors/MT5.js | 2 +- public/project/floors/MT6.js | 2 +- public/project/floors/tower5.js | 2 +- public/project/floors/tower7.js | 2 +- public/project/functions.js | 48 +- public/project/items.js | 2653 ++++--- public/project/plugin/fiveLayer.js | 207 + public/project/plugin/halo.js | 56 + public/project/plugin/hero.js | 86 + public/project/plugin/heroFourFrames.js | 59 + public/project/plugin/hotReload.js | 245 + public/project/plugin/itemDetail.js | 129 + public/project/plugin/loopMap.js | 239 + public/project/plugin/popup.js | 123 + public/project/plugin/remainEnemy.js | 75 + public/project/plugin/removeMap.js | 117 + public/project/plugin/replay.js | 90 + public/project/plugin/shop.js | 76 + public/project/plugin/skillTree.js | 319 + public/project/plugin/skills.js | 181 + public/project/plugin/study.js | 79 + public/project/plugin/towerBoss.js | 1657 +++++ public/project/plugin/ui.js | 94 + public/project/plugin/utils.js | 92 +- public/project/plugins.js | 4122 +---------- script/compress.ts | 4 +- src/data/desc.json | 12 +- src/data/skill.json | 4 +- src/plugin/chase/chase1.ts | 2 +- src/plugin/settings.ts | 5 +- src/types/data.d.ts | 2 + src/types/plugin.d.ts | 92 +- src/ui/settings.vue | 2 +- src/ui/skill.vue | 5 +- src/ui/skillTree.vue | 30 +- src/ui/statusBar.vue | 4 +- 53 files changed, 9640 insertions(+), 9816 deletions(-) create mode 100644 public/project/plugin/fiveLayer.js create mode 100644 public/project/plugin/halo.js create mode 100644 public/project/plugin/hero.js create mode 100644 public/project/plugin/heroFourFrames.js create mode 100644 public/project/plugin/hotReload.js create mode 100644 public/project/plugin/itemDetail.js create mode 100644 public/project/plugin/loopMap.js create mode 100644 public/project/plugin/popup.js create mode 100644 public/project/plugin/remainEnemy.js create mode 100644 public/project/plugin/removeMap.js create mode 100644 public/project/plugin/replay.js create mode 100644 public/project/plugin/shop.js create mode 100644 public/project/plugin/skillTree.js create mode 100644 public/project/plugin/skills.js create mode 100644 public/project/plugin/study.js create mode 100644 public/project/plugin/towerBoss.js create mode 100644 public/project/plugin/ui.js diff --git a/.prettierignore b/.prettierignore index 16d1f60..d1c3410 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ vite.config.ts public/project/data.js -story.md \ No newline at end of file +story.md +public/project/floors/*.js \ No newline at end of file diff --git a/package.json b/package.json index a8bfa6f..00103eb 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "preview": "vite preview", "preview-node": "cd dist && node server.cjs", "update": "ts-node-esm script/update.ts", - "declare": "ts-node-esm script/declare.ts" + "declare": "ts-node-esm script/declare.ts", + "type": "vue-tsc --noEmit" }, "dependencies": { "@ant-design/icons-vue": "^6.1.0", diff --git a/public/_server/CodeMirror/defs.js b/public/_server/CodeMirror/defs.js index 8e0ee9f..c9460d9 100644 --- a/public/_server/CodeMirror/defs.js +++ b/public/_server/CodeMirror/defs.js @@ -1,4130 +1,4315 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ - { - "!name": "browser", - "Node": { - "!type": "fn()", - "prototype": { - "nextSibling": { - "!type": "+Element", - "!doc": "返回紧接其父节点的childNodes列表中指定节点之后的节点;如果指定节点是该列表中的最后一个节点,则返回null.", + { + '!name': 'browser', + Node: { + '!type': 'fn()', + prototype: { + nextSibling: { + '!type': '+Element', + '!doc': '返回紧接其父节点的childNodes列表中指定节点之后的节点;如果指定节点是该列表中的最后一个节点,则返回null.' + }, + previousSibling: { + '!type': '+Element', + '!doc': '返回紧接其父节点的childNodes列表中指定节点之前的节点,如果指定节点是该列表中的第一个节点,则返回null.' + }, + lastChild: { + '!type': '+Element', + '!doc': '返回节点的最后一个孩子.' + }, + firstChild: { + '!type': '+Element', + '!doc': '返回树中该节点的第一个子节点;如果该节点为无子节点,则返回null.如果该节点是Document,则返回其直接子节点列表中的第一个节点.' + }, + childNodes: { + '!type': '+NodeList', + '!doc': '返回给定元素的子节点的集合.' + }, + parentNode: { + '!type': '+Element', + '!doc': '返回DOM树中指定节点的父级.' + }, + tagName: { + '!type': 'string', + '!doc': '将当前节点的名称作为字符串返回.' + }, + insertBefore: { + '!type': + 'fn(newElt: +Element, before: +Element) -> +Element', + '!doc': '将指定的节点插入到参考元素之前,作为当前节点的子级.' + }, + removeChild: { + '!type': 'fn(oldNode: +Element) -> +Element', + '!doc': '从DOM中删除一个子节点.返回已删除的节点.' + }, + appendChild: { + '!type': 'fn(newNode: +Element) -> +Element', + '!doc': '将一个节点添加到指定父节点的子节点列表的末尾.如果该节点已经存在,则将其从当前父节点中删除,然后添加到新的父节点中.' + }, + cloneNode: { + '!type': 'fn(deep: bool) -> +Element', + '!doc': '返回在其上调用此方法的节点的副本.' + }, + addEventListener: { + '!type': + 'fn(type: string, listener: fn(e: +Event), capture: bool)', + '!doc': '在单个目标上注册单个事件侦听器.事件目标可以是文档中的单个元素,文档本身,窗口或XMLHttpRequest.' + }, + removeEventListener: { + '!type': 'fn(type: string, listener: fn(), capture: bool)', + '!doc': '允许从事件目标中删除事件侦听器.' + }, + innerText: { + '!type': 'string', + '!doc': '获取或设置节点及其后代的文本内容.' + } + }, + '!doc': '节点是一个接口,许多DOM类型都从该接口继承,并允许类似地对待(或测试)这些各种类型.' }, - "previousSibling": { - "!type": "+Element", - "!doc": "返回紧接其父节点的childNodes列表中指定节点之前的节点,如果指定节点是该列表中的第一个节点,则返回null.", + Element: { + '!type': 'fn()', + prototype: { + '!proto': 'Node.prototype', + getAttribute: { + '!type': 'fn(name: string) -> string', + '!doc': '返回指定元素上的命名属性的值.如果命名属性不存在,则返回的值将为null或" "(空字符串).' + }, + setAttribute: { + '!type': 'fn(name: string, value: string)', + '!doc': '在指定元素上添加新属性或更改现有属性的值.' + }, + removeAttribute: { + '!type': 'fn(name: string)', + '!doc': '从指定元素中删除属性.' + }, + getElementsByTagName: { + '!type': 'fn(tagName: string) -> +NodeList', + '!doc': '返回具有给定标签名的元素列表.搜索指定元素下面的子树,不包括元素本身.返回的列表是活动的,这意味着它将自动使用DOM树进行更新.因此,无需使用相同的元素和参数多次调用element.getElementsByTagName.' + }, + getElementsByClassName: { + '!type': 'fn(name: string) -> +NodeList', + '!doc': '返回具有所有给定类名称的一组元素.在文档对象上调用时,将搜索整个文档,包括根节点.您还可以在任何元素上调用getElementsByClassName;它将仅返回元素,它们是具有给定类名的指定根元素的后代.' + }, + children: { + '!type': '+HTMLCollection', + '!doc': '返回给定元素的子元素的集合.' + }, + className: { + '!type': 'string', + '!doc': '获取并设置指定元素的class属性的值.' + }, + style: { + cssText: 'string', + alignmentBaseline: 'string', + background: 'string', + backgroundAttachment: 'string', + backgroundClip: 'string', + backgroundColor: 'string', + backgroundImage: 'string', + backgroundOrigin: 'string', + backgroundPosition: 'string', + backgroundPositionX: 'string', + backgroundPositionY: 'string', + backgroundRepeat: 'string', + backgroundRepeatX: 'string', + backgroundRepeatY: 'string', + backgroundSize: 'string', + baselineShift: 'string', + border: 'string', + borderBottom: 'string', + borderBottomColor: 'string', + borderBottomLeftRadius: 'string', + borderBottomRightRadius: 'string', + borderBottomStyle: 'string', + borderBottomWidth: 'string', + borderCollapse: 'string', + borderColor: 'string', + borderImage: 'string', + borderImageOutset: 'string', + borderImageRepeat: 'string', + borderImageSlice: 'string', + borderImageSource: 'string', + borderImageWidth: 'string', + borderLeft: 'string', + borderLeftColor: 'string', + borderLeftStyle: 'string', + borderLeftWidth: 'string', + borderRadius: 'string', + borderRight: 'string', + borderRightColor: 'string', + borderRightStyle: 'string', + borderRightWidth: 'string', + borderSpacing: 'string', + borderStyle: 'string', + borderTop: 'string', + borderTopColor: 'string', + borderTopLeftRadius: 'string', + borderTopRightRadius: 'string', + borderTopStyle: 'string', + borderTopWidth: 'string', + borderWidth: 'string', + bottom: 'string', + boxShadow: 'string', + boxSizing: 'string', + captionSide: 'string', + clear: 'string', + clip: 'string', + clipPath: 'string', + clipRule: 'string', + color: 'string', + colorInterpolation: 'string', + colorInterpolationFilters: 'string', + colorProfile: 'string', + colorRendering: 'string', + content: 'string', + counterIncrement: 'string', + counterReset: 'string', + cursor: 'string', + direction: 'string', + display: 'string', + dominantBaseline: 'string', + emptyCells: 'string', + enableBackground: 'string', + fill: 'string', + fillOpacity: 'string', + fillRule: 'string', + filter: 'string', + float: 'string', + floodColor: 'string', + floodOpacity: 'string', + font: 'string', + fontFamily: 'string', + fontSize: 'string', + fontStretch: 'string', + fontStyle: 'string', + fontVariant: 'string', + fontWeight: 'string', + glyphOrientationHorizontal: 'string', + glyphOrientationVertical: 'string', + height: 'string', + imageRendering: 'string', + kerning: 'string', + left: 'string', + letterSpacing: 'string', + lightingColor: 'string', + lineHeight: 'string', + listStyle: 'string', + listStyleImage: 'string', + listStylePosition: 'string', + listStyleType: 'string', + margin: 'string', + marginBottom: 'string', + marginLeft: 'string', + marginRight: 'string', + marginTop: 'string', + marker: 'string', + markerEnd: 'string', + markerMid: 'string', + markerStart: 'string', + mask: 'string', + maxHeight: 'string', + maxWidth: 'string', + minHeight: 'string', + minWidth: 'string', + opacity: 'string', + orphans: 'string', + outline: 'string', + outlineColor: 'string', + outlineOffset: 'string', + outlineStyle: 'string', + outlineWidth: 'string', + overflow: 'string', + overflowWrap: 'string', + overflowX: 'string', + overflowY: 'string', + padding: 'string', + paddingBottom: 'string', + paddingLeft: 'string', + paddingRight: 'string', + paddingTop: 'string', + page: 'string', + pageBreakAfter: 'string', + pageBreakBefore: 'string', + pageBreakInside: 'string', + pointerEvents: 'string', + position: 'string', + quotes: 'string', + resize: 'string', + right: 'string', + shapeRendering: 'string', + size: 'string', + speak: 'string', + src: 'string', + stopColor: 'string', + stopOpacity: 'string', + stroke: 'string', + strokeDasharray: 'string', + strokeDashoffset: 'string', + strokeLinecap: 'string', + strokeLinejoin: 'string', + strokeMiterlimit: 'string', + strokeOpacity: 'string', + strokeWidth: 'string', + tabSize: 'string', + tableLayout: 'string', + textAlign: 'string', + textAnchor: 'string', + textDecoration: 'string', + textIndent: 'string', + textLineThrough: 'string', + textLineThroughColor: 'string', + textLineThroughMode: 'string', + textLineThroughStyle: 'string', + textLineThroughWidth: 'string', + textOverflow: 'string', + textOverline: 'string', + textOverlineColor: 'string', + textOverlineMode: 'string', + textOverlineStyle: 'string', + textOverlineWidth: 'string', + textRendering: 'string', + textShadow: 'string', + textTransform: 'string', + textUnderline: 'string', + textUnderlineColor: 'string', + textUnderlineMode: 'string', + textUnderlineStyle: 'string', + textUnderlineWidth: 'string', + top: 'string', + unicodeBidi: 'string', + unicodeRange: 'string', + vectorEffect: 'string', + verticalAlign: 'string', + visibility: 'string', + whiteSpace: 'string', + width: 'string', + wordBreak: 'string', + wordSpacing: 'string', + wordWrap: 'string', + writingMode: 'string', + zIndex: 'string', + zoom: 'string', + '!doc': '返回一个表示元素的style属性的对象.' + }, + classList: { + '!type': '+DOMTokenList', + '!doc': '返回元素的class属性的标记列表.' + }, + title: { + '!type': 'string', + '!doc': ' "\u5efa\u7acb\u5f53\u9f20\u6807\u60ac\u505c\u5728\u663e\u793a\u7684\u8282\u70b9\u4e0a\u65f6\u5728"\u5de5\u5177\u63d0\u793a"\u5f39\u51fa\u7a97\u53e3\u4e2d\u663e\u793a\u7684\u6587\u672c.",' + }, + width: { + '!type': 'number', + '!doc': '返回元素的布局宽度.' + }, + height: { + '!type': 'number', + '!doc': '元素相对于元素的offsetParent的高度.' + }, + getContext: { + '!type': 'fn(id: string) -> CanvasRenderingContext2D', + '!doc': ' DOM画布元素公开了HTMLCanvasElement接口,该接口提供了用于操纵画布元素的布局和表示的属性和方法.HTMLCanvasElement接口继承了元素对象接口的属性和方法.' + }, + innerHTML: { + '!type': 'string', + '!doc': '设置或获取描述元素后代的HTML语法.' + } + }, + '!doc': '表示HTML或XML文档中的元素.' }, - "lastChild": { - "!type": "+Element", - "!doc": "返回节点的最后一个孩子." + Document: { + '!type': 'fn()', + prototype: { + '!proto': 'Node.prototype', + height: { + '!type': 'number', + '!doc': '返回当前文档的元素的高度.' + }, + width: { + '!type': 'number', + '!doc': '以像素为单位返回当前文档的元素的宽度.' + }, + body: { + '!type': '+Element', + '!doc': '返回当前文档的或节点.' + }, + cookie: { + '!type': 'string', + '!doc': '获取并设置与当前文档关联的cookie.' + }, + URL: 'string', + title: { + '!type': 'string', + '!doc': '获取或设置文档的标题.' + }, + getElementById: { + '!type': 'fn(id: string) -> +Element', + '!doc': '通过元素ID返回对该元素的引用.' + }, + getElementsByTagName: { + '!type': 'fn(tagName: string) -> +NodeList', + '!doc': '返回具有给定标签名称的元素的NodeList.将搜索整个文档,包括根节点.返回的NodeList处于活动状态,这意味着它会自动更新自身以与DOM树保持同步,而无需再次调用document.getElementsByTagName.' + }, + getElementsByName: { + '!type': 'fn(name: string) -> +HTMLCollection', + '!doc': '返回HTML文档中具有给定名称的元素列表.' + }, + getElementsByClassName: + 'Element.prototype.getElementsByClassName' + }, + '!doc': '浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素).' }, - "firstChild": { - "!type": "+Element", - "!doc": "返回树中该节点的第一个子节点;如果该节点为无子节点,则返回null.如果该节点是Document,则返回其直接子节点列表中的第一个节点.", + document: { + '!type': '+Document', + '!doc': '浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和
之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素).' }, - "childNodes": { - "!type": "+NodeList", - "!doc": "返回给定元素的子节点的集合." + Event: { + '!type': 'fn()', + prototype: { + stopPropagation: { + '!type': 'fn()', + '!doc': '防止当前事件进一步传播.' + }, + preventDefault: { + '!type': 'fn()', + '!doc': '如果可以取消事件,则取消该事件,而不停止事件的进一步传播.' + }, + stopImmediatePropagation: { + '!type': 'fn()', + '!doc': '防止同一事件的其他侦听器被调用.' + }, + type: { + '!type': 'string', + '!doc': '返回包含事件类型的字符串.' + }, + target: { + '!type': '+Element', + '!doc': ' EventTarget是由对象实现的DOM接口,这些对象可以接收DOM事件并具有侦听器.最常见的EventTarget是DOM元素,尽管其他对象也可以是EventTarget,例如文档,窗口,XMLHttpRequest,和别的.' + }, + clientX: { + '!type': 'number', + '!doc': '返回事件发生的应用程序客户区域内的水平坐标(与页面内的坐标相反).例如,单击客户区域左上角将始终显示clientX值为0的鼠标事件,无论页面是否水平滚动.' + }, + clientY: { + '!type': 'number', + '!doc': '返回事件发生在应用程序客户区中的垂直坐标(与页面中的坐标相反).例如,单击客户区左上角将始终显示不管页面是否垂直滚动,clientY值为0的鼠标事件.' + }, + keyCode: { + '!type': 'number', + '!doc': '返回按键事件中的非字符键或任何其他类型的键盘事件中的任何键的Unicode值.' + }, + charCode: { + '!type': 'number', + '!doc': '返回在按键事件期间按下的字符键的Unicode值.' + }, + which: { + '!type': 'number', + '!doc': '返回所按下键的数字keyCode或所按下字母数字键的字符代码(charCode).' + }, + button: { + '!type': 'number', + '!doc': '指示导致事件的鼠标按钮.' + }, + shiftKey: { + '!type': 'bool', + '!doc': '指示事件触发时是否按下SHIFT键.' + }, + ctrlKey: { + '!type': 'bool', + '!doc': '指示事件触发时是否按下了CTRL键.' + }, + altKey: { + '!type': 'bool', + '!doc': '指示事件触发时是否按下ALT键.' + } + } }, - "parentNode": { - "!type": "+Element", - "!doc": "返回DOM树中指定节点的父级." + Storage: { + length: { + '!type': 'number', + '!doc': '存储接口的length只读属性返回一个整数,该整数表示存储在存储对象中的数据项的数量.' + }, + setItem: { + '!type': 'fn(name: string, value: string)', + '!doc': '存储接口的setItem()方法在传递键名称和值时,会将该键添加到存储中,或者更新该键的值(如果已存在).' + }, + getItem: { + '!type': 'fn(name: string) -> string', + '!doc': '存储接口的getItem()方法在传递键名时将返回该键的值.' + }, + key: { + '!type': 'fn(index: number) -> string', + '!doc': '存储接口的key()方法传递数字n时,返回存储中第n个键的名称.键的顺序是用户代理定义的,因此您不应依赖它.' + }, + removeItem: { + '!type': 'fn(key: string)', + '!doc': '存储接口的removeItem()方法在传递了键名后,将从存储中删除该键.' + }, + clear: { + '!type': 'fn()', + '!doc': '存储接口的clear()方法在被调用时将从存储中清空所有键.' + } }, - "tagName": { - "!type": "string", - "!doc": "将当前节点的名称作为字符串返回." + localStorage: { + '!type': 'Storage', + '!doc': ' localStorage属性允许您访问本地存储对象.localStorage与sessionStorage类似.唯一的区别是,虽然存储在localStorage中的数据没有到期时间,但是浏览会话时存储在sessionStorage中的数据将被清除.结束-也就是说,当浏览器关闭时. n n请注意,存储在localStorage或sessionStorage中的数据特定于页面协议.' }, - "insertBefore": { - "!type": "fn(newElt: +Element, before: +Element) -> +Element", - "!doc": "将指定的节点插入到参考元素之前,作为当前节点的子级.", + console: { + assert: { + '!type': 'fn(assertion: bool, text: string)', + '!doc': '如果断言为false,则将错误消息写入控制台.' + }, + error: { + '!type': 'fn(...msg: ?)', + '!doc': '将错误消息输出到Web控制台.' + }, + info: { + '!type': 'fn(...msg: ?)', + '!doc': '将参考消息输出到Web控制台.' + }, + log: { + '!type': 'fn(...msg: ?)', + '!doc': '将消息输出到Web控制台.' + }, + time: { + '!type': 'fn(label: string)', + '!doc': '启动计时器,您可以使用该计时器来跟踪操作需要多长时间.' + }, + timeEnd: { + '!type': 'fn(label: string)', + '!doc': '停止以前通过调用console.time()启动的计时器.' + }, + trace: { + '!type': 'fn()', + '!doc': '将堆栈跟踪输出到Web控制台.' + }, + warn: { + '!type': 'fn(...msg: ?)', + '!doc': '将警告消息输出到Web控制台.' + }, + '!doc': '控制台对象提供对浏览器调试控制台的访问.其工作方式的细节因浏览器而异,但实际上提供了一组事实上的功能.' }, - "removeChild": { - "!type": "fn(oldNode: +Element) -> +Element", - "!doc": "从DOM中删除一个子节点.返回已删除的节点.", + window: { + '!type': '', + '!doc': '窗口对象代表一个包含DOM文档的窗口.' }, - "appendChild": { - "!type": "fn(newNode: +Element) -> +Element", - "!doc": "将一个节点添加到指定父节点的子节点列表的末尾.如果该节点已经存在,则将其从当前父节点中删除,然后添加到新的父节点中.", + self: { + '!type': '', + '!doc': '将对象引用返回到窗口对象.' }, - "cloneNode": { - "!type": "fn(deep: bool) -> +Element", - "!doc": "返回在其上调用此方法的节点的副本." + devicePixelRatio: 'number', + requestAnimationFrame: { + '!type': 'fn(callback: fn(timestamp: number)) -> number', + '!doc': ' Window.requestAnimationFrame()方法告诉浏览器您希望执行动画,并请求浏览器在下一次重绘之前调用指定的函数来更新动画.该方法将回调作为参数在重新粉刷之前被调用.' }, - "addEventListener": { - "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)", - "!doc": "在单个目标上注册单个事件侦听器.事件目标可以是文档中的单个元素,文档本身,窗口或XMLHttpRequest.", + cancelAnimationFrame: { + '!type': 'fn(number)n', + '!doc': '取消先前安排的动画帧请求.' }, - "removeEventListener": { - "!type": "fn(type: string, listener: fn(), capture: bool)", - "!doc": "允许从事件目标中删除事件侦听器.", + alert: { + '!type': 'fn(message: string)', + '!doc': '显示具有指定内容和确定按钮的警报对话框.' }, - "innerText": { - "!type": "string", - "!doc": "获取或设置节点及其后代的文本内容." + confirm: { + '!type': 'fn(message: string) -> bool', + '!doc': '显示带有消息和两个按钮(确定和取消)的模式对话框.' + }, + prompt: { + '!type': 'fn(message: string, value: string) -> string', + '!doc': '显示一个对话框,提示用户输入一些文本.' + }, + setTimeout: { + '!type': 'fn(f: fn(), ms: number) -> number', + '!doc': '在指定的延迟后调用函数或执行代码段.' + }, + clearTimeout: { + '!type': 'fn(timeout: number)', + '!doc': '清除window.setTimeout()设置的延迟.' + }, + setInterval: { + '!type': 'fn(f: fn(), ms: number) -> number', + '!doc': '反复调用一个函数或执行代码段,每次调用该函数之间有固定的时间延迟.' + }, + clearInterval: { + '!type': 'fn(interval: number)', + '!doc': '取消使用setInterval设置的重复操作.' + }, + atob: { + '!type': 'fn(encoded: string) -> string', + '!doc': '解码使用base-64编码编码的数据字符串.' + }, + btoa: { + '!type': 'fn(data: string) -> string', + '!doc': '从一串二进制数据创建一个base-64编码的ASCII字符串.' + }, + getComputedStyle: { + '!type': + 'fn(node: +Element, pseudo?: string) -> Element.prototype.style', + '!doc': '给出元素的所有CSS属性的最终使用值.' + }, + CanvasRenderingContext2D: { + canvas: '+Element', + width: 'number', + height: 'number', + commit: 'fn()', + save: 'fn()', + restore: 'fn()', + currentTransform: '?', + scale: 'fn(x: number, y: number)', + rotate: 'fn(angle: number)', + translate: 'fn(x: number, y: number)', + transform: + 'fn(a: number, b: number, c: number, d: number, e: number, f: number)', + setTransform: + 'fn(a: number, b: number, c: number, d: number, e: number, f: number)', + resetTransform: 'fn()', + globalAlpha: 'number', + globalCompositeOperation: 'string', + imageSmoothingEnabled: 'bool', + strokeStyle: 'string', + fillStyle: 'string', + createLinearGradient: + 'fn(x0: number, y0: number, x1: number, y1: number) -> ?', + createPattern: 'fn(image: ?, repetition: string) -> ?', + shadowOffsetX: 'number', + shadowOffsetY: 'number', + shadowBlur: 'number', + shadowColor: 'string', + clearRect: 'fn(x: number, y: number, w: number, h: number)', + fillRect: 'fn(x: number, y: number, w: number, h: number)', + strokeRect: 'fn(x: number, y: number, w: number, h: number)', + fillRule: 'string', + fill: 'fn()', + beginPath: 'fn()', + stroke: 'fn()', + clip: 'fn()', + resetClip: 'fn()', + fillText: + 'fn(text: string, x: number, y: number, maxWidth: number)', + strokeText: + 'fn(text: string, x: number, y: number, maxWidth: number)', + measureText: 'fn(text: string) -> ?', + drawImage: 'fn(image: ?, dx: number, dy: number)', + createImageData: 'fn(sw: number, sh: number) -> ?', + getImageData: + 'fn(sx: number, sy: number, sw: number, sh: number) -> ?', + putImageData: 'fn(imagedata: ?, dx: number, dy: number)', + lineWidth: 'number', + lineCap: 'string', + lineJoin: 'string', + miterLimit: 'number', + setLineDash: 'fn(segments: [number])', + getLineDash: 'fn() -> [number]', + lineDashOffset: 'number', + font: 'string', + textAlign: 'string', + textBaseline: 'string', + direction: 'string', + closePath: 'fn()', + moveTo: 'fn(x: number, y: number)', + lineTo: 'fn(x: number, y: number)', + quadraticCurveTo: + 'fn(cpx: number, cpy: number, x: number, y: number)', + bezierCurveTo: + 'fn(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number)', + arcTo: 'fn(x1: number, y1: number, x2: number, y2: number, radius: number)', + rect: 'fn(x: number, y: number, w: number, h: number)', + arc: 'fn(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: bool)', + ellipse: + 'fn(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise: bool)' } - }, - "!doc": "节点是一个接口,许多DOM类型都从该接口继承,并允许类似地对待(或测试)这些各种类型.", }, - "Element": { - "!type": "fn()", - "prototype": { - "!proto": "Node.prototype", - "getAttribute": { - "!type": "fn(name: string) -> string", - "!doc": "返回指定元素上的命名属性的值.如果命名属性不存在,则返回的值将为null或\" \"(空字符串).", + { + '!name': 'ecmascript', + Infinity: { + '!type': 'number', + '!doc': '代表无穷大的数值.' }, - "setAttribute": { - "!type": "fn(name: string, value: string)", - "!doc": "在指定元素上添加新属性或更改现有属性的值.", + undefined: { + '!type': '?', + '!doc': '该值未定义.' }, - "removeAttribute": { - "!type": "fn(name: string)", - "!doc": "从指定元素中删除属性.", + NaN: { + '!type': 'number', + '!doc': '代表非数字的值.' }, - "getElementsByTagName": { - "!type": "fn(tagName: string) -> +NodeList", - "!doc": "返回具有给定标签名的元素列表.搜索指定元素下面的子树,不包括元素本身.返回的列表是活动的,这意味着它将自动使用DOM树进行更新.因此,无需使用相同的元素和参数多次调用element.getElementsByTagName." + Object: { + '!type': 'fn()', + create: { + '!type': 'fn(proto: ?) -> !custom:Object_create', + '!doc': '使用指定的原型对象和属性创建一个新对象.' + }, + defineProperty: { + '!type': + 'fn(obj: ?, prop: string, desc: propertyDescriptor) -> !custom:Object_defineProperty', + '!doc': '直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象.如果想了解如何将Object.defineProperty方法与类似二进制标志的语法一起使用,请参阅本文.' + }, + keys: { + '!type': 'fn(obj: ?) -> [string]', + '!doc': '返回一个给定对象自己的可枚举属性的数组,其顺序与for-in循环所提供的顺序相同(不同之处在于for-in循环也枚举了原型链中的属性). ' + }, + assign: { + '!type': 'fn(target: ?, source: ?, source?: ?) -> !0', + '!effects': ['copy !1 !0', 'copy !2 !0', 'copy !3 !0'], + '!doc': ' Object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象.它将返回目标对象.,' + }, + prototype: { + '!stdProto': 'Object', + toString: { + '!type': 'fn() -> string', + '!doc': '返回表示对象的字符串.' + }, + hasOwnProperty: { + '!type': 'fn(prop: string) -> bool', + '!doc': '返回一个布尔值,指示对象是否具有指定的属性.' + } + } }, - "getElementsByClassName": { - "!type": "fn(name: string) -> +NodeList", - "!doc": "返回具有所有给定类名称的一组元素.在文档对象上调用时,将搜索整个文档,包括根节点.您还可以在任何元素上调用getElementsByClassName;它将仅返回元素,它们是具有给定类名的指定根元素的后代." + Function: { + '!type': 'fn(body: string) -> fn()', + prototype: { + '!stdProto': 'Function', + apply: { + '!type': 'fn(this: ?, args: [?])', + '!effects': [ + 'call and return !this this=!0 !1. !1. !1.' + ], + '!doc': '调用具有给定值的函数,并以数组(或类似对象的数组)形式提供参数.' + }, + call: { + '!type': 'fn(this: ?, args?: ?) -> !this.!ret', + '!effects': ['call and return !this this=!0 !1 !2 !3 !4'], + '!doc': '调用具有给定值和单独提供的参数的函数.' + }, + bind: { + '!type': 'fn(this: ?, args?: ?) -> !custom:Function_bind', + '!doc': '创建一个新函数,该函数在被调用时将其this关键字设置为提供的值,并在调用新函数时提供给定的参数序列.' + }, + prototype: '?' + } }, - "children": { - "!type": "+HTMLCollection", - "!doc": "返回给定元素的子元素的集合." + Array: { + '!type': 'fn(size: number) -> !custom:Array_ctor', + isArray: { + '!type': 'fn(value: ?) -> bool', + '!doc': '如果对象是数组,则返回true,否则返回false.' + }, + from: { + '!type': + 'fn(arrayLike: ?, mapFn?: fn(elt: ?, i: number) -> ?, thisArg?: ?) -> [!0.]', + '!effects': ['call !1 this=!2 !0. number'], + '!doc': ' Array.from()方法从类似数组或可迭代的对象创建一个新的Array实例.,' + }, + of: { + '!type': 'fn(elementN: ?) -> [!0]', + '!doc': ' Array.of()方法创建一个新的Array实例,该实例具有可变数量的参数,而不考虑参数的数量或类型.,' + }, + prototype: { + '!stdProto': 'Array', + length: { + '!type': 'number', + '!doc': '一个无符号的32位整数,指定数组中的元素数.' + }, + concat: { + '!type': 'fn(other: [?]) -> !this', + '!doc': '返回一个新数组,该数组由该数组与其他数组和/或值组成.' + }, + join: { + '!type': 'fn(separator?: string) -> string', + '!doc': '将数组的所有元素连接到字符串中.' + }, + splice: { + '!type': + 'fn(pos: number, amount: number, newelt?: ?) -> [?]', + '!doc': '更改数组的内容,在删除旧元素的同时添加新元素.' + }, + pop: { + '!type': 'fn() -> !this.', + '!doc': '从数组中删除最后一个元素并返回该元素.' + }, + push: { + '!type': 'fn(newelt: ?) -> number', + '!effects': ['propagate !0 !this.'], + '!doc': '通过添加给定元素并返回数组的新长度来更改数组.' + }, + shift: { + '!type': 'fn() -> !this.', + '!doc': '从数组中删除第一个元素并返回该元素.此方法更改数组的长度.' + }, + unshift: { + '!type': 'fn(newelt: ?) -> number', + '!effects': ['propagate !0 !this.'], + '!doc': '将一个或多个元素添加到数组的开头,并返回数组的新长度.' + }, + slice: { + '!type': 'fn(from?: number, to?: number) -> !this', + '!doc': '返回数组一部分的浅表副本.' + }, + reverse: { + '!type': 'fn()', + '!doc': '就地反转数组.第一个数组元素变为最后一个,而最后一个数组变为第一个.' + }, + sort: { + '!type': 'fn(compare?: fn(a: ?, b: ?) -> number)', + '!effects': ['call !0 !this. !this.'], + '!doc': '将数组中的元素排序并返回数组.' + }, + indexOf: { + '!type': 'fn(elt: ?, from?: number) -> number', + '!doc': '返回在数组中可以找到给定元素的第一个索引;如果不存在,则返回-1.' + }, + lastIndexOf: { + '!type': 'fn(elt: ?, from?: number) -> number', + '!doc': '返回在数组中找到给定元素的最后一个索引,如果不存在则返回-1.从fromIndex开始向后搜索数组.' + }, + filter: { + '!type': + 'fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> !this', + '!effects': ['call !0 this=!1 !this. number !this'], + '!doc': '创建一个新数组,其中包含所有通过提供的功能实现的测试的元素.' + }, + forEach: { + '!type': + 'fn(f: fn(elt: ?, i: number, array: +Array), context?: ?)', + '!effects': ['call !0 this=!1 !this. number !this'], + '!doc': '每个数组元素执行一次提供的功能.' + }, + map: { + '!type': + 'fn(f: fn(elt: ?, i: number, array: +Array) -> ?, context?: ?) -> [!0.!ret]', + '!effects': ['call !0 this=!1 !this. number !this'], + '!doc': '创建一个新数组,其结果是对该数组中的每个元素调用提供的函数.' + }, + reduce: { + '!type': + 'fn(combine: fn(sum: ?, elt: ?, i: number, array: +Array) -> ?, init?: ?) -> !0.!ret', + '!effects': ['call !0 !1 !this. number !this'], + '!doc': '对一个累加器和数组的每个值(从左到右)应用一个函数,以将其减小为单个值.' + }, + fill: { + '!type': + 'fn(value: ?, start?: number, end?: number) -> !this', + '!doc': ' fill()方法使用静态值填充数组的所有元素,从开始索引到结束索引.,' + }, + find: { + '!type': + 'fn(callback: fn(element: ?, index: number, array: [?]) -> bool, thisArg?: ?) -> !this.', + '!effects': ['call !0 this=!2 !this. number'], + '!doc': '如果数组中的元素满足提供的测试功能,则find()方法将在数组中返回一个值.否则,返回undefined.,' + }, + findIndex: { + '!type': + 'fn(callback: fn(element: ?, index: number, array: [?]), thisArg?: ?) -> number', + '!effects': ['call !0 this=!2 !this. number'], + '!doc': '如果数组中的元素满足提供的测试功能,则findIndex()方法将返回数组中的索引.否则返回-1.,' + }, + keys: { + '!type': 'fn() -> +iter[:t=number]', + '!doc': ' keys()方法返回一个新的数组迭代器,其中包含数组中每个索引的键.,' + }, + values: { + '!type': 'fn() -> +iter[:t=!this.]', + '!doc': ' values()方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的值.,' + }, + includes: { + '!type': 'fn(value: ?, fromIndex?: number) -> bool', + '!doc': '确定数组是否包含某个元素,并根据需要返回true或false.,' + } + } }, - "className": { - "!type": "string", - "!doc": "获取并设置指定元素的class属性的值.", + String: { + '!type': 'fn(value: ?) -> string', + prototype: { + '!stdProto': 'String', + length: { + '!type': 'number', + '!doc': '表示字符串的长度.' + }, + '': 'string', + charAt: { + '!type': 'fn(i: number) -> string', + '!doc': '从字符串中返回指定的字符.' + }, + charCodeAt: { + '!type': 'fn(i: number) -> number', + '!doc': '返回给定索引处字符的数字Unicode值(Unicode代码点> 0x10000除外).' + }, + indexOf: { + '!type': 'fn(char: string, from?: number) -> number', + '!doc': '返回指定值首次出现的调用String对象中的索引,从fromIndex开始搜索, n如果未找到该值,则返回-1.' + }, + lastIndexOf: { + '!type': 'fn(char: string, from?: number) -> number', + '!doc': '返回指定值最后一次出现的调用String对象内的索引,如果未找到则返回-1.从fromIndex开始向后搜索调用字符串.' + }, + substring: { + '!type': 'fn(from: number, to?: number) -> string', + '!doc': '返回一个索引与另一个索引之间或字符串末尾的字符串子集.
from为起始位置,to为终止位置.' + }, + substr: { + '!type': 'fn(from: number, length?: number) -> string', + '!doc': '以指定的字符数返回从指定位置开始的字符串中的字符.
from为起始位置,length为长度' + }, + slice: { + '!type': 'fn(from: number, to?: number) -> string', + '!doc': '提取字符串的一部分并返回新的字符串.' + }, + padStart: { + '!type': + 'fn(targetLength: number, padString?: string) -> string', + '!doc': '用另一个字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.' + }, + padEnd: { + '!type': + 'fn(targetLength: number, padString?: string) -> string', + '!doc': '用给定的字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.' + }, + trim: { + '!type': 'fn() -> string', + '!doc': '从字符串的两端删除空格.' + }, + trimStart: { + '!type': 'fn() -> string', + '!doc': '从字符串的开头删除空格.' + }, + trimEnd: { + '!type': 'fn() -> string', + '!doc': '从字符串末尾删除空格.' + }, + toUpperCase: { + '!type': 'fn() -> string', + '!doc': '返回转换为大写的调用字符串值.' + }, + toLowerCase: { + '!type': 'fn() -> string', + '!doc': '返回转换为小写的调用字符串值.' + }, + split: { + '!type': + 'fn(pattern?: string|+RegExp, limit?: number) -> [string]', + '!doc': '通过将字符串分成子字符串,将String对象拆分为字符串数组.' + }, + concat: { + '!type': 'fn(other: string) -> string', + '!doc': '将两个或多个字符串的文本合并,并返回一个新字符串.' + }, + match: { + '!type': 'fn(pattern: +RegExp) -> [string]', + '!doc': '用于将字符串与正则表达式匹配时用于检索匹配.' + }, + replace: { + '!type': + 'fn(pattern: string|+RegExp, replacement: string) -> string', + '!doc': '返回一个新字符串,该字符串的某个或所有匹配项都由替换项替换.该模式可以是字符串或RegExp,并且替换项可以是字符串或每个匹配项将调用的函数. ' + }, + endsWith: { + '!type': + 'fn(searchString: string, position?: number) -> bool', + '!doc': ' endsWith()方法确定一个字符串是否以另一个字符串的字符结尾,并根据需要返回true或false.,' + }, + startsWith: { + '!type': + 'fn(searchString: string, position?: number) -> bool', + '!doc': ' startsWith()方法确定一个字符串是否以另一个字符串的字符开头,并根据需要返回true或false.,' + } + } }, - "style": { - "cssText": "string", - "alignmentBaseline": "string", - "background": "string", - "backgroundAttachment": "string", - "backgroundClip": "string", - "backgroundColor": "string", - "backgroundImage": "string", - "backgroundOrigin": "string", - "backgroundPosition": "string", - "backgroundPositionX": "string", - "backgroundPositionY": "string", - "backgroundRepeat": "string", - "backgroundRepeatX": "string", - "backgroundRepeatY": "string", - "backgroundSize": "string", - "baselineShift": "string", - "border": "string", - "borderBottom": "string", - "borderBottomColor": "string", - "borderBottomLeftRadius": "string", - "borderBottomRightRadius": "string", - "borderBottomStyle": "string", - "borderBottomWidth": "string", - "borderCollapse": "string", - "borderColor": "string", - "borderImage": "string", - "borderImageOutset": "string", - "borderImageRepeat": "string", - "borderImageSlice": "string", - "borderImageSource": "string", - "borderImageWidth": "string", - "borderLeft": "string", - "borderLeftColor": "string", - "borderLeftStyle": "string", - "borderLeftWidth": "string", - "borderRadius": "string", - "borderRight": "string", - "borderRightColor": "string", - "borderRightStyle": "string", - "borderRightWidth": "string", - "borderSpacing": "string", - "borderStyle": "string", - "borderTop": "string", - "borderTopColor": "string", - "borderTopLeftRadius": "string", - "borderTopRightRadius": "string", - "borderTopStyle": "string", - "borderTopWidth": "string", - "borderWidth": "string", - "bottom": "string", - "boxShadow": "string", - "boxSizing": "string", - "captionSide": "string", - "clear": "string", - "clip": "string", - "clipPath": "string", - "clipRule": "string", - "color": "string", - "colorInterpolation": "string", - "colorInterpolationFilters": "string", - "colorProfile": "string", - "colorRendering": "string", - "content": "string", - "counterIncrement": "string", - "counterReset": "string", - "cursor": "string", - "direction": "string", - "display": "string", - "dominantBaseline": "string", - "emptyCells": "string", - "enableBackground": "string", - "fill": "string", - "fillOpacity": "string", - "fillRule": "string", - "filter": "string", - "float": "string", - "floodColor": "string", - "floodOpacity": "string", - "font": "string", - "fontFamily": "string", - "fontSize": "string", - "fontStretch": "string", - "fontStyle": "string", - "fontVariant": "string", - "fontWeight": "string", - "glyphOrientationHorizontal": "string", - "glyphOrientationVertical": "string", - "height": "string", - "imageRendering": "string", - "kerning": "string", - "left": "string", - "letterSpacing": "string", - "lightingColor": "string", - "lineHeight": "string", - "listStyle": "string", - "listStyleImage": "string", - "listStylePosition": "string", - "listStyleType": "string", - "margin": "string", - "marginBottom": "string", - "marginLeft": "string", - "marginRight": "string", - "marginTop": "string", - "marker": "string", - "markerEnd": "string", - "markerMid": "string", - "markerStart": "string", - "mask": "string", - "maxHeight": "string", - "maxWidth": "string", - "minHeight": "string", - "minWidth": "string", - "opacity": "string", - "orphans": "string", - "outline": "string", - "outlineColor": "string", - "outlineOffset": "string", - "outlineStyle": "string", - "outlineWidth": "string", - "overflow": "string", - "overflowWrap": "string", - "overflowX": "string", - "overflowY": "string", - "padding": "string", - "paddingBottom": "string", - "paddingLeft": "string", - "paddingRight": "string", - "paddingTop": "string", - "page": "string", - "pageBreakAfter": "string", - "pageBreakBefore": "string", - "pageBreakInside": "string", - "pointerEvents": "string", - "position": "string", - "quotes": "string", - "resize": "string", - "right": "string", - "shapeRendering": "string", - "size": "string", - "speak": "string", - "src": "string", - "stopColor": "string", - "stopOpacity": "string", - "stroke": "string", - "strokeDasharray": "string", - "strokeDashoffset": "string", - "strokeLinecap": "string", - "strokeLinejoin": "string", - "strokeMiterlimit": "string", - "strokeOpacity": "string", - "strokeWidth": "string", - "tabSize": "string", - "tableLayout": "string", - "textAlign": "string", - "textAnchor": "string", - "textDecoration": "string", - "textIndent": "string", - "textLineThrough": "string", - "textLineThroughColor": "string", - "textLineThroughMode": "string", - "textLineThroughStyle": "string", - "textLineThroughWidth": "string", - "textOverflow": "string", - "textOverline": "string", - "textOverlineColor": "string", - "textOverlineMode": "string", - "textOverlineStyle": "string", - "textOverlineWidth": "string", - "textRendering": "string", - "textShadow": "string", - "textTransform": "string", - "textUnderline": "string", - "textUnderlineColor": "string", - "textUnderlineMode": "string", - "textUnderlineStyle": "string", - "textUnderlineWidth": "string", - "top": "string", - "unicodeBidi": "string", - "unicodeRange": "string", - "vectorEffect": "string", - "verticalAlign": "string", - "visibility": "string", - "whiteSpace": "string", - "width": "string", - "wordBreak": "string", - "wordSpacing": "string", - "wordWrap": "string", - "writingMode": "string", - "zIndex": "string", - "zoom": "string", - "!doc": "返回一个表示元素的style属性的对象." + Number: { + '!type': 'fn(value: ?) -> number', + MAX_VALUE: { + '!type': 'number', + '!doc': ' JavaScript中可表示的最大数值.' + }, + MIN_VALUE: { + '!type': 'number', + '!doc': ' JavaScript中可表示的最小正数值.' + }, + POSITIVE_INFINITY: { + '!type': 'number', + '!doc': '代表正无穷大值的值.' + }, + NEGATIVE_INFINITY: { + '!type': 'number', + '!doc': '代表负无穷大值的值.' + }, + prototype: { + '!stdProto': 'Number', + toString: { + '!type': 'fn(radix?: number) -> string', + '!doc': '返回代表指定Number对象的字符串' + }, + toFixed: { + '!type': 'fn(digits: number) -> string', + '!doc': '使用定点符号格式化数字' + }, + toExponential: { + '!type': 'fn(digits: number) -> string', + '!doc': '返回以指数表示形式表示Number对象的字符串' + }, + toPrecision: { + '!type': 'fn(digits: number) -> string', + '!doc': ' toPrecision()方法返回一个字符串,该数字表示指定精度的数字.' + } + }, + EPSILON: { + '!type': 'number', + '!doc': ' Number.EPSILON属性表示一个数值与可以表示为Number的最小值之间的差异.,' + }, + MAX_SAFE_INTEGER: { + '!type': 'number', + '!doc': ' Number.MAX_SAFE_INTEGER常量表示JavaScript中的最大安全整数(2 ^ 53-1).,' + }, + MIN_SAFE_INTEGER: { + '!type': 'number', + '!doc': ' Number.MIN_SAFE_INTEGER常量表示JavaScript(-(2 ^ 53-1))中的最小安全整数.,' + }, + isFinite: { + '!type': 'fn(testValue: ?) -> bool', + '!doc': ' Number.isFinite()方法确定传递的值是否为有限值.,' + }, + isInteger: { + '!type': 'fn(testValue: ?) -> bool', + '!doc': ' Number.isInteger()方法确定传递的值是否为整数.,' + }, + isNaN: { + '!type': 'fn(testValue: ?) -> bool', + '!doc': ' Number.isNaN()方法确定传递的值是否为NaN.原始全局isNaN()的更可靠的版本.,' + }, + isSafeInteger: { + '!type': 'fn(testValue: ?) -> bool', + '!doc': ' Number.isSafeInteger()方法确定所提供的值是否是一个安全整数的数字.安全整数是该数字的整数.' + }, + parseFloat: { + '!type': 'fn(string: string) -> number', + '!doc': ' Number.parseFloat()方法解析字符串参数并返回浮点数.,' + }, + parseInt: { + '!type': 'fn(string: string, radix?: number) -> number', + '!doc': ' Number.parseInt()方法解析字符串参数并返回指定基数或基数的整数.,' + } }, - "classList": { - "!type": "+DOMTokenList", - "!doc": "返回元素的class属性的标记列表." + Boolean: { + '!type': 'fn(value: ?) -> bool', + prototype: { + '!stdProto': 'Boolean' + } }, - "title": { - "!type": "string", - "!doc": " \"\u5efa\u7acb\u5f53\u9f20\u6807\u60ac\u505c\u5728\u663e\u793a\u7684\u8282\u70b9\u4e0a\u65f6\u5728\"\u5de5\u5177\u63d0\u793a\"\u5f39\u51fa\u7a97\u53e3\u4e2d\u663e\u793a\u7684\u6587\u672c.\"," + abstract: '?', + arguments: '?', + boolean: '?', + break: '?', + byte: '?', + case: '?', + catch: '?', + char: '?', + const: '?', + continue: '?', + debugger: '?', + default: '?', + delete: '?', + do: '?', + double: '?', + else: '?', + eval: '?', + false: 'bool', + final: '?', + finally: '?', + float: '?', + for: '?', + function: '?', + goto: '?', + if: '?', + implements: '?', + in: '?', + instanceof: '?', + int: '?', + interface: '?', + long: '?', + native: '?', + new: '?', + null: '?', + package: '?', + private: '?', + protected: '?', + public: '?', + return: '?', + short: '?', + static: '?', + switch: '?', + synchronized: '?', + this: '?', + throw: '?', + throws: '?', + transient: '?', + true: 'bool', + try: '?', + typeof: '?', + var: '?', + void: '?', + volatile: '?', + while: '?', + with: '?', + yield: '?', + RegExp: { + '!type': 'fn(source: string, flags?: string)', + prototype: { + '!stdProto': 'RegExp', + exec: { + '!type': 'fn(input: string) -> [string]', + '!doc': '搜索指定字符串中的匹配项.返回结果数组,或者为null.' + }, + test: { + '!type': 'fn(input: string) -> bool', + '!doc': '执行正则表达式和指定字符串之间的匹配搜索.返回true或false.' + } + }, + '!doc': '创建正则表达式对象以将文本与模式匹配.' }, - "width": { - "!type": "number", - "!doc": "返回元素的布局宽度." + parseInt: { + '!type': 'fn(string: string, radix?: number) -> number', + '!doc': '解析字符串参数并返回指定基数或基数的整数.' }, - "height": { - "!type": "number", - "!doc": "元素相对于元素的offsetParent的高度." + parseFloat: { + '!type': 'fn(string: string) -> number', + '!doc': '解析字符串参数并返回浮点数.' }, - "getContext": { - "!type": "fn(id: string) -> CanvasRenderingContext2D", - "!doc": " DOM画布元素公开了HTMLCanvasElement接口,该接口提供了用于操纵画布元素的布局和表示的属性和方法.HTMLCanvasElement接口继承了元素对象接口的属性和方法.", + isNaN: { + '!type': 'fn(value: number) -> bool', + '!doc': '确定值是否为NaN.请注意,此函数已损坏.您可能对ECMAScript 6 Number.isNaN感兴趣.' }, - "innerHTML": { - "!type": "string", - "!doc": "设置或获取描述元素后代的HTML语法.", + isFinite: { + '!type': 'fn(value: number) -> bool', + '!doc': '确定传递的值是否为有限数字.' + }, + eval: { + '!type': 'fn(code: string) -> ?', + '!doc': '评估以字符串形式表示的JavaScript代码.' + }, + encodeURI: { + '!type': 'fn(uri: string) -> string', + '!doc': '通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)(对于字符而言将仅是四个转义序列由两个"代理"字符组成).' + }, + encodeURIComponent: { + '!type': 'fn(uri: string) -> string', + '!doc': '通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)组件(对于由两个"代理"字符组成的字符).' + }, + decodeURI: { + '!type': 'fn(uri: string) -> string', + '!doc': '解码以前由encodeURI或类似例程创建的统一资源标识符(URI).' + }, + decodeURIComponent: { + '!type': 'fn(uri: string) -> string', + '!doc': '解码以前由encodeURIComponent或类似例程创建的统一资源标识符(URI)组件.' + }, + Math: { + E: { + '!type': 'number', + '!doc': '自然对数的底数,e约为2.718.' + }, + LN2: { + '!type': 'number', + '!doc': ' 2的自然对数,大约为0.693.' + }, + LN10: { + '!type': 'number', + '!doc': ' 10的自然对数,大约为2.302.' + }, + LOG2E: { + '!type': 'number', + '!doc': ' E的以2为底的对数(大约1.442).' + }, + LOG10E: { + '!type': 'number', + '!doc': ' E的以10为底的对数(约0.434).' + }, + SQRT1_2: { + '!type': 'number', + '!doc': ' 1/2的平方根;等效于2的平方根上的1,大约为0.707.' + }, + SQRT2: { + '!type': 'number', + '!doc': ' 2的平方根,大约为1.414.' + }, + PI: { + '!type': 'number', + '!doc': '圆的周长与其直径之比,大约为3.14159.' + }, + abs: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的绝对值.' + }, + cos: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的余弦.' + }, + sin: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的正弦.' + }, + tan: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的正切值.' + }, + acos: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的反余弦(以弧度为单位).' + }, + asin: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的反正弦(以弧度为单位).' + }, + atan: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的反正切(以弧度为单位).' + }, + atan2: { + '!type': 'fn(y: number, x: number) -> number', + '!doc': '返回其参数商的反正切值.' + }, + ceil: { + '!type': 'fn(number) -> number', + '!doc': '返回大于或等于数字的最小整数.' + }, + floor: { + '!type': 'fn(number) -> number', + '!doc': '返回小于或等于数字的最大整数.' + }, + round: { + '!type': 'fn(number) -> number', + '!doc': '返回四舍五入到最接近整数的数字的值.' + }, + exp: { + '!type': 'fn(number) -> number', + '!doc': '返回E ^ x,其中x是自变量,E是欧拉常数,自然对数的底.' + }, + log: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的自然对数(以E为底).' + }, + sqrt: { + '!type': 'fn(number) -> number', + '!doc': '返回数字的平方根.' + }, + pow: { + '!type': 'fn(number, number) -> number', + '!doc': '将基数返回指数幂,即baseexponent.' + }, + max: { + '!type': 'fn(number, number) -> number', + '!doc': '返回零个或多个数字中的最大值.' + }, + min: { + '!type': 'fn(number, number) -> number', + '!doc': '返回零个或多个数字中的最小值.' + }, + random: { + '!type': 'fn() -> number', + '!doc': '返回一个浮点伪随机数,范围为[0,1),即从0(包括)到不包括1(排除),然后您可以缩放到所需的值范围.' + }, + log10: { + '!type': 'fn(x: number) -> number', + '!doc': ' Math.log10()函数返回数字的以10为底的对数.' + }, + log2: { + '!type': 'fn(x: number) -> number', + '!doc': ' Math.log2()函数返回数字的以2为底的对数.' + }, + sign: { + '!type': 'fn(x: number) -> number', + '!doc': ' Math.sign()函数返回数字的符号,指示数字是正数,负数还是零.,' + }, + trunc: { + '!type': 'fn(x: number) -> number', + '!doc': ' Math.trunc()函数通过删除任何小数位来返回数字的整数部分.它不舍入任何数字.该函数可以用floor()和ceil()函数表示: ,' + }, + '!doc': '一个内置对象,具有用于数学常数和函数的属性和方法.' + }, + JSON: { + parse: { + '!type': + 'fn(json: string, reviver?: fn(key: string, value: ?) -> ?) -> ?', + '!doc': '将字符串解析为JSON,可以选择转换解析产生的值.' + }, + stringify: { + '!type': + 'fn(value: ?, replacer?: fn(key: string, value: ?) -> ?, space?: string|number) -> string', + '!doc': '将值转换为JSON,如果指定了replacer函数,则可以选择替换值,如果指定了replacer数组,则可以选择仅包括指定的属性.' + }, + '!doc': ' JSON(JavaScript对象表示法)是一种数据交换格式.尽管它不是严格的子集,但它非常类似于JavaScript语法的子集.(有关详细信息,请参见JavaScript参考中的JSON.)在编写任何类型的基于JavaScript的应用程序(包括网站和浏览器扩展程序)时非常有用.例如,您可以将JSON格式的用户信息存储在cookie中,或者可以将扩展名首选项以JSON形式存储在字符串值的浏览器首选项中.' } - }, - "!doc": "表示HTML或XML文档中的元素.", }, - "Document": { - "!type": "fn()", - "prototype": { - "!proto": "Node.prototype", - "height": { - "!type": "number", - "!doc": "返回当前文档的元素的高度.", + { + '!name': 'core', + '!define': { + image: { + '!doc': '图片信息', + width: 'number', + height: 'number', + src: 'string' + }, + audio: { + '!doc': '音乐音效信息', + currentTime: 'number', + play: 'fn()', + pause: 'fn()', + paused: 'bool', + duration: 'number', + volume: 'number' + }, + flag: { + '!doc': '当前变量', + hard: { + '!type': 'number', + '!doc': '当前难度编号' + }, + hatred: { + '!type': 'number', + '!doc': '当前仇恨值' + }, + poison: { + '!type': 'bool', + '!doc': '是否处于中毒状态' + }, + weak: { + '!type': 'number', + '!doc': '是否处于衰弱状态' + }, + curse: { + '!type': 'number', + '!doc': '是否处于诅咒状态' + }, + no_zone: { + '!type': 'bool', + '!doc': '无视领域伤害' + }, + no_repulse: { + '!type': 'bool', + '!doc': '无视阻击伤害' + }, + no_lasel: { + '!type': 'bool', + '!doc': '无视激光伤害' + }, + no_ambush: { + '!type': 'bool', + '!doc': '无视捕捉' + }, + __bgm__: { + '!type': 'string', + '!doc': '背景音乐' + }, + __weather__: { + '!doc': '天气' + }, + __color__: { + '!doc': '色调' + }, + __volume__: { + '!type': 'number', + '!doc': '音量' + }, + skill: { + '!type': 'number', + '!doc': '当前开启的技能编号' + }, + skillName: { + '!type': 'string', + '!doc': '当前开启的技能名' + }, + input: { + '!type': 'string|number', + '!doc': '等待用户输入后的存放值' + }, + type: { + '!type': 'number', + '!doc': '等待用户操作后获得的操作类型' + }, + keycode: { + '!type': 'number', + '!doc': '等待用户操作后用户按键的键值' + }, + x: { + '!type': 'number', + '!doc': '等待用户操作后用户点击的网格横坐标' + }, + y: { + '!type': 'number', + '!doc': '等待用户操作后用户点击的网格纵坐标' + }, + px: { + '!type': 'number', + '!doc': '等待用户操作后用户点击的像素横坐标' + }, + py: { + '!type': 'number', + '!doc': '等待用户操作后用户点击的像素纵坐标' + }, + __visited__: { + '!doc': '当前访问过的楼层' + }, + __leaveLoc__: { + '!doc': '每个楼层的离开位置,用于楼传平面塔模式' + }, + cannotMoveDirectly: { + '!type': 'bool', + '!doc': '当前是否全局不可瞬移' + } + }, + hero: { + '!doc': '勇士当前属性', + image: { + '!type': 'string', + '!doc': '行走图' + }, + animate: { + '!type': 'bool', + '!doc': '是否开启帧动画' + }, + name: { + '!type': 'string', + '!doc': '勇士名' + }, + lv: { + '!type': 'number', + '!doc': '勇士等级' + }, + hpmax: { + '!type': 'number', + '!doc': '勇士生命上限' + }, + hp: { + '!type': 'number', + '!doc': '勇士当前生命值' + }, + atk: { + '!type': 'number', + '!doc': '勇士当前攻击力' + }, + def: { + '!type': 'number', + '!doc': '勇士当前防御力' + }, + manamax: { + '!type': 'number', + '!doc': '勇士当前魔力上限,负数无效' + }, + mana: { + '!type': 'number', + '!doc': '勇士当前魔力值' + }, + mdef: { + '!type': 'number', + '!doc': '勇士当前护盾值' + }, + money: { + '!type': 'number', + '!doc': '勇士当前金币' + }, + exp: { + '!type': 'number', + '!doc': '勇士当前经验' + }, + equipment: { + '!type': '[string]', + '!doc': '勇士当前装备' + }, + items: { + '!doc': '勇士当前道具', + constants: { + '!doc': '永久道具' + }, + tools: { + '!doc': '消耗道具', + yellowKey: { + '!type': 'number', + '!doc': '黄钥匙个数' + }, + blueKey: { + '!type': 'number', + '!doc': '蓝钥匙个数' + }, + redKey: { + '!type': 'number', + '!doc': '红钥匙个数' + }, + greenKey: { + '!type': 'number', + '!doc': '绿钥匙个数' + }, + steelKey: { + '!type': 'number', + '!doc': '铁门钥匙个数' + } + }, + equips: { + '!doc': '在背包中未装备上的装备' + } + }, + loc: { + '!doc': '勇士当前坐标和朝向', + x: 'number', + y: 'number', + direction: { + '!doc': '朝向,只能为 up,down,left,right 之一', + '!type': 'string' + } + }, + flags: { + '!type': 'flag', + '!doc': '当前游戏中用到的变量' + }, + followers: { + '!type': '[?]', + '!doc': '跟随者信息' + }, + steps: { + '!type': 'number', + '!doc': '当前步数' + } + }, + block: { + '!doc': '地图图块信息', + x: { + '!type': 'number', + '!doc': '图块的x坐标' + }, + y: { + '!type': 'number', + '!doc': '图块的y坐标' + }, + id: { + '!type': 'number', + '!doc': '图块的数字' + }, + event: { + '!doc': '图块上的事件信息', + id: { + '!type': 'string', + '!doc': '图块的ID' + }, + cls: { + '!type': 'string', + '!doc': '图块的类别,一般为所在图片名去掉后缀' + }, + disabled: { + '!type': 'bool', + '!doc': '启用状态' + } + } + }, + blockInfo: { + '!doc': '图块的更多信息', + animate: { + '!type': 'number', + '!doc': '动画帧数' + }, + cls: { + '!type': 'string', + '!doc': '图块类别' + }, + faceIds: { + '!doc': '行走图朝向', + up: 'string', + down: 'string', + left: 'string', + right: 'string' + }, + height: { + '!type': 'number', + '!doc': '图块高度' + }, + id: { + '!type': 'string', + '!doc': '图块ID' + }, + image: { + '!type': 'image', + '!doc': '图块所在的图片' + }, + name: { + '!type': 'string', + '!doc': '图块名称' + }, + number: { + '!type': 'number', + '!doc': '图块使用的数字' + }, + posX: { + '!type': 'number', + '!doc': '图块在图片上的横坐标' + }, + posY: { + '!type': 'number', + '!doc': '图块在图片上的纵坐标' + } + }, + enemy: { + '!doc': '怪物信息', + id: { + '!type': 'string', + '!doc': '怪物ID' + }, + name: { + '!type': 'string', + '!doc': '怪物名称' + }, + displayIdInBook: { + '!type': 'string', + '!doc': '在怪物手册映射ID' + }, + hp: { + '!type': 'number', + '!doc': '怪物生命值' + }, + atk: { + '!type': 'number', + '!doc': '怪物攻击' + }, + def: { + '!type': 'number', + '!doc': '怪物防御' + }, + money: { + '!type': 'number', + '!doc': '怪物金币' + }, + exp: { + '!type': 'number', + '!doc': '怪物经验' + }, + special: { + '!type': '[number]', + '!doc': '怪物特殊属性' + }, + point: { + '!type': 'number', + '!doc': '怪物加点' + }, + value: { + '!type': 'number', + '!doc': '怪物特殊属性值:阻激夹域伤害值;吸血比例;光环增加生命比例' + }, + zoneSquare: { + '!type': 'bool', + '!doc': '领域怪是否九宫格伤害;区域光环是否九宫格范围' + }, + range: { + '!type': 'number', + '!doc': '领域伤害的范围;区域光环范围' + }, + notBomb: { + '!type': 'bool', + '!doc': '怪物不可炸' + }, + n: { + '!type': 'number', + '!doc': '多连击的连击数;净化比例' + }, + add: { + '!type': 'bool', + '!doc': '吸血是否加到自身;光环是否叠加' + }, + atkValue: { + '!type': 'number', + '!doc': '反击比例;退化扣除攻击;光环增加攻击;' + }, + defValue: { + '!type': 'number', + '!doc': '破甲比例;退化扣除防御;光环增加防御' + }, + damage: { + '!type': 'number', + '!doc': '固伤值' + } + }, + item: { + '!doc': '道具信息', + id: { + '!type': 'string', + '!doc': '道具ID' + }, + cls: { + '!type': 'string', + '!doc': '道具类型' + }, + name: { + '!type': 'string', + '!doc': '道具名称' + }, + text: { + '!type': 'string', + '!doc': '道具描述' + }, + hideInToolbox: { + '!type': 'bool', + '!doc': '不显示在道具栏' + }, + equip: { + '!doc': '装备属性', + type: { + '!type': 'number|string', + '!doc': '装备类型' + }, + animate: { + '!type': 'string', + '!doc': '装备动画' + }, + value: { + '!doc': '数值加成' + }, + percentage: { + '!doc': '比例加成' + } + }, + hideInReplay: { + '!type': 'bool', + '!doc': '回放不绘制道具栏' + } + }, + floor: { + '!doc': '楼层信息', + floorId: { + '!type': 'string', + '!doc': '楼层ID' + }, + title: { + '!type': 'string', + '!doc': '楼层中文名' + }, + name: { + '!type': 'string', + '!doc': '状态栏显示值' + }, + width: { + '!type': 'number', + '!doc': '地图宽' + }, + height: { + '!type': 'number', + '!doc': '地图高' + }, + canFlyTo: { + '!type': 'bool', + '!doc': '该楼是否可以楼传飞到' + }, + canFlyFrom: { + '!type': 'bool', + '!doc': '该楼是否可以楼传飞出' + }, + canUseQuickShop: { + '!type': 'bool', + '!doc': '该楼是否可快捷商店' + }, + cannotViewMap: { + '!type': 'bool', + '!doc': '该层是否不允许被浏览地图看到,也不统计' + }, + cannotMoveDirectly: { + '!type': 'bool', + '!doc': '该层是否不允许瞬间移动' + }, + upFloor: { + '!type': '[number]', + '!doc': '上楼点' + }, + downFloor: { + '!type': '[number]', + '!doc': '下楼点' + }, + flyPoint: { + '!type': '[number]', + '!doc': '楼传落点' + }, + color: { + '!doc': '楼层色调' + }, + weather: { + '!doc': '楼层天气' + }, + bgm: { + '!type': 'string', + '!doc': '楼层背景音乐' + }, + ratio: { + '!type': 'number', + '!doc': '宝石/血瓶效果' + }, + map: { + '!type': '[[number]]', + '!doc': '地图数据' + }, + blocks: { + '!type': '[block]', + '!doc': '本层图块信息' + } + }, + animate: { + '!doc': '动画信息', + se: { + '!type': 'string', + '!doc': '动画音效' + } + } }, - "width": { - "!type": "number", - "!doc": "以像素为单位返回当前文档的元素的宽度.", + core: { + '!doc': '核心游戏控制', + __SIZE__: { + '!type': 'number', + '!doc': '窗口宽度,为13或15' + }, + __PIXELS__: { + '!type': 'number', + '!doc': '窗口像素宽度,为416或480' + }, + __HALF_SIZE__: { + '!type': 'number', + '!doc': '窗口宽度的一半,为6或7' + }, + floorIds: { + '!type': '[string]', + '!doc': '全部楼层ID列表' + }, + floors: { + '!doc': '全部楼层信息' + }, + floorPartitions: { + '!type': '[[string]]', + '!doc': '楼层分区信息' + }, + material: { + '!doc': '游戏所用到的资源', + animates: { + '!doc': '注册的动画' + }, + images: { + '!doc': '注册的图片' + }, + bgms: { + '!doc': '注册的背景音乐' + }, + sounds: { + '!doc': '注册的音效' + }, + enemys: { + '!doc': '怪物定义' + }, + items: { + '!doc': '道具定义' + } + }, + timeout: { + '!doc': '当前异步事件句柄' + }, + interval: { + '!doc': '当前异步事件延时' + }, + animateFrame: { + '!doc': '当前各个帧动画' + }, + musicStatus: { + '!doc': '音乐音效状态', + bgmStatus: { + '!type': 'bool', + '!doc': '是否播放BGM' + }, + soundStatus: { + '!type': 'bool', + '!doc': '是否播放SE' + }, + playingBgm: { + '!type': 'string', + '!doc': '正在播放的bgm' + }, + lastBgm: { + '!type': 'string', + '!doc': '上次播放的bgm' + }, + playingSounds: { + '!doc': '正在播放的SE' + }, + volume: { + '!type': 'number', + '!doc': '当前bgm音量' + } + }, + platform: { + '!doc': '平台信息', + isPC: 'bool', + isAndroid: 'bool', + isIOS: 'bool' + }, + domStyle: { + '!doc': '界面样式', + scale: { + '!type': 'number', + '!doc': '当前界面放缩比例' + }, + ratio: { + '!type': 'number', + '!doc': '高清UI放缩比例' + }, + hdCanvas: { + '!type': '[string]', + '!doc': '高清UI的系统画布' + }, + availableScale: { + '!type': '[number]', + '!doc': '当前界面支持的放缩比例' + }, + isVertical: { + '!type': 'bool', + '!doc': '当前是否是竖屏' + }, + showStatusBar: { + '!type': 'bool', + '!doc': '当前是否显示状态栏' + }, + toolbarBtn: { + '!type': 'bool', + '!doc': '当前工具栏是否是1-8的按钮' + } + }, + bigmap: { + '!doc': '大地图信息', + canvas: { + '!type': '[string]', + '!doc': '大地图的画布' + }, + width: { + '!type': 'number', + '!doc': '大地图高度' + }, + height: { + '!type': 'number', + '!doc': '大地图宽度' + }, + offsetX: { + '!type': 'number', + '!doc': '大地图视角横向偏移量' + }, + offsetY: { + '!type': 'number', + '!doc': '大地图视角纵向偏移量' + }, + posX: { + '!type': 'number', + '!doc': '大地图视角横向基准格' + }, + posY: { + '!type': 'number', + '!doc': '大地图视角纵向基准格' + }, + v2: { + '!type': 'bool', + '!doc': '是否是新版大地图绘制方式' + }, + threshold: { + '!type': 'number', + '!doc': '新版大地图绘制方式的分界线' + }, + extend: { + '!type': 'number', + '!doc': '新版大地图模式下向每一侧额外计算的数量' + }, + scale: { + '!type': 'number', + '!doc': '缩略图的比例放缩' + }, + tempCanvas: { + '!type': 'CanvasRenderingContext2D', + '!doc': '临时画布' + } + }, + saves: { + '!doc': '当前存档信息' + }, + dymCanvas: { + '!doc': '各个自定义画布' + }, + statusBar: { + '!doc': '状态栏信息' + }, + canvas: { + '!doc': '系统画布' + }, + flags: { + '!doc': '系统开关' + }, + values: { + '!doc': '全局数值,如毒衰效果' + }, + firstData: { + '!doc': '初始属性,如出生点' + }, + status: { + '!doc': '状态信息', + hero: { + '!type': 'hero', + '!doc': '勇士信息' + }, + automaticRoute: { + '!doc': '自动寻路信息' + }, + bgmaps: { + '!doc': '各地图背景层' + }, + fgmaps: { + '!doc': '各地图前景层' + }, + mapBlockObjs: { + '!doc': '以<位置,block>存放的各地图图块信息' + }, + boxAnimateObjs: { + '!doc': '(手册和剧情文本的)帧动画对象' + }, + checkBlock: { + '!doc': '阻激夹域捕捉信息', + damage: { + '!doc': '每个点的伤害信息' + }, + type: { + '!doc': '每个点的伤害类型' + }, + repluse: { + '!doc': '每个点的阻击信息' + }, + ambush: { + '!doc': '每个点的捕捉信息' + }, + needCache: { + '!type': 'bool', + '!doc': '该楼层是否需要计算缓存' + }, + cache: { + '!doc': '每个点的光环缓存' + } + }, + damage: { + '!doc': '每个点的显伤信息' + }, + ctrlDown: { + '!type': 'bool', + '!doc': 'Ctrl键是否被按下' + }, + curtainColor: { + '!doc': '当前画面色调' + }, + event: { + '!doc': '当前事件', + data: { + '!doc': '事件信息,如坐标等' + }, + id: { + '!type': 'string', + '!doc': '事件类型,如选择项/确认框' + }, + interval: { + '!type': 'number', + '!doc': '打字机效果的定时器' + }, + selection: { + '!type': 'number', + '!doc': '选择项和确认框的当前选中项' + }, + ui: { + '!doc': '当前事件的界面信息,如楼传/手册/SL' + } + }, + floorAnimateObjs: { + '!doc': '楼层贴图的帧动画' + }, + floorId: { + '!type': 'string', + '!doc': '当前楼层ID' + }, + gameOver: { + '!type': 'bool', + '!doc': '游戏是否已结束' + }, + globalAnimateObjs: { + '!doc': '各全局动画' + }, + globalAnimateStatus: { + '!type': 'number', + '!doc': '全局动画的帧状态' + }, + globalAttribute: { + '!doc': '全局css属性' + }, + hard: { + '!type': 'string', + '!doc': '状态栏一角的难度名' + }, + downTime: { + '!type': 'number', + '!doc': '方向键已按下的时间' + }, + heroCenter: { + '!doc': '勇士中心像素坐标', + px: { + '!type': 'number', + '!doc': '勇士中心的横坐标' + }, + py: { + '!type': 'number', + '!doc': '勇士中心的纵坐标' + } + }, + heroMoving: { + '!type': 'number', + '!doc': '勇士行走的状态值' + }, + heroStop: { + '!type': 'bool', + '!doc': '勇士是否已停下' + }, + holdingKeys: { + '!type': '[number]', + '!doc': '当前按下的键' + }, + id2number: { + '!doc': '图块ID到数字的对应关系' + }, + lockControl: { + '!type': 'bool', + '!doc': '当前是否是锁定操作状态' + }, + maps: { + '!doc': '当前各地图信息' + }, + number2Block: { + '!doc': '数字到图块对象的对应关系' + }, + played: { + '!type': 'bool', + '!doc': '当前是否游戏中(不包括标题画面和录像回放)' + }, + replay: { + '!doc': '当前录像回放信息', + animate: { + '!type': 'bool', + '!doc': '回放是否正处于动画中' + }, + pausing: { + '!type': 'bool', + '!doc': '回放是否暂停中' + }, + replaying: { + '!type': 'bool', + '!doc': '当前是否回放中' + }, + save: { + '!type': '[]', + '!doc': '录像中的存档' + }, + speed: { + '!type': 'number', + '!doc': '回放速度' + }, + steps: { + '!type': 'number', + '!doc': '回放步数' + }, + toReplay: { + '!type': '[string]', + '!doc': '待回放的列表' + }, + totalList: { + '!type': '[string]', + '!doc': '回放总列表' + } + }, + route: { + '!type': '[string]', + '!doc': '当前录像内容' + }, + shops: { + '!doc': '全局商店列表' + }, + textAttribute: { + '!doc': '当前剧情文本属性' + }, + thisMap: { + '!type': 'floor', + '!doc': '当前地图信息' + } + }, + init: { + '!doc': '初始化core', + '!type': 'fn(coreData: ?, callback: fn())' + }, + doFunc: { + '!doc': '执行一个函数;如果函数名是字符串则转发到插件中', + '!type': 'fn(func: name|fn(), _this?: ?)' + }, + control: { + '!doc': '负责整个游戏的核心控制系统,分为如下几个部分:
- requestAnimationFrame相关
- 标题界面,开始和重新开始游戏
- 自动寻路和人物行走相关
- 画布、位置、阻激夹域、显伤等相关
- 录像的回放相关
- 存读档,自动存档,同步存档等相关
- 人物属性和状态、位置、变量等相关
- 天气、色调、音乐和音效的播放
- 状态栏和工具栏相关
- 界面resize相关', + showStatusBar: { + '!doc': '显示状态栏', + '!type': 'fn()' + }, + startReplay: { + '!doc': '开始播放录像', + '!type': 'fn(list: [string])' + }, + triggerReplay: { + '!doc': '播放或暂停录像回放', + '!type': 'fn()' + }, + screenFlash: { + '!doc': '画面闪烁
例如:core.screenFlash([255, 0, 0, 1], 3); // 红屏一闪而过
color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若填负数则会被视为0)的颜色数组,必填
time: 单次闪烁时长,实际闪烁效果为先花其三分之一的时间渐变到目标色调,再花剩余三分之二的时间渐变回去
times: 闪烁的总次数,不填或填0都视为1
moveMode: 渐变方式
callback: 闪烁全部完毕后的回调函数,可选', + '!type': + 'fn(color: [number], time: number, times?: number, moveMode?: string, callback?: fn())' + }, + setCurtain: { + '!doc': '更改画面色调,不计入存档。如需长期生效请使用core.events._action_setCurtain()函数
例如:core.setCurtain(); // 恢复画面色调,用时四分之三秒
color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若为负数则会被视为0)的颜色数组,不填视为[0, 0, 0, 0]
time: 渐变时间,单位为毫秒。不填视为750ms,负数视为0(无渐变,立即更改)
moveMode: 渐变方式
callback: 更改完毕后的回调函数,可选。事件流中常取core.doAction', + '!type': + 'fn(color?: [number], time?: number, moveMode?: string, callback?: fn())' + }, + updateDamage: { + '!doc': '注意!请勿使用该函数!请使用core.updateStatusBar()代替!!重算并绘制地图显伤
例如:core.updateDamage(); // 更新当前地图的显伤,绘制在显伤层(废话)
floorId: 地图id,不填视为当前地图。预览地图时填写
ctx: 绘制到的画布,如果填写了就会画在该画布而不是显伤层', + '!type': + 'fn(floorId?: string, ctx?: string|CanvasRenderingContext2D)' + }, + drawDamage: { + '!doc': '仅绘制地图显伤', + '!type': 'fn(string|CanvasRenderingContext2D)' + }, + nextX: { + '!doc': "获取主角面前第n格的横坐标
例如:core.closeDoor(core.nextX(), core.nextY(), 'yellowDoor', core.turnHero); // 在主角面前关上一扇黄门,然后主角顺时针旋转90°
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1", + '!type': 'fn(n?: number) -> number' + }, + nextY: { + '!doc': '获取主角面前第n格的纵坐标
例如:core.jumpHero(core.nextX(2), core.nextY(2)); // 主角向前跃过一格,即跳跃靴道具的使用效果
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1', + '!type': 'fn(n?: number) -> number' + }, + clearContinueAutomaticRoute: { + '!doc': '清空剩下的自动寻路列表', + '!type': 'fn(callback?: fn())' + }, + updateViewport: { + '!doc': '更新大地图的可见区域', + '!type': 'fn()' + }, + getMappedName: { + '!doc': '获得映射文件名', + '!type': 'fn(name: string) -> string' + }, + addFlag: { + '!doc': "增减一个flag变量,等价于 core.setFlag(name, core.getFlag(name, 0) + value)
例如:core.addFlag('hatred', 1); // 增加1点仇恨值
name: 变量名,支持中文
value: 变量的增量", + '!type': 'fn(name: string, value: number)' + }, + setFlag: { + '!doc': "设置一个flag变量
例如:core.setFlag('poison', true); // 令主角中毒
name: 变量名,支持中文
value: 变量的新值,不填或填null视为删除", + '!type': 'fn(name: string, value: ?)' + }, + playSound: { + '!doc': '播放一个音效
sound: 音效名;可以使用文件别名。
pitch: 播放的音调;可选,如果设置则为30-300之间的数值。
callback: 可选,播放完毕后执行的回调函数。
返回:一个数字,可用于core.stopSound的参数来只停止该音效。', + '!type': + 'fn(sound: string, pitch?: number, callback?: fn()) -> number' + }, + stopSound: { + '!doc': '停止播放音效。如果未指定id则停止所有音效,否则只停止指定的音效。', + '!type': 'fn(id?: number)' + }, + getPlayingSounds: { + '!doc': '获得当前正在播放的所有(指定)音效的id列表
name: 音效名,可用别名;不填代表返回正在播放的全部音效
返回值: 一个列表,每一项为一个正在播放的音效id;可用core.stopSound立刻停止播放', + '!type': 'fn(name?: string) -> [number]' + }, + addGameCanvasTranslate: { + '!doc': '加减画布偏移', + '!type': 'fn(x?: number, y?: number)' + }, + addBuff: { + '!doc': "增减主角某个属性的百分比修正倍率,加减法叠加和抵消。等价于 core.setBuff(name, core.getBuff(name) + value)
例如:core.addBuff('atk', -0.1); // 主角获得一层“攻击力减一成”的负面效果
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 倍率的增量", + '!type': 'fn(name: string, value: number)' + }, + drawHero: { + '!doc': '绘制主角和跟随者并重置视野到以主角为中心
例如:core.drawHero(); // 原地绘制主角的静止帧并重置视野野
status: 只能为 stop, leftFoot 和 rightFoot,不填用stop。
offset: 相对主角逻辑位置的偏移量,不填视为无偏移。
frame: 绘制的第几帧', + '!type': + 'fn(status?: string, offset?: number, frame?: number)' + }, + pauseBgm: { + '!doc': '暂停背景音乐的播放', + '!type': 'fn()' + }, + setBgmSpeed: { + '!doc': '设置背景音乐的播放速度和音调
speed: 播放速度,必须为30-300中间的值。100为正常速度。
usePitch: 是否同时改变音调(部分设备可能不支持)', + '!type': 'fn(speed: number, usePitch?: bool)' + }, + setReplaySpeed: { + '!doc': '设置播放速度', + '!type': 'fn(speed: number)' + }, + pauseReplay: { + '!doc': '暂停播放', + '!type': 'fn()' + }, + doSL: { + '!doc': '实际进行存读档事件', + '!type': 'fn(id?: string, type?: string)' + }, + setStatus: { + '!doc': "设置主角的某个属性
例如:core.setStatus('atk', 100); // 设置攻击力为100
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.setHeroLoc(name, value),其他的会直接对 core.status.hero[name] 赋值
value: 属性的新值", + '!type': 'fn(name: string, value: number)' + }, + setAutomaticRoute: { + '!doc': '半自动寻路,用于鼠标或手指拖动
例如:core.setAutomaticRoute(0, 0, [{direction: "right", x: 4, y: 9}, {direction: "right", x: 5, y: 9}]);
destX: 鼠标或手指的起拖点横坐标
destY: 鼠标或手指的起拖点纵坐标
stepPostfix: 拖动轨迹的数组表示,每项为一步的方向和目标点。', + '!type': + 'fn(destX: number, destY: number, stepPostfix: [{x: number, y: number, direction: string}])' + }, + setHeroOpacity: { + '!doc': '改变勇士的不透明度', + '!type': + 'fn(opacity?: number, moveMode?: string, time?: number, callback?: fn())' + }, + gatherFollowers: { + '!doc': '立刻聚集所有的跟随者', + '!type': 'fn()' + }, + getStatus: { + '!doc': "读取主角的某个属性,不包括百分比修正
例如:core.getStatus('atk'); // 读取主角的攻击力
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.getHeroLoc(name),其他的会直接读取 core.status.hero[name]", + '!type': 'fn(name: string) -> number' + }, + setHeroLoc: { + '!doc': "设置勇士位置
值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;
如需立刻重新绘制地图还需调用:core.clearMap('hero'); core.drawHero(); 来对界面进行更新。
例如:core.setHeroLoc('x', 5) // 将勇士当前位置的横坐标设置为5。
name: 要设置的坐标属性
value: 新值
noGather: 是否聚集跟随者", + '!type': + 'fn(name: string, value: string|number, noGather?: bool)' + }, + getLvName: { + '!doc': '根据级别的数字获取对应的名称,后者定义在全塔属性
例如:core.getLvName(); // 获取主角当前级别的名称,如“下级佣兵”
lv: 级别的数字,不填则视为主角当前的级别
返回值:级别的名称,如果不存在就还是返回数字', + '!type': 'fn(lv?: number) -> string|number' + }, + getNextLvUpNeed: { + '!doc': '获得下次升级需要的经验值。
升级扣除模式下会返回经验差值;非扣除模式下会返回总共需要的经验值。
如果无法进行下次升级,返回null。', + '!type': 'fn() -> number' + }, + addStatus: { + '!doc': "增减主角的某个属性,等价于core.setStatus(name, core.getStatus(name) + value)
例如:core.addStatus('atk', 100'); // 给主角攻击力加100
name: 属性的英文名
value: 属性的增量", + '!type': 'fn(name: string, value: number)' + }, + speedUpReplay: { + '!doc': '加速播放', + '!type': 'fn()' + }, + loadData: { + '!doc': '从本地读档', + '!type': 'fn(data?: ?, callback?: fn())' + }, + debug: { + '!doc': '开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。
此模式下不可回放录像和上传成绩。', + '!type': 'fn()' + }, + moveOneStep: { + '!doc': '每移动一格后执行的事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】', + '!type': 'fn(callback?: fn())' + }, + clearStatus: { + '!doc': '清除游戏状态和数据', + '!type': 'fn()' + }, + updateFollowers: { + '!doc': '更新跟随者坐标', + '!type': 'fn()' + }, + waitHeroToStop: { + '!doc': '等待主角停下
例如:core.waitHeroToStop(core.vibrate); // 等待主角停下,然后视野左右抖动1秒
callback: 主角停止后的回调函数', + '!type': 'fn(callback?: fn())' + }, + hideStatusBar: { + '!doc': '隐藏状态栏
showToolbox: 是否不隐藏竖屏工具栏', + '!type': 'fn(showToolbox?: bool)' + }, + getBuff: { + '!doc': "读取主角某个属性的百分比修正倍率,初始值为1
例如:core.getBuff('atk'); // 主角当前能发挥出多大比例的攻击力
name: 属性的英文名", + '!type': 'fn(name: string) -> number' + }, + triggerDebuff: { + '!doc': "获得或移除毒衰咒效果
action: 要获得还是移除,'get'为获得,'remove'为移除
type: 获得或移除的内容(poison/weak/curse),可以为字符串或数组", + '!type': 'fn(action: string, type: string|[string])' + }, + setToolbarButton: { + '!doc': '改变工具栏为按钮1-8', + '!type': 'fn(useButton?: bool)' + }, + getSaves: { + '!doc': '获得某些存档内容', + '!type': 'fn(ids?: ?, callback?: fn())' + }, + replay: { + '!doc': '回放下一个操作', + '!type': 'fn()' + }, + getStatusOrDefault: { + '!doc': '从status中获得属性,如果不存在则从勇士属性中获取', + '!type': 'fn(status?: ?, name?: string)' + }, + unregisterReplayAction: { + '!doc': '注销一个录像行为', + '!type': 'fn(name: string)' + }, + unregisterWeather: { + '!doc': '注销一个天气', + '!type': 'fn(name: string)' + }, + setBuff: { + '!doc': "设置主角某个属性的百分比修正倍率,初始值为1,
倍率存放在flag: '__'+name+'_buff__' 中
例如:core.setBuff('atk', 0.5); // 主角能发挥出的攻击力减半
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 新的百分比修正倍率,不填(效果上)视为1", + '!type': 'fn(name: string, value: number)' + }, + continueAutomaticRoute: { + '!doc': '继续剩下的自动寻路操作', + '!type': 'fn()' + }, + setAutoHeroMove: { + '!doc': '连续行走
例如:core.setAutoHeroMove([{direction: "up", step: 1}, {direction: "left", step: 3}]); // 上左左左
steps: 压缩的步伐数组,每项表示朝某方向走多少步', + '!type': 'fn(steps: [?])' + }, + unregisterResize: { + '!doc': '注销一个resize函数', + '!type': 'fn(name: string)' + }, + saveAndStopAutomaticRoute: { + '!doc': '保存剩下的寻路,并停止', + '!type': 'fn()' + }, + hideStartAnimate: { + '!doc': '淡出标题画面
例如:core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择
callback: 标题画面完全淡出后的回调函数', + '!type': 'fn(callback?: fn())' + }, + getAllSaves: { + '!doc': '获得所有存档内容', + '!type': 'fn(callback?: fn())' + }, + updateHeroIcon: { + '!doc': '更新状态栏的勇士图标', + '!type': 'fn(name: string)' + }, + setMusicBtn: { + '!doc': '设置音乐图标的显隐状态', + '!type': 'fn()' + }, + isPlaying: { + '!doc': '游戏是否已经开始', + '!type': 'fn() -> bool' + }, + triggerBgm: { + '!doc': '开启或关闭背景音乐的播放', + '!type': 'fn()' + }, + moveHero: { + '!doc': '连续前进,不撞南墙不回头
例如:core.moveHero(); // 连续前进
direction: 可选,如果设置了就会先转身到该方向
callback: 可选,如果设置了就只走一步
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】', + '!type': 'fn(direction?: string, callback?: fn())' + }, + getRealStatusOrDefault: { + '!doc': '从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取', + '!type': 'fn(status?: ?, name?: string)' + }, + getStatusLabel: { + '!doc': '获得某个状态的名字,如atk->攻击,def->防御等', + '!type': 'fn(name: string) -> string' + }, + removeSave: { + '!doc': '删除某个存档', + '!type': 'fn(index?: number, callback?: fn())' + }, + registerAnimationFrame: { + '!doc': '注册一个 animationFrame
name: 名称,可用来作为注销使用
needPlaying: 是否只在游戏运行时才执行(在标题界面不执行)
func: 要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数', + '!type': + 'fn(name: string, needPlaying: bool, func?: fn(timestamp: number))' + }, + getHeroLoc: { + '!doc': '读取主角的位置和/或朝向
例如:core.getHeroLoc(); // 读取主角的位置和朝向
name: 要读取横坐标还是纵坐标还是朝向还是都读取
返回值:name ? core.status.hero.loc[name] : core.status.hero.loc', + '!type': 'fn(name: string) -> string|number' + }, + stopAutomaticRoute: { + '!doc': '停止自动寻路操作', + '!type': 'fn()' + }, + setWeather: { + '!doc': "设置天气,不计入存档。如需长期生效请使用core.events._action_setWeather()函数
例如:core.setWeather('fog', 10); // 设置十级大雾天
type: 新天气的类型,不填视为晴天
level: 新天气(晴天除外)的级别,必须为不大于10的正整数,不填视为5", + '!type': 'fn(type?: string, level?: number)' + }, + updateStatusBar: { + '!doc': '刷新状态栏和地图显伤
doNotCheckAutoEvents: 是否不检查自动事件', + '!type': 'fn(doNotCheckAutoEvents?: bool, immediate?: bool)' + }, + autosave: { + '!doc': '自动存档', + '!type': 'fn(removeLast?: bool)' + }, + clearStatusBar: { + '!doc': '清空状态栏', + '!type': 'fn()' + }, + moveAction: { + '!doc': '尝试前进一步,如果面前不可被踏入就会直接触发该点事件
请勿直接使用此函数,如有需要请使用「勇士前进一步或撞击」事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】', + '!type': 'fn(callback?: fn())' + }, + hasFlag: { + '!doc': "判定一个flag变量是否存在且不为false、0、''、null、undefined和NaN
例如:core.hasFlag('poison'); // 判断主角当前是否中毒
name: 变量名,支持中文
此函数等价于 !!core.getFlag(name)", + '!type': 'fn(name: string) -> bool' + }, + rewindReplay: { + '!doc': '回退到上一个录像节点', + '!type': 'fn()' + }, + playBgm: { + '!doc': "播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__
例如:core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟
bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名
startTime: 跳过前多少秒,不填则不跳过", + '!type': 'fn(bgm: string, startTime?: number)' + }, + isReplaying: { + '!doc': '是否正在播放录像', + '!type': 'fn() -> bool' + }, + isMoving: { + '!doc': '当前是否正在移动', + '!type': 'fn() -> bool' + }, + getSaveIndexes: { + '!doc': '获得所有存在存档的存档位', + '!type': 'fn(callback?: fn())' + }, + unlockControl: { + '!doc': '解锁用户控制行为', + '!type': 'fn()' + }, + syncSave: { + '!doc': '同步存档到服务器', + '!type': 'fn(type?: string)' + }, + removeFlag: { + '!doc': '删除某个flag/变量', + '!type': 'fn(name: string)' + }, + registerResize: { + '!doc': '注册一个resize函数
name: 名称,可供注销使用
func: 可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。', + '!type': 'fn(name: string, func: fn(obj: ?))' + }, + registerWeather: { + '!doc': '注册一个天气
name: 要注册的天气名
initFunc: 当切换到此天气时的初始化;接受level(天气等级)为参数;可用于创建多个节点(如初始化雪花)
frameFunc: 每帧的天气效果变化;可接受timestamp(从页面加载完毕到当前所经过的时间)和level(天气等级)作为参数
天气应当仅在weather层进行绘制,推荐使用core.animateFrame.weather.nodes用于节点信息。', + '!type': + 'fn(name: string, initFunc: fn(level: number), frameFunc?: fn(timestamp: number, level: number))' + }, + stopReplay: { + '!doc': '停止播放', + '!type': 'fn(force?: bool)' + }, + turnHero: { + '!doc': '主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
例如:core.turnHero(); // 主角顺时针旋转90°,即单击主角或按下Z键的效果
direction: 主角的新朝向,可为 up, down, left, right, :left, :right, :back 七种之一', + '!type': 'fn(direction?: string)' + }, + resumeReplay: { + '!doc': '恢复播放', + '!type': 'fn()' + }, + resize: { + '!doc': '屏幕分辨率改变后重新自适应', + '!type': 'fn()' + }, + getSave: { + '!doc': '获得某个存档内容', + '!type': 'fn(index?: number, callback?: fn(data: ?))' + }, + setViewport: { + '!doc': '设置视野范围
px,py: 左上角相对大地图的像素坐标,不需要为32倍数', + '!type': 'fn(px?: number, py?: number)' + }, + chooseReplayFile: { + '!doc': '选择录像文件', + '!type': 'fn()' + }, + lockControl: { + '!doc': '锁定用户控制,常常用于事件处理', + '!type': 'fn()' + }, + updateCheckBlock: { + '!doc': '更新领域、夹击、阻击的伤害地图', + '!type': 'fn(floorId?: string)' + }, + checkBlock: { + '!doc': '检查并执行领域、夹击、阻击事件', + '!type': 'fn()' + }, + clearAutomaticRouteNode: { + '!doc': '清除自动寻路路线', + '!type': 'fn(x?: number, y?: number)' + }, + getFlag: { + '!doc': '读取一个flag变量
name: 变量名,支持中文
defaultValue: 当变量不存在时的返回值,可选(事件流中默认填0)。', + '!type': 'fn(name: string, defaultValue?: ?)' + }, + getNakedStatus: { + '!doc': '获得勇士原始属性(无装备和衰弱影响)', + '!type': 'fn(name: string)' + }, + nearHero: { + '!doc': '判定主角是否身处某个点的锯齿领域(取曼哈顿距离)
例如:core.nearHero(6, 6, 6); // 判定主角是否身处点(6,6)的半径为6的锯齿领域
x: 领域的中心横坐标
y: 领域的中心纵坐标
n: 领域的半径,不填视为1', + '!type': 'fn(x: number, y: number, n?: number) -> bool' + }, + stepReplay: { + '!doc': '单步播放', + '!type': 'fn()' + }, + hasSave: { + '!doc': '判断某个存档位是否存在存档', + '!type': 'fn(index?: number) -> bool' + }, + showStartAnimate: { + '!doc': '进入标题画面
例如:core.showStartAnimate(); // 重启游戏但不重置bgm
noAnimate: 可选,true表示不由黑屏淡入而是立即亮屏
callback: 可选,完全亮屏后的回调函数', + '!type': 'fn(noAnimate?: bool, callback?: fn())' + }, + moveViewport: { + '!doc': '移动视野范围', + '!type': + 'fn(x: number, y: number, moveMode?: string, time?: number, callback?: fn())' + }, + syncLoad: { + '!doc': '从服务器加载存档', + '!type': 'fn()' + }, + setHeroMoveInterval: { + '!doc': '设置行走的效果动画', + '!type': 'fn(callback?: fn())' + }, + registerReplayAction: { + '!doc': '注册一个录像行为
name: 自定义名称,可用于注销使用
func: 具体执行录像的函数,可为一个函数或插件中的函数名;
需要接受一个action参数,代表录像回放时的下一个操作
func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。', + '!type': + 'fn(name: string, func: fn(action?: string) -> bool)' + }, + checkAutosave: { + '!doc': '实际将自动存档写入存储', + '!type': 'fn()' + }, + resumeBgm: { + '!doc': '恢复背景音乐的播放
resumeTime: 从哪一秒开始恢复播放', + '!type': 'fn(resumeTime?: number)' + }, + setGameCanvasTranslate: { + '!doc': '设置大地图的偏移量', + '!type': + 'fn(ctx: string|CanvasRenderingContext2D, x: number, y: number)' + }, + checkBgm: { + '!doc': '检查bgm状态', + '!type': 'fn()' + }, + setDisplayScale: { + '!doc': '设置屏幕放缩', + '!type': 'fn(delta: number)' + }, + speedDownReplay: { + '!doc': '减速播放', + '!type': 'fn()' + }, + getRealStatus: { + '!doc': "计算主角的某个属性,包括百分比修正
例如:core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值
name: 属性的英文名,请注意只能用于数值类属性哦,否则乘法会得到NaN", + '!type': 'fn(name: string)' + }, + saveData: { + '!doc': '存档到本地', + '!type': 'fn()' + }, + unregisterAnimationFrame: { + '!doc': '注销一个animationFrame', + '!type': 'fn(name: string)' + }, + tryMoveDirectly: { + '!doc': '尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致
例如:core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件
destX: 目标点的横坐标
destY: 目标点的纵坐标', + '!type': 'fn(destX: number, destY: number)' + }, + moveDirectly: { + '!doc': '瞬间移动', + '!type': + 'fn(destX?: number, destY?: number, ignoreSteps?: number)' + }, + clearRouteFolding: { + '!doc': '清空录像折叠信息', + '!type': 'fn()' + }, + checkRouteFolding: { + '!doc': '检查录像折叠信息', + '!type': 'fn()' + }, + setSwitch: { + '!doc': '设置某个独立开关', + '!type': + 'fn(x: number, y: number, floorId: string, name: string, value: ?)' + }, + getSwitch: { + '!doc': '获得某个独立开关', + '!type': + 'fn(x: number, y: number, floorId: string, name: string, defaultValue?: ?)' + }, + addSwitch: { + '!doc': '增加某个独立开关', + '!type': + 'fn(x: number, y: number, floorId: string, name: string, value: number)' + }, + removeSwitch: { + '!doc': '删除某个独立开关', + '!type': + 'fn(x: number, y: number, floorId: string, name: string)' + }, + removeSwitch: { + '!doc': '判定某个独立开关', + '!type': + 'fn(x: number, y: number, floorId: string, name: string) -> bool' + } + }, + icons: { + '!doc': '图标信息', + getTilesetOffset: { + '!doc': '根据图块数字或ID获得所在的tileset和坐标信息', + '!type': + 'fn(id?: string) -> {image: ?, x: number, y: number}' + }, + getClsFromId: { + '!doc': '根据ID获得其图标类型', + '!type': 'fn(id?: string) -> string' + }, + getAllIconIds: { + '!doc': '获得所有图标的ID', + '!type': 'fn() -> [string]' + }, + getIcons: { + '!doc': '获得所有图标类型', + '!type': 'fn()' + } + }, + items: { + '!doc': '道具相关的函数', + getEquip: { + '!doc': '检查主角某种类型的装备目前是什么
例如:core.getEquip(1) // 主角目前装备了什么盾牌
equipType: 装备类型,自然数
返回值:装备id,null表示未穿戴', + '!type': 'fn(equipType: number) -> string' + }, + loadEquip: { + '!doc': "尝试穿上某件背包里面的装备并提示
例如:core.loadEquip('sword5') // 尝试装备上背包里面的神圣剑,无回调
equipId: 装备id
callback: 穿戴成功或失败后的回调函数", + '!type': 'fn(equipId: string, callback?: fn())' + }, + itemCount: { + '!doc': "统计某种道具的持有量
例如:core.itemCount('yellowKey') // 持有多少把黄钥匙
itemId: 道具id
返回值:该种道具的持有量,不包括已穿戴的装备", + '!type': 'fn(itemId: string) -> number' + }, + getItems: { + '!doc': '获得所有道具', + '!type': 'fn()' + }, + canUseItem: { + '!doc': "检查能否使用某种道具
例如:core.canUseItem('pickaxe') // 能否使用破墙镐
itemId: 道具id
返回值:true表示可以使用", + '!type': 'fn(itemId: string) -> bool' + }, + hasItem: { + '!doc': "检查主角是否持有某种道具(不包括已穿戴的装备)
例如:core.hasItem('yellowKey') // 主角是否持有黄钥匙
itemId: 道具id
返回值:true表示持有", + '!type': 'fn(itemId: string) -> bool' + }, + addItem: { + '!doc': "静默增减某种道具的持有量 不会更新游戏画面或是显示提示
例如:core.addItem('yellowKey', -2) // 没收两把黄钥匙
itemId: 道具id
itemNum: 增加量,负数表示没收", + '!type': 'fn(itemId: string, itemNum?: number)' + }, + unloadEquip: { + '!doc': '脱下某个类型的装备
例如:core.unloadEquip(1) // 卸下盾牌,无回调
equipType: 装备类型编号,自然数
callback: 卸下装备后的回调函数', + '!type': 'fn(equipType: number, callback?: fn())' + }, + quickLoadEquip: { + '!doc': '快速换装
例如:core.quickLoadEquip(1) // 快速换上1号套装
index: 套装编号,自然数', + '!type': 'fn(index: number)' + }, + getItemEffect: { + '!doc': "即捡即用类的道具获得时的效果
例如:core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果
itemId: 道具id
itemNum: 道具数量,可选,默认为1", + '!type': 'fn(itemId: string, itemNum?: number)' + }, + quickSaveEquip: { + '!doc': '保存当前套装
例如:core.quickSaveEquip(1) // 将当前套装保存为1号套装
index: 套装编号,自然数', + '!type': 'fn(index: number)' + }, + setItem: { + '!doc': "设置某种道具的持有量
例如:core.setItem('yellowKey', 3) // 设置黄钥匙为3把
itemId: 道具id
itemNum: 新的持有量,可选,自然数,默认为0", + '!type': 'fn(itemId: string, itemNum?: number)' + }, + compareEquipment: { + '!doc': "比较两件(类型可不同)装备的优劣
例如:core.compareEquipment('sword5', 'shield5') // 比较神圣剑和神圣盾的优劣
compareEquipId: 装备甲的id
beComparedEquipId: 装备乙的id
返回值:两装备的各属性差,甲减乙,0省略", + '!type': + 'fn(compareEquipId: string, beComparedEquipId: string) -> {value: ?, percentage: ?}' + }, + removeItem: { + '!doc': '删除某个物品', + '!type': 'fn(itemId?: string, itemNum?: number)' + }, + getEquipTypeById: { + '!doc': "判定某件装备的类型
例如:core.getEquipTypeById('shield5') // 1(盾牌)
equipId: 装备id
返回值:类型编号,自然数", + '!type': 'fn(equipId: string) -> number' + }, + getEquipTypeByName: { + '!doc': '根据类型获得一个可用的装备孔', + '!type': 'fn(name?: string)' + }, + useItem: { + '!doc': "使用一个道具
例如:core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调
itemId: 道具id
noRoute: 是否不计入录像,快捷键使用的请填true,否则可省略
callback: 道具使用完毕或使用失败后的回调函数", + '!type': + 'fn(itemId: string, noRoute?: bool, callback?: fn())' + }, + hasEquip: { + '!doc': "检查主角是否穿戴着某件装备
例如:core.hasEquip('sword5') // 主角是否装备了神圣剑
itemId: 装备id
返回值:true表示已装备", + '!type': 'fn(itemId: string) -> bool' + }, + getItemEffectTip: { + '!doc': "即捡即用类的道具获得时的额外提示
例如:core.getItemEffectTip(redPotion) // (获得 红血瓶)',生命+100'
itemId: 道具id
返回值:图块属性itemEffectTip的内容", + '!type': 'fn(itemId: string) -> string' + }, + canEquip: { + '!doc': "检查能否穿上某件装备
例如:core.canEquip('sword5', true) // 主角可以装备神圣剑吗,如果不能会有提示
equipId: 装备id
hint: 无法穿上时是否提示(比如是因为未持有还是别的什么原因)
返回值:true表示可以穿上,false表示无法穿上", + '!type': 'fn(equipId: string, hint?: bool) -> bool' + }, + setEquip: { + '!doc': "设置某个装备的属性并计入存档
例如:core.setEquip('sword1', 'value', 'atk', 300, '+='); // 设置铁剑的攻击力数值再加300
equipId: 装备id
valueType: 增幅类型,只能是value(数值)或percentage(百分比)
name: 要修改的属性名称,如atk
value: 要修改到的属性数值
operator: 操作符,可选,如+=表示在原始值上增加
prefix: 独立开关前缀,一般不需要", + '!type': + 'fn(equipId: string, valueType: string, name: string, value: ?, operator?: string, prefix?: string)' + } + }, + utils: { + '!doc': '工具函数库,里面有各个样板中使用到的工具函数。', + scan: { + '!doc': '朝向到x,y映射', + up: { + x: 'number', + y: 'number' + }, + down: { + x: 'number', + y: 'number' + }, + left: { + x: 'number', + y: 'number' + }, + right: { + x: 'number', + y: 'number' + } + }, + applyEasing: { + '!doc': '获得变速移动曲线', + '!type': 'fn(mode?: string) -> fn(t: number) -> number' + }, + clamp: { + '!doc': '将x限定在[a,b]区间内,注意a和b可交换
例如:core.clamp(1200, 1, 1000); // 1000
x: 原始值,!x为true时x一律视为0
a: 下限值,大于b将导致与b交换
b: 上限值,小于a将导致与a交换', + '!type': 'fn(x: number, a: number, b: number) -> number' + }, + rand: { + '!doc': '不支持SL的随机数
例如:1 + core.rand(6); // 随机生成一个小于7的正整数,模拟骰子的效果
num: 填正数表示生成小于num的随机自然数,否则生成小于1的随机正数
返回值:随机数,即使读档也不会改变结果', + '!type': 'fn(num?: number) -> number' + }, + clone: { + '!doc': "深拷贝一个对象(函数将原样返回)
例如:core.clone(core.status.hero, (name, value) => (name == 'items' || typeof value == 'number'), false); // 深拷贝主角的属性和道具
data: 待拷贝对象
filter: 过滤器,可选,表示data为数组或对象时拷贝哪些项或属性,true表示拷贝
recursion: 过滤器是否递归,可选。true表示过滤器也被递归
返回值:拷贝的结果,注意函数将原样返回", + '!type': + 'fn(data?: ?, filter?: fn(name: string, value: ?) -> bool, recursion?: bool)' + }, + cloneArray: { + '!doc': '深拷贝一个1D或2D数组对象
例如:core.cloneArray(core.status.thisMap.map)', + '!type': + 'fn(data?: [number]|[[number]]) -> [number]|[[number]]' + }, + setLocalForage: { + '!doc': '往数据库写入一段数据', + '!type': + 'fn(key: string, value?: ?, successCallback?: fn(), errorCallback?: fn())' + }, + getGlobal: { + '!doc': "读取一个全局存储,适用于global:xxx,支持录像。
例如:if (core.getGlobal('一周目已通关', false) === true) core.getItem('dagger'); // 二周目游戏进行到此处时会获得一把屠龙匕首
key: 全局变量名称,支持中文
defaultValue: 可选,当此全局变量不存在或值为null、undefined时,用此值代替
返回值:全局变量的值", + '!type': 'fn(key: string, defaultValue?: ?)' + }, + replaceText: { + '!doc': "将一段文字中的${}(表达式)进行替换。
例如:core.replaceText('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); // 把主角的生命值和持有的黄钥匙数量代入这句话
text: 模板字符串,可以使用${}计算js表达式,支持“状态、物品、变量、独立开关、全局存储、图块id、图块类型、敌人数据、装备id”等量参与运算
返回值:替换完毕后的字符串", + '!type': 'fn(text: string, prefix?: string) -> string' + }, + removeLocalStorage: { + '!doc': '移除本地存储', + '!type': 'fn(key: string)' + }, + unzip: { + '!doc': '解压一段内容', + '!type': + 'fn(blobOrUrl?: ?, success?: fn(data: ?), error?: fn(error: string), convertToText?: bool, onprogress?: fn(loaded: number, total: number))' + }, + formatTime: { + '!doc': '格式化时间', + '!type': 'fn(time: number) -> string' + }, + readFile: { + '!doc': '尝试请求读取一个本地文件内容 [异步]
success: 成功后的回调
error: 失败后的回调
readType: 不设置则以文本读取,否则以DataUrl形式读取', + '!type': + 'fn(success?: fn(data: string), error?: fn(message: string), readType?: bool)' + }, + readFileContent: { + '!doc': '文件读取完毕后的内容处理 [异步]', + '!type': 'fn(content: string)' + }, + formatDate: { + '!doc': '格式化日期为字符串', + '!type': 'fn(date: ?) -> string' + }, + download: { + '!doc': "弹窗请求下载一个文本文件
例如:core.download('route.txt', JSON.stringify(core.status.route)); // 弹窗请求下载录像
filename: 文件名
content: 文件内容", + '!type': 'fn(filename: string, content: string)' + }, + encodeBase64: { + '!doc': "base64加密
例如:core.encodeBase64('abcd'); // 'YWJjZA=='
str: 明文
返回值:密文", + '!type': 'fn(str: string) -> string' + }, + strlen: { + '!doc': "求字符串的国标码字节数,也可用于等宽字体下文本的宽度测算。请注意样板的默认字体Verdana不是等宽字体
例如:core.strlen('无敌ad'); // 6
str: 待测字符串
返回值:字符串的国标码字节数,每个汉字为2,每个ASCII字符为1", + '!type': 'fn(str: string) -> number' + }, + myprompt: { + '!doc': '让用户输入一段文字', + '!type': + 'fn(hint: string, value: string, callback?: fn(data?: string))' + }, + getCookie: { + '!doc': '访问浏览器cookie', + '!type': 'fn(name: string) -> string' + }, + decodeRoute: { + '!doc': '录像解压的最后一步,即一压的逆过程
例如:core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-|
route: 录像解压倒数第二步的结果,即一压的结果
返回值:原始录像', + '!type': 'fn(route: string) -> [string]' + }, + formatDate2: { + '!doc': '格式化日期为最简字符串', + '!type': 'fn(date: ?) -> string' + }, + unshift: { + '!doc': "将b(可以是另一个数组)插入数组a的开头,此函数用于弥补a.unshift(b)中b只能是单项的不足。
例如:core.unshift(todo, {type: 'unfollow'}); // 在事件指令数组todo的开头插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新首项或前缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", + '!type': 'fn(a: [?], b: ?) -> [?]' + }, + same: { + '!doc': "判定深层相等, 会逐层比较每个元素
例如:core.same(['1', 2], ['1', 2]); // true", + '!type': 'fn(a?: ?, b?: ?) -> bool' + }, + setTwoDigits: { + '!doc': '两位数显示', + '!type': 'fn(x: number) -> string' + }, + splitImage: { + '!doc': "等比例切分一张图片
例如:core.splitImage(core.material.images.images['npc48.png'], 32, 48); // 把npc48.png切分成若干32×48px的小人
image: 图片名(支持映射前的中文名)或图片对象(参见上面的例子),获取不到时返回[]
width: 子图的宽度,单位为像素。原图总宽度必须是其倍数,不填视为32
height: 子图的高度,单位为像素。原图总高度必须是其倍数,不填视为正方形
返回值:子图组成的数组,在原图中呈先行后列,从左到右、从上到下排列。", + '!type': + 'fn(image?: string|image, width?: number, height?: number) -> [image]' + }, + decompress: { + '!doc': '解压缩一个数据', + '!type': 'fn(value: ?)' + }, + showWithAnimate: { + '!doc': '动画显示某对象', + '!type': 'fn(obj?: ?, speed?: number, callback?: fn())' + }, + subarray: { + '!doc': "判定一个数组是否为另一个数组的前缀,用于录像接续播放。请注意函数名没有大写字母
例如:core.subarray(['ad', '米库', '小精灵', '小破草', '小艾'], ['ad', '米库', '小精灵']); // ['小破草', '小艾']
a: 可能的母数组,不填或比b短将返回null
b: 可能的前缀,不填或比a长将返回null
返回值:如果b不是a的前缀将返回null,否则将返回a去掉此前缀后的剩余数组", + '!type': 'fn(a?: [?], b?: [?]) -> [?]|null' + }, + turnDirection: { + '!doc': '计算应当转向某个方向
turn: 转向的方向,可为 up,down,left,right,:left,:right,:back 七种
direction: 当前方向', + '!type': 'fn(turn: string, direction?: string) -> string' + }, + myconfirm: { + '!doc': "显示确认框,类似core.drawConfirmBox(),但不打断事件流
例如:core.myconfirm('重启游戏?', core.restart); // 弹窗询问玩家是否重启游戏
hint: 弹窗的内容,支持 ${} 语法
yesCallback: 确定后的回调函数
noCallback: 取消后的回调函数,可选", + '!type': + 'fn(hint: string, yesCallback?: fn(), noCallback?: fn())' + }, + calValue: { + '!doc': "计算一个表达式的值,支持status:xxx等的计算。
例如:core.calValue('status:hp + status:def'); // 计算主角的生命值加防御力
value: 待求值的表达式
prefix: 独立开关前缀,一般可省略
返回值:求出的值", + '!type': 'fn(value: string, prefix?: string)' + }, + encodeRoute: { + '!doc': '录像压缩缩
例如:core.encodeRoute(core.status.route); // 压缩当前录像
route: 原始录像,自定义内容(不予压缩,原样写入)必须由0-9A-Za-z和下划线、冒号组成,所以中文和数组需要用JSON.stringify预处理再base64压缩才能交由一压
返回值:一压的结果', + '!type': 'fn(route: [string]) -> string' + }, + decodeBase64: { + '!doc': 'base64解密
例如:core.decodeBase64(\'YWJjZA==\'); // "abcd"
str: 密文
返回值:明文', + '!type': 'fn(str: string) -> string' + }, + http: { + '!doc': '发送一个HTTP请求 [异步]
type: 请求类型,只能为GET或POST
url: 目标地址
formData: 如果是POST请求则为表单数据
success: 成功后的回调
error: 失败后的回调', + '!type': + 'fn(type: string, url: string, formData: ?, success?: fn(data: string), error?: fn(message: string), mimeType?: string, responseType?: string, onprogress?: fn(loaded: number, total: number))' + }, + getGuid: { + '!doc': '获得或生成浏览器唯一的guid', + '!type': 'fn() -> string' + }, + getLocalStorage: { + '!doc': '获得本地存储', + '!type': 'fn(key: string, defaultValue?: ?)' + }, + arrayToRGB: { + '!doc': '颜色数组转字符串
例如:core.arrayToRGB([102, 204, 255]); // "#66ccff"
color: 一行三列的数组,必须为不大于255的自然数
返回值:该颜色的#xxxxxx字符串表示', + '!type': 'fn(color: [number]) -> string' + }, + arrayToRGBA: { + '!doc': '颜色数组转字符串
例如:core.arrayToRGBA([102, 204, 255, 0.3]); // "rgba(102,204,255,0.3)"
color: 一行三列或一行四列的数组,前三个元素必须为不大于255的自然数。第四个元素(如果有)必须为0或不大于1的数字,第四个元素不填视为1
返回值:该颜色的rgba(...)字符串表示', + '!type': 'fn(color: [number]) -> string' + }, + formatBigNumber: { + '!doc': '大数字格式化,单位为10000的倍数(w,e,z,j,g),末尾四舍五入
例如:core.formatBigNumber(123456789, false); // "12346w"
x: 原数字
onMap: 可选,true表示用于地图显伤,结果总字符数最多为5,否则最多为6
返回值:格式化结果', + '!type': 'fn(x: number, onMap?: bool) -> string' + }, + removeLocalForage: { + '!doc': '移除本地数据库的数据', + '!type': + 'fn(key: string, successCallback?: fn(), errorCallback?: fn())' + }, + matchWildcard: { + '!doc': "通配符匹配,用于搜索图块等批量处理。
例如:core.playSound(core.matchWildcard('*Key', itemId) ? 'item.mp3' : 'door.mp3'); // 判断捡到的是钥匙还是别的道具,从而播放不同的音效
pattern: 模式串,每个星号表示任意多个(0个起)字符
string: 待测串
返回值:true表示匹配成功,false表示匹配失败", + '!type': 'fn(pattern: string, string: string) -> bool' + }, + setLocalStorage: { + '!doc': '设置本地存储', + '!type': 'fn(key: string, value?: ?)' + }, + hideWithAnimate: { + '!doc': '动画使某对象消失', + '!type': 'fn(obj?: ?, speed?: number, callback?: fn())' + }, + copy: { + '!doc': '尝试复制一段文本到剪切板。', + '!type': 'fn(data: string) -> bool' + }, + isset: { + '!doc': '判断一个值是否不为null,undefined和NaN
例如:core.isset(0/0); // false,因为0/0等于NaN
v: 待测值,可选
返回值:false表示待测值为null、undefined、NaN或未填写,true表示为其他值。', + '!type': 'fn(v?: ?) -> bool' + }, + replaceValue: { + '!doc': "对一个表达式中的特殊规则进行替换,如status:xxx等。
例如:core.replaceValue('status:atk+item:yellowKey'); // 把这两个冒号表达式替换为core.getStatus('hp')和core.itemCount('yellowKey')这样的函数调用
value: 模板字符串,注意独立开关不会被替换
返回值:替换完毕后的字符串", + '!type': 'fn(value: string) -> string' + }, + getLocalForage: { + '!doc': '从本地数据库读出一段数据', + '!type': + 'fn(key: string, defaultValue?: ?, successCallback?: fn(data: ?), errorCallback?: fn())' + }, + inArray: { + '!doc': '判定array是不是一个数组,以及element是否在该数组中。
array: 可能的数组,不为数组或不填将导致返回值为false
element: 待查找的元素
返回值:如果array为数组且具有element这项,就返回true,否则返回false', + '!type': 'fn(array?: ?, element?: ?) -> bool' + }, + setGlobal: { + '!doc': "设置一个全局存储,适用于global:xxx,录像播放时将忽略此函数。
例如:core.setBlobal('一周目已通关', true); // 设置全局存储“一周目已通关”为true,方便二周目游戏中的新要素。
key: 全局变量名称,支持中文
value: 全局变量的新值,不填或null表示清除此全局存储", + '!type': 'fn(key: string, value?: ?)' + }, + rand2: { + '!doc': '支持SL的随机数,并计入录像
例如:1 + core.rand2(6); // 随机生成一个小于7的正整数,模拟骰子的效果
num: 正整数,0或不填会被视为2147483648
返回值:属于 [0, num) 的随机数', + '!type': 'fn(num?: number) -> number' + }, + setStatusBarInnerHTML: { + '!doc': "填写非自绘状态栏
例如:core.setStatusBarInnerHTML('hp', core.status.hero.hp, 'color: #66CCFF'); // 更新状态栏中的主角生命,使用加载画面的宣传色
name: 状态栏项的名称,如'hp', 'atk', 'def'等。必须是core.statusBar中的一个合法项
value: 要填写的内容,大数字会被格式化为至多6个字符,无中文的内容会被自动设为斜体
css: 额外的css样式,可选。如更改颜色等", + '!type': 'fn(name: string, value: ?, css?: string)' + }, + matchRegex: { + '!doc': '是否满足正则表达式', + '!type': 'fn(pattern: string, string: string) -> string' + }, + push: { + '!doc': "将b(可以是另一个数组)插入数组a的末尾,此函数用于弥补a.push(b)中b只能是单项的不足。
例如:core.push(todo, {type: 'unfollow'}); // 在事件指令数组todo的末尾插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新末项或后缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", + '!type': 'fn(a: [?], b: ?) -> [?]' + }, + formatSize: { + '!doc': '格式化文件大小', + '!type': 'fn(size: number) -> string' + } + }, + actions: { + '!doc': '主要是处理一些和用户交互相关的内容。', + onup: { + '!doc': '当点击(触摸)事件放开时', + '!type': 'fn(loc: {x: number, y: number, size: number})' + }, + pressKey: { + '!doc': '按住某个键时', + '!type': 'fn(keyCode: number)' + }, + keyUp: { + '!doc': '根据放开键的code来执行一系列操作', + '!type': + 'fn(keyCode: number, altKey?: bool, fromReplay?: bool)' + }, + ondown: { + '!doc': '点击(触摸)事件按下时', + '!type': 'fn(loc: {x: number, y: number, size: number})' + }, + registerAction: { + '!doc': '此函数将注册一个用户交互行为。
action: 要注册的交互类型,如 ondown, onclick, keyDown 等等。
name: 你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。
func: 执行函数。
如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。
priority: 优先级;优先级高的将会被执行。此项可不填,默认为0', + '!type': + 'fn(action: string, name: string, func: string|fn(params: ?), priority?: number)' + }, + onkeyDown: { + '!doc': '按下某个键时', + '!type': 'fn(e: Event)' + }, + keyDown: { + '!doc': '根据按下键的code来执行一系列操作', + '!type': 'fn(keyCode: number)' + }, + onStatusBarClick: { + '!doc': '点击自绘状态栏时', + '!type': 'fn(e?: Event)' + }, + longClick: { + '!doc': '长按', + '!type': + 'fn(x: number, y: number, px: number, py: number, fromEvent?: bool)' + }, + unregisterAction: { + '!doc': '注销一个用户交互行为', + '!type': 'fn(action: string, name: string)' + }, + keyDownCtrl: { + '!doc': '长按Ctrl键时', + '!type': 'fn() -> bool' + }, + onclick: { + '!doc': '具体点击屏幕上(x,y)点时,执行的操作', + '!type': + 'fn(x: number, y: number, px: number, py: number, stepPostfix?: [?])' + }, + doRegisteredAction: { + '!doc': '执行一个用户交互行为', + '!type': 'fn(action: string, params: ?)' + }, + onkeyUp: { + '!doc': '放开某个键时', + '!type': 'fn(e: Event)' + }, + onmousewheel: { + '!doc': '滑动鼠标滚轮时的操作', + '!type': 'fn(direct: number)' + }, + onmove: { + '!doc': '当在触摸屏上滑动时', + '!type': 'fn(loc: {x: number, y: number, size: number})' + } + }, + loader: { + '!doc': '资源加载相关的函数', + loadImages: { + '!doc': '加载一系列图片', + '!type': + 'fn(dir: string, names: [string], toSave: ?, callback?: fn()) ' + }, + loadImagesFromZip: { + '!doc': '从zip中加载一系列图片', + '!type': + 'fn(url: string, names: [string], toSave?: ?, onprogress?: ?, onfinished?: ?)' + }, + loadBgm: { + '!doc': '加载一个bgm', + '!type': 'fn(name: string)' + }, + loadOneMusic: { + '!doc': '加载一个音乐或音效', + '!type': 'fn(name: string)' + }, + freeBgm: { + '!doc': '释放一个bgm的缓存', + '!type': 'fn(name: string)' + }, + loadOneSound: { + '!doc': '加载一个音效', + '!type': 'fn(name: string)' + }, + loadImage: { + '!doc': '加载某一张图片', + '!type': 'fn(dir: name, imgName: name, callback?: fn())' + } + }, + maps: { + '!doc': '负责一切和地图相关的处理内容,包括如下几个方面:
- 地图的初始化,保存和读取,地图数组的生成
- 是否可移动或瞬间移动的判定
- 地图的绘制
- 获得某个点的图块信息
- 启用和禁用图块,改变图块
- 移动/跳跃图块,淡入淡出图块
- 全局动画控制,动画的绘制', + noPass: { + '!doc': '判定某个点是否不可被踏入(不基于主角生命值和图块cannotIn属性)
例如:core.noPass(0, 0); // 判断地图左上角能否被踏入
x: 目标点的横坐标
y: 目标点的纵坐标
floorId: 目标点所在的地图id,不填视为当前地图
返回值:true表示可踏入', + '!type': + 'fn(x: number, y: number, floorId?: string) -> bool' + }, + drawAnimate: { + '!doc': "播放动画,注意即使指定了主角的坐标也不会跟随主角移动,如有需要请使用core.drawHeroAnimate(name, callback)函数
例如:core.drawAnimate('attack', core.nextX(), core.nextY(), false, core.vibrate); // 在主角面前一格播放普攻动画,动画停止后视野左右抖动1秒
name: 动画文件名,不含后缀
x: 横坐标
y: 纵坐标
alignWindow: 是否是相对窗口的坐标
callback: 动画停止后的回调函数,可选
返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数)", + '!type': + 'fn(name: string, x: number, y: number, alignWindow: bool, callback?: fn()) -> number' + }, + drawHeroAnimate: { + '!doc': '播放跟随勇士的动画
name: 动画名
callback: 动画停止后的回调函数,可选
返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数)', + '!type': 'fn(name: string, callback?: fn()) -> number' + }, + stopAnimate: { + '!doc': '立刻停止一个动画播放
id: 播放动画的编号,即drawAnimate或drawHeroAnimate的返回值;不填视为所有动画br/>doCallback: 是否执行该动画的回调函数', + '!type': 'fn(id?: number, doCallback?: bool)' + }, + getPlayingAnimates: { + '!doc': '获得当前正在播放的所有(指定)动画的id列表
name: 动画名;不填代表返回全部正在播放的动画
返回值: 一个数组,每一项为一个正在播放的动画;可用core.stopAnimate停止播放。', + '!type': 'fn(name?: string) -> [number]' + }, + getBlockCls: { + '!doc': "判定某个点的图块类型
例如:if(core.getBlockCls(x1, y1) != 'enemys' && core.getBlockCls(x2, y2) != 'enemy48') core.openDoor(x3, y3); // 另一个简单的机关门事件,打败或炸掉这一对不同身高的敌人就开门
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块类型,即“地形、四帧动画、矮敌人、高敌人、道具、矮npc、高npc、自动元件、额外地形”之一", + '!type': + 'fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string' + }, + drawMap: { + '!doc': '地图重绘
例如:core.drawMap(); // 重绘当前地图,常用于更改贴图或改变自动元件后的刷新
floorId: 地图id,可省略表示当前楼层
callback: 重绘完毕后的回调函数,可选', + '!type': 'fn(floorId?: string)' + }, + nearStair: { + '!doc': '当前位置是否在楼梯边;在楼传平面塔模式下对箭头也有效', + '!type': 'fn() -> bool' + }, + turnBlock: { + '!doc': '事件转向', + '!type': + 'fn(direction?: string, x?: number, y?: number, floorId?: string)' + }, + getMapArray: { + '!doc': "生成事件层矩阵
例如:core.getMapArray('MT0'); // 生成主塔0层的事件层矩阵,隐藏的图块视为0
floorId: 地图id,不填视为当前地图
showDisable: 可选,true表示隐藏的图块也会被表示出来
返回值:事件层矩阵,注意对其阵元的访问是[y][x]", + '!type': + 'fn(floorId?: string, noCache?: bool) -> [[number]]' + }, + getMapNumber: { + '!doc': '获得事件层某个点的数字', + '!type': + 'fn(x: number, y: number, floorId?: string, noCache?: bool) -> number' + }, + jumpBlock: { + '!doc': '跳跃图块;从V2.7开始不再有音效
例如:core.jumpBlock(0, 0, 0, 0); // 令地图左上角的图块原地跳跃半秒,再花半秒淡出
sx: 起点的横坐标
sy: 起点的纵坐标
ex: 终点的横坐标
ey: 终点的纵坐标
time: 单步和淡出用时,单位为毫秒。不填视为半秒
keep: 是否不淡出,true表示不淡出
callback: 落地或淡出后的回调函数,可选', + '!type': + 'fn(sx: number, sy: number, ex: number, ey: number, time?: number, keep?: bool, callback?: fn())' + }, + replaceBlock: { + '!doc': '批量替换图块
例如:core.replaceBlock(21, 22, core.floorIds); // 把游戏中地上当前所有的黄钥匙都变成蓝钥匙
fromNumber: 旧图块的数字
toNumber: 新图块的数字
floorId: 地图id或其数组,不填视为当前地图', + '!type': + 'fn(fromNumber: number, toNumber: number, floorId?: string|[string])' + }, + drawBlock: { + '!doc': '绘制一个图块', + '!type': 'fn(block?: block, animate?: number)' + }, + resetMap: { + '!doc': '重置地图', + '!type': 'fn(floorId?: string|[string])' + }, + animateSetBlock: { + '!doc': '动画形式转变某点图块', + '!type': + 'fn(number: number|string, x: number, y: number, floorId?: string, time?: number, callback?: fn())' + }, + animateSetBlocks: { + '!doc': '动画形式同时转变若干点图块', + '!type': + 'fn(number: number|string, locs: [?], floorId?: string, time?: number, callback?: fn())' + }, + compressMap: { + '!doc': '压缩地图', + '!type': + 'fn(mapArr: [[number]], floorId?: string) -> [[number]]' + }, + enemyExists: { + '!doc': '某个点是否存在(指定的)怪物', + '!type': + 'fn(x: number, y: number, id?: string, floorId?: string) -> bool' + }, + npcExists: { + '!doc': '某个点是否存在NPC', + '!type': + 'fn(x: number, y: number, floorId?: string) -> bool' + }, + getBlockByNumber: { + '!doc': '根据数字获得图块', + '!type': 'fn(number: number) -> block' + }, + removeBlock: { + '!doc': '删除一个图块,对应于「隐藏事件」并同时删除
例如:core.removeBlock(0, 0); // 尝试删除地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图', + '!type': 'fn(x: number, y: number, floorId?: string)' + }, + hideBlock: { + '!doc': '隐藏一个图块,对应于「隐藏事件」且不删除
例如:core.hideBlock(0, 0); // 隐藏地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图', + '!type': 'fn(x: number, y: number, floorId?: string)' + }, + removeBlockByIndex: { + '!doc': '根据block的索引删除该块', + '!type': 'fn(index: number, floorId?: string)' + }, + stairExists: { + '!doc': '某个点是否存在楼梯', + '!type': + 'fn(x: number, y: number, floorId?: string) -> bool' + }, + isMapBlockDisabled: { + '!doc': '某个点图块是否被强制启用或禁用', + '!type': + 'fn(floorId?: string, x?: number, y?: number, flags?: ?) -> bool' + }, + setMapBlockDisabled: { + '!doc': '设置某个点图块的强制启用或禁用状态', + '!type': + 'fn(floorId?: string, x?: number, y?: number, disabled?: bool)' + }, + setBlockOpacity: { + '!doc': '设置某个点图块的不透明度', + '!type': + 'fn(opacity?: number, x?: number, y?: number, floorId?: string)' + }, + setBlockFilter: { + '!doc': '设置某个点图块的特效', + '!type': + 'fn(filter?: ?, x?: number, y?: number, floorId?: string)' + }, + decompressMap: { + '!doc': '解压缩地图', + '!type': + 'fn(mapArr: [[number]], floorId?: string) -> [[number]]' + }, + automaticRoute: { + '!doc': '自动寻路
例如:core.automaticRoute(0, 0); // 自动寻路到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:每步走完后主角的loc属性组成的一维数组', + '!type': + 'fn(destX: number, destY: number) -> [{x: number, y: number, direction: string}]' + }, + resizeMap: { + '!doc': '更改地图画布的尺寸', + '!type': 'fn(floorId?: string)' + }, + getFgNumber: { + '!doc': '判定某点的前景层的数字
例如:core.getFgNumber(); // 判断主角脚下的前景层图块的数字
x: 横坐标,不填为勇士坐标
y: 纵坐标,不填为勇士坐标floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存而强制重算', + '!type': + 'fn(x: number, y: number, floorId?: string, noCache?: bool) -> number' + }, + moveBlock: { + '!doc': "移动图块
例如:core.moveBlock(0, 0, ['down']); // 令地图左上角的图块下移一格
x: 起点的横坐标
y: 起点的纵坐标
steps: 步伐数组
time: 单步和淡出用时,单位为毫秒。不填视为半秒
keep: 是否不淡出,true表示不淡出
callback: 移动或淡出后的回调函数,可选", + '!type': + 'fn(x: number, y: number, steps: [string], time?: number, keep?: bool, callback?: fn())' + }, + getBgNumber: { + '!doc': '判定某点的背景层的数字
例如:core.getBgNumber(); // 判断主角脚下的背景层图块的数字
x: 横坐标,不填为勇士坐标
y: 纵坐标,不填为勇士坐标
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存而强制重算', + '!type': + 'fn(x?: number, y?: number, floorId?: string, noCache?: bool) -> number' + }, + getIdOfThis: { + '!doc': '获得当前事件点的ID', + '!type': 'fn(id?: string) -> string' + }, + searchBlock: { + '!doc': "搜索图块, 支持通配符和正则表达式
例如:core.searchBlock('*Door'); // 搜索当前地图的所有门
id: 图块id,支持星号表示任意多个(0个起)字符
floorId: 地图id或数组,不填视为当前地图
showDisable: 隐藏点是否计入,true表示计入
返回值:一个详尽的数组,一般只用到其长度", + '!type': + 'fn(id: string, floorId?: string|[string], showDisable?: bool) -> [{floorId: string, index: number, x: number, y: number, block: block}]' + }, + searchBlockWithFilter: { + '!doc': "根据给定的筛选函数搜索全部满足条件的图块
例如:core.searchBlockWithFilter(function (block) { return block.event.id.endsWith('Door'); }); // 搜索当前地图的所有门
blockFilter: 筛选函数,可接受block输入,应当返回一个boolean值
floorId: 地图id或数组,不填视为当前地图
showDisable: 隐藏点是否计入,true表示计入
返回值:一个详尽的数组", + '!type': + 'fn(blockFilter: fn(block: block) -> bool, floorId?: string|[string], showDisable?: bool): [{floorId: string, index: number, x: number, y: number, block: block}]' + }, + hideBgFgMap: { + '!doc': '隐藏前景/背景地图', + '!type': + 'fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())' + }, + getBlockInfo: { + '!doc': '获得某个图块或素材的信息,包括ID,cls,图片,坐标,faceIds等等', + '!type': 'fn(block?: number|string|block) -> blockInfo' + }, + getFaceDownId: { + '!doc': '获得某个图块对应行走图朝向向下的那一项的id;如果不存在行走图绑定则返回自身id。', + '!type': 'fn(block?: string|number|block) -> string' + }, + canMoveDirectlyArray: { + '!doc': '获得某些点可否通行的信息', + '!type': 'fn(locs?: [[number]])' + }, + hideFloorImage: { + '!doc': '隐藏一个楼层贴图', + '!type': + 'fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())' + }, + extractBlocks: { + '!doc': '根据需求解析出blocks', + '!type': 'fn(map?: ?)' + }, + extractBlocksForUI: { + '!doc': '根据需求为UI解析出blocks', + '!type': 'fn(map?: ?, flags?: ?)' + }, + getBlockId: { + '!doc': "判定某个点的图块id
例如:if(core.getBlockId(x1, y1) != 'greenSlime' && core.getBlockId(x2, y2) != 'redSlime') core.openDoor(x3, y3); // 一个简单的机关门事件,打败或炸掉这一对绿头怪和红头怪就开门
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块id,该点无图块则返回null", + '!type': + 'fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string' + }, + getBlockNumber: { + '!doc': '判定某个点的图块数字
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块数字,该点无图块则返回null', + '!type': + 'fn(x: number, y: number, floorId?: string, showDisable?: bool) -> number' + }, + getBlockOpacity: { + '!doc': '获得某个点图块的不透明度', + '!type': + 'fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> number' + }, + getBlockFilter: { + '!doc': '获得某个点图块的特效', + '!type': + 'fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> ?' + }, + loadFloor: { + '!doc': '从文件或存档中加载某个楼层', + '!type': 'fn(floorId?: string, map?: ?)' + }, + generateMovableArray: { + '!doc': '可通行性判定
例如:core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动
floorId: 地图id,不填视为当前地图
返回值:从各点可移动方向的三维数组', + '!type': 'fn(floorId?: string) -> [[[string]]]' + }, + terrainExists: { + '!doc': '某个点是否存在(指定的)地形', + '!type': + 'fn(x: number, y: number, id?: string, floorId?: string) -> bool' + }, + getBlockById: { + '!doc': '根据ID获得图块', + '!type': 'fn(id: string) -> block' + }, + drawBg: { + '!doc': '绘制背景层(含贴图,其与背景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawBg(); // 绘制当前地图的背景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要', + '!type': + 'fn(floorId?: string, ctx?: CanvasRenderingContext2D)' + }, + showBlock: { + '!doc': '显示(隐藏或显示的)图块,此函数将被“显示事件”指令和勾选了“不消失”的“移动/跳跃事件”指令(如阻击怪)的终点调用
例如:core.showBlock(0, 0); // 显示地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图', + '!type': 'fn(x: number, y: number, floorId?: string)' + }, + getMapBlocksObj: { + '!doc': '以x,y的形式返回每个点的事件', + '!type': 'fn(floorId?: string, noCache?: bool)' + }, + removeGlobalAnimate: { + '!doc': '删除一个或所有全局动画', + '!type': 'fn(x?: number, y?: number, name?: string)' + }, + drawEvents: { + '!doc': '绘制事件层
例如:core.drawEvents(); // 绘制当前地图的事件层
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
ctx: 某画布的ctx,用于绘制缩略图,一般不需要', + '!type': + 'fn(floorId?: string, blocks?: [block], ctx?: CanvasRenderingContext2D)' + }, + canMoveDirectly: { + '!doc': '能否瞬移到某点,并求出节约的步数。
例如:core.canMoveDirectly(0, 0); // 能否瞬移到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:正数表示节约的步数,-1表示不可瞬移', + '!type': 'fn(destX: number, destY: number) -> number' + }, + saveMap: { + '!doc': '将当前地图重新变成数字,以便于存档', + '!type': 'fn(floorId?: string)' + }, + drawBoxAnimate: { + '!doc': '绘制UI层的box动画', + '!type': 'fn()' + }, + setBgFgBlock: { + '!doc': "转变图层块
例如:core.setBgFgBlock('bg', 167, 6, 6); // 把当前地图背景层的中心块改为滑冰
name: 背景还是前景
number: 新图层块的数字(也支持纯数字字符串如'1')或id
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + '!type': + 'fn(name: string, number: number|string, x: number, y: number, floorId?: string)' + }, + drawFg: { + '!doc': '绘制前景层(含贴图,其与前景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawFg(); // 绘制当前地图的前景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要', + '!type': + 'fn(floorId?: string, ctx?: CanvasRenderingContext2D)' + }, + getBlock: { + '!doc': '获得某个点的block', + '!type': + 'fn(x: number, y: number, floorId?: string, showDisable?: bool) -> block' + }, + initBlock: { + '!doc': '初始化一个图块', + '!type': + 'fn(x: number, y: number, id: string|number, addInfo?: bool, eventFloor?: ?) -> block' + }, + addGlobalAnimate: { + '!doc': '添加一个全局动画', + '!type': 'fn(block?: block)' + }, + animateBlock: { + '!doc': '显示/隐藏某个块时的动画效果', + '!type': + 'fn(loc?: [number]|[[number]], type?: string|number, time?: number, callback?: fn())' + }, + loadMap: { + '!doc': '将存档中的地图信息重新读取出来', + '!type': 'fn(data?: ?, floorId?: string, flags?: ?)' + }, + setBlock: { + '!doc': "转变图块
例如:core.setBlock(1, 0, 0); // 把地图左上角变成黄墙
number: 新图块的数字(也支持纯数字字符串如'1')或id
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + '!type': + 'fn(number: number|string, x: number, y: number, floorId?: string)' + }, + getFgMapArray: { + '!doc': "生成前景层矩阵
例如:core.getFgMapArray('MT0'); // 生成主塔0层的前景层矩阵,使用缓存
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存
返回值:前景层矩阵,注意对其阵元的访问是[y][x]", + '!type': + 'fn(floorId?: string, noCache?: bool) -> [[number]]' + }, + getBgMapArray: { + '!doc': "生成背景层矩阵
例如:core.getBgMapArray('MT0'); // 生成主塔0层的背景层矩阵,使用缓存
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存
返回值:背景层矩阵,注意对其阵元的访问是[y][x]", + '!type': + 'fn(floorId?: string, noCache?: bool) -> [[number]]' + }, + canMoveHero: { + '!doc': '单点单朝向的可通行性判定;受各图层cannotInOut、起点cannotMove和canGoDeadZone影响,不受canPass和noPass影响
x: 起点横坐标,不填视为主角当前的
y: 起点纵坐标,不填视为主角当前的
direction: 移动的方向,不填视为主角面对的方向
floorId: 地图id,不填视为当前地图', + '!type': + 'fn(x?: number, y?: number, direction?: string, floorId?: string) -> bool' + }, + drawThumbnail: { + '!doc': '绘制缩略图
例如:core.drawThumbnail(); // 绘制当前地图的缩略图
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
options: 绘制信息,可选。可以增绘主角位置和朝向、采用不同于游戏中的主角行走图、增绘显伤、提供flags用于存读档,同时包含要绘制到的画布名或画布的ctx或还有其他信息,如起绘坐标、绘制大小、是否绘制全图、截取中心', + '!type': + 'fn(floorId?: string, blocks?: [block], options?: ?)' + }, + hideBlockByIndex: { + '!doc': '根据图块的索引来隐藏图块', + '!type': 'fn(index?: number, floorId?: string)' + }, + getNumberById: { + '!doc': "根据图块id得到数字(地图矩阵中的值)
例如:core.getNumberById('yellowWall'); // 1
id: 图块id
返回值:图块的数字,定义在project\\maps.js(请注意和project\\icons.js中的“图块索引”相区分!)", + '!type': 'fn(id: string) -> number' + }, + removeBlockByIndexes: { + '!doc': '一次性删除多个block', + '!type': 'fn(indexes?: [number], floorId?: string)' + }, + hideBlockByIndexes: { + '!doc': '一次性隐藏多个block', + '!type': 'fn(indexes?: [number], floorId?: string)' + }, + generateGroundPattern: { + '!doc': '生成groundPattern', + '!type': 'fn(floorId?: string)' + }, + showBgFgMap: { + '!doc': '显示前景/背景地图', + '!type': + 'fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())' + }, + showFloorImage: { + '!doc': '显示一个楼层贴图', + '!type': + 'fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())' + } + }, + ui: { + '!doc': '负责一切UI界面的绘制。主要包括三个部分:
- 设置某个画布的属性与在某个画布上绘制的相关API
- 具体的某个UI界面的绘制
- 动态创建画布相关的API', + resizeCanvas: { + '!doc': '重新设置一个自定义画布的大小', + '!type': + 'fn(name: string, x?: number, y?: number, styleOnly?: bool, isTempCanvas?: bool)' + }, + deleteCanvas: { + '!doc': '删除一个自定义画布
name: 画布名,也可以传入一个函数对所有画布进行筛选', + '!type': 'fn(name: string|fn(name: string) -> bool)' + }, + deleteAllCanvas: { + '!doc': '清空所有的自定义画布', + '!type': 'fn()' + }, + drawIcon: { + '!doc': '在某个canvas上绘制一个图标', + '!type': + 'fn(name: string|CanvasRenderingContext2D, id: string, x: number, y: number, w?: number, h?: number, frame?: number)' + }, + drawFly: { + '!doc': '绘制楼层传送器', + '!type': 'fn(page?: ?)' + }, + setOpacity: { + '!doc': '设置某个canvas整体的透明度;此函数直接改变画布本身,对已经绘制的内容也生效
如果仅想对接下来的绘制生效请使用setAlpha', + '!type': + 'fn(name: string|CanvasRenderingContext2D, opacity: number)' + }, + getTextContentHeight: { + '!doc': '获得某段文字的预计绘制高度;参数说明详见 drawTextContent', + '!type': 'fn(content: string, config?: ?)' + }, + drawArrow: { + '!doc': '在某个canvas上绘制一个箭头', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)' + }, + strokeEllipse: { + '!doc': '在某个canvas上绘制一个椭圆的边框', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string, lineWidth?: number)' + }, + fillCircle: { + '!doc': '在某个canvas上绘制一个圆', + '!url': 'https://www.w3school.com.cn/tags/canvas_arc.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, style?: string)' + }, + strokeRoundRect: { + '!doc': '在某个canvas上绘制一个圆角矩形的边框', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, lineWidth?: number, angle?: number)' + }, + getContextByName: { + '!doc': '根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。
也可以传画布的context自身,则返回自己。', + '!type': + 'fn(canvas: string|CanvasRenderingContext2D) -> CanvasRenderingContext2D' + }, + drawImage: { + '!doc': "在一个画布上绘制图片
后面的8个坐标参数与canvas的drawImage的八个参数完全相同。
name: 可以是系统画布之一,也可以是任意自定义动态创建的画布名 画布名称或者画布的context
image: 要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取;支持加':x',':y',':o'翻转),图片本身,或者一个画布。
angle:旋转角度", + '!url': 'http://www.w3school.com.cn/html5/canvas_drawimage.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, image: string|image, x: number, y: number, w?: number, h?: number, x1?: number, y1?: number, w1?: number, h1?: number, angle?: number)' + }, + drawTip: { + '!doc': '左上角绘制一段提示
text: 要提示的字符串,支持${}语法
id: 要绘制的图标ID
frame: 要绘制该图标的第几帧', + '!type': 'fn(text: string, id?: string, frame?: number)' + }, + drawBackground: { + '!doc': '绘制一个背景图,可绘制winskin或纯色背景;支持小箭头绘制', + '!type': + 'fn(left: string, top: string, right: string, bottom: string, posInfo?: {px: number, py: number, direction: string})' + }, + fillEllipse: { + '!doc': '在某个canvas上绘制一个椭圆', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string)' + }, + setFillStyle: { + '!doc': '设置某个canvas的绘制属性(如颜色等)', + '!url': 'https://www.w3school.com.cn/tags/canvas_fillstyle.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, style: string)' + }, + drawText: { + '!doc': '地图中间绘制一段文字', + '!type': 'fn(contents: string, callback?: fn())' + }, + drawConfirmBox: { + '!doc': '绘制一个确认框
此项会打断事件流,如需不打断版本的请使用core.myconfirm()
text: 要绘制的内容,支持 ${} 语法
yesCallback: 点击确认后的回调
noCallback: 点击取消后的回调', + '!type': + 'fn(text: string, yesCallback?: fn(), noCallback?: fn())' + }, + drawUIEventSelector: { + '!doc': '自绘一个闪烁的选择光标
code: 选择光标的编号,必填
background: 要绘制的光标背景,必须是一个合法的WindowSkin
x, y, w, h: 绘制的坐标和长宽
z: 可选,光标的的z值', + '!type': + 'fn(code: number, background: string, x: number, y: number, w: number, h: number, z?: number)' + }, + clearUIEventSelector: { + '!doc': '清除若干个自绘的选择光标
codes: 清除的光标编号;可以是单个编号或编号数组;不填则清除所有光标', + '!type': 'fn(codes?: number|[number])' + }, + fillPolygon: { + '!doc': '在某个canvas上绘制一个多边形', + '!type': + 'fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string)' + }, + fillText: { + '!doc': '在某个画布上绘制一段文字
text: 要绘制的文本
style: 绘制的样式
font: 绘制的字体
最大宽度,超过此宽度会自动放缩', + '!url': 'https://www.w3school.com.cn/tags/canvas_filltext.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, font?: string, maxWidth?: number)' + }, + setTextBaseline: { + '!doc': '设置某个canvas的基准线
baseline: 可为alphabetic, top, hanging, middle, ideographic, bottom', + '!url': 'https://www.w3school.com.cn/tags/canvas_textbaseline.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, baseline: string)' + }, + loadCanvas: { + '!doc': '加载某个canvas状态', + '!type': 'fn(name: string|CanvasRenderingContext2D)' + }, + splitLines: { + '!doc': '字符串自动换行的分割', + '!type': + 'fn(name: string|CanvasRenderingContext2D, text: string, maxWidth?: number, font?: string)' + }, + setAlpha: { + '!doc': '设置某个canvas接下来绘制的不透明度;不会影响已经绘制的内容
返回设置之前画布的不透明度
如果需要修改画布本身的不透明度请使用setOpacity', + '!url': 'https://www.w3school.com.cn/tags/canvas_globalalpha.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, alpha: number) -> number' + }, + setFilter: { + '!doc': '设置某个canvas接下来绘制的filter', + '!type': + 'fn(name: string|CanvasRenderingContext2D, style: string)' + }, + setLineWidth: { + '!doc': '设置某个canvas的线宽度', + '!url': 'https://www.w3school.com.cn/tags/canvas_linewidth.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, lineWidth: number)' + }, + drawTextBox: { + '!doc': '绘制一个对话框', + '!type': 'fn(content: string, showAll?: bool)' + }, + relocateCanvas: { + '!doc': '重新定位一个自定义画布', + '!type': + 'fn(name: string, x: number, y: number, useDelta: bool)' + }, + rotateCanvas: { + '!doc': '设置一个自定义画布的旋转角度
centerX, centerY: 旋转中心(以屏幕像素为基准);不填视为图片正中心。', + '!type': + 'fn(name: string, angle: number, centerX?: number, centerY?: number)' + }, + closePanel: { + '!doc': '结束一切事件和绘制,关闭UI窗口,返回游戏进程', + '!type': 'fn()' + }, + textImage: { + '!doc': '文本图片化', + '!type': 'fn(content: string, lineHeight?: number) -> image' + }, + drawStatusBar: { + '!doc': '绘制状态栏', + '!type': 'fn()' + }, + setStrokeStyle: { + '!doc': '设置某个canvas边框属性', + '!url': 'https://www.w3school.com.cn/tags/canvas_strokestyle.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, style: string)' + }, + clearUI: { + '!doc': '清空UI层内容', + '!type': 'fn()' + }, + drawWindowSkin: { + '!doc': '绘制WindowSkin', + '!type': + 'fn(background: string, ctx: string|CanvasRenderingContext2D, x: number, y: number, w: string, h: string, direction?: string, px?: number, py?: number)' + }, + fillRect: { + '!doc': '绘制一个矩形。
x,y: 绘制的坐标
width,height: 绘制的长宽
style: 绘制的样式
angle: 旋转的角度,弧度制,如Math.PI/2代表90度', + '!url': 'https://www.w3school.com.cn/tags/canvas_fillrect.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, angle?: number)' + }, + drawScrollText: { + '!doc': '绘制滚动字幕', + '!type': + 'fn(content: string, time: number, lineHeight?: number, callback?: fn())' + }, + strokePolygon: { + '!doc': '在某个canvas上绘制一个多边形的边框', + '!type': + 'fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string, lineWidth?: number)' + }, + strokeCircle: { + '!doc': '在某个canvas上绘制一个圆的边框', + '!url': 'https://www.w3school.com.cn/tags/canvas_arc.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: ?, style?: string, lineWidth?: number)' + }, + drawWaiting: { + '!doc': '绘制等待界面', + '!type': 'fn(text: string)' + }, + setFont: { + '!doc': '设置某个canvas的文字字体', + '!url': 'https://www.w3school.com.cn/tags/canvas_font.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, font: string)' + }, + drawChoices: { + '!doc': '绘制一个选项界面', + '!type': + 'fn(content?: string, choices?: [?], width?: number, ctx?: string|CanvasRenderingContext2D)' + }, + setFontForMaxWidth: { + '!doc': '根据最大宽度自动缩小字体', + '!type': + 'fn(name: string|CanvasRenderingContext2D, text: string, maxWidth: number, font?: ?) -> string' + }, + clearMap: { + '!doc': "清空某个画布图层
name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。
如果name也可以是'all',若为all则为清空所有系统画布。", + '!url': 'https://www.w3school.com.cn/tags/canvas_clearrect.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x?: number, y?: number, width?: number, height?: number)' + }, + drawTextContent: { + '!doc': '绘制一段文字到某个画布上面
ctx: 要绘制到的画布
content: 要绘制的内容;转义字符不允许保留 \\t, \\b 和 \\f
config: 绘制配置项,目前暂时包含如下内容(均为可选)
left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右
fontSize:字体大小;lineHeight:行高;time:打字机间隔;font:字体名
返回值:绘制信息', + '!type': + 'fn(ctx: string|CanvasRenderingContext2D, content: string, config: ?)' + }, + calWidth: { + '!doc': '计算某段文字的宽度', + '!url': 'https://www.w3school.com.cn/tags/canvas_measuretext.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, text: string, font?: string) -> number' + }, + fillArc: { + '!doc': '在某个canvas上绘制一个扇形', + '!url': 'https://www.w3school.com.cn/tags/canvas_arc.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string)' + }, + strokeArc: { + '!doc': '在某个canvas上绘制一段弧', + '!url': 'https://www.w3school.com.cn/tags/canvas_arc.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string, lineWidth?: number)' + }, + drawLine: { + '!doc': '在某个canvas上绘制一条线', + '!url': 'https://www.w3school.com.cn/tags/canvas_lineto.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)' + }, + drawPagination: { + '!doc': '绘制分页', + '!type': 'fn(page?: ?, totalPage?: ?, y?: number)' + }, + getToolboxItems: { + '!doc': '获得所有应该在道具栏显示的某个类型道具', + '!type': 'fn(cls: string) -> [string]' + }, + strokeRect: { + '!doc': '绘制一个矩形的边框
style: 绘制的样式
lineWidth: 线宽
angle: 旋转角度,弧度制,如Math.PI/2为90度', + '!url': 'https://www.w3school.com.cn/tags/canvas_strokerect.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, lineWidth?: number, angle?: number)' + }, + drawBook: { + '!doc': '绘制怪物手册', + '!type': 'fn(index?: ?)' + }, + fillRoundRect: { + '!doc': '在某个canvas上绘制一个圆角矩形', + '!type': + 'fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, angle?: number)' + }, + fillBoldText: { + '!doc': '在某个画布上绘制一个描边文字
text: 要绘制的文本
style: 绘制的样式
strokeStyle: 要绘制的描边颜色
font: 绘制的字体
maxWidth: 最大宽度,超过此宽度会自动放缩', + '!type': + 'fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, strokeStyle?: string, font?: string, maxWidth?: number)' + }, + saveCanvas: { + '!doc': '保存某个canvas状态', + '!type': 'fn(name: string|CanvasRenderingContext2D)' + }, + createCanvas: { + '!doc': '动态创建一个画布。
name: 要创建的画布名,如果已存在则会直接取用当前存在的。
x,y: 创建的画布相对窗口左上角的像素坐标
width,height: 创建的长宽。
zIndex: 创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。
返回创建的画布的context,也可以通过core.dymCanvas[name]调用。', + '!type': + 'fn(name: string, x: number, y: number, width: number, height: number, zIndex: number) -> CanvasRenderingContext2D' + }, + setTextAlign: { + '!doc': '设置某个canvas的对齐', + '!url': 'https://www.w3school.com.cn/tags/canvas_textalign.asp', + '!type': + 'fn(name: string|CanvasRenderingContext2D, align: string)' + } + }, + enemys: { + '!doc': '定义了一系列和怪物相关的API函数。', + getEnemys: { + '!doc': '获得所有怪物原始数据的一个副本。
请使用core.material.enemys获得当前各项怪物属性。', + '!type': 'fn()' + }, + getEnemyValue: { + '!doc': '获得某个点上怪物的某个属性值', + '!type': + 'fn(enemy?: string|enemy, name: string, x?: number, y?: number, floorId?: string)' + }, + getSpecials: { + '!doc': '获得所有特殊属性的定义', + '!type': 'fn() -> [[?]]' + }, + getSpecialColor: { + '!doc': '获得某个怪物所有特殊属性的颜色', + '!type': 'fn(enemy: string|enemy) -> [string]' + }, + getSpecialFlag: { + '!doc': '获得某个怪物所有特殊属性的额外标记。

例如,1为全图性技能,需要进行遍历全图(光环/支援等)', + '!type': 'fn(enemy: string|enemy) -> number' + }, + getSpecialHint: { + '!doc': "获得某种敌人的某种特殊属性的介绍
例如:core.getSpecialHint('bat', 1) // '先攻:怪物首先攻击'
enemy: 敌人id或敌人对象,用于确定属性的具体数值,否则可选
special: 属性编号,可以是该敌人没有的属性
返回值:属性的介绍,以属性名加中文冒号开头", + '!type': + 'fn(enemy: string|enemy, special: number) -> string' + }, + getSpecialText: { + '!doc': "获得某种敌人的全部特殊属性名称
例如:core.getSpecialText('greenSlime') // ['先攻', '3连击', '破甲', '反击']
enemy: 敌人id或敌人对象,如core.material.enemys.greenSlime
返回值:字符串数组", + '!type': 'fn(enemy: string|enemy) -> [string]' + }, + hasSpecial: { + '!doc': "判定某种特殊属性的有无
例如:core.hasSpecial('greenSlime', 1) // 判定绿头怪有无先攻属性
special: 敌人id或敌人对象或正整数数组或自然数
test: 待检查的属性编号
", + '!type': + 'fn(special: number|[number]|string|number, test: number) -> bool' + }, + nextCriticals: { + '!doc': "获得某只敌人接下来的若干个临界及其减伤,算法基于useLoop开关选择回合法或二分法
例如:core.nextCriticals('greenSlime', 9, 0, 0, 'MT0') // 绿头怪接下来的9个临界
enemy: 敌人id或敌人对象
number: 要计算的临界数量,可选,默认为1
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回:两列的二维数组,每行表示一个临界及其减伤", + '!type': + 'fn(enemy: string|enemy, number?: number, x?: number, y?: number, floorId?: string) -> [[number]]' + }, + getDefDamage: { + '!doc': "计算再加若干点防御能使某只敌人对主角的总伤害降低多少
例如:core.getDefDamage('greenSlime', 10, 0, 0, 'MT0') // 再加10点防御能使绿头怪的伤害降低多少
enemy: 敌人id或敌人对象
k: 假设主角增加的防御力,可选,默认为1
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选", + '!type': + 'fn(enemy: string|enemy, k?: number, x?: number, y?: number, floorId?: string) -> number' + }, + canBattle: { + '!doc': "判定主角当前能否打败某只敌人
例如:core.canBattle('greenSlime',0,0,'MT0') // 能否打败主塔0层左上角的绿头怪(假设有)
enemy: 敌人id或敌人对象
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:true表示可以打败,false表示无法打败", + '!type': + 'fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> bool' + }, + getEnemyInfo: { + '!doc': '获得怪物真实属性
hero: 可选,此时的勇士属性
此函数将会计算包括坚固、模仿、光环等若干效果,将同时被怪物手册和伤害计算调用', + '!type': + 'fn(enemy: string|enemy, hero?: ?, x?: number, y?: number, floorId?: string) -> {hp: number, atk: number, def: number, money: number, exp: number, special: [number], point: number, guards: [?]}' + }, + getDamageInfo: { + '!doc': "获得战斗伤害信息
例如:core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害
enemy: 敌人id或敌人对象
hero: 可选,此时的勇士属性
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:伤害计算信息,如果因为没有破防或无敌怪等其他原因无法战斗,则返回null", + '!type': + 'fn(enemy: string|enemy, hero?: ?, x?: number, y?: number, floorId?: string) -> {damage: number, per_damage: number, hero_per_damage: number, init_damage: number, mon_hp: number, mon_atk: number, mon_def: number, turn: number}' + }, + getDamage: { + '!doc': "获得某只敌人对主角的总伤害
例如:core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害
enemy: 敌人id或敌人对象
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:总伤害,如果因为没有破防或无敌怪等其他原因无法战斗,则返回null", + '!type': + 'fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> number' + }, + getDamageString: { + '!doc': "获得某只敌人的地图显伤,包括颜色
例如:core.getDamageString('greenSlime', 0, 0, 'MT0') // 绿头怪的地图显伤
enemy: 敌人id或敌人对象
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:damage: 表示伤害值或为'???',color: 形如'#RrGgBb'", + '!type': + 'fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> {color: string, damage: string}' + }, + getCurrentEnemys: { + '!doc': "获得某张地图的敌人集合,用于手册绘制
例如:core.getCurrentEnemys('MT0') // 主塔0层的敌人集合
floorId: 地图id,可选
返回值:敌人集合,按伤害升序排列,支持多朝向怪合并", + '!type': 'fn(floorId?: string) -> [enemy]' + }, + hasEnemyLeft: { + '!doc': "检查某些楼层是否还有漏打的(某种)敌人
例如:core.hasEnemyLeft('greenSlime', ['sample0', 'sample1']) // 样板0层和1层是否有漏打的绿头怪
enemyId: 敌人id,可选,null表示任意敌人
floorId: 地图id或其数组,可选,不填为当前地图
返回值:地图中是否还存在该种敌人", + '!type': + 'fn(enemyId?: string, floorId?: string|[string]) -> bool' + } + }, + events: { + '!doc': 'events.js将处理所有和事件相关的操作,主要分为五个部分:
- 游戏的开始和结束
- 系统事件的处理
- 自定义事件的处理
- 点击状态栏图标所进行的操作
- 一些具体事件的执行内容', + afterChangeFloor: { + '!doc': '转换楼层结束的事件', + '!type': 'fn(floorId?: string)' + }, + popEventLoc: { + '!doc': '将当前点坐标入栈', + '!type': 'fn()' + }, + afterOpenDoor: { + '!doc': '开一个门后触发的事件', + '!type': 'fn(doorId?: string, x?: number, y?: number)' + }, + checkLvUp: { + '!doc': '检查升级事件', + '!type': 'fn()' + }, + insertAction: { + '!doc': "插入一段事件;此项不可插入公共事件,请用 core.insertCommonEvent
例如:core.insertAction('一段文字'); // 插入一个显示文章
action: 单个事件指令,或事件指令数组
x: 新的当前点横坐标,可选
y: 新的当前点纵坐标,可选
callback: 新的回调函数,可选
addToLast: 插入的位置,true表示插入到末尾,否则插入到开头", + '!type': + 'fn(action: string|?|[?], x?: number, y?: number, callback?: fn(), addToLast?: bool)' + }, + unfollow: { + '!doc': '取消跟随
name: 取消跟随的行走图,不填则取消全部跟随者', + '!type': 'fn(name?: string)' + }, + hasVisitedFloor: { + '!doc': '是否到达过某个楼层', + '!type': 'fn(floorId?: string) -> bool' + }, + startEvents: { + '!doc': '开始执行一系列自定义事件', + '!type': + 'fn(list?: [?], x?: number, y?: number, callback?: fn())' + }, + setHeroIcon: { + '!doc': "更改主角行走图
例如:core.setHeroIcon('npc48.png', true); // 把主角从阳光变成样板0层左下角的小姐姐,但不立即刷新
name: 新的行走图文件名,可以是全塔属性中映射前的中文名。映射后会被存入core.status.hero.image
noDraw: true表示不立即刷新(刷新会导致大地图下视野重置到以主角为中心)", + '!type': 'fn(name: string, noDraw?: bool)' + }, + changingFloor: { + '!doc': '楼层转换中', + '!type': + 'fn(floorId?: string, heroLoc?: {x: number, y: number, direction: string})' + }, + setEvents: { + '!doc': '直接设置事件列表', + '!type': + 'fn(list?: [?], x?: number, y?: number, callback?: fn())' + }, + setValue: { + '!doc': '数值操作', + '!type': + 'fn(name: string, operator: string, value: ?, prefix?: string)' + }, + precompile: { + '!doc': '预编辑事件', + '!type': 'fn(data?: ?)' + }, + vibrate: { + '!doc': '视野抖动
例如:core.vibrate(); // 视野抖动1秒
direction: 抖动方向;可填 horizontal(左右),vertical(上下),diagonal1(左上右下),diagonal2(左下右上)
time: 抖动时长
speed: 抖动速度
power: 抖动幅度
callback: 抖动平息后的回调函数,可选', + '!type': + 'fn(direction?: string, time?: number, speed?: number, power?: number, callback?: fn())' + }, + confirmRestart: { + '!doc': '询问是否需要重新开始', + '!type': 'fn()' + }, + battle: { + '!doc': "战斗,如果填写了坐标就会删除该点的敌人并触发战后事件
例如:core.battle('greenSlime'); // 和从天而降的绿头怪战斗(如果打得过)
id: 敌人id,必填
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
force: true表示强制战斗,可选
callback: 回调函数,可选", + '!type': + 'fn(id: string, x?: number, y?: number, force?: bool, callback?: fn())' + }, + follow: { + '!doc': '跟随
name: 要跟随的一个合法的4x4的行走图名称,需要在全塔属性注册', + '!type': 'fn(name: string)' + }, + beforeBattle: { + '!doc': '战斗前触发的事件;返回false代表不进行战斗', + '!type': + 'fn(enemyId?: string, x?: number, y?: number) -> bool' + }, + registerEvent: { + '!doc': '注册一个自定义事件
type: 事件类型
func: 事件的处理函数,可接受(data, x, y, prefix)参数
data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀', + '!type': + 'fn(type: string, func: fn(data: ?, x?: number, y?: number, prefix?: string))' + }, + flyTo: { + '!doc': '飞往某一层', + '!type': 'fn(toId?: string, callback?: fn()) -> bool' + }, + afterGetItem: { + '!doc': '获得一个道具后的事件', + '!type': + 'fn(id?: string, x?: number, y?: number, isGentleClick?: bool)' + }, + doAction: { + '!doc': '执行下一个事件指令,常作为回调
例如:core.setCurtain([0,0,0,1], null, null, core.doAction); // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果', + '!type': 'fn()' + }, + openBook: { + '!doc': '点击怪物手册时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + save: { + '!doc': '点击存档按钮时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + load: { + '!doc': '点击读档按钮时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + getNextItem: { + '!doc': '轻按获得面前的物品或周围唯一物品
noRoute: 若为true则不计入录像', + '!type': 'fn(noRoute?: bool)' + }, + hasAsync: { + '!doc': '当前是否有未处理完毕的异步事件(不包含动画和音效)', + '!type': 'fn() -> bool' + }, + stopAsync: { + '!doc': '立刻停止所有正在进行的异步事件', + '!type': 'fn()' + }, + openEquipbox: { + '!doc': '点击装备栏时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + recoverEvents: { + '!doc': '恢复一个事件', + '!type': 'fn(data?: ?)' + }, + setGlobalFlag: { + '!doc': "设置一个系统开关
例如:core.setGlobalFlag('steelDoorWithoutKey', true); // 使全塔的所有铁门都不再需要钥匙就能打开
name: 系统开关的英文名
value: 开关的新值,您可以用!core.flags[name]简单地表示将此开关反转", + '!type': 'fn(name: string, value: bool)' + }, + moveImage: { + '!doc': '移动一张图片并/或改变其透明度
例如:core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明
code: 图片编号
to: 新的左上角坐标,省略表示原地改变透明度
opacityVal: 新的透明度,省略表示不变
time: 移动用时,单位为毫秒。不填视为1秒
callback: 图片移动完毕后的回调函数,可选', + '!type': + 'fn(code: number, to?: [number], opacityVal?: number, moveMode?: string, time?: number, callback?: fn())' + }, + rotateImage: { + '!doc': '旋转一张图片
code: 图片编号
center: 旋转中心像素坐标(以屏幕为基准);不填视为图片本身中心
angle: 旋转角度;正数为顺时针,负数为逆时针
moveMode: 旋转模式
time: 旋转用时,单位为毫秒。不填视为1秒
callback: 图片旋转完毕后的回调函数,可选', + '!type': + 'fn(code: number, center?: [number], angle?: number, moveMode?: string, time?: number, callback?: fn())' + }, + scaleImage: { + '!doc': '放缩一张图片', + '!type': + 'fn(code: number, center?: [number], scale?: number, moveMode?: string, time?: number, callback?: fn())' + }, + moveTextBox: { + '!doc': '移动对话框', + '!type': + 'fn(code: number, loc: [number], relative?: bool, moveMode?: string, time?: number, callback?: fn())' + }, + clearTextBox: { + '!doc': '清除对话框', + '!type': 'fn(code: number)' + }, + openSettings: { + '!doc': '点击设置按钮时的操作', + '!type': 'fn(fromUserAction?: bool)' + }, + afterPushBox: { + '!doc': '推箱子后的事件', + '!type': 'fn()' + }, + unregisterSystemEvent: { + '!doc': '注销一个系统事件', + '!type': 'fn(type: string)' + }, + trigger: { + '!doc': '触发(x,y)点的系统事件;会执行该点图块的script属性,同时支持战斗(会触发战后)、道具(会触发道具后)、楼层切换等等
callback: 执行完毕的回调函数
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】', + '!type': 'fn(x?: number, y?: number, callback?: fn())' + }, + restart: { + '!doc': '重新开始游戏;此函数将回到标题页面', + '!type': 'fn()' + }, + doEvent: { + '!doc': '执行一个自定义事件', + '!type': + 'fn(data?: ?, x?: number, y?: number, prefix?: string)' + }, + win: { + '!doc': '游戏获胜事件', + '!type': 'fn(reason?: string, norank?: bool, noexit?: bool)' + }, + setGlobalAttribute: { + '!doc': '设置全塔属性', + '!type': 'fn(name: string, value: string)' + }, + setNameMap: { + '!doc': '设置文件别名', + '!type': 'fn(name: string, value?: string)' + }, + setTextAttribute: { + '!doc': '设置剧情文本的属性', + '!type': 'fn(data: ?)' + }, + openToolbox: { + '!doc': '点击工具栏时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + setVolume: { + '!doc': '调节bgm的音量
例如:core.setVolume(0, 100, core.jumpHero); // 0.1秒内淡出bgm,然后主角原地跳跃半秒
value: 新的音量,为0或不大于1的正数。注意系统设置中是这个值的平方根的十倍
time: 渐变用时,单位为毫秒。不填或小于100毫秒都视为0
callback: 渐变完成后的回调函数,可选', + '!type': 'fn(value: number, time?: number, callback?: fn())' + }, + pushEventLoc: { + '!doc': '将当前点坐标入栈', + '!type': + 'fn(x?: number, y?: number, floorId?: string) -> bool' + }, + openKeyBoard: { + '!doc': '点击虚拟键盘时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + insertCommonEvent: { + '!doc': "插入一个公共事件
例如:core.insertCommonEvent('加点事件', [3]);
name: 公共事件名;如果公共事件不存在则直接忽略
args: 参数列表,为一个数组,将依次赋值给 flag:arg1, flag:arg2, ...
x: 新的当前点横坐标,可选
y: 新的当前点纵坐标,可选
callback: 新的回调函数,可选
addToLast: 插入的位置,true表示插入到末尾,否则插入到开头", + '!type': + 'fn(name?: string, args?: [?], x?: number, y?: number, callback?: fn(), addToLast?: bool)' + }, + hideImage: { + '!doc': '隐藏一张图片
例如:core.hideImage(1, 1000, core.jumpHero); // 1秒内淡出1号图片,然后主角原地跳跃半秒
code: 图片编号
time: 淡出时间,单位为毫秒
callback: 图片完全消失后的回调函数,可选', + '!type': 'fn(code: number, time?: number, callback?: fn())' + }, + visitFloor: { + '!doc': '到达某楼层', + '!type': 'fn(floorId?: string)' + }, + openQuickShop: { + '!doc': '点击快捷商店按钮时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + afterBattle: { + '!doc': '战斗结束后触发的事件', + '!type': 'fn(enemyId?: string, x?: number, y?: number)' + }, + pushBox: { + '!doc': '推箱子', + '!type': 'fn(data?: ?)' + }, + autoEventExecuted: { + '!doc': '当前是否执行过某个自动事件', + '!type': 'fn(symbol?: string, value?: ?) -> bool' + }, + onSki: { + '!doc': '当前是否在冰上', + '!type': 'fn(number?: number) -> bool' + }, + showImage: { + '!doc': "显示一张图片
例如:core.showImage(1, core.material.images.images['winskin.png'], [0,0,128,128], [0,0,416,416], 0.5, 1000); // 裁剪winskin.png的最左边128×128px,放大到铺满整个视野,1秒内淡入到50%透明,编号为1
code: 图片编号,为不大于50的正整数,加上100后就是对应画布层的z值,较大的会遮罩较小的,注意色调层的z值为125,UI层为140
image: 图片文件名(可以是全塔属性中映射前的中文名)或图片对象(见上面的例子)
sloc: 一行且至多四列的数组,表示从原图裁剪的左上角坐标和宽高,可选
loc: 一行且至多四列的数组,表示图片在视野中的左上角坐标和宽高,可选
opacityVal: 不透明度,为小于1的正数。不填视为1
time: 淡入时间,单位为毫秒。不填视为0
callback: 图片完全显示出来后的回调函数,可选", + '!type': + 'fn(code: number, image: string|image, sloc?: [number], loc?: [number], opacityVal?: number, time?: number, callback?: fn())' + }, + getItem: { + '!doc': "获得道具并提示,如果填写了坐标就会删除该点的该道具
例如:core.getItem('book'); // 获得敌人手册并提示
id: 道具id,必填
num: 获得的数量,不填视为1,填了就别填坐标了
x: 道具的横坐标,可选
y: 道具的纵坐标,可选
callback: 回调函数,可选", + '!type': + 'fn(id: string, num?: number, x?: number, y?: number, callback?: fn())' + }, + registerSystemEvent: { + '!doc': '注册一个系统事件
type: 事件名
func: 为事件的处理函数,可接受(data,callback)参数', + '!type': + 'fn(type: string, func: fn(data?: ?, callback?: fn()))' + }, + startGame: { + '!doc': "开始新游戏
例如:core.startGame('咸鱼乱撞', 0, ''); // 开始一局咸鱼乱撞难度的新游戏,随机种子为0
hard: 难度名,会显示在左下角(横屏)或右下角(竖屏)
seed: 随机种子,相同的种子保证了录像的可重复性
route: 经由base64压缩后的录像,用于从头开始的录像回放
callback: 回调函数,可选", + '!type': + 'fn(hard: string, seed: number, route: string, callback?: fn())' + }, + doSystemEvent: { + '!doc': '执行一个系统事件', + '!type': 'fn(type: string, data?: ?, callback?: fn())' + }, + resetGame: { + '!doc': '初始化游戏', + '!type': + 'fn(hero?: ?, hard?: ?, floorId?: string, maps?: ?, values?: ?)' + }, + setFloorInfo: { + '!doc': "设置一项楼层属性并刷新状态栏
例如:core.setFloorInfo('ratio', 2, 'MT0'); // 把主塔0层的血瓶和宝石变为双倍效果
name: 要修改的属性名
values: 属性的新值。
floorId: 楼层id,不填视为当前层
prefix: 独立开关前缀,一般不需要", + '!type': + 'fn(name: string, values: ?, floorId?: string, prefix?: string)' + }, + openDoor: { + '!doc': '开门(包括三种基础墙)
例如:core.openDoor(0, 0, true, core.jumpHero); // 打开左上角的门,需要钥匙,然后主角原地跳跃半秒
x: 门的横坐标
y: 门的纵坐标
needKey: true表示需要钥匙,会导致机关门打不开
callback: 门完全打开后或打不开时的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】', + '!type': + 'fn(x: number, y: number, needKey?: bool, callback?: fn())' + }, + setEnemy: { + '!doc': "设置一项敌人属性并计入存档
例如:core.setEnemy('greenSlime', 'def', 0); // 把绿头怪的防御设为0
id: 敌人id
name: 属性的英文缩写
value: 属性的新值,可选
operator: 运算操作符如+=,可选
prefix: 独立开关前缀,一般不需要,下同", + '!type': + 'fn(id: string, name: string, value: ?, operator?: string, prefix?: string)' + }, + setEnemyOnPoint: { + '!doc': "设置某个点的敌人属性。如果该点不是怪物,则忽略此函数。
例如:core.setEnemyOnPoint(3, 5, null, 'atk', 100, '+='); // 仅将(3,5)点怪物的攻击力加100。", + '!type': + 'fn(x: number, y: number, floorId?: string, name: string, value: ?, operator?: string, prefix?: string)' + }, + resetEnemyOnPoint: { + '!doc': '重置某个点的怪物属性', + '!type': 'fn(x: number, y: number, floorId?: string)' + }, + moveEnemyOnPoint: { + '!doc': '将某个点已经设置的敌人属性移动到其他点', + '!type': + 'fn(fromX: number, fromY: number, toX: number, toY: number, floorId?: string)' + }, + autoEventExecuting: { + '!doc': '当前是否在执行某个自动事件', + '!type': 'fn(symbol?: string, value?: ?) -> bool' + }, + checkAutoEvents: { + '!doc': '检测自动事件', + '!type': 'fn()' + }, + showGif: { + '!doc': '绘制一张动图或擦除所有动图
例如:core.showGif(); // 擦除所有动图
name: 动图文件名,可以是全塔属性中映射前的中文名
x: 动图在视野中的左上角横坐标
y: 动图在视野中的左上角纵坐标', + '!type': 'fn(name?: string, x?: number, y?: number)' + }, + unregisterEvent: { + '!doc': '注销一个自定义事件', + '!type': 'fn(type: string)' + }, + jumpHero: { + '!doc': '主角跳跃,跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。
例如:core.jumpHero(); // 主角原地跳跃半秒
ex: 跳跃后的横坐标
ey: 跳跃后的纵坐标
time: 跳跃时长,单位为毫秒。不填视为半秒
callback: 跳跃完毕后的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】', + '!type': + 'fn(ex?: number, ey?: number, time?: number, callback?: fn())' + }, + closeDoor: { + '!doc': "关门,目标点必须为空地
例如:core.closeDoor(0, 0, 'yellowWall', core.jumpHero); // 在左上角关掉一堵黄墙,然后主角原地跳跃半秒
x: 横坐标
y: 纵坐标
id: 门的id,也可以用三种基础墙
callback: 门完全关上后的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", + '!type': + 'fn(x: number, y: number, id: string, callback?: fn())' + }, + eventMoveHero: { + '!doc': "强制移动主角(包括后退),这个函数的作者已经看不懂这个函数了
例如:core.eventMoveHero(['forward'], 125, core.jumpHero); // 主角强制前进一步,用时1/8秒,然后主角原地跳跃半秒
steps: 步伐数组,注意后退时跟随者的行为会很难看
time: 每步的用时,单位为毫秒。0或不填则取主角的移速,如果后者也不存在就取0.1秒
callback: 移动完毕后的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", + '!type': 'fn(steps: [step], time?: number, callback?: fn())' + }, + changeFloor: { + '!doc': "场景切换
例如:core.changeFloor('MT0'); // 传送到主塔0层,主角坐标和朝向不变,黑屏时间取用户定义的值
floorId: 传送的目标地图id,可以填':before'和':next'分别表示楼下或楼上
stair: 传送的位置
heroLoc: 传送的坐标;会覆盖stair
time: 传送的黑屏时间,单位为毫秒;不填为用户设置值
callback: 传送的回调函数
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", + '!type': + 'fn(floorId: string, stair?: string, heroLoc?: {x?: number, y?: number, direction?: string}, time?: number, callback?: fn())' + }, + getCommonEvent: { + '!doc': '获得一个公共事件', + '!type': 'fn(name: string) -> [?]' + }, + lose: { + '!doc': '游戏失败事件', + '!type': 'fn(reason?: string)' + }, + gameOver: { + '!doc': '游戏结束
例如:core.gameOver(); // 游戏失败
ending: 结局名,省略表示失败
fromReplay: true表示在播放录像,可选
norank: true表示不计入榜单,可选', + '!type': + 'fn(ending?: string, fromReplay?: bool, norank?: bool)' + }, + useFly: { + '!doc': '点击楼层传送器时的打开操作', + '!type': 'fn(fromUserAction?: bool)' + }, + tryUseItem: { + '!doc': "尝试使用一个道具
例如:core.tryUseItem('pickaxe'); // 尝试使用破墙镐
itemId: 道具id,其中敌人手册、传送器和飞行器会被特殊处理", + '!type': 'fn(itemId: string)' + } + }, + plugin: { + '!doc': '插件编写中内置了一些常用的插件。', + drawLight: { + '!doc': "绘制一段灯光效果
name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建
color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。
lights:可选,一个数组,定义了每个独立的灯光。其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。
lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。
例如:core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。
core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。", + '!type': + 'fn(name: string|CanvasRenderingContext2D, color?: number, lights?: [[number]], lightDec?: number)' + }, + openShop: { + '!doc': '打开一个全局商店
shopId: 要开启的商店ID
noRoute: 打开行为是否不计入录像', + '!type': 'fn(shopId: string, noRoute?: bool)' + }, + isShopVisited: { + '!doc': '某个全局商店是否被访问过', + '!type': 'fn(id: string) -> bool' + }, + listShopIds: { + '!doc': '列出所有应当显示的快捷商店列表', + '!type': 'fn() -> [string]' + }, + canOpenShop: { + '!doc': '当前能否打开某个商店', + '!type': 'fn(id: string) -> bool' + }, + setShopVisited: { + '!doc': '设置某个商店的访问状态', + '!type': 'fn(id: string, visited?: bool)' + }, + canUseQuickShop: { + '!doc': '当前能否使用某个快捷商店
如果返回一个字符串,则代表不能,返回的字符串作为不能的提示;返回null表示可以使用', + '!type': 'fn(id: string) -> string' + }, + removeMaps: { + '!doc': '删除某一些楼层;删除后不会存入存档,不可浏览地图也不可飞到。
fromId: 开始删除的楼层ID
toId: 删除到的楼层编号;可选,不填则视为fromId
例如:core.plugin.removeMap.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层
core.plugin.removeMap.removeMaps("MT10") 只删除MT10层', + '!type': 'fn(fromId: string, toId?: string)' + }, + resumeMaps: { + '!doc': '恢复某一些被删除楼层。
fromId: 开始恢复的楼层ID
toId: 恢复到的楼层编号;可选,不填则视为fromId
例如:core.plugin.removeMap.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层
core.plugin.removeMap.resumeMaps("MT10") 只删恢复MT10层', + '!type': 'fn(fromId: string, toId?: string)' + }, + autoRemoveMaps: { + '!doc': '根据楼层分区信息自动砍层与恢复', + '!type': 'fn(floorId: string)' + }, + openItemShop: { + '!doc': '打开一个道具商店', + '!type': 'fn(itemShopId: string)' + } + } }, - "body": { - "!type": "+Element", - "!doc": "返回当前文档的或节点.", + lzw_encode: { + '!doc': 'LZW压缩算法', + '!url': 'https://gist.github.com/revolunet/843889', + '!type': 'fn(s: string) -> string' }, - "cookie": { - "!type": "string", - "!doc": "获取并设置与当前文档关联的cookie.", + lzw_decode: { + '!doc': 'LZW解压缩算法', + '!url': 'https://gist.github.com/revolunet/843889', + '!type': 'fn(s: string) -> string' }, - "URL": "string", - "title": { - "!type": "string", - "!doc": "获取或设置文档的标题." + hero: { + '!type': 'heroStatus', + '!doc': '勇士信息,为 core.status.hero 的简写' }, - "getElementById": { - "!type": "fn(id: string) -> +Element", - "!doc": "通过元素ID返回对该元素的引用." - }, - "getElementsByTagName": { - "!type": "fn(tagName: string) -> +NodeList", - "!doc": "返回具有给定标签名称的元素的NodeList.将搜索整个文档,包括根节点.返回的NodeList处于活动状态,这意味着它会自动更新自身以与DOM树保持同步,而无需再次调用document.getElementsByTagName." - }, - "getElementsByName": { - "!type": "fn(name: string) -> +HTMLCollection", - "!doc": "返回HTML文档中具有给定名称的元素列表.", - }, - "getElementsByClassName": "Element.prototype.getElementsByClassName" - }, - "!doc": "浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和
之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素)." - }, - "document": { - "!type": "+Document", - "!doc": "浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和
之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素)." - }, - "Event": { - "!type": "fn()", - "prototype": { - "stopPropagation": { - "!type": "fn()", - "!doc": "防止当前事件进一步传播." - }, - "preventDefault": { - "!type": "fn()", - "!doc": "如果可以取消事件,则取消该事件,而不停止事件的进一步传播." - }, - "stopImmediatePropagation": { - "!type": "fn()", - "!doc": "防止同一事件的其他侦听器被调用." - }, - "type": { - "!type": "string", - "!doc": "返回包含事件类型的字符串." - }, - "target": { - "!type": "+Element", - "!doc": " EventTarget是由对象实现的DOM接口,这些对象可以接收DOM事件并具有侦听器.最常见的EventTarget是DOM元素,尽管其他对象也可以是EventTarget,例如文档,窗口,XMLHttpRequest,和别的." - }, - "clientX": { - "!type": "number", - "!doc": "返回事件发生的应用程序客户区域内的水平坐标(与页面内的坐标相反).例如,单击客户区域左上角将始终显示clientX值为0的鼠标事件,无论页面是否水平滚动." - }, - "clientY": { - "!type": "number", - "!doc": "返回事件发生在应用程序客户区中的垂直坐标(与页面中的坐标相反).例如,单击客户区左上角将始终显示不管页面是否垂直滚动,clientY值为0的鼠标事件." - }, - "keyCode": { - "!type": "number", - "!doc": "返回按键事件中的非字符键或任何其他类型的键盘事件中的任何键的Unicode值.", - }, - "charCode": { - "!type": "number", - "!doc": "返回在按键事件期间按下的字符键的Unicode值." - }, - "which": { - "!type": "number", - "!doc": "返回所按下键的数字keyCode或所按下字母数字键的字符代码(charCode)." - }, - "button": { - "!type": "number", - "!doc": "指示导致事件的鼠标按钮." - }, - "shiftKey": { - "!type": "bool", - "!doc": "指示事件触发时是否按下SHIFT键.", - }, - "ctrlKey": { - "!type": "bool", - "!doc": "指示事件触发时是否按下了CTRL键.", - }, - "altKey": { - "!type": "bool", - "!doc": "指示事件触发时是否按下ALT键.", + flags: { + '!type': 'flag', + '!doc': '游戏中用到的变量,为 core.status.hero.flags 的简写' } - } - }, - "Storage": { - "length": { - "!type": "number", - "!doc": "存储接口的length只读属性返回一个整数,该整数表示存储在存储对象中的数据项的数量.", - }, - "setItem": { - "!type": "fn(name: string, value: string)", - "!doc": "存储接口的setItem()方法在传递键名称和值时,会将该键添加到存储中,或者更新该键的值(如果已存在).", - }, - "getItem": { - "!type": "fn(name: string) -> string", - "!doc": "存储接口的getItem()方法在传递键名时将返回该键的值.", - }, - "key": { - "!type": "fn(index: number) -> string", - "!doc": "存储接口的key()方法传递数字n时,返回存储中第n个键的名称.键的顺序是用户代理定义的,因此您不应依赖它." - }, - "removeItem": { - "!type": "fn(key: string)", - "!doc": "存储接口的removeItem()方法在传递了键名后,将从存储中删除该键.", - }, - "clear": { - "!type": "fn()", - "!doc": "存储接口的clear()方法在被调用时将从存储中清空所有键." - } - }, - "localStorage": { - "!type": "Storage", - "!doc": " localStorage属性允许您访问本地存储对象.localStorage与sessionStorage类似.唯一的区别是,虽然存储在localStorage中的数据没有到期时间,但是浏览会话时存储在sessionStorage中的数据将被清除.结束-也就是说,当浏览器关闭时.\ n \ n请注意,存储在localStorage或sessionStorage中的数据特定于页面协议." - }, - "console": { - "assert": { - "!type": "fn(assertion: bool, text: string)", - "!doc": "如果断言为false,则将错误消息写入控制台.", - }, - "error": { - "!type": "fn(...msg: ?)", - "!doc": "将错误消息输出到Web控制台.", - }, - "info": { - "!type": "fn(...msg: ?)", - "!doc": "将参考消息输出到Web控制台.", - }, - "log": { - "!type": "fn(...msg: ?)", - "!doc": "将消息输出到Web控制台.", - }, - "time": { - "!type": "fn(label: string)", - "!doc": "启动计时器,您可以使用该计时器来跟踪操作需要多长时间.", - }, - "timeEnd": { - "!type": "fn(label: string)", - "!doc": "停止以前通过调用console.time()启动的计时器.", - }, - "trace": { - "!type": "fn()", - "!doc": "将堆栈跟踪输出到Web控制台.", - }, - "warn": { - "!type": "fn(...msg: ?)", - "!doc": "将警告消息输出到Web控制台.", - }, - "!doc": "控制台对象提供对浏览器调试控制台的访问.其工作方式的细节因浏览器而异,但实际上提供了一组事实上的功能.", - }, - "window": { - "!type": "", - "!doc": "窗口对象代表一个包含DOM文档的窗口.", - }, - "self": { - "!type": "", - "!doc": "将对象引用返回到窗口对象.", - }, - "devicePixelRatio": "number", - "requestAnimationFrame": { - "!type": "fn(callback: fn(timestamp: number)) -> number", - "!doc": " Window.requestAnimationFrame()方法告诉浏览器您希望执行动画,并请求浏览器在下一次重绘之前调用指定的函数来更新动画.该方法将回调作为参数在重新粉刷之前被调用." - }, - "cancelAnimationFrame": { - "!type": "fn(number)n", - "!doc": "取消先前安排的动画帧请求.", - }, - "alert": { - "!type": "fn(message: string)", - "!doc": "显示具有指定内容和确定按钮的警报对话框." - }, - "confirm": { - "!type": "fn(message: string) -> bool", - "!doc": "显示带有消息和两个按钮(确定和取消)的模式对话框.", - }, - "prompt": { - "!type": "fn(message: string, value: string) -> string", - "!doc": "显示一个对话框,提示用户输入一些文本.", - }, - "setTimeout": { - "!type": "fn(f: fn(), ms: number) -> number", - "!doc": "在指定的延迟后调用函数或执行代码段." - }, - "clearTimeout": { - "!type": "fn(timeout: number)", - "!doc": "清除window.setTimeout()设置的延迟.", - }, - "setInterval": { - "!type": "fn(f: fn(), ms: number) -> number", - "!doc": "反复调用一个函数或执行代码段,每次调用该函数之间有固定的时间延迟.", - }, - "clearInterval": { - "!type": "fn(interval: number)", - "!doc": "取消使用setInterval设置的重复操作.", - }, - "atob": { - "!type": "fn(encoded: string) -> string", - "!doc": "解码使用base-64编码编码的数据字符串." - }, - "btoa": { - "!type": "fn(data: string) -> string", - "!doc": "从一串二进制数据创建一个base-64编码的ASCII字符串.", - }, - "getComputedStyle": { - "!type": "fn(node: +Element, pseudo?: string) -> Element.prototype.style", - "!doc": "给出元素的所有CSS属性的最终使用值.", - }, - "CanvasRenderingContext2D": { - "canvas": "+Element", - "width": "number", - "height": "number", - "commit": "fn()", - "save": "fn()", - "restore": "fn()", - "currentTransform": "?", - "scale": "fn(x: number, y: number)", - "rotate": "fn(angle: number)", - "translate": "fn(x: number, y: number)", - "transform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)", - "setTransform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)", - "resetTransform": "fn()", - "globalAlpha": "number", - "globalCompositeOperation": "string", - "imageSmoothingEnabled": "bool", - "strokeStyle": "string", - "fillStyle": "string", - "createLinearGradient": "fn(x0: number, y0: number, x1: number, y1: number) -> ?", - "createPattern": "fn(image: ?, repetition: string) -> ?", - "shadowOffsetX": "number", - "shadowOffsetY": "number", - "shadowBlur": "number", - "shadowColor": "string", - "clearRect": "fn(x: number, y: number, w: number, h: number)", - "fillRect": "fn(x: number, y: number, w: number, h: number)", - "strokeRect": "fn(x: number, y: number, w: number, h: number)", - "fillRule": "string", - "fill": "fn()", - "beginPath": "fn()", - "stroke": "fn()", - "clip": "fn()", - "resetClip": "fn()", - "fillText": "fn(text: string, x: number, y: number, maxWidth: number)", - "strokeText": "fn(text: string, x: number, y: number, maxWidth: number)", - "measureText": "fn(text: string) -> ?", - "drawImage": "fn(image: ?, dx: number, dy: number)", - "createImageData": "fn(sw: number, sh: number) -> ?", - "getImageData": "fn(sx: number, sy: number, sw: number, sh: number) -> ?", - "putImageData": "fn(imagedata: ?, dx: number, dy: number)", - "lineWidth": "number", - "lineCap": "string", - "lineJoin": "string", - "miterLimit": "number", - "setLineDash": "fn(segments: [number])", - "getLineDash": "fn() -> [number]", - "lineDashOffset": "number", - "font": "string", - "textAlign": "string", - "textBaseline": "string", - "direction": "string", - "closePath": "fn()", - "moveTo": "fn(x: number, y: number)", - "lineTo": "fn(x: number, y: number)", - "quadraticCurveTo": "fn(cpx: number, cpy: number, x: number, y: number)", - "bezierCurveTo": "fn(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number)", - "arcTo": "fn(x1: number, y1: number, x2: number, y2: number, radius: number)", - "rect": "fn(x: number, y: number, w: number, h: number)", - "arc": "fn(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: bool)", - "ellipse": "fn(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise: bool)" } - }, - { - "!name": "ecmascript", - "Infinity": { - "!type": "number", - "!doc": "代表无穷大的数值." - }, - "undefined": { - "!type": "?", - "!doc": "该值未定义.", - }, - "NaN": { - "!type": "number", - "!doc": "代表非数字的值." - }, - "Object": { - "!type": "fn()", - "create": { - "!type": "fn(proto: ?) -> !custom:Object_create", - "!doc": "使用指定的原型对象和属性创建一个新对象.", - }, - "defineProperty": { - "!type": "fn(obj: ?, prop: string, desc: propertyDescriptor) -> !custom:Object_defineProperty", - "!doc": "直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象.如果想了解如何将Object.defineProperty方法与类似二进制标志的语法一起使用,请参阅本文." - }, - "keys": { - "!type": "fn(obj: ?) -> [string]", - "!doc": "返回一个给定对象自己的可枚举属性的数组,其顺序与for-in循环所提供的顺序相同(不同之处在于for-in循环也枚举了原型链中的属性). " - }, - "assign": { - "!type": "fn(target: ?, source: ?, source?: ?) -> !0", - "!effects": ["copy !1 !0", "copy !2 !0", "copy !3 !0"], - "!doc": " Object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象.它将返回目标对象.,", - }, - "prototype": { - "!stdProto": "Object", - "toString": { - "!type": "fn() -> string", - "!doc": "返回表示对象的字符串." - }, - "hasOwnProperty": { - "!type": "fn(prop: string) -> bool", - "!doc": "返回一个布尔值,指示对象是否具有指定的属性.", - } - }, - }, - "Function": { - "!type": "fn(body: string) -> fn()", - "prototype": { - "!stdProto": "Function", - "apply": { - "!type": "fn(this: ?, args: [?])", - "!effects": [ - "call and return !this this=!0 !1. !1. !1." - ], - "!doc": "调用具有给定值的函数,并以数组(或类似对象的数组)形式提供参数.", - }, - "call": { - "!type": "fn(this: ?, args?: ?) -> !this.!ret", - "!effects": [ - "call and return !this this=!0 !1 !2 !3 !4" - ], - "!doc": "调用具有给定值和单独提供的参数的函数.", - }, - "bind": { - "!type": "fn(this: ?, args?: ?) -> !custom:Function_bind", - "!doc": "创建一个新函数,该函数在被调用时将其this关键字设置为提供的值,并在调用新函数时提供给定的参数序列.", - }, - "prototype": "?" - }, - }, - "Array": { - "!type": "fn(size: number) -> !custom:Array_ctor", - "isArray": { - "!type": "fn(value: ?) -> bool", - "!doc": "如果对象是数组,则返回true,否则返回false.", - }, - "from": { - "!type": "fn(arrayLike: ?, mapFn?: fn(elt: ?, i: number) -> ?, thisArg?: ?) -> [!0.]", - "!effects": [ - "call !1 this=!2 !0. number" - ], - "!doc": " Array.from()方法从类似数组或可迭代的对象创建一个新的Array实例.,", - }, - "of": { - "!type": "fn(elementN: ?) -> [!0]", - "!doc": " Array.of()方法创建一个新的Array实例,该实例具有可变数量的参数,而不考虑参数的数量或类型.,", - }, - "prototype": { - "!stdProto": "Array", - "length": { - "!type": "number", - "!doc": "一个无符号的32位整数,指定数组中的元素数.", - }, - "concat": { - "!type": "fn(other: [?]) -> !this", - "!doc": "返回一个新数组,该数组由该数组与其他数组和/或值组成.", - }, - "join": { - "!type": "fn(separator?: string) -> string", - "!doc": "将数组的所有元素连接到字符串中." - }, - "splice": { - "!type": "fn(pos: number, amount: number, newelt?: ?) -> [?]", - "!doc": "更改数组的内容,在删除旧元素的同时添加新元素.", - }, - "pop": { - "!type": "fn() -> !this.", - "!doc": "从数组中删除最后一个元素并返回该元素.", - }, - "push": { - "!type": "fn(newelt: ?) -> number", - "!effects": [ - "propagate !0 !this." - ], - "!doc": "通过添加给定元素并返回数组的新长度来更改数组.", - }, - "shift": { - "!type": "fn() -> !this.", - "!doc": "从数组中删除第一个元素并返回该元素.此方法更改数组的长度.", - }, - "unshift": { - "!type": "fn(newelt: ?) -> number", - "!effects": [ - "propagate !0 !this." - ], - "!doc": "将一个或多个元素添加到数组的开头,并返回数组的新长度.", - }, - "slice": { - "!type": "fn(from?: number, to?: number) -> !this", - "!doc": "返回数组一部分的浅表副本." - }, - "reverse": { - "!type": "fn()", - "!doc": "就地反转数组.第一个数组元素变为最后一个,而最后一个数组变为第一个.", - }, - "sort": { - "!type": "fn(compare?: fn(a: ?, b: ?) -> number)", - "!effects": [ - "call !0 !this. !this." - ], - "!doc": "将数组中的元素排序并返回数组." - }, - "indexOf": { - "!type": "fn(elt: ?, from?: number) -> number", - "!doc": "返回在数组中可以找到给定元素的第一个索引;如果不存在,则返回-1.", - }, - "lastIndexOf": { - "!type": "fn(elt: ?, from?: number) -> number", - "!doc": "返回在数组中找到给定元素的最后一个索引,如果不存在则返回-1.从fromIndex开始向后搜索数组.", - }, - "filter": { - "!type": "fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> !this", - "!effects": [ - "call !0 this=!1 !this. number !this" - ], - "!doc": "创建一个新数组,其中包含所有通过提供的功能实现的测试的元素.", - }, - "forEach": { - "!type": "fn(f: fn(elt: ?, i: number, array: +Array), context?: ?)", - "!effects": [ - "call !0 this=!1 !this. number !this" - ], - "!doc": "每个数组元素执行一次提供的功能." - }, - "map": { - "!type": "fn(f: fn(elt: ?, i: number, array: +Array) -> ?, context?: ?) -> [!0.!ret]", - "!effects": [ - "call !0 this=!1 !this. number !this" - ], - "!doc": "创建一个新数组,其结果是对该数组中的每个元素调用提供的函数.", - }, - "reduce": { - "!type": "fn(combine: fn(sum: ?, elt: ?, i: number, array: +Array) -> ?, init?: ?) -> !0.!ret", - "!effects": [ - "call !0 !1 !this. number !this" - ], - "!doc": "对一个累加器和数组的每个值(从左到右)应用一个函数,以将其减小为单个值.", - }, - "fill": { - "!type": "fn(value: ?, start?: number, end?: number) -> !this", - "!doc": " fill()方法使用静态值填充数组的所有元素,从开始索引到结束索引.,", - }, - "find": { - "!type": "fn(callback: fn(element: ?, index: number, array: [?]) -> bool, thisArg?: ?) -> !this.", - "!effects": ["call !0 this=!2 !this. number"], - "!doc": "如果数组中的元素满足提供的测试功能,则find()方法将在数组中返回一个值.否则,返回undefined.,", - }, - "findIndex": { - "!type": "fn(callback: fn(element: ?, index: number, array: [?]), thisArg?: ?) -> number", - "!effects": ["call !0 this=!2 !this. number"], - "!doc": "如果数组中的元素满足提供的测试功能,则findIndex()方法将返回数组中的索引.否则返回-1.,", - }, - "keys": { - "!type": "fn() -> +iter[:t=number]", - "!doc": " keys()方法返回一个新的数组迭代器,其中包含数组中每个索引的键.,", - }, - "values": { - "!type": "fn() -> +iter[:t=!this.]", - "!doc": " values()方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的值.,", - }, - "includes": { - "!type": "fn(value: ?, fromIndex?: number) -> bool", - "!doc": "确定数组是否包含某个元素,并根据需要返回true或false.,", - } - }, - }, - "String": { - "!type": "fn(value: ?) -> string", - "prototype": { - "!stdProto": "String", - "length": { - "!type": "number", - "!doc": "表示字符串的长度." - }, - "": "string", - "charAt": { - "!type": "fn(i: number) -> string", - "!doc": "从字符串中返回指定的字符.", - }, - "charCodeAt": { - "!type": "fn(i: number) -> number", - "!doc": "返回给定索引处字符的数字Unicode值(Unicode代码点> 0x10000除外).", - }, - "indexOf": { - "!type": "fn(char: string, from?: number) -> number", - "!doc": "返回指定值首次出现的调用String对象中的索引,从fromIndex开始搜索,\ n如果未找到该值,则返回-1.", - }, - "lastIndexOf": { - "!type": "fn(char: string, from?: number) -> number", - "!doc": "返回指定值最后一次出现的调用String对象内的索引,如果未找到则返回-1.从fromIndex开始向后搜索调用字符串.", - }, - "substring": { - "!type": "fn(from: number, to?: number) -> string", - "!doc": "返回一个索引与另一个索引之间或字符串末尾的字符串子集.
from为起始位置,to为终止位置.", - }, - "substr": { - "!type": "fn(from: number, length?: number) -> string", - "!doc": "以指定的字符数返回从指定位置开始的字符串中的字符.
from为起始位置,length为长度", - }, - "slice": { - "!type": "fn(from: number, to?: number) -> string", - "!doc": "提取字符串的一部分并返回新的字符串.", - }, - "padStart": { - "!type": "fn(targetLength: number, padString?: string) -> string", - "!doc": "用另一个字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.", - }, - "padEnd": { - "!type": "fn(targetLength: number, padString?: string) -> string", - "!doc": "用给定的字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.", - }, - "trim": { - "!type": "fn() -> string", - "!doc": "从字符串的两端删除空格.", - }, - "trimStart": { - "!type": "fn() -> string", - "!doc": "从字符串的开头删除空格.", - }, - "trimEnd": { - "!type": "fn() -> string", - "!doc": "从字符串末尾删除空格.", - }, - "toUpperCase": { - "!type": "fn() -> string", - "!doc": "返回转换为大写的调用字符串值." - }, - "toLowerCase": { - "!type": "fn() -> string", - "!doc": "返回转换为小写的调用字符串值." - }, - "split": { - "!type": "fn(pattern?: string|+RegExp, limit?: number) -> [string]", - "!doc": "通过将字符串分成子字符串,将String对象拆分为字符串数组.", - }, - "concat": { - "!type": "fn(other: string) -> string", - "!doc": "将两个或多个字符串的文本合并,并返回一个新字符串." - }, - "match": { - "!type": "fn(pattern: +RegExp) -> [string]", - "!doc": "用于将字符串与正则表达式匹配时用于检索匹配.", - }, - "replace": { - "!type": "fn(pattern: string|+RegExp, replacement: string) -> string", - "!doc": "返回一个新字符串,该字符串的某个或所有匹配项都由替换项替换.该模式可以是字符串或RegExp,并且替换项可以是字符串或每个匹配项将调用的函数. " - }, - "endsWith": { - "!type": "fn(searchString: string, position?: number) -> bool", - "!doc": " endsWith()方法确定一个字符串是否以另一个字符串的字符结尾,并根据需要返回true或false.,", - }, - "startsWith": { - "!type": "fn(searchString: string, position?: number) -> bool", - "!doc": " startsWith()方法确定一个字符串是否以另一个字符串的字符开头,并根据需要返回true或false.,", - } - }, - }, - "Number": { - "!type": "fn(value: ?) -> number", - "MAX_VALUE": { - "!type": "number", - "!doc": " JavaScript中可表示的最大数值." - }, - "MIN_VALUE": { - "!type": "number", - "!doc": " JavaScript中可表示的最小正数值." - }, - "POSITIVE_INFINITY": { - "!type": "number", - "!doc": "代表正无穷大值的值." - }, - "NEGATIVE_INFINITY": { - "!type": "number", - "!doc": "代表负无穷大值的值." - }, - "prototype": { - "!stdProto": "Number", - "toString": { - "!type": "fn(radix?: number) -> string", - "!doc": "返回代表指定Number对象的字符串" - }, - "toFixed": { - "!type": "fn(digits: number) -> string", - "!doc": "使用定点符号格式化数字" - }, - "toExponential": { - "!type": "fn(digits: number) -> string", - "!doc": "返回以指数表示形式表示Number对象的字符串" - }, - "toPrecision": { - "!type": "fn(digits: number) -> string", - "!doc": " toPrecision()方法返回一个字符串,该数字表示指定精度的数字.", - } - }, - "EPSILON": { - "!type": "number", - "!doc": " Number.EPSILON属性表示一个数值与可以表示为Number的最小值之间的差异.,", - }, - "MAX_SAFE_INTEGER": { - "!type": "number", - "!doc": " Number.MAX_SAFE_INTEGER常量表示JavaScript中的最大安全整数(2 ^ 53-1).,", - }, - "MIN_SAFE_INTEGER": { - "!type": "number", - "!doc": " Number.MIN_SAFE_INTEGER常量表示JavaScript(-(2 ^ 53-1))中的最小安全整数.,", - }, - "isFinite": { - "!type": "fn(testValue: ?) -> bool", - "!doc": " Number.isFinite()方法确定传递的值是否为有限值.,", - }, - "isInteger": { - "!type": "fn(testValue: ?) -> bool", - "!doc": " Number.isInteger()方法确定传递的值是否为整数.,", - }, - "isNaN": { - "!type": "fn(testValue: ?) -> bool", - "!doc": " Number.isNaN()方法确定传递的值是否为NaN.原始全局isNaN()的更可靠的版本.,", - }, - "isSafeInteger": { - "!type": "fn(testValue: ?) -> bool", - "!doc": " Number.isSafeInteger()方法确定所提供的值是否是一个安全整数的数字.安全整数是该数字的整数.", - }, - "parseFloat": { - "!type": "fn(string: string) -> number", - "!doc": " Number.parseFloat()方法解析字符串参数并返回浮点数.,", - }, - "parseInt": { - "!type": "fn(string: string, radix?: number) -> number", - "!doc": " Number.parseInt()方法解析字符串参数并返回指定基数或基数的整数.,", - }, - }, - "Boolean": { - "!type": "fn(value: ?) -> bool", - "prototype": { - "!stdProto": "Boolean" - }, - }, - "abstract": "?", - "arguments": "?", - "boolean": "?", - "break": "?", - "byte": "?", - "case": "?", - "catch": "?", - "char": "?", - "const": "?", - "continue": "?", - "debugger": "?", - "default": "?", - "delete": "?", - "do": "?", - "double": "?", - "else": "?", - "eval": "?", - "false": "bool", - "final": "?", - "finally": "?", - "float": "?", - "for": "?", - "function": "?", - "goto": "?", - "if": "?", - "implements": "?", - "in": "?", - "instanceof": "?", - "int": "?", - "interface": "?", - "long": "?", - "native": "?", - "new": "?", - "null": "?", - "package": "?", - "private": "?", - "protected": "?", - "public": "?", - "return": "?", - "short": "?", - "static": "?", - "switch": "?", - "synchronized": "?", - "this": "?", - "throw": "?", - "throws": "?", - "transient": "?", - "true": "bool", - "try": "?", - "typeof": "?", - "var": "?", - "void": "?", - "volatile": "?", - "while": "?", - "with": "?", - "yield": "?", - "RegExp": { - "!type": "fn(source: string, flags?: string)", - "prototype": { - "!stdProto": "RegExp", - "exec": { - "!type": "fn(input: string) -> [string]", - "!doc": "搜索指定字符串中的匹配项.返回结果数组,或者为null.", - }, - "test": { - "!type": "fn(input: string) -> bool", - "!doc": "执行正则表达式和指定字符串之间的匹配搜索.返回true或false.", - } - }, - "!doc": "创建正则表达式对象以将文本与模式匹配.", - }, - "parseInt": { - "!type": "fn(string: string, radix?: number) -> number", - "!doc": "解析字符串参数并返回指定基数或基数的整数." - }, - "parseFloat": { - "!type": "fn(string: string) -> number", - "!doc": "解析字符串参数并返回浮点数." - }, - "isNaN": { - "!type": "fn(value: number) -> bool", - "!doc": "确定值是否为NaN.请注意,此函数已损坏.您可能对ECMAScript 6 Number.isNaN感兴趣.", - }, - "isFinite": { - "!type": "fn(value: number) -> bool", - "!doc": "确定传递的值是否为有限数字." - }, - "eval": { - "!type": "fn(code: string) -> ?", - "!doc": "评估以字符串形式表示的JavaScript代码." - }, - "encodeURI": { - "!type": "fn(uri: string) -> string", - "!doc": "通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)(对于字符而言将仅是四个转义序列由两个\"代理\"字符组成).", - }, - "encodeURIComponent": { - "!type": "fn(uri: string) -> string", - "!doc": "通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)组件(对于由两个\"代理\"字符组成的字符).", - }, - "decodeURI": { - "!type": "fn(uri: string) -> string", - "!doc": "解码以前由encodeURI或类似例程创建的统一资源标识符(URI).", - }, - "decodeURIComponent": { - "!type": "fn(uri: string) -> string", - "!doc": "解码以前由encodeURIComponent或类似例程创建的统一资源标识符(URI)组件.", - }, - "Math": { - "E": { - "!type": "number", - "!doc": "自然对数的底数,e约为2.718." - }, - "LN2": { - "!type": "number", - "!doc": " 2的自然对数,大约为0.693." - }, - "LN10": { - "!type": "number", - "!doc": " 10的自然对数,大约为2.302." - }, - "LOG2E": { - "!type": "number", - "!doc": " E的以2为底的对数(大约1.442).", - }, - "LOG10E": { - "!type": "number", - "!doc": " E的以10为底的对数(约0.434)." - }, - "SQRT1_2": { - "!type": "number", - "!doc": " 1/2的平方根;等效于2的平方根上的1,大约为0.707." - }, - "SQRT2": { - "!type": "number", - "!doc": " 2的平方根,大约为1.414." - }, - "PI": { - "!type": "number", - "!doc": "圆的周长与其直径之比,大约为3.14159." - }, - "abs": { - "!type": "fn(number) -> number", - "!doc": "返回数字的绝对值." - }, - "cos": { - "!type": "fn(number) -> number", - "!doc": "返回数字的余弦." - }, - "sin": { - "!type": "fn(number) -> number", - "!doc": "返回数字的正弦." - }, - "tan": { - "!type": "fn(number) -> number", - "!doc": "返回数字的正切值." - }, - "acos": { - "!type": "fn(number) -> number", - "!doc": "返回数字的反余弦(以弧度为单位)." - }, - "asin": { - "!type": "fn(number) -> number", - "!doc": "返回数字的反正弦(以弧度为单位)." - }, - "atan": { - "!type": "fn(number) -> number", - "!doc": "返回数字的反正切(以弧度为单位)." - }, - "atan2": { - "!type": "fn(y: number, x: number) -> number", - "!doc": "返回其参数商的反正切值." - }, - "ceil": { - "!type": "fn(number) -> number", - "!doc": "返回大于或等于数字的最小整数." - }, - "floor": { - "!type": "fn(number) -> number", - "!doc": "返回小于或等于数字的最大整数." - }, - "round": { - "!type": "fn(number) -> number", - "!doc": "返回四舍五入到最接近整数的数字的值." - }, - "exp": { - "!type": "fn(number) -> number", - "!doc": "返回E ^ x,其中x是自变量,E是欧拉常数,自然对数的底." - }, - "log": { - "!type": "fn(number) -> number", - "!doc": "返回数字的自然对数(以E为底).", - }, - "sqrt": { - "!type": "fn(number) -> number", - "!doc": "返回数字的平方根." - }, - "pow": { - "!type": "fn(number, number) -> number", - "!doc": "将基数返回指数幂,即baseexponent." - }, - "max": { - "!type": "fn(number, number) -> number", - "!doc": "返回零个或多个数字中的最大值." - }, - "min": { - "!type": "fn(number, number) -> number", - "!doc": "返回零个或多个数字中的最小值." - }, - "random": { - "!type": "fn() -> number", - "!doc": "返回一个浮点伪随机数,范围为[0,1),即从0(包括)到不包括1(排除),然后您可以缩放到所需的值范围." - }, - "log10": { - "!type": "fn(x: number) -> number", - "!doc": " Math.log10()函数返回数字的以10为底的对数.", - }, - "log2": { - "!type": "fn(x: number) -> number", - "!doc": " Math.log2()函数返回数字的以2为底的对数.", - }, - "sign": { - "!type": "fn(x: number) -> number", - "!doc": " Math.sign()函数返回数字的符号,指示数字是正数,负数还是零.,", - }, - "trunc": { - "!type": "fn(x: number) -> number", - "!doc": " Math.trunc()函数通过删除任何小数位来返回数字的整数部分.它不舍入任何数字.该函数可以用floor()和ceil()函数表示: ,", - }, - "!doc": "一个内置对象,具有用于数学常数和函数的属性和方法.", - }, - "JSON": { - "parse": { - "!type": "fn(json: string, reviver?: fn(key: string, value: ?) -> ?) -> ?", - "!doc": "将字符串解析为JSON,可以选择转换解析产生的值.", - }, - "stringify": { - "!type": "fn(value: ?, replacer?: fn(key: string, value: ?) -> ?, space?: string|number) -> string", - "!doc": "将值转换为JSON,如果指定了replacer函数,则可以选择替换值,如果指定了replacer数组,则可以选择仅包括指定的属性.", - }, - "!doc": " JSON(JavaScript对象表示法)是一种数据交换格式.尽管它不是严格的子集,但它非常类似于JavaScript语法的子集.(有关详细信息,请参见JavaScript参考中的JSON.)在编写任何类型的基于JavaScript的应用程序(包括网站和浏览器扩展程序)时非常有用.例如,您可以将JSON格式的用户信息存储在cookie中,或者可以将扩展名首选项以JSON形式存储在字符串值的浏览器首选项中." - } - }, - { - "!name": "core", - "!define": { - "image": { - "!doc": "图片信息", - "width": "number", - "height": "number", - "src": "string" - }, - "audio": { - "!doc": "音乐音效信息", - "currentTime": "number", - "play": "fn()", - "pause": "fn()", - "paused": "bool", - "duration": "number", - "volume": "number", - }, - "flag": { - "!doc": "当前变量", - "hard": { - "!type": "number", - "!doc": "当前难度编号" - }, - "hatred": { - "!type": "number", - "!doc": "当前仇恨值" - }, - "poison": { - "!type": "bool", - "!doc": "是否处于中毒状态" - }, - "weak": { - "!type": "number", - "!doc": "是否处于衰弱状态" - }, - "curse": { - "!type": "number", - "!doc": "是否处于诅咒状态" - }, - "no_zone": { - "!type": "bool", - "!doc": "无视领域伤害" - }, - "no_repulse": { - "!type": "bool", - "!doc": "无视阻击伤害" - }, - "no_lasel": { - "!type": "bool", - "!doc": "无视激光伤害" - }, - "no_ambush": { - "!type": "bool", - "!doc": "无视捕捉" - }, - "__bgm__": { - "!type": "string", - "!doc": "背景音乐" - }, - "__weather__": { - "!doc": "天气" - }, - "__color__": { - "!doc": "色调" - }, - "__volume__": { - "!type": "number", - "!doc": "音量" - }, - "skill": { - "!type": "number", - "!doc": "当前开启的技能编号" - }, - "skillName": { - "!type": "string", - "!doc": "当前开启的技能名" - }, - "input": { - "!type": "string|number", - "!doc": "等待用户输入后的存放值" - }, - "type": { - "!type": "number", - "!doc": "等待用户操作后获得的操作类型" - }, - "keycode": { - "!type": "number", - "!doc": "等待用户操作后用户按键的键值" - }, - "x": { - "!type": "number", - "!doc": "等待用户操作后用户点击的网格横坐标" - }, - "y": { - "!type": "number", - "!doc": "等待用户操作后用户点击的网格纵坐标" - }, - "px": { - "!type": "number", - "!doc": "等待用户操作后用户点击的像素横坐标" - }, - "py": { - "!type": "number", - "!doc": "等待用户操作后用户点击的像素纵坐标" - }, - "__visited__": { - "!doc": "当前访问过的楼层" - }, - "__leaveLoc__": { - "!doc": "每个楼层的离开位置,用于楼传平面塔模式" - }, - "cannotMoveDirectly": { - "!type": "bool", - "!doc": "当前是否全局不可瞬移" - }, - }, - "hero": { - "!doc": "勇士当前属性", - "image": { - "!type": "string", - "!doc": "行走图" - }, - "animate": { - "!type": "bool", - "!doc": "是否开启帧动画" - }, - "name": { - "!type": "string", - "!doc": "勇士名" - }, - "lv": { - "!type": "number", - "!doc": "勇士等级" - }, - "hpmax": { - "!type": "number", - "!doc": "勇士生命上限" - }, - "hp": { - "!type": "number", - "!doc": "勇士当前生命值" - }, - "atk": { - "!type": "number", - "!doc": "勇士当前攻击力" - }, - "def": { - "!type": "number", - "!doc": "勇士当前防御力" - }, - "manamax": { - "!type": "number", - "!doc": "勇士当前魔力上限,负数无效" - }, - "mana": { - "!type": "number", - "!doc": "勇士当前魔力值" - }, - "mdef": { - "!type": "number", - "!doc": "勇士当前护盾值" - }, - "money": { - "!type": "number", - "!doc": "勇士当前金币" - }, - "exp": { - "!type": "number", - "!doc": "勇士当前经验" - }, - "equipment": { - "!type": "[string]", - "!doc": "勇士当前装备" - }, - "items": { - "!doc": "勇士当前道具", - "constants": { - "!doc": "永久道具" - }, - "tools": { - "!doc": "消耗道具", - "yellowKey": { - "!type": "number", - "!doc": "黄钥匙个数" - }, - "blueKey": { - "!type": "number", - "!doc": "蓝钥匙个数" - }, - "redKey": { - "!type": "number", - "!doc": "红钥匙个数" - }, - "greenKey": { - "!type": "number", - "!doc": "绿钥匙个数" - }, - "steelKey": { - "!type": "number", - "!doc": "铁门钥匙个数" - }, - }, - "equips": { - "!doc": "在背包中未装备上的装备" - }, - }, - "loc": { - "!doc": "勇士当前坐标和朝向", - "x": "number", - "y": "number", - "direction": { - "!doc": "朝向,只能为 up,down,left,right 之一", - "!type": "string" - }, - }, - "flags": { - "!type": "flag", - "!doc": "当前游戏中用到的变量" - }, - "followers": { - "!type": "[?]", - "!doc": "跟随者信息" - }, - "steps": { - "!type": "number", - "!doc": "当前步数" - } - }, - "block": { - "!doc": "地图图块信息", - "x": { - "!type": "number", - "!doc": "图块的x坐标" - }, - "y": { - "!type": "number", - "!doc": "图块的y坐标" - }, - "id": { - "!type": "number", - "!doc": "图块的数字" - }, - "event": { - "!doc": "图块上的事件信息", - "id": { - "!type": "string", - "!doc": "图块的ID" - }, - "cls": { - "!type": "string", - "!doc": "图块的类别,一般为所在图片名去掉后缀" - }, - "disabled": { - "!type": "bool", - "!doc": "启用状态" - } - } - }, - "blockInfo": { - "!doc": "图块的更多信息", - "animate": { - "!type": "number", - "!doc": "动画帧数" - }, - "cls": { - "!type": "string", - "!doc": "图块类别" - }, - "faceIds": { - "!doc": "行走图朝向", - "up": "string", - "down": "string", - "left": "string", - "right": "string" - }, - "height": { - "!type": "number", - "!doc": "图块高度" - }, - "id": { - "!type": "string", - "!doc": "图块ID" - }, - "image": { - "!type": "image", - "!doc": "图块所在的图片" - }, - "name": { - "!type": "string", - "!doc": "图块名称" - }, - "number": { - "!type": "number", - "!doc": "图块使用的数字" - }, - "posX": { - "!type": "number", - "!doc": "图块在图片上的横坐标" - }, - "posY": { - "!type": "number", - "!doc": "图块在图片上的纵坐标" - }, - }, - "enemy": { - "!doc": "怪物信息", - "id": { - "!type": "string", - "!doc": "怪物ID" - }, - "name": { - "!type": "string", - "!doc": "怪物名称" - }, - "displayIdInBook": { - "!type": "string", - "!doc": "在怪物手册映射ID" - }, - "hp": { - "!type": "number", - "!doc": "怪物生命值" - }, - "atk": { - "!type": "number", - "!doc": "怪物攻击" - }, - "def": { - "!type": "number", - "!doc": "怪物防御" - }, - "money": { - "!type": "number", - "!doc": "怪物金币" - }, - "exp": { - "!type": "number", - "!doc": "怪物经验" - }, - "special": { - "!type": "[number]", - "!doc": "怪物特殊属性" - }, - "point": { - "!type": "number", - "!doc": "怪物加点" - }, - "value": { - "!type": "number", - "!doc": "怪物特殊属性值:阻激夹域伤害值;吸血比例;光环增加生命比例" - }, - "zoneSquare": { - "!type": "bool", - "!doc": "领域怪是否九宫格伤害;区域光环是否九宫格范围" - }, - "range": { - "!type": "number", - "!doc": "领域伤害的范围;区域光环范围" - }, - "notBomb": { - "!type": "bool", - "!doc": "怪物不可炸" - }, - "n": { - "!type": "number", - "!doc": "多连击的连击数;净化比例" - }, - "add": { - "!type": "bool", - "!doc": "吸血是否加到自身;光环是否叠加" - }, - "atkValue": { - "!type": "number", - "!doc": "反击比例;退化扣除攻击;光环增加攻击;" - }, - "defValue": { - "!type": "number", - "!doc": "破甲比例;退化扣除防御;光环增加防御" - }, - "damage": { - "!type": "number", - "!doc": "固伤值" - }, - }, - "item": { - "!doc": "道具信息", - "id": { - "!type": "string", - "!doc": "道具ID" - }, - "cls": { - "!type": "string", - "!doc": "道具类型" - }, - "name": { - "!type": "string", - "!doc": "道具名称" - }, - "text": { - "!type": "string", - "!doc": "道具描述" - }, - "hideInToolbox": { - "!type": "bool", - "!doc": "不显示在道具栏" - }, - "equip": { - "!doc": "装备属性", - "type": { - "!type": "number|string", - "!doc": "装备类型" - }, - "animate": { - "!type": "string", - "!doc": "装备动画" - }, - "value": { - "!doc": "数值加成" - }, - "percentage": { - "!doc": "比例加成" - } - }, - "hideInReplay": { - "!type": "bool", - "!doc": "回放不绘制道具栏" - }, - }, - "floor": { - "!doc": "楼层信息", - "floorId": { - "!type": "string", - "!doc": "楼层ID" - }, - "title": { - "!type": "string", - "!doc": "楼层中文名" - }, - "name": { - "!type": "string", - "!doc": "状态栏显示值" - }, - "width": { - "!type": "number", - "!doc": "地图宽" - }, - "height": { - "!type": "number", - "!doc": "地图高" - }, - "canFlyTo": { - "!type": "bool", - "!doc": "该楼是否可以楼传飞到" - }, - "canFlyFrom": { - "!type": "bool", - "!doc": "该楼是否可以楼传飞出" - }, - "canUseQuickShop": { - "!type": "bool", - "!doc": "该楼是否可快捷商店" - }, - "cannotViewMap": { - "!type": "bool", - "!doc": "该层是否不允许被浏览地图看到,也不统计" - }, - "cannotMoveDirectly": { - "!type": "bool", - "!doc": "该层是否不允许瞬间移动" - }, - "upFloor": { - "!type": "[number]", - "!doc": "上楼点" - }, - "downFloor": { - "!type": "[number]", - "!doc": "下楼点" - }, - "flyPoint": { - "!type": "[number]", - "!doc": "楼传落点" - }, - "color": { - "!doc": "楼层色调" - }, - "weather": { - "!doc": "楼层天气" - }, - "bgm": { - "!type": "string", - "!doc": "楼层背景音乐" - }, - "ratio": { - "!type": "number", - "!doc": "宝石/血瓶效果" - }, - "map": { - "!type": "[[number]]", - "!doc": "地图数据" - }, - "blocks": { - "!type": "[block]", - "!doc": "本层图块信息" - } - }, - "animate": { - "!doc": "动画信息", - "se": { - "!type": "string", - "!doc": "动画音效" - } - } - }, - "core": { - "!doc": "核心游戏控制", - "__SIZE__": { - "!type": "number", - "!doc": "窗口宽度,为13或15" - }, - "__PIXELS__": { - "!type": "number", - "!doc": "窗口像素宽度,为416或480" - }, - "__HALF_SIZE__": { - "!type": "number", - "!doc": "窗口宽度的一半,为6或7" - }, - "floorIds": { - "!type": "[string]", - "!doc": "全部楼层ID列表" - }, - "floors": { - "!doc": "全部楼层信息" - }, - "floorPartitions": { - "!type": "[[string]]", - "!doc": "楼层分区信息" - }, - "material": { - "!doc": "游戏所用到的资源", - "animates": { - "!doc": "注册的动画" - }, - "images": { - "!doc": "注册的图片" - }, - "bgms": { - "!doc": "注册的背景音乐" - }, - "sounds": { - "!doc": "注册的音效" - }, - "enemys": { - "!doc": "怪物定义", - }, - "items": { - "!doc": "道具定义" - } - }, - "timeout": { - "!doc": "当前异步事件句柄" - }, - "interval": { - "!doc": "当前异步事件延时" - }, - "animateFrame": { - "!doc": "当前各个帧动画" - }, - "musicStatus": { - "!doc": "音乐音效状态", - "bgmStatus": { - "!type": "bool", - "!doc": "是否播放BGM" - }, - "soundStatus": { - "!type": "bool", - "!doc": "是否播放SE" - }, - "playingBgm": { - "!type": "string", - "!doc": "正在播放的bgm" - }, - "lastBgm": { - "!type": "string", - "!doc": "上次播放的bgm" - }, - "playingSounds": { - "!doc": "正在播放的SE" - }, - "volume": { - "!type": "number", - "!doc": "当前bgm音量" - } - }, - "platform": { - "!doc": "平台信息", - "isPC": "bool", - "isAndroid": "bool", - "isIOS": "bool", - }, - "domStyle": { - "!doc": "界面样式", - "scale": { - "!type": "number", - "!doc": "当前界面放缩比例", - }, - "ratio": { - "!type": "number", - "!doc": "高清UI放缩比例" - }, - "hdCanvas": { - "!type": "[string]", - "!doc": "高清UI的系统画布" - }, - "availableScale": { - "!type": "[number]", - "!doc": "当前界面支持的放缩比例" - }, - "isVertical": { - "!type": "bool", - "!doc": "当前是否是竖屏" - }, - "showStatusBar": { - "!type": "bool", - "!doc": "当前是否显示状态栏" - }, - "toolbarBtn": { - "!type": "bool", - "!doc": "当前工具栏是否是1-8的按钮" - }, - }, - "bigmap": { - "!doc": "大地图信息", - "canvas": { - "!type": "[string]", - "!doc": "大地图的画布" - }, - "width": { - "!type": "number", - "!doc": "大地图高度" - }, - "height": { - "!type": "number", - "!doc": "大地图宽度" - }, - "offsetX": { - "!type": "number", - "!doc": "大地图视角横向偏移量" - }, - "offsetY": { - "!type": "number", - "!doc": "大地图视角纵向偏移量" - }, - "posX": { - "!type": "number", - "!doc": "大地图视角横向基准格" - }, - "posY": { - "!type": "number", - "!doc": "大地图视角纵向基准格" - }, - "v2": { - "!type": "bool", - "!doc": "是否是新版大地图绘制方式" - }, - "threshold": { - "!type": "number", - "!doc": "新版大地图绘制方式的分界线" - }, - "extend": { - "!type": "number", - "!doc": "新版大地图模式下向每一侧额外计算的数量" - }, - "scale": { - "!type": "number", - "!doc": "缩略图的比例放缩" - }, - "tempCanvas": { - "!type": "CanvasRenderingContext2D", - "!doc": "临时画布" - } - }, - "saves": { - "!doc": "当前存档信息" - }, - "dymCanvas": { - "!doc": "各个自定义画布" - }, - "statusBar": { - "!doc": "状态栏信息" - }, - "canvas": { - "!doc": "系统画布" - }, - "flags": { - "!doc": "系统开关" - }, - "values": { - "!doc": "全局数值,如毒衰效果" - }, - "firstData": { - "!doc": "初始属性,如出生点" - }, - "status": { - "!doc": "状态信息", - "hero": { - "!type": "hero", - "!doc": "勇士信息" - }, - "automaticRoute": { - "!doc": "自动寻路信息" - }, - "bgmaps": { - "!doc": "各地图背景层" - }, - "fgmaps": { - "!doc": "各地图前景层" - }, - "mapBlockObjs": { - "!doc": "以<位置,block>存放的各地图图块信息" - }, - "boxAnimateObjs": { - "!doc": "(手册和剧情文本的)帧动画对象" - }, - "checkBlock": { - "!doc": "阻激夹域捕捉信息", - "damage": { - "!doc": "每个点的伤害信息" - }, - "type": { - "!doc": "每个点的伤害类型" - }, - "repluse": { - "!doc": "每个点的阻击信息" - }, - "ambush": { - "!doc": "每个点的捕捉信息" - }, - "needCache": { - "!type": "bool", - "!doc": "该楼层是否需要计算缓存" - }, - "cache": { - "!doc": "每个点的光环缓存" - }, - }, - "damage": { - "!doc": "每个点的显伤信息", - }, - "ctrlDown": { - "!type": "bool", - "!doc": "Ctrl键是否被按下" - }, - "curtainColor": { - "!doc": "当前画面色调" - }, - "event": { - "!doc": "当前事件", - "data": { - "!doc": "事件信息,如坐标等" - }, - "id": { - "!type": "string", - "!doc": "事件类型,如选择项/确认框" - }, - "interval": { - "!type": "number", - "!doc": "打字机效果的定时器" - }, - "selection": { - "!type": "number", - "!doc": "选择项和确认框的当前选中项" - }, - "ui": { - "!doc": "当前事件的界面信息,如楼传/手册/SL" - } - }, - "floorAnimateObjs": { - "!doc": "楼层贴图的帧动画" - }, - "floorId": { - "!type": "string", - "!doc": "当前楼层ID" - }, - "gameOver": { - "!type": "bool", - "!doc": "游戏是否已结束" - }, - "globalAnimateObjs": { - "!doc": "各全局动画" - }, - "globalAnimateStatus": { - "!type": "number", - "!doc": "全局动画的帧状态" - }, - "globalAttribute": { - "!doc": "全局css属性" - }, - "hard": { - "!type": "string", - "!doc": "状态栏一角的难度名" - }, - "downTime": { - "!type": "number", - "!doc": "方向键已按下的时间" - }, - "heroCenter": { - "!doc": "勇士中心像素坐标", - "px": { - "!type": "number", - "!doc": "勇士中心的横坐标" - }, - "py": { - "!type": "number", - "!doc": "勇士中心的纵坐标" - }, - }, - "heroMoving": { - "!type": "number", - "!doc": "勇士行走的状态值" - }, - "heroStop": { - "!type": "bool", - "!doc": "勇士是否已停下" - }, - "holdingKeys": { - "!type": "[number]", - "!doc": "当前按下的键" - }, - "id2number": { - "!doc": "图块ID到数字的对应关系" - }, - "lockControl": { - "!type": "bool", - "!doc": "当前是否是锁定操作状态" - }, - "maps": { - "!doc": "当前各地图信息" - }, - "number2Block": { - "!doc": "数字到图块对象的对应关系" - }, - "played": { - "!type": "bool", - "!doc": "当前是否游戏中(不包括标题画面和录像回放)" - }, - "replay": { - "!doc": "当前录像回放信息", - "animate": { - "!type": "bool", - "!doc": "回放是否正处于动画中" - }, - "pausing": { - "!type": "bool", - "!doc": "回放是否暂停中" - }, - "replaying": { - "!type": "bool", - "!doc": "当前是否回放中" - }, - "save": { - "!type": "[]", - "!doc": "录像中的存档" - }, - "speed": { - "!type": "number", - "!doc": "回放速度" - }, - "steps": { - "!type": "number", - "!doc": "回放步数" - }, - "toReplay": { - "!type": "[string]", - "!doc": "待回放的列表" - }, - "totalList": { - "!type": "[string]", - "!doc": "回放总列表" - } - }, - "route": { - "!type": "[string]", - "!doc": "当前录像内容" - }, - "shops": { - "!doc": "全局商店列表" - }, - "textAttribute": { - "!doc": "当前剧情文本属性" - }, - "thisMap": { - "!type": "floor", - "!doc": "当前地图信息" - } - }, - "init": { - "!doc": "初始化core", - "!type": "fn(coreData: ?, callback: fn())" - }, - "doFunc": { - "!doc": "执行一个函数;如果函数名是字符串则转发到插件中", - "!type": "fn(func: name|fn(), _this?: ?)" - }, - "control": { - "!doc": "负责整个游戏的核心控制系统,分为如下几个部分:
- requestAnimationFrame相关
- 标题界面,开始和重新开始游戏
- 自动寻路和人物行走相关
- 画布、位置、阻激夹域、显伤等相关
- 录像的回放相关
- 存读档,自动存档,同步存档等相关
- 人物属性和状态、位置、变量等相关
- 天气、色调、音乐和音效的播放
- 状态栏和工具栏相关
- 界面resize相关", - "showStatusBar": { - "!doc": "显示状态栏", - "!type": "fn()" - }, - "startReplay": { - "!doc": "开始播放录像", - "!type": "fn(list: [string])" - }, - "triggerReplay": { - "!doc": "播放或暂停录像回放", - "!type": "fn()" - }, - "screenFlash": { - "!doc": "画面闪烁
例如:core.screenFlash([255, 0, 0, 1], 3); // 红屏一闪而过
color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若填负数则会被视为0)的颜色数组,必填
time: 单次闪烁时长,实际闪烁效果为先花其三分之一的时间渐变到目标色调,再花剩余三分之二的时间渐变回去
times: 闪烁的总次数,不填或填0都视为1
moveMode: 渐变方式
callback: 闪烁全部完毕后的回调函数,可选", - "!type": "fn(color: [number], time: number, times?: number, moveMode?: string, callback?: fn())" - }, - "setCurtain": { - "!doc": "更改画面色调,不计入存档。如需长期生效请使用core.events._action_setCurtain()函数
例如:core.setCurtain(); // 恢复画面色调,用时四分之三秒
color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若为负数则会被视为0)的颜色数组,不填视为[0, 0, 0, 0]
time: 渐变时间,单位为毫秒。不填视为750ms,负数视为0(无渐变,立即更改)
moveMode: 渐变方式
callback: 更改完毕后的回调函数,可选。事件流中常取core.doAction", - "!type": "fn(color?: [number], time?: number, moveMode?: string, callback?: fn())" - }, - "updateDamage": { - "!doc": "注意!请勿使用该函数!请使用core.updateStatusBar()代替!!重算并绘制地图显伤
例如:core.updateDamage(); // 更新当前地图的显伤,绘制在显伤层(废话)
floorId: 地图id,不填视为当前地图。预览地图时填写
ctx: 绘制到的画布,如果填写了就会画在该画布而不是显伤层", - "!type": "fn(floorId?: string, ctx?: string|CanvasRenderingContext2D)" - }, - "drawDamage": { - "!doc": "仅绘制地图显伤", - "!type": "fn(string|CanvasRenderingContext2D)" - }, - "nextX": { - "!doc": "获取主角面前第n格的横坐标
例如:core.closeDoor(core.nextX(), core.nextY(), 'yellowDoor', core.turnHero); // 在主角面前关上一扇黄门,然后主角顺时针旋转90°
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1", - "!type": "fn(n?: number) -> number" - }, - "nextY": { - "!doc": "获取主角面前第n格的纵坐标
例如:core.jumpHero(core.nextX(2), core.nextY(2)); // 主角向前跃过一格,即跳跃靴道具的使用效果
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1", - "!type": "fn(n?: number) -> number" - }, - "clearContinueAutomaticRoute": { - "!doc": "清空剩下的自动寻路列表", - "!type": "fn(callback?: fn())" - }, - "updateViewport": { - "!doc": "更新大地图的可见区域", - "!type": "fn()" - }, - "getMappedName": { - "!doc": "获得映射文件名", - "!type": "fn(name: string) -> string" - }, - "addFlag": { - "!doc": "增减一个flag变量,等价于 core.setFlag(name, core.getFlag(name, 0) + value)
例如:core.addFlag('hatred', 1); // 增加1点仇恨值
name: 变量名,支持中文
value: 变量的增量", - "!type": "fn(name: string, value: number)" - }, - "setFlag": { - "!doc": "设置一个flag变量
例如:core.setFlag('poison', true); // 令主角中毒
name: 变量名,支持中文
value: 变量的新值,不填或填null视为删除", - "!type": "fn(name: string, value: ?)" - }, - "playSound": { - "!doc": "播放一个音效
sound: 音效名;可以使用文件别名。
pitch: 播放的音调;可选,如果设置则为30-300之间的数值。
callback: 可选,播放完毕后执行的回调函数。
返回:一个数字,可用于core.stopSound的参数来只停止该音效。", - "!type": "fn(sound: string, pitch?: number, callback?: fn()) -> number" - }, - "stopSound": { - "!doc": "停止播放音效。如果未指定id则停止所有音效,否则只停止指定的音效。", - "!type": "fn(id?: number)" - }, - "getPlayingSounds": { - "!doc": "获得当前正在播放的所有(指定)音效的id列表
name: 音效名,可用别名;不填代表返回正在播放的全部音效
返回值: 一个列表,每一项为一个正在播放的音效id;可用core.stopSound立刻停止播放", - "!type": "fn(name?: string) -> [number]" - }, - "addGameCanvasTranslate": { - "!doc": "加减画布偏移", - "!type": "fn(x?: number, y?: number)" - }, - "addBuff": { - "!doc": "增减主角某个属性的百分比修正倍率,加减法叠加和抵消。等价于 core.setBuff(name, core.getBuff(name) + value)
例如:core.addBuff('atk', -0.1); // 主角获得一层“攻击力减一成”的负面效果
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 倍率的增量", - "!type": "fn(name: string, value: number)" - }, - "drawHero": { - "!doc": "绘制主角和跟随者并重置视野到以主角为中心
例如:core.drawHero(); // 原地绘制主角的静止帧并重置视野野
status: 只能为 stop, leftFoot 和 rightFoot,不填用stop。
offset: 相对主角逻辑位置的偏移量,不填视为无偏移。
frame: 绘制的第几帧", - "!type": "fn(status?: string, offset?: number, frame?: number)" - }, - "pauseBgm": { - "!doc": "暂停背景音乐的播放", - "!type": "fn()" - }, - "setBgmSpeed": { - "!doc": "设置背景音乐的播放速度和音调
speed: 播放速度,必须为30-300中间的值。100为正常速度。
usePitch: 是否同时改变音调(部分设备可能不支持)", - "!type": "fn(speed: number, usePitch?: bool)" - }, - "setReplaySpeed": { - "!doc": "设置播放速度", - "!type": "fn(speed: number)" - }, - "pauseReplay": { - "!doc": "暂停播放", - "!type": "fn()" - }, - "doSL": { - "!doc": "实际进行存读档事件", - "!type": "fn(id?: string, type?: string)" - }, - "setStatus": { - "!doc": "设置主角的某个属性
例如:core.setStatus('atk', 100); // 设置攻击力为100
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.setHeroLoc(name, value),其他的会直接对 core.status.hero[name] 赋值
value: 属性的新值", - "!type": "fn(name: string, value: number)" - }, - "setAutomaticRoute": { - "!doc": "半自动寻路,用于鼠标或手指拖动
例如:core.setAutomaticRoute(0, 0, [{direction: \"right\", x: 4, y: 9}, {direction: \"right\", x: 5, y: 9}]);
destX: 鼠标或手指的起拖点横坐标
destY: 鼠标或手指的起拖点纵坐标
stepPostfix: 拖动轨迹的数组表示,每项为一步的方向和目标点。", - "!type": "fn(destX: number, destY: number, stepPostfix: [{x: number, y: number, direction: string}])" - }, - "setHeroOpacity": { - "!doc": "改变勇士的不透明度", - "!type": "fn(opacity?: number, moveMode?: string, time?: number, callback?: fn())" - }, - "gatherFollowers": { - "!doc": "立刻聚集所有的跟随者", - "!type": "fn()" - }, - "getStatus": { - "!doc": "读取主角的某个属性,不包括百分比修正
例如:core.getStatus('atk'); // 读取主角的攻击力
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.getHeroLoc(name),其他的会直接读取 core.status.hero[name]", - "!type": "fn(name: string) -> number" - }, - "setHeroLoc": { - "!doc": "设置勇士位置
值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;
如需立刻重新绘制地图还需调用:core.clearMap('hero'); core.drawHero(); 来对界面进行更新。
例如:core.setHeroLoc('x', 5) // 将勇士当前位置的横坐标设置为5。
name: 要设置的坐标属性
value: 新值
noGather: 是否聚集跟随者", - "!type": "fn(name: string, value: string|number, noGather?: bool)" - }, - "getLvName": { - "!doc": "根据级别的数字获取对应的名称,后者定义在全塔属性
例如:core.getLvName(); // 获取主角当前级别的名称,如“下级佣兵”
lv: 级别的数字,不填则视为主角当前的级别
返回值:级别的名称,如果不存在就还是返回数字", - "!type": "fn(lv?: number) -> string|number" - }, - "getNextLvUpNeed": { - "!doc": "获得下次升级需要的经验值。
升级扣除模式下会返回经验差值;非扣除模式下会返回总共需要的经验值。
如果无法进行下次升级,返回null。", - "!type": "fn() -> number" - }, - "addStatus": { - "!doc": "增减主角的某个属性,等价于core.setStatus(name, core.getStatus(name) + value)
例如:core.addStatus('atk', 100'); // 给主角攻击力加100
name: 属性的英文名
value: 属性的增量", - "!type": "fn(name: string, value: number)" - }, - "speedUpReplay": { - "!doc": "加速播放", - "!type": "fn()" - }, - "loadData": { - "!doc": "从本地读档", - "!type": "fn(data?: ?, callback?: fn())" - }, - "debug": { - "!doc": "开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。
此模式下不可回放录像和上传成绩。", - "!type": "fn()" - }, - "moveOneStep": { - "!doc": "每移动一格后执行的事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(callback?: fn())" - }, - "clearStatus": { - "!doc": "清除游戏状态和数据", - "!type": "fn()" - }, - "updateFollowers": { - "!doc": "更新跟随者坐标", - "!type": "fn()" - }, - "waitHeroToStop": { - "!doc": "等待主角停下
例如:core.waitHeroToStop(core.vibrate); // 等待主角停下,然后视野左右抖动1秒
callback: 主角停止后的回调函数", - "!type": "fn(callback?: fn())" - }, - "hideStatusBar": { - "!doc": "隐藏状态栏
showToolbox: 是否不隐藏竖屏工具栏", - "!type": "fn(showToolbox?: bool)" - }, - "getBuff": { - "!doc": "读取主角某个属性的百分比修正倍率,初始值为1
例如:core.getBuff('atk'); // 主角当前能发挥出多大比例的攻击力
name: 属性的英文名", - "!type": "fn(name: string) -> number" - }, - "triggerDebuff": { - "!doc": "获得或移除毒衰咒效果
action: 要获得还是移除,'get'为获得,'remove'为移除
type: 获得或移除的内容(poison/weak/curse),可以为字符串或数组", - "!type": "fn(action: string, type: string|[string])" - }, - "setToolbarButton": { - "!doc": "改变工具栏为按钮1-8", - "!type": "fn(useButton?: bool)" - }, - "getSaves": { - "!doc": "获得某些存档内容", - "!type": "fn(ids?: ?, callback?: fn())" - }, - "replay": { - "!doc": "回放下一个操作", - "!type": "fn()" - }, - "getStatusOrDefault": { - "!doc": "从status中获得属性,如果不存在则从勇士属性中获取", - "!type": "fn(status?: ?, name?: string)" - }, - "unregisterReplayAction": { - "!doc": "注销一个录像行为", - "!type": "fn(name: string)" - }, - "unregisterWeather": { - "!doc": "注销一个天气", - "!type": "fn(name: string)" - }, - "setBuff": { - "!doc": "设置主角某个属性的百分比修正倍率,初始值为1,
倍率存放在flag: '__'+name+'_buff__' 中
例如:core.setBuff('atk', 0.5); // 主角能发挥出的攻击力减半
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 新的百分比修正倍率,不填(效果上)视为1", - "!type": "fn(name: string, value: number)" - }, - "continueAutomaticRoute": { - "!doc": "继续剩下的自动寻路操作", - "!type": "fn()" - }, - "setAutoHeroMove": { - "!doc": "连续行走
例如:core.setAutoHeroMove([{direction: \"up\", step: 1}, {direction: \"left\", step: 3}]); // 上左左左
steps: 压缩的步伐数组,每项表示朝某方向走多少步", - "!type": "fn(steps: [?])" - }, - "unregisterResize": { - "!doc": "注销一个resize函数", - "!type": "fn(name: string)" - }, - "saveAndStopAutomaticRoute": { - "!doc": "保存剩下的寻路,并停止", - "!type": "fn()" - }, - "hideStartAnimate": { - "!doc": "淡出标题画面
例如:core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择
callback: 标题画面完全淡出后的回调函数", - "!type": "fn(callback?: fn())" - }, - "getAllSaves": { - "!doc": "获得所有存档内容", - "!type": "fn(callback?: fn())" - }, - "updateHeroIcon": { - "!doc": "更新状态栏的勇士图标", - "!type": "fn(name: string)" - }, - "setMusicBtn": { - "!doc": "设置音乐图标的显隐状态", - "!type": "fn()" - }, - "isPlaying": { - "!doc": "游戏是否已经开始", - "!type": "fn() -> bool" - }, - "triggerBgm": { - "!doc": "开启或关闭背景音乐的播放", - "!type": "fn()" - }, - "moveHero": { - "!doc": "连续前进,不撞南墙不回头
例如:core.moveHero(); // 连续前进
direction: 可选,如果设置了就会先转身到该方向
callback: 可选,如果设置了就只走一步
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(direction?: string, callback?: fn())" - }, - "getRealStatusOrDefault": { - "!doc": "从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取", - "!type": "fn(status?: ?, name?: string)" - }, - "getStatusLabel": { - "!doc": "获得某个状态的名字,如atk->攻击,def->防御等", - "!type": "fn(name: string) -> string" - }, - "removeSave": { - "!doc": "删除某个存档", - "!type": "fn(index?: number, callback?: fn())" - }, - "registerAnimationFrame": { - "!doc": "注册一个 animationFrame
name: 名称,可用来作为注销使用
needPlaying: 是否只在游戏运行时才执行(在标题界面不执行)
func: 要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数", - "!type": "fn(name: string, needPlaying: bool, func?: fn(timestamp: number))" - }, - "getHeroLoc": { - "!doc": "读取主角的位置和/或朝向
例如:core.getHeroLoc(); // 读取主角的位置和朝向
name: 要读取横坐标还是纵坐标还是朝向还是都读取
返回值:name ? core.status.hero.loc[name] : core.status.hero.loc", - "!type": "fn(name: string) -> string|number" - }, - "stopAutomaticRoute": { - "!doc": "停止自动寻路操作", - "!type": "fn()" - }, - "setWeather": { - "!doc": "设置天气,不计入存档。如需长期生效请使用core.events._action_setWeather()函数
例如:core.setWeather('fog', 10); // 设置十级大雾天
type: 新天气的类型,不填视为晴天
level: 新天气(晴天除外)的级别,必须为不大于10的正整数,不填视为5", - "!type": "fn(type?: string, level?: number)" - }, - "updateStatusBar": { - "!doc": "刷新状态栏和地图显伤
doNotCheckAutoEvents: 是否不检查自动事件", - "!type": "fn(doNotCheckAutoEvents?: bool, immediate?: bool)" - }, - "autosave": { - "!doc": "自动存档", - "!type": "fn(removeLast?: bool)" - }, - "clearStatusBar": { - "!doc": "清空状态栏", - "!type": "fn()" - }, - "moveAction": { - "!doc": "尝试前进一步,如果面前不可被踏入就会直接触发该点事件
请勿直接使用此函数,如有需要请使用「勇士前进一步或撞击」事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(callback?: fn())" - }, - "hasFlag": { - "!doc": "判定一个flag变量是否存在且不为false、0、''、null、undefined和NaN
例如:core.hasFlag('poison'); // 判断主角当前是否中毒
name: 变量名,支持中文
此函数等价于 !!core.getFlag(name)", - "!type": "fn(name: string) -> bool" - }, - "rewindReplay": { - "!doc": "回退到上一个录像节点", - "!type": "fn()" - }, - "playBgm": { - "!doc": "播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__
例如:core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟
bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名
startTime: 跳过前多少秒,不填则不跳过", - "!type": "fn(bgm: string, startTime?: number)" - }, - "isReplaying": { - "!doc": "是否正在播放录像", - "!type": "fn() -> bool" - }, - "isMoving": { - "!doc": "当前是否正在移动", - "!type": "fn() -> bool" - }, - "getSaveIndexes": { - "!doc": "获得所有存在存档的存档位", - "!type": "fn(callback?: fn())" - }, - "unlockControl": { - "!doc": "解锁用户控制行为", - "!type": "fn()" - }, - "syncSave": { - "!doc": "同步存档到服务器", - "!type": "fn(type?: string)" - }, - "removeFlag": { - "!doc": "删除某个flag/变量", - "!type": "fn(name: string)" - }, - "registerResize": { - "!doc": "注册一个resize函数
name: 名称,可供注销使用
func: 可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。", - "!type": "fn(name: string, func: fn(obj: ?))" - }, - "registerWeather": { - "!doc": "注册一个天气
name: 要注册的天气名
initFunc: 当切换到此天气时的初始化;接受level(天气等级)为参数;可用于创建多个节点(如初始化雪花)
frameFunc: 每帧的天气效果变化;可接受timestamp(从页面加载完毕到当前所经过的时间)和level(天气等级)作为参数
天气应当仅在weather层进行绘制,推荐使用core.animateFrame.weather.nodes用于节点信息。", - "!type": "fn(name: string, initFunc: fn(level: number), frameFunc?: fn(timestamp: number, level: number))" - }, - "stopReplay": { - "!doc": "停止播放", - "!type": "fn(force?: bool)" - }, - "turnHero": { - "!doc": "主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
例如:core.turnHero(); // 主角顺时针旋转90°,即单击主角或按下Z键的效果
direction: 主角的新朝向,可为 up, down, left, right, :left, :right, :back 七种之一", - "!type": "fn(direction?: string)" - }, - "resumeReplay": { - "!doc": "恢复播放", - "!type": "fn()" - }, - "resize": { - "!doc": "屏幕分辨率改变后重新自适应", - "!type": "fn()" - }, - "getSave": { - "!doc": "获得某个存档内容", - "!type": "fn(index?: number, callback?: fn(data: ?))" - }, - "setViewport": { - "!doc": "设置视野范围
px,py: 左上角相对大地图的像素坐标,不需要为32倍数", - "!type": "fn(px?: number, py?: number)" - }, - "chooseReplayFile": { - "!doc": "选择录像文件", - "!type": "fn()" - }, - "lockControl": { - "!doc": "锁定用户控制,常常用于事件处理", - "!type": "fn()" - }, - "updateCheckBlock": { - "!doc": "更新领域、夹击、阻击的伤害地图", - "!type": "fn(floorId?: string)" - }, - "checkBlock": { - "!doc": "检查并执行领域、夹击、阻击事件", - "!type": "fn()" - }, - "clearAutomaticRouteNode": { - "!doc": "清除自动寻路路线", - "!type": "fn(x?: number, y?: number)" - }, - "getFlag": { - "!doc": "读取一个flag变量
name: 变量名,支持中文
defaultValue: 当变量不存在时的返回值,可选(事件流中默认填0)。", - "!type": "fn(name: string, defaultValue?: ?)" - }, - "getNakedStatus": { - "!doc": "获得勇士原始属性(无装备和衰弱影响)", - "!type": "fn(name: string)" - }, - "nearHero": { - "!doc": "判定主角是否身处某个点的锯齿领域(取曼哈顿距离)
例如:core.nearHero(6, 6, 6); // 判定主角是否身处点(6,6)的半径为6的锯齿领域
x: 领域的中心横坐标
y: 领域的中心纵坐标
n: 领域的半径,不填视为1", - "!type": "fn(x: number, y: number, n?: number) -> bool" - }, - "stepReplay": { - "!doc": "单步播放", - "!type": "fn()" - }, - "hasSave": { - "!doc": "判断某个存档位是否存在存档", - "!type": "fn(index?: number) -> bool" - }, - "showStartAnimate": { - "!doc": "进入标题画面
例如:core.showStartAnimate(); // 重启游戏但不重置bgm
noAnimate: 可选,true表示不由黑屏淡入而是立即亮屏
callback: 可选,完全亮屏后的回调函数", - "!type": "fn(noAnimate?: bool, callback?: fn())" - }, - "moveViewport": { - "!doc": "移动视野范围", - "!type": "fn(x: number, y: number, moveMode?: string, time?: number, callback?: fn())" - }, - "syncLoad": { - "!doc": "从服务器加载存档", - "!type": "fn()" - }, - "setHeroMoveInterval": { - "!doc": "设置行走的效果动画", - "!type": "fn(callback?: fn())" - }, - "registerReplayAction": { - "!doc": "注册一个录像行为
name: 自定义名称,可用于注销使用
func: 具体执行录像的函数,可为一个函数或插件中的函数名;
需要接受一个action参数,代表录像回放时的下一个操作
func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。", - "!type": "fn(name: string, func: fn(action?: string) -> bool)" - }, - "checkAutosave": { - "!doc": "实际将自动存档写入存储", - "!type": "fn()" - }, - "resumeBgm": { - "!doc": "恢复背景音乐的播放
resumeTime: 从哪一秒开始恢复播放", - "!type": "fn(resumeTime?: number)" - }, - "setGameCanvasTranslate": { - "!doc": "设置大地图的偏移量", - "!type": "fn(ctx: string|CanvasRenderingContext2D, x: number, y: number)" - }, - "checkBgm": { - "!doc": "检查bgm状态", - "!type": "fn()" - }, - "setDisplayScale": { - "!doc": "设置屏幕放缩", - "!type": "fn(delta: number)" - }, - "speedDownReplay": { - "!doc": "减速播放", - "!type": "fn()" - }, - "getRealStatus": { - "!doc": "计算主角的某个属性,包括百分比修正
例如:core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值
name: 属性的英文名,请注意只能用于数值类属性哦,否则乘法会得到NaN", - "!type": "fn(name: string)" - }, - "saveData": { - "!doc": "存档到本地", - "!type": "fn()" - }, - "unregisterAnimationFrame": { - "!doc": "注销一个animationFrame", - "!type": "fn(name: string)" - }, - "tryMoveDirectly": { - "!doc": "尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致
例如:core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件
destX: 目标点的横坐标
destY: 目标点的纵坐标", - "!type": "fn(destX: number, destY: number)" - }, - "moveDirectly": { - "!doc": "瞬间移动", - "!type": "fn(destX?: number, destY?: number, ignoreSteps?: number)" - }, - "clearRouteFolding": { - "!doc": "清空录像折叠信息", - "!type": "fn()" - }, - "checkRouteFolding": { - "!doc": "检查录像折叠信息", - "!type": "fn()" - }, - "setSwitch": { - "!doc": "设置某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string, value: ?)" - }, - "getSwitch": { - "!doc": "获得某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string, defaultValue?: ?)" - }, - "addSwitch": { - "!doc": "增加某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string, value: number)" - }, - "removeSwitch": { - "!doc": "删除某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string)" - }, - "removeSwitch": { - "!doc": "判定某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string) -> bool" - } - }, - "icons": { - "!doc": "图标信息", - "getTilesetOffset": { - "!doc": "根据图块数字或ID获得所在的tileset和坐标信息", - "!type": "fn(id?: string) -> {image: ?, x: number, y: number}" - }, - "getClsFromId": { - "!doc": "根据ID获得其图标类型", - "!type": "fn(id?: string) -> string" - }, - "getAllIconIds": { - "!doc": "获得所有图标的ID", - "!type": "fn() -> [string]" - }, - "getIcons": { - "!doc": "获得所有图标类型", - "!type": "fn()" - } - }, - "items": { - "!doc": "道具相关的函数", - "getEquip": { - "!doc": "检查主角某种类型的装备目前是什么
例如:core.getEquip(1) // 主角目前装备了什么盾牌
equipType: 装备类型,自然数
返回值:装备id,null表示未穿戴", - "!type": "fn(equipType: number) -> string" - }, - "loadEquip": { - "!doc": "尝试穿上某件背包里面的装备并提示
例如:core.loadEquip('sword5') // 尝试装备上背包里面的神圣剑,无回调
equipId: 装备id
callback: 穿戴成功或失败后的回调函数", - "!type": "fn(equipId: string, callback?: fn())" - }, - "itemCount": { - "!doc": "统计某种道具的持有量
例如:core.itemCount('yellowKey') // 持有多少把黄钥匙
itemId: 道具id
返回值:该种道具的持有量,不包括已穿戴的装备", - "!type": "fn(itemId: string) -> number" - }, - "getItems": { - "!doc": "获得所有道具", - "!type": "fn()" - }, - "canUseItem": { - "!doc": "检查能否使用某种道具
例如:core.canUseItem('pickaxe') // 能否使用破墙镐
itemId: 道具id
返回值:true表示可以使用", - "!type": "fn(itemId: string) -> bool" - }, - "hasItem": { - "!doc": "检查主角是否持有某种道具(不包括已穿戴的装备)
例如:core.hasItem('yellowKey') // 主角是否持有黄钥匙
itemId: 道具id
返回值:true表示持有", - "!type": "fn(itemId: string) -> bool" - }, - "addItem": { - "!doc": "静默增减某种道具的持有量 不会更新游戏画面或是显示提示
例如:core.addItem('yellowKey', -2) // 没收两把黄钥匙
itemId: 道具id
itemNum: 增加量,负数表示没收", - "!type": "fn(itemId: string, itemNum?: number)" - }, - "unloadEquip": { - "!doc": "脱下某个类型的装备
例如:core.unloadEquip(1) // 卸下盾牌,无回调
equipType: 装备类型编号,自然数
callback: 卸下装备后的回调函数", - "!type": "fn(equipType: number, callback?: fn())" - }, - "quickLoadEquip": { - "!doc": "快速换装
例如:core.quickLoadEquip(1) // 快速换上1号套装
index: 套装编号,自然数", - "!type": "fn(index: number)" - }, - "getItemEffect": { - "!doc": "即捡即用类的道具获得时的效果
例如:core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果
itemId: 道具id
itemNum: 道具数量,可选,默认为1", - "!type": "fn(itemId: string, itemNum?: number)" - }, - "quickSaveEquip": { - "!doc": "保存当前套装
例如:core.quickSaveEquip(1) // 将当前套装保存为1号套装
index: 套装编号,自然数", - "!type": "fn(index: number)" - }, - "setItem": { - "!doc": "设置某种道具的持有量
例如:core.setItem('yellowKey', 3) // 设置黄钥匙为3把
itemId: 道具id
itemNum: 新的持有量,可选,自然数,默认为0", - "!type": "fn(itemId: string, itemNum?: number)" - }, - "compareEquipment": { - "!doc": "比较两件(类型可不同)装备的优劣
例如:core.compareEquipment('sword5', 'shield5') // 比较神圣剑和神圣盾的优劣
compareEquipId: 装备甲的id
beComparedEquipId: 装备乙的id
返回值:两装备的各属性差,甲减乙,0省略", - "!type": "fn(compareEquipId: string, beComparedEquipId: string) -> {value: ?, percentage: ?}" - }, - "removeItem": { - "!doc": "删除某个物品", - "!type": "fn(itemId?: string, itemNum?: number)" - }, - "getEquipTypeById": { - "!doc": "判定某件装备的类型
例如:core.getEquipTypeById('shield5') // 1(盾牌)
equipId: 装备id
返回值:类型编号,自然数", - "!type": "fn(equipId: string) -> number" - }, - "getEquipTypeByName": { - "!doc": "根据类型获得一个可用的装备孔", - "!type": "fn(name?: string)" - }, - "useItem": { - "!doc": "使用一个道具
例如:core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调
itemId: 道具id
noRoute: 是否不计入录像,快捷键使用的请填true,否则可省略
callback: 道具使用完毕或使用失败后的回调函数", - "!type": "fn(itemId: string, noRoute?: bool, callback?: fn())" - }, - "hasEquip": { - "!doc": "检查主角是否穿戴着某件装备
例如:core.hasEquip('sword5') // 主角是否装备了神圣剑
itemId: 装备id
返回值:true表示已装备", - "!type": "fn(itemId: string) -> bool" - }, - "getItemEffectTip": { - "!doc": "即捡即用类的道具获得时的额外提示
例如:core.getItemEffectTip(redPotion) // (获得 红血瓶)',生命+100'
itemId: 道具id
返回值:图块属性itemEffectTip的内容", - "!type": "fn(itemId: string) -> string" - }, - "canEquip": { - "!doc": "检查能否穿上某件装备
例如:core.canEquip('sword5', true) // 主角可以装备神圣剑吗,如果不能会有提示
equipId: 装备id
hint: 无法穿上时是否提示(比如是因为未持有还是别的什么原因)
返回值:true表示可以穿上,false表示无法穿上", - "!type": "fn(equipId: string, hint?: bool) -> bool" - }, - "setEquip": { - "!doc": "设置某个装备的属性并计入存档
例如:core.setEquip('sword1', 'value', 'atk', 300, '+='); // 设置铁剑的攻击力数值再加300
equipId: 装备id
valueType: 增幅类型,只能是value(数值)或percentage(百分比)
name: 要修改的属性名称,如atk
value: 要修改到的属性数值
operator: 操作符,可选,如+=表示在原始值上增加
prefix: 独立开关前缀,一般不需要", - "!type": "fn(equipId: string, valueType: string, name: string, value: ?, operator?: string, prefix?: string)" - } - }, - "utils": { - "!doc": "工具函数库,里面有各个样板中使用到的工具函数。", - "scan": { - "!doc": "朝向到x,y映射", - "up": { - "x": "number", - "y": "number" - }, - "down": { - "x": "number", - "y": "number" - }, - "left": { - "x": "number", - "y": "number" - }, - "right": { - "x": "number", - "y": "number" - } - }, - "applyEasing": { - "!doc": "获得变速移动曲线", - "!type": "fn(mode?: string) -> fn(t: number) -> number" - }, - "clamp": { - "!doc": "将x限定在[a,b]区间内,注意a和b可交换
例如:core.clamp(1200, 1, 1000); // 1000
x: 原始值,!x为true时x一律视为0
a: 下限值,大于b将导致与b交换
b: 上限值,小于a将导致与a交换", - "!type": "fn(x: number, a: number, b: number) -> number" - }, - "rand": { - "!doc": "不支持SL的随机数
例如:1 + core.rand(6); // 随机生成一个小于7的正整数,模拟骰子的效果
num: 填正数表示生成小于num的随机自然数,否则生成小于1的随机正数
返回值:随机数,即使读档也不会改变结果", - "!type": "fn(num?: number) -> number" - }, - "clone": { - "!doc": "深拷贝一个对象(函数将原样返回)
例如:core.clone(core.status.hero, (name, value) => (name == 'items' || typeof value == 'number'), false); // 深拷贝主角的属性和道具
data: 待拷贝对象
filter: 过滤器,可选,表示data为数组或对象时拷贝哪些项或属性,true表示拷贝
recursion: 过滤器是否递归,可选。true表示过滤器也被递归
返回值:拷贝的结果,注意函数将原样返回", - "!type": "fn(data?: ?, filter?: fn(name: string, value: ?) -> bool, recursion?: bool)" - }, - "cloneArray": { - "!doc": "深拷贝一个1D或2D数组对象
例如:core.cloneArray(core.status.thisMap.map)", - "!type": "fn(data?: [number]|[[number]]) -> [number]|[[number]]" - }, - "setLocalForage": { - "!doc": "往数据库写入一段数据", - "!type": "fn(key: string, value?: ?, successCallback?: fn(), errorCallback?: fn())" - }, - "getGlobal": { - "!doc": "读取一个全局存储,适用于global:xxx,支持录像。
例如:if (core.getGlobal('一周目已通关', false) === true) core.getItem('dagger'); // 二周目游戏进行到此处时会获得一把屠龙匕首
key: 全局变量名称,支持中文
defaultValue: 可选,当此全局变量不存在或值为null、undefined时,用此值代替
返回值:全局变量的值", - "!type": "fn(key: string, defaultValue?: ?)" - }, - "replaceText": { - "!doc": "将一段文字中的${}(表达式)进行替换。
例如:core.replaceText('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); // 把主角的生命值和持有的黄钥匙数量代入这句话
text: 模板字符串,可以使用${}计算js表达式,支持“状态、物品、变量、独立开关、全局存储、图块id、图块类型、敌人数据、装备id”等量参与运算
返回值:替换完毕后的字符串", - "!type": "fn(text: string, prefix?: string) -> string" - }, - "removeLocalStorage": { - "!doc": "移除本地存储", - "!type": "fn(key: string)" - }, - "unzip": { - "!doc": "解压一段内容", - "!type": "fn(blobOrUrl?: ?, success?: fn(data: ?), error?: fn(error: string), convertToText?: bool, onprogress?: fn(loaded: number, total: number))" - }, - "formatTime": { - "!doc": "格式化时间", - "!type": "fn(time: number) -> string" - }, - "readFile": { - "!doc": "尝试请求读取一个本地文件内容 [异步]
success: 成功后的回调
error: 失败后的回调
readType: 不设置则以文本读取,否则以DataUrl形式读取", - "!type": "fn(success?: fn(data: string), error?: fn(message: string), readType?: bool)" - }, - "readFileContent": { - "!doc": "文件读取完毕后的内容处理 [异步]", - "!type": "fn(content: string)" - }, - "formatDate": { - "!doc": "格式化日期为字符串", - "!type": "fn(date: ?) -> string" - }, - "download": { - "!doc": "弹窗请求下载一个文本文件
例如:core.download('route.txt', JSON.stringify(core.status.route)); // 弹窗请求下载录像
filename: 文件名
content: 文件内容", - "!type": "fn(filename: string, content: string)" - }, - "encodeBase64": { - "!doc": "base64加密
例如:core.encodeBase64('abcd'); // 'YWJjZA=='
str: 明文
返回值:密文", - "!type": "fn(str: string) -> string" - }, - "strlen": { - "!doc": "求字符串的国标码字节数,也可用于等宽字体下文本的宽度测算。请注意样板的默认字体Verdana不是等宽字体
例如:core.strlen('无敌ad'); // 6
str: 待测字符串
返回值:字符串的国标码字节数,每个汉字为2,每个ASCII字符为1", - "!type": "fn(str: string) -> number" - }, - "myprompt": { - "!doc": "让用户输入一段文字", - "!type": "fn(hint: string, value: string, callback?: fn(data?: string))" - }, - "getCookie": { - "!doc": "访问浏览器cookie", - "!type": "fn(name: string) -> string" - }, - "decodeRoute": { - "!doc": "录像解压的最后一步,即一压的逆过程
例如:core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-|
route: 录像解压倒数第二步的结果,即一压的结果
返回值:原始录像", - "!type": "fn(route: string) -> [string]" - }, - "formatDate2": { - "!doc": "格式化日期为最简字符串", - "!type": "fn(date: ?) -> string" - }, - "unshift": { - "!doc": "将b(可以是另一个数组)插入数组a的开头,此函数用于弥补a.unshift(b)中b只能是单项的不足。
例如:core.unshift(todo, {type: 'unfollow'}); // 在事件指令数组todo的开头插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新首项或前缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", - "!type": "fn(a: [?], b: ?) -> [?]" - }, - "same": { - "!doc": "判定深层相等, 会逐层比较每个元素
例如:core.same(['1', 2], ['1', 2]); // true", - "!type": "fn(a?: ?, b?: ?) -> bool" - }, - "setTwoDigits": { - "!doc": "两位数显示", - "!type": "fn(x: number) -> string" - }, - "splitImage": { - "!doc": "等比例切分一张图片
例如:core.splitImage(core.material.images.images['npc48.png'], 32, 48); // 把npc48.png切分成若干32×48px的小人
image: 图片名(支持映射前的中文名)或图片对象(参见上面的例子),获取不到时返回[]
width: 子图的宽度,单位为像素。原图总宽度必须是其倍数,不填视为32
height: 子图的高度,单位为像素。原图总高度必须是其倍数,不填视为正方形
返回值:子图组成的数组,在原图中呈先行后列,从左到右、从上到下排列。", - "!type": "fn(image?: string|image, width?: number, height?: number) -> [image]" - }, - "decompress": { - "!doc": "解压缩一个数据", - "!type": "fn(value: ?)" - }, - "showWithAnimate": { - "!doc": "动画显示某对象", - "!type": "fn(obj?: ?, speed?: number, callback?: fn())" - }, - "subarray": { - "!doc": "判定一个数组是否为另一个数组的前缀,用于录像接续播放。请注意函数名没有大写字母
例如:core.subarray(['ad', '米库', '小精灵', '小破草', '小艾'], ['ad', '米库', '小精灵']); // ['小破草', '小艾']
a: 可能的母数组,不填或比b短将返回null
b: 可能的前缀,不填或比a长将返回null
返回值:如果b不是a的前缀将返回null,否则将返回a去掉此前缀后的剩余数组", - "!type": "fn(a?: [?], b?: [?]) -> [?]|null" - }, - "turnDirection": { - "!doc": "计算应当转向某个方向
turn: 转向的方向,可为 up,down,left,right,:left,:right,:back 七种
direction: 当前方向", - "!type": "fn(turn: string, direction?: string) -> string" - }, - "myconfirm": { - "!doc": "显示确认框,类似core.drawConfirmBox(),但不打断事件流
例如:core.myconfirm('重启游戏?', core.restart); // 弹窗询问玩家是否重启游戏
hint: 弹窗的内容,支持 ${} 语法
yesCallback: 确定后的回调函数
noCallback: 取消后的回调函数,可选", - "!type": "fn(hint: string, yesCallback?: fn(), noCallback?: fn())" - }, - "calValue": { - "!doc": "计算一个表达式的值,支持status:xxx等的计算。
例如:core.calValue('status:hp + status:def'); // 计算主角的生命值加防御力
value: 待求值的表达式
prefix: 独立开关前缀,一般可省略
返回值:求出的值", - "!type": "fn(value: string, prefix?: string)" - }, - "encodeRoute": { - "!doc": "录像压缩缩
例如:core.encodeRoute(core.status.route); // 压缩当前录像
route: 原始录像,自定义内容(不予压缩,原样写入)必须由0-9A-Za-z和下划线、冒号组成,所以中文和数组需要用JSON.stringify预处理再base64压缩才能交由一压
返回值:一压的结果", - "!type": "fn(route: [string]) -> string" - }, - "decodeBase64": { - "!doc": "base64解密
例如:core.decodeBase64('YWJjZA=='); // \"abcd\"
str: 密文
返回值:明文", - "!type": "fn(str: string) -> string" - }, - "http": { - "!doc": "发送一个HTTP请求 [异步]
type: 请求类型,只能为GET或POST
url: 目标地址
formData: 如果是POST请求则为表单数据
success: 成功后的回调
error: 失败后的回调", - "!type": "fn(type: string, url: string, formData: ?, success?: fn(data: string), error?: fn(message: string), mimeType?: string, responseType?: string, onprogress?: fn(loaded: number, total: number))" - }, - "getGuid": { - "!doc": "获得或生成浏览器唯一的guid", - "!type": "fn() -> string" - }, - "getLocalStorage": { - "!doc": "获得本地存储", - "!type": "fn(key: string, defaultValue?: ?)" - }, - "arrayToRGB": { - "!doc": "颜色数组转字符串
例如:core.arrayToRGB([102, 204, 255]); // \"#66ccff\"
color: 一行三列的数组,必须为不大于255的自然数
返回值:该颜色的#xxxxxx字符串表示", - "!type": "fn(color: [number]) -> string" - }, - "arrayToRGBA": { - "!doc": "颜色数组转字符串
例如:core.arrayToRGBA([102, 204, 255, 0.3]); // \"rgba(102,204,255,0.3)\"
color: 一行三列或一行四列的数组,前三个元素必须为不大于255的自然数。第四个元素(如果有)必须为0或不大于1的数字,第四个元素不填视为1
返回值:该颜色的rgba(...)字符串表示", - "!type": "fn(color: [number]) -> string" - }, - "formatBigNumber": { - "!doc": "大数字格式化,单位为10000的倍数(w,e,z,j,g),末尾四舍五入
例如:core.formatBigNumber(123456789, false); // \"12346w\"
x: 原数字
onMap: 可选,true表示用于地图显伤,结果总字符数最多为5,否则最多为6
返回值:格式化结果", - "!type": "fn(x: number, onMap?: bool) -> string" - }, - "removeLocalForage": { - "!doc": "移除本地数据库的数据", - "!type": "fn(key: string, successCallback?: fn(), errorCallback?: fn())" - }, - "matchWildcard": { - "!doc": "通配符匹配,用于搜索图块等批量处理。
例如:core.playSound(core.matchWildcard('*Key', itemId) ? 'item.mp3' : 'door.mp3'); // 判断捡到的是钥匙还是别的道具,从而播放不同的音效
pattern: 模式串,每个星号表示任意多个(0个起)字符
string: 待测串
返回值:true表示匹配成功,false表示匹配失败", - "!type": "fn(pattern: string, string: string) -> bool" - }, - "setLocalStorage": { - "!doc": "设置本地存储", - "!type": "fn(key: string, value?: ?)" - }, - "hideWithAnimate": { - "!doc": "动画使某对象消失", - "!type": "fn(obj?: ?, speed?: number, callback?: fn())" - }, - "copy": { - "!doc": "尝试复制一段文本到剪切板。", - "!type": "fn(data: string) -> bool" - }, - "isset": { - "!doc": "判断一个值是否不为null,undefined和NaN
例如:core.isset(0/0); // false,因为0/0等于NaN
v: 待测值,可选
返回值:false表示待测值为null、undefined、NaN或未填写,true表示为其他值。", - "!type": "fn(v?: ?) -> bool" - }, - "replaceValue": { - "!doc": "对一个表达式中的特殊规则进行替换,如status:xxx等。
例如:core.replaceValue('status:atk+item:yellowKey'); // 把这两个冒号表达式替换为core.getStatus('hp')和core.itemCount('yellowKey')这样的函数调用
value: 模板字符串,注意独立开关不会被替换
返回值:替换完毕后的字符串", - "!type": "fn(value: string) -> string" - }, - "getLocalForage": { - "!doc": "从本地数据库读出一段数据", - "!type": "fn(key: string, defaultValue?: ?, successCallback?: fn(data: ?), errorCallback?: fn())" - }, - "inArray": { - "!doc": "判定array是不是一个数组,以及element是否在该数组中。
array: 可能的数组,不为数组或不填将导致返回值为false
element: 待查找的元素
返回值:如果array为数组且具有element这项,就返回true,否则返回false", - "!type": "fn(array?: ?, element?: ?) -> bool" - }, - "setGlobal": { - "!doc": "设置一个全局存储,适用于global:xxx,录像播放时将忽略此函数。
例如:core.setBlobal('一周目已通关', true); // 设置全局存储“一周目已通关”为true,方便二周目游戏中的新要素。
key: 全局变量名称,支持中文
value: 全局变量的新值,不填或null表示清除此全局存储", - "!type": "fn(key: string, value?: ?)" - }, - "rand2": { - "!doc": "支持SL的随机数,并计入录像
例如:1 + core.rand2(6); // 随机生成一个小于7的正整数,模拟骰子的效果
num: 正整数,0或不填会被视为2147483648
返回值:属于 [0, num) 的随机数", - "!type": "fn(num?: number) -> number" - }, - "setStatusBarInnerHTML": { - "!doc": "填写非自绘状态栏
例如:core.setStatusBarInnerHTML('hp', core.status.hero.hp, 'color: #66CCFF'); // 更新状态栏中的主角生命,使用加载画面的宣传色
name: 状态栏项的名称,如'hp', 'atk', 'def'等。必须是core.statusBar中的一个合法项
value: 要填写的内容,大数字会被格式化为至多6个字符,无中文的内容会被自动设为斜体
css: 额外的css样式,可选。如更改颜色等", - "!type": "fn(name: string, value: ?, css?: string)" - }, - "matchRegex": { - "!doc": "是否满足正则表达式", - "!type": "fn(pattern: string, string: string) -> string" - }, - "push": { - "!doc": "将b(可以是另一个数组)插入数组a的末尾,此函数用于弥补a.push(b)中b只能是单项的不足。
例如:core.push(todo, {type: 'unfollow'}); // 在事件指令数组todo的末尾插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新末项或后缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", - "!type": "fn(a: [?], b: ?) -> [?]" - }, - "formatSize": { - "!doc": "格式化文件大小", - "!type": "fn(size: number) -> string" - } - }, - "actions": { - "!doc": "主要是处理一些和用户交互相关的内容。", - "onup": { - "!doc": "当点击(触摸)事件放开时", - "!type": "fn(loc: {x: number, y: number, size: number})" - }, - "pressKey": { - "!doc": "按住某个键时", - "!type": "fn(keyCode: number)" - }, - "keyUp": { - "!doc": "根据放开键的code来执行一系列操作", - "!type": "fn(keyCode: number, altKey?: bool, fromReplay?: bool)" - }, - "ondown": { - "!doc": "点击(触摸)事件按下时", - "!type": "fn(loc: {x: number, y: number, size: number})" - }, - "registerAction": { - "!doc": "此函数将注册一个用户交互行为。
action: 要注册的交互类型,如 ondown, onclick, keyDown 等等。
name: 你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。
func: 执行函数。
如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。
priority: 优先级;优先级高的将会被执行。此项可不填,默认为0", - "!type": "fn(action: string, name: string, func: string|fn(params: ?), priority?: number)" - }, - "onkeyDown": { - "!doc": "按下某个键时", - "!type": "fn(e: Event)" - }, - "keyDown": { - "!doc": "根据按下键的code来执行一系列操作", - "!type": "fn(keyCode: number)" - }, - "onStatusBarClick": { - "!doc": "点击自绘状态栏时", - "!type": "fn(e?: Event)" - }, - "longClick": { - "!doc": "长按", - "!type": "fn(x: number, y: number, px: number, py: number, fromEvent?: bool)" - }, - "unregisterAction": { - "!doc": "注销一个用户交互行为", - "!type": "fn(action: string, name: string)" - }, - "keyDownCtrl": { - "!doc": "长按Ctrl键时", - "!type": "fn() -> bool" - }, - "onclick": { - "!doc": "具体点击屏幕上(x,y)点时,执行的操作", - "!type": "fn(x: number, y: number, px: number, py: number, stepPostfix?: [?])" - }, - "doRegisteredAction": { - "!doc": "执行一个用户交互行为", - "!type": "fn(action: string, params: ?)" - }, - "onkeyUp": { - "!doc": "放开某个键时", - "!type": "fn(e: Event)" - }, - "onmousewheel": { - "!doc": "滑动鼠标滚轮时的操作", - "!type": "fn(direct: number)" - }, - "onmove": { - "!doc": "当在触摸屏上滑动时", - "!type": "fn(loc: {x: number, y: number, size: number})" - } - }, - "loader": { - "!doc": "资源加载相关的函数", - "loadImages": { - "!doc": "加载一系列图片", - "!type": "fn(dir: string, names: [string], toSave: ?, callback?: fn()) " - }, - "loadImagesFromZip": { - "!doc": "从zip中加载一系列图片", - "!type": "fn(url: string, names: [string], toSave?: ?, onprogress?: ?, onfinished?: ?)" - }, - "loadBgm": { - "!doc": "加载一个bgm", - "!type": "fn(name: string)" - }, - "loadOneMusic": { - "!doc": "加载一个音乐或音效", - "!type": "fn(name: string)" - }, - "freeBgm": { - "!doc": "释放一个bgm的缓存", - "!type": "fn(name: string)" - }, - "loadOneSound": { - "!doc": "加载一个音效", - "!type": "fn(name: string)" - }, - "loadImage": { - "!doc": "加载某一张图片", - "!type": "fn(dir: name, imgName: name, callback?: fn())" - } - }, - "maps": { - "!doc": "负责一切和地图相关的处理内容,包括如下几个方面:
- 地图的初始化,保存和读取,地图数组的生成
- 是否可移动或瞬间移动的判定
- 地图的绘制
- 获得某个点的图块信息
- 启用和禁用图块,改变图块
- 移动/跳跃图块,淡入淡出图块
- 全局动画控制,动画的绘制", - "noPass": { - "!doc": "判定某个点是否不可被踏入(不基于主角生命值和图块cannotIn属性)
例如:core.noPass(0, 0); // 判断地图左上角能否被踏入
x: 目标点的横坐标
y: 目标点的纵坐标
floorId: 目标点所在的地图id,不填视为当前地图
返回值:true表示可踏入", - "!type": "fn(x: number, y: number, floorId?: string) -> bool" - }, - "drawAnimate": { - "!doc": "播放动画,注意即使指定了主角的坐标也不会跟随主角移动,如有需要请使用core.drawHeroAnimate(name, callback)函数
例如:core.drawAnimate('attack', core.nextX(), core.nextY(), false, core.vibrate); // 在主角面前一格播放普攻动画,动画停止后视野左右抖动1秒
name: 动画文件名,不含后缀
x: 横坐标
y: 纵坐标
alignWindow: 是否是相对窗口的坐标
callback: 动画停止后的回调函数,可选
返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数)", - "!type": "fn(name: string, x: number, y: number, alignWindow: bool, callback?: fn()) -> number" - }, - "drawHeroAnimate": { - "!doc": "播放跟随勇士的动画
name: 动画名
callback: 动画停止后的回调函数,可选
返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数)", - "!type": "fn(name: string, callback?: fn()) -> number" - }, - "stopAnimate": { - "!doc": "立刻停止一个动画播放
id: 播放动画的编号,即drawAnimate或drawHeroAnimate的返回值;不填视为所有动画br/>doCallback: 是否执行该动画的回调函数", - "!type": "fn(id?: number, doCallback?: bool)" - }, - "getPlayingAnimates": { - "!doc": "获得当前正在播放的所有(指定)动画的id列表
name: 动画名;不填代表返回全部正在播放的动画
返回值: 一个数组,每一项为一个正在播放的动画;可用core.stopAnimate停止播放。", - "!type": "fn(name?: string) -> [number]" - }, - "getBlockCls": { - "!doc": "判定某个点的图块类型
例如:if(core.getBlockCls(x1, y1) != 'enemys' && core.getBlockCls(x2, y2) != 'enemy48') core.openDoor(x3, y3); // 另一个简单的机关门事件,打败或炸掉这一对不同身高的敌人就开门
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块类型,即“地形、四帧动画、矮敌人、高敌人、道具、矮npc、高npc、自动元件、额外地形”之一", - "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string" - }, - "drawMap": { - "!doc": "地图重绘
例如:core.drawMap(); // 重绘当前地图,常用于更改贴图或改变自动元件后的刷新
floorId: 地图id,可省略表示当前楼层
callback: 重绘完毕后的回调函数,可选", - "!type": "fn(floorId?: string)" - }, - "nearStair": { - "!doc": "当前位置是否在楼梯边;在楼传平面塔模式下对箭头也有效", - "!type": "fn() -> bool" - }, - "turnBlock": { - "!doc": "事件转向", - "!type": "fn(direction?: string, x?: number, y?: number, floorId?: string)" - }, - "getMapArray": { - "!doc": "生成事件层矩阵
例如:core.getMapArray('MT0'); // 生成主塔0层的事件层矩阵,隐藏的图块视为0
floorId: 地图id,不填视为当前地图
showDisable: 可选,true表示隐藏的图块也会被表示出来
返回值:事件层矩阵,注意对其阵元的访问是[y][x]", - "!type": "fn(floorId?: string, noCache?: bool) -> [[number]]" - }, - "getMapNumber": { - "!doc": "获得事件层某个点的数字", - "!type": "fn(x: number, y: number, floorId?: string, noCache?: bool) -> number" - }, - "jumpBlock": { - "!doc": "跳跃图块;从V2.7开始不再有音效
例如:core.jumpBlock(0, 0, 0, 0); // 令地图左上角的图块原地跳跃半秒,再花半秒淡出
sx: 起点的横坐标
sy: 起点的纵坐标
ex: 终点的横坐标
ey: 终点的纵坐标
time: 单步和淡出用时,单位为毫秒。不填视为半秒
keep: 是否不淡出,true表示不淡出
callback: 落地或淡出后的回调函数,可选", - "!type": "fn(sx: number, sy: number, ex: number, ey: number, time?: number, keep?: bool, callback?: fn())" - }, - "replaceBlock": { - "!doc": "批量替换图块
例如:core.replaceBlock(21, 22, core.floorIds); // 把游戏中地上当前所有的黄钥匙都变成蓝钥匙
fromNumber: 旧图块的数字
toNumber: 新图块的数字
floorId: 地图id或其数组,不填视为当前地图", - "!type": "fn(fromNumber: number, toNumber: number, floorId?: string|[string])" - }, - "drawBlock": { - "!doc": "绘制一个图块", - "!type": "fn(block?: block, animate?: number)" - }, - "resetMap": { - "!doc": "重置地图", - "!type": "fn(floorId?: string|[string])" - }, - "animateSetBlock": { - "!doc": "动画形式转变某点图块", - "!type": "fn(number: number|string, x: number, y: number, floorId?: string, time?: number, callback?: fn())" - }, - "animateSetBlocks": { - "!doc": "动画形式同时转变若干点图块", - "!type": "fn(number: number|string, locs: [?], floorId?: string, time?: number, callback?: fn())" - }, - "compressMap": { - "!doc": "压缩地图", - "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]" - }, - "enemyExists": { - "!doc": "某个点是否存在(指定的)怪物", - "!type": "fn(x: number, y: number, id?: string, floorId?: string) -> bool" - }, - "npcExists": { - "!doc": "某个点是否存在NPC", - "!type": "fn(x: number, y: number, floorId?: string) -> bool" - }, - "getBlockByNumber": { - "!doc": "根据数字获得图块", - "!type": "fn(number: number) -> block" - }, - "removeBlock": { - "!doc": "删除一个图块,对应于「隐藏事件」并同时删除
例如:core.removeBlock(0, 0); // 尝试删除地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", - "!type": "fn(x: number, y: number, floorId?: string)" - }, - "hideBlock": { - "!doc": "隐藏一个图块,对应于「隐藏事件」且不删除
例如:core.hideBlock(0, 0); // 隐藏地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", - "!type": "fn(x: number, y: number, floorId?: string)" - }, - "removeBlockByIndex": { - "!doc": "根据block的索引删除该块", - "!type": "fn(index: number, floorId?: string)" - }, - "stairExists": { - "!doc": "某个点是否存在楼梯", - "!type": "fn(x: number, y: number, floorId?: string) -> bool" - }, - "isMapBlockDisabled": { - "!doc": "某个点图块是否被强制启用或禁用", - "!type": "fn(floorId?: string, x?: number, y?: number, flags?: ?) -> bool" - }, - "setMapBlockDisabled": { - "!doc": "设置某个点图块的强制启用或禁用状态", - "!type": "fn(floorId?: string, x?: number, y?: number, disabled?: bool)" - }, - "setBlockOpacity": { - "!doc": "设置某个点图块的不透明度", - "!type": "fn(opacity?: number, x?: number, y?: number, floorId?: string)" - }, - "setBlockFilter": { - "!doc": "设置某个点图块的特效", - "!type": "fn(filter?: ?, x?: number, y?: number, floorId?: string)" - }, - "decompressMap": { - "!doc": "解压缩地图", - "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]" - }, - "automaticRoute": { - "!doc": "自动寻路
例如:core.automaticRoute(0, 0); // 自动寻路到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:每步走完后主角的loc属性组成的一维数组", - "!type": "fn(destX: number, destY: number) -> [{x: number, y: number, direction: string}]" - }, - "resizeMap": { - "!doc": "更改地图画布的尺寸", - "!type": "fn(floorId?: string)" - }, - "getFgNumber": { - "!doc": "判定某点的前景层的数字
例如:core.getFgNumber(); // 判断主角脚下的前景层图块的数字
x: 横坐标,不填为勇士坐标
y: 纵坐标,不填为勇士坐标floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存而强制重算", - "!type": "fn(x: number, y: number, floorId?: string, noCache?: bool) -> number" - }, - "moveBlock": { - "!doc": "移动图块
例如:core.moveBlock(0, 0, ['down']); // 令地图左上角的图块下移一格
x: 起点的横坐标
y: 起点的纵坐标
steps: 步伐数组
time: 单步和淡出用时,单位为毫秒。不填视为半秒
keep: 是否不淡出,true表示不淡出
callback: 移动或淡出后的回调函数,可选", - "!type": "fn(x: number, y: number, steps: [string], time?: number, keep?: bool, callback?: fn())" - }, - "getBgNumber": { - "!doc": "判定某点的背景层的数字
例如:core.getBgNumber(); // 判断主角脚下的背景层图块的数字
x: 横坐标,不填为勇士坐标
y: 纵坐标,不填为勇士坐标
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存而强制重算", - "!type": "fn(x?: number, y?: number, floorId?: string, noCache?: bool) -> number" - }, - "getIdOfThis": { - "!doc": "获得当前事件点的ID", - "!type": "fn(id?: string) -> string" - }, - "searchBlock": { - "!doc": "搜索图块, 支持通配符和正则表达式
例如:core.searchBlock('*Door'); // 搜索当前地图的所有门
id: 图块id,支持星号表示任意多个(0个起)字符
floorId: 地图id或数组,不填视为当前地图
showDisable: 隐藏点是否计入,true表示计入
返回值:一个详尽的数组,一般只用到其长度", - "!type": "fn(id: string, floorId?: string|[string], showDisable?: bool) -> [{floorId: string, index: number, x: number, y: number, block: block}]" - }, - "searchBlockWithFilter": { - "!doc": "根据给定的筛选函数搜索全部满足条件的图块
例如:core.searchBlockWithFilter(function (block) { return block.event.id.endsWith('Door'); }); // 搜索当前地图的所有门
blockFilter: 筛选函数,可接受block输入,应当返回一个boolean值
floorId: 地图id或数组,不填视为当前地图
showDisable: 隐藏点是否计入,true表示计入
返回值:一个详尽的数组", - "!type": "fn(blockFilter: fn(block: block) -> bool, floorId?: string|[string], showDisable?: bool): [{floorId: string, index: number, x: number, y: number, block: block}]" - }, - "hideBgFgMap": { - "!doc": "隐藏前景/背景地图", - "!type": "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())" - }, - "getBlockInfo": { - "!doc": "获得某个图块或素材的信息,包括ID,cls,图片,坐标,faceIds等等", - "!type": "fn(block?: number|string|block) -> blockInfo" - }, - "getFaceDownId": { - "!doc": "获得某个图块对应行走图朝向向下的那一项的id;如果不存在行走图绑定则返回自身id。", - "!type": "fn(block?: string|number|block) -> string" - }, - "canMoveDirectlyArray": { - "!doc": "获得某些点可否通行的信息", - "!type": "fn(locs?: [[number]])" - }, - "hideFloorImage": { - "!doc": "隐藏一个楼层贴图", - "!type": "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())" - }, - "extractBlocks": { - "!doc": "根据需求解析出blocks", - "!type": "fn(map?: ?)" - }, - "extractBlocksForUI": { - "!doc": "根据需求为UI解析出blocks", - "!type": "fn(map?: ?, flags?: ?)" - }, - "getBlockId": { - "!doc": "判定某个点的图块id
例如:if(core.getBlockId(x1, y1) != 'greenSlime' && core.getBlockId(x2, y2) != 'redSlime') core.openDoor(x3, y3); // 一个简单的机关门事件,打败或炸掉这一对绿头怪和红头怪就开门
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块id,该点无图块则返回null", - "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string" - }, - "getBlockNumber": { - "!doc": "判定某个点的图块数字
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块数字,该点无图块则返回null", - "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> number" - }, - "getBlockOpacity": { - "!doc": "获得某个点图块的不透明度", - "!type": "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> number" - }, - "getBlockFilter": { - "!doc": "获得某个点图块的特效", - "!type": "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> ?" - }, - "loadFloor": { - "!doc": "从文件或存档中加载某个楼层", - "!type": "fn(floorId?: string, map?: ?)" - }, - "generateMovableArray": { - "!doc": "可通行性判定
例如:core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动
floorId: 地图id,不填视为当前地图
返回值:从各点可移动方向的三维数组", - "!type": "fn(floorId?: string) -> [[[string]]]" - }, - "terrainExists": { - "!doc": "某个点是否存在(指定的)地形", - "!type": "fn(x: number, y: number, id?: string, floorId?: string) -> bool" - }, - "getBlockById": { - "!doc": "根据ID获得图块", - "!type": "fn(id: string) -> block" - }, - "drawBg": { - "!doc": "绘制背景层(含贴图,其与背景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawBg(); // 绘制当前地图的背景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", - "!type": "fn(floorId?: string, ctx?: CanvasRenderingContext2D)" - }, - "showBlock": { - "!doc": "显示(隐藏或显示的)图块,此函数将被“显示事件”指令和勾选了“不消失”的“移动/跳跃事件”指令(如阻击怪)的终点调用
例如:core.showBlock(0, 0); // 显示地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", - "!type": "fn(x: number, y: number, floorId?: string)" - }, - "getMapBlocksObj": { - "!doc": "以x,y的形式返回每个点的事件", - "!type": "fn(floorId?: string, noCache?: bool)" - }, - "removeGlobalAnimate": { - "!doc": "删除一个或所有全局动画", - "!type": "fn(x?: number, y?: number, name?: string)" - }, - "drawEvents": { - "!doc": "绘制事件层
例如:core.drawEvents(); // 绘制当前地图的事件层
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", - "!type": "fn(floorId?: string, blocks?: [block], ctx?: CanvasRenderingContext2D)" - }, - "canMoveDirectly": { - "!doc": "能否瞬移到某点,并求出节约的步数。
例如:core.canMoveDirectly(0, 0); // 能否瞬移到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:正数表示节约的步数,-1表示不可瞬移", - "!type": "fn(destX: number, destY: number) -> number" - }, - "saveMap": { - "!doc": "将当前地图重新变成数字,以便于存档", - "!type": "fn(floorId?: string)" - }, - "drawBoxAnimate": { - "!doc": "绘制UI层的box动画", - "!type": "fn()" - }, - "setBgFgBlock": { - "!doc": "转变图层块
例如:core.setBgFgBlock('bg', 167, 6, 6); // 把当前地图背景层的中心块改为滑冰
name: 背景还是前景
number: 新图层块的数字(也支持纯数字字符串如'1')或id
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", - "!type": "fn(name: string, number: number|string, x: number, y: number, floorId?: string)" - }, - "drawFg": { - "!doc": "绘制前景层(含贴图,其与前景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawFg(); // 绘制当前地图的前景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", - "!type": "fn(floorId?: string, ctx?: CanvasRenderingContext2D)" - }, - "getBlock": { - "!doc": "获得某个点的block", - "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> block" - }, - "initBlock": { - "!doc": "初始化一个图块", - "!type": "fn(x: number, y: number, id: string|number, addInfo?: bool, eventFloor?: ?) -> block" - }, - "addGlobalAnimate": { - "!doc": "添加一个全局动画", - "!type": "fn(block?: block)" - }, - "animateBlock": { - "!doc": "显示/隐藏某个块时的动画效果", - "!type": "fn(loc?: [number]|[[number]], type?: string|number, time?: number, callback?: fn())" - }, - "loadMap": { - "!doc": "将存档中的地图信息重新读取出来", - "!type": "fn(data?: ?, floorId?: string, flags?: ?)" - }, - "setBlock": { - "!doc": "转变图块
例如:core.setBlock(1, 0, 0); // 把地图左上角变成黄墙
number: 新图块的数字(也支持纯数字字符串如'1')或id
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", - "!type": "fn(number: number|string, x: number, y: number, floorId?: string)" - }, - "getFgMapArray": { - "!doc": "生成前景层矩阵
例如:core.getFgMapArray('MT0'); // 生成主塔0层的前景层矩阵,使用缓存
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存
返回值:前景层矩阵,注意对其阵元的访问是[y][x]", - "!type": "fn(floorId?: string, noCache?: bool) -> [[number]]" - }, - "getBgMapArray": { - "!doc": "生成背景层矩阵
例如:core.getBgMapArray('MT0'); // 生成主塔0层的背景层矩阵,使用缓存
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存
返回值:背景层矩阵,注意对其阵元的访问是[y][x]", - "!type": "fn(floorId?: string, noCache?: bool) -> [[number]]" - }, - "canMoveHero": { - "!doc": "单点单朝向的可通行性判定;受各图层cannotInOut、起点cannotMove和canGoDeadZone影响,不受canPass和noPass影响
x: 起点横坐标,不填视为主角当前的
y: 起点纵坐标,不填视为主角当前的
direction: 移动的方向,不填视为主角面对的方向
floorId: 地图id,不填视为当前地图", - "!type": "fn(x?: number, y?: number, direction?: string, floorId?: string) -> bool" - }, - "drawThumbnail": { - "!doc": "绘制缩略图
例如:core.drawThumbnail(); // 绘制当前地图的缩略图
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
options: 绘制信息,可选。可以增绘主角位置和朝向、采用不同于游戏中的主角行走图、增绘显伤、提供flags用于存读档,同时包含要绘制到的画布名或画布的ctx或还有其他信息,如起绘坐标、绘制大小、是否绘制全图、截取中心", - "!type": "fn(floorId?: string, blocks?: [block], options?: ?)" - }, - "hideBlockByIndex": { - "!doc": "根据图块的索引来隐藏图块", - "!type": "fn(index?: number, floorId?: string)" - }, - "getNumberById": { - "!doc": "根据图块id得到数字(地图矩阵中的值)
例如:core.getNumberById('yellowWall'); // 1
id: 图块id
返回值:图块的数字,定义在project\\maps.js(请注意和project\\icons.js中的“图块索引”相区分!)", - "!type": "fn(id: string) -> number" - }, - "removeBlockByIndexes": { - "!doc": "一次性删除多个block", - "!type": "fn(indexes?: [number], floorId?: string)" - }, - "hideBlockByIndexes": { - "!doc": "一次性隐藏多个block", - "!type": "fn(indexes?: [number], floorId?: string)" - }, - "generateGroundPattern": { - "!doc": "生成groundPattern", - "!type": "fn(floorId?: string)" - }, - "showBgFgMap": { - "!doc": "显示前景/背景地图", - "!type": "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())" - }, - "showFloorImage": { - "!doc": "显示一个楼层贴图", - "!type": "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())" - } - }, - "ui": { - "!doc": "负责一切UI界面的绘制。主要包括三个部分:
- 设置某个画布的属性与在某个画布上绘制的相关API
- 具体的某个UI界面的绘制
- 动态创建画布相关的API", - "resizeCanvas": { - "!doc": "重新设置一个自定义画布的大小", - "!type": "fn(name: string, x?: number, y?: number, styleOnly?: bool, isTempCanvas?: bool)" - }, - "deleteCanvas": { - "!doc": "删除一个自定义画布
name: 画布名,也可以传入一个函数对所有画布进行筛选", - "!type": "fn(name: string|fn(name: string) -> bool)" - }, - "deleteAllCanvas": { - "!doc": "清空所有的自定义画布", - "!type": "fn()" - }, - "drawIcon": { - "!doc": "在某个canvas上绘制一个图标", - "!type": "fn(name: string|CanvasRenderingContext2D, id: string, x: number, y: number, w?: number, h?: number, frame?: number)" - }, - "drawFly": { - "!doc": "绘制楼层传送器", - "!type": "fn(page?: ?)" - }, - "setOpacity": { - "!doc": "设置某个canvas整体的透明度;此函数直接改变画布本身,对已经绘制的内容也生效
如果仅想对接下来的绘制生效请使用setAlpha", - "!type": "fn(name: string|CanvasRenderingContext2D, opacity: number)" - }, - "getTextContentHeight": { - "!doc": "获得某段文字的预计绘制高度;参数说明详见 drawTextContent", - "!type": "fn(content: string, config?: ?)" - }, - "drawArrow": { - "!doc": "在某个canvas上绘制一个箭头", - "!type": "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)" - }, - "strokeEllipse": { - "!doc": "在某个canvas上绘制一个椭圆的边框", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string, lineWidth?: number)" - }, - "fillCircle": { - "!doc": "在某个canvas上绘制一个圆", - "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, style?: string)" - }, - "strokeRoundRect": { - "!doc": "在某个canvas上绘制一个圆角矩形的边框", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, lineWidth?: number, angle?: number)" - }, - "getContextByName": { - "!doc": "根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。
也可以传画布的context自身,则返回自己。", - "!type": "fn(canvas: string|CanvasRenderingContext2D) -> CanvasRenderingContext2D" - }, - "drawImage": { - "!doc": "在一个画布上绘制图片
后面的8个坐标参数与canvas的drawImage的八个参数完全相同。
name: 可以是系统画布之一,也可以是任意自定义动态创建的画布名 画布名称或者画布的context
image: 要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取;支持加':x',':y',':o'翻转),图片本身,或者一个画布。
angle:旋转角度", - "!url": "http://www.w3school.com.cn/html5/canvas_drawimage.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, image: string|image, x: number, y: number, w?: number, h?: number, x1?: number, y1?: number, w1?: number, h1?: number, angle?: number)" - }, - "drawTip": { - "!doc": "左上角绘制一段提示
text: 要提示的字符串,支持${}语法
id: 要绘制的图标ID
frame: 要绘制该图标的第几帧", - "!type": "fn(text: string, id?: string, frame?: number)" - }, - "drawBackground": { - "!doc": "绘制一个背景图,可绘制winskin或纯色背景;支持小箭头绘制", - "!type": "fn(left: string, top: string, right: string, bottom: string, posInfo?: {px: number, py: number, direction: string})" - }, - "fillEllipse": { - "!doc": "在某个canvas上绘制一个椭圆", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string)" - }, - "setFillStyle": { - "!doc": "设置某个canvas的绘制属性(如颜色等)", - "!url": "https://www.w3school.com.cn/tags/canvas_fillstyle.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" - }, - "drawText": { - "!doc": "地图中间绘制一段文字", - "!type": "fn(contents: string, callback?: fn())" - }, - "drawConfirmBox": { - "!doc": "绘制一个确认框
此项会打断事件流,如需不打断版本的请使用core.myconfirm()
text: 要绘制的内容,支持 ${} 语法
yesCallback: 点击确认后的回调
noCallback: 点击取消后的回调", - "!type": "fn(text: string, yesCallback?: fn(), noCallback?: fn())" - }, - "drawUIEventSelector": { - "!doc": "自绘一个闪烁的选择光标
code: 选择光标的编号,必填
background: 要绘制的光标背景,必须是一个合法的WindowSkin
x, y, w, h: 绘制的坐标和长宽
z: 可选,光标的的z值", - "!type": "fn(code: number, background: string, x: number, y: number, w: number, h: number, z?: number)" - }, - "clearUIEventSelector": { - "!doc": "清除若干个自绘的选择光标
codes: 清除的光标编号;可以是单个编号或编号数组;不填则清除所有光标", - "!type": "fn(codes?: number|[number])" - }, - "fillPolygon": { - "!doc": "在某个canvas上绘制一个多边形", - "!type": "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string)" - }, - "fillText": { - "!doc": "在某个画布上绘制一段文字
text: 要绘制的文本
style: 绘制的样式
font: 绘制的字体
最大宽度,超过此宽度会自动放缩", - "!url": "https://www.w3school.com.cn/tags/canvas_filltext.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, font?: string, maxWidth?: number)" - }, - "setTextBaseline": { - "!doc": "设置某个canvas的基准线
baseline: 可为alphabetic, top, hanging, middle, ideographic, bottom", - "!url": "https://www.w3school.com.cn/tags/canvas_textbaseline.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, baseline: string)" - }, - "loadCanvas": { - "!doc": "加载某个canvas状态", - "!type": "fn(name: string|CanvasRenderingContext2D)" - }, - "splitLines": { - "!doc": "字符串自动换行的分割", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth?: number, font?: string)" - }, - "setAlpha": { - "!doc": "设置某个canvas接下来绘制的不透明度;不会影响已经绘制的内容
返回设置之前画布的不透明度
如果需要修改画布本身的不透明度请使用setOpacity", - "!url": "https://www.w3school.com.cn/tags/canvas_globalalpha.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, alpha: number) -> number" - }, - "setFilter": { - "!doc": "设置某个canvas接下来绘制的filter", - "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" - }, - "setLineWidth": { - "!doc": "设置某个canvas的线宽度", - "!url": "https://www.w3school.com.cn/tags/canvas_linewidth.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, lineWidth: number)" - }, - "drawTextBox": { - "!doc": "绘制一个对话框", - "!type": "fn(content: string, showAll?: bool)" - }, - "relocateCanvas": { - "!doc": "重新定位一个自定义画布", - "!type": "fn(name: string, x: number, y: number, useDelta: bool)" - }, - "rotateCanvas": { - "!doc": "设置一个自定义画布的旋转角度
centerX, centerY: 旋转中心(以屏幕像素为基准);不填视为图片正中心。", - "!type": "fn(name: string, angle: number, centerX?: number, centerY?: number)" - }, - "closePanel": { - "!doc": "结束一切事件和绘制,关闭UI窗口,返回游戏进程", - "!type": "fn()" - }, - "textImage": { - "!doc": "文本图片化", - "!type": "fn(content: string, lineHeight?: number) -> image" - }, - "drawStatusBar": { - "!doc": "绘制状态栏", - "!type": "fn()" - }, - "setStrokeStyle": { - "!doc": "设置某个canvas边框属性", - "!url": "https://www.w3school.com.cn/tags/canvas_strokestyle.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" - }, - "clearUI": { - "!doc": "清空UI层内容", - "!type": "fn()" - }, - "drawWindowSkin": { - "!doc": "绘制WindowSkin", - "!type": "fn(background: string, ctx: string|CanvasRenderingContext2D, x: number, y: number, w: string, h: string, direction?: string, px?: number, py?: number)" - }, - "fillRect": { - "!doc": "绘制一个矩形。
x,y: 绘制的坐标
width,height: 绘制的长宽
style: 绘制的样式
angle: 旋转的角度,弧度制,如Math.PI/2代表90度", - "!url": "https://www.w3school.com.cn/tags/canvas_fillrect.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, angle?: number)" - }, - "drawScrollText": { - "!doc": "绘制滚动字幕", - "!type": "fn(content: string, time: number, lineHeight?: number, callback?: fn())" - }, - "strokePolygon": { - "!doc": "在某个canvas上绘制一个多边形的边框", - "!type": "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string, lineWidth?: number)" - }, - "strokeCircle": { - "!doc": "在某个canvas上绘制一个圆的边框", - "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: ?, style?: string, lineWidth?: number)" - }, - "drawWaiting": { - "!doc": "绘制等待界面", - "!type": "fn(text: string)" - }, - "setFont": { - "!doc": "设置某个canvas的文字字体", - "!url": "https://www.w3school.com.cn/tags/canvas_font.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, font: string)" - }, - "drawChoices": { - "!doc": "绘制一个选项界面", - "!type": "fn(content?: string, choices?: [?], width?: number, ctx?: string|CanvasRenderingContext2D)" - }, - "setFontForMaxWidth": { - "!doc": "根据最大宽度自动缩小字体", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth: number, font?: ?) -> string" - }, - "clearMap": { - "!doc": "清空某个画布图层
name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。
如果name也可以是'all',若为all则为清空所有系统画布。", - "!url": "https://www.w3school.com.cn/tags/canvas_clearrect.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x?: number, y?: number, width?: number, height?: number)" - }, - "drawTextContent": { - "!doc": "绘制一段文字到某个画布上面
ctx: 要绘制到的画布
content: 要绘制的内容;转义字符不允许保留 \\t, \\b 和 \\f
config: 绘制配置项,目前暂时包含如下内容(均为可选)
left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右
fontSize:字体大小;lineHeight:行高;time:打字机间隔;font:字体名
返回值:绘制信息", - "!type": "fn(ctx: string|CanvasRenderingContext2D, content: string, config: ?)" - }, - "calWidth": { - "!doc": "计算某段文字的宽度", - "!url": "https://www.w3school.com.cn/tags/canvas_measuretext.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, font?: string) -> number" - }, - "fillArc": { - "!doc": "在某个canvas上绘制一个扇形", - "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string)" - }, - "strokeArc": { - "!doc": "在某个canvas上绘制一段弧", - "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string, lineWidth?: number)" - }, - "drawLine": { - "!doc": "在某个canvas上绘制一条线", - "!url": "https://www.w3school.com.cn/tags/canvas_lineto.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)" - }, - "drawPagination": { - "!doc": "绘制分页", - "!type": "fn(page?: ?, totalPage?: ?, y?: number)" - }, - "getToolboxItems": { - "!doc": "获得所有应该在道具栏显示的某个类型道具", - "!type": "fn(cls: string) -> [string]" - }, - "strokeRect": { - "!doc": "绘制一个矩形的边框
style: 绘制的样式
lineWidth: 线宽
angle: 旋转角度,弧度制,如Math.PI/2为90度", - "!url": "https://www.w3school.com.cn/tags/canvas_strokerect.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, lineWidth?: number, angle?: number)" - }, - "drawBook": { - "!doc": "绘制怪物手册", - "!type": "fn(index?: ?)" - }, - "fillRoundRect": { - "!doc": "在某个canvas上绘制一个圆角矩形", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, angle?: number)" - }, - "fillBoldText": { - "!doc": "在某个画布上绘制一个描边文字
text: 要绘制的文本
style: 绘制的样式
strokeStyle: 要绘制的描边颜色
font: 绘制的字体
maxWidth: 最大宽度,超过此宽度会自动放缩", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, strokeStyle?: string, font?: string, maxWidth?: number)" - }, - "saveCanvas": { - "!doc": "保存某个canvas状态", - "!type": "fn(name: string|CanvasRenderingContext2D)" - }, - "createCanvas": { - "!doc": "动态创建一个画布。
name: 要创建的画布名,如果已存在则会直接取用当前存在的。
x,y: 创建的画布相对窗口左上角的像素坐标
width,height: 创建的长宽。
zIndex: 创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。
返回创建的画布的context,也可以通过core.dymCanvas[name]调用。", - "!type": "fn(name: string, x: number, y: number, width: number, height: number, zIndex: number) -> CanvasRenderingContext2D" - }, - "setTextAlign": { - "!doc": "设置某个canvas的对齐", - "!url": "https://www.w3school.com.cn/tags/canvas_textalign.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, align: string)" - }, - }, - "enemys": { - "!doc": "定义了一系列和怪物相关的API函数。", - "getEnemys": { - "!doc": "获得所有怪物原始数据的一个副本。
请使用core.material.enemys获得当前各项怪物属性。", - "!type": "fn()" - }, - "getEnemyValue": { - "!doc": "获得某个点上怪物的某个属性值", - "!type": "fn(enemy?: string|enemy, name: string, x?: number, y?: number, floorId?: string)" - }, - "getSpecials": { - "!doc": "获得所有特殊属性的定义", - "!type": "fn() -> [[?]]" - }, - "getSpecialColor": { - "!doc": "获得某个怪物所有特殊属性的颜色", - "!type": "fn(enemy: string|enemy) -> [string]" - }, - "getSpecialFlag": { - "!doc": "获得某个怪物所有特殊属性的额外标记。

例如,1为全图性技能,需要进行遍历全图(光环/支援等)", - "!type": "fn(enemy: string|enemy) -> number" - }, - "getSpecialHint": { - "!doc": "获得某种敌人的某种特殊属性的介绍
例如:core.getSpecialHint('bat', 1) // '先攻:怪物首先攻击'
enemy: 敌人id或敌人对象,用于确定属性的具体数值,否则可选
special: 属性编号,可以是该敌人没有的属性
返回值:属性的介绍,以属性名加中文冒号开头", - "!type": "fn(enemy: string|enemy, special: number) -> string" - }, - "getSpecialText": { - "!doc": "获得某种敌人的全部特殊属性名称
例如:core.getSpecialText('greenSlime') // ['先攻', '3连击', '破甲', '反击']
enemy: 敌人id或敌人对象,如core.material.enemys.greenSlime
返回值:字符串数组", - "!type": "fn(enemy: string|enemy) -> [string]" - }, - "hasSpecial": { - "!doc": "判定某种特殊属性的有无
例如:core.hasSpecial('greenSlime', 1) // 判定绿头怪有无先攻属性
special: 敌人id或敌人对象或正整数数组或自然数
test: 待检查的属性编号
", - "!type": "fn(special: number|[number]|string|number, test: number) -> bool" - }, - "nextCriticals": { - "!doc": "获得某只敌人接下来的若干个临界及其减伤,算法基于useLoop开关选择回合法或二分法
例如:core.nextCriticals('greenSlime', 9, 0, 0, 'MT0') // 绿头怪接下来的9个临界
enemy: 敌人id或敌人对象
number: 要计算的临界数量,可选,默认为1
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回:两列的二维数组,每行表示一个临界及其减伤", - "!type": "fn(enemy: string|enemy, number?: number, x?: number, y?: number, floorId?: string) -> [[number]]" - }, - "getDefDamage": { - "!doc": "计算再加若干点防御能使某只敌人对主角的总伤害降低多少
例如:core.getDefDamage('greenSlime', 10, 0, 0, 'MT0') // 再加10点防御能使绿头怪的伤害降低多少
enemy: 敌人id或敌人对象
k: 假设主角增加的防御力,可选,默认为1
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选", - "!type": "fn(enemy: string|enemy, k?: number, x?: number, y?: number, floorId?: string) -> number" - }, - "canBattle": { - "!doc": "判定主角当前能否打败某只敌人
例如:core.canBattle('greenSlime',0,0,'MT0') // 能否打败主塔0层左上角的绿头怪(假设有)
enemy: 敌人id或敌人对象
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:true表示可以打败,false表示无法打败", - "!type": "fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> bool" - }, - "getEnemyInfo": { - "!doc": "获得怪物真实属性
hero: 可选,此时的勇士属性
此函数将会计算包括坚固、模仿、光环等若干效果,将同时被怪物手册和伤害计算调用", - "!type": "fn(enemy: string|enemy, hero?: ?, x?: number, y?: number, floorId?: string) -> {hp: number, atk: number, def: number, money: number, exp: number, special: [number], point: number, guards: [?]}" - }, - "getDamageInfo": { - "!doc": "获得战斗伤害信息
例如:core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害
enemy: 敌人id或敌人对象
hero: 可选,此时的勇士属性
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:伤害计算信息,如果因为没有破防或无敌怪等其他原因无法战斗,则返回null", - "!type": "fn(enemy: string|enemy, hero?: ?, x?: number, y?: number, floorId?: string) -> {damage: number, per_damage: number, hero_per_damage: number, init_damage: number, mon_hp: number, mon_atk: number, mon_def: number, turn: number}" - }, - "getDamage": { - "!doc": "获得某只敌人对主角的总伤害
例如:core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害
enemy: 敌人id或敌人对象
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:总伤害,如果因为没有破防或无敌怪等其他原因无法战斗,则返回null", - "!type": "fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> number" - }, - "getDamageString": { - "!doc": "获得某只敌人的地图显伤,包括颜色
例如:core.getDamageString('greenSlime', 0, 0, 'MT0') // 绿头怪的地图显伤
enemy: 敌人id或敌人对象
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
floorId: 敌人所在的地图,可选
返回值:damage: 表示伤害值或为'???',color: 形如'#RrGgBb'", - "!type": "fn(enemy: string|enemy, x?: number, y?: number, floorId?: string) -> {color: string, damage: string}" - }, - "getCurrentEnemys": { - "!doc": "获得某张地图的敌人集合,用于手册绘制
例如:core.getCurrentEnemys('MT0') // 主塔0层的敌人集合
floorId: 地图id,可选
返回值:敌人集合,按伤害升序排列,支持多朝向怪合并", - "!type": "fn(floorId?: string) -> [enemy]" - }, - "hasEnemyLeft": { - "!doc": "检查某些楼层是否还有漏打的(某种)敌人
例如:core.hasEnemyLeft('greenSlime', ['sample0', 'sample1']) // 样板0层和1层是否有漏打的绿头怪
enemyId: 敌人id,可选,null表示任意敌人
floorId: 地图id或其数组,可选,不填为当前地图
返回值:地图中是否还存在该种敌人", - "!type": "fn(enemyId?: string, floorId?: string|[string]) -> bool" - } - }, - "events": { - "!doc": "events.js将处理所有和事件相关的操作,主要分为五个部分:
- 游戏的开始和结束
- 系统事件的处理
- 自定义事件的处理
- 点击状态栏图标所进行的操作
- 一些具体事件的执行内容", - "afterChangeFloor": { - "!doc": "转换楼层结束的事件", - "!type": "fn(floorId?: string)" - }, - "popEventLoc": { - "!doc": "将当前点坐标入栈", - "!type": "fn()" - }, - "afterOpenDoor": { - "!doc": "开一个门后触发的事件", - "!type": "fn(doorId?: string, x?: number, y?: number)" - }, - "checkLvUp": { - "!doc": "检查升级事件", - "!type": "fn()" - }, - "insertAction": { - "!doc": "插入一段事件;此项不可插入公共事件,请用 core.insertCommonEvent
例如:core.insertAction('一段文字'); // 插入一个显示文章
action: 单个事件指令,或事件指令数组
x: 新的当前点横坐标,可选
y: 新的当前点纵坐标,可选
callback: 新的回调函数,可选
addToLast: 插入的位置,true表示插入到末尾,否则插入到开头", - "!type": "fn(action: string|?|[?], x?: number, y?: number, callback?: fn(), addToLast?: bool)" - }, - "unfollow": { - "!doc": "取消跟随
name: 取消跟随的行走图,不填则取消全部跟随者", - "!type": "fn(name?: string)" - }, - "hasVisitedFloor": { - "!doc": "是否到达过某个楼层", - "!type": "fn(floorId?: string) -> bool" - }, - "startEvents": { - "!doc": "开始执行一系列自定义事件", - "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())" - }, - "setHeroIcon": { - "!doc": "更改主角行走图
例如:core.setHeroIcon('npc48.png', true); // 把主角从阳光变成样板0层左下角的小姐姐,但不立即刷新
name: 新的行走图文件名,可以是全塔属性中映射前的中文名。映射后会被存入core.status.hero.image
noDraw: true表示不立即刷新(刷新会导致大地图下视野重置到以主角为中心)", - "!type": "fn(name: string, noDraw?: bool)" - }, - "changingFloor": { - "!doc": "楼层转换中", - "!type": "fn(floorId?: string, heroLoc?: {x: number, y: number, direction: string})" - }, - "setEvents": { - "!doc": "直接设置事件列表", - "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())" - }, - "setValue": { - "!doc": "数值操作", - "!type": "fn(name: string, operator: string, value: ?, prefix?: string)" - }, - "precompile": { - "!doc": "预编辑事件", - "!type": "fn(data?: ?)" - }, - "vibrate": { - "!doc": "视野抖动
例如:core.vibrate(); // 视野抖动1秒
direction: 抖动方向;可填 horizontal(左右),vertical(上下),diagonal1(左上右下),diagonal2(左下右上)
time: 抖动时长
speed: 抖动速度
power: 抖动幅度
callback: 抖动平息后的回调函数,可选", - "!type": "fn(direction?: string, time?: number, speed?: number, power?: number, callback?: fn())" - }, - "confirmRestart": { - "!doc": "询问是否需要重新开始", - "!type": "fn()" - }, - "battle": { - "!doc": "战斗,如果填写了坐标就会删除该点的敌人并触发战后事件
例如:core.battle('greenSlime'); // 和从天而降的绿头怪战斗(如果打得过)
id: 敌人id,必填
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
force: true表示强制战斗,可选
callback: 回调函数,可选", - "!type": "fn(id: string, x?: number, y?: number, force?: bool, callback?: fn())" - }, - "follow": { - "!doc": "跟随
name: 要跟随的一个合法的4x4的行走图名称,需要在全塔属性注册", - "!type": "fn(name: string)" - }, - "beforeBattle": { - "!doc": "战斗前触发的事件;返回false代表不进行战斗", - "!type": "fn(enemyId?: string, x?: number, y?: number) -> bool" - }, - "registerEvent": { - "!doc": "注册一个自定义事件
type: 事件类型
func: 事件的处理函数,可接受(data, x, y, prefix)参数
data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀", - "!type": "fn(type: string, func: fn(data: ?, x?: number, y?: number, prefix?: string))" - }, - "flyTo": { - "!doc": "飞往某一层", - "!type": "fn(toId?: string, callback?: fn()) -> bool" - }, - "afterGetItem": { - "!doc": "获得一个道具后的事件", - "!type": "fn(id?: string, x?: number, y?: number, isGentleClick?: bool)" - }, - "doAction": { - "!doc": "执行下一个事件指令,常作为回调
例如:core.setCurtain([0,0,0,1], null, null, core.doAction); // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果", - "!type": "fn()" - }, - "openBook": { - "!doc": "点击怪物手册时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "save": { - "!doc": "点击存档按钮时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "load": { - "!doc": "点击读档按钮时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "getNextItem": { - "!doc": "轻按获得面前的物品或周围唯一物品
noRoute: 若为true则不计入录像", - "!type": "fn(noRoute?: bool)" - }, - "hasAsync": { - "!doc": "当前是否有未处理完毕的异步事件(不包含动画和音效)", - "!type": "fn() -> bool" - }, - "stopAsync": { - "!doc": "立刻停止所有正在进行的异步事件", - "!type": "fn()" - }, - "openEquipbox": { - "!doc": "点击装备栏时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "recoverEvents": { - "!doc": "恢复一个事件", - "!type": "fn(data?: ?)" - }, - "setGlobalFlag": { - "!doc": "设置一个系统开关
例如:core.setGlobalFlag('steelDoorWithoutKey', true); // 使全塔的所有铁门都不再需要钥匙就能打开
name: 系统开关的英文名
value: 开关的新值,您可以用!core.flags[name]简单地表示将此开关反转", - "!type": "fn(name: string, value: bool)" - }, - "moveImage": { - "!doc": "移动一张图片并/或改变其透明度
例如:core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明
code: 图片编号
to: 新的左上角坐标,省略表示原地改变透明度
opacityVal: 新的透明度,省略表示不变
time: 移动用时,单位为毫秒。不填视为1秒
callback: 图片移动完毕后的回调函数,可选", - "!type": "fn(code: number, to?: [number], opacityVal?: number, moveMode?: string, time?: number, callback?: fn())" - }, - "rotateImage": { - "!doc": "旋转一张图片
code: 图片编号
center: 旋转中心像素坐标(以屏幕为基准);不填视为图片本身中心
angle: 旋转角度;正数为顺时针,负数为逆时针
moveMode: 旋转模式
time: 旋转用时,单位为毫秒。不填视为1秒
callback: 图片旋转完毕后的回调函数,可选", - "!type": "fn(code: number, center?: [number], angle?: number, moveMode?: string, time?: number, callback?: fn())" - }, - "scaleImage": { - "!doc": "放缩一张图片", - "!type": "fn(code: number, center?: [number], scale?: number, moveMode?: string, time?: number, callback?: fn())" - }, - "moveTextBox": { - "!doc": "移动对话框", - "!type": "fn(code: number, loc: [number], relative?: bool, moveMode?: string, time?: number, callback?: fn())" - }, - "clearTextBox": { - "!doc": "清除对话框", - "!type": "fn(code: number)" - }, - "openSettings": { - "!doc": "点击设置按钮时的操作", - "!type": "fn(fromUserAction?: bool)" - }, - "afterPushBox": { - "!doc": "推箱子后的事件", - "!type": "fn()" - }, - "unregisterSystemEvent": { - "!doc": "注销一个系统事件", - "!type": "fn(type: string)" - }, - "trigger": { - "!doc": "触发(x,y)点的系统事件;会执行该点图块的script属性,同时支持战斗(会触发战后)、道具(会触发道具后)、楼层切换等等
callback: 执行完毕的回调函数
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(x?: number, y?: number, callback?: fn())" - }, - "restart": { - "!doc": "重新开始游戏;此函数将回到标题页面", - "!type": "fn()" - }, - "doEvent": { - "!doc": "执行一个自定义事件", - "!type": "fn(data?: ?, x?: number, y?: number, prefix?: string)" - }, - "win": { - "!doc": "游戏获胜事件", - "!type": "fn(reason?: string, norank?: bool, noexit?: bool)" - }, - "setGlobalAttribute": { - "!doc": "设置全塔属性", - "!type": "fn(name: string, value: string)" - }, - "setNameMap": { - "!doc": "设置文件别名", - "!type": "fn(name: string, value?: string)" - }, - "setTextAttribute": { - "!doc": "设置剧情文本的属性", - "!type": "fn(data: ?)" - }, - "openToolbox": { - "!doc": "点击工具栏时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "setVolume": { - "!doc": "调节bgm的音量
例如:core.setVolume(0, 100, core.jumpHero); // 0.1秒内淡出bgm,然后主角原地跳跃半秒
value: 新的音量,为0或不大于1的正数。注意系统设置中是这个值的平方根的十倍
time: 渐变用时,单位为毫秒。不填或小于100毫秒都视为0
callback: 渐变完成后的回调函数,可选", - "!type": "fn(value: number, time?: number, callback?: fn())" - }, - "pushEventLoc": { - "!doc": "将当前点坐标入栈", - "!type": "fn(x?: number, y?: number, floorId?: string) -> bool" - }, - "openKeyBoard": { - "!doc": "点击虚拟键盘时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "insertCommonEvent": { - "!doc": "插入一个公共事件
例如:core.insertCommonEvent('加点事件', [3]);
name: 公共事件名;如果公共事件不存在则直接忽略
args: 参数列表,为一个数组,将依次赋值给 flag:arg1, flag:arg2, ...
x: 新的当前点横坐标,可选
y: 新的当前点纵坐标,可选
callback: 新的回调函数,可选
addToLast: 插入的位置,true表示插入到末尾,否则插入到开头", - "!type": "fn(name?: string, args?: [?], x?: number, y?: number, callback?: fn(), addToLast?: bool)" - }, - "hideImage": { - "!doc": "隐藏一张图片
例如:core.hideImage(1, 1000, core.jumpHero); // 1秒内淡出1号图片,然后主角原地跳跃半秒
code: 图片编号
time: 淡出时间,单位为毫秒
callback: 图片完全消失后的回调函数,可选", - "!type": "fn(code: number, time?: number, callback?: fn())" - }, - "visitFloor": { - "!doc": "到达某楼层", - "!type": "fn(floorId?: string)" - }, - "openQuickShop": { - "!doc": "点击快捷商店按钮时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "afterBattle": { - "!doc": "战斗结束后触发的事件", - "!type": "fn(enemyId?: string, x?: number, y?: number)" - }, - "pushBox": { - "!doc": "推箱子", - "!type": "fn(data?: ?)" - }, - "autoEventExecuted": { - "!doc": "当前是否执行过某个自动事件", - "!type": "fn(symbol?: string, value?: ?) -> bool" - }, - "onSki": { - "!doc": "当前是否在冰上", - "!type": "fn(number?: number) -> bool" - }, - "showImage": { - "!doc": "显示一张图片
例如:core.showImage(1, core.material.images.images['winskin.png'], [0,0,128,128], [0,0,416,416], 0.5, 1000); // 裁剪winskin.png的最左边128×128px,放大到铺满整个视野,1秒内淡入到50%透明,编号为1
code: 图片编号,为不大于50的正整数,加上100后就是对应画布层的z值,较大的会遮罩较小的,注意色调层的z值为125,UI层为140
image: 图片文件名(可以是全塔属性中映射前的中文名)或图片对象(见上面的例子)
sloc: 一行且至多四列的数组,表示从原图裁剪的左上角坐标和宽高,可选
loc: 一行且至多四列的数组,表示图片在视野中的左上角坐标和宽高,可选
opacityVal: 不透明度,为小于1的正数。不填视为1
time: 淡入时间,单位为毫秒。不填视为0
callback: 图片完全显示出来后的回调函数,可选", - "!type": "fn(code: number, image: string|image, sloc?: [number], loc?: [number], opacityVal?: number, time?: number, callback?: fn())" - }, - "getItem": { - "!doc": "获得道具并提示,如果填写了坐标就会删除该点的该道具
例如:core.getItem('book'); // 获得敌人手册并提示
id: 道具id,必填
num: 获得的数量,不填视为1,填了就别填坐标了
x: 道具的横坐标,可选
y: 道具的纵坐标,可选
callback: 回调函数,可选", - "!type": "fn(id: string, num?: number, x?: number, y?: number, callback?: fn())" - }, - "registerSystemEvent": { - "!doc": "注册一个系统事件
type: 事件名
func: 为事件的处理函数,可接受(data,callback)参数", - "!type": "fn(type: string, func: fn(data?: ?, callback?: fn()))" - }, - "startGame": { - "!doc": "开始新游戏
例如:core.startGame('咸鱼乱撞', 0, ''); // 开始一局咸鱼乱撞难度的新游戏,随机种子为0
hard: 难度名,会显示在左下角(横屏)或右下角(竖屏)
seed: 随机种子,相同的种子保证了录像的可重复性
route: 经由base64压缩后的录像,用于从头开始的录像回放
callback: 回调函数,可选", - "!type": "fn(hard: string, seed: number, route: string, callback?: fn())" - }, - "doSystemEvent": { - "!doc": "执行一个系统事件", - "!type": "fn(type: string, data?: ?, callback?: fn())" - }, - "resetGame": { - "!doc": "初始化游戏", - "!type": "fn(hero?: ?, hard?: ?, floorId?: string, maps?: ?, values?: ?)" - }, - "setFloorInfo": { - "!doc": "设置一项楼层属性并刷新状态栏
例如:core.setFloorInfo('ratio', 2, 'MT0'); // 把主塔0层的血瓶和宝石变为双倍效果
name: 要修改的属性名
values: 属性的新值。
floorId: 楼层id,不填视为当前层
prefix: 独立开关前缀,一般不需要", - "!type": "fn(name: string, values: ?, floorId?: string, prefix?: string)" - }, - "openDoor": { - "!doc": "开门(包括三种基础墙)
例如:core.openDoor(0, 0, true, core.jumpHero); // 打开左上角的门,需要钥匙,然后主角原地跳跃半秒
x: 门的横坐标
y: 门的纵坐标
needKey: true表示需要钥匙,会导致机关门打不开
callback: 门完全打开后或打不开时的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(x: number, y: number, needKey?: bool, callback?: fn())" - }, - "setEnemy": { - "!doc": "设置一项敌人属性并计入存档
例如:core.setEnemy('greenSlime', 'def', 0); // 把绿头怪的防御设为0
id: 敌人id
name: 属性的英文缩写
value: 属性的新值,可选
operator: 运算操作符如+=,可选
prefix: 独立开关前缀,一般不需要,下同", - "!type": "fn(id: string, name: string, value: ?, operator?: string, prefix?: string)" - }, - "setEnemyOnPoint": { - "!doc": "设置某个点的敌人属性。如果该点不是怪物,则忽略此函数。
例如:core.setEnemyOnPoint(3, 5, null, 'atk', 100, '+='); // 仅将(3,5)点怪物的攻击力加100。", - "!type": "fn(x: number, y: number, floorId?: string, name: string, value: ?, operator?: string, prefix?: string)" - }, - "resetEnemyOnPoint": { - "!doc": "重置某个点的怪物属性", - "!type": "fn(x: number, y: number, floorId?: string)" - }, - "moveEnemyOnPoint": { - "!doc": "将某个点已经设置的敌人属性移动到其他点", - "!type": "fn(fromX: number, fromY: number, toX: number, toY: number, floorId?: string)" - }, - "autoEventExecuting": { - "!doc": "当前是否在执行某个自动事件", - "!type": "fn(symbol?: string, value?: ?) -> bool" - }, - "checkAutoEvents": { - "!doc": "检测自动事件", - "!type": "fn()" - }, - "showGif": { - "!doc": "绘制一张动图或擦除所有动图
例如:core.showGif(); // 擦除所有动图
name: 动图文件名,可以是全塔属性中映射前的中文名
x: 动图在视野中的左上角横坐标
y: 动图在视野中的左上角纵坐标", - "!type": "fn(name?: string, x?: number, y?: number)" - }, - "unregisterEvent": { - "!doc": "注销一个自定义事件", - "!type": "fn(type: string)" - }, - "jumpHero": { - "!doc": "主角跳跃,跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。
例如:core.jumpHero(); // 主角原地跳跃半秒
ex: 跳跃后的横坐标
ey: 跳跃后的纵坐标
time: 跳跃时长,单位为毫秒。不填视为半秒
callback: 跳跃完毕后的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(ex?: number, ey?: number, time?: number, callback?: fn())" - }, - "closeDoor": { - "!doc": "关门,目标点必须为空地
例如:core.closeDoor(0, 0, 'yellowWall', core.jumpHero); // 在左上角关掉一堵黄墙,然后主角原地跳跃半秒
x: 横坐标
y: 纵坐标
id: 门的id,也可以用三种基础墙
callback: 门完全关上后的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(x: number, y: number, id: string, callback?: fn())" - }, - "eventMoveHero": { - "!doc": "强制移动主角(包括后退),这个函数的作者已经看不懂这个函数了
例如:core.eventMoveHero(['forward'], 125, core.jumpHero); // 主角强制前进一步,用时1/8秒,然后主角原地跳跃半秒
steps: 步伐数组,注意后退时跟随者的行为会很难看
time: 每步的用时,单位为毫秒。0或不填则取主角的移速,如果后者也不存在就取0.1秒
callback: 移动完毕后的回调函数,可选
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(steps: [step], time?: number, callback?: fn())" - }, - "changeFloor": { - "!doc": "场景切换
例如:core.changeFloor('MT0'); // 传送到主塔0层,主角坐标和朝向不变,黑屏时间取用户定义的值
floorId: 传送的目标地图id,可以填':before'和':next'分别表示楼下或楼上
stair: 传送的位置
heroLoc: 传送的坐标;会覆盖stair
time: 传送的黑屏时间,单位为毫秒;不填为用户设置值
callback: 传送的回调函数
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(floorId: string, stair?: string, heroLoc?: {x?: number, y?: number, direction?: string}, time?: number, callback?: fn())" - }, - "getCommonEvent": { - "!doc": "获得一个公共事件", - "!type": "fn(name: string) -> [?]" - }, - "lose": { - "!doc": "游戏失败事件", - "!type": "fn(reason?: string)" - }, - "gameOver": { - "!doc": "游戏结束
例如:core.gameOver(); // 游戏失败
ending: 结局名,省略表示失败
fromReplay: true表示在播放录像,可选
norank: true表示不计入榜单,可选", - "!type": "fn(ending?: string, fromReplay?: bool, norank?: bool)" - }, - "useFly": { - "!doc": "点击楼层传送器时的打开操作", - "!type": "fn(fromUserAction?: bool)" - }, - "tryUseItem": { - "!doc": "尝试使用一个道具
例如:core.tryUseItem('pickaxe'); // 尝试使用破墙镐
itemId: 道具id,其中敌人手册、传送器和飞行器会被特殊处理", - "!type": "fn(itemId: string)" - } - }, - "plugin": { - "!doc": "插件编写中内置了一些常用的插件。", - "drawLight": { - "!doc": "绘制一段灯光效果
name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建
color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。
lights:可选,一个数组,定义了每个独立的灯光。其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。
lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。
例如:core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。
core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。", - "!type": "fn(name: string|CanvasRenderingContext2D, color?: number, lights?: [[number]], lightDec?: number)" - }, - "openShop": { - "!doc": "打开一个全局商店
shopId: 要开启的商店ID
noRoute: 打开行为是否不计入录像", - "!type": "fn(shopId: string, noRoute?: bool)" - }, - "isShopVisited": { - "!doc": "某个全局商店是否被访问过", - "!type": "fn(id: string) -> bool" - }, - "listShopIds": { - "!doc": "列出所有应当显示的快捷商店列表", - "!type": "fn() -> [string]" - }, - "canOpenShop": { - "!doc": "当前能否打开某个商店", - "!type": "fn(id: string) -> bool" - }, - "setShopVisited": { - "!doc": "设置某个商店的访问状态", - "!type": "fn(id: string, visited?: bool)" - }, - "canUseQuickShop": { - "!doc": "当前能否使用某个快捷商店
如果返回一个字符串,则代表不能,返回的字符串作为不能的提示;返回null表示可以使用", - "!type": "fn(id: string) -> string" - }, - "removeMaps": { - "!doc": "删除某一些楼层;删除后不会存入存档,不可浏览地图也不可飞到。
fromId: 开始删除的楼层ID
toId: 删除到的楼层编号;可选,不填则视为fromId
例如:core.removeMaps(\"MT1\", \"MT300\") 删除MT1~MT300之间的全部层
core.removeMaps(\"MT10\") 只删除MT10层", - "!type": "fn(fromId: string, toId?: string)" - }, - "resumeMaps": { - "!doc": "恢复某一些被删除楼层。
fromId: 开始恢复的楼层ID
toId: 恢复到的楼层编号;可选,不填则视为fromId
例如:core.resumeMaps(\"MT1\", \"MT300\") 恢复MT1~MT300之间的全部层
core.resumeMaps(\"MT10\") 只删恢复MT10层", - "!type": "fn(fromId: string, toId?: string)" - }, - "autoRemoveMaps": { - "!doc": "根据楼层分区信息自动砍层与恢复", - "!type": "fn(floorId: string)" - }, - "openItemShop": { - "!doc": "打开一个道具商店", - "!type": "fn(itemShopId: string)" - } - } - }, - "lzw_encode": { - "!doc": "LZW压缩算法", - "!url": "https://gist.github.com/revolunet/843889", - "!type": "fn(s: string) -> string" - }, - "lzw_decode": { - "!doc": "LZW解压缩算法", - "!url": "https://gist.github.com/revolunet/843889", - "!type": "fn(s: string) -> string" - }, - "hero": { - "!type": "heroStatus", - "!doc": "勇士信息,为 core.status.hero 的简写", - }, - "flags": { - "!type": "flag", - "!doc": "游戏中用到的变量,为 core.status.hero.flags 的简写", - } - } -]; \ No newline at end of file +]; diff --git a/public/_server/MotaAction.g4 b/public/_server/MotaAction.g4 index bcbbbe9..04bccf0 100644 --- a/public/_server/MotaAction.g4 +++ b/public/_server/MotaAction.g4 @@ -3850,7 +3850,7 @@ isShopVisited_e /* isShopVisited_e default : ['shop1'] allShops : ['IdString_0'] -var code = 'core.isShopVisited(\'' + IdString_0 + '\')'; +var code = 'core.plugin.shop.isShopVisited(\'' + IdString_0 + '\')'; return [code, Blockly.JavaScript.ORDER_ATOMIC]; */; diff --git a/public/_server/table/plugins.comment.js b/public/_server/table/plugins.comment.js index 8929557..960b1cc 100644 --- a/public/_server/table/plugins.comment.js +++ b/public/_server/table/plugins.comment.js @@ -13,74 +13,8 @@ var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_leaf": true, "_type": "textarea", "_range": "typeof(thiseval)=='string'", - "_data": "自定义插件" + "_data": "双击查看插件详情" }, - "shop": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string'", - "_data": "全局商店" - }, - "drawLight": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "灯光效果" - }, - "removeMap": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "砍层插件" - }, - "fiveLayers": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "五图层(背景前景2)" - }, - "itemShop": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "道具商店" - }, - "enemyLevel": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "手册显示怪物境界" - }, - "dynamicHp": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "动态血量变化" - }, - "multiHeros": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "多角色" - }, - "itemCategory": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "物品分类插件" - }, - "heroFourFrames": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "勇士四帧行走动画" - }, - "startCanvas": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "自绘标题界面居中" - } } if (obj[key]) return obj[key]; return { diff --git a/public/libs/actions.js b/public/libs/actions.js index 9db5262..5adc24a 100644 --- a/public/libs/actions.js +++ b/public/libs/actions.js @@ -1623,7 +1623,7 @@ actions.prototype._keyUpViewMaps = function (keycode) { ////// 快捷商店界面时的点击操作 ////// actions.prototype._clickQuickShop = function (x, y) { - var shopIds = core.listShopIds(); + var shopIds = core.plugin.shop.listShopIds(); if (this._out(x)) return; var topIndex = this._HY_ - @@ -1631,15 +1631,15 @@ actions.prototype._clickQuickShop = function (x, y) { (core.status.event.ui.offset || 0); if (y >= topIndex && y < topIndex + shopIds.length) { var shopId = shopIds[y - topIndex]; - if (!core.canOpenShop(shopId)) { + if (!core.plugin.shop.canOpenShop(shopId)) { core.playSound('操作失败'); core.drawTip('当前项尚未开启'); return; } - var message = core.canUseQuickShop(shopId); + var message = core.plugin.shop.canUseQuickShop(shopId); if (message == null) { // core.ui.closePanel(); - core.openShop(shopIds[y - topIndex], false); + core.plugin.shop.openShop(shopIds[y - topIndex], false); } else { core.playSound('操作失败'); core.drawTip(message); @@ -1660,7 +1660,7 @@ actions.prototype._keyUpQuickShop = function (keycode) { return; } this._selectChoices( - core.listShopIds().length + 1, + core.plugin.shop.listShopIds().length + 1, keycode, this._clickQuickShop ); diff --git a/public/libs/control.js b/public/libs/control.js index 754c6ad..e2ab60f 100644 --- a/public/libs/control.js +++ b/public/libs/control.js @@ -1499,7 +1499,7 @@ control.prototype._checkBlock_disableQuickShop = function () { // 禁用快捷商店 if (core.flags.disableShopOnDamage) { Object.keys(core.status.shops).forEach(function (shopId) { - core.setShopVisited(shopId, false); + core.plugin.shop.setShopVisited(shopId, false); }); } }; @@ -1695,7 +1695,7 @@ control.prototype.drawDamage = function (ctx) { control.prototype._drawDamage_draw = function (ctx, onMap) { if (!core.hasItem('book')) return; - core.drawHalo(ctx, onMap); + core.plugin.halo.drawHalo(ctx, onMap); core.setFont(ctx, "14px 'normal'"); core.setTextAlign(ctx, 'left'); @@ -3058,7 +3058,7 @@ control.prototype.getRealStatus = function (name) { ////// 从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 ////// control.prototype.getRealStatusOrDefault = function (status, name) { - return core.getHeroStatusOf(status, name); + return core.plugin.hero.getHeroStatusOf(status, name); }; ////// 获得勇士原始属性(无装备和衰弱影响) ////// diff --git a/public/libs/core.js b/public/libs/core.js index 757c450..71412ea 100644 --- a/public/libs/core.js +++ b/public/libs/core.js @@ -6,11 +6,6 @@ 'use strict'; -// /** -// * @type {CoreMixin} -// */ -// const core = (() => { - function core() { this._WIDTH_ = 15; this._HEIGHT_ = 15; @@ -613,6 +608,24 @@ core.prototype._init_plugins = function () { } } + if (!main.pluginUseCompress) { + (async function () { + const pluginList = main.plugin; + for await (const one of pluginList) { + const script = document.createElement('script'); + script.src = `project/plugin/${one}.js`; + document.body.appendChild(script); + await new Promise(res => { + script.addEventListener('load', res); + }); + } + })(); + } else { + const script = document.createElement('script'); + script.src = `project/plugin.min.js`; + document.body.appendChild(script); + } + core._forwardFunc('plugin'); if (!main.replayChecking && main.mode === 'play') { main.forward(); @@ -682,7 +695,4 @@ core.prototype.doFunc = function (func, _this) { return func.apply(_this, Array.prototype.slice.call(arguments, 2)); }; -// return new Core(); - -// })(); var core = new core(); diff --git a/public/libs/events.js b/public/libs/events.js index b64715b..7ee8920 100644 --- a/public/libs/events.js +++ b/public/libs/events.js @@ -2206,13 +2206,13 @@ events.prototype._action_unloadEquip = function (data, x, y, prefix) { }; events.prototype._action_openShop = function (data, x, y, prefix) { - core.setShopVisited(data.id, true); - if (data.open) core.openShop(data.id, true); + core.plugin.shop.setShopVisited(data.id, true); + if (data.open) core.plugin.shop.openShop(data.id, true); core.doAction(); }; events.prototype._action_disableShop = function (data, x, y, prefix) { - core.setShopVisited(data.id, false); + core.plugin.shop.setShopVisited(data.id, false); core.doAction(); }; @@ -3436,18 +3436,18 @@ 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) return; - if (!core.canOpenShop(shopId)) { + if (!core.plugin.shop.canOpenShop(shopId)) { core.playSound('操作失败'); core.drawTip('当前无法打开快捷商店!'); return; } - var message = core.canUseQuickShop(shopId); + var message = core.plugin.shop.canUseQuickShop(shopId); if (message != null) { core.playSound('操作失败'); core.drawTip(message); return; } - core.openShop(shopId, false); + core.plugin.shop.openShop(shopId, false); return; } diff --git a/public/libs/ui.js b/public/libs/ui.js index 716dd67..054682d 100644 --- a/public/libs/ui.js +++ b/public/libs/ui.js @@ -3101,11 +3101,11 @@ ui.prototype._drawNotes = function () { ui.prototype._drawQuickShop = function () { core.status.event.id = 'selectShop'; var shopList = core.status.shops, - keys = core.listShopIds(); + keys = core.plugin.shop.listShopIds(); var choices = keys.map(function (shopId) { return { text: shopList[shopId].textInList, - color: core.isShopVisited(shopId) ? null : '#999999' + color: core.plugin.shop.isShopVisited(shopId) ? null : '#999999' }; }); choices.push('返回游戏'); diff --git a/public/project/data.js b/public/project/data.js index 37fb00f..d5018f6 100644 --- a/public/project/data.js +++ b/public/project/data.js @@ -314,7 +314,25 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "font": "normal" }, "splitImages": [], - "plugin": [] + "plugin": [ + "utils", + "ui", + "shop", + "study", + "hero", + "fiveLayer", + "loopMap", + "removeMap", + "heroFourFrames", + "itemDetail", + "skills", + "towerBoss", + "popup", + "hotReload", + "replay", + "skillTree", + "halo" + ] }, "firstData": { "title": "人类:开天辟地", diff --git a/public/project/floors/MT0.js b/public/project/floors/MT0.js index 85d318b..c871c8f 100644 --- a/public/project/floors/MT0.js +++ b/public/project/floors/MT0.js @@ -35,7 +35,7 @@ main.floors.MT0= "\r[red]注意!!!\r[]该塔新增了很多新的功能,同时对样板的ui进行了大幅度的改动,操作也有改变,由于内容过多,这里不再一一描述,具体请在道具栏查看百科全书!!百科全书是在你面前的几个道具中的其中一个", { "type": "function", - "function": "function(){\ncore.showChapter('序章 起源');\n}" + "function": "function(){\ncore.plugin.gameUi.showChapter('序章 起源');\n}" } ], "parallelDo": "", diff --git a/public/project/floors/MT16.js b/public/project/floors/MT16.js index bfbcb8b..312da97 100644 --- a/public/project/floors/MT16.js +++ b/public/project/floors/MT16.js @@ -136,7 +136,7 @@ main.floors.MT16= }, { "type": "function", - "function": "function(){\ncore.autoFixRouteBoss(true);\n}" + "function": "function(){\ncore.plugin.towerBoss.autoFixRouteBoss(true);\n}" } ] }, @@ -413,7 +413,7 @@ main.floors.MT16= "这里是漏怪检测,将会检测\r[gold]洞穴、山路、山脚、平原\r[white]地区的怪物是否清完", { "type": "function", - "function": "function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(5, 17));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" + "function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(5, 17));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" }, { "type": "loadBgm", diff --git a/public/project/floors/MT21.js b/public/project/floors/MT21.js index da2d73d..720679e 100644 --- a/public/project/floors/MT21.js +++ b/public/project/floors/MT21.js @@ -118,7 +118,7 @@ main.floors.MT21 = { { type: 'function', function: - "function(){\ncore.showChapter('第二章 智慧');\ncore.removeMaps('tower1', 'tower7', true);\ndelete flags.tower1;\ndelete flags.wordsTimeOut;\ndelete flags.boom;\ndelete flags.booming;\n}" + "function(){\ncore.plugin.gameUi.showChapter('第二章 智慧');\ncore.plugin.removeMap.removeMaps('tower1', 'tower7', true);\ndelete flags.tower1;\ndelete flags.wordsTimeOut;\ndelete flags.boom;\ndelete flags.booming;\n}" }, { type: 'setValue', diff --git a/public/project/floors/MT31.js b/public/project/floors/MT31.js index 05145ec..249b3ef 100644 --- a/public/project/floors/MT31.js +++ b/public/project/floors/MT31.js @@ -119,7 +119,7 @@ main.floors.MT31 = { { type: 'function', function: - 'function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(17, 22));\nif (enemy.length === 0) {\n\tcore.insertAction([\'当前无剩余怪物!\', { "type": "hide", "remove": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}' + 'function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(17, 22));\nif (enemy.length === 0) {\n\tcore.insertAction([\'当前无剩余怪物!\', { "type": "hide", "remove": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}' } ] }, diff --git a/public/project/floors/MT32.js b/public/project/floors/MT32.js index d147a65..e293761 100644 --- a/public/project/floors/MT32.js +++ b/public/project/floors/MT32.js @@ -16,7 +16,7 @@ main.floors.MT32= "firstArrive": [ { "type": "function", - "function": "function(){\ncore.removeMaps('MT17', 'MT21', true)\n}" + "function": "function(){\ncore.plugin.removeMap.removeMaps('MT17', 'MT21', true)\n}" } ], "eachArrive": [], diff --git a/public/project/floors/MT35.js b/public/project/floors/MT35.js index 075ae77..1a32956 100644 --- a/public/project/floors/MT35.js +++ b/public/project/floors/MT35.js @@ -76,7 +76,7 @@ main.floors.MT35= "这里是漏怪检测,会检测\r[gold]智慧小径\r[]区域是否有遗漏怪物", { "type": "function", - "function": "function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(30, 40));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" + "function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(30, 40));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" } ], "7,0": [ @@ -101,7 +101,7 @@ main.floors.MT35= }, { "type": "function", - "function": "function(){\ncore.removeMaps('MT22', 'MT31', true);\n}" + "function": "function(){\ncore.plugin.removeMap.removeMaps('MT22', 'MT31', true);\n}" }, { "type": "changeFloor", diff --git a/public/project/floors/MT5.js b/public/project/floors/MT5.js index 5b282c7..a4eba57 100644 --- a/public/project/floors/MT5.js +++ b/public/project/floors/MT5.js @@ -120,7 +120,7 @@ main.floors.MT5= "这里是漏怪检测,会检测\r[gold]山洞\r[]区域的怪物是否清空", { "type": "function", - "function": "function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(0, 5));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" + "function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(0, 5));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" } ] }, diff --git a/public/project/floors/MT6.js b/public/project/floors/MT6.js index 8d4395f..e498cf8 100644 --- a/public/project/floors/MT6.js +++ b/public/project/floors/MT6.js @@ -63,7 +63,7 @@ main.floors.MT6= "\t[原始人]\b[down,hero]感觉好像可以学习一些简单的东西了。", { "type": "function", - "function": "function(){\ncore.showChapter('第一章 勇气');\n}" + "function": "function(){\ncore.plugin.gameUi.showChapter('第一章 勇气');\n}" }, { "type": "setValue", diff --git a/public/project/floors/tower5.js b/public/project/floors/tower5.js index 69a4371..419fada 100644 --- a/public/project/floors/tower5.js +++ b/public/project/floors/tower5.js @@ -21,7 +21,7 @@ main.floors.tower5= "这里是漏怪检测,会检测\r[gold]智慧之塔\r[]区域是否有遗漏怪物", { "type": "function", - "function": "function(){\nconst enemy = core.getRemainEnemyString([\"tower1\", \"tower2\", \"tower3\", \"tower4\", \"tower5\", \"tower6\"]);\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" + "function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString([\"tower1\", \"tower2\", \"tower3\", \"tower4\", \"tower5\", \"tower6\"]);\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}" } ] }, diff --git a/public/project/floors/tower7.js b/public/project/floors/tower7.js index 2932eee..8cfd80b 100644 --- a/public/project/floors/tower7.js +++ b/public/project/floors/tower7.js @@ -157,7 +157,7 @@ main.floors.tower7= }, { "type": "function", - "function": "function(){\ncore.initTowerBoss();\n}" + "function": "function(){\ncore.plugin.towerBoss.initTowerBoss();\n}" } ], "eachArrive": [], diff --git a/public/project/functions.js b/public/project/functions.js index 8c01593..135701a 100644 --- a/public/project/functions.js +++ b/public/project/functions.js @@ -120,6 +120,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 正在切换楼层过程中执行的操作;此函数的执行时间是“屏幕完全变黑“的那一刻 // floorId为要切换到的楼层ID;heroLoc表示勇士切换到的位置 + const { checkLoopMap } = core.plugin.loopMap; + flags.floorChanging = true; // ---------- 此时还没有进行切换,当前floorId还是原来的 ---------- // @@ -136,7 +138,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { } // 根据分区信息自动砍层与恢复 - if (core.autoRemoveMaps) core.autoRemoveMaps(floorId); + if (core.plugin.removeMap.autoRemoveMaps) + core.plugin.removeMap.autoRemoveMaps(floorId); // 重置画布尺寸 core.maps.resizeMap(floorId); @@ -177,7 +180,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { if (weather) core.setWeather(weather[0], weather[1]); else core.setWeather(); - core.checkLoopMap(); + checkLoopMap(); // ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等 }, @@ -365,8 +368,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { flags[`night_${floorId}`] += enemy.day; } - if (core.getSkillLevel(11) > 0) { - core.declineStudiedSkill(); + if (core.plugin.skillTree.getSkillLevel(11) > 0) { + core.plugin.study.declineStudiedSkill(); } // 如果是融化怪,需要特殊标记一下 @@ -618,7 +621,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { core.formatBigNumber( Math.max( (enemy.value || 0) - - core.getHeroStatusOn('def') + core.plugin.hero.getHeroStatusOn('def') ) ) + '点伤害' @@ -740,7 +743,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { def: hero_def, mdef: hero_mdef, hp: hero_hp - } = core.getHeroStatusOf( + } = core.plugin.hero.getHeroStatusOf( hero, ['atk', 'def', 'mdef', 'hp'], hero?.x, @@ -919,6 +922,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 后面三个参数主要是可以在光环等效果上可以适用 floorId = floorId || core.status.floorId; + const { backDir } = core.plugin.utils; + // 勇士位置应该在这里进行计算,四个位置依次遍历,去重 let toMap = []; if ( @@ -947,12 +952,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { } if ( core.noPass(nx, ny) || - !core.canMoveHero(nx, ny, core.backDir(dir), floorId) + !core.canMoveHero(nx, ny, backDir(dir), floorId) ) { continue; } const toGet = ['atk', 'def']; - const status = core.getHeroStatusOf( + const status = core.plugin.hero.getHeroStatusOf( hero, toGet, nx, @@ -982,13 +987,14 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { def: mon_def, special: mon_special } = enemyInfo; - let { atk: hero_atk, def: hero_def } = core.getHeroStatusOf( - hero, - ['atk', 'def'], - x, - y, - floorId - ); + let { atk: hero_atk, def: hero_def } = + core.plugin.hero.getHeroStatusOf( + hero, + ['atk', 'def'], + x, + y, + floorId + ); let hero_hp = core.getRealStatusOrDefault(hero, 'hp'), hero_IQ = core.getRealStatusOrDefault(hero, 'mdef'), @@ -1277,7 +1283,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { !core.status.floorId.startsWith('tower') && flags.skill2 ) { - core.jumpSkill(); + core.plugin.skillEffects.jumpSkill(); core.status.route.push('key:50'); // 将按键记在录像中 } else { if (core.hasItem('pickaxe')) { @@ -1332,7 +1338,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { version: core.firstData.version, guid: core.getGuid(), time: new Date().getTime(), - skills: core.saveSkillTree() + skills: core.plugin.skillTree.saveSkillTree() }; return data; @@ -1381,7 +1387,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { core.setFlag('__fromLoad__', true); // TODO:增加自己的一些读档处理 - core.loadSkillTree(data.skills); + core.plugin.skillTree.loadSkillTree(data.skills); // 切换到对应的楼层 core.changeFloor(data.floorId, null, data.hero.loc, 0, function () { @@ -1452,7 +1458,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 已学习的技能 if ( - core.getSkillLevel(11) > 0 && + core.plugin.skillTree.getSkillLevel(11) > 0 && (core.status.hero.special?.num ?? []).length > 0 ) { core.plugin.showStudiedSkill.value = true; @@ -1733,6 +1739,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 2, 将楼层属性中的cannotMoveDirectly这个开关勾上,即禁止在该层楼使用瞬移。 // 3. 将flag:cannotMoveDirectly置为true,即可使用flag控制在某段剧情范围内禁止瞬移。 + const { checkLoopMap } = core.plugin.loopMap; + // 增加步数 core.status.hero.steps++; // 更新跟随者状态,并绘制 @@ -1779,7 +1787,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { ); } - core.checkLoopMap(); + checkLoopMap(); // 追猎 if (core.status.checkBlock.haveHunt) { diff --git a/public/project/items.js b/public/project/items.js index 5b208c9..0abaab4 100644 --- a/public/project/items.js +++ b/public/project/items.js @@ -1,1368 +1,1285 @@ -var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = -{ - "yellowKey": { - "cls": "tools", - "name": "黄钥匙", - "text": "可以打开一扇黄门", - "hideInToolbox": true - }, - "blueKey": { - "cls": "tools", - "name": "蓝钥匙", - "text": "可以打开一扇蓝门", - "hideInToolbox": true - }, - "redKey": { - "cls": "tools", - "name": "红钥匙", - "text": "可以打开一扇红门", - "hideInToolbox": true - }, - "redGem": { - "cls": "items", - "name": "小红宝石", - "text": "攻击+${core.values.redGem}", - "itemEffect": "core.status.hero.atk += 1 * core.status.thisMap.ratio", - "itemEffectTip": ",攻击+${1 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.atk += core.values.redGem", - "canUseItemEffect": "true" - }, - "blueGem": { - "cls": "items", - "name": "小蓝宝石", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 1 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${1 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "greenGem": { - "cls": "items", - "name": "小绿宝石", - "text": ",护盾+${core.values.greenGem}", - "itemEffect": "core.status.hero.mdef += 20 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)", - "itemEffectTip": ",智慧+${20 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)}", - "useItemEffect": "core.status.hero.mdef += core.values.greenGem", - "canUseItemEffect": "true" - }, - "yellowGem": { - "cls": "items", - "name": "黄宝石", - "text": "可以进行加点", - "itemEffect": "core.status.hero.hp+=1000;core.status.hero.atk+=6;core.status.hero.def+=6;core.status.hero.mdef+=10;", - "itemEffectTip": ",全属性提升", - "useItemEvent": [ - { - "type": "choices", - "choices": [ - { - "text": "攻击+1", - "action": [ - { - "type": "setValue", - "name": "status:atk", - "operator": "+=", - "value": "1" - } - ] - }, - { - "text": "防御+2", - "action": [ - { - "type": "setValue", - "name": "status:def", - "operator": "+=", - "value": "2" - } - ] - }, - { - "text": "生命+200", - "action": [ - { - "type": "setValue", - "name": "status:hp", - "operator": "+=", - "value": "200" - } - ] - } - ] - } - ], - "canUseItemEffect": "true" - }, - "redPotion": { - "cls": "items", - "name": "红血瓶", - "text": ",生命+${core.values.redPotion}", - "itemEffect": "core.status.hero.hp += 100 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${100 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.redPotion", - "canUseItemEffect": "true" - }, - "bluePotion": { - "cls": "items", - "name": "蓝血瓶", - "text": ",生命+${core.values.bluePotion}", - "itemEffect": "core.status.hero.hp += 200 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${200 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.bluePotion", - "canUseItemEffect": "true" - }, - "yellowPotion": { - "cls": "items", - "name": "黄血瓶", - "text": ",生命+${core.values.yellowPotion}", - "itemEffect": "core.status.hero.hp += 400 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${400 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.yellowPotion", - "canUseItemEffect": "true" - }, - "greenPotion": { - "cls": "items", - "name": "绿血瓶", - "text": ",生命+${core.values.greenPotion}", - "itemEffect": "core.status.hero.hp += 800 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${800 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.greenPotion", - "canUseItemEffect": "true" - }, - "sword0": { - "cls": "items", - "name": "破旧的剑", - "text": "一把已经生锈的剑", - "equip": { - "type": 0, - "animate": "sword", - "value": { - "atk": 0 - } - }, - "itemEffect": "core.status.hero.atk += 0", - "itemEffectTip": ",攻击+0" - }, - "sword1": { - "cls": "equips", - "name": "智慧之剑", - "text": "借助曾经闯入此塔的智慧挑战者的智慧锻造而成,攻击+15,额外攻击+10", - "equip": { - "type": "武器", - "animate": "jianji", - "value": { - "atk": 15, - "mana": 10 - }, - "percentage": {} - }, - "itemEffect": "", - "itemEffectTip": ",攻击+10" - }, - "sword2": { - "cls": "equips", - "name": "铁剑", - "text": "真的是一把很普通的银剑,哦不,铁剑。攻击+180,额外攻击+50", - "equip": { - "type": 0, - "animate": "sword", - "value": { - "mana": 50, - "atk": 180 - }, - "percentage": {} - }, - "itemEffect": "core.status.hero.atk += 20", - "itemEffectTip": ",攻击+20" - }, - "sword3": { - "cls": "items", - "name": "骑士剑", - "text": "一把很普通的骑士剑", - "equip": { - "type": 0, - "animate": "sword", - "value": { - "atk": 40 - } - }, - "itemEffect": "core.status.hero.atk += 40", - "itemEffectTip": ",攻击+40" - }, - "sword4": { - "cls": "items", - "name": "圣剑", - "text": "一把很普通的圣剑", - "equip": { - "type": 0, - "animate": "sword", - "value": { - "atk": 80 - } - }, - "itemEffect": "core.status.hero.atk += 80", - "itemEffectTip": ",攻击+80" - }, - "sword5": { - "cls": "items", - "name": "神圣剑", - "text": "一把很普通的神圣剑", - "equip": { - "type": 0, - "animate": "sword", - "value": { - "atk": 160 - } - }, - "itemEffect": "core.status.hero.atk += 100", - "itemEffectTip": ",攻击+100" - }, - "shield0": { - "cls": "items", - "name": "破旧的盾", - "text": "一个很破旧的铁盾", - "equip": { - "type": 1, - "value": { - "def": 0 - } - }, - "itemEffect": "core.status.hero.def += 0", - "itemEffectTip": ",防御+0" - }, - "shield1": { - "cls": "equips", - "name": "智慧之盾", - "text": "借助曾经闯入此塔的智慧挑战者的智慧锻造而成,防御+15,生命回复+15", - "equip": { - "type": "盾牌", - "value": { - "hpmax": 15, - "def": 15 - }, - "percentage": {} - }, - "itemEffect": "", - "itemEffectTip": ",防御+10" - }, - "shield2": { - "cls": "equips", - "name": "铁盾", - "text": "一个真的很普通的铁盾,这次没错了,就是铁盾!防御+120,生命回复+30", - "equip": { - "type": 1, - "value": { - "hpmax": 30, - "def": 120 - }, - "percentage": {} - }, - "itemEffect": "core.status.hero.def += 20", - "itemEffectTip": ",防御+20" - }, - "shield3": { - "cls": "items", - "name": "骑士盾", - "text": "一个很普通的骑士盾", - "equip": { - "type": 1, - "value": { - "def": 40 - } - }, - "itemEffect": "core.status.hero.def += 40", - "itemEffectTip": ",防御+40" - }, - "shield4": { - "cls": "items", - "name": "圣盾", - "text": "一个很普通的圣盾", - "equip": { - "type": 1, - "value": { - "def": 80 - } - }, - "itemEffect": "core.status.hero.def += 80", - "itemEffectTip": ",防御+80" - }, - "shield5": { - "cls": "items", - "name": "神圣盾", - "text": "一个很普通的神圣盾", - "equip": { - "type": 1, - "value": { - "def": 100, - "mdef": 100 - } - }, - "itemEffect": "core.status.hero.def += 100;core.status.hero.mdef += 100", - "itemEffectTip": ",防御+100,护盾+100" - }, - "superPotion": { - "cls": "items", - "name": "圣水", - "itemEffect": "core.status.hero.hp *= 2", - "itemEffectTip": ",生命值翻倍", - "useItemEffect": "core.status.hero.hp *= 2;", - "canUseItemEffect": "true", - "text": "生命值翻倍" - }, - "silverCoin": { - "cls": "items", - "name": "银币", - "itemEffect": "core.status.hero.money += 500", - "itemEffectTip": ",金币+500" - }, - "book": { - "cls": "constants", - "name": "怪物手册", - "text": "可以查看当前楼层各怪物属性", - "hideInToolbox": true, - "useItemEffect": "core.ui.drawBook(0);", - "canUseItemEffect": "true" - }, - "fly": { - "cls": "constants", - "name": "楼层传送器", - "text": "可以自由往来去过的楼层", - "hideInReplay": true, - "hideInToolbox": true, - "useItemEffect": "core.ui.drawFly();", - "canUseItemEffect": "(function () {\n\treturn core.status.maps[core.status.floorId].canFlyFrom;\n})();" - }, - "coin": { - "cls": "constants", - "name": "幸运金币", - "text": "持有时打败怪物可得双倍金币" - }, - "freezeBadge": { - "cls": "constants", - "name": "冰冻徽章", - "text": "可以将面前的熔岩变成平地", - "useItemEffect": "(function () {\n\tvar success = false;\n\n\tvar snowFourDirections = false; // 是否四方向雪花;如果是将其改成true\n\tif (snowFourDirections) {\n\t\t// 四方向雪花\n\t\tfor (var direction in core.utils.scan) {\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})();", - "canUseItemEffect": "true" - }, - "cross": { - "cls": "constants", - "name": "查看技能", - "text": "查看勇士的技能", - "canUseItemEffect": true, - "useItemEffect": "core.openSkill();" - }, - "dagger": { - "cls": "constants", - "name": "屠龙匕首", - "text": "该道具尚未被定义" - }, - "amulet": { - "cls": "constants", - "name": "护符", - "text": "持有时无视负面地形" - }, - "bigKey": { - "cls": "tools", - "name": "大黄门钥匙", - "text": "可以开启当前层所有黄门", - "itemEffect": "core.addItem('yellowKey', 1);\ncore.addItem('blueKey', 1);\ncore.addItem('redKey', 1);", - "itemEffectTip": ",全钥匙+1", - "useItemEffect": "(function () {\n\tvar actions = core.searchBlock(\"yellowDoor\").map(function (block) {\n\t\treturn { \"type\": \"openDoor\", \"loc\": [block.x, block.y], \"async\": true };\n\t});\n\tactions.push({ \"type\": \"waitAsync\" });\n\tactions.push({ \"type\": \"tip\", \"text\": core.material.items[itemId].name + \"使用成功\" });\n\tcore.insertAction(actions);\n})();", - "canUseItemEffect": "(function () {\n\treturn core.searchBlock('yellowDoor').length > 0;\n})();" - }, - "greenKey": { - "cls": "tools", - "name": "绿钥匙", - "text": "可以打开一扇绿门" - }, - "steelKey": { - "cls": "tools", - "name": "铁门钥匙", - "text": "可以打开一扇铁门" - }, - "pickaxe": { - "cls": "tools", - "name": "破墙镐", - "text": "可以破坏勇士面前的墙", - "useItemEffect": "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable) return false;\n\t\treturn block.event.canBreak;\n\t};\n\n\tvar success = false;\n\tvar pickaxeFourDirections = false; // 是否四方向破;如果是将其改成true\n\tif (pickaxeFourDirections) {\n\t\t// 四方向破\n\t\tfor (var direction in core.utils.scan) {\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})();", - "canUseItemEffect": "true" - }, - "icePickaxe": { - "cls": "tools", - "name": "破冰镐", - "text": "可以破坏勇士面前的一堵冰墙", - "useItemEffect": "(function () {\n\tcore.removeBlock(core.nextX(), core.nextY());\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", - "canUseItemEffect": "(function () {\n\treturn core.getBlockId(core.nextX(), core.nextY()) == 'ice';\n})();" - }, - "bomb": { - "cls": "tools", - "name": "炸弹", - "text": "可以炸掉勇士面前的怪物", - "useItemEffect": "(function () {\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable || block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tvar 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})();", - "canUseItemEffect": "true" - }, - "centerFly": { - "cls": "constants", - "name": "快捷键查看器", - "text": "可以查看本塔里面的所有快捷键", - "useItemEffect": "", - "canUseItemEffect": "true", - "useItemEvent": [ - "8:打开定点查看界面,如果开启了定点查看代替怪物手册,也可以按X打开定点查看界面\nJ:打开技能树界面\n1:开关断灭之刃技能\n2:使用跳跃技能/破墙镐", - "平面楼传界面:\n上下左右:移动地图\n,:前移10层\n.:后移10层\nPgUp:上楼\nPgDn:下楼\nB:显隐地图名\nZ:开关3D模式", - "浏览地图界面(2.8.1新增):\nG:传送至该地图", - "手机端:点击右下角难度可以切换至数字键盘" - ] - }, - "upFly": { - "cls": "tools", - "name": "上楼器", - "text": "可以飞往楼上的相同位置", - "useItemEffect": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) + 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();", - "canUseItemEffect": "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index < core.floorIds.length - 1) {\n\t\tvar toId = core.floorIds[index + 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();" - }, - "downFly": { - "cls": "tools", - "name": "下楼器", - "text": "可以飞往楼下的相同位置", - "useItemEffect": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) - 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();", - "canUseItemEffect": "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index > 0) {\n\t\tvar toId = core.floorIds[index - 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();" - }, - "earthquake": { - "cls": "tools", - "name": "地震卷轴", - "text": "可以破坏当前层的所有墙", - "useItemEffect": "(function () {\n\tvar indexes = [];\n\tfor (var index in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[index];\n\t\tif (!block.disable && block.event.canBreak) {\n\t\t\tindexes.push(index);\n\t\t}\n\t}\n\tcore.removeBlockByIndexes(indexes);\n\tcore.drawMap();\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", - "canUseItemEffect": "(function () {\n\treturn core.status.thisMap.blocks.filter(function (block) {\n\t\treturn !block.disable && block.event.canBreak;\n\t}).length > 0;\n})();" - }, - "poisonWine": { - "cls": "tools", - "name": "解毒药水", - "text": "可以解除中毒状态", - "useItemEffect": "", - "canUseItemEffect": "core.hasFlag('poison');" - }, - "weakWine": { - "cls": "tools", - "name": "解衰药水", - "text": "可以解除衰弱状态", - "useItemEffect": "", - "canUseItemEffect": "core.hasFlag('weak');" - }, - "curseWine": { - "cls": "tools", - "name": "解咒药水", - "text": "可以解除诅咒状态", - "useItemEffect": "", - "canUseItemEffect": "core.hasFlag('curse');" - }, - "superWine": { - "cls": "tools", - "name": "万能药水", - "text": "可以解除所有不良状态", - "useItemEffect": "", - "canUseItemEffect": "(function() {\n\treturn core.hasFlag('poison') || core.hasFlag('weak') || core.hasFlag('curse');\n})();" - }, - "hammer": { - "cls": "tools", - "name": "圣锤", - "text": "该道具尚未被定义" - }, - "lifeWand": { - "cls": "tools", - "name": "生命魔杖", - "text": "可以恢复100点生命值", - "useItemEvent": [ - { - "type": "comment", - "text": "先恢复一个魔杖(因为使用道具必须消耗一个)" - }, - { - "type": "function", - "function": "function(){\ncore.addItem('lifeWand', 1);\n}" - }, - { - "type": "input", - "text": "请输入生命魔杖使用次数:(0-${item:lifeWand})" - }, - { - "type": "if", - "condition": "flag:input<=item:lifeWand", - "true": [ - { - "type": "setValue", - "name": "item:lifeWand", - "operator": "-=", - "value": "flag:input" - }, - { - "type": "setValue", - "name": "status:hp", - "operator": "+=", - "value": "flag:input*100" - }, - "成功使用${flag:input}次生命魔杖,恢复${flag:input*100}点生命。" - ], - "false": [ - "输入不合法!" - ] - } - ], - "canUseItemEffect": "true" - }, - "jumpShoes": { - "cls": "tools", - "name": "跳跃靴", - "text": "能跳跃到前方两格处", - "useItemEffect": "core.playSound(\"jump.mp3\"); core.insertAction({ \"type\": \"jumpHero\", \"loc\": [core.nextX(2), core.nextY(2)] });", - "canUseItemEffect": "(function () {\n\tvar nx = core.nextX(2),\n\t\tny = core.nextY(2);\n\treturn nx >= 0 && nx < core.bigmap.width && ny >= 0 && ny < core.bigmap.height && core.getBlockId(nx, ny) == null;\n})();" - }, - "skill1": { - "cls": "constants", - "name": "技能树", - "text": "打开技能树", - "hideInReplay": true, - "useItemEffect": "core.openTree();", - "canUseItemEffect": "true" - }, - "wand": { - "cls": "constants", - "name": "定点查看", - "text": "可以定点查看怪物属性", - "canUseItemEffect": true, - "useItemEffect": "core.openFixed();" - }, - "I319": { - "cls": "items", - "name": "新物品" - }, - "I320": { - "cls": "items", - "name": "新物品" - }, - "I321": { - "cls": "items", - "name": "新物品" - }, - "I322": { - "cls": "constants", - "name": "快捷键查看器", - "text": "可以查看本塔里面的所有快捷键", - "useItemEffect": "", - "canUseItemEffect": "true", - "useItemEvent": [ - "8:打开定点查看界面,如果开启了定点查看代替怪物手册,也可以按X打开定点查看界面\nJ:打开技能树界面\n1:开关断灭之刃技能\n2:使用跳跃技能/破墙镐", - "平面楼传界面:\n上下左右:移动地图\n,:前移10层\n.:后移10层\nPgUp:上楼\nPgDn:下楼\nB:显隐地图名\nZ:开关3D模式", - "浏览地图界面(2.8.1新增):\nG:传送至该地图", - "手机端:点击右下角难度可以切换至数字键盘" - ] - }, - "I323": { - "cls": "items", - "name": "新物品" - }, - "I324": { - "cls": "items", - "name": "新物品" - }, - "I325": { - "cls": "items", - "name": "新物品" - }, - "I326": { - "cls": "items", - "name": "新物品" - }, - "I327": { - "cls": "items", - "name": "新物品" - }, - "I328": { - "cls": "items", - "name": "新物品" - }, - "I329": { - "cls": "items", - "name": "新物品" - }, - "I330": { - "cls": "constants", - "name": "系统设置", - "text": "可以修改一些本塔的设置", - "canUseItemEffect": true, - "useItemEvent": [ - { - "type": "while", - "condition": "true", - "data": [ - { - "type": "choices", - "text": "系统设置", - "choices": [ - { - "text": "显示血瓶宝石详细信息", - "action": [ - { - "type": "if", - "condition": "flag:itemDetail", - "true": [ - { - "type": "setValue", - "name": "flag:itemDetail", - "value": "false" - }, - { - "type": "function", - "function": "function(){\ncore.getItemDetail();\n}" - }, - "已关闭显示血瓶宝石详细信息" - ], - "false": [ - { - "type": "setValue", - "name": "flag:itemDetail", - "value": "true" - }, - { - "type": "function", - "function": "function(){\ncore.getItemDetail();\n}" - }, - "已显示血瓶宝石详细信息" - ] - } - ] - }, - { - "text": "定点查看代替怪物手册", - "action": [ - { - "type": "if", - "condition": "flag:fixToBook", - "true": [ - { - "type": "setValue", - "name": "flag:fixToBook", - "value": "false" - }, - "现在按X为怪物手册,按8为定点查看" - ], - "false": [ - { - "type": "setValue", - "name": "flag:fixToBook", - "value": "true" - }, - "现在按X或8均可使用定点查看" - ] - } - ] - }, - { - "text": "启用小地图", - "action": [ - { - "type": "if", - "condition": "flag:__useMinimap__", - "true": [ - { - "type": "setValue", - "name": "flag:__useMinimap__", - "value": "false" - }, - "现在已关闭小地图,切换楼层后即可不再绘制" - ], - "false": [ - { - "type": "setValue", - "name": "flag:__useMinimap__", - "value": "true" - }, - "现在已启用小地图,切换楼层之后即可显示" - ] - } - ] - }, - { - "text": "启用带地图的楼传", - "action": [ - { - "type": "if", - "condition": "flag:usePlatFly", - "true": [ - { - "type": "setValue", - "name": "flag:usePlatFly", - "value": "false" - }, - "现在楼传变为原版楼传" - ], - "false": [ - { - "type": "setValue", - "name": "flag:usePlatFly", - "value": "true" - }, - "现在楼传界面将绘制小地图" - ] - } - ] - }, - { - "text": "退出", - "action": [ - { - "type": "exit" - } - ] - } - ] - } - ] - } - ] - }, - "I376": { - "cls": "items", - "name": "中红宝石", - "text": "攻击+${core.values.redGem}", - "itemEffect": "core.status.hero.atk += 2 * core.status.thisMap.ratio", - "itemEffectTip": ",攻击+${2 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.atk += core.values.redGem", - "canUseItemEffect": "true" - }, - "I377": { - "cls": "items", - "name": "新物品" - }, - "I378": { - "cls": "items", - "name": "中蓝宝石", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 2 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${2 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "I379": { - "cls": "items", - "name": "新物品" - }, - "I380": { - "cls": "items", - "name": "新物品" - }, - "I381": { - "cls": "items", - "name": "中绿宝石", - "text": ",护盾+${core.values.greenGem}", - "itemEffect": "core.status.hero.mdef += 40 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)", - "itemEffectTip": ",智慧+${40 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)}", - "useItemEffect": "core.status.hero.mdef += core.values.greenGem", - "canUseItemEffect": "true" - }, - "I382": { - "cls": "items", - "name": "新物品" - }, - "I383": { - "cls": "items", - "name": "新物品" - }, - "I384": { - "cls": "items", - "name": "新物品" - }, - "I385": { - "cls": "items", - "name": "新物品" - }, - "I386": { - "cls": "items", - "name": "新物品" - }, - "I387": { - "cls": "items", - "name": "新物品" - }, - "I388": { - "cls": "items", - "name": "新物品" - }, - "I389": { - "cls": "items", - "name": "新物品" - }, - "I390": { - "cls": "items", - "name": "大红宝石", - "text": "攻击+${core.values.redGem}", - "itemEffect": "core.status.hero.atk += 4 * core.status.thisMap.ratio", - "itemEffectTip": ",攻击+${4 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.atk += core.values.redGem", - "canUseItemEffect": "true" - }, - "I391": { - "cls": "items", - "name": "新物品" - }, - "I392": { - "cls": "items", - "name": "新物品" - }, - "I393": { - "cls": "items", - "name": "新物品" - }, - "I394": { - "cls": "items", - "name": "新物品" - }, - "I395": { - "cls": "items", - "name": "新物品" - }, - "I396": { - "cls": "items", - "name": "大蓝宝石", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 4 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${4 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "I397": { - "cls": "items", - "name": "新物品" - }, - "I398": { - "cls": "items", - "name": "新物品" - }, - "I399": { - "cls": "items", - "name": "新物品" - }, - "I400": { - "cls": "items", - "name": "新物品" - }, - "I401": { - "cls": "items", - "name": "新物品" - }, - "I402": { - "cls": "items", - "name": "新物品" - }, - "I403": { - "cls": "items", - "name": "大绿宝石", - "text": ",护盾+${core.values.greenGem}", - "itemEffect": "core.status.hero.mdef += 80 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)", - "itemEffectTip": ",智慧+${80 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)}", - "useItemEffect": "core.status.hero.mdef += core.values.greenGem", - "canUseItemEffect": "true" - }, - "I404": { - "cls": "items", - "name": "新物品" - }, - "I405": { - "cls": "items", - "name": "新物品" - }, - "I406": { - "cls": "items", - "name": "新物品" - }, - "I407": { - "cls": "items", - "name": "新物品" - }, - "I408": { - "cls": "items", - "name": "新物品" - }, - "I409": { - "cls": "items", - "name": "新物品" - }, - "I410": { - "cls": "items", - "name": "新物品" - }, - "I411": { - "cls": "items", - "name": "新物品" - }, - "I412": { - "cls": "items", - "name": "新物品" - }, - "I413": { - "cls": "items", - "name": "新物品" - }, - "I414": { - "cls": "items", - "name": "新物品" - }, - "I415": { - "cls": "items", - "name": "新物品" - }, - "I416": { - "cls": "items", - "name": "新物品" - }, - "I417": { - "cls": "items", - "name": "新物品" - }, - "I418": { - "cls": "items", - "name": "新物品" - }, - "I419": { - "cls": "items", - "name": "新物品" - }, - "I420": { - "cls": "items", - "name": "超大红宝石", - "text": "攻击+${core.values.redGem}", - "itemEffect": "core.status.hero.atk += 8 * core.status.thisMap.ratio", - "itemEffectTip": ",攻击+${8 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.atk += core.values.redGem", - "canUseItemEffect": "true" - }, - "I421": { - "cls": "items", - "name": "新物品" - }, - "I422": { - "cls": "items", - "name": "新物品" - }, - "I423": { - "cls": "items", - "name": "新物品" - }, - "I424": { - "cls": "items", - "name": "新物品" - }, - "I425": { - "cls": "items", - "name": "新物品" - }, - "I426": { - "cls": "items", - "name": "新物品" - }, - "I427": { - "cls": "items", - "name": "新物品" - }, - "I428": { - "cls": "items", - "name": "新物品" - }, - "I429": { - "cls": "items", - "name": "新物品" - }, - "I430": { - "cls": "items", - "name": "超大蓝宝石", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 8 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${8 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "I431": { - "cls": "items", - "name": "新物品" - }, - "I432": { - "cls": "items", - "name": "新物品" - }, - "I433": { - "cls": "items", - "name": "新物品" - }, - "I434": { - "cls": "items", - "name": "新物品" - }, - "I435": { - "cls": "items", - "name": "新物品" - }, - "I436": { - "cls": "items", - "name": "新物品" - }, - "I437": { - "cls": "items", - "name": "新物品" - }, - "I438": { - "cls": "items", - "name": "新物品" - }, - "I439": { - "cls": "items", - "name": "新物品" - }, - "I440": { - "cls": "items", - "name": "新物品" - }, - "I441": { - "cls": "items", - "name": "超大绿宝石", - "text": ",护盾+${core.values.greenGem}", - "itemEffect": "core.status.hero.mdef += 160 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)", - "itemEffectTip": ",智慧+${160 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)}", - "useItemEffect": "core.status.hero.mdef += core.values.greenGem", - "canUseItemEffect": "true" - }, - "I442": { - "cls": "items", - "name": "新物品" - }, - "I443": { - "cls": "items", - "name": "新物品" - }, - "I444": { - "cls": "items", - "name": "新物品" - }, - "I445": { - "cls": "items", - "name": "新物品" - }, - "I446": { - "cls": "items", - "name": "新物品" - }, - "I447": { - "cls": "items", - "name": "新物品" - }, - "I448": { - "cls": "items", - "name": "新物品" - }, - "I449": { - "cls": "items", - "name": "新物品" - }, - "I450": { - "cls": "items", - "name": "新物品" - }, - "I451": { - "cls": "items", - "name": "新物品" - }, - "I452": { - "cls": "items", - "name": "新物品" - }, - "I453": { - "cls": "items", - "name": "新物品" - }, - "I454": { - "cls": "items", - "name": "新物品" - }, - "I455": { - "cls": "items", - "name": "新物品" - }, - "I456": { - "cls": "items", - "name": "新物品" - }, - "I457": { - "cls": "items", - "name": "新物品" - }, - "I458": { - "cls": "items", - "name": "新物品" - }, - "I459": { - "cls": "items", - "name": "新物品" - }, - "I460": { - "cls": "items", - "name": "新物品" - }, - "I461": { - "cls": "items", - "name": "新物品" - }, - "I462": { - "cls": "items", - "name": "新物品" - }, - "I463": { - "cls": "items", - "name": "新物品" - }, - "I464": { - "cls": "items", - "name": "新物品" - }, - "I465": { - "cls": "items", - "name": "新物品" - }, - "I466": { - "cls": "items", - "name": "璀璨红宝石", - "text": "攻击+${core.values.redGem}", - "itemEffect": "core.status.hero.atk += 16 * core.status.thisMap.ratio", - "itemEffectTip": ",攻击+${16 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.atk += core.values.redGem", - "canUseItemEffect": "true" - }, - "I467": { - "cls": "items", - "name": "璀璨蓝宝石", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 16 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${16 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "I468": { - "cls": "items", - "name": "璀璨绿宝石", - "text": ",护盾+${core.values.greenGem}", - "itemEffect": "core.status.hero.mdef += 320 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)", - "itemEffectTip": ",智慧+${320 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)}", - "useItemEffect": "core.status.hero.mdef += core.values.greenGem", - "canUseItemEffect": "true" - }, - "I469": { - "cls": "items", - "name": "新物品" - }, - "I470": { - "cls": "items", - "name": "新物品", - "text": "攻击+${core.values.redGem}", - "itemEffect": "core.status.hero.atk += 32 * core.status.thisMap.ratio", - "itemEffectTip": ",攻击+${32 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.atk += core.values.redGem", - "canUseItemEffect": "true" - }, - "I471": { - "cls": "items", - "name": "新物品", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 32 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${32 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "I472": { - "cls": "items", - "name": "新物品", - "text": ",防御+${core.values.blueGem}", - "itemEffect": "core.status.hero.def += 8 * core.status.thisMap.ratio", - "itemEffectTip": ",防御+${8 * core.status.thisMap.ratio}", - "useItemEffect": "core.status.hero.def += core.values.blueGem", - "canUseItemEffect": "true" - }, - "I473": { - "cls": "items", - "name": "新物品" - }, - "I474": { - "cls": "items", - "name": "新物品" - }, - "I475": { - "cls": "items", - "name": "新物品" - }, - "I476": { - "cls": "items", - "name": "史诗绿宝石", - "text": ",护盾+${core.values.greenGem}", - "itemEffect": "core.status.hero.mdef += 1280 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)", - "itemEffectTip": ",智慧+${1280 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (core.getSkillLevel(11) / 20 + 1)}", - "useItemEffect": "core.status.hero.mdef += core.values.greenGem", - "canUseItemEffect": "true" - }, - "I477": { - "cls": "items", - "name": "新物品" - }, - "I478": { - "cls": "items", - "name": "新物品" - }, - "I479": { - "cls": "items", - "name": "新物品" - }, - "I480": { - "cls": "items", - "name": "新物品" - }, - "I481": { - "cls": "items", - "name": "新物品" - }, - "I482": { - "cls": "items", - "name": "大红血瓶", - "text": ",生命+${core.values.redPotion}", - "itemEffect": "core.status.hero.hp += 1000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${1000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.redPotion", - "canUseItemEffect": "true" - }, - "I483": { - "cls": "items", - "name": "新物品" - }, - "I484": { - "cls": "items", - "name": "大蓝血瓶", - "text": ",生命+${core.values.redPotion}", - "itemEffect": "core.status.hero.hp += 2000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${2000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.redPotion", - "canUseItemEffect": "true" - }, - "I485": { - "cls": "items", - "name": "新物品" - }, - "I486": { - "cls": "items", - "name": "新物品" - }, - "I487": { - "cls": "items", - "name": "大绿血瓶", - "text": ",生命+${core.values.redPotion}", - "itemEffect": "core.status.hero.hp += 8000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${8000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.redPotion", - "canUseItemEffect": "true" - }, - "I488": { - "cls": "items", - "name": "新物品" - }, - "I489": { - "cls": "items", - "name": "新物品" - }, - "I490": { - "cls": "items", - "name": "新物品" - }, - "I491": { - "cls": "items", - "name": "大黄血瓶", - "text": ",生命+${core.values.redPotion}", - "itemEffect": "core.status.hero.hp += 4000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)", - "itemEffectTip": ",生命+${4000 * core.status.thisMap.ratio * (1 + core.getSkillLevel(13) / 50)}", - "useItemEffect": "core.status.hero.hp += core.values.redPotion", - "canUseItemEffect": "true" - }, - "I558": { - "cls": "constants", - "name": "bgm查看器", - "canUseItemEffect": "true", - "text": "可以查看游戏内你已经听过的bgm,歌曲名格式:歌手——歌曲名", - "useItemEffect": "core.openBgms();" - }, - "I559": { - "cls": "constants", - "name": "系统设置", - "canUseItemEffect": "true", - "text": "内含所有系统设置项", - "useItemEffect": "if (!core.isReplaying()) core.plugin.settingsOpened.value = true;" - }, - "I560": { - "cls": "constants", - "name": "百科全书", - "canUseItemEffect": "true", - "text": "一个包含游戏中所有功能详细说明的百科全书,可以查看游戏中所有的功能", - "useItemEffect": "if (!core.isReplaying()) core.plugin.descOpened.value = true;" - }, - "I565": { - "cls": "constants", - "name": "学习", - "canUseItemEffect": "true", - "text": "可以学习怪物的技能,学习后持续${core.getSkillLevel(11) * 3 + 2}场战斗" - }, - "I574": { - "cls": "items", - "name": "新物品", - "canUseItemEffect": "true" - }, - "I575": { - "cls": "equips", - "name": "智慧之靴", - "canUseItemEffect": "true", - "text": "用智慧制作出的靴子,穿上后增加10%的攻击力和10%的防御", - "equip": { - "type": "鞋子", - "value": {}, - "percentage": { - "def": 10, - "atk": 10 - } - } - }, - "I589": { - "cls": "equips", - "name": "杰克的衣服", - "canUseItemEffect": "true", - "text": "杰克为主角留下的衣服,可以抵御寒冷。防御+25,免疫怪物的霜冻属性。", - "equip": { - "type": "衣服", - "value": { - "def": 25 - }, - "percentage": {} - } - }, - "I641": { - "cls": "equips", - "name": "寒冰护符", - "canUseItemEffect": "true", - "text": "!!html饰品。与寒冰没有任何关系,但是为什么叫寒冰护符呢?攻击和额外攻击各增加5%", - "equip": { - "type": 0, - "value": {}, - "percentage": { - "atk": 5, - "mana": 5 - } - } - }, - "I642": { - "cls": "constants", - "name": "成就", - "canUseItemEffect": "true", - "useItemEffect": "core.plugin.achievementOpened.value = true;", - "text": "可以查看成就" - } -} \ No newline at end of file +var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = { + yellowKey: { + cls: 'tools', + name: '黄钥匙', + text: '可以打开一扇黄门', + hideInToolbox: true + }, + blueKey: { + cls: 'tools', + name: '蓝钥匙', + text: '可以打开一扇蓝门', + hideInToolbox: true + }, + redKey: { + cls: 'tools', + name: '红钥匙', + text: '可以打开一扇红门', + hideInToolbox: true + }, + redGem: { + cls: 'items', + name: '小红宝石', + text: '攻击+${core.values.redGem}', + itemEffect: 'core.status.hero.atk += 1 * core.status.thisMap.ratio', + itemEffectTip: ',攻击+${1 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.atk += core.values.redGem', + canUseItemEffect: 'true' + }, + blueGem: { + cls: 'items', + name: '小蓝宝石', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 1 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${1 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + greenGem: { + cls: 'items', + name: '小绿宝石', + text: ',护盾+${core.values.greenGem}', + itemEffect: + 'core.status.hero.mdef += 20 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)', + itemEffectTip: + ',智慧+${20 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)}', + useItemEffect: 'core.status.hero.mdef += core.values.greenGem', + canUseItemEffect: 'true' + }, + yellowGem: { + cls: 'items', + name: '黄宝石', + text: '可以进行加点', + itemEffect: + 'core.status.hero.hp+=1000;core.status.hero.atk+=6;core.status.hero.def+=6;core.status.hero.mdef+=10;', + itemEffectTip: ',全属性提升', + useItemEvent: [ + { + type: 'choices', + choices: [ + { + text: '攻击+1', + action: [ + { + type: 'setValue', + name: 'status:atk', + operator: '+=', + value: '1' + } + ] + }, + { + text: '防御+2', + action: [ + { + type: 'setValue', + name: 'status:def', + operator: '+=', + value: '2' + } + ] + }, + { + text: '生命+200', + action: [ + { + type: 'setValue', + name: 'status:hp', + operator: '+=', + value: '200' + } + ] + } + ] + } + ], + canUseItemEffect: 'true' + }, + redPotion: { + cls: 'items', + name: '红血瓶', + text: ',生命+${core.values.redPotion}', + itemEffect: + 'core.status.hero.hp += 100 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${100 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.redPotion', + canUseItemEffect: 'true' + }, + bluePotion: { + cls: 'items', + name: '蓝血瓶', + text: ',生命+${core.values.bluePotion}', + itemEffect: + 'core.status.hero.hp += 200 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${200 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.bluePotion', + canUseItemEffect: 'true' + }, + yellowPotion: { + cls: 'items', + name: '黄血瓶', + text: ',生命+${core.values.yellowPotion}', + itemEffect: + 'core.status.hero.hp += 400 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${400 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.yellowPotion', + canUseItemEffect: 'true' + }, + greenPotion: { + cls: 'items', + name: '绿血瓶', + text: ',生命+${core.values.greenPotion}', + itemEffect: + 'core.status.hero.hp += 800 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${800 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.greenPotion', + canUseItemEffect: 'true' + }, + sword0: { + cls: 'items', + name: '破旧的剑', + text: '一把已经生锈的剑', + equip: { + type: 0, + animate: 'sword', + value: { + atk: 0 + } + }, + itemEffect: 'core.status.hero.atk += 0', + itemEffectTip: ',攻击+0' + }, + sword1: { + cls: 'equips', + name: '智慧之剑', + text: '借助曾经闯入此塔的智慧挑战者的智慧锻造而成,攻击+15,额外攻击+10', + equip: { + type: '武器', + animate: 'jianji', + value: { + atk: 15, + mana: 10 + }, + percentage: {} + }, + itemEffect: '', + itemEffectTip: ',攻击+10' + }, + sword2: { + cls: 'equips', + name: '铁剑', + text: '真的是一把很普通的银剑,哦不,铁剑。攻击+180,额外攻击+50', + equip: { + type: 0, + animate: 'sword', + value: { + mana: 50, + atk: 180 + }, + percentage: {} + }, + itemEffect: 'core.status.hero.atk += 20', + itemEffectTip: ',攻击+20' + }, + sword3: { + cls: 'items', + name: '骑士剑', + text: '一把很普通的骑士剑', + equip: { + type: 0, + animate: 'sword', + value: { + atk: 40 + } + }, + itemEffect: 'core.status.hero.atk += 40', + itemEffectTip: ',攻击+40' + }, + sword4: { + cls: 'items', + name: '圣剑', + text: '一把很普通的圣剑', + equip: { + type: 0, + animate: 'sword', + value: { + atk: 80 + } + }, + itemEffect: 'core.status.hero.atk += 80', + itemEffectTip: ',攻击+80' + }, + sword5: { + cls: 'items', + name: '神圣剑', + text: '一把很普通的神圣剑', + equip: { + type: 0, + animate: 'sword', + value: { + atk: 160 + } + }, + itemEffect: 'core.status.hero.atk += 100', + itemEffectTip: ',攻击+100' + }, + shield0: { + cls: 'items', + name: '破旧的盾', + text: '一个很破旧的铁盾', + equip: { + type: 1, + value: { + def: 0 + } + }, + itemEffect: 'core.status.hero.def += 0', + itemEffectTip: ',防御+0' + }, + shield1: { + cls: 'equips', + name: '智慧之盾', + text: '借助曾经闯入此塔的智慧挑战者的智慧锻造而成,防御+15,生命回复+15', + equip: { + type: '盾牌', + value: { + hpmax: 15, + def: 15 + }, + percentage: {} + }, + itemEffect: '', + itemEffectTip: ',防御+10' + }, + shield2: { + cls: 'equips', + name: '铁盾', + text: '一个真的很普通的铁盾,这次没错了,就是铁盾!防御+120,生命回复+30', + equip: { + type: 1, + value: { + hpmax: 30, + def: 120 + }, + percentage: {} + }, + itemEffect: 'core.status.hero.def += 20', + itemEffectTip: ',防御+20' + }, + shield3: { + cls: 'items', + name: '骑士盾', + text: '一个很普通的骑士盾', + equip: { + type: 1, + value: { + def: 40 + } + }, + itemEffect: 'core.status.hero.def += 40', + itemEffectTip: ',防御+40' + }, + shield4: { + cls: 'items', + name: '圣盾', + text: '一个很普通的圣盾', + equip: { + type: 1, + value: { + def: 80 + } + }, + itemEffect: 'core.status.hero.def += 80', + itemEffectTip: ',防御+80' + }, + shield5: { + cls: 'items', + name: '神圣盾', + text: '一个很普通的神圣盾', + equip: { + type: 1, + value: { + def: 100, + mdef: 100 + } + }, + itemEffect: 'core.status.hero.def += 100;core.status.hero.mdef += 100', + itemEffectTip: ',防御+100,护盾+100' + }, + superPotion: { + cls: 'items', + name: '圣水', + itemEffect: 'core.status.hero.hp *= 2', + itemEffectTip: ',生命值翻倍', + useItemEffect: 'core.status.hero.hp *= 2;', + canUseItemEffect: 'true', + text: '生命值翻倍' + }, + silverCoin: { + cls: 'items', + name: '银币', + itemEffect: 'core.status.hero.money += 500', + itemEffectTip: ',金币+500' + }, + book: { + cls: 'constants', + name: '怪物手册', + text: '可以查看当前楼层各怪物属性', + hideInToolbox: true, + useItemEffect: 'core.ui.drawBook(0);', + canUseItemEffect: 'true' + }, + fly: { + cls: 'constants', + name: '楼层传送器', + text: '可以自由往来去过的楼层', + hideInReplay: true, + hideInToolbox: true, + useItemEffect: 'core.ui.drawFly();', + canUseItemEffect: + '(function () {\n\treturn core.status.maps[core.status.floorId].canFlyFrom;\n})();' + }, + coin: { + cls: 'constants', + name: '幸运金币', + text: '持有时打败怪物可得双倍金币' + }, + freezeBadge: { + cls: 'constants', + name: '冰冻徽章', + text: '可以将面前的熔岩变成平地', + useItemEffect: + "(function () {\n\tvar success = false;\n\n\tvar snowFourDirections = false; // 是否四方向雪花;如果是将其改成true\n\tif (snowFourDirections) {\n\t\t// 四方向雪花\n\t\tfor (var direction in core.utils.scan) {\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})();", + canUseItemEffect: 'true' + }, + cross: { + cls: 'constants', + name: '查看技能', + text: '查看勇士的技能', + canUseItemEffect: true, + useItemEffect: 'core.plugin.gameUi.openSkill();' + }, + dagger: { + cls: 'constants', + name: '屠龙匕首', + text: '该道具尚未被定义' + }, + amulet: { + cls: 'constants', + name: '护符', + text: '持有时无视负面地形' + }, + bigKey: { + cls: 'tools', + name: '大黄门钥匙', + text: '可以开启当前层所有黄门', + itemEffect: + "core.addItem('yellowKey', 1);\ncore.addItem('blueKey', 1);\ncore.addItem('redKey', 1);", + itemEffectTip: ',全钥匙+1', + useItemEffect: + '(function () {\n\tvar actions = core.searchBlock("yellowDoor").map(function (block) {\n\t\treturn { "type": "openDoor", "loc": [block.x, block.y], "async": true };\n\t});\n\tactions.push({ "type": "waitAsync" });\n\tactions.push({ "type": "tip", "text": core.material.items[itemId].name + "使用成功" });\n\tcore.insertAction(actions);\n})();', + canUseItemEffect: + "(function () {\n\treturn core.searchBlock('yellowDoor').length > 0;\n})();" + }, + greenKey: { + cls: 'tools', + name: '绿钥匙', + text: '可以打开一扇绿门' + }, + steelKey: { + cls: 'tools', + name: '铁门钥匙', + text: '可以打开一扇铁门' + }, + pickaxe: { + cls: 'tools', + name: '破墙镐', + text: '可以破坏勇士面前的墙', + useItemEffect: + "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable) return false;\n\t\treturn block.event.canBreak;\n\t};\n\n\tvar success = false;\n\tvar pickaxeFourDirections = false; // 是否四方向破;如果是将其改成true\n\tif (pickaxeFourDirections) {\n\t\t// 四方向破\n\t\tfor (var direction in core.utils.scan) {\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})();", + canUseItemEffect: 'true' + }, + icePickaxe: { + cls: 'tools', + name: '破冰镐', + text: '可以破坏勇士面前的一堵冰墙', + useItemEffect: + "(function () {\n\tcore.removeBlock(core.nextX(), core.nextY());\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", + canUseItemEffect: + "(function () {\n\treturn core.getBlockId(core.nextX(), core.nextY()) == 'ice';\n})();" + }, + bomb: { + cls: 'tools', + name: '炸弹', + text: '可以炸掉勇士面前的怪物', + useItemEffect: + "(function () {\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable || block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tvar 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})();", + canUseItemEffect: 'true' + }, + centerFly: { + cls: 'constants', + name: '快捷键查看器', + text: '可以查看本塔里面的所有快捷键', + useItemEffect: '', + canUseItemEffect: 'true', + useItemEvent: [ + '8:打开定点查看界面,如果开启了定点查看代替怪物手册,也可以按X打开定点查看界面\nJ:打开技能树界面\n1:开关断灭之刃技能\n2:使用跳跃技能/破墙镐', + '平面楼传界面:\n上下左右:移动地图\n,:前移10层\n.:后移10层\nPgUp:上楼\nPgDn:下楼\nB:显隐地图名\nZ:开关3D模式', + '浏览地图界面(2.8.1新增):\nG:传送至该地图', + '手机端:点击右下角难度可以切换至数字键盘' + ] + }, + upFly: { + cls: 'tools', + name: '上楼器', + text: '可以飞往楼上的相同位置', + useItemEffect: + '(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) + 1];\n\tif (core.status.event.id == \'action\') {\n\t\tcore.insertAction([\n\t\t\t{ "type": "changeFloor", "loc": [core.getHeroLoc(\'x\'), core.getHeroLoc(\'y\')], "floorId": floorId },\n\t\t\t{ "type": "tip", "text": core.material.items[itemId].name + \'使用成功\' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + \'使用成功\');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();', + canUseItemEffect: + "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index < core.floorIds.length - 1) {\n\t\tvar toId = core.floorIds[index + 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();" + }, + downFly: { + cls: 'tools', + name: '下楼器', + text: '可以飞往楼下的相同位置', + useItemEffect: + '(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) - 1];\n\tif (core.status.event.id == \'action\') {\n\t\tcore.insertAction([\n\t\t\t{ "type": "changeFloor", "loc": [core.getHeroLoc(\'x\'), core.getHeroLoc(\'y\')], "floorId": floorId },\n\t\t\t{ "type": "tip", "text": core.material.items[itemId].name + \'使用成功\' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + \'使用成功\');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();', + canUseItemEffect: + "(function () {\n\tvar floorId = core.status.floorId,\n\t\tindex = core.floorIds.indexOf(floorId);\n\tif (index > 0) {\n\t\tvar toId = core.floorIds[index - 1],\n\t\t\ttoX = core.getHeroLoc('x'),\n\t\t\ttoY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width,\n\t\t\tmh = core.floors[toId].height;\n\t\tif (toX >= 0 && toX < mw && toY >= 0 && toY < mh && core.getBlock(toX, toY, toId) == null) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();" + }, + earthquake: { + cls: 'tools', + name: '地震卷轴', + text: '可以破坏当前层的所有墙', + useItemEffect: + "(function () {\n\tvar indexes = [];\n\tfor (var index in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[index];\n\t\tif (!block.disable && block.event.canBreak) {\n\t\t\tindexes.push(index);\n\t\t}\n\t}\n\tcore.removeBlockByIndexes(indexes);\n\tcore.drawMap();\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();", + canUseItemEffect: + '(function () {\n\treturn core.status.thisMap.blocks.filter(function (block) {\n\t\treturn !block.disable && block.event.canBreak;\n\t}).length > 0;\n})();' + }, + poisonWine: { + cls: 'tools', + name: '解毒药水', + text: '可以解除中毒状态', + useItemEffect: '', + canUseItemEffect: "core.hasFlag('poison');" + }, + weakWine: { + cls: 'tools', + name: '解衰药水', + text: '可以解除衰弱状态', + useItemEffect: '', + canUseItemEffect: "core.hasFlag('weak');" + }, + curseWine: { + cls: 'tools', + name: '解咒药水', + text: '可以解除诅咒状态', + useItemEffect: '', + canUseItemEffect: "core.hasFlag('curse');" + }, + superWine: { + cls: 'tools', + name: '万能药水', + text: '可以解除所有不良状态', + useItemEffect: '', + canUseItemEffect: + "(function() {\n\treturn core.hasFlag('poison') || core.hasFlag('weak') || core.hasFlag('curse');\n})();" + }, + hammer: { + cls: 'tools', + name: '圣锤', + text: '该道具尚未被定义' + }, + lifeWand: { + cls: 'tools', + name: '生命魔杖', + text: '可以恢复100点生命值', + useItemEvent: [ + { + type: 'comment', + text: '先恢复一个魔杖(因为使用道具必须消耗一个)' + }, + { + type: 'function', + function: "function(){\ncore.addItem('lifeWand', 1);\n}" + }, + { + type: 'input', + text: '请输入生命魔杖使用次数:(0-${item:lifeWand})' + }, + { + type: 'if', + condition: 'flag:input<=item:lifeWand', + true: [ + { + type: 'setValue', + name: 'item:lifeWand', + operator: '-=', + value: 'flag:input' + }, + { + type: 'setValue', + name: 'status:hp', + operator: '+=', + value: 'flag:input*100' + }, + '成功使用${flag:input}次生命魔杖,恢复${flag:input*100}点生命。' + ], + false: ['输入不合法!'] + } + ], + canUseItemEffect: 'true' + }, + jumpShoes: { + cls: 'tools', + name: '跳跃靴', + text: '能跳跃到前方两格处', + useItemEffect: + 'core.playSound("jump.mp3"); core.insertAction({ "type": "jumpHero", "loc": [core.nextX(2), core.nextY(2)] });', + canUseItemEffect: + '(function () {\n\tvar nx = core.nextX(2),\n\t\tny = core.nextY(2);\n\treturn nx >= 0 && nx < core.bigmap.width && ny >= 0 && ny < core.bigmap.height && core.getBlockId(nx, ny) == null;\n})();' + }, + skill1: { + cls: 'constants', + name: '技能树', + text: '打开技能树', + hideInReplay: true, + useItemEffect: 'core.plugin.skillTree.openTree();', + canUseItemEffect: 'true' + }, + wand: { + cls: 'constants', + name: '定点查看', + text: '可以定点查看怪物属性', + canUseItemEffect: true, + useItemEffect: 'core.openFixed();' + }, + I319: { + cls: 'items', + name: '新物品' + }, + I320: { + cls: 'items', + name: '新物品' + }, + I321: { + cls: 'items', + name: '新物品' + }, + I322: { + cls: 'constants', + name: '快捷键查看器', + text: '可以查看本塔里面的所有快捷键', + useItemEffect: '', + canUseItemEffect: 'true', + useItemEvent: [ + '8:打开定点查看界面,如果开启了定点查看代替怪物手册,也可以按X打开定点查看界面\nJ:打开技能树界面\n1:开关断灭之刃技能\n2:使用跳跃技能/破墙镐', + '平面楼传界面:\n上下左右:移动地图\n,:前移10层\n.:后移10层\nPgUp:上楼\nPgDn:下楼\nB:显隐地图名\nZ:开关3D模式', + '浏览地图界面(2.8.1新增):\nG:传送至该地图', + '手机端:点击右下角难度可以切换至数字键盘' + ] + }, + I323: { + cls: 'items', + name: '新物品' + }, + I324: { + cls: 'items', + name: '新物品' + }, + I325: { + cls: 'items', + name: '新物品' + }, + I326: { + cls: 'items', + name: '新物品' + }, + I327: { + cls: 'items', + name: '新物品' + }, + I328: { + cls: 'items', + name: '新物品' + }, + I329: { + cls: 'items', + name: '新物品' + }, + I330: { + cls: 'constants', + name: '系统设置', + text: '可以修改一些本塔的设置', + canUseItemEffect: true, + useItemEvent: null + }, + I376: { + cls: 'items', + name: '中红宝石', + text: '攻击+${core.values.redGem}', + itemEffect: 'core.status.hero.atk += 2 * core.status.thisMap.ratio', + itemEffectTip: ',攻击+${2 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.atk += core.values.redGem', + canUseItemEffect: 'true' + }, + I377: { + cls: 'items', + name: '新物品' + }, + I378: { + cls: 'items', + name: '中蓝宝石', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 2 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${2 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + I379: { + cls: 'items', + name: '新物品' + }, + I380: { + cls: 'items', + name: '新物品' + }, + I381: { + cls: 'items', + name: '中绿宝石', + text: ',护盾+${core.values.greenGem}', + itemEffect: + 'core.status.hero.mdef += 40 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)', + itemEffectTip: + ',智慧+${40 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)}', + useItemEffect: 'core.status.hero.mdef += core.values.greenGem', + canUseItemEffect: 'true' + }, + I382: { + cls: 'items', + name: '新物品' + }, + I383: { + cls: 'items', + name: '新物品' + }, + I384: { + cls: 'items', + name: '新物品' + }, + I385: { + cls: 'items', + name: '新物品' + }, + I386: { + cls: 'items', + name: '新物品' + }, + I387: { + cls: 'items', + name: '新物品' + }, + I388: { + cls: 'items', + name: '新物品' + }, + I389: { + cls: 'items', + name: '新物品' + }, + I390: { + cls: 'items', + name: '大红宝石', + text: '攻击+${core.values.redGem}', + itemEffect: 'core.status.hero.atk += 4 * core.status.thisMap.ratio', + itemEffectTip: ',攻击+${4 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.atk += core.values.redGem', + canUseItemEffect: 'true' + }, + I391: { + cls: 'items', + name: '新物品' + }, + I392: { + cls: 'items', + name: '新物品' + }, + I393: { + cls: 'items', + name: '新物品' + }, + I394: { + cls: 'items', + name: '新物品' + }, + I395: { + cls: 'items', + name: '新物品' + }, + I396: { + cls: 'items', + name: '大蓝宝石', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 4 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${4 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + I397: { + cls: 'items', + name: '新物品' + }, + I398: { + cls: 'items', + name: '新物品' + }, + I399: { + cls: 'items', + name: '新物品' + }, + I400: { + cls: 'items', + name: '新物品' + }, + I401: { + cls: 'items', + name: '新物品' + }, + I402: { + cls: 'items', + name: '新物品' + }, + I403: { + cls: 'items', + name: '大绿宝石', + text: ',护盾+${core.values.greenGem}', + itemEffect: + 'core.status.hero.mdef += 80 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)', + itemEffectTip: + ',智慧+${80 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)}', + useItemEffect: 'core.status.hero.mdef += core.values.greenGem', + canUseItemEffect: 'true' + }, + I404: { + cls: 'items', + name: '新物品' + }, + I405: { + cls: 'items', + name: '新物品' + }, + I406: { + cls: 'items', + name: '新物品' + }, + I407: { + cls: 'items', + name: '新物品' + }, + I408: { + cls: 'items', + name: '新物品' + }, + I409: { + cls: 'items', + name: '新物品' + }, + I410: { + cls: 'items', + name: '新物品' + }, + I411: { + cls: 'items', + name: '新物品' + }, + I412: { + cls: 'items', + name: '新物品' + }, + I413: { + cls: 'items', + name: '新物品' + }, + I414: { + cls: 'items', + name: '新物品' + }, + I415: { + cls: 'items', + name: '新物品' + }, + I416: { + cls: 'items', + name: '新物品' + }, + I417: { + cls: 'items', + name: '新物品' + }, + I418: { + cls: 'items', + name: '新物品' + }, + I419: { + cls: 'items', + name: '新物品' + }, + I420: { + cls: 'items', + name: '超大红宝石', + text: '攻击+${core.values.redGem}', + itemEffect: 'core.status.hero.atk += 8 * core.status.thisMap.ratio', + itemEffectTip: ',攻击+${8 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.atk += core.values.redGem', + canUseItemEffect: 'true' + }, + I421: { + cls: 'items', + name: '新物品' + }, + I422: { + cls: 'items', + name: '新物品' + }, + I423: { + cls: 'items', + name: '新物品' + }, + I424: { + cls: 'items', + name: '新物品' + }, + I425: { + cls: 'items', + name: '新物品' + }, + I426: { + cls: 'items', + name: '新物品' + }, + I427: { + cls: 'items', + name: '新物品' + }, + I428: { + cls: 'items', + name: '新物品' + }, + I429: { + cls: 'items', + name: '新物品' + }, + I430: { + cls: 'items', + name: '超大蓝宝石', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 8 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${8 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + I431: { + cls: 'items', + name: '新物品' + }, + I432: { + cls: 'items', + name: '新物品' + }, + I433: { + cls: 'items', + name: '新物品' + }, + I434: { + cls: 'items', + name: '新物品' + }, + I435: { + cls: 'items', + name: '新物品' + }, + I436: { + cls: 'items', + name: '新物品' + }, + I437: { + cls: 'items', + name: '新物品' + }, + I438: { + cls: 'items', + name: '新物品' + }, + I439: { + cls: 'items', + name: '新物品' + }, + I440: { + cls: 'items', + name: '新物品' + }, + I441: { + cls: 'items', + name: '超大绿宝石', + text: ',护盾+${core.values.greenGem}', + itemEffect: + 'core.status.hero.mdef += 160 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)', + itemEffectTip: + ',智慧+${160 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)}', + useItemEffect: 'core.status.hero.mdef += core.values.greenGem', + canUseItemEffect: 'true' + }, + I442: { + cls: 'items', + name: '新物品' + }, + I443: { + cls: 'items', + name: '新物品' + }, + I444: { + cls: 'items', + name: '新物品' + }, + I445: { + cls: 'items', + name: '新物品' + }, + I446: { + cls: 'items', + name: '新物品' + }, + I447: { + cls: 'items', + name: '新物品' + }, + I448: { + cls: 'items', + name: '新物品' + }, + I449: { + cls: 'items', + name: '新物品' + }, + I450: { + cls: 'items', + name: '新物品' + }, + I451: { + cls: 'items', + name: '新物品' + }, + I452: { + cls: 'items', + name: '新物品' + }, + I453: { + cls: 'items', + name: '新物品' + }, + I454: { + cls: 'items', + name: '新物品' + }, + I455: { + cls: 'items', + name: '新物品' + }, + I456: { + cls: 'items', + name: '新物品' + }, + I457: { + cls: 'items', + name: '新物品' + }, + I458: { + cls: 'items', + name: '新物品' + }, + I459: { + cls: 'items', + name: '新物品' + }, + I460: { + cls: 'items', + name: '新物品' + }, + I461: { + cls: 'items', + name: '新物品' + }, + I462: { + cls: 'items', + name: '新物品' + }, + I463: { + cls: 'items', + name: '新物品' + }, + I464: { + cls: 'items', + name: '新物品' + }, + I465: { + cls: 'items', + name: '新物品' + }, + I466: { + cls: 'items', + name: '璀璨红宝石', + text: '攻击+${core.values.redGem}', + itemEffect: 'core.status.hero.atk += 16 * core.status.thisMap.ratio', + itemEffectTip: ',攻击+${16 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.atk += core.values.redGem', + canUseItemEffect: 'true' + }, + I467: { + cls: 'items', + name: '璀璨蓝宝石', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 16 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${16 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + I468: { + cls: 'items', + name: '璀璨绿宝石', + text: ',护盾+${core.values.greenGem}', + itemEffect: + 'core.status.hero.mdef += 320 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)', + itemEffectTip: + ',智慧+${320 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)}', + useItemEffect: 'core.status.hero.mdef += core.values.greenGem', + canUseItemEffect: 'true' + }, + I469: { + cls: 'items', + name: '新物品' + }, + I470: { + cls: 'items', + name: '新物品', + text: '攻击+${core.values.redGem}', + itemEffect: 'core.status.hero.atk += 32 * core.status.thisMap.ratio', + itemEffectTip: ',攻击+${32 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.atk += core.values.redGem', + canUseItemEffect: 'true' + }, + I471: { + cls: 'items', + name: '新物品', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 32 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${32 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + I472: { + cls: 'items', + name: '新物品', + text: ',防御+${core.values.blueGem}', + itemEffect: 'core.status.hero.def += 8 * core.status.thisMap.ratio', + itemEffectTip: ',防御+${8 * core.status.thisMap.ratio}', + useItemEffect: 'core.status.hero.def += core.values.blueGem', + canUseItemEffect: 'true' + }, + I473: { + cls: 'items', + name: '新物品' + }, + I474: { + cls: 'items', + name: '新物品' + }, + I475: { + cls: 'items', + name: '新物品' + }, + I476: { + cls: 'items', + name: '史诗绿宝石', + text: ',护盾+${core.values.greenGem}', + itemEffect: + 'core.status.hero.mdef += 1280 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)', + itemEffectTip: + ',智慧+${1280 * core.status.thisMap.ratio / core.getFlag("hard") * (core.plugin.skillTree.getSkillLevel(11) / 20 + 1)}', + useItemEffect: 'core.status.hero.mdef += core.values.greenGem', + canUseItemEffect: 'true' + }, + I477: { + cls: 'items', + name: '新物品' + }, + I478: { + cls: 'items', + name: '新物品' + }, + I479: { + cls: 'items', + name: '新物品' + }, + I480: { + cls: 'items', + name: '新物品' + }, + I481: { + cls: 'items', + name: '新物品' + }, + I482: { + cls: 'items', + name: '大红血瓶', + text: ',生命+${core.values.redPotion}', + itemEffect: + 'core.status.hero.hp += 1000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${1000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.redPotion', + canUseItemEffect: 'true' + }, + I483: { + cls: 'items', + name: '新物品' + }, + I484: { + cls: 'items', + name: '大蓝血瓶', + text: ',生命+${core.values.redPotion}', + itemEffect: + 'core.status.hero.hp += 2000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${2000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.redPotion', + canUseItemEffect: 'true' + }, + I485: { + cls: 'items', + name: '新物品' + }, + I486: { + cls: 'items', + name: '新物品' + }, + I487: { + cls: 'items', + name: '大绿血瓶', + text: ',生命+${core.values.redPotion}', + itemEffect: + 'core.status.hero.hp += 8000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${8000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.redPotion', + canUseItemEffect: 'true' + }, + I488: { + cls: 'items', + name: '新物品' + }, + I489: { + cls: 'items', + name: '新物品' + }, + I490: { + cls: 'items', + name: '新物品' + }, + I491: { + cls: 'items', + name: '大黄血瓶', + text: ',生命+${core.values.redPotion}', + itemEffect: + 'core.status.hero.hp += 4000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)', + itemEffectTip: + ',生命+${4000 * core.status.thisMap.ratio * (1 + core.plugin.skillTree.getSkillLevel(13) / 50)}', + useItemEffect: 'core.status.hero.hp += core.values.redPotion', + canUseItemEffect: 'true' + }, + I558: { + cls: 'constants', + name: 'bgm查看器', + canUseItemEffect: 'true', + text: '可以查看游戏内你已经听过的bgm,歌曲名格式:歌手——歌曲名', + useItemEffect: 'core.openBgms();' + }, + I559: { + cls: 'constants', + name: '系统设置', + canUseItemEffect: 'true', + text: '内含所有系统设置项', + useItemEffect: + 'if (!core.isReplaying()) core.plugin.settingsOpened.value = true;' + }, + I560: { + cls: 'constants', + name: '百科全书', + canUseItemEffect: 'true', + text: '一个包含游戏中所有功能详细说明的百科全书,可以查看游戏中所有的功能', + useItemEffect: + 'if (!core.isReplaying()) core.plugin.descOpened.value = true;' + }, + I565: { + cls: 'constants', + name: '学习', + canUseItemEffect: 'true', + text: '可以学习怪物的技能,学习后持续${core.plugin.skillTree.getSkillLevel(11) * 3 + 2}场战斗' + }, + I574: { + cls: 'items', + name: '新物品', + canUseItemEffect: 'true' + }, + I575: { + cls: 'equips', + name: '智慧之靴', + canUseItemEffect: 'true', + text: '用智慧制作出的靴子,穿上后增加10%的攻击力和10%的防御', + equip: { + type: '鞋子', + value: {}, + percentage: { + def: 10, + atk: 10 + } + } + }, + I589: { + cls: 'equips', + name: '杰克的衣服', + canUseItemEffect: 'true', + text: '杰克为主角留下的衣服,可以抵御寒冷。防御+25,免疫怪物的霜冻属性。', + equip: { + type: '衣服', + value: { + def: 25 + }, + percentage: {} + } + }, + I641: { + cls: 'equips', + name: '寒冰护符', + canUseItemEffect: 'true', + text: '!!html饰品。与寒冰没有任何关系,但是为什么叫寒冰护符呢?攻击和额外攻击各增加5%', + equip: { + type: 0, + value: {}, + percentage: { + atk: 5, + mana: 5 + } + } + }, + I642: { + cls: 'constants', + name: '成就', + canUseItemEffect: 'true', + useItemEffect: 'core.plugin.achievementOpened.value = true;', + text: '可以查看成就' + } +}; diff --git a/public/project/plugin/fiveLayer.js b/public/project/plugin/fiveLayer.js new file mode 100644 index 0000000..58e3e54 --- /dev/null +++ b/public/project/plugin/fiveLayer.js @@ -0,0 +1,207 @@ +'use strict'; + +(function () { + // 创建新图层 + function createCanvas(name, zIndex) { + if (!name) return; + var canvas = document.createElement('canvas'); + canvas.id = name; + canvas.className = 'gameCanvas'; + // 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高 + if (main.mode != 'editor') canvas.style.zIndex = zIndex || 0; + // 将图层插入进游戏内容 + document.getElementById('gameDraw').appendChild(canvas); + var ctx = canvas.getContext('2d'); + core.canvas[name] = ctx; + + return canvas; + } + + var bg2Canvas = createCanvas('bg2', 20); + var fg2Canvas = createCanvas('fg2', 63); + // 大地图适配 + core.bigmap.canvas = [ + 'bg2', + 'fg2', + 'bg', + 'event', + 'event2', + 'fg', + 'damage' + ]; + core.initStatus.bg2maps = {}; + core.initStatus.fg2maps = {}; + + if (main.mode == 'editor') { + /*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/ + // 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层) + // 背景层2(bg2) 插入事件层(event)之前(即bg与event之间) + document + .getElementById('mapEdit') + .insertBefore(bg2Canvas, document.getElementById('event')); + // 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后) + document + .getElementById('mapEdit') + .insertBefore(fg2Canvas, document.getElementById('ebm')); + // 原本有三个图层 从4开始添加 + var num = 4; + // 新增图层存入editor.dom中 + editor.dom.bg2c = core.canvas.bg2.canvas; + editor.dom.bg2Ctx = core.canvas.bg2; + editor.dom.fg2c = core.canvas.fg2.canvas; + editor.dom.fg2Ctx = core.canvas.fg2; + editor.dom.maps.push('bg2map', 'fg2map'); + editor.dom.canvas.push('bg2', 'fg2'); + + // 创建编辑器上的按钮 + var createCanvasBtn = name => { + // 电脑端创建按钮 + var input = document.createElement('input'); + // layerMod4/layerMod5 + var id = 'layerMod' + num++; + // bg2map/fg2map + var value = name + 'map'; + input.type = 'radio'; + input.name = 'layerMod'; + input.id = id; + input.value = value; + editor.dom[id] = input; + input.onchange = () => { + editor.uifunctions.setLayerMod(value); + }; + return input; + }; + + var createCanvasBtn_mobile = name => { + // 手机端往选择列表中添加子选项 + var input = document.createElement('option'); + var id = 'layerMod' + num++; + var value = name + 'map'; + input.name = 'layerMod'; + input.value = value; + editor.dom[id] = input; + return input; + }; + if (!editor.isMobile) { + var input = createCanvasBtn('bg2'); + var input2 = createCanvasBtn('fg2'); + // 获取事件层及其父节点 + var child = document.getElementById('layerMod'), + parent = child.parentNode; + // 背景层2插入事件层前 + parent.insertBefore(input, child); + // 不能直接更改背景层2的innerText 所以创建文本节点 + var txt = document.createTextNode('背2'); + // 插入事件层前(即新插入的背景层2前) + parent.insertBefore(txt, child); + // 向最后插入前景层2(即插入前景层后) + parent.appendChild(input2); + var txt2 = document.createTextNode('前2'); + parent.appendChild(txt2); + } else { + var input = createCanvasBtn_mobile('bg2'); + var input2 = createCanvasBtn_mobile('fg2'); + // 手机端因为是选项 所以可以直接改innerText + input.innerText = '背景2'; + input2.innerText = '前景2'; + var parent = document.getElementById('layerMod'); + parent.insertBefore(input, parent.children[1]); + parent.appendChild(input2); + } + } + core.maps._loadFloor_doNotCopy = function () { + return [ + 'firstArrive', + 'eachArrive', + 'blocks', + 'parallelDo', + 'map', + 'bgmap', + 'fgmap', + 'bg2map', + 'fg2map', + 'events', + 'changeFloor', + 'afterBattle', + 'afterGetItem', + 'afterOpenDoor', + 'cannotMove' + ]; + }; + ////// 绘制背景和前景层 ////// + core.maps._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) { + config.ctx = cacheCtx; + core.maps._drawBg_drawBackground(floorId, config); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages( + floorId, + config.ctx, + 'bg', + null, + null, + config.onMap + ); + core.maps._drawBgFgMap(floorId, 'bg', config); + if (config.onMap) { + core.drawImage( + toDrawCtx, + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + core.clearMap('bg2'); + core.clearMap(cacheCtx); + } + core.maps._drawBgFgMap(floorId, 'bg2', config); + if (config.onMap) + core.drawImage( + 'bg2', + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + config.ctx = toDrawCtx; + }; + core.maps._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) { + config.ctx = cacheCtx; + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages( + floorId, + config.ctx, + 'fg', + null, + null, + config.onMap + ); + core.maps._drawBgFgMap(floorId, 'fg', config); + if (config.onMap) { + core.drawImage( + toDrawCtx, + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + core.clearMap('fg2'); + core.clearMap(cacheCtx); + } + core.maps._drawBgFgMap(floorId, 'fg2', config); + if (config.onMap) + core.drawImage( + 'fg2', + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + config.ctx = toDrawCtx; + }; + ////// 移动判定 ////// + core.maps._generateMovableArray_arrays = function (floorId) { + return { + bgArray: this.getBgMapArray(floorId), + fgArray: this.getFgMapArray(floorId), + eventArray: this.getMapArray(floorId), + bg2Array: this._getBgFgMapArray('bg2', floorId), + fg2Array: this._getBgFgMapArray('fg2', floorId) + }; + }; +})(); diff --git a/public/project/plugin/halo.js b/public/project/plugin/halo.js new file mode 100644 index 0000000..587e87c --- /dev/null +++ b/public/project/plugin/halo.js @@ -0,0 +1,56 @@ +'use strict'; + +(function () { + /** + * 绘制光环范围 + * @param {CanvasRenderingContext2D} ctx + * @param {boolean} onMap + */ + function drawHalo(ctx, onMap) { + if (main.replayChecking) return; + if (!core.getLocalStorage('showHalo', true)) return; + const halo = core.status.checkBlock.halo; + ctx.save(); + for (const [loc, range] of Object.entries(halo)) { + const [x, y] = loc.split(',').map(v => parseInt(v)); + for (const r of range) { + const [type, value, color, border] = r.split(':'); + if (type === 'square') { + // 正方形光环 + const n = parseInt(value); + const r = Math.floor(n / 2); + let left = x - r, + right = x + r, + top = y - r, + bottom = y + r; + if (onMap && core.bigmap.v2) { + left -= core.bigmap.posX; + top -= core.bigmap.posY; + right -= core.bigmap.posX; + bottom -= core.bigmap.posY; + if ( + right < -1 || + left > core._PX_ / 32 + 1 || + top < -1 || + bottom > core._PY_ / 32 + 1 + ) { + continue; + } + } + ctx.fillStyle = color; + ctx.strokeStyle = border ?? color; + ctx.lineWidth = 1; + ctx.globalAlpha = 0.1; + ctx.fillRect(left * 32, top * 32, n * 32, n * 32); + ctx.globalAlpha = 0.6; + ctx.strokeRect(left * 32, top * 32, n * 32, n * 32); + } + } + } + ctx.restore(); + } + + core.plugin.halo = { + drawHalo + }; +})(); diff --git a/public/project/plugin/hero.js b/public/project/plugin/hero.js new file mode 100644 index 0000000..f7daece --- /dev/null +++ b/public/project/plugin/hero.js @@ -0,0 +1,86 @@ +'use strict'; + +(function () { + /** + * 获取勇士在某一点的属性 + * @param {keyof HeroStatus | 'all'} name + * @param {number} x + * @param {number} y + * @param {FloorIds} floorId + */ + function getHeroStatusOn(name, x, y, floorId) { + return this.getRealStatusOf(core.status.hero, name, x, y, floorId); + } + + function getHeroStatusOf(status, name, x, y, floorId) { + return getRealStatus(status, name, x, y, floorId); + } + + function getRealStatus(status, name, x, y, floorId) { + if (name instanceof Array) { + return Object.fromEntries( + name.map(v => [ + v, + v !== 'all' && getRealStatus(status, v, x, y, floorId) + ]) + ); + } + + if (name === 'all') { + return Object.fromEntries( + Object.keys(core.status.hero).map(v => [ + v, + v !== 'all' && getRealStatus(status, v, x, y, floorId) + ]) + ); + } + + let s = status?.[name] ?? core.status.hero[name]; + if (s === null || s === void 0) { + throw new ReferenceError( + `Wrong hero status property name is delivered: ${name}` + ); + } + + x ??= core.status.hero.loc.x; + y ??= core.status.hero.loc.y; + floorId ??= core.status.floorId; + + // 永夜、极昼 + if (name === 'atk' || name === 'def') { + s += window.flags?.[`night_${floorId}`] ?? 0; + } + + // 技能 + if (flags.bladeOn && flags.blade) { + const level = core.plugin.skillTree.getSkillLevel(2); + if (name === 'atk') { + s *= 1 + 0.1 * level; + } + if (name === 'def') { + s *= 1 - 0.1 * level; + } + } + if (flags.shield && flags.shieldOn) { + const level = core.plugin.skillTree.getSkillLevel(10); + if (name === 'atk') { + s *= 1 - 0.1 * level; + } + if (name === 'def') { + s *= 1 + 0.1 * level; + } + } + + // buff + if (typeof s === 'number') s *= core.getBuff(name); + + // 取整 + if (typeof s === 'number') s = Math.floor(s); + return s; + } + + core.plugin.hero = { + getHeroStatusOf, + getHeroStatusOn + }; +})(); diff --git a/public/project/plugin/heroFourFrames.js b/public/project/plugin/heroFourFrames.js new file mode 100644 index 0000000..5b5deea --- /dev/null +++ b/public/project/plugin/heroFourFrames.js @@ -0,0 +1,59 @@ +'use strict'; + +(function () { + ['up', 'down', 'left', 'right'].forEach(one => { + // 指定中间帧动画 + core.material.icons.hero[one].midFoot = 2; + }); + + var heroMoving = timestamp => { + if (core.status.heroMoving <= 0) return; + if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) { + core.animateFrame.leftLeg++; + core.animateFrame.moveTime = timestamp; + } + core.drawHero( + ['stop', 'leftFoot', 'midFoot', 'rightFoot'][ + core.animateFrame.leftLeg % 4 + ], + 4 * core.status.heroMoving + ); + }; + core.registerAnimationFrame('heroMoving', true, heroMoving); + + core.events._eventMoveHero_moving = function (step, moveSteps) { + var curr = moveSteps[0]; + var direction = curr[0], + x = core.getHeroLoc('x'), + y = core.getHeroLoc('y'); + // ------ 前进/后退 + var o = direction == 'backward' ? -1 : 1; + if (direction == 'forward' || direction == 'backward') + direction = core.getHeroLoc('direction'); + var faceDirection = direction; + if (direction == 'leftup' || direction == 'leftdown') + faceDirection = 'left'; + if (direction == 'rightup' || direction == 'rightdown') + faceDirection = 'right'; + core.setHeroLoc('direction', direction); + if (curr[1] <= 0) { + core.setHeroLoc('direction', faceDirection); + moveSteps.shift(); + return true; + } + if (step <= 4) core.drawHero('stop', 4 * o * step); + else if (step <= 8) core.drawHero('leftFoot', 4 * o * step); + else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8)); + else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) { + if (step == 8 || step == 16) { + core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true); + core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true); + core.updateFollowers(); + curr[1]--; + if (curr[1] <= 0) moveSteps.shift(); + core.setHeroLoc('direction', faceDirection); + return step == 16; + } + return false; + }; +})(); diff --git a/public/project/plugin/hotReload.js b/public/project/plugin/hotReload.js new file mode 100644 index 0000000..2cee838 --- /dev/null +++ b/public/project/plugin/hotReload.js @@ -0,0 +1,245 @@ +'use strict'; + +(function () { + if (main.mode !== 'play' || main.replayChecking) return; + + /** + * 发送请求 + * @param {string} url + * @param {string} type + * @param {string} data + * @returns {Promise} + */ + async function post(url, type, data) { + const xhr = new XMLHttpRequest(); + xhr.open(type, url); + xhr.send(data); + const res = await new Promise(res => { + xhr.onload = () => { + if (xhr.status !== 200) { + console.error(`hot reload: http ${xhr.status}`); + res('@error'); + } else res('success'); + }; + xhr.onerror = () => { + res('@error'); + console.error(`hot reload: error on connection`); + }; + }); + if (res === 'success') return xhr.response; + else return '@error'; + } + + /** + * 热重载css + * @param {string} data + */ + function reloadCss(data) { + const css = document.getElementById('mota-css'); + css.remove(); + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = data; + link.id = 'mota-css'; + document.head.appendChild(link); + console.log(`css hot reload: ${data}`); + } + + /** + * 热重载楼层 + * @param {string} data + */ + async function reloadFloor(data) { + // 如果被砍层了直接忽略 + if ( + core.status.maps[data].deleted || + core.status.maps[data].forceDelete + ) + return; + // 首先重新加载main.floors对应的楼层 + await import(`/project/floors/${data}.js?v=${Date.now()}`); + // 然后写入core.floors并解析 + core.floors[data] = main.floors[data]; + const floor = core.loadFloor(data); + if (core.isPlaying()) { + core.status.maps[data] = floor; + delete core.status.mapBlockObjs[data]; + core.extractBlocks(data); + if (data === core.status.floorId) { + core.drawMap(data); + let weather = core.getFlag('__weather__', null); + if (!weather && core.status.thisMap.weather) + weather = core.status.thisMap.weather; + if (weather) core.setWeather(weather[0], weather[1]); + else core.setWeather(); + } + core.updateStatusBar(true, true); + } + console.log(`floor hot reload: ${data}`); + } + + /** + * 热重载脚本编辑及插件编写 + * @param {string} data + */ + async function reloadScript(data) { + if (data === 'plugins') { + // 插件编写比较好办 + const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; + // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 + const script = document.createElement('script'); + script.src = `/project/plugins.js?v=${Date.now()}`; + document.body.appendChild(script); + await new Promise(res => { + script.onload = () => res('success'); + }); + const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; + // 找到差异的函数 + for (const id in before) { + const fn = before[id]; + if (typeof fn !== 'function') continue; + if (fn.toString() !== after[id]?.toString()) { + try { + core.plugin[id] = after[id]; + core.plugin[id].call(core.plugin); + core.updateStatusBar(true, true); + console.log(`plugin hot reload: ${id}`); + } catch (e) { + console.error(e); + } + } + } + } else if (data === 'functions') { + // 脚本编辑略微麻烦点 + const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; + // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 + const script = document.createElement('script'); + script.src = `/project/functions.js?v=${Date.now()}`; + document.body.appendChild(script); + await new Promise(res => { + script.onload = () => res('success'); + }); + const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; + // 找到差异的函数 + for (const mod in before) { + const fns = before[mod]; + for (const id in fns) { + const fn = fns[id]; + if (typeof fn !== 'function' || id === 'hasSpecial') + continue; + const now = after[mod][id]; + if (fn.toString() !== now.toString()) { + try { + if (mod === 'events') { + core.events.eventdata[id] = now; + } else if (mod === 'enemys') { + core.enemys.enemydata[id] = now; + } else if (mod === 'actions') { + core.actions.actionsdata[id] = now; + } else if (mod === 'control') { + core.control.controldata[id] = now; + } else if (mod === 'ui') { + core.ui.uidata[id] = now; + } + core.updateStatusBar(true, true); + console.log(`function hot reload: ${mod}.${id}`); + } catch (e) { + console.error(e); + } + } + } + } + } + } + + /** + * 属性热重载,包括全塔属性等 + * @param {string} data + */ + async function reloadData(data) { + const script = document.createElement('script'); + script.src = `/project/${data}.js?v=${Date.now()}`; + document.body.appendChild(script); + await new Promise(res => { + script.onload = () => res('success'); + }); + + let after; + if (data === 'data') after = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + if (data === 'enemys') + after = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; + if (data === 'icons') + after = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; + if (data === 'items') + after = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a; + if (data === 'maps') after = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; + if (data === 'events') + after = events_c12a15a8_c380_4b28_8144_256cba95f760; + + if (data === 'enemys') { + core.enemys.enemys = after; + for (var enemyId in after) { + core.enemys.enemys[enemyId].id = enemyId; + } + core.material.enemys = core.getEnemys(); + } else if (data === 'icons') { + core.icons.icons = after; + core.material.icons = core.getIcons(); + } else if (data === 'items') { + core.items.items = after; + for (var itemId in after) { + core.items.items[itemId].id = itemId; + } + core.material.items = core.getItems(); + } else if (data === 'maps') { + core.maps.blocksInfo = after; + core.status.mapBlockObjs = {}; + core.status.number2block = {}; + Object.values(core.status.maps).forEach(v => delete v.blocks); + core.extractBlocks(); + core.setWeather( + core.animateFrame.weather.type, + core.animateFrame.weather.level + ); + core.drawMap(); + } else if (data === 'events') { + core.events.commonEvent = after.commonEvent; + } else if (data === 'data') { + location.reload(); + } + core.updateStatusBar(true, true); + console.log(`data hot reload: ${data}`); + } + + // 初始化 + (async function () { + const data = await post('/reload', 'POST', 'test'); + if (data === '@error') { + console.log(`未检测到node服务,热重载插件将无法使用`); + } else { + console.log(`热重载插件加载成功`); + // reload + setInterval(async () => { + const res = await post('/reload', 'POST'); + if (res === '@error') return; + if (res === 'true') location.reload(); + else return; + }, 1000); + + // hot reload + setInterval(async () => { + const res = await post('/hotReload', 'POST'); + const data = res.split('@@'); + data.forEach(v => { + if (v === '') return; + const [type, file] = v.split(':'); + if (type === 'css') reloadCss(file); + if (type === 'data') reloadData(file); + if (type === 'floor') reloadFloor(file); + if (type === 'script') reloadScript(file); + }); + }, 1000); + } + })(); +})(); diff --git a/public/project/plugin/itemDetail.js b/public/project/plugin/itemDetail.js new file mode 100644 index 0000000..c66f846 --- /dev/null +++ b/public/project/plugin/itemDetail.js @@ -0,0 +1,129 @@ +'use strict'; + +(function () { + core.control.updateDamage = function (floorId, ctx) { + floorId = floorId || core.status.floorId; + if (!floorId || core.status.gameOver || main.mode != 'play') return; + const onMap = ctx == null; + + // 没有怪物手册 + if (!core.hasItem('book')) return; + core.status.damage.posX = core.bigmap.posX; + core.status.damage.posY = core.bigmap.posY; + if (!onMap) { + const width = core.floors[floorId].width, + height = core.floors[floorId].height; + // 地图过大的缩略图不绘制显伤 + if (width * height > core.bigmap.threshold) return; + } + this._updateDamage_damage(floorId, onMap); + this._updateDamage_extraDamage(floorId, onMap); + getItemDetail(floorId, onMap); // 宝石血瓶详细信息 + this.drawDamage(ctx); + }; + + // 获取宝石信息 并绘制 + function getItemDetail(floorId, onMap) { + if (!core.getFlag('itemDetail')) return; + floorId ??= core.status.thisMap.floorId; + let diff = {}; + const before = core.status.hero; + const hero = core.clone(core.status.hero); + const handler = { + set(target, key, v) { + diff[key] = v - (target[key] || 0); + if (!diff[key]) diff[key] = void 0; + return true; + } + }; + core.status.hero = new Proxy(hero, handler); + + core.status.maps[floorId].blocks.forEach(function (block) { + if (block.event.cls !== 'items' || block.disable) return; + const x = block.x, + y = block.y; + // v2优化,只绘制范围内的部分 + if (onMap && core.bigmap.v2) { + if ( + x < core.bigmap.posX - core.bigmap.extend || + x > core.bigmap.posX + core._PX_ + core.bigmap.extend || + y < core.bigmap.posY - core.bigmap.extend || + y > core.bigmap.posY + core._PY_ + core.bigmap.extend + ) { + return; + } + } + diff = {}; + const id = block.event.id; + const item = core.material.items[id]; + if (item.cls === 'equips') { + // 装备也显示 + const diff = core.clone(item.equip.value ?? {}); + const per = item.equip.percentage ?? {}; + for (const name in per) { + diff[name + 'per'] = per[name].toString() + '%'; + } + drawItemDetail(diff, x, y); + return; + } + // 跟数据统计原理一样 执行效果 前后比较 + core.setFlag('__statistics__', true); + try { + eval(item.itemEffect); + } catch (error) {} + drawItemDetail(diff, x, y); + }); + core.status.hero = before; + window.hero = before; + window.flags = before.flags; + } + + // 绘制 + function drawItemDetail(diff, x, y) { + const px = 32 * x + 2, + py = 32 * y + 31; + let content = ''; + // 获得数据和颜色 + let i = 0; + for (const name in diff) { + if (!diff[name]) continue; + let color = '#fff'; + + if (typeof diff[name] === 'number') + content = core.formatBigNumber(diff[name], true); + else content = diff[name]; + switch (name) { + case 'atk': + case 'atkper': + color = '#FF7A7A'; + break; + case 'def': + case 'defper': + color = '#00E6F1'; + break; + case 'mdef': + case 'mdefper': + color = '#6EFF83'; + break; + case 'hp': + color = '#A4FF00'; + break; + case 'hpmax': + case 'hpmaxper': + color = '#F9FF00'; + break; + case 'mana': + color = '#c66'; + break; + } + // 绘制 + core.status.damage.data.push({ + text: content, + px: px, + py: py - 10 * i, + color: color + }); + i++; + } + } +})(); diff --git a/public/project/plugin/loopMap.js b/public/project/plugin/loopMap.js new file mode 100644 index 0000000..036daee --- /dev/null +++ b/public/project/plugin/loopMap.js @@ -0,0 +1,239 @@ +'use strict'; + +(function () { + const { slide } = core.plugin.utils; + const list = ['tower6']; + + /** + * 设置循环地图的偏移量 + * @param {number} offset 横向偏移量 + * @param {FloorIds} floorId + */ + function setLoopMap(offset, floorId) { + const floor = core.status.maps[floorId]; + if (offset < 9) { + moveMap(floor.width - 17, floorId); + } + if (offset > floor.width - 9) { + moveMap(17 - floor.width, floorId); + } + } + + /** + * 当勇士移动时自动设置循环地图 + * @param {FloorIds} floorId + */ + function autoSetLoopMap(floorId) { + setLoopMap(core.status.hero.loc.x, floorId); + } + + function checkLoopMap() { + if (isLoopMap(core.status.floorId)) { + autoSetLoopMap(core.status.floorId); + } + } + + /** + * 移动地图 + * @param {number} delta + * @param {FloorIds} floorId + */ + function moveMap(delta, floorId) { + core.extractBlocks(floorId); + const floor = core.status.maps[floorId]; + core.setHeroLoc('x', core.status.hero.loc.x + delta); + flags[`loop_${floorId}`] += delta; + flags[`loop_${floorId}`] %= floor.width; + const origin = floor.blocks.slice(); + for (let i = 0; i < origin.length; i++) { + core.removeBlockByIndex(0, floorId); + core.removeGlobalAnimate(origin[i].x, origin[i].y); + } + origin.forEach(v => { + let to = v.x + delta; + if (to >= floor.width) to -= floor.width; + if (to < 0) to += floor.width; + core.setBlock(v.id, to, v.y, floorId, true); + core.setMapBlockDisabled(floorId, to, v.y, false); + }); + core.drawMap(); + core.drawHero(); + } + + function isLoopMap(floorId) { + return list.includes(floorId); + } + + events.prototype._sys_changeFloor = function (data, callback) { + data = data.event.data; + let heroLoc = {}; + if (isLoopMap(data.floorId)) { + const floor = core.status.maps[data.floorId]; + flags[`loop_${data.floorId}`] ??= 0; + let tx = data.loc[0] + flags[`loop_${data.floorId}`]; + tx %= floor.width; + if (tx < 0) tx += floor.width; + heroLoc = { + x: tx, + y: data.loc[1] + }; + } else if (data.loc) heroLoc = { x: data.loc[0], y: data.loc[1] }; + if (data.direction) heroLoc.direction = data.direction; + if (core.status.event.id != 'action') core.status.event.id = null; + core.changeFloor( + data.floorId, + data.stair, + heroLoc, + data.time, + function () { + core.replay(); + if (callback) callback(); + } + ); + }; + + events.prototype.trigger = function (x, y, callback) { + var _executeCallback = function () { + // 因为trigger之后还有可能触发其他同步脚本(比如阻激夹域检测) + // 所以这里强制callback被异步触发 + if (callback) { + setTimeout(callback, 1); // +1是为了录像检测系统 + } + return; + }; + if (core.status.gameOver) return _executeCallback(); + if (core.status.event.id == 'action') { + core.insertAction( + { + type: 'function', + function: + 'function () { core.events._trigger_inAction(' + + x + + ',' + + y + + '); }', + async: true + }, + null, + null, + null, + true + ); + return _executeCallback(); + } + if (core.status.event.id) return _executeCallback(); + + let block = core.getBlock(x, y); + const id = core.status.floorId; + const loop = isLoopMap(id); + if (loop && flags[`loop_${id}`] !== 0) { + if (block && block.event.trigger === 'changeFloor') { + delete block.event.trigger; + core.maps._addInfo(block); + } else { + const floor = core.status.maps[id]; + let tx = x - flags[`loop_${id}`]; + tx %= floor.width; + if (tx < 0) tx += floor.width; + const c = core.floors[id].changeFloor[`${tx},${y}`]; + if (c) { + const b = { event: {}, x: tx, y }; + b.event.data = c; + b.event.trigger = 'changeFloor'; + block = b; + } + } + } + + if (block == null) return _executeCallback(); + + // 执行该点的脚本 + if (block.event.script) { + core.clearRouteFolding(); + try { + eval(block.event.script); + } catch (ee) { + console.error(ee); + } + } + + // 碰触事件 + if (block.event.event) { + core.clearRouteFolding(); + core.insertAction(block.event.event, block.x, block.y); + // 不再执行该点的系统事件 + return _executeCallback(); + } + + if (block.event.trigger && block.event.trigger != 'null') { + var noPass = block.event.noPass, + trigger = block.event.trigger; + if (noPass) core.clearAutomaticRouteNode(x, y); + + // 转换楼层能否穿透 + if ( + trigger == 'changeFloor' && + !noPass && + this._trigger_ignoreChangeFloor(block) && + !loop + ) + return _executeCallback(); + core.status.automaticRoute.moveDirectly = false; + this.doSystemEvent(trigger, block); + } + return _executeCallback(); + }; + + maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { + floorId = floorId || core.status.floorId; + if (!floorId) return []; + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + + if (!noCache && core.status[name + 'maps'][floorId]) + return core.status[name + 'maps'][floorId]; + + var arr = + main.mode == 'editor' && + !(window.editor && editor.uievent && editor.uievent.isOpen) + ? core.cloneArray(editor[name + 'map']) + : null; + if (arr == null) + arr = core.cloneArray(core.floors[floorId][name + 'map'] || []); + + if (isLoopMap(floorId) && window.flags) { + flags[`loop_${floorId}`] ??= 0; + arr.forEach(v => { + slide(v, flags[`loop_${floorId}`] % width); + }); + } + + for (var y = 0; y < height; ++y) { + if (arr[y] == null) arr[y] = Array(width).fill(0); + } + (core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach( + function (one) { + arr[one[1]][one[0]] = one[2] || 0; + } + ); + (core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach( + function (one) { + arr[one[1]][one[0]] = 0; + } + ); + if (main.mode == 'editor') { + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + arr[y][x] = arr[y][x].idnum || arr[y][x] || 0; + } + } + } + if (core.status[name + 'maps']) + core.status[name + 'maps'][floorId] = arr; + return arr; + }; + + core.plugin.loopMap = { + checkLoopMap + }; +})(); diff --git a/public/project/plugin/popup.js b/public/project/plugin/popup.js new file mode 100644 index 0000000..7858eda --- /dev/null +++ b/public/project/plugin/popup.js @@ -0,0 +1,123 @@ +'use strict'; + +(function () { + // 伤害弹出 + // 复写阻激夹域检测 + control.prototype.checkBlock = function (forceMockery) { + var x = core.getHeroLoc('x'), + y = core.getHeroLoc('y'), + loc = x + ',' + y; + var damage = core.status.checkBlock.damage[loc]; + if (damage) { + if (!main.replayChecking) + core.addPop( + (x - core.bigmap.offsetX / 32) * 32 + 12, + (y - core.bigmap.offsetY / 32) * 32 + 20, + -damage.toString() + ); + core.status.hero.hp -= damage; + var text = + Object.keys(core.status.checkBlock.type[loc] || {}).join( + ',' + ) || '伤害'; + core.drawTip('受到' + text + damage + '点'); + core.drawHeroAnimate('zone'); + this._checkBlock_disableQuickShop(); + core.status.hero.statistics.extraDamage += damage; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } else { + core.updateStatusBar(); + } + } + this._checkBlock_repulse(core.status.checkBlock.repulse[loc]); + checkMockery(loc, forceMockery); + }; + + control.prototype.moveHero = function (direction, callback) { + // 如果正在移动,直接return + if (core.status.heroMoving != 0) return; + if (core.isset(direction)) core.setHeroLoc('direction', direction); + + const nx = core.nextX(); + const ny = core.nextY(); + if (core.status.checkBlock.mockery[`${nx},${ny}`]) { + core.autosave(); + } + + if (callback) return this.moveAction(callback); + this._moveHero_moving(); + }; + + /** + * 电摇嘲讽 + * @param {LocString} loc + * @param {boolean} force + */ + function checkMockery(loc, force) { + if (core.status.lockControl && !force) return; + const mockery = core.status.checkBlock.mockery[loc]; + if (mockery) { + mockery.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + const action = []; + const [tx, ty] = mockery[0]; + let { x, y } = core.status.hero.loc; + const dir = + x > tx ? 'left' : x < tx ? 'right' : y > ty ? 'up' : 'down'; + const { x: dx, y: dy } = core.utils.scan[dir]; + + action.push({ type: 'changePos', direction: dir }); + const blocks = core.getMapBlocksObj(); + while (1) { + x += dx; + y += dy; + const block = blocks[`${x},${y}`]; + if (block) { + block.event.cls === ''; + if ( + [ + 'animates', + 'autotile', + 'tileset', + 'npcs', + 'npc48' + ].includes(block.event.cls) + ) { + action.push( + { + type: 'hide', + loc: [[x, y]], + remove: true, + time: 0 + }, + { + type: 'function', + function: `function() { core.removeGlobalAnimate(${x}, ${y}) }` + }, + { + type: 'animate', + name: 'hand', + loc: [x, y], + async: true + } + ); + } + if (block.event.cls.startsWith('enemy')) { + action.push({ type: 'moveAction' }); + } + } + action.push({ type: 'moveAction' }); + if (x === tx && y === ty) break; + } + action.push({ + type: 'function', + function: `function() { core.checkBlock(true); }` + }); + action.push({ type: 'stopAsync' }); + core.insertAction(action); + } + } +})(); diff --git a/public/project/plugin/remainEnemy.js b/public/project/plugin/remainEnemy.js new file mode 100644 index 0000000..932a9ca --- /dev/null +++ b/public/project/plugin/remainEnemy.js @@ -0,0 +1,75 @@ +'use strict'; + +(function () { + /** + * 检查漏怪 + * @param {FloorIds[]} floorIds + */ + function checkRemainEnemy(floorIds) { + /** + * @type {Record} + */ + const enemy = {}; + floorIds.forEach(v => { + core.extractBlocks(v); + const blocks = core.status.maps[v].blocks; + blocks.forEach(block => { + if (!block.event.cls.startsWith('enemy') || block.disable) + return; + /** + * @type {EnemyIds} + */ + const id = block.event.id; + enemy[v] ??= []; + const info = enemy[v]; + info.push({ loc: [block.x, block.y], id }); + }); + }); + return enemy; + } + + /** + * 获取剩余怪物字符串 + * @param {FloorIds[]} floorIds + */ + function getRemainEnemyString(floorIds) { + const enemy = checkRemainEnemy(floorIds); + const str = []; + let now = []; + for (const floor in enemy) { + /** + * @type {{loc: LocArr, id: EnemyIds}[]} + */ + const all = enemy[floor]; + /** + * @type {Record} + */ + const remain = {}; + all.forEach(v => { + const id = v.id; + remain[id] ??= 0; + remain[id]++; + }); + const title = core.status.maps[floor].title; + for (const id in remain) { + const name = core.material.enemys[id].name; + now.push(`${title}(${floor}): ${name} * ${remain[id]}`); + if (now.length === 10) { + str.push(now.join('\n')); + now = []; + } + } + } + if (now.length > 0) { + str.push(now.join('\n')); + str[0] = `当前剩余怪物:\n${str[0]}`; + } + + return str; + } + + core.plugin.remainEnemy = { + checkRemainEnemy, + getRemainEnemyString + }; +})(); diff --git a/public/project/plugin/removeMap.js b/public/project/plugin/removeMap.js new file mode 100644 index 0000000..8bd0467 --- /dev/null +++ b/public/project/plugin/removeMap.js @@ -0,0 +1,117 @@ +'use strict'; + +(function () { + function removeMaps(fromId, toId, force) { + toId = toId || fromId; + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + if (toIndex < 0) toIndex = core.floorIds.length - 1; + flags.__visited__ = flags.__visited__ || {}; + flags.__removed__ = flags.__removed__ || []; + flags.__disabled__ = flags.__disabled__ || {}; + flags.__leaveLoc__ = flags.__leaveLoc__ || {}; + flags.__forceDelete__ ??= {}; + let deleted = false; + for (var i = fromIndex; i <= toIndex; ++i) { + var floorId = core.floorIds[i]; + if (core.status.maps[floorId].deleted) continue; + delete flags.__visited__[floorId]; + flags.__removed__.push(floorId); + delete flags.__disabled__[floorId]; + delete flags.__leaveLoc__[floorId]; + (core.status.autoEvents || []).forEach(event => { + if (event.floorId == floorId && event.currentFloor) { + core.autoEventExecuting(event.symbol, false); + core.autoEventExecuted(event.symbol, false); + } + }); + core.status.maps[floorId].deleted = true; + core.status.maps[floorId].canFlyTo = false; + core.status.maps[floorId].canFlyFrom = false; + core.status.maps[floorId].cannotViewMap = true; + if (force) { + core.status.maps[floorId].forceDelete = true; + flags.__forceDelete__[floorId] = true; + } + deleteFlags(floorId); + deleted = true; + } + if (deleted && !main.replayChecking) { + core.splitArea(); + } + } + + function deleteFlags(floorId) { + delete flags[`jump_${floorId}`]; + delete flags[`inte_${floorId}`]; + delete flags[`loop_${floorId}`]; + delete flags[`melt_${floorId}`]; + delete flags[`night_${floorId}`]; + } + + // 恢复楼层 + // core.plugin.removeMap.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层 + // core.plugin.removeMap.resumeMaps("MT10") 只恢复MT10层 + function resumeMaps(fromId, toId) { + toId = toId || fromId; + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + if (toIndex < 0) toIndex = core.floorIds.length - 1; + flags.__removed__ = flags.__removed__ || []; + for (var i = fromIndex; i <= toIndex; ++i) { + var floorId = core.floorIds[i]; + if (!core.status.maps[floorId].deleted) continue; + if ( + core.status.maps[floorId].forceDelete || + flags.__forceDelete__[floorId] + ) + continue; + flags.__removed__ = flags.__removed__.filter(f => { + return f != floorId; + }); + core.status.maps[floorId] = core.loadFloor(floorId); + } + } + + // 分区砍层相关 + var inAnyPartition = floorId => { + var inPartition = false; + (core.floorPartitions || []).forEach(floor => { + var fromIndex = core.floorIds.indexOf(floor[0]); + var toIndex = core.floorIds.indexOf(floor[1]); + var index = core.floorIds.indexOf(floorId); + if (fromIndex < 0 || index < 0) return; + if (toIndex < 0) toIndex = core.floorIds.length - 1; + if (index >= fromIndex && index <= toIndex) inPartition = true; + }); + return inPartition; + }; + + // 分区砍层 + function autoRemoveMaps(floorId) { + if (main.mode != 'play' || !inAnyPartition(floorId)) return; + // 根据分区信息自动砍层与恢复 + (core.floorPartitions || []).forEach(floor => { + var fromIndex = core.floorIds.indexOf(floor[0]); + var toIndex = core.floorIds.indexOf(floor[1]); + var index = core.floorIds.indexOf(floorId); + if (fromIndex < 0 || index < 0) return; + if (toIndex < 0) toIndex = core.floorIds.length - 1; + if (index >= fromIndex && index <= toIndex) { + core.plugin.removeMap.resumeMaps( + core.floorIds[fromIndex], + core.floorIds[toIndex] + ); + } else { + removeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]); + } + }); + } + + core.plugin.removeMap = { + removeMaps, + deleteFlags, + resumeMaps, + autoRemoveMaps + }; +})(); diff --git a/public/project/plugin/replay.js b/public/project/plugin/replay.js new file mode 100644 index 0000000..cf53f32 --- /dev/null +++ b/public/project/plugin/replay.js @@ -0,0 +1,90 @@ +'use strict'; + +(function () { + const { studySkill, canStudySkill } = core.plugin.study; + const replayableSettings = ['autoSkill']; + + // 注册修改设置的录像操作 + core.registerReplayAction('settings', name => { + if (!name.startsWith('set:')) return false; + const [, setting, value] = name.split(':'); + const v = eval(value); + if (typeof v !== 'boolean') return false; + if (!replayableSettings.includes(setting)) return false; + flags[setting] = v; + core.replay(); + return true; + }); + + core.registerReplayAction('upgradeSkill', name => { + if (!name.startsWith('skill:')) return false; + const skill = parseInt(name.slice(6)); + core.plugin.skillTree.upgradeSkill(skill); + core.replay(); + return true; + }); + + core.registerReplayAction('study', name => { + if (!name.startsWith('study:')) return false; + const [num, x, y] = name + .slice(6) + .split(',') + .map(v => parseInt(v)); + if (!canStudySkill(num)) return false; + const id = core.getBlockId(x, y); + const enemy = core.getEnemyInfo(id, void 0, x, y); + if (!enemy.special.includes(num)) return false; + studySkill(enemy, num); + core.replay(); + return true; + }); + + // 商店 + let shopOpened = false; + let openedShopId = ''; + core.registerReplayAction('openShop', name => { + if (!name.startsWith('openShop:')) return false; + openedShopId = name.slice(9); + shopOpened = true; + core.replay(); + return true; + }); + + core.registerReplayAction('buy', name => { + if (!name.startsWith('buy:') && !name.startsWith('sell:')) return false; + if (!shopOpened) return false; + if (!openedShopId) return false; + const [type, id, num] = name + .split(':') + .map(v => (/^\d+$/.test(v) ? parseInt(v) : v)); + const shop = core.status.shops[id]; + const item = shop.choices.find(v => v.id === id); + if (!item) return false; + flags.itemShop ??= {}; + flags.itemShop[openedShopId] ??= {}; + flags.itemShop[openedShopId][id] ??= 0; + if (num > item.number - flags.itemShop[openedShopId][id]) { + return false; + } + let cost = 0; + if (type === 'buy') { + cost = item.money * num; + } else { + cost = -item.sell * num; + } + if (cost > core.status.hero.money) return false; + core.status.hero.money -= cost; + flags.itemShop[openedShopId][id] += type === 'buy' ? num : -num; + core.replay(); + return true; + }); + + core.registerReplayAction('closeShop', name => { + if (name !== 'closeShop') return false; + if (!shopOpened) return false; + shopOpened = false; + openedShopId = ''; + core.replay(); + return true; + }); +})(); diff --git a/public/project/plugin/shop.js b/public/project/plugin/shop.js new file mode 100644 index 0000000..4b3176d --- /dev/null +++ b/public/project/plugin/shop.js @@ -0,0 +1,76 @@ +'use strict'; + +(function () { + const { openItemShop } = core.plugin.gameUi; + + function openShop(shopId, noRoute) { + var shop = core.status.shops[shopId]; + // Step 1: 检查能否打开此商店 + if (!this.canOpenShop(shopId)) { + core.drawTip('该商店尚未开启'); + return false; + } + + // Step 3: 检查道具商店 or 公共事件 + if (shop.item) { + if (openItemShop) openItemShop(shopId); + return; + } + return true; + } + + /// 是否访问过某个快捷商店 + function isShopVisited(id) { + flags.__shops__ ??= {}; + var shops = core.getFlag('__shops__'); + if (!shops[id]) shops[id] = {}; + return shops[id].visited; + } + + /// 当前应当显示的快捷商店列表 + function listShopIds() { + return Object.keys(core.status.shops).filter(id => { + return ( + core.plugin.shop.isShopVisited(id) || + !core.status.shops[id].mustEnable + ); + }); + } + + /// 是否能够打开某个商店 + function canOpenShop(id) { + if (this.isShopVisited(id)) return true; + var shop = core.status.shops[id]; + if (shop.item || shop.commonEvent || shop.mustEnable) return false; + return true; + } + + /// 启用或禁用某个快捷商店 + function setShopVisited(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; + } + + /// 能否使用快捷商店 + function canUseQuickShop() { + // 如果返回一个字符串,表示不能,字符串为不能使用的提示 + // 返回null代表可以使用 + + // 检查当前楼层的canUseQuickShop选项是否为false + if (core.status.thisMap.canUseQuickShop === false) + return '当前楼层不能使用快捷商店。'; + return null; + } + + core.plugin.shop = { + openShop, + isShopVisited, + listShopIds, + canOpenShop, + setShopVisited, + canUseQuickShop + }; +})(); diff --git a/public/project/plugin/skillTree.js b/public/project/plugin/skillTree.js new file mode 100644 index 0000000..2364b57 --- /dev/null +++ b/public/project/plugin/skillTree.js @@ -0,0 +1,319 @@ +'use strict'; + +(function () { + /** + * @type {number[]} + */ + let levels = []; + + /** + * @type {Record} + */ + const skills = { + chapter1: [ + { + index: 0, + title: '力量', + desc: ['力量就是根本!可以通过智慧增加力量,每级增加2点攻击。'], + consume: '10 * level + 10', + front: [], + loc: [1, 2], + max: 10, + effect: ['攻击 + ${level * 2}'] + }, + { + index: 1, + title: '致命一击', + desc: ['爆发出全部力量攻击敌人,每级增加5点额外攻击。'], + consume: '30 * level + 30', + front: [[0, 5]], + loc: [2, 1], + max: 10, + effect: ['额外攻击 + ${level * 5}'] + }, + { + index: 2, + title: '断灭之刃', + desc: [ + '主动技能,快捷键1,', + '开启后会在战斗时会额外增加一定量的攻击,但同时减少一定量的防御。' + ], + consume: '200 * level + 400', + front: [[1, 5]], + loc: [4, 1], + max: 5, + effect: ['增加${level * 10}%攻击,减少${level * 10}%防御'] + }, + { + index: 3, + title: '坚韧', + desc: ['由智慧转化出坚韧!每级增加2点防御'], + consume: '10 * level + 10', + front: [], + loc: [1, 4], + max: 10, + effect: ['防御 + ${level * 2}'] + }, + { + index: 4, + title: '回春', + desc: ['让智慧化为治愈之泉水!每级增加1点生命回复'], + consume: '20 * level + 20', + front: [[3, 5]], + loc: [2, 5], + max: 25, + effect: ['生命回复 + ${level}'] + }, + { + index: 5, + title: '治愈之泉', + desc: [ + '让生命变得更多一些吧!每吃50瓶血瓶就增加当前生命回复10%的生命回复' + ], + consume: '1500', + front: [[4, 25]], + loc: [4, 5], + max: 1, + effect: ['50瓶血10%生命回复'] + }, + { + index: 6, + title: '坚固之盾', + desc: ['让护甲更加坚硬一些吧!每级增加10点防御'], + consume: '50 + level * 50', + front: [[3, 5]], + loc: [2, 3], + max: 10, + effect: ['防御 + ${level * 10}'] + }, + { + index: 7, + title: '无上之盾', + desc: [ + '第一章终极技能,战斗时智慧会充当等量护盾' + ], + consume: '2500', + front: [ + [6, 10], + [5, 1], + [2, 2] + ], + loc: [5, 3], + max: 1, + effect: ['战斗时智慧会充当护盾'] + } + ], + chapter2: [ + { + index: 8, + title: '锋利', + desc: ['让剑变得更加锋利!每级使攻击增加1%(buff式增加)'], + consume: 'level > 5 ? 50 * level ** 2 : 250 * level + 250', + front: [], + loc: [1, 2], + max: 15, + effect: ['攻击增加${level}%'] + }, + { + index: 9, + title: '坚硬', + desc: ['让盾牌变得更加坚固!每级使防御增加1%(buff式增加)'], + consume: 'level > 5 ? 50 * level ** 2 : 250 * level + 250', + front: [], + loc: [1, 4], + max: 15, + effect: ['防御增加${level}%'] + }, + { + index: 10, + title: '铸剑为盾', + desc: [ + '主动技能,快捷键3,', + '减少一定的攻击,增加一定的防御' + ], + consume: '500 * level + 1000', + front: [[9, 5]], + loc: [2, 5], + max: 5, + effect: ['增加${level * 10}%的防御,减少${level * 10}%的攻击'] + }, + { + index: 11, + title: '学习', + desc: [ + '主动技能,可以消耗500智慧学习一个怪物的技能,', + '持续5场战斗,每学习一次消耗的智慧点增加250,每次升级使持续的战斗次数增加3次。更多信息可在学习后在百科全书查看。' + ], + consume: '2500 * level ** 2 + 2500', + front: [ + [8, 10], + [12, 5] + ], + loc: [4, 1], + max: 6, + effect: ['学习怪物技能,持续${level * 3 + 2}场战斗'] + }, + { + index: 12, + title: '聪慧', + desc: ['使主角变得更加聪明,每级使绿宝石增加的智慧点上升5%'], + consume: 'level > 5 ? 100 * level ** 2 : 250 * level + 1250', + front: [ + [8, 10], + [9, 10] + ], + loc: [3, 3], + max: 20, + effect: ['增加${level * 5}%绿宝石效果'] + }, + { + index: 13, + title: '治愈', + desc: ['使主角能够更好地回复生命,每级使血瓶的加血量增加2%'], + consume: 'level > 5 ? 100 * level ** 2 : 250 * level + 1250', + front: [[10, 3]], + loc: [4, 5], + max: 20, + effect: ['增加${level * 2}%的血瓶回血量'] + }, + { + index: 14, + title: '胜利之号', + desc: [ + '第二章终极技能,', + '每打一个怪物,勇士在本楼层对怪物造成的伤害便增加1%' + ], + consume: '15000', + front: [ + [13, 10], + [12, 10], + [11, 3] + ], + loc: [5, 3], + max: 1, + effect: ['每打一个怪,勇士造成的伤害增加1%'] + } + ] + }; + + core.plugin.skills = skills; + + function getSkillFromIndex(index) { + for (const [, skill] of Object.entries(skills)) { + const s = skill.find(v => v.index === index); + if (s) return s; + } + } + + /** + * 获取技能等级 + * @param {number} skill + */ + function getSkillLevel(skill) { + return (levels[skill] ??= 0); + } + + function getSkillConsume(skill) { + return eval( + this.getSkillFromIndex(skill).consume.replace( + /level(:\d+)?/g, + (str, $1) => { + if ($1) return `core.plugin.skillTree.getSkillLevel(${$1})`; + else return `core.plugin.skillTree.getSkillLevel(${skill})`; + } + ) + ); + } + + function openTree() { + if (main.replayChecking) return; + core.plugin.skillTreeOpened.value = true; + } + + /** + * 能否升级某个技能 + * @param {number} skill + */ + function canUpgrade(skill) { + const consume = core.plugin.skillTree.getSkillConsume(skill); + if (consume > core.status.hero.mdef) return false; + const level = core.plugin.skillTree.getSkillLevel(skill); + const s = getSkillFromIndex(skill); + if (level === s.max) return false; + const front = s.front; + for (const [skill, level] of front) { + if (core.plugin.skillTree.getSkillLevel(skill) < level) + return false; + } + return true; + } + + /** + * 实际升级效果 + * @param {number} skill + */ + function upgradeSkill(skill) { + if (!canUpgrade(skill)) return false; + switch (skill) { + case 0: // 力量 +2攻击 + core.status.hero.atk += 2; + break; + case 1: // 致命一击 +5额外攻击 + core.status.hero.mana += 5; + break; + case 2: // 断灭之刃 + core.setFlag('bladeOn', true); + break; + case 3: // 坚韧 +2防御 + core.status.hero.def += 2; + break; + case 4: // 回春 +1回复 + core.status.hero.hpmax += 1; + break; + case 5: // 治愈之泉 + core.setFlag('spring', true); + break; + case 6: // 坚固之盾 +10防御 + core.status.hero.def += 10; + break; + case 7: // 无上之盾 + core.setFlag('superSheild', true); + break; + case 8: // 锋利 +1%攻击 + core.addBuff('atk', 0.01); + break; + case 9: // 锋利 +1%防御 + core.addBuff('def', 0.01); + break; + case 10: // 铸剑为盾 + core.setFlag('shieldOn', true); + break; + case 11: // 学习 + core.setItem('I565', 1); + break; + } + const consume = getSkillConsume(skill); + core.status.hero.mdef -= consume; + levels[skill]++; + core.updateStatusBar(); + return true; + } + + function saveSkillTree() { + return levels.slice(); + } + + function loadSkillTree(data) { + levels = data ?? []; + } + + core.plugin.skillTree = { + getSkillConsume, + getSkillFromIndex, + getSkillLevel, + saveSkillTree, + loadSkillTree, + upgradeSkill, + openTree + }; +})(); diff --git a/public/project/plugin/skills.js b/public/project/plugin/skills.js new file mode 100644 index 0000000..46cb7ba --- /dev/null +++ b/public/project/plugin/skills.js @@ -0,0 +1,181 @@ +'use strict'; + +(function () { + // 所有的主动技能效果 + var ignoreInJump = { + event: ['X20007', 'X20001', 'X20006', 'X20014', 'X20010', 'X20007'], + bg: [ + 'X20037', + 'X20038', + 'X20039', + 'X20045', + 'X20047', + 'X20053', + 'X20054', + 'X20055', + 'X20067', + 'X20068', + 'X20075', + 'X20076' + ] + }; + + /** @type {FloorIds[]} */ + const jumpIgnoreFloor = ['MT31', 'snowTown']; + // 跳跃 + function jumpSkill() { + if (core.status.floorId.startsWith('tower')) + return core.drawTip('当无法使用该技能'); + if (jumpIgnoreFloor.includes(core.status.floorId) || flags.onChase) { + return core.drawTip('当前楼层无法使用该技能'); + } + if (!flags.skill2) return; + if (!flags['jump_' + core.status.floorId]) + flags['jump_' + core.status.floorId] = 0; + if ( + core.status.floorId == 'MT14' && + flags['jump_' + core.status.floorId] == 2 && + !flags.MT14Jump + ) { + if ( + !( + core.status.hero.loc.x === 77 && + core.status.hero.loc.y === 5 && + core.status.hero.loc.direction === 'right' + ) + ) { + return core.drawTip('该地图还有一个必跳的地方,你还没有跳'); + } else flags.MT14Jump = true; + } + if (flags['jump_' + core.status.floorId] >= 3) + return core.drawTip('当前地图使用次数已用完'); + var direction = core.status.hero.loc.direction; + var loc = core.status.hero.loc; + var checkLoc = {}; + switch (direction) { + case 'up': + checkLoc.x = loc.x; + checkLoc.y = loc.y - 1; + break; + case 'right': + checkLoc.x = loc.x + 1; + checkLoc.y = loc.y; + break; + case 'down': + checkLoc.x = loc.x; + checkLoc.y = loc.y + 1; + break; + case 'left': + checkLoc.x = loc.x - 1; + checkLoc.y = loc.y; + break; + } + // 前方是否可通行 或 是怪物 + var cls = core.getBlockCls(checkLoc.x, checkLoc.y); + var noPass = core.noPass(checkLoc.x, checkLoc.y); + var id = core.getBlockId(checkLoc.x, checkLoc.y) || ''; + var bgId = + core.getBlockByNumber(core.getBgNumber(checkLoc.x, checkLoc.y)) + .event.id || ''; + // 可以通行 + if ( + !noPass || + cls == 'items' || + (id.startsWith('X') && !ignoreInJump.event.includes(id)) || + (bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId)) + ) + return core.drawTip('当前无法使用技能'); + // 不是怪物且不可以通行 + if (noPass && !(cls == 'enemys' || cls == 'enemy48')) { + var toLoc = checkNoPass(direction, checkLoc.x, checkLoc.y, true); + if (!toLoc) return; + core.autosave(); + if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard; + core.updateStatusBar(); + flags['jump_' + core.status.floorId]++; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose('你跳死了'); + } + core.playSound('015-Jump01.ogg'); + core.insertAction([ + { type: 'jumpHero', loc: [toLoc.x, toLoc.y], time: 500 } + ]); + } + // 是怪物 + if (cls == 'enemys' || cls == 'enemy48') { + var firstNoPass = checkNoPass( + direction, + checkLoc.x, + checkLoc.y, + false + ); + if (!firstNoPass) return; + core.autosave(); + if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard; + core.updateStatusBar(); + flags['jump_' + core.status.floorId]++; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose('你跳死了'); + } + core.playSound('015-Jump01.ogg'); + core.insertAction([ + { + type: 'jump', + from: [checkLoc.x, checkLoc.y], + to: [firstNoPass.x, firstNoPass.y], + time: 500, + keep: true + } + ]); + } + // 检查一条线上的不可通过 + function checkNoPass(direction, x, y, startNo) { + if (!startNo) startNo = false; + switch (direction) { + case 'up': + y--; + break; + case 'right': + x++; + break; + case 'down': + y++; + break; + case 'left': + x--; + break; + } + if ( + x > core.status.thisMap.width - 1 || + y > core.status.thisMap.height - 1 || + x < 0 || + y < 0 + ) + return core.drawTip('当前无法使用技能'); + var id = core.getBlockId(x, y) || ''; + if (core.getBgNumber(x, y)) + var bgId = + core.getBlockByNumber(core.getBgNumber(x, y)).event.id || + ''; + else var bgId = ''; + if ( + core.noPass(x, y) || + core.getBlockCls(x, y) == 'items' || + (id.startsWith('X') && !ignoreInJump.event.includes(id)) || + (bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId)) || + core.getBlockCls(x, y) == 'animates' + ) + return checkNoPass(direction, x, y, true); + if (!startNo) return checkNoPass(direction, x, y, false); + return { x: x, y: y }; + } + } + + core.plugin.skillEffects = { + jumpSkill + }; +})(); diff --git a/public/project/plugin/study.js b/public/project/plugin/study.js new file mode 100644 index 0000000..80b6897 --- /dev/null +++ b/public/project/plugin/study.js @@ -0,0 +1,79 @@ +'use strict'; + +(function () { + // 负责勇士技能:学习 + const values = { + 1: ['crit'], + 6: ['n'], + 7: ['hungry'], + 8: ['together'], + 10: ['courage'], + 11: ['charge'] + }; + + const cannotStudy = [9, 12, 14, 15, 24]; + + function canStudySkill(number) { + const s = (core.status.hero.special ??= { num: [], last: [] }); + if (core.plugin.skillTree.getSkillLevel(11) === 0) return false; + if (s.num.length >= 1) return false; + if (s.num.includes(number)) return false; + if (cannotStudy.includes(number)) return false; + return true; + } + + function studySkill(enemy, number) { + core.status.hero.special ??= { num: [], last: [] }; + const s = core.status.hero.special; + const specials = core.getSpecials(); + let special = specials[number - 1][1]; + if (special instanceof Function) special = special(enemy); + if (!canStudySkill(number)) { + if (!main.replayChecking) { + core.tip('error', `无法学习${special}`); + } + return; + } + s.num.push(number); + s.last.push(core.plugin.skillTree.getSkillLevel(11) * 3 + 2); + const value = values[number] ?? []; + for (const key of value) { + s[key] = enemy[key]; + } + } + + function forgetStudiedSkill(num, i) { + const s = core.status.hero.special; + const index = i !== void 0 && i !== null ? i : s.num.indexOf(num); + if (index === -1) return; + s.num.splice(index, 1); + s.last.splice(index, 1); + const value = values[number] ?? []; + for (const key of value) { + delete s[key]; + } + } + + function declineStudiedSkill() { + const s = (core.status.hero.special ??= { num: [], last: [] }); + s.last = s.last.map(v => v - 1); + } + + function checkStudiedSkill() { + const s = core.status.hero.special; + for (let i = 0; i < s.last.length; i++) { + if (s.last[i] <= 0) { + this.forgetStudiedSkill(void 0, i); + i--; + } + } + } + + core.plugin.study = { + canStudySkill, + studySkill, + forgetStudiedSkill, + declineStudiedSkill, + checkStudiedSkill + }; +})(); diff --git a/public/project/plugin/towerBoss.js b/public/project/plugin/towerBoss.js new file mode 100644 index 0000000..e4ead35 --- /dev/null +++ b/public/project/plugin/towerBoss.js @@ -0,0 +1,1657 @@ +'use strict'; + +// 1000多行,改不动了 + +(function () { + // 智慧boss + // 变量们 + var stage = 1, + hp = 10000, + seconds = 0, + boomLocs = [], // 随机轰炸 + heroHp; + // 初始化 + function initTowerBoss() { + stage = 1; + hp = 10000; + seconds = 0; + heroHp = core.status.hero.hp; + dynamicChangeHp(0, 10000, 10000); + autoFixRouteBoss(true); + core.insertAction([{ type: 'sleep', time: 1000, noSkip: true }]); + setTimeout(bossCore, 1000); + } + // 录像自动修正 + function autoFixRouteBoss(isStart) { + var route = core.status.route; + if (isStart) { + // 开始修正 记录当前录像长度 + flags.startFix = route.length - 1; + return; + } + // 结束修正 删除录像 并追加跳过步骤 + route.splice(flags.startFix); + route.push('choices:0'); + delete flags.startFix; + } + // 血条 + function healthBar(now, total) { + var nowLength = (now / total) * 476; // 当前血量下绘制长度 + var color = [ + 255 * 2 - (now / total) * 2 * 255, + (now / total) * 2 * 255, + 0, + 1 + ]; // 根据当前血量计算颜色 + // 建画布 + if (!core.dymCanvas.healthBar) + core.createCanvas('healthBar', 0, 0, 480, 16, 140); + else core.clearMap('healthBar'); + // 底 + core.fillRect('healthBar', 0, 0, 480, 16, '#bbbbbb'); + // css特效 + var style = document.getElementById('healthBar').getContext('2d'); + style.shadowColor = 'rgba(0, 0, 0, 0.8)'; + style.shadowBlur = 5; + style.shadowOffsetX = 10; + style.shadowOffsetY = 5; + style.filter = 'blur(1px)'; + // 绘制 + core.fillRect('healthBar', 2, 2, nowLength, 12, color); + // css特效 + style.shadowColor = 'rgba(0, 0, 0, 0.5)'; + style.shadowOffsetX = 0; + style.shadowOffsetY = 0; + // 绘制边框 + core.strokeRect('healthBar', 1, 1, 478, 14, '#ffffff', 2); + // 绘制文字 + style.shadowColor = 'rgba(0, 0, 0, 1)'; + style.shadowBlur = 3; + style.shadowOffsetX = 2; + style.shadowOffsetY = 1; + style.filter = 'none'; + core.fillText( + 'healthBar', + now + '/' + total, + 5, + 13.5, + '#ffffff', + '16px normal' + ); + } + // 血量变化 + function dynamicChangeHp(from, to, total) { + var frame = 0, + speed = (to - from) / 50, + now = from; + var interval = window.setInterval(() => { + frame++; + if (frame == 50) { + clearInterval(interval); + healthBar(to, total); + } + now += speed; + healthBar(now, total); + }, 20); + } + // boss说话跳字 + function skipWord(words, x, y, time) { + x = x || 0; + y = y || 16; + time = time || 3000; + // 创建画布 + if (!core.dymCanvas.words) + core.createCanvas('words', x, y, 480, 24, 135); + else core.clearMap('words'); + if (flags.wordsTimeOut) clearTimeout(flags.wordsTimeOut); + dynamicCurtain(y, y + 24, time / 3); + // css + var style = document.getElementById('words').getContext('2d'); + style.shadowColor = 'rgba(0, 0, 0, 1)'; + style.shadowBlur = 3; + style.shadowOffsetX = 2; + style.shadowOffsetY = 1; + // 一个一个绘制 + skip1(0); + // 跳字 + function skip1(now) { + if (parseInt(now) >= words.length) { + flags.wordsTimeOut = setTimeout(() => { + core.deleteCanvas('words'); + core.deleteCanvas('wordsBg'); + }, time); + return; + } + var frame = 0, + blur = 2, + nx = 4 + now * 24; + var skip2 = window.setInterval(() => { + blur -= 0.4; + frame++; + core.clearMap('words', nx, 0, 24, 24); + style.filter = 'blur(' + blur + 'px)'; + core.fillText( + 'words', + words[now], + nx, + 20, + '#ffffff', + '22px normal' + ); + if (frame == 5) { + clearInterval(skip2); + skip1(now + 1); + } + }, 20); + } + } + // 匀变速下降背景 + function dynamicCurtain(from, to, time, width) { + width = width || 480; + if (!core.dymCanvas.wordsBg) + core.createCanvas('wordsBg', 0, from, width, 24, 130); + else core.clearMap('wordsBg'); + time /= 1000; + var ny = from, + frame = 0, + a = (2 * (to - from)) / Math.pow(time * 50, 2), + speed = a * time * 50; + var style = document.getElementById('wordsBg').getContext('2d'); + style.shadowColor = 'rgba(0, 0, 0, 0.8)'; + var wordsInterval = window.setInterval(() => { + frame++; + speed -= a; + ny += speed; + core.clearMap('wordsBg'); + style.shadowBlur = 8; + style.shadowOffsetY = 2; + core.fillRect( + 'wordsBg', + 0, + 0, + width, + ny - from, + [180, 180, 180, 0.7] + ); + style.shadowBlur = 3; + style.shadowOffsetY = 0; + core.strokeRect( + 'wordsBg', + 1, + 1, + width - 2, + ny - from - 2, + [255, 255, 255, 0.7], + 2 + ); + if (frame >= time * 50) { + clearInterval(wordsInterval); + core.clearMap('wordsBg'); + style.shadowBlur = 8; + style.shadowOffsetY = 2; + core.fillRect( + 'wordsBg', + 0, + 0, + width, + to - from, + [180, 180, 180, 0.7] + ); + style.shadowBlur = 3; + style.shadowOffsetY = 0; + core.strokeRect( + 'wordsBg', + 1, + 1, + width - 2, + ny - from - 2, + [255, 255, 255, 0.7], + 2 + ); + } + }, 20); + } + // 攻击boss + function attackBoss() { + // 每秒钟地面随机出现伤害图块 踩上去攻击boss 500血 + if (flags.canAttack) return; + if (Math.random() < 0.8) return; + if (hp > 3500) { + var nx = Math.floor(Math.random() * 13 + 1), + ny = Math.floor(Math.random() * 13 + 1); + } else if (hp > 2000) { + var nx = Math.floor(Math.random() * 11 + 2), + ny = Math.floor(Math.random() * 11 + 2); + } else if (hp > 1000) { + var nx = Math.floor(Math.random() * 9 + 3), + ny = Math.floor(Math.random() * 9 + 3); + } else { + var nx = Math.floor(Math.random() * 7 + 4), + ny = Math.floor(Math.random() * 7 + 4); + } + // 在地图上显示 + flags.canAttack = true; + if (!core.dymCanvas.attackBoss) + core.createCanvas('attackBoss', 0, 0, 480, 480, 35); + else core.clearMap('attackBoss'); + var style = document.getElementById('attackBoss').getContext('2d'); + var frame1 = 0, + blur = 3, + scale = 2, + speed = 0.04, + a = 0.0008; + var atkAnimate = window.setInterval(() => { + core.clearMap('attackBoss'); + frame1++; + speed -= a; + scale -= speed; + blur -= 0.06; + style.filter = 'blur(' + blur + 'px)'; + core.strokeCircle( + 'attackBoss', + nx * 32 + 16, + ny * 32 + 16, + 16 * scale, + [255, 150, 150, 0.7], + 4 + ); + core.fillCircle( + 'attackBoss', + nx * 32 + 16, + ny * 32 + 16, + 3 * scale, + [255, 150, 150, 0.7] + ); + if (frame1 == 50) { + clearInterval(atkAnimate); + core.clearMap('attactkBoss'); + style.filter = 'none'; + core.strokeCircle( + 'attackBoss', + nx * 32 + 16, + ny * 32 + 16, + 16, + [255, 150, 150, 0.7], + 4 + ); + core.fillCircle( + 'attackBoss', + nx * 32 + 16, + ny * 32 + 16, + 3, + [255, 150, 150, 0.7] + ); + } + }, 20); + // 实时检测勇士位置 + var frame2 = 0; + var atkBoss = window.setInterval(() => { + frame2++; + var x = core.status.hero.loc.x, + y = core.status.hero.loc.y; + // 2秒超时 + if (frame2 > 100) { + setTimeout(() => { + delete flags.canAttack; + }, 4000); + clearInterval(atkBoss); + core.deleteCanvas('attackBoss'); + return; + } + if (nx == x && ny == y) { + setTimeout(() => { + delete flags.canAttack; + }, 4000); + dynamicChangeHp(hp, hp - 500, 10000); + hp -= 500; + clearInterval(atkBoss); + core.deleteCanvas('attackBoss'); + if (hp > 3500) core.drawAnimate('hand', 7, 1); + else if (hp > 2000) core.drawAnimate('hand', 7, 2); + else if (hp > 1000) core.drawAnimate('hand', 7, 3); + else core.drawAnimate('hand', 7, 4); + return; + } + }, 20); + } + // 核心函数 + function bossCore() { + var interval = window.setInterval(() => { + if (stage == 1) { + if (seconds == 8) skipWord('智慧之神:果然,你和别人不一样。'); + if (seconds == 12) skipWord('智慧之神:你知道去躲避那些攻击。'); + if (seconds == 16) + skipWord( + '智慧之神:之前的那些人总会一头撞上我的攻击,悲剧收场。' + ); + if (seconds == 20) + skipWord('提示:踩在红圈上可以对智慧之神造成伤害'); + if (seconds > 10) attackBoss(); + if (seconds % 10 == 0) intelligentArrow(); + if (seconds % 7 == 0 && seconds != 0) intelligentDoor(); + if (seconds > 20 && seconds % 13 == 0) icyMomentem(); + } + if (stage == 1 && hp <= 7000) { + stage++; + seconds = 0; + skipWord('智慧之神:不错小伙子'); + core.pauseBgm(); + } + if (stage == 2) { + if (seconds == 4) skipWord('智慧之神:你的确拥有智慧。'); + if (seconds == 8) + skipWord('智慧之神:或许你就是那个未来的救星。'); + if (seconds == 12) + skipWord('智慧之神:不过,这场战斗才刚刚开始'); + if (seconds == 25) skipWord('提示:方形区域均为危险区域'); + if (seconds == 15) + setTimeout(() => { + core.playSound('thunder.mp3'); + }, 500); + if (seconds == 16) startStage2(); + if (seconds > 20) attackBoss(); + if (seconds % 4 == 0 && seconds > 20) randomThunder(); + if (seconds > 30 && seconds % 12 == 0) ballThunder(); + } + if (hp <= 3500 && stage == 2) { + stage++; + seconds = 0; + skipWord('智慧之神:不得不说小伙子'); + core.pauseBgm(); + } + if (stage >= 3) { + if (seconds == 4) skipWord('智慧之神:拥有智慧就是不一样。'); + if (seconds == 8) + skipWord('智慧之神:不过,你还得再过我一关!'); + if (seconds == 12) startStage3(); + if (seconds == 15) { + flags.booming = true; + randomBoom(); + } + if (seconds > 20) attackBoss(); + if (seconds > 20 && seconds % 10 == 0) chainThunder(); + if (hp == 2000 && stage == 3) { + stage++; + flags.booming = false; + skipWord('智慧之神:还没有结束!'); + startStage4(); + setTimeout(() => { + flags.booming = true; + randomBoom(); + }, 5000); + } + if (hp == 1000 && stage == 4) { + stage++; + flags.booming = false; + skipWord('智慧之神:还没有结束!!!!!!'); + startStage5(); + setTimeout(() => { + flags.booming = true; + randomBoom(); + }, 5000); + } + } + if (hp == 0) { + clearInterval(interval); + clearInterval(flags.boom); + core.status.hero.hp = heroHp; + core.plugin.towerBoss.autoFixRouteBoss(false); + delete flags.__bgm__; + core.pauseBgm(); + core.insertAction([ + '\t[智慧之神,E557]\b[down,7,4]看来你真的会成为那个拯救未来的人。', + '\t[智慧之神,E557]\b[down,7,4]记住,拥有智慧便可以掌控万物。', + '\t[低级智人]\b[up,hero]智慧?智慧到底是什么?', + '\t[智慧之神,E557]\b[down,7,4]最终,你会知道答案的。', + '\t[智慧之神,E557]\b[down,7,4]继续向东前进吧,那里能找到你想要的答案。', + { type: 'openDoor', loc: [13, 6], floorId: 'MT19' }, + '\t[智慧之神,E557]\b[down,7,4]我这就把你送出去', + { type: 'setValue', name: 'flag:boss1', value: 'true' }, + { type: 'changeFloor', floorId: 'MT20', loc: [7, 9] }, + { type: 'forbidSave' }, + { type: 'showStatusBar' }, + { + type: 'function', + function: '() => {\ncore.deleteAllCanvas();\n}' + } + ]); + } + seconds++; + }, 1000); + } + // ------ 第一阶段 10000~7000血 ------ // + // 技能1 智慧之箭 1000伤害 + function intelligentArrow(fromSelf) { + // 坐标 + var loc = Math.floor(Math.random() * 13 + 1); + var direction = Math.random() > 0.5 ? 'horizon' : 'vertical'; + // 执行次数 + if (!fromSelf) { + var times = Math.ceil(Math.random() * 8) + 4; + var nowTime = 1; + var times1 = window.setInterval(() => { + intelligentArrow(true); + nowTime++; + if (nowTime >= times) { + clearInterval(times1); + } + }, 200); + } + // 防重复 + if (core.dymCanvas['inteArrow' + loc + direction]) + return intelligentArrow(true); + // 危险区域 + if (!core.dymCanvas.danger1) + core.createCanvas('danger1', 0, 0, 480, 480, 35); + if (direction == 'horizon') { + for (var nx = 1; nx < 14; nx++) { + core.fillRect( + 'danger1', + nx * 32 + 2, + loc * 32 + 2, + 28, + 28, + [255, 0, 0, 0.6] + ); + } + } else { + for (var ny = 1; ny < 14; ny++) { + core.fillRect( + 'danger1', + loc * 32 + 2, + ny * 32 + 2, + 28, + 28, + [255, 0, 0, 0.6] + ); + } + } + // 箭 + if (!core.dymCanvas['inteArrow' + loc + direction]) + core.createCanvas( + 'inteArrow' + loc + direction, + 0, + 0, + 544, + 544, + 65 + ); + core.clearMap('inteArrow' + loc + direction); + if (direction == 'horizon') + core.drawImage( + 'inteArrow' + loc + direction, + 'arrow.png', + 448, + loc * 32, + 102, + 32 + ); + else + core.drawImage( + 'inteArrow' + loc + direction, + 'arrow.png', + 0, + 0, + 259, + 75, + loc * 32 - 32, + 480, + 102, + 32, + Math.PI / 2 + ); + // 动画与伤害函数 + setTimeout(() => { + core.playSound('arrow.mp3'); + core.deleteCanvas('danger1'); + // 动画效果 + var nloc = 0, + speed = 0; + var damaged = {}; + var skill1 = window.setInterval(() => { + speed -= 1; + nloc += speed; + if (direction == 'horizon') + core.relocateCanvas('inteArrow' + loc + direction, nloc, 0); + else + core.relocateCanvas('inteArrow' + loc + direction, 0, nloc); + if (nloc < -480) { + core.deleteCanvas('inteArrow' + loc + direction); + clearInterval(skill1); + } + // 伤害判定 + if (!damaged[loc + direction]) { + var x = core.status.hero.loc.x, + y = core.status.hero.loc.y; + if (direction == 'horizon') { + if (y == loc && Math.floor((480 + nloc) / 32) == x) { + damaged[loc + direction] = true; + core.drawHeroAnimate('hand'); + core.status.hero.hp -= 1000; + core.addPop(x * 32 + 16, y * 32 + 16, -1000); + core.updateStatusBar(); + if (core.status.hero.hp < 0) { + clearInterval(skill1); + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } + } + } else { + if (x == loc && Math.floor((480 + nloc) / 32) == y) { + damaged[loc + direction] = true; + core.drawHeroAnimate('hand'); + core.status.hero.hp -= 1000; + core.addPop(x * 32 + 16, y * 32 + 16, -1000); + core.updateStatusBar(); + if (core.status.hero.hp < 0) { + clearInterval(skill1); + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } + } + } + } + }, 20); + }, 3000); + } + // 技能2 智慧之门 随机传送 + function intelligentDoor() { + if (Math.random() < 0.5) return; + // 随机位置 + var toX = Math.floor(Math.random() * 13) + 1, + toY = Math.floor(Math.random() * 13) + 1; + // 在勇士身上绘制动画 + core.drawHeroAnimate('magicAtk'); + // 在目标位置绘制动画 + if (!core.dymCanvas['door' + toX + '_' + toY]) + core.createCanvas('door' + toX + '_' + toY, 0, 0, 480, 480, 35); + else core.clearMap('door' + toX + '_' + toY); + var style = document + .getElementById('door' + toX + '_' + toY) + .getContext('2d'); + var frame = 0, + width = 0, + a = 0.0128, + speed = 0.64; + // 动画 + var skill2 = window.setInterval(() => { + frame++; + if (frame < 40) return; + if (frame == 100) { + clearInterval(skill2); + // 执行传送 + core.insertAction([{ type: 'changePos', loc: [toX, toY] }]); + // 删除传送门 + setTimeout(() => { + core.deleteCanvas('door' + toX + '_' + toY); + }, 2000); + return; + } + width += speed * 2; + speed -= a; + core.clearMap('door' + toX + '_' + toY); + style.shadowColor = 'rgba(255, 255, 255, 1)'; + style.shadowBlur = 7; + style.filter = 'blur(5px)'; + core.fillRect( + 'door' + toX + '_' + toY, + toX * 32, + toY * 32 - 24, + width, + 48, + [255, 255, 255, 0.7] + ); + style.shadowColor = 'rgba(0, 0, 0, 0.5)'; + style.filter = 'blur(3px)'; + core.strokeRect( + 'door' + toX + '_' + toY, + toX * 32, + toY * 32 - 24, + width, + 48, + [255, 255, 255, 0.7], + 3 + ); + }, 20); + } + // 技能3 万冰之势 全屏随机转换滑冰 如果转换时在滑冰上造成5000点伤害 + function icyMomentem() { + if (flags.haveIce) return; + if (Math.random() < 0.5) return; + var times = Math.floor(Math.random() * 100); + // 防卡 就setInterval吧 + var locs = [], + now = 0; + flags.haveIce = true; + if (!core.dymCanvas.icyMomentem) + core.createCanvas('icyMomentem', 0, 0, 480, 480, 35); + else core.clearMap('icyMomentem'); + var skill3 = window.setInterval(() => { + var nx = Math.floor(Math.random() * 13) + 1, + ny = Math.floor(Math.random() * 13) + 1; + if (!locs.includes([nx, ny])) { + locs.push([nx, ny]); + core.fillRect( + 'icyMomentem', + locs[now][0] * 32 + 2, + locs[now][1] * 32 + 2, + 28, + 28, + [150, 150, 255, 0.6] + ); + } + if (now == times) { + clearInterval(skill3); + skill3Effect(); + } + now++; + }, 20); + // 动画和伤害函数 + function skill3Effect() { + // 防卡 setInterval + var index = 0; + var effect = window.setInterval(() => { + var x = core.status.hero.loc.x, + y = core.status.hero.loc.y; + core.clearMap( + 'icyMomentem', + locs[index][0] * 32, + locs[index][1] * 32, + 32, + 32 + ); + core.setBgFgBlock('bg', 167, locs[index][0], locs[index][1]); + core.drawAnimate('ice', locs[index][0], locs[index][1]); + if (x == locs[index][0] && y == locs[index][1]) { + core.drawHeroAnimate('hand'); + core.status.hero.hp -= 5000; + core.addPop(x * 32 + 16, y * 32 + 16, -5000); + core.updateStatusBar(); + if (core.status.hero.hp < 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + clearInterval(effect); + return; + } + } + if (index >= locs.length - 1) { + clearInterval(effect); + setTimeout(() => { + deleteIce(locs); + }, 5000); + } + index++; + }, 50); + } + // 删除函数 + function deleteIce(locs) { + // 照样 setInterval + var index = 0; + var deleteIce = window.setInterval(() => { + core.setBgFgBlock('bg', 0, locs[index][0], locs[index][1]); + index++; + if (index >= locs.length) { + clearInterval(deleteIce); + core.deleteCanvas('icyMomentem'); + setTimeout(() => { + delete flags.haveIce; + }, 5000); + } + }, 50); + } + } + // ------ 第二阶段 7000~3500 ------ // + // 开始第二阶段 + function startStage2() { + // 闪烁 + core.createCanvas('flash', 0, 0, 480, 480, 160); + var alpha = 0; + var frame = 0; + var start1 = window.setInterval(() => { + core.clearMap('flash'); + frame++; + if (frame <= 8) alpha += 0.125; + else alpha -= 0.01; + core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); + if (alpha == 0) { + clearInterval(start1); + core.deleteCanvas('flash'); + } + if (frame == 8) { + changeWeather(); + } + }); + // 切换天气 + function changeWeather() { + core.setWeather(); + core.setWeather('rain', 10); + core.setWeather('fog', 8); + // 色调也得换 + core.setCurtain([0, 0, 0, 0.3]); + // bgm + core.playBgm('towerBoss2.mp3'); + } + } + // ----- 打雷相关 ----- // + // 随机打雷 + function randomThunder() { + var x = Math.floor(Math.random() * 13) + 1, + y = Math.floor(Math.random() * 13) + 1, + power = Math.ceil(Math.random() * 6); + // 绘制危险区域 + if (!core.dymCanvas.thunderDanger) + core.createCanvas('thunderDanger', 0, 0, 480, 480, 35); + else core.clearMap('thunderDanger'); + // 3*3范围 + for (var nx = x - 1; nx <= x + 1; nx++) { + for (var ny = y - 1; ny <= y + 1; ny++) { + core.fillRect( + 'thunderDanger', + nx * 32 + 2, + ny * 32 + 2, + 28, + 28, + [255, 255, 255, 0.6] + ); + } + } + core.deleteCanvas('flash'); + setTimeout(() => { + core.playSound('thunder.mp3'); + }, 500); + setTimeout(() => { + core.deleteCanvas('thunderDanger'); + drawThunder(x, y, power); + }, 1000); + } + // 绘制 + function drawThunder(x, y, power) { + var route = getThunderRoute(x * 32 + 16, y * 32 + 16, power); + // 开始绘制 + if (!core.dymCanvas.thunder) + core.createCanvas('thunder', 0, 0, 480, 480, 65); + else core.clearMap('thunder'); + var style = core.dymCanvas.thunder; + style.shadowColor = 'rgba(220, 220, 255, 1)'; + style.shadowBlur = power; + style.filter = 'blur(2.5px)'; + for (var num in route) { + // 一个个绘制 + for (var i = 0; i < route[num].length - 1; i++) { + var now = route[num][i], + next = route[num][i + 1]; + core.drawLine( + 'thunder', + now[0], + now[1], + next[0], + next[1], + '#ffffff', + 2.5 + ); + } + } + // 伤害 + getThunderRoute(x, y, power); + // 闪一下 + var frame1 = 0, + alpha = 0.5; + if (!core.dymCanvas.flash) + core.createCanvas('flash', 0, 0, 480, 480, 160); + else core.clearMap('flash'); + var thunderFlash = window.setInterval(() => { + alpha -= 0.05; + frame1++; + core.clearMap('flash'); + core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); + if (frame1 >= 10) { + clearInterval(thunderFlash); + core.deleteCanvas('flash'); + // 删除闪电 + setTimeout(() => { + core.deleteCanvas('thunder'); + }, 700); + } + }, 20); + } + // 获得雷电路径 + function getThunderRoute(x, y, power) { + var route = []; + for (var num = 0; num < power; num++) { + var nx = x, + ny = y; + route[num] = []; + for (var i = 0; ny >= 0; i++) { + if (i > 0) { + nx += Math.random() * 30 - 15; + ny -= Math.random() * 80 + 30; + } else { + nx += Math.random() * 16 - 8; + ny += Math.random() * 16 - 8; + } + route[num].push([nx, ny]); + } + } + return route; + } + // 打雷伤害判定 + function getThunderDamage(x, y, power) { + var hx = core.status.hero.loc.x, + hy = core.status.hero.loc.y; + if (Math.abs(hx - x) <= 1 && Math.abs(hy - y) <= 1) { + core.status.hero.hp -= 3000 * power; + core.addPop(x * 32 + 16, y * 32 + 16, -3000 * power); + core.updateStatusBar(); + if (core.status.hero.hp < 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } + } + } + // ----- 打雷 END ----- // + // 球形闪电 横竖 + function ballThunder() { + // 随机数量 + var times = Math.ceil(Math.random() * 12) + 6; + var now = 0, + locs = []; + // setInterval执行 + var ballThunder = window.setInterval(() => { + // 画布 + if (!core.dymCanvas['ballThunder' + now]) + core.createCanvas('ballThunder' + now, 0, 0, 480, 480, 35); + else core.clearMap('ballThunder' + now); + var nx = Math.floor(Math.random() * 13) + 1, + ny = Math.floor(Math.random() * 13) + 1; + // 添加位置 绘制危险区域 + if (!locs.includes([nx, ny])) { + locs.push([nx, ny]); + // 横竖都要画 + for (var mx = 1; mx < 14; mx++) { + core.fillRect( + 'ballThunder' + now, + mx * 32 + 2, + ny * 32 + 2, + 28, + 28, + [190, 190, 255, 0.6] + ); + } + for (var my = 1; my < 14; my++) { + core.fillRect( + 'ballThunder' + now, + nx * 32 + 2, + my * 32 + 2, + 28, + 28, + [190, 190, 255, 0.6] + ); + } + } + now++; + if (now >= times) { + clearInterval(ballThunder); + setTimeout(() => { + thunderAnimate(locs); + }, 1000); + } + }, 200); + // 动画 伤害 + function thunderAnimate(locs) { + var frame = 0; + // 画布 + if (!core.dymCanvas.ballAnimate) + core.createCanvas('ballAnimate', 0, 0, 480, 480, 65); + else core.clearMap('ballAnimate'); + var style = core.dymCanvas.ballAnimate; + style.shadowColor = 'rgba(255, 255, 255, 1)'; + var damaged = []; + var animate = window.setInterval(() => { + core.clearMap('ballAnimate'); + for (var i = 0; i < locs.length; i++) { + style.shadowBlur = 16 * Math.random(); + // 错开执行动画 + if (frame - 10 * i > 0) { + var now = frame - 10 * i; + if (now == 1) core.playSound('electron.mp3'); + // 动画 + var nx = locs[i][0] * 32 + 16, + ny = locs[i][1] * 32 + 16; + if (now <= 2) { + core.fillCircle( + 'ballAnimate', + nx, + ny, + 16 + 3 * now, + [255, 255, 255, 0.9] + ); + } else { + // 上 + core.fillCircle( + 'ballAnimate', + nx, + ny - 4 * now, + 7 + 2 * Math.random(), + [255, 255, 255, 0.7] + ); + // 下 + core.fillCircle( + 'ballAnimate', + nx, + ny + 4 * now, + 7 + 2 * Math.random(), + [255, 255, 255, 0.7] + ); + // 左 + core.fillCircle( + 'ballAnimate', + nx - 4 * now, + ny, + 7 + 2 * Math.random(), + [255, 255, 255, 0.7] + ); + // 右 + core.fillCircle( + 'ballAnimate', + nx + 4 * now, + ny, + 7 + 2 * Math.random(), + [255, 255, 255, 0.7] + ); + } + // 清除危险区域 + core.clearMap( + 'ballThunder' + i, + nx - 16, + ny - 16 - 4 * now, + 32, + 32 + ); + core.clearMap( + 'ballThunder' + i, + nx - 16, + ny - 16 + 4 * now, + 32, + 32 + ); + core.clearMap( + 'ballThunder' + i, + nx - 16 - 4 * now, + ny - 16, + 32, + 32 + ); + core.clearMap( + 'ballThunder' + i, + nx - 16 + 4 * now, + ny - 16, + 32, + 32 + ); + // 伤害 + if (!damaged[i]) { + var x = core.status.hero.loc.x, + y = core.status.hero.loc.y; + if ( + ((Math.floor((nx - 16 - 4 * now) / 32) == x || + Math.floor((nx - 16 + 4 * now) / 32) == + x) && + locs[i][1] == y) || + ((Math.floor((ny - 16 - 4 * now) / 32) == y || + Math.floor((ny - 16 + 4 * now) / 32) == + y) && + locs[i][0] == x) + ) { + damaged[i] = true; + core.status.hero.hp -= 3000; + core.addPop(x * 32 + 16, y * 32 + 16, -3000); + core.updateStatusBar(); + core.playSound('electron.mp3'); + if (core.status.hero.hp < 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + clearInterval(animate); + return; + } + } + } + // 结束 + if (i == locs.length - 1 && now > 120) { + clearInterval(animate); + } + } + } + frame++; + }, 20); + } + } + // ------ 第三阶段 3500~0 ------ // + function startStage3() { + // 闪烁 + core.createCanvas('flash', 0, 0, 480, 480, 160); + var alpha = 0; + var frame = 0; + var start1 = window.setInterval(() => { + core.clearMap('flash'); + frame++; + if (frame <= 8) alpha += 0.125; + else alpha -= 0.01; + core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); + if (alpha == 0) { + clearInterval(start1); + core.deleteCanvas('flash'); + } + if (frame == 8) { + core.playSound('thunder.mp3'); + changeTerra(); + core.insertAction([{ type: 'changePos', loc: [7, 7] }]); + } + }); + // 改变地形 + function changeTerra() { + for (var nx = 0; nx < 15; nx++) { + for (var ny = 0; ny < 15; ny++) { + if (nx == 0 || nx == 14 || ny == 0 || ny == 14) { + core.removeBlock(nx, ny); + } + if ( + (nx == 1 || nx == 13 || ny == 1 || ny == 13) && + nx != 0 && + nx != 14 && + ny != 0 && + ny != 14 + ) { + core.setBlock(527, nx, ny); + } + } + } + core.createCanvas('tower7', 0, 0, 480, 480, 15); + // 画贴图 + core.drawImage( + 'tower7', + 'tower7.jpeg', + 360, + 0, + 32, + 480, + 0, + 0, + 32, + 480 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 840, + 0, + 32, + 480, + 448, + 0, + 32, + 480 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 392, + 0, + 416, + 32, + 32, + 0, + 416, + 32 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 392, + 448, + 416, + 32, + 32, + 448, + 416, + 32 + ); + core.setBlock('E557', 7, 2); + core.playBgm('towerBoss3.mp3'); + } + } + // 进入第四阶段 + function startStage4() { + // 闪烁 + core.createCanvas('flash', 0, 0, 480, 480, 160); + var alpha = 0; + var frame = 0; + var start1 = window.setInterval(() => { + core.clearMap('flash'); + frame++; + if (frame <= 8) alpha += 0.125; + else alpha -= 0.01; + core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); + if (alpha == 0) { + clearInterval(start1); + core.deleteCanvas('flash'); + } + if (frame == 8) { + core.playSound('thunder.mp3'); + changeTerra(); + core.insertAction([{ type: 'changePos', loc: [7, 7] }]); + } + }); + // 改变地形 + function changeTerra() { + for (var nx = 1; nx < 14; nx++) { + for (var ny = 1; ny < 14; ny++) { + if (nx == 1 || nx == 13 || ny == 1 || ny == 13) { + core.removeBlock(nx, ny); + } + if ( + (nx == 2 || nx == 12 || ny == 2 || ny == 12) && + nx != 1 && + nx != 13 && + ny != 1 && + ny != 13 + ) { + core.setBlock(527, nx, ny); + } + } + } + core.createCanvas('tower7', 0, 0, 480, 480, 15); + // 画贴图 + core.drawImage( + 'tower7', + 'tower7.jpeg', + 360, + 0, + 64, + 480, + 0, + 0, + 64, + 480 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 776, + 0, + 64, + 480, + 416, + 0, + 64, + 480 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 424, + 0, + 352, + 64, + 64, + 0, + 352, + 64 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 424, + 416, + 352, + 64, + 64, + 416, + 352, + 64 + ); + core.setBlock('E557', 7, 3); + } + } + // 进入第五阶段 + function startStage5() { + // 闪烁 + core.createCanvas('flash', 0, 0, 480, 480, 160); + var alpha = 0; + var frame = 0; + var start1 = window.setInterval(() => { + core.clearMap('flash'); + frame++; + if (frame <= 8) alpha += 0.125; + else alpha -= 0.01; + core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); + if (alpha == 0) { + clearInterval(start1); + core.deleteCanvas('flash'); + } + if (frame == 8) { + core.playSound('thunder.mp3'); + changeTerra(); + core.insertAction([{ type: 'changePos', loc: [7, 7] }]); + } + }); + // 改变地形 + function changeTerra() { + for (var nx = 2; nx < 13; nx++) { + for (var ny = 2; ny < 13; ny++) { + if (nx == 2 || nx == 12 || ny == 2 || ny == 12) { + core.removeBlock(nx, ny); + } + if ( + (nx == 3 || nx == 11 || ny == 3 || ny == 11) && + nx != 2 && + nx != 12 && + ny != 2 && + ny != 12 + ) { + core.setBlock(527, nx, ny); + } + } + } + core.createCanvas('tower7', 0, 0, 480, 480, 15); + // 画贴图 + core.drawImage( + 'tower7', + 'tower7.jpeg', + 360, + 0, + 96, + 480, + 0, + 0, + 96, + 480 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 744, + 0, + 96, + 480, + 384, + 0, + 96, + 480 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 456, + 0, + 288, + 96, + 96, + 0, + 288, + 96 + ); + core.drawImage( + 'tower7', + 'tower7.jpeg', + 456, + 384, + 288, + 96, + 96, + 384, + 288, + 96 + ); + core.setBlock('E557', 7, 4); + } + } + // 链状闪电 随机连接 碰到勇士则受伤 + function chainThunder() { + // 随机次数 + var times = Math.ceil(Math.random() * 6) + 3; + // 画布 + if (!core.dymCanvas.chainDanger) + core.createCanvas('chainDanger', 0, 0, 480, 480, 35); + else core.clearMap('chainDanger'); + // setInterval执行 + var locs = [], + now = 0; + var chain = window.setInterval(() => { + if (hp > 2000) { + var nx = Math.floor(Math.random() * 11) + 2, + ny = Math.floor(Math.random() * 11) + 2; + } else if (hp > 1000) { + var nx = Math.floor(Math.random() * 9) + 3, + ny = Math.floor(Math.random() * 9) + 3; + } else { + var nx = Math.floor(Math.random() * 7) + 4, + ny = Math.floor(Math.random() * 7) + 4; + } + if (!locs.includes([nx, ny])) { + locs.push([nx, ny]); + } else return; + // 危险线 + if (now > 0) { + core.drawLine( + 'chainDanger', + locs[now - 1][0] * 32 + 16, + locs[now - 1][1] * 32 + 16, + nx * 32 + 16, + ny * 32 + 16, + [220, 100, 255, 0.6], + 3 + ); + } + if (now >= times) { + clearInterval(chain); + setTimeout(() => { + getChainRoute(locs); + core.deleteCanvas('chainDanger'); + }, 1000); + } + now++; + }, 100); + } + // 链状闪电 动画 + function chainAnimate(route) { + if (!route) return chainThunder(); + // 画布 + if (!core.dymCanvas.chain) + core.createCanvas('chain', 0, 0, 480, 480, 65); + else core.clearMap('chain'); + var style = core.dymCanvas.chain; + style.shadowBlur = 3; + style.shadowColor = 'rgba(255, 255, 255, 1)'; + style.filter = 'blur(2px)'; + // 当然还是setInterval + var frame = 0, + now = 0; + var animate = window.setInterval(() => { + if (now >= route.length - 1) { + clearInterval(animate); + setTimeout(() => { + core.deleteCanvas('chain'); + }, 1000); + return; + } + frame++; + if (frame % 2 != 0) return; + core.drawLine( + 'chain', + route[now][0], + route[now][1], + route[now + 1][0], + route[now + 1][1], + '#ffffff', + 3 + ); + // 节点 + if (now == 0) { + core.fillCircle( + 'chain', + route[0][0], + route[0][1], + 7, + '#ffffff' + ); + } + if ( + (route[now + 1][0] - 16) % 32 == 0 && + (route[now + 1][1] - 16) % 32 == 0 + ) { + core.fillCircle( + 'chain', + route[now + 1][0], + route[now + 1][1], + 7, + '#ffffff' + ); + } + // 判断伤害 + lineDamage( + route[now][0], + route[now][1], + route[now + 1][0], + route[now + 1][1], + 4000 + ); + now++; + }, 20); + } + // 链状闪电 获得闪电路径 + function getChainRoute(locs) { + // 照样用setInterval + var now = 0, + routes = []; + var route = window.setInterval(() => { + var nx = locs[now][0] * 32 + 16, + ny = locs[now][1] * 32 + 16; + var tx = locs[now + 1][0] * 32 + 16, + ty = locs[now + 1][1] * 32 + 16; + var dx = tx - nx, + dy = ty - ny; + var angle = Math.atan(dy / dx); + if (dy < 0 && dx < 0) angle += Math.PI; + if (dx < 0 && dy > 0) angle += Math.PI; + // 循环 + 随机 + var times = 0; + while (true) { + times++; + nx += Math.random() * 50 * Math.cos(angle); + ny += Math.random() * 50 * Math.sin(angle); + routes.push([nx, ny]); + if ( + Math.sqrt(Math.pow(ny - ty, 2) + Math.pow(nx - tx, 2)) <= + 100 + ) { + routes.push([tx, ty]); + break; + } + if (times >= 20) { + clearInterval(route); + routes = null; + return; + } + } + now++; + if (now >= locs.length - 1) { + clearInterval(route); + chainAnimate(routes); + } + }, 2); + } + // 随机轰炸 + function randomBoom() { + // 停止轰炸 + if (!flags.booming) { + clearInterval(flags.boom); + return; + } + // 根据阶段数 分攻击速率 和范围 + var boomTime; + var range; + if (hp > 2000) { + boomTime = 500; + range = 11; + } else if (hp > 1000) { + boomTime = 400; + range = 9; + } else { + boomTime = 300; + range = 7; + } + // setInterval + flags.boom = window.setInterval(() => { + var nx = Math.floor(Math.random() * range) + (15 - range) / 2, + ny = Math.floor(Math.random() * range) + (15 - range) / 2; + boomLocs.push([nx, ny, 0]); + if (!flags.booming) clearInterval(flags.boom); + }, boomTime); + // 动画要在这里调用 + boomingAnimate(); + } + // 随机轰炸 动画 + function boomingAnimate() { + // 直接setInterval + if (!core.dymCanvas.boom) core.createCanvas('boom', 0, 0, 480, 480, 65); + else core.clearMap('boom'); + var boomAnimate = window.setInterval(() => { + if (boomLocs.length == 0) return; + if (!flags.booming && boomLocs.length == 0) { + clearInterval(boomAnimate); + return; + } + core.clearMap('boom'); + boomLocs.forEach((loc, index) => { + loc[2]++; + var x = loc[0] * 32 + 16, + y = loc[1] * 32 + 16; + if (loc[2] >= 20) { + var alpha = 1, + radius = 12; + } else { + var radius = 0.12 * Math.pow(20 - loc[2], 2) + 12, + alpha = Math.max(1, 2 - loc[2] * 0.1); + } + var angle = (loc[2] * Math.PI) / 50; + // 开始绘制 + core.fillCircle('boom', x, y, 3, [255, 50, 50, alpha]); + core.strokeCircle( + 'boom', + x, + y, + radius, + [255, 50, 50, alpha], + 2 + ); + // 旋转的线 + core.drawLine( + 'boom', + x + radius * Math.cos(angle), + y + radius * Math.sin(angle), + x + (radius + 15) * Math.cos(angle), + y + (radius + 15) * Math.sin(angle), + [255, 50, 50, alpha], + 1 + ); + angle += Math.PI; + core.drawLine( + 'boom', + x + radius * Math.cos(angle), + y + radius * Math.sin(angle), + x + (radius + 15) * Math.cos(angle), + y + (radius + 15) * Math.sin(angle), + [255, 50, 50, alpha], + 1 + ); + // 炸弹 下落 + if (loc[2] > 70) { + var h = + y - + (20 * (85 - loc[2]) + 2.8 * Math.pow(85 - loc[2], 2)); + core.drawImage('boom', 'boom.png', x - 18, h - 80, 36, 80); + } + if (loc[2] == 85) { + core.drawAnimate( + 'explosion1', + (x - 16) / 32, + (y - 16) / 32 + ); + boomLocs.splice(index, 1); + if (boomLocs.length == 0) core.deleteCanvas('boom'); + // 伤害判定 + var hx = core.status.hero.loc.x, + hy = core.status.hero.loc.y; + if (loc[0] == hx && loc[1] == hy) { + core.status.hero.hp -= 3000; + core.addPop(x * 32 + 16, y * 32 + 16, -3000); + core.updateStatusBar(); + if (core.status.hero.hp < 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + clearInterval(boomAnimate); + flags.booming = false; + return; + } + } + } + }); + }, 20); + } + // 直线型伤害判定 + function lineDamage(x1, y1, x2, y2, damage) { + // 获得勇士坐标 + var x = core.status.hero.loc.x, + y = core.status.hero.loc.y; + // 是否可能碰到勇士 + if ( + (x1 < x * 32 - 12 && x2 < x * 32 - 12) || + (x1 > x * 32 + 12 && x2 > x * 32 + 12) || + (y1 < y * 32 - 16 && y2 < y * 32 - 16) || + (y1 > y * 32 + 16 && y2 > y * 32 + 16) + ) + return; + // 对角线的端点是否在直线异侧 勇士视为24 * 32 + for (var time = 1; time <= 2; time++) { + // 左下右上 + if (time == 1) { + var loc1 = [x * 32 - 12, y * 32 + 16], + loc2 = [x * 32 + 12, y * 32 - 16]; + // 直线方程 y == (y2 - y1) / (x2 - x1) * (x - x1) + y1 + var n1 = + ((y2 - y1) / (x2 - x1)) * (loc1[0] - x1) + y1 - loc1[1], + n2 = + ((y2 - y1) / (x2 - x1)) * (loc2[0] - x1) + y1 - loc2[1]; + if (n1 * n2 <= 0) { + core.status.hero.hp -= damage; + core.addPop(x * 32 + 16, y * 32 + 16, -damage); + core.updateStatusBar(); + core.playSound('electron.mp3'); + if (core.status.hero.hp < 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } + return; + } + } else { + // 左上右下 + var loc1 = [x * 32 - 12, y * 32 - 16], + loc2 = [x * 32 + 12, y * 32 + 16]; + // 直线方程 y == (y2 - y1) / (x2 - x1) * (x - x1) + y1 + var n1 = + ((y2 - y1) / (x2 - x1)) * (loc1[0] - x1) + y1 - loc1[1], + n2 = + ((y2 - y1) / (x2 - x1)) * (loc2[0] - x1) + y1 - loc2[1]; + if (n1 * n2 <= 0) { + core.status.hero.hp -= damage; + core.addPop(x * 32 + 16, y * 32 + 16, -damage); + core.updateStatusBar(); + core.playSound('electron.mp3'); + if (core.status.hero.hp < 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } + return; + } + } + } + } + + core.plugin.towerBoss = { + initTowerBoss, + autoFixRouteBoss + }; +})(); diff --git a/public/project/plugin/ui.js b/public/project/plugin/ui.js new file mode 100644 index 0000000..a0c2b69 --- /dev/null +++ b/public/project/plugin/ui.js @@ -0,0 +1,94 @@ +'use strict'; + +(function () { + if (main.replayChecking) return (core.plugin.gameUi = {}); + + function openItemShop(itemShopId) { + if (!main.replayChecking) { + core.plugin.openedShopId = itemShopId; + core.plugin.shopOpened.value = true; + } + } + + function updateVueStatusBar() { + if (main.replayChecking) return; + core.plugin.statusBarStatus.value = !core.plugin.statusBarStatus.value; + core.checkMarkedEnemy(); + } + + ui.prototype.drawBook = function () { + if (!core.isReplaying()) return (core.plugin.bookOpened.value = true); + }; + + ui.prototype._drawToolbox = function () { + if (!core.isReplaying()) return (core.plugin.toolOpened.value = true); + }; + + ui.prototype._drawEquipbox = function () { + if (!core.isReplaying()) return (core.plugin.equipOpened.value = true); + }; + + ui.prototype.drawFly = function () { + if (!core.isReplaying()) return (core.plugin.flyOpened.value = true); + }; + + control.prototype.updateStatusBar_update = function () { + core.control.updateNextFrame = false; + if (!core.isPlaying() || core.hasFlag('__statistics__')) return; + core.control.controldata.updateStatusBar(); + if (!core.control.noAutoEvents) core.checkAutoEvents(); + core.control._updateStatusBar_setToolboxIcon(); + core.clearRouteFolding(); + core.control.noAutoEvents = true; + // 更新vue状态栏 + updateVueStatusBar(); + }; + + control.prototype.showStatusBar = function () { + if (main.mode == 'editor') return; + core.removeFlag('hideStatusBar'); + core.plugin.showStatusBar.value = true; + core.dom.tools.hard.style.display = 'block'; + core.dom.toolBar.style.display = 'block'; + }; + + control.prototype.hideStatusBar = function (showToolbox) { + if (main.mode == 'editor') return; + + // 如果原本就是隐藏的,则先显示 + if (!core.domStyle.showStatusBar) this.showStatusBar(); + if (core.isReplaying()) showToolbox = true; + core.plugin.showStatusBar.value = false; + + var toolItems = core.dom.tools; + core.setFlag('hideStatusBar', true); + core.setFlag('showToolbox', showToolbox || null); + if ( + (!core.domStyle.isVertical && !core.flags.extendToolbar) || + !showToolbox + ) { + for (var i = 0; i < toolItems.length; ++i) + toolItems[i].style.display = 'none'; + } + if (!core.domStyle.isVertical && !core.flags.extendToolbar) { + core.dom.toolBar.style.display = 'none'; + } + }; + + function showChapter(chapter) { + if (core.isReplaying()) return; + core.plugin.chapterContent.value = chapter; + core.plugin.chapterShowed.value = true; + } + + function openSkill() { + if (core.isReplaying()) return; + core.plugin.skillOpened.value = true; + } + + core.plugin.gameUi = { + openItemShop, + openSkill, + showChapter + }; +})(); diff --git a/public/project/plugin/utils.js b/public/project/plugin/utils.js index a22829d..83c4866 100644 --- a/public/project/plugin/utils.js +++ b/public/project/plugin/utils.js @@ -1,51 +1,55 @@ -/** - * 滑动数组 - * @param {any[]} arr - * @param {number} delta - */ -function slide(arr, delta) { - if (delta === 0) return arr; - delta %= arr.length; - if (delta > 0) { - arr.unshift(...arr.splice(arr.length - delta, delta)); - return arr; +'use strict'; + +(function () { + /** + * 滑动数组 + * @param {any[]} arr + * @param {number} delta + */ + function slide(arr, delta) { + if (delta === 0) return arr; + delta %= arr.length; + if (delta > 0) { + arr.unshift(...arr.splice(arr.length - delta, delta)); + return arr; + } + if (delta < 0) { + arr.push(...arr.splice(0, -delta)); + return arr; + } } - if (delta < 0) { - arr.push(...arr.splice(0, -delta)); - return arr; + + function backDir(dir) { + return { + up: 'down', + down: 'up', + left: 'right', + right: 'left' + }[dir]; } -} -function backDir(dir) { - return { - up: 'down', - down: 'up', - left: 'right', - right: 'left' - }[dir]; -} + function has(v) { + return v !== null && v !== void 0; + } -function has(v) { - return v !== null && v !== void 0; -} - -function maxGameScale(n = 0) { - const index = core.domStyle.availableScale.indexOf(core.domStyle.scale); - core.control.setDisplayScale( - core.domStyle.availableScale.length - 1 - index - n - ); - if (!core.isPlaying() && core.flags.enableHDCanvas) { - core.domStyle.ratio = Math.max( - window.devicePixelRatio || 1, - core.domStyle.scale + function maxGameScale(n = 0) { + const index = core.domStyle.availableScale.indexOf(core.domStyle.scale); + core.control.setDisplayScale( + core.domStyle.availableScale.length - 1 - index - n ); - core.resize(); + if (!core.isPlaying() && core.flags.enableHDCanvas) { + core.domStyle.ratio = Math.max( + window.devicePixelRatio || 1, + core.domStyle.scale + ); + core.resize(); + } } -} -core.plugin.utils = { - slide, - backDir, - has, - maxGameScale -}; + core.plugin.utils = { + slide, + backDir, + has, + maxGameScale + }; +})(); diff --git a/public/project/plugins.js b/public/project/plugins.js index b3889cb..22e05cf 100644 --- a/public/project/plugins.js +++ b/public/project/plugins.js @@ -2,4127 +2,13 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { init: function () { - // 只看插件没用,插件是与vite样板高度融合的,所以要看的话就在游戏内的百科全书-关于游戏内点那个开源地址吧 + // 这看不到插件,插件全放到plugin文件夹里面了,要看的话去 关于游戏 的开源地址里面看 + // 直接把仓库clone下来,或者下载zip都行 + // 脚本编辑同理 this._afterLoadResources = function () {}; }, - sprite: function () { - const sprites = {}; - - // 终于能用es6了(恼 - class Sprite { - constructor(x, y, w, h, z, reference, name) { - this.x = x; - this.y = y; - this.width = w; - this.height = h; - this.zIndex = z; - this.reference = reference; - /** @type {HTMLCanvasElement} */ - this.canvas = null; - /** @type {CanvasRenderingContext2D} */ - this.context = null; - this.count = 0; - this.name = name; - this.key = []; - this.init(); - } - - init() { - const name = this.name || `_sprite_${Sprite.count}`; - this.name = name; - if (this.reference === 'window') { - const canvas = document.createElement('canvas'); - this.canvas = canvas; - this.context = canvas.getContext('2d'); - canvas.width = this.width; - canvas.height = this.height; - canvas.style.width = this.width + 'px'; - canvas.style.height = this.height + 'px'; - canvas.style.position = 'absolute'; - canvas.style.top = this.y + 'px'; - canvas.style.left = this.x + 'px'; - canvas.style.zIndex = this.zIndex.toString(); - document.body.appendChild(canvas); - } else { - this.context = core.createCanvas( - name, - this.x, - this.y, - this.width, - this.height, - this.zIndex - ); - this.canvas = this.context.canvas; - this.count = Sprite.count; - this.canvas.style.pointerEvents = 'auto'; - } - Sprite.count++; - sprites[this.name] = this; - } - - setCss(css) { - css = css.replace('\n', ';').replace(';;', ';'); - const effects = css.split(';'); - const canvas = this.canvas; - effects.forEach(v => { - const content = v.split(':'); - let name = content[0]; - let value = content[1]; - name = name - .trim() - .split('-') - .reduce((pre, curr, i, a) => { - if (i === 0 && curr !== '') return curr; - if (a[0] === '' && i === 1) return curr; - return pre + curr.toUpperCase()[0] + curr.slice(1); - }, ''); - if (name in canvas.style) canvas.style[name] = value; - }); - return this; - } - - move(x, y, isDelta) { - if (x !== undefined && x !== null) this.x = x; - if (y !== undefined && y !== null) this.y = y; - if (this.reference === 'window') { - var ele = this.canvas; - ele.style.left = - x + (isDelta ? parseFloat(ele.style.left) : 0) + 'px'; - ele.style.top = - y + (isDelta ? parseFloat(ele.style.top) : 0) + 'px'; - } else core.relocateCanvas(this.context, x, y, isDelta); - return this; - } - - resize(w, h, styleOnly) { - if (w !== undefined && w !== null) this.width = w; - if (h !== undefined && h !== null) this.height = h; - if (this.reference === 'window') { - const ele = this.canvas; - ele.style.width = w + 'px'; - ele.style.height = h + 'px'; - if (!styleOnly) { - ele.width = w; - ele.height = h; - } - } else core.resizeCanvas(this.context, w, h, styleOnly); - return this; - } - - rotate(angle, cx, cy) { - if (this.reference === 'window') { - const left = this.x; - const top = this.y; - this.canvas.style.transformOrigin = - cx - left + 'px ' + (cy - top) + 'px'; - if (angle === 0) { - canvas.style.transform = ''; - } else { - canvas.style.transform = 'rotate(' + angle + 'deg)'; - } - } else { - core.rotateCanvas(this.context, angle, cx, cy); - } - return this; - } - - destroy() { - if (this.reference === 'window') { - if (this.canvas) document.body.removeChild(this.canvas); - } else { - core.deleteCanvas(this.name); - } - this.key?.forEach(v => - document.removeEventListener(v[0], v[1]) - ); - sprites[this.name] = void 0; - } - - /** - * 类似样板registerAction接口,但是是以该sprite的左上角为(0,0)计算的 - * @param {keyof HTMLElementEventMap} type - * @param {(...param: any[]) => void} handler - */ - on(type, handler) { - if (this.reference !== 'game') - throw new ReferenceError( - `当sprite的reference为window时,不可使用该函数` - ); - const mouse = [ - 'auxclick', - 'click', - 'contextmenu', - 'dblclick', - 'mousedown', - 'mouseup', - 'mouseenter', - 'mouseleave', - 'mousemove', - 'mouseout', - 'mouseover' - ]; - const key = ['keydown', 'keypress', 'keyup']; - const touch = [ - 'touchstart', - 'touchend', - 'touchcancel', - 'touchmove' - ]; - if (mouse.includes(type)) { - this.addEventListener(type, e => { - const px = e.offsetX / core.domStyle.scale, - py = e.offsetY / core.domStyle.scale; - handler(px, py); - }); - } else if (type === 'wheel') { - this.addEventListener('wheel', e => { - handler(e.deltaY, e.deltaX, e.deltaZ); - }); - } else if (key.includes(type)) { - // 键盘事件只能加到document上 - const listener = e => { - handler( - e.key, - e.keyCode, - e.altKey, - e.ctrlKey, - e.shiftKey - ); - }; - this.key.push([type, listener]); - document.addEventListener(type, listener); - } else if (touch.includes(type)) { - this.addEventListener(type, e => { - /** @type {TouchList} */ - const touches = e.touches; - const locs = []; - for (let i = 0; i < touches.length; i++) { - const t = touches[i]; - const { x, y } = core.actions._getClickLoc( - t.clientX, - t.clientY - ); - const px = x / core.domStyle.scale, - py = y / core.domStyle.scale; - locs.push([px, py]); - } - handler(...locs); - }); - } - } - - addEventListener() { - this.canvas.addEventListener.apply(this.canvas, arguments); - } - - removeEventListener() { - this.canvas.removeEventListener.apply(this.canvas, arguments); - } - } - - this.getSprite = function (name) { - const s = sprites[name]; - if (!s) throw new ReferenceError(`不能获得不存在的sprite`); - return sprites[name]; - }; - - Sprite.count = 0; - - window.Sprite = Sprite; - }, - 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 3: 检查道具商店 or 公共事件 - if (shop.item) { - if (core.openItemShop) { - core.openItemShop(shopId); - } else { - core.playSound('操作失败'); - core.insertAction( - '道具商店插件不存在!请检查是否存在该插件!' - ); - } - return; - } - return true; - }; - - /// 是否访问过某个快捷商店 - this.isShopVisited = function (id) { - flags.__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(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 () { - // 如果返回一个字符串,表示不能,字符串为不能使用的提示 - // 返回null代表可以使用 - - // 检查当前楼层的canUseQuickShop选项是否为false - if (core.status.thisMap.canUseQuickShop === false) - return '当前楼层不能使用快捷商店。'; - return null; - }; - }, - removeMap: function () { - // 高层塔砍层插件,删除后不会存入存档,不可浏览地图也不可飞到。 - // 推荐用法: - // 对于超高层或分区域塔,当在1区时将2区以后的地图删除;1区结束时恢复2区,进二区时删除1区地图,以此类推 - // 这样可以大幅减少存档空间,以及加快存读档速度 - - // 删除楼层 - // core.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层 - // core.removeMaps("MT10") 只删除MT10层 - this.removeMaps = function (fromId, toId, force) { - toId = toId || fromId; - var fromIndex = core.floorIds.indexOf(fromId), - toIndex = core.floorIds.indexOf(toId); - if (toIndex < 0) toIndex = core.floorIds.length - 1; - flags.__visited__ = flags.__visited__ || {}; - flags.__removed__ = flags.__removed__ || []; - flags.__disabled__ = flags.__disabled__ || {}; - flags.__leaveLoc__ = flags.__leaveLoc__ || {}; - flags.__forceDelete__ ??= {}; - let deleted = false; - for (var i = fromIndex; i <= toIndex; ++i) { - var floorId = core.floorIds[i]; - if (core.status.maps[floorId].deleted) continue; - delete flags.__visited__[floorId]; - flags.__removed__.push(floorId); - delete flags.__disabled__[floorId]; - delete flags.__leaveLoc__[floorId]; - (core.status.autoEvents || []).forEach(event => { - if (event.floorId == floorId && event.currentFloor) { - core.autoEventExecuting(event.symbol, false); - core.autoEventExecuted(event.symbol, false); - } - }); - core.status.maps[floorId].deleted = true; - core.status.maps[floorId].canFlyTo = false; - core.status.maps[floorId].canFlyFrom = false; - core.status.maps[floorId].cannotViewMap = true; - if (force) { - core.status.maps[floorId].forceDelete = true; - flags.__forceDelete__[floorId] = true; - } - deleteFlags(floorId); - deleted = true; - } - if (deleted && !main.replayChecking) { - core.splitArea(); - } - }; - - function deleteFlags(floorId) { - delete flags[`jump_${floorId}`]; - delete flags[`inte_${floorId}`]; - delete flags[`loop_${floorId}`]; - delete flags[`melt_${floorId}`]; - delete flags[`night_${floorId}`]; - } - - // 恢复楼层 - // core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层 - // core.resumeMaps("MT10") 只恢复MT10层 - this.resumeMaps = function (fromId, toId) { - toId = toId || fromId; - var fromIndex = core.floorIds.indexOf(fromId), - toIndex = core.floorIds.indexOf(toId); - if (toIndex < 0) toIndex = core.floorIds.length - 1; - flags.__removed__ = flags.__removed__ || []; - for (var i = fromIndex; i <= toIndex; ++i) { - var floorId = core.floorIds[i]; - if (!core.status.maps[floorId].deleted) continue; - if ( - core.status.maps[floorId].forceDelete || - flags.__forceDelete__[floorId] - ) - continue; - flags.__removed__ = flags.__removed__.filter(f => { - return f != floorId; - }); - core.status.maps[floorId] = core.loadFloor(floorId); - } - }; - - // 分区砍层相关 - var inAnyPartition = floorId => { - var inPartition = false; - (core.floorPartitions || []).forEach(floor => { - var fromIndex = core.floorIds.indexOf(floor[0]); - var toIndex = core.floorIds.indexOf(floor[1]); - var index = core.floorIds.indexOf(floorId); - if (fromIndex < 0 || index < 0) return; - if (toIndex < 0) toIndex = core.floorIds.length - 1; - if (index >= fromIndex && index <= toIndex) inPartition = true; - }); - return inPartition; - }; - - // 分区砍层 - this.autoRemoveMaps = function (floorId) { - if (main.mode != 'play' || !inAnyPartition(floorId)) return; - // 根据分区信息自动砍层与恢复 - (core.floorPartitions || []).forEach(floor => { - var fromIndex = core.floorIds.indexOf(floor[0]); - var toIndex = core.floorIds.indexOf(floor[1]); - var index = core.floorIds.indexOf(floorId); - if (fromIndex < 0 || index < 0) return; - if (toIndex < 0) toIndex = core.floorIds.length - 1; - if (index >= fromIndex && index <= toIndex) { - core.resumeMaps( - core.floorIds[fromIndex], - core.floorIds[toIndex] - ); - } else { - core.removeMaps( - core.floorIds[fromIndex], - core.floorIds[toIndex] - ); - } - }); - }; - }, - fiveLayers: function () { - // 是否启用五图层(增加背景2层和前景2层) 将__enable置为true即会启用;启用后请保存后刷新编辑器 - // 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层 - // 另外 请注意加入两个新图层 会让大地图的性能降低一些 - // 插件作者:ad - var __enable = true; - if (!__enable) return; - - // 创建新图层 - function createCanvas(name, zIndex) { - if (!name) return; - var canvas = document.createElement('canvas'); - canvas.id = name; - canvas.className = 'gameCanvas'; - // 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高 - if (main.mode != 'editor') canvas.style.zIndex = zIndex || 0; - // 将图层插入进游戏内容 - document.getElementById('gameDraw').appendChild(canvas); - var ctx = canvas.getContext('2d'); - core.canvas[name] = ctx; - - return canvas; - } - - var bg2Canvas = createCanvas('bg2', 20); - var fg2Canvas = createCanvas('fg2', 63); - // 大地图适配 - core.bigmap.canvas = [ - 'bg2', - 'fg2', - 'bg', - 'event', - 'event2', - 'fg', - 'damage' - ]; - core.initStatus.bg2maps = {}; - core.initStatus.fg2maps = {}; - - if (main.mode == 'editor') { - /*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/ - // 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层) - // 背景层2(bg2) 插入事件层(event)之前(即bg与event之间) - document - .getElementById('mapEdit') - .insertBefore(bg2Canvas, document.getElementById('event')); - // 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后) - document - .getElementById('mapEdit') - .insertBefore(fg2Canvas, document.getElementById('ebm')); - // 原本有三个图层 从4开始添加 - var num = 4; - // 新增图层存入editor.dom中 - editor.dom.bg2c = core.canvas.bg2.canvas; - editor.dom.bg2Ctx = core.canvas.bg2; - editor.dom.fg2c = core.canvas.fg2.canvas; - editor.dom.fg2Ctx = core.canvas.fg2; - editor.dom.maps.push('bg2map', 'fg2map'); - editor.dom.canvas.push('bg2', 'fg2'); - - // 创建编辑器上的按钮 - var createCanvasBtn = name => { - // 电脑端创建按钮 - var input = document.createElement('input'); - // layerMod4/layerMod5 - var id = 'layerMod' + num++; - // bg2map/fg2map - var value = name + 'map'; - input.type = 'radio'; - input.name = 'layerMod'; - input.id = id; - input.value = value; - editor.dom[id] = input; - input.onchange = () => { - editor.uifunctions.setLayerMod(value); - }; - return input; - }; - - var createCanvasBtn_mobile = name => { - // 手机端往选择列表中添加子选项 - var input = document.createElement('option'); - var id = 'layerMod' + num++; - var value = name + 'map'; - input.name = 'layerMod'; - input.value = value; - editor.dom[id] = input; - return input; - }; - if (!editor.isMobile) { - var input = createCanvasBtn('bg2'); - var input2 = createCanvasBtn('fg2'); - // 获取事件层及其父节点 - var child = document.getElementById('layerMod'), - parent = child.parentNode; - // 背景层2插入事件层前 - parent.insertBefore(input, child); - // 不能直接更改背景层2的innerText 所以创建文本节点 - var txt = document.createTextNode('背2'); - // 插入事件层前(即新插入的背景层2前) - parent.insertBefore(txt, child); - // 向最后插入前景层2(即插入前景层后) - parent.appendChild(input2); - var txt2 = document.createTextNode('前2'); - parent.appendChild(txt2); - } else { - var input = createCanvasBtn_mobile('bg2'); - var input2 = createCanvasBtn_mobile('fg2'); - // 手机端因为是选项 所以可以直接改innerText - input.innerText = '背景2'; - input2.innerText = '前景2'; - var parent = document.getElementById('layerMod'); - parent.insertBefore(input, parent.children[1]); - parent.appendChild(input2); - } - } - core.maps._loadFloor_doNotCopy = function () { - return [ - 'firstArrive', - 'eachArrive', - 'blocks', - 'parallelDo', - 'map', - 'bgmap', - 'fgmap', - 'bg2map', - 'fg2map', - 'events', - 'changeFloor', - 'afterBattle', - 'afterGetItem', - 'afterOpenDoor', - 'cannotMove' - ]; - }; - ////// 绘制背景和前景层 ////// - core.maps._drawBg_draw = function ( - floorId, - toDrawCtx, - cacheCtx, - config - ) { - config.ctx = cacheCtx; - core.maps._drawBg_drawBackground(floorId, config); - // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 - core.maps._drawFloorImages( - floorId, - config.ctx, - 'bg', - null, - null, - config.onMap - ); - core.maps._drawBgFgMap(floorId, 'bg', config); - if (config.onMap) { - core.drawImage( - toDrawCtx, - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - core.clearMap('bg2'); - core.clearMap(cacheCtx); - } - core.maps._drawBgFgMap(floorId, 'bg2', config); - if (config.onMap) - core.drawImage( - 'bg2', - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - config.ctx = toDrawCtx; - }; - core.maps._drawFg_draw = function ( - floorId, - toDrawCtx, - cacheCtx, - config - ) { - config.ctx = cacheCtx; - // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 - core.maps._drawFloorImages( - floorId, - config.ctx, - 'fg', - null, - null, - config.onMap - ); - core.maps._drawBgFgMap(floorId, 'fg', config); - if (config.onMap) { - core.drawImage( - toDrawCtx, - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - core.clearMap('fg2'); - core.clearMap(cacheCtx); - } - core.maps._drawBgFgMap(floorId, 'fg2', config); - if (config.onMap) - core.drawImage( - 'fg2', - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - config.ctx = toDrawCtx; - }; - ////// 移动判定 ////// - core.maps._generateMovableArray_arrays = function (floorId) { - return { - bgArray: this.getBgMapArray(floorId), - fgArray: this.getFgMapArray(floorId), - eventArray: this.getMapArray(floorId), - bg2Array: this._getBgFgMapArray('bg2', floorId), - fg2Array: this._getBgFgMapArray('fg2', floorId) - }; - }; - }, - itemShop: function () { - this.openItemShop = function (itemShopId) { - if (!main.replayChecking) { - core.plugin.openedShopId = itemShopId; - core.plugin.shopOpened.value = true; - } - }; - }, - heroFourFrames: function () { - // 样板的勇士/跟随者移动时只使用2、4两帧,观感较差。本插件可以将四帧全用上。 - - // 是否启用本插件 - var __enable = true; - if (!__enable) return; - - ['up', 'down', 'left', 'right'].forEach(one => { - // 指定中间帧动画 - core.material.icons.hero[one].midFoot = 2; - }); - - var heroMoving = timestamp => { - if (core.status.heroMoving <= 0) return; - if ( - timestamp - core.animateFrame.moveTime > - core.values.moveSpeed - ) { - core.animateFrame.leftLeg++; - core.animateFrame.moveTime = timestamp; - } - core.drawHero( - ['stop', 'leftFoot', 'midFoot', 'rightFoot'][ - core.animateFrame.leftLeg % 4 - ], - 4 * core.status.heroMoving - ); - }; - core.registerAnimationFrame('heroMoving', true, heroMoving); - - core.events._eventMoveHero_moving = function (step, moveSteps) { - var curr = moveSteps[0]; - var direction = curr[0], - x = core.getHeroLoc('x'), - y = core.getHeroLoc('y'); - // ------ 前进/后退 - var o = direction == 'backward' ? -1 : 1; - if (direction == 'forward' || direction == 'backward') - direction = core.getHeroLoc('direction'); - var faceDirection = direction; - if (direction == 'leftup' || direction == 'leftdown') - faceDirection = 'left'; - if (direction == 'rightup' || direction == 'rightdown') - faceDirection = 'right'; - core.setHeroLoc('direction', direction); - if (curr[1] <= 0) { - core.setHeroLoc('direction', faceDirection); - moveSteps.shift(); - return true; - } - if (step <= 4) core.drawHero('stop', 4 * o * step); - else if (step <= 8) core.drawHero('leftFoot', 4 * o * step); - else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8)); - else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) { - if (step == 8 || step == 16) { - core.setHeroLoc( - 'x', - x + o * core.utils.scan2[direction].x, - true - ); - core.setHeroLoc( - 'y', - y + o * core.utils.scan2[direction].y, - true - ); - core.updateFollowers(); - curr[1]--; - if (curr[1] <= 0) moveSteps.shift(); - core.setHeroLoc('direction', faceDirection); - return step == 16; - } - return false; - }; - }, - itemDetail: function () { - core.control.updateDamage = function (floorId, ctx) { - floorId = floorId || core.status.floorId; - if (!floorId || core.status.gameOver || main.mode != 'play') return; - const onMap = ctx == null; - - // 没有怪物手册 - if (!core.hasItem('book')) return; - core.status.damage.posX = core.bigmap.posX; - core.status.damage.posY = core.bigmap.posY; - if (!onMap) { - const width = core.floors[floorId].width, - height = core.floors[floorId].height; - // 地图过大的缩略图不绘制显伤 - if (width * height > core.bigmap.threshold) return; - } - this._updateDamage_damage(floorId, onMap); - this._updateDamage_extraDamage(floorId, onMap); - core.getItemDetail(floorId, onMap); // 宝石血瓶详细信息 - this.drawDamage(ctx); - }; - - // 获取宝石信息 并绘制 - this.getItemDetail = function (floorId, onMap) { - if (!core.getFlag('itemDetail')) return; - floorId ??= core.status.thisMap.floorId; - let diff = {}; - const before = core.status.hero; - const hero = core.clone(core.status.hero); - const handler = { - set(target, key, v) { - diff[key] = v - (target[key] || 0); - if (!diff[key]) diff[key] = void 0; - return true; - } - }; - core.status.hero = new Proxy(hero, handler); - - core.status.maps[floorId].blocks.forEach(function (block) { - if (block.event.cls !== 'items' || block.disable) return; - const x = block.x, - y = block.y; - // v2优化,只绘制范围内的部分 - if (onMap && core.bigmap.v2) { - if ( - x < core.bigmap.posX - core.bigmap.extend || - x > core.bigmap.posX + core._PX_ + core.bigmap.extend || - y < core.bigmap.posY - core.bigmap.extend || - y > core.bigmap.posY + core._PY_ + core.bigmap.extend - ) { - return; - } - } - diff = {}; - const id = block.event.id; - const item = core.material.items[id]; - if (item.cls === 'equips') { - // 装备也显示 - const diff = core.clone(item.equip.value ?? {}); - const per = item.equip.percentage ?? {}; - for (const name in per) { - diff[name + 'per'] = per[name].toString() + '%'; - } - drawItemDetail(diff, x, y); - return; - } - // 跟数据统计原理一样 执行效果 前后比较 - core.setFlag('__statistics__', true); - try { - eval(item.itemEffect); - } catch (error) {} - drawItemDetail(diff, x, y); - }); - core.status.hero = before; - window.hero = before; - window.flags = before.flags; - }; - - // 绘制 - function drawItemDetail(diff, x, y) { - const px = 32 * x + 2, - py = 32 * y + 31; - let content = ''; - // 获得数据和颜色 - let i = 0; - for (const name in diff) { - if (!diff[name]) continue; - let color = '#fff'; - - if (typeof diff[name] === 'number') - content = core.formatBigNumber(diff[name], true); - else content = diff[name]; - switch (name) { - case 'atk': - case 'atkper': - color = '#FF7A7A'; - break; - case 'def': - case 'defper': - color = '#00E6F1'; - break; - case 'mdef': - case 'mdefper': - color = '#6EFF83'; - break; - case 'hp': - color = '#A4FF00'; - break; - case 'hpmax': - case 'hpmaxper': - color = '#F9FF00'; - break; - case 'mana': - color = '#c66'; - break; - } - // 绘制 - core.status.damage.data.push({ - text: content, - px: px, - py: py - 10 * i, - color: color - }); - i++; - } - } - }, - skills: function () { - // 所有的主动技能效果 - var ignoreInJump = { - event: ['X20007', 'X20001', 'X20006', 'X20014', 'X20010', 'X20007'], - bg: [ - 'X20037', - 'X20038', - 'X20039', - 'X20045', - 'X20047', - 'X20053', - 'X20054', - 'X20055', - 'X20067', - 'X20068', - 'X20075', - 'X20076' - ] - }; - - /** @type {FloorIds[]} */ - const jumpIgnoreFloor = ['MT31', 'snowTown']; - // 跳跃 - this.jumpSkill = function () { - if (core.status.floorId.startsWith('tower')) - return core.drawTip('当无法使用该技能'); - if ( - jumpIgnoreFloor.includes(core.status.floorId) || - flags.onChase - ) { - return core.drawTip('当前楼层无法使用该技能'); - } - if (!flags.skill2) return; - if (!flags['jump_' + core.status.floorId]) - flags['jump_' + core.status.floorId] = 0; - if ( - core.status.floorId == 'MT14' && - flags['jump_' + core.status.floorId] == 2 && - !flags.MT14Jump - ) { - if ( - !( - core.status.hero.loc.x === 77 && - core.status.hero.loc.y === 5 && - core.status.hero.loc.direction === 'right' - ) - ) { - return core.drawTip('该地图还有一个必跳的地方,你还没有跳'); - } else flags.MT14Jump = true; - } - if (flags['jump_' + core.status.floorId] >= 3) - return core.drawTip('当前地图使用次数已用完'); - var direction = core.status.hero.loc.direction; - var loc = core.status.hero.loc; - var checkLoc = {}; - switch (direction) { - case 'up': - checkLoc.x = loc.x; - checkLoc.y = loc.y - 1; - break; - case 'right': - checkLoc.x = loc.x + 1; - checkLoc.y = loc.y; - break; - case 'down': - checkLoc.x = loc.x; - checkLoc.y = loc.y + 1; - break; - case 'left': - checkLoc.x = loc.x - 1; - checkLoc.y = loc.y; - break; - } - // 前方是否可通行 或 是怪物 - var cls = core.getBlockCls(checkLoc.x, checkLoc.y); - var noPass = core.noPass(checkLoc.x, checkLoc.y); - var id = core.getBlockId(checkLoc.x, checkLoc.y) || ''; - var bgId = - core.getBlockByNumber(core.getBgNumber(checkLoc.x, checkLoc.y)) - .event.id || ''; - // 可以通行 - if ( - !noPass || - cls == 'items' || - (id.startsWith('X') && !ignoreInJump.event.includes(id)) || - (bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId)) - ) - return core.drawTip('当前无法使用技能'); - // 不是怪物且不可以通行 - if (noPass && !(cls == 'enemys' || cls == 'enemy48')) { - var toLoc = checkNoPass( - direction, - checkLoc.x, - checkLoc.y, - true - ); - if (!toLoc) return; - core.autosave(); - if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard; - core.updateStatusBar(); - flags['jump_' + core.status.floorId]++; - if (core.status.hero.hp <= 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose('你跳死了'); - } - core.playSound('015-Jump01.ogg'); - core.insertAction([ - { type: 'jumpHero', loc: [toLoc.x, toLoc.y], time: 500 } - ]); - } - // 是怪物 - if (cls == 'enemys' || cls == 'enemy48') { - var firstNoPass = checkNoPass( - direction, - checkLoc.x, - checkLoc.y, - false - ); - if (!firstNoPass) return; - core.autosave(); - if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard; - core.updateStatusBar(); - flags['jump_' + core.status.floorId]++; - if (core.status.hero.hp <= 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose('你跳死了'); - } - core.playSound('015-Jump01.ogg'); - core.insertAction([ - { - type: 'jump', - from: [checkLoc.x, checkLoc.y], - to: [firstNoPass.x, firstNoPass.y], - time: 500, - keep: true - } - ]); - } - // 检查一条线上的不可通过 - function checkNoPass(direction, x, y, startNo) { - if (!startNo) startNo = false; - switch (direction) { - case 'up': - y--; - break; - case 'right': - x++; - break; - case 'down': - y++; - break; - case 'left': - x--; - break; - } - if ( - x > core.status.thisMap.width - 1 || - y > core.status.thisMap.height - 1 || - x < 0 || - y < 0 - ) - return core.drawTip('当前无法使用技能'); - var id = core.getBlockId(x, y) || ''; - if (core.getBgNumber(x, y)) - var bgId = - core.getBlockByNumber(core.getBgNumber(x, y)).event - .id || ''; - else var bgId = ''; - if ( - core.noPass(x, y) || - core.getBlockCls(x, y) == 'items' || - (id.startsWith('X') && !ignoreInJump.event.includes(id)) || - (bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId)) || - core.getBlockCls(x, y) == 'animates' - ) - return checkNoPass(direction, x, y, true); - if (!startNo) return checkNoPass(direction, x, y, false); - return { x: x, y: y }; - } - }; - }, - towerBoss: function () { - // 智慧boss - // 变量们 - var stage = 1, - hp = 10000, - seconds = 0, - boomLocs = [], // 随机轰炸 - heroHp; - // 初始化 - this.initTowerBoss = function () { - stage = 1; - hp = 10000; - seconds = 0; - heroHp = core.status.hero.hp; - core.dynamicChangeHp(0, 10000, 10000); - core.autoFixRouteBoss(true); - core.insertAction([{ type: 'sleep', time: 1000, noSkip: true }]); - setTimeout(core.bossCore, 1000); - }; - // 录像自动修正 - this.autoFixRouteBoss = function (isStart) { - var route = core.status.route; - if (isStart) { - // 开始修正 记录当前录像长度 - flags.startFix = route.length - 1; - return; - } - // 结束修正 删除录像 并追加跳过步骤 - route.splice(flags.startFix); - route.push('choices:0'); - delete flags.startFix; - }; - // 血条 - this.healthBar = function (now, total) { - var nowLength = (now / total) * 476; // 当前血量下绘制长度 - var color = [ - 255 * 2 - (now / total) * 2 * 255, - (now / total) * 2 * 255, - 0, - 1 - ]; // 根据当前血量计算颜色 - // 建画布 - if (!core.dymCanvas.healthBar) - core.createCanvas('healthBar', 0, 0, 480, 16, 140); - else core.clearMap('healthBar'); - // 底 - core.fillRect('healthBar', 0, 0, 480, 16, '#bbbbbb'); - // css特效 - var style = document.getElementById('healthBar').getContext('2d'); - style.shadowColor = 'rgba(0, 0, 0, 0.8)'; - style.shadowBlur = 5; - style.shadowOffsetX = 10; - style.shadowOffsetY = 5; - style.filter = 'blur(1px)'; - // 绘制 - core.fillRect('healthBar', 2, 2, nowLength, 12, color); - // css特效 - style.shadowColor = 'rgba(0, 0, 0, 0.5)'; - style.shadowOffsetX = 0; - style.shadowOffsetY = 0; - // 绘制边框 - core.strokeRect('healthBar', 1, 1, 478, 14, '#ffffff', 2); - // 绘制文字 - style.shadowColor = 'rgba(0, 0, 0, 1)'; - style.shadowBlur = 3; - style.shadowOffsetX = 2; - style.shadowOffsetY = 1; - style.filter = 'none'; - core.fillText( - 'healthBar', - now + '/' + total, - 5, - 13.5, - '#ffffff', - '16px normal' - ); - }; - // 血量变化 - this.dynamicChangeHp = function (from, to, total) { - var frame = 0, - speed = (to - from) / 50, - now = from; - var interval = window.setInterval(() => { - frame++; - if (frame == 50) { - clearInterval(interval); - core.healthBar(to, total); - } - now += speed; - core.healthBar(now, total); - }, 20); - }; - // boss说话跳字 - this.skipWord = function (words, x, y, time) { - x = x || 0; - y = y || 16; - time = time || 3000; - // 创建画布 - if (!core.dymCanvas.words) - core.createCanvas('words', x, y, 480, 24, 135); - else core.clearMap('words'); - if (flags.wordsTimeOut) clearTimeout(flags.wordsTimeOut); - core.dynamicCurtain(y, y + 24, time / 3); - // css - var style = document.getElementById('words').getContext('2d'); - style.shadowColor = 'rgba(0, 0, 0, 1)'; - style.shadowBlur = 3; - style.shadowOffsetX = 2; - style.shadowOffsetY = 1; - // 一个一个绘制 - skip1(0); - // 跳字 - function skip1(now) { - if (parseInt(now) >= words.length) { - flags.wordsTimeOut = setTimeout(() => { - core.deleteCanvas('words'); - core.deleteCanvas('wordsBg'); - }, time); - return; - } - var frame = 0, - blur = 2, - nx = 4 + now * 24; - var skip2 = window.setInterval(() => { - blur -= 0.4; - frame++; - core.clearMap('words', nx, 0, 24, 24); - style.filter = 'blur(' + blur + 'px)'; - core.fillText( - 'words', - words[now], - nx, - 20, - '#ffffff', - '22px normal' - ); - if (frame == 5) { - clearInterval(skip2); - skip1(now + 1); - } - }, 20); - } - }; - // 匀变速下降背景 - this.dynamicCurtain = function (from, to, time, width) { - width = width || 480; - if (!core.dymCanvas.wordsBg) - core.createCanvas('wordsBg', 0, from, width, 24, 130); - else core.clearMap('wordsBg'); - time /= 1000; - var ny = from, - frame = 0, - a = (2 * (to - from)) / Math.pow(time * 50, 2), - speed = a * time * 50; - var style = document.getElementById('wordsBg').getContext('2d'); - style.shadowColor = 'rgba(0, 0, 0, 0.8)'; - var wordsInterval = window.setInterval(() => { - frame++; - speed -= a; - ny += speed; - core.clearMap('wordsBg'); - style.shadowBlur = 8; - style.shadowOffsetY = 2; - core.fillRect( - 'wordsBg', - 0, - 0, - width, - ny - from, - [180, 180, 180, 0.7] - ); - style.shadowBlur = 3; - style.shadowOffsetY = 0; - core.strokeRect( - 'wordsBg', - 1, - 1, - width - 2, - ny - from - 2, - [255, 255, 255, 0.7], - 2 - ); - if (frame >= time * 50) { - clearInterval(wordsInterval); - core.clearMap('wordsBg'); - style.shadowBlur = 8; - style.shadowOffsetY = 2; - core.fillRect( - 'wordsBg', - 0, - 0, - width, - to - from, - [180, 180, 180, 0.7] - ); - style.shadowBlur = 3; - style.shadowOffsetY = 0; - core.strokeRect( - 'wordsBg', - 1, - 1, - width - 2, - ny - from - 2, - [255, 255, 255, 0.7], - 2 - ); - } - }, 20); - }; - // 攻击boss - this.attackBoss = function () { - // 每秒钟地面随机出现伤害图块 踩上去攻击boss 500血 - if (flags.canAttack) return; - if (Math.random() < 0.8) return; - if (hp > 3500) { - var nx = Math.floor(Math.random() * 13 + 1), - ny = Math.floor(Math.random() * 13 + 1); - } else if (hp > 2000) { - var nx = Math.floor(Math.random() * 11 + 2), - ny = Math.floor(Math.random() * 11 + 2); - } else if (hp > 1000) { - var nx = Math.floor(Math.random() * 9 + 3), - ny = Math.floor(Math.random() * 9 + 3); - } else { - var nx = Math.floor(Math.random() * 7 + 4), - ny = Math.floor(Math.random() * 7 + 4); - } - // 在地图上显示 - flags.canAttack = true; - if (!core.dymCanvas.attackBoss) - core.createCanvas('attackBoss', 0, 0, 480, 480, 35); - else core.clearMap('attackBoss'); - var style = document.getElementById('attackBoss').getContext('2d'); - var frame1 = 0, - blur = 3, - scale = 2, - speed = 0.04, - a = 0.0008; - var atkAnimate = window.setInterval(() => { - core.clearMap('attackBoss'); - frame1++; - speed -= a; - scale -= speed; - blur -= 0.06; - style.filter = 'blur(' + blur + 'px)'; - core.strokeCircle( - 'attackBoss', - nx * 32 + 16, - ny * 32 + 16, - 16 * scale, - [255, 150, 150, 0.7], - 4 - ); - core.fillCircle( - 'attackBoss', - nx * 32 + 16, - ny * 32 + 16, - 3 * scale, - [255, 150, 150, 0.7] - ); - if (frame1 == 50) { - clearInterval(atkAnimate); - core.clearMap('attactkBoss'); - style.filter = 'none'; - core.strokeCircle( - 'attackBoss', - nx * 32 + 16, - ny * 32 + 16, - 16, - [255, 150, 150, 0.7], - 4 - ); - core.fillCircle( - 'attackBoss', - nx * 32 + 16, - ny * 32 + 16, - 3, - [255, 150, 150, 0.7] - ); - } - }, 20); - // 实时检测勇士位置 - var frame2 = 0; - var atkBoss = window.setInterval(() => { - frame2++; - var x = core.status.hero.loc.x, - y = core.status.hero.loc.y; - // 2秒超时 - if (frame2 > 100) { - setTimeout(() => { - delete flags.canAttack; - }, 4000); - clearInterval(atkBoss); - core.deleteCanvas('attackBoss'); - return; - } - if (nx == x && ny == y) { - setTimeout(() => { - delete flags.canAttack; - }, 4000); - core.dynamicChangeHp(hp, hp - 500, 10000); - hp -= 500; - clearInterval(atkBoss); - core.deleteCanvas('attackBoss'); - if (hp > 3500) core.drawAnimate('hand', 7, 1); - else if (hp > 2000) core.drawAnimate('hand', 7, 2); - else if (hp > 1000) core.drawAnimate('hand', 7, 3); - else core.drawAnimate('hand', 7, 4); - return; - } - }, 20); - }; - // 核心函数 - this.bossCore = function () { - var interval = window.setInterval(() => { - if (stage == 1) { - if (seconds == 8) - core.skipWord('智慧之神:果然,你和别人不一样。'); - if (seconds == 12) - core.skipWord('智慧之神:你知道去躲避那些攻击。'); - if (seconds == 16) - core.skipWord( - '智慧之神:之前的那些人总会一头撞上我的攻击,悲剧收场。' - ); - if (seconds == 20) - core.skipWord('提示:踩在红圈上可以对智慧之神造成伤害'); - if (seconds > 10) core.attackBoss(); - if (seconds % 10 == 0) core.intelligentArrow(); - if (seconds % 7 == 0 && seconds != 0) - core.intelligentDoor(); - if (seconds > 20 && seconds % 13 == 0) core.icyMomentem(); - } - if (stage == 1 && hp <= 7000) { - stage++; - seconds = 0; - core.skipWord('智慧之神:不错小伙子'); - core.pauseBgm(); - } - if (stage == 2) { - if (seconds == 4) - core.skipWord('智慧之神:你的确拥有智慧。'); - if (seconds == 8) - core.skipWord('智慧之神:或许你就是那个未来的救星。'); - if (seconds == 12) - core.skipWord('智慧之神:不过,这场战斗才刚刚开始'); - if (seconds == 25) - core.skipWord('提示:方形区域均为危险区域'); - if (seconds == 15) - setTimeout(() => { - core.playSound('thunder.mp3'); - }, 500); - if (seconds == 16) core.startStage2(); - if (seconds > 20) core.attackBoss(); - if (seconds % 4 == 0 && seconds > 20) core.randomThunder(); - if (seconds > 30 && seconds % 12 == 0) core.ballThunder(); - } - if (hp <= 3500 && stage == 2) { - stage++; - seconds = 0; - core.skipWord('智慧之神:不得不说小伙子'); - core.pauseBgm(); - } - if (stage >= 3) { - if (seconds == 4) - core.skipWord('智慧之神:拥有智慧就是不一样。'); - if (seconds == 8) - core.skipWord('智慧之神:不过,你还得再过我一关!'); - if (seconds == 12) core.startStage3(); - if (seconds == 15) { - flags.booming = true; - core.randomBoom(); - } - if (seconds > 20) core.attackBoss(); - if (seconds > 20 && seconds % 10 == 0) core.chainThunder(); - if (hp == 2000 && stage == 3) { - stage++; - flags.booming = false; - core.skipWord('智慧之神:还没有结束!'); - core.startStage4(); - setTimeout(() => { - flags.booming = true; - core.randomBoom(); - }, 5000); - } - if (hp == 1000 && stage == 4) { - stage++; - flags.booming = false; - core.skipWord('智慧之神:还没有结束!!!!!!'); - core.startStage5(); - setTimeout(() => { - flags.booming = true; - core.randomBoom(); - }, 5000); - } - } - if (hp == 0) { - clearInterval(interval); - clearInterval(flags.boom); - core.status.hero.hp = heroHp; - core.autoFixRouteBoss(false); - delete flags.__bgm__; - core.pauseBgm(); - core.insertAction([ - '\t[智慧之神,E557]\b[down,7,4]看来你真的会成为那个拯救未来的人。', - '\t[智慧之神,E557]\b[down,7,4]记住,拥有智慧便可以掌控万物。', - '\t[低级智人]\b[up,hero]智慧?智慧到底是什么?', - '\t[智慧之神,E557]\b[down,7,4]最终,你会知道答案的。', - '\t[智慧之神,E557]\b[down,7,4]继续向东前进吧,那里能找到你想要的答案。', - { type: 'openDoor', loc: [13, 6], floorId: 'MT19' }, - '\t[智慧之神,E557]\b[down,7,4]我这就把你送出去', - { type: 'setValue', name: 'flag:boss1', value: 'true' }, - { type: 'changeFloor', floorId: 'MT20', loc: [7, 9] }, - { type: 'forbidSave' }, - { type: 'showStatusBar' }, - { - type: 'function', - function: '() => {\ncore.deleteAllCanvas();\n}' - } - ]); - } - seconds++; - }, 1000); - }; - // ------ 第一阶段 10000~7000血 ------ // - // 技能1 智慧之箭 1000伤害 - this.intelligentArrow = function (fromSelf) { - // 坐标 - var loc = Math.floor(Math.random() * 13 + 1); - var direction = Math.random() > 0.5 ? 'horizon' : 'vertical'; - // 执行次数 - if (!fromSelf) { - var times = Math.ceil(Math.random() * 8) + 4; - var nowTime = 1; - var times1 = window.setInterval(() => { - core.intelligentArrow(true); - nowTime++; - if (nowTime >= times) { - clearInterval(times1); - } - }, 200); - } - // 防重复 - if (core.dymCanvas['inteArrow' + loc + direction]) - return core.intelligentArrow(true); - // 危险区域 - if (!core.dymCanvas.danger1) - core.createCanvas('danger1', 0, 0, 480, 480, 35); - if (direction == 'horizon') { - for (var nx = 1; nx < 14; nx++) { - core.fillRect( - 'danger1', - nx * 32 + 2, - loc * 32 + 2, - 28, - 28, - [255, 0, 0, 0.6] - ); - } - } else { - for (var ny = 1; ny < 14; ny++) { - core.fillRect( - 'danger1', - loc * 32 + 2, - ny * 32 + 2, - 28, - 28, - [255, 0, 0, 0.6] - ); - } - } - // 箭 - if (!core.dymCanvas['inteArrow' + loc + direction]) - core.createCanvas( - 'inteArrow' + loc + direction, - 0, - 0, - 544, - 544, - 65 - ); - core.clearMap('inteArrow' + loc + direction); - if (direction == 'horizon') - core.drawImage( - 'inteArrow' + loc + direction, - 'arrow.png', - 448, - loc * 32, - 102, - 32 - ); - else - core.drawImage( - 'inteArrow' + loc + direction, - 'arrow.png', - 0, - 0, - 259, - 75, - loc * 32 - 32, - 480, - 102, - 32, - Math.PI / 2 - ); - // 动画与伤害函数 - setTimeout(() => { - core.playSound('arrow.mp3'); - core.deleteCanvas('danger1'); - // 动画效果 - var nloc = 0, - speed = 0; - var damaged = {}; - var skill1 = window.setInterval(() => { - speed -= 1; - nloc += speed; - if (direction == 'horizon') - core.relocateCanvas( - 'inteArrow' + loc + direction, - nloc, - 0 - ); - else - core.relocateCanvas( - 'inteArrow' + loc + direction, - 0, - nloc - ); - if (nloc < -480) { - core.deleteCanvas('inteArrow' + loc + direction); - clearInterval(skill1); - } - // 伤害判定 - if (!damaged[loc + direction]) { - var x = core.status.hero.loc.x, - y = core.status.hero.loc.y; - if (direction == 'horizon') { - if ( - y == loc && - Math.floor((480 + nloc) / 32) == x - ) { - damaged[loc + direction] = true; - core.drawHeroAnimate('hand'); - core.status.hero.hp -= 1000; - core.addPop(x * 32 + 16, y * 32 + 16, -1000); - core.updateStatusBar(); - if (core.status.hero.hp < 0) { - clearInterval(skill1); - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - return; - } - } - } else { - if ( - x == loc && - Math.floor((480 + nloc) / 32) == y - ) { - damaged[loc + direction] = true; - core.drawHeroAnimate('hand'); - core.status.hero.hp -= 1000; - core.addPop(x * 32 + 16, y * 32 + 16, -1000); - core.updateStatusBar(); - if (core.status.hero.hp < 0) { - clearInterval(skill1); - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - return; - } - } - } - } - }, 20); - }, 3000); - }; - // 技能2 智慧之门 随机传送 - this.intelligentDoor = function () { - if (Math.random() < 0.5) return; - // 随机位置 - var toX = Math.floor(Math.random() * 13) + 1, - toY = Math.floor(Math.random() * 13) + 1; - // 在勇士身上绘制动画 - core.drawHeroAnimate('magicAtk'); - // 在目标位置绘制动画 - if (!core.dymCanvas['door' + toX + '_' + toY]) - core.createCanvas('door' + toX + '_' + toY, 0, 0, 480, 480, 35); - else core.clearMap('door' + toX + '_' + toY); - var style = document - .getElementById('door' + toX + '_' + toY) - .getContext('2d'); - var frame = 0, - width = 0, - a = 0.0128, - speed = 0.64; - // 动画 - var skill2 = window.setInterval(() => { - frame++; - if (frame < 40) return; - if (frame == 100) { - clearInterval(skill2); - // 执行传送 - core.insertAction([{ type: 'changePos', loc: [toX, toY] }]); - // 删除传送门 - setTimeout(() => { - core.deleteCanvas('door' + toX + '_' + toY); - }, 2000); - return; - } - width += speed * 2; - speed -= a; - core.clearMap('door' + toX + '_' + toY); - style.shadowColor = 'rgba(255, 255, 255, 1)'; - style.shadowBlur = 7; - style.filter = 'blur(5px)'; - core.fillRect( - 'door' + toX + '_' + toY, - toX * 32, - toY * 32 - 24, - width, - 48, - [255, 255, 255, 0.7] - ); - style.shadowColor = 'rgba(0, 0, 0, 0.5)'; - style.filter = 'blur(3px)'; - core.strokeRect( - 'door' + toX + '_' + toY, - toX * 32, - toY * 32 - 24, - width, - 48, - [255, 255, 255, 0.7], - 3 - ); - }, 20); - }; - // 技能3 万冰之势 全屏随机转换滑冰 如果转换时在滑冰上造成5000点伤害 - this.icyMomentem = function () { - if (flags.haveIce) return; - if (Math.random() < 0.5) return; - var times = Math.floor(Math.random() * 100); - // 防卡 就setInterval吧 - var locs = [], - now = 0; - flags.haveIce = true; - if (!core.dymCanvas.icyMomentem) - core.createCanvas('icyMomentem', 0, 0, 480, 480, 35); - else core.clearMap('icyMomentem'); - var skill3 = window.setInterval(() => { - var nx = Math.floor(Math.random() * 13) + 1, - ny = Math.floor(Math.random() * 13) + 1; - if (!locs.includes([nx, ny])) { - locs.push([nx, ny]); - core.fillRect( - 'icyMomentem', - locs[now][0] * 32 + 2, - locs[now][1] * 32 + 2, - 28, - 28, - [150, 150, 255, 0.6] - ); - } - if (now == times) { - clearInterval(skill3); - skill3Effect(); - } - now++; - }, 20); - // 动画和伤害函数 - function skill3Effect() { - // 防卡 setInterval - var index = 0; - var effect = window.setInterval(() => { - var x = core.status.hero.loc.x, - y = core.status.hero.loc.y; - core.clearMap( - 'icyMomentem', - locs[index][0] * 32, - locs[index][1] * 32, - 32, - 32 - ); - core.setBgFgBlock( - 'bg', - 167, - locs[index][0], - locs[index][1] - ); - core.drawAnimate('ice', locs[index][0], locs[index][1]); - if (x == locs[index][0] && y == locs[index][1]) { - core.drawHeroAnimate('hand'); - core.status.hero.hp -= 5000; - core.addPop(x * 32 + 16, y * 32 + 16, -5000); - core.updateStatusBar(); - if (core.status.hero.hp < 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - clearInterval(effect); - return; - } - } - if (index >= locs.length - 1) { - clearInterval(effect); - setTimeout(() => { - deleteIce(locs); - }, 5000); - } - index++; - }, 50); - } - // 删除函数 - function deleteIce(locs) { - // 照样 setInterval - var index = 0; - var deleteIce = window.setInterval(() => { - core.setBgFgBlock('bg', 0, locs[index][0], locs[index][1]); - index++; - if (index >= locs.length) { - clearInterval(deleteIce); - core.deleteCanvas('icyMomentem'); - setTimeout(() => { - delete flags.haveIce; - }, 5000); - } - }, 50); - } - }; - // ------ 第二阶段 7000~3500 ------ // - // 开始第二阶段 - this.startStage2 = function () { - // 闪烁 - core.createCanvas('flash', 0, 0, 480, 480, 160); - var alpha = 0; - var frame = 0; - var start1 = window.setInterval(() => { - core.clearMap('flash'); - frame++; - if (frame <= 8) alpha += 0.125; - else alpha -= 0.01; - core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); - if (alpha == 0) { - clearInterval(start1); - core.deleteCanvas('flash'); - } - if (frame == 8) { - changeWeather(); - } - }); - // 切换天气 - function changeWeather() { - core.setWeather(); - core.setWeather('rain', 10); - core.setWeather('fog', 8); - // 色调也得换 - core.setCurtain([0, 0, 0, 0.3]); - // bgm - core.playBgm('towerBoss2.mp3'); - } - }; - // ----- 打雷相关 ----- // - // 随机打雷 - this.randomThunder = function () { - var x = Math.floor(Math.random() * 13) + 1, - y = Math.floor(Math.random() * 13) + 1, - power = Math.ceil(Math.random() * 6); - // 绘制危险区域 - if (!core.dymCanvas.thunderDanger) - core.createCanvas('thunderDanger', 0, 0, 480, 480, 35); - else core.clearMap('thunderDanger'); - // 3*3范围 - for (var nx = x - 1; nx <= x + 1; nx++) { - for (var ny = y - 1; ny <= y + 1; ny++) { - core.fillRect( - 'thunderDanger', - nx * 32 + 2, - ny * 32 + 2, - 28, - 28, - [255, 255, 255, 0.6] - ); - } - } - core.deleteCanvas('flash'); - setTimeout(() => { - core.playSound('thunder.mp3'); - }, 500); - setTimeout(() => { - core.deleteCanvas('thunderDanger'); - core.drawThunder(x, y, power); - }, 1000); - }; - // 绘制 - this.drawThunder = function (x, y, power) { - var route = core.getThunderRoute(x * 32 + 16, y * 32 + 16, power); - // 开始绘制 - if (!core.dymCanvas.thunder) - core.createCanvas('thunder', 0, 0, 480, 480, 65); - else core.clearMap('thunder'); - var style = core.dymCanvas.thunder; - style.shadowColor = 'rgba(220, 220, 255, 1)'; - style.shadowBlur = power; - style.filter = 'blur(2.5px)'; - for (var num in route) { - // 一个个绘制 - for (var i = 0; i < route[num].length - 1; i++) { - var now = route[num][i], - next = route[num][i + 1]; - core.drawLine( - 'thunder', - now[0], - now[1], - next[0], - next[1], - '#ffffff', - 2.5 - ); - } - } - // 伤害 - core.getThunderDamage(x, y, power); - // 闪一下 - var frame1 = 0, - alpha = 0.5; - if (!core.dymCanvas.flash) - core.createCanvas('flash', 0, 0, 480, 480, 160); - else core.clearMap('flash'); - var thunderFlash = window.setInterval(() => { - alpha -= 0.05; - frame1++; - core.clearMap('flash'); - core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); - if (frame1 >= 10) { - clearInterval(thunderFlash); - core.deleteCanvas('flash'); - // 删除闪电 - setTimeout(() => { - core.deleteCanvas('thunder'); - }, 700); - } - }, 20); - }; - // 获得雷电路径 - this.getThunderRoute = function (x, y, power) { - var route = []; - for (var num = 0; num < power; num++) { - var nx = x, - ny = y; - route[num] = []; - for (var i = 0; ny >= 0; i++) { - if (i > 0) { - nx += Math.random() * 30 - 15; - ny -= Math.random() * 80 + 30; - } else { - nx += Math.random() * 16 - 8; - ny += Math.random() * 16 - 8; - } - route[num].push([nx, ny]); - } - } - return route; - }; - // 打雷伤害判定 - this.getThunderDamage = function (x, y, power) { - var hx = core.status.hero.loc.x, - hy = core.status.hero.loc.y; - if (Math.abs(hx - x) <= 1 && Math.abs(hy - y) <= 1) { - core.status.hero.hp -= 3000 * power; - core.addPop(x * 32 + 16, y * 32 + 16, -3000 * power); - core.updateStatusBar(); - if (core.status.hero.hp < 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - return; - } - } - }; - // ----- 打雷 END ----- // - // 球形闪电 横竖 - this.ballThunder = function () { - // 随机数量 - var times = Math.ceil(Math.random() * 12) + 6; - var now = 0, - locs = []; - // setInterval执行 - var ballThunder = window.setInterval(() => { - // 画布 - if (!core.dymCanvas['ballThunder' + now]) - core.createCanvas('ballThunder' + now, 0, 0, 480, 480, 35); - else core.clearMap('ballThunder' + now); - var nx = Math.floor(Math.random() * 13) + 1, - ny = Math.floor(Math.random() * 13) + 1; - // 添加位置 绘制危险区域 - if (!locs.includes([nx, ny])) { - locs.push([nx, ny]); - // 横竖都要画 - for (var mx = 1; mx < 14; mx++) { - core.fillRect( - 'ballThunder' + now, - mx * 32 + 2, - ny * 32 + 2, - 28, - 28, - [190, 190, 255, 0.6] - ); - } - for (var my = 1; my < 14; my++) { - core.fillRect( - 'ballThunder' + now, - nx * 32 + 2, - my * 32 + 2, - 28, - 28, - [190, 190, 255, 0.6] - ); - } - } - now++; - if (now >= times) { - clearInterval(ballThunder); - setTimeout(() => { - thunderAnimate(locs); - }, 1000); - } - }, 200); - // 动画 伤害 - function thunderAnimate(locs) { - var frame = 0; - // 画布 - if (!core.dymCanvas.ballAnimate) - core.createCanvas('ballAnimate', 0, 0, 480, 480, 65); - else core.clearMap('ballAnimate'); - var style = core.dymCanvas.ballAnimate; - style.shadowColor = 'rgba(255, 255, 255, 1)'; - var damaged = []; - var animate = window.setInterval(() => { - core.clearMap('ballAnimate'); - for (var i = 0; i < locs.length; i++) { - style.shadowBlur = 16 * Math.random(); - // 错开执行动画 - if (frame - 10 * i > 0) { - var now = frame - 10 * i; - if (now == 1) core.playSound('electron.mp3'); - // 动画 - var nx = locs[i][0] * 32 + 16, - ny = locs[i][1] * 32 + 16; - if (now <= 2) { - core.fillCircle( - 'ballAnimate', - nx, - ny, - 16 + 3 * now, - [255, 255, 255, 0.9] - ); - } else { - // 上 - core.fillCircle( - 'ballAnimate', - nx, - ny - 4 * now, - 7 + 2 * Math.random(), - [255, 255, 255, 0.7] - ); - // 下 - core.fillCircle( - 'ballAnimate', - nx, - ny + 4 * now, - 7 + 2 * Math.random(), - [255, 255, 255, 0.7] - ); - // 左 - core.fillCircle( - 'ballAnimate', - nx - 4 * now, - ny, - 7 + 2 * Math.random(), - [255, 255, 255, 0.7] - ); - // 右 - core.fillCircle( - 'ballAnimate', - nx + 4 * now, - ny, - 7 + 2 * Math.random(), - [255, 255, 255, 0.7] - ); - } - // 清除危险区域 - core.clearMap( - 'ballThunder' + i, - nx - 16, - ny - 16 - 4 * now, - 32, - 32 - ); - core.clearMap( - 'ballThunder' + i, - nx - 16, - ny - 16 + 4 * now, - 32, - 32 - ); - core.clearMap( - 'ballThunder' + i, - nx - 16 - 4 * now, - ny - 16, - 32, - 32 - ); - core.clearMap( - 'ballThunder' + i, - nx - 16 + 4 * now, - ny - 16, - 32, - 32 - ); - // 伤害 - if (!damaged[i]) { - var x = core.status.hero.loc.x, - y = core.status.hero.loc.y; - if ( - ((Math.floor((nx - 16 - 4 * now) / 32) == - x || - Math.floor((nx - 16 + 4 * now) / 32) == - x) && - locs[i][1] == y) || - ((Math.floor((ny - 16 - 4 * now) / 32) == - y || - Math.floor((ny - 16 + 4 * now) / 32) == - y) && - locs[i][0] == x) - ) { - damaged[i] = true; - core.status.hero.hp -= 3000; - core.addPop( - x * 32 + 16, - y * 32 + 16, - -3000 - ); - core.updateStatusBar(); - core.playSound('electron.mp3'); - if (core.status.hero.hp < 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - clearInterval(animate); - return; - } - } - } - // 结束 - if (i == locs.length - 1 && now > 120) { - clearInterval(animate); - } - } - } - frame++; - }, 20); - } - }; - // ------ 第三阶段 3500~0 ------ // - this.startStage3 = function () { - // 闪烁 - core.createCanvas('flash', 0, 0, 480, 480, 160); - var alpha = 0; - var frame = 0; - var start1 = window.setInterval(() => { - core.clearMap('flash'); - frame++; - if (frame <= 8) alpha += 0.125; - else alpha -= 0.01; - core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); - if (alpha == 0) { - clearInterval(start1); - core.deleteCanvas('flash'); - } - if (frame == 8) { - core.playSound('thunder.mp3'); - changeTerra(); - core.insertAction([{ type: 'changePos', loc: [7, 7] }]); - } - }); - // 改变地形 - function changeTerra() { - for (var nx = 0; nx < 15; nx++) { - for (var ny = 0; ny < 15; ny++) { - if (nx == 0 || nx == 14 || ny == 0 || ny == 14) { - core.removeBlock(nx, ny); - } - if ( - (nx == 1 || nx == 13 || ny == 1 || ny == 13) && - nx != 0 && - nx != 14 && - ny != 0 && - ny != 14 - ) { - core.setBlock(527, nx, ny); - } - } - } - core.createCanvas('tower7', 0, 0, 480, 480, 15); - // 画贴图 - core.drawImage( - 'tower7', - 'tower7.jpeg', - 360, - 0, - 32, - 480, - 0, - 0, - 32, - 480 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 840, - 0, - 32, - 480, - 448, - 0, - 32, - 480 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 392, - 0, - 416, - 32, - 32, - 0, - 416, - 32 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 392, - 448, - 416, - 32, - 32, - 448, - 416, - 32 - ); - core.setBlock('E557', 7, 2); - core.playBgm('towerBoss3.mp3'); - } - }; - // 进入第四阶段 - this.startStage4 = function () { - // 闪烁 - core.createCanvas('flash', 0, 0, 480, 480, 160); - var alpha = 0; - var frame = 0; - var start1 = window.setInterval(() => { - core.clearMap('flash'); - frame++; - if (frame <= 8) alpha += 0.125; - else alpha -= 0.01; - core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); - if (alpha == 0) { - clearInterval(start1); - core.deleteCanvas('flash'); - } - if (frame == 8) { - core.playSound('thunder.mp3'); - changeTerra(); - core.insertAction([{ type: 'changePos', loc: [7, 7] }]); - } - }); - // 改变地形 - function changeTerra() { - for (var nx = 1; nx < 14; nx++) { - for (var ny = 1; ny < 14; ny++) { - if (nx == 1 || nx == 13 || ny == 1 || ny == 13) { - core.removeBlock(nx, ny); - } - if ( - (nx == 2 || nx == 12 || ny == 2 || ny == 12) && - nx != 1 && - nx != 13 && - ny != 1 && - ny != 13 - ) { - core.setBlock(527, nx, ny); - } - } - } - core.createCanvas('tower7', 0, 0, 480, 480, 15); - // 画贴图 - core.drawImage( - 'tower7', - 'tower7.jpeg', - 360, - 0, - 64, - 480, - 0, - 0, - 64, - 480 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 776, - 0, - 64, - 480, - 416, - 0, - 64, - 480 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 424, - 0, - 352, - 64, - 64, - 0, - 352, - 64 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 424, - 416, - 352, - 64, - 64, - 416, - 352, - 64 - ); - core.setBlock('E557', 7, 3); - } - }; - // 进入第五阶段 - this.startStage5 = function () { - // 闪烁 - core.createCanvas('flash', 0, 0, 480, 480, 160); - var alpha = 0; - var frame = 0; - var start1 = window.setInterval(() => { - core.clearMap('flash'); - frame++; - if (frame <= 8) alpha += 0.125; - else alpha -= 0.01; - core.fillRect('flash', 0, 0, 480, 480, [255, 255, 255, alpha]); - if (alpha == 0) { - clearInterval(start1); - core.deleteCanvas('flash'); - } - if (frame == 8) { - core.playSound('thunder.mp3'); - changeTerra(); - core.insertAction([{ type: 'changePos', loc: [7, 7] }]); - } - }); - // 改变地形 - function changeTerra() { - for (var nx = 2; nx < 13; nx++) { - for (var ny = 2; ny < 13; ny++) { - if (nx == 2 || nx == 12 || ny == 2 || ny == 12) { - core.removeBlock(nx, ny); - } - if ( - (nx == 3 || nx == 11 || ny == 3 || ny == 11) && - nx != 2 && - nx != 12 && - ny != 2 && - ny != 12 - ) { - core.setBlock(527, nx, ny); - } - } - } - core.createCanvas('tower7', 0, 0, 480, 480, 15); - // 画贴图 - core.drawImage( - 'tower7', - 'tower7.jpeg', - 360, - 0, - 96, - 480, - 0, - 0, - 96, - 480 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 744, - 0, - 96, - 480, - 384, - 0, - 96, - 480 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 456, - 0, - 288, - 96, - 96, - 0, - 288, - 96 - ); - core.drawImage( - 'tower7', - 'tower7.jpeg', - 456, - 384, - 288, - 96, - 96, - 384, - 288, - 96 - ); - core.setBlock('E557', 7, 4); - } - }; - // 链状闪电 随机连接 碰到勇士则受伤 - this.chainThunder = function () { - // 随机次数 - var times = Math.ceil(Math.random() * 6) + 3; - // 画布 - if (!core.dymCanvas.chainDanger) - core.createCanvas('chainDanger', 0, 0, 480, 480, 35); - else core.clearMap('chainDanger'); - // setInterval执行 - var locs = [], - now = 0; - var chain = window.setInterval(() => { - if (hp > 2000) { - var nx = Math.floor(Math.random() * 11) + 2, - ny = Math.floor(Math.random() * 11) + 2; - } else if (hp > 1000) { - var nx = Math.floor(Math.random() * 9) + 3, - ny = Math.floor(Math.random() * 9) + 3; - } else { - var nx = Math.floor(Math.random() * 7) + 4, - ny = Math.floor(Math.random() * 7) + 4; - } - if (!locs.includes([nx, ny])) { - locs.push([nx, ny]); - } else return; - // 危险线 - if (now > 0) { - core.drawLine( - 'chainDanger', - locs[now - 1][0] * 32 + 16, - locs[now - 1][1] * 32 + 16, - nx * 32 + 16, - ny * 32 + 16, - [220, 100, 255, 0.6], - 3 - ); - } - if (now >= times) { - clearInterval(chain); - setTimeout(() => { - core.getChainRoute(locs); - core.deleteCanvas('chainDanger'); - }, 1000); - } - now++; - }, 100); - }; - // 链状闪电 动画 - this.chainAnimate = function (route) { - if (!route) return core.chainThunder(); - // 画布 - if (!core.dymCanvas.chain) - core.createCanvas('chain', 0, 0, 480, 480, 65); - else core.clearMap('chain'); - var style = core.dymCanvas.chain; - style.shadowBlur = 3; - style.shadowColor = 'rgba(255, 255, 255, 1)'; - style.filter = 'blur(2px)'; - // 当然还是setInterval - var frame = 0, - now = 0; - var animate = window.setInterval(() => { - if (now >= route.length - 1) { - clearInterval(animate); - setTimeout(() => { - core.deleteCanvas('chain'); - }, 1000); - return; - } - frame++; - if (frame % 2 != 0) return; - core.drawLine( - 'chain', - route[now][0], - route[now][1], - route[now + 1][0], - route[now + 1][1], - '#ffffff', - 3 - ); - // 节点 - if (now == 0) { - core.fillCircle( - 'chain', - route[0][0], - route[0][1], - 7, - '#ffffff' - ); - } - if ( - (route[now + 1][0] - 16) % 32 == 0 && - (route[now + 1][1] - 16) % 32 == 0 - ) { - core.fillCircle( - 'chain', - route[now + 1][0], - route[now + 1][1], - 7, - '#ffffff' - ); - } - // 判断伤害 - core.lineDamage( - route[now][0], - route[now][1], - route[now + 1][0], - route[now + 1][1], - 4000 - ); - now++; - }, 20); - }; - // 链状闪电 获得闪电路径 - this.getChainRoute = function (locs) { - // 照样用setInterval - var now = 0, - routes = []; - var route = window.setInterval(() => { - var nx = locs[now][0] * 32 + 16, - ny = locs[now][1] * 32 + 16; - var tx = locs[now + 1][0] * 32 + 16, - ty = locs[now + 1][1] * 32 + 16; - var dx = tx - nx, - dy = ty - ny; - var angle = Math.atan(dy / dx); - if (dy < 0 && dx < 0) angle += Math.PI; - if (dx < 0 && dy > 0) angle += Math.PI; - // 循环 + 随机 - var times = 0; - while (true) { - times++; - nx += Math.random() * 50 * Math.cos(angle); - ny += Math.random() * 50 * Math.sin(angle); - routes.push([nx, ny]); - if ( - Math.sqrt( - Math.pow(ny - ty, 2) + Math.pow(nx - tx, 2) - ) <= 100 - ) { - routes.push([tx, ty]); - break; - } - if (times >= 20) { - clearInterval(route); - routes = null; - return; - } - } - now++; - if (now >= locs.length - 1) { - clearInterval(route); - core.chainAnimate(routes); - } - }, 2); - }; - // 随机轰炸 - this.randomBoom = function () { - // 停止轰炸 - if (!flags.booming) { - clearInterval(flags.boom); - return; - } - // 根据阶段数 分攻击速率 和范围 - var boomTime; - var range; - if (hp > 2000) { - boomTime = 500; - range = 11; - } else if (hp > 1000) { - boomTime = 400; - range = 9; - } else { - boomTime = 300; - range = 7; - } - // setInterval - flags.boom = window.setInterval(() => { - var nx = Math.floor(Math.random() * range) + (15 - range) / 2, - ny = Math.floor(Math.random() * range) + (15 - range) / 2; - boomLocs.push([nx, ny, 0]); - if (!flags.booming) clearInterval(flags.boom); - }, boomTime); - // 动画要在这里调用 - core.boomingAnimate(); - }; - // 随机轰炸 动画 - this.boomingAnimate = function () { - // 直接setInterval - if (!core.dymCanvas.boom) - core.createCanvas('boom', 0, 0, 480, 480, 65); - else core.clearMap('boom'); - var boomAnimate = window.setInterval(() => { - if (boomLocs.length == 0) return; - if (!flags.booming && boomLocs.length == 0) { - clearInterval(boomAnimate); - return; - } - core.clearMap('boom'); - boomLocs.forEach((loc, index) => { - loc[2]++; - var x = loc[0] * 32 + 16, - y = loc[1] * 32 + 16; - if (loc[2] >= 20) { - var alpha = 1, - radius = 12; - } else { - var radius = 0.12 * Math.pow(20 - loc[2], 2) + 12, - alpha = Math.max(1, 2 - loc[2] * 0.1); - } - var angle = (loc[2] * Math.PI) / 50; - // 开始绘制 - core.fillCircle('boom', x, y, 3, [255, 50, 50, alpha]); - core.strokeCircle( - 'boom', - x, - y, - radius, - [255, 50, 50, alpha], - 2 - ); - // 旋转的线 - core.drawLine( - 'boom', - x + radius * Math.cos(angle), - y + radius * Math.sin(angle), - x + (radius + 15) * Math.cos(angle), - y + (radius + 15) * Math.sin(angle), - [255, 50, 50, alpha], - 1 - ); - angle += Math.PI; - core.drawLine( - 'boom', - x + radius * Math.cos(angle), - y + radius * Math.sin(angle), - x + (radius + 15) * Math.cos(angle), - y + (radius + 15) * Math.sin(angle), - [255, 50, 50, alpha], - 1 - ); - // 炸弹 下落 - if (loc[2] > 70) { - var h = - y - - (20 * (85 - loc[2]) + - 2.8 * Math.pow(85 - loc[2], 2)); - core.drawImage( - 'boom', - 'boom.png', - x - 18, - h - 80, - 36, - 80 - ); - } - if (loc[2] == 85) { - core.drawAnimate( - 'explosion1', - (x - 16) / 32, - (y - 16) / 32 - ); - boomLocs.splice(index, 1); - if (boomLocs.length == 0) core.deleteCanvas('boom'); - // 伤害判定 - var hx = core.status.hero.loc.x, - hy = core.status.hero.loc.y; - if (loc[0] == hx && loc[1] == hy) { - core.status.hero.hp -= 3000; - core.addPop(x * 32 + 16, y * 32 + 16, -3000); - core.updateStatusBar(); - if (core.status.hero.hp < 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - clearInterval(boomAnimate); - flags.booming = false; - return; - } - } - } - }); - }, 20); - }; - // 直线型伤害判定 - this.lineDamage = function (x1, y1, x2, y2, damage) { - // 获得勇士坐标 - var x = core.status.hero.loc.x, - y = core.status.hero.loc.y; - // 是否可能碰到勇士 - if ( - (x1 < x * 32 - 12 && x2 < x * 32 - 12) || - (x1 > x * 32 + 12 && x2 > x * 32 + 12) || - (y1 < y * 32 - 16 && y2 < y * 32 - 16) || - (y1 > y * 32 + 16 && y2 > y * 32 + 16) - ) - return; - // 对角线的端点是否在直线异侧 勇士视为24 * 32 - for (var time = 1; time <= 2; time++) { - // 左下右上 - if (time == 1) { - var loc1 = [x * 32 - 12, y * 32 + 16], - loc2 = [x * 32 + 12, y * 32 - 16]; - // 直线方程 y == (y2 - y1) / (x2 - x1) * (x - x1) + y1 - var n1 = - ((y2 - y1) / (x2 - x1)) * (loc1[0] - x1) + - y1 - - loc1[1], - n2 = - ((y2 - y1) / (x2 - x1)) * (loc2[0] - x1) + - y1 - - loc2[1]; - if (n1 * n2 <= 0) { - core.status.hero.hp -= damage; - core.addPop(x * 32 + 16, y * 32 + 16, -damage); - core.updateStatusBar(); - core.playSound('electron.mp3'); - if (core.status.hero.hp < 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - return; - } - return; - } - } else { - // 左上右下 - var loc1 = [x * 32 - 12, y * 32 - 16], - loc2 = [x * 32 + 12, y * 32 + 16]; - // 直线方程 y == (y2 - y1) / (x2 - x1) * (x - x1) + y1 - var n1 = - ((y2 - y1) / (x2 - x1)) * (loc1[0] - x1) + - y1 - - loc1[1], - n2 = - ((y2 - y1) / (x2 - x1)) * (loc2[0] - x1) + - y1 - - loc2[1]; - if (n1 * n2 <= 0) { - core.status.hero.hp -= damage; - core.addPop(x * 32 + 16, y * 32 + 16, -damage); - core.updateStatusBar(); - core.playSound('electron.mp3'); - if (core.status.hero.hp < 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - return; - } - return; - } - } - } - }; - }, - popupDamage: function () { - // 伤害弹出 - // 复写阻激夹域检测 - control.prototype.checkBlock = function (forceMockery) { - var x = core.getHeroLoc('x'), - y = core.getHeroLoc('y'), - loc = x + ',' + y; - var damage = core.status.checkBlock.damage[loc]; - if (damage) { - if (!main.replayChecking) - core.addPop( - (x - core.bigmap.offsetX / 32) * 32 + 12, - (y - core.bigmap.offsetY / 32) * 32 + 20, - -damage.toString() - ); - core.status.hero.hp -= damage; - var text = - Object.keys(core.status.checkBlock.type[loc] || {}).join( - ',' - ) || '伤害'; - core.drawTip('受到' + text + damage + '点'); - core.drawHeroAnimate('zone'); - this._checkBlock_disableQuickShop(); - core.status.hero.statistics.extraDamage += damage; - if (core.status.hero.hp <= 0) { - core.status.hero.hp = 0; - core.updateStatusBar(); - core.events.lose(); - return; - } else { - core.updateStatusBar(); - } - } - this._checkBlock_repulse(core.status.checkBlock.repulse[loc]); - checkMockery(loc, forceMockery); - }; - - control.prototype.moveHero = function (direction, callback) { - // 如果正在移动,直接return - if (core.status.heroMoving != 0) return; - if (core.isset(direction)) core.setHeroLoc('direction', direction); - - const nx = core.nextX(); - const ny = core.nextY(); - if (core.status.checkBlock.mockery[`${nx},${ny}`]) { - core.autosave(); - } - - if (callback) return this.moveAction(callback); - this._moveHero_moving(); - }; - - /** - * 电摇嘲讽 - * @param {LocString} loc - * @param {boolean} force - */ - function checkMockery(loc, force) { - if (core.status.lockControl && !force) return; - const mockery = core.status.checkBlock.mockery[loc]; - if (mockery) { - mockery.sort((a, b) => - a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] - ); - const action = []; - const [tx, ty] = mockery[0]; - let { x, y } = core.status.hero.loc; - const dir = - x > tx ? 'left' : x < tx ? 'right' : y > ty ? 'up' : 'down'; - const { x: dx, y: dy } = core.utils.scan[dir]; - - action.push({ type: 'changePos', direction: dir }); - const blocks = core.getMapBlocksObj(); - while (1) { - x += dx; - y += dy; - const block = blocks[`${x},${y}`]; - if (block) { - block.event.cls === ''; - if ( - [ - 'animates', - 'autotile', - 'tileset', - 'npcs', - 'npc48' - ].includes(block.event.cls) - ) { - action.push( - { - type: 'hide', - loc: [[x, y]], - remove: true, - time: 0 - }, - { - type: 'function', - function: `function() { core.removeGlobalAnimate(${x}, ${y}) }` - }, - { - type: 'animate', - name: 'hand', - loc: [x, y], - async: true - } - ); - } - if (block.event.cls.startsWith('enemy')) { - action.push({ type: 'moveAction' }); - } - } - action.push({ type: 'moveAction' }); - if (x === tx && y === ty) break; - } - action.push({ - type: 'function', - function: `function() { core.checkBlock(true); }` - }); - action.push({ type: 'stopAsync' }); - core.insertAction(action); - } - } - }, - hotReload: function () { - if (main.mode !== 'play' || main.replayChecking) return; - - /** - * 发送请求 - * @param {string} url - * @param {string} type - * @param {string} data - * @returns {Promise} - */ - async function post(url, type, data) { - const xhr = new XMLHttpRequest(); - xhr.open(type, url); - xhr.send(data); - const res = await new Promise(res => { - xhr.onload = () => { - if (xhr.status !== 200) { - console.error(`hot reload: http ${xhr.status}`); - res('@error'); - } else res('success'); - }; - xhr.onerror = () => { - res('@error'); - console.error(`hot reload: error on connection`); - }; - }); - if (res === 'success') return xhr.response; - else return '@error'; - } - - /** - * 热重载css - * @param {string} data - */ - function reloadCss(data) { - const css = document.getElementById('mota-css'); - css.remove(); - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.href = data; - link.id = 'mota-css'; - document.head.appendChild(link); - console.log(`css hot reload: ${data}`); - } - - /** - * 热重载楼层 - * @param {string} data - */ - async function reloadFloor(data) { - // 如果被砍层了直接忽略 - if ( - core.status.maps[data].deleted || - core.status.maps[data].forceDelete - ) - return; - // 首先重新加载main.floors对应的楼层 - await import(`/project/floors/${data}.js?v=${Date.now()}`); - // 然后写入core.floors并解析 - core.floors[data] = main.floors[data]; - const floor = core.loadFloor(data); - if (core.isPlaying()) { - core.status.maps[data] = floor; - delete core.status.mapBlockObjs[data]; - core.extractBlocks(data); - if (data === core.status.floorId) { - core.drawMap(data); - let weather = core.getFlag('__weather__', null); - if (!weather && core.status.thisMap.weather) - weather = core.status.thisMap.weather; - if (weather) core.setWeather(weather[0], weather[1]); - else core.setWeather(); - } - core.updateStatusBar(true, true); - } - console.log(`floor hot reload: ${data}`); - } - - /** - * 热重载脚本编辑及插件编写 - * @param {string} data - */ - async function reloadScript(data) { - if (data === 'plugins') { - // 插件编写比较好办 - const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; - // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 - const script = document.createElement('script'); - script.src = `/project/plugins.js?v=${Date.now()}`; - document.body.appendChild(script); - await new Promise(res => { - script.onload = () => res('success'); - }); - const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; - // 找到差异的函数 - for (const id in before) { - const fn = before[id]; - if (typeof fn !== 'function') continue; - if (fn.toString() !== after[id]?.toString()) { - try { - core.plugin[id] = after[id]; - core.plugin[id].call(core.plugin); - core.updateStatusBar(true, true); - console.log(`plugin hot reload: ${id}`); - } catch (e) { - console.error(e); - } - } - } - } else if (data === 'functions') { - // 脚本编辑略微麻烦点 - const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; - // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 - const script = document.createElement('script'); - script.src = `/project/functions.js?v=${Date.now()}`; - document.body.appendChild(script); - await new Promise(res => { - script.onload = () => res('success'); - }); - const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; - // 找到差异的函数 - for (const mod in before) { - const fns = before[mod]; - for (const id in fns) { - const fn = fns[id]; - if (typeof fn !== 'function' || id === 'hasSpecial') - continue; - const now = after[mod][id]; - if (fn.toString() !== now.toString()) { - try { - if (mod === 'events') { - core.events.eventdata[id] = now; - } else if (mod === 'enemys') { - core.enemys.enemydata[id] = now; - } else if (mod === 'actions') { - core.actions.actionsdata[id] = now; - } else if (mod === 'control') { - core.control.controldata[id] = now; - } else if (mod === 'ui') { - core.ui.uidata[id] = now; - } - core.updateStatusBar(true, true); - console.log( - `function hot reload: ${mod}.${id}` - ); - } catch (e) { - console.error(e); - } - } - } - } - } - } - - /** - * 属性热重载,包括全塔属性等 - * @param {string} data - */ - async function reloadData(data) { - const script = document.createElement('script'); - script.src = `/project/${data}.js?v=${Date.now()}`; - document.body.appendChild(script); - await new Promise(res => { - script.onload = () => res('success'); - }); - - let after; - if (data === 'data') - after = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; - if (data === 'enemys') - after = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; - if (data === 'icons') - after = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; - if (data === 'items') - after = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a; - if (data === 'maps') - after = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; - if (data === 'events') - after = events_c12a15a8_c380_4b28_8144_256cba95f760; - - if (data === 'enemys') { - core.enemys.enemys = after; - for (var enemyId in after) { - core.enemys.enemys[enemyId].id = enemyId; - } - core.material.enemys = core.getEnemys(); - } else if (data === 'icons') { - core.icons.icons = after; - core.material.icons = core.getIcons(); - } else if (data === 'items') { - core.items.items = after; - for (var itemId in after) { - core.items.items[itemId].id = itemId; - } - core.material.items = core.getItems(); - } else if (data === 'maps') { - core.maps.blocksInfo = after; - core.status.mapBlockObjs = {}; - core.status.number2block = {}; - Object.values(core.status.maps).forEach(v => delete v.blocks); - core.extractBlocks(); - core.setWeather( - core.animateFrame.weather.type, - core.animateFrame.weather.level - ); - core.drawMap(); - } else if (data === 'events') { - core.events.commonEvent = after.commonEvent; - } else if (data === 'data') { - location.reload(); - } - core.updateStatusBar(true, true); - console.log(`data hot reload: ${data}`); - } - - // 初始化 - (async function () { - const data = await post('/reload', 'POST', 'test'); - if (data === '@error') { - console.log(`未检测到node服务,热重载插件将无法使用`); - } else { - console.log(`热重载插件加载成功`); - // reload - setInterval(async () => { - const res = await post('/reload', 'POST'); - if (res === '@error') return; - if (res === 'true') location.reload(); - else return; - }, 1000); - - // hot reload - setInterval(async () => { - const res = await post('/hotReload', 'POST'); - const data = res.split('@@'); - data.forEach(v => { - if (v === '') return; - const [type, file] = v.split(':'); - if (type === 'css') reloadCss(file); - if (type === 'data') reloadData(file); - if (type === 'floor') reloadFloor(file); - if (type === 'script') reloadScript(file); - }); - }, 1000); - } - })(); - }, - uiChange: function () { - if (main.replayChecking) return; - - function updateVueStatusBar() { - if (main.replayChecking) return; - core.plugin.statusBarStatus.value = - !core.plugin.statusBarStatus.value; - core.checkMarkedEnemy(); - } - - ui.prototype.drawBook = function () { - if (!core.isReplaying()) - return (core.plugin.bookOpened.value = true); - }; - - ui.prototype._drawToolbox = function () { - if (!core.isReplaying()) - return (core.plugin.toolOpened.value = true); - }; - - ui.prototype._drawEquipbox = function () { - if (!core.isReplaying()) - return (core.plugin.equipOpened.value = true); - }; - - ui.prototype.drawFly = function () { - if (!core.isReplaying()) - return (core.plugin.flyOpened.value = true); - }; - - control.prototype.updateStatusBar_update = function () { - core.control.updateNextFrame = false; - if (!core.isPlaying() || core.hasFlag('__statistics__')) return; - core.control.controldata.updateStatusBar(); - if (!core.control.noAutoEvents) core.checkAutoEvents(); - core.control._updateStatusBar_setToolboxIcon(); - core.clearRouteFolding(); - core.control.noAutoEvents = true; - // 更新vue状态栏 - updateVueStatusBar(); - }; - - control.prototype.showStatusBar = function () { - if (main.mode == 'editor') return; - core.removeFlag('hideStatusBar'); - core.plugin.showStatusBar.value = true; - core.dom.tools.hard.style.display = 'block'; - core.dom.toolBar.style.display = 'block'; - }; - - control.prototype.hideStatusBar = function (showToolbox) { - if (main.mode == 'editor') return; - - // 如果原本就是隐藏的,则先显示 - if (!core.domStyle.showStatusBar) this.showStatusBar(); - if (core.isReplaying()) showToolbox = true; - core.plugin.showStatusBar.value = false; - - var toolItems = core.dom.tools; - core.setFlag('hideStatusBar', true); - core.setFlag('showToolbox', showToolbox || null); - if ( - (!core.domStyle.isVertical && !core.flags.extendToolbar) || - !showToolbox - ) { - for (var i = 0; i < toolItems.length; ++i) - toolItems[i].style.display = 'none'; - } - if (!core.domStyle.isVertical && !core.flags.extendToolbar) { - core.dom.toolBar.style.display = 'none'; - } - }; - - this.showChapter = function (chapter) { - if (core.isReplaying()) return; - core.plugin.chapterContent.value = chapter; - core.plugin.chapterShowed.value = true; - }; - - this.openSkill = function () { - if (core.isReplaying()) return; - core.plugin.skillOpened.value = true; - }; - }, - remainEnemy: function () { - /** - * 检查漏怪 - * @param {FloorIds[]} floorIds - */ - this.checkRemainEnemy = function (floorIds) { - /** - * @type {Record} - */ - const enemy = {}; - floorIds.forEach(v => { - core.extractBlocks(v); - const blocks = core.status.maps[v].blocks; - blocks.forEach(block => { - if (!block.event.cls.startsWith('enemy') || block.disable) - return; - /** - * @type {EnemyIds} - */ - const id = block.event.id; - enemy[v] ??= []; - const info = enemy[v]; - info.push({ loc: [block.x, block.y], id }); - }); - }); - return enemy; - }; - - /** - * 获取剩余怪物字符串 - * @param {FloorIds[]} floorIds - */ - this.getRemainEnemyString = function (floorIds) { - const enemy = this.checkRemainEnemy(floorIds); - const str = []; - let now = []; - for (const floor in enemy) { - /** - * @type {{loc: LocArr, id: EnemyIds}[]} - */ - const all = enemy[floor]; - /** - * @type {Record} - */ - const remain = {}; - all.forEach(v => { - const id = v.id; - remain[id] ??= 0; - remain[id]++; - }); - const title = core.status.maps[floor].title; - for (const id in remain) { - const name = core.material.enemys[id].name; - now.push(`${title}(${floor}): ${name} * ${remain[id]}`); - if (now.length === 10) { - str.push(now.join('\n')); - now = []; - } - } - } - if (now.length > 0) { - str.push(now.join('\n')); - str[0] = `当前剩余怪物:\n${str[0]}`; - } - - return str; - }; - }, - replay: function () { - const replayableSettings = ['autoSkill']; - - // 注册修改设置的录像操作 - core.registerReplayAction('settings', name => { - if (!name.startsWith('set:')) return false; - const [, setting, value] = name.split(':'); - const v = eval(value); - if (typeof v !== 'boolean') return false; - if (!replayableSettings.includes(setting)) return false; - flags[setting] = v; - core.replay(); - return true; - }); - - core.registerReplayAction('upgradeSkill', name => { - if (!name.startsWith('skill:')) return false; - const skill = parseInt(name.slice(6)); - core.upgradeSkill(skill); - core.replay(); - return true; - }); - - core.registerReplayAction('study', name => { - if (!name.startsWith('study:')) return false; - const [num, x, y] = name - .slice(6) - .split(',') - .map(v => parseInt(v)); - if (!core.canStudySkill(num)) return false; - const id = core.getBlockId(x, y); - const enemy = core.getEnemyInfo(id, void 0, x, y); - if (!enemy.special.includes(num)) return false; - core.studySkill(enemy, num); - core.replay(); - return true; - }); - - // 商店 - let shopOpened = false; - let openedShopId = ''; - core.registerReplayAction('openShop', name => { - if (!name.startsWith('openShop:')) return false; - openedShopId = name.slice(9); - shopOpened = true; - core.replay(); - return true; - }); - - core.registerReplayAction('buy', name => { - if (!name.startsWith('buy:') && !name.startsWith('sell:')) - return false; - if (!shopOpened) return false; - if (!openedShopId) return false; - const [type, id, num] = name - .split(':') - .map(v => (/^\d+$/.test(v) ? parseInt(v) : v)); - const shop = core.status.shops[id]; - const item = shop.choices.find(v => v.id === id); - if (!item) return false; - flags.itemShop ??= {}; - flags.itemShop[openedShopId] ??= {}; - flags.itemShop[openedShopId][id] ??= 0; - if (num > item.number - flags.itemShop[openedShopId][id]) { - return false; - } - let cost = 0; - if (type === 'buy') { - cost = item.money * num; - } else { - cost = -item.sell * num; - } - if (cost > core.status.hero.money) return false; - core.status.hero.money -= cost; - flags.itemShop[openedShopId][id] += type === 'buy' ? num : -num; - core.replay(); - return true; - }); - - core.registerReplayAction('closeShop', name => { - if (name !== 'closeShop') return false; - if (!shopOpened) return false; - shopOpened = false; - openedShopId = ''; - core.replay(); - return true; - }); - }, - skillTree: function () { - /** - * @type {number[]} - */ - let levels = []; - - /** - * @type {Record} - */ - const skills = { - chapter1: [ - { - index: 0, - title: '力量', - desc: [ - '力量就是根本!可以通过智慧增加力量,每级增加2点攻击。' - ], - consume: '10 * level + 10', - front: [], - loc: [1, 2], - max: 10, - effect: ['攻击 + ${level * 2}'] - }, - { - index: 1, - title: '致命一击', - desc: ['爆发出全部力量攻击敌人,每级增加5点额外攻击。'], - consume: '30 * level + 30', - front: [[0, 5]], - loc: [2, 1], - max: 10, - effect: ['额外攻击 + ${level * 5}'] - }, - { - index: 2, - title: '断灭之刃', - desc: [ - '主动技能,快捷键1,', - '开启后会在战斗时会额外增加一定量的攻击,但同时减少一定量的防御。' - ], - consume: '200 * level + 400', - front: [[1, 5]], - loc: [4, 1], - max: 5, - effect: ['增加${level * 10}%攻击,减少${level * 10}%防御'] - }, - { - index: 3, - title: '坚韧', - desc: ['由智慧转化出坚韧!每级增加2点防御'], - consume: '10 * level + 10', - front: [], - loc: [1, 4], - max: 10, - effect: ['防御 + ${level * 2}'] - }, - { - index: 4, - title: '回春', - desc: ['让智慧化为治愈之泉水!每级增加1点生命回复'], - consume: '20 * level + 20', - front: [[3, 5]], - loc: [2, 5], - max: 25, - effect: ['生命回复 + ${level}'] - }, - { - index: 5, - title: '治愈之泉', - desc: [ - '让生命变得更多一些吧!每吃50瓶血瓶就增加当前生命回复10%的生命回复' - ], - consume: '1500', - front: [[4, 25]], - loc: [4, 5], - max: 1, - effect: ['50瓶血10%生命回复'] - }, - { - index: 6, - title: '坚固之盾', - desc: ['让护甲更加坚硬一些吧!每级增加10点防御'], - consume: '50 + level * 50', - front: [[3, 5]], - loc: [2, 3], - max: 10, - effect: ['防御 + ${level * 10}'] - }, - { - index: 7, - title: '无上之盾', - desc: [ - '第一章终极技能,战斗时智慧会充当等量护盾' - ], - consume: '2500', - front: [ - [6, 10], - [5, 1], - [2, 2] - ], - loc: [5, 3], - max: 1, - effect: ['战斗时智慧会充当护盾'] - } - ], - chapter2: [ - { - index: 8, - title: '锋利', - desc: ['让剑变得更加锋利!每级使攻击增加1%(buff式增加)'], - consume: 'level > 5 ? 50 * level ** 2 : 250 * level + 250', - front: [], - loc: [1, 2], - max: 15, - effect: ['攻击增加${level}%'] - }, - { - index: 9, - title: '坚硬', - desc: [ - '让盾牌变得更加坚固!每级使防御增加1%(buff式增加)' - ], - consume: 'level > 5 ? 50 * level ** 2 : 250 * level + 250', - front: [], - loc: [1, 4], - max: 15, - effect: ['防御增加${level}%'] - }, - { - index: 10, - title: '铸剑为盾', - desc: [ - '主动技能,快捷键3,', - '减少一定的攻击,增加一定的防御' - ], - consume: '500 * level + 1000', - front: [[9, 5]], - loc: [2, 5], - max: 5, - effect: [ - '增加${level * 10}%的防御,减少${level * 10}%的攻击' - ] - }, - { - index: 11, - title: '学习', - desc: [ - '主动技能,可以消耗500智慧学习一个怪物的技能,', - '持续5场战斗,每学习一次消耗的智慧点增加250,每次升级使持续的战斗次数增加3次。更多信息可在学习后在百科全书查看。' - ], - consume: '2500 * level ** 2 + 2500', - front: [ - [8, 10], - [12, 5] - ], - loc: [4, 1], - max: 6, - effect: ['学习怪物技能,持续${level * 3 + 2}场战斗'] - }, - { - index: 12, - title: '聪慧', - desc: [ - '使主角变得更加聪明,每级使绿宝石增加的智慧点上升5%' - ], - consume: - 'level > 5 ? 100 * level ** 2 : 250 * level + 1250', - front: [ - [8, 10], - [9, 10] - ], - loc: [3, 3], - max: 20, - effect: ['增加${level * 5}%绿宝石效果'] - }, - { - index: 13, - title: '治愈', - desc: [ - '使主角能够更好地回复生命,每级使血瓶的加血量增加2%' - ], - consume: - 'level > 5 ? 100 * level ** 2 : 250 * level + 1250', - front: [[10, 3]], - loc: [4, 5], - max: 20, - effect: ['增加${level * 2}%的血瓶回血量'] - }, - { - index: 14, - title: '胜利之号', - desc: [ - '第二章终极技能,', - '每打一个怪物,勇士在本楼层对怪物造成的伤害便增加1%' - ], - consume: '15000', - front: [ - [13, 10], - [12, 10], - [11, 3] - ], - loc: [5, 3], - max: 1, - effect: ['每打一个怪,勇士造成的伤害增加1%'] - } - ] - }; - - core.plugin.skills = skills; - - this.getSkillFromIndex = function (index) { - for (const [, skill] of Object.entries(skills)) { - const s = skill.find(v => v.index === index); - if (s) return s; - } - }; - - /** - * 获取技能等级 - * @param {number} skill - */ - this.getSkillLevel = function (skill) { - return (levels[skill] ??= 0); - }; - - this.getSkillConsume = function (skill) { - return eval( - this.getSkillFromIndex(skill).consume.replace( - /level(:\d+)?/g, - (str, $1) => { - if ($1) return `core.getSkillLevel(${$1})`; - else return `core.getSkillLevel(${skill})`; - } - ) - ); - }; - - this.openTree = function () { - if (main.replayChecking) return; - core.plugin.skillTreeOpened.value = true; - }; - - /** - * 能否升级某个技能 - * @param {number} skill - */ - function canUpgrade(skill) { - const consume = core.getSkillConsume(skill); - if (consume > core.status.hero.mdef) return false; - const level = core.getSkillLevel(skill); - const s = core.getSkillFromIndex(skill); - if (level === s.max) return false; - const front = s.front; - for (const [skill, level] of front) { - if (core.getSkillLevel(skill) < level) return false; - } - return true; - } - - /** - * 实际升级效果 - * @param {number} skill - */ - this.upgradeSkill = function (skill) { - if (!canUpgrade(skill)) return false; - switch (skill) { - case 0: // 力量 +2攻击 - core.status.hero.atk += 2; - break; - case 1: // 致命一击 +5额外攻击 - core.status.hero.mana += 5; - break; - case 2: // 断灭之刃 - core.setFlag('bladeOn', true); - break; - case 3: // 坚韧 +2防御 - core.status.hero.def += 2; - break; - case 4: // 回春 +1回复 - core.status.hero.hpmax += 1; - break; - case 5: // 治愈之泉 - core.setFlag('spring', true); - break; - case 6: // 坚固之盾 +10防御 - core.status.hero.def += 10; - break; - case 7: // 无上之盾 - core.setFlag('superSheild', true); - break; - case 8: // 锋利 +1%攻击 - core.addBuff('atk', 0.01); - break; - case 9: // 锋利 +1%防御 - core.addBuff('def', 0.01); - break; - case 10: // 铸剑为盾 - core.setFlag('shieldOn', true); - break; - case 11: // 学习 - core.setItem('I565', 1); - break; - } - const consume = core.getSkillConsume(skill); - core.status.hero.mdef -= consume; - levels[skill]++; - core.updateStatusBar(); - return true; - }; - - this.saveSkillTree = function () { - return levels.slice(); - }; - - this.loadSkillTree = function (data) { - levels = data ?? []; - }; - }, - loopMap: function () { - const list = (this.loopMapList = ['tower6']); - - /** - * 设置循环地图的偏移量 - * @param {number} offset 横向偏移量 - * @param {FloorIds} floorId - */ - this.setLoopMap = function (offset, floorId) { - const floor = core.status.maps[floorId]; - if (offset < 9) { - moveMap(floor.width - 17, floorId); - } - if (offset > floor.width - 9) { - moveMap(17 - floor.width, floorId); - } - }; - - /** - * 当勇士移动时自动设置循环地图 - * @param {FloorIds} floorId - */ - this.autoSetLoopMap = function (floorId) { - this.setLoopMap(core.status.hero.loc.x, floorId); - }; - - this.checkLoopMap = function () { - if (isLoopMap(core.status.floorId)) { - this.autoSetLoopMap(core.status.floorId); - } - }; - - /** - * 移动地图 - * @param {number} delta - * @param {FloorIds} floorId - */ - function moveMap(delta, floorId) { - core.extractBlocks(floorId); - const floor = core.status.maps[floorId]; - core.setHeroLoc('x', core.status.hero.loc.x + delta); - flags[`loop_${floorId}`] += delta; - flags[`loop_${floorId}`] %= floor.width; - const origin = floor.blocks.slice(); - for (let i = 0; i < origin.length; i++) { - core.removeBlockByIndex(0, floorId); - core.removeGlobalAnimate(origin[i].x, origin[i].y); - } - origin.forEach(v => { - let to = v.x + delta; - if (to >= floor.width) to -= floor.width; - if (to < 0) to += floor.width; - core.setBlock(v.id, to, v.y, floorId, true); - core.setMapBlockDisabled(floorId, to, v.y, false); - }); - core.drawMap(); - core.drawHero(); - } - - function isLoopMap(floorId) { - return list.includes(floorId); - } - - events.prototype._sys_changeFloor = function (data, callback) { - data = data.event.data; - let heroLoc = {}; - if (isLoopMap(data.floorId)) { - const floor = core.status.maps[data.floorId]; - flags[`loop_${data.floorId}`] ??= 0; - let tx = data.loc[0] + flags[`loop_${data.floorId}`]; - tx %= floor.width; - if (tx < 0) tx += floor.width; - heroLoc = { - x: tx, - y: data.loc[1] - }; - } else if (data.loc) heroLoc = { x: data.loc[0], y: data.loc[1] }; - if (data.direction) heroLoc.direction = data.direction; - if (core.status.event.id != 'action') core.status.event.id = null; - core.changeFloor( - data.floorId, - data.stair, - heroLoc, - data.time, - function () { - core.replay(); - if (callback) callback(); - } - ); - }; - - events.prototype.trigger = function (x, y, callback) { - var _executeCallback = function () { - // 因为trigger之后还有可能触发其他同步脚本(比如阻激夹域检测) - // 所以这里强制callback被异步触发 - if (callback) { - setTimeout(callback, 1); // +1是为了录像检测系统 - } - return; - }; - if (core.status.gameOver) return _executeCallback(); - if (core.status.event.id == 'action') { - core.insertAction( - { - type: 'function', - function: - 'function () { core.events._trigger_inAction(' + - x + - ',' + - y + - '); }', - async: true - }, - null, - null, - null, - true - ); - return _executeCallback(); - } - if (core.status.event.id) return _executeCallback(); - - let block = core.getBlock(x, y); - const id = core.status.floorId; - const loop = isLoopMap(id); - if (loop && flags[`loop_${id}`] !== 0) { - if (block && block.event.trigger === 'changeFloor') { - delete block.event.trigger; - core.maps._addInfo(block); - } else { - const floor = core.status.maps[id]; - let tx = x - flags[`loop_${id}`]; - tx %= floor.width; - if (tx < 0) tx += floor.width; - const c = core.floors[id].changeFloor[`${tx},${y}`]; - if (c) { - const b = { event: {}, x: tx, y }; - b.event.data = c; - b.event.trigger = 'changeFloor'; - block = b; - } - } - } - - if (block == null) return _executeCallback(); - - // 执行该点的脚本 - if (block.event.script) { - core.clearRouteFolding(); - try { - eval(block.event.script); - } catch (ee) { - console.error(ee); - } - } - - // 碰触事件 - if (block.event.event) { - core.clearRouteFolding(); - core.insertAction(block.event.event, block.x, block.y); - // 不再执行该点的系统事件 - return _executeCallback(); - } - - if (block.event.trigger && block.event.trigger != 'null') { - var noPass = block.event.noPass, - trigger = block.event.trigger; - if (noPass) core.clearAutomaticRouteNode(x, y); - - // 转换楼层能否穿透 - if ( - trigger == 'changeFloor' && - !noPass && - this._trigger_ignoreChangeFloor(block) && - !loop - ) - return _executeCallback(); - core.status.automaticRoute.moveDirectly = false; - this.doSystemEvent(trigger, block); - } - return _executeCallback(); - }; - - maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { - floorId = floorId || core.status.floorId; - if (!floorId) return []; - var width = core.floors[floorId].width; - var height = core.floors[floorId].height; - - if (!noCache && core.status[name + 'maps'][floorId]) - return core.status[name + 'maps'][floorId]; - - var arr = - main.mode == 'editor' && - !(window.editor && editor.uievent && editor.uievent.isOpen) - ? core.cloneArray(editor[name + 'map']) - : null; - if (arr == null) - arr = core.cloneArray(core.floors[floorId][name + 'map'] || []); - - if (isLoopMap(floorId) && window.flags) { - flags[`loop_${floorId}`] ??= 0; - arr.forEach(v => { - core.slide(v, flags[`loop_${floorId}`] % width); - }); - } - - for (var y = 0; y < height; ++y) { - if (arr[y] == null) arr[y] = Array(width).fill(0); - } - (core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach( - function (one) { - arr[one[1]][one[0]] = one[2] || 0; - } - ); - (core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach( - function (one) { - arr[one[1]][one[0]] = 0; - } - ); - if (main.mode == 'editor') { - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - arr[y][x] = arr[y][x].idnum || arr[y][x] || 0; - } - } - } - if (core.status[name + 'maps']) - core.status[name + 'maps'][floorId] = arr; - return arr; - }; - }, - study: function () { - // 负责勇士技能:学习 - const values = { - 1: ['crit'], - 6: ['n'], - 7: ['hungry'], - 8: ['together'], - 10: ['courage'], - 11: ['charge'] - }; - - const cannotStudy = [9, 12, 14, 15, 24]; - - this.canStudySkill = function (number) { - const s = (core.status.hero.special ??= { num: [], last: [] }); - if (core.getSkillLevel(11) === 0) return false; - if (s.num.length >= 1) return false; - if (s.num.includes(number)) return false; - if (cannotStudy.includes(number)) return false; - return true; - }; - - this.studySkill = function (enemy, number) { - core.status.hero.special ??= { num: [], last: [] }; - const s = core.status.hero.special; - const specials = core.getSpecials(); - let special = specials[number - 1][1]; - if (special instanceof Function) special = special(enemy); - if (!this.canStudySkill(number)) { - if (!main.replayChecking) { - core.tip('error', `无法学习${special}`); - } - return; - } - s.num.push(number); - s.last.push(core.getSkillLevel(11) * 3 + 2); - const value = values[number] ?? []; - for (const key of value) { - s[key] = enemy[key]; - } - }; - - this.forgetStudiedSkill = function (num, i) { - const s = core.status.hero.special; - const index = i !== void 0 && i !== null ? i : s.num.indexOf(num); - if (index === -1) return; - s.num.splice(index, 1); - s.last.splice(index, 1); - const value = values[number] ?? []; - for (const key of value) { - delete s[key]; - } - }; - - this.declineStudiedSkill = function () { - const s = (core.status.hero.special ??= { num: [], last: [] }); - s.last = s.last.map(v => v - 1); - }; - - this.checkStudiedSkill = function () { - const s = core.status.hero.special; - for (let i = 0; i < s.last.length; i++) { - if (s.last[i] <= 0) { - this.forgetStudiedSkill(void 0, i); - i--; - } - } - }; - }, - haloRange: function () { - /** - * 绘制光环范围 - * @param {CanvasRenderingContext2D} ctx - * @param {boolean} onMap - */ - this.drawHalo = function (ctx, onMap) { - if (main.replayChecking) return; - if (!core.getLocalStorage('showHalo', true)) return; - const halo = core.status.checkBlock.halo; - ctx.save(); - for (const [loc, range] of Object.entries(halo)) { - const [x, y] = loc.split(',').map(v => parseInt(v)); - for (const r of range) { - const [type, value, color, border] = r.split(':'); - if (type === 'square') { - // 正方形光环 - const n = parseInt(value); - const r = Math.floor(n / 2); - let left = x - r, - right = x + r, - top = y - r, - bottom = y + r; - if (onMap && core.bigmap.v2) { - left -= core.bigmap.posX; - top -= core.bigmap.posY; - right -= core.bigmap.posX; - bottom -= core.bigmap.posY; - if ( - right < -1 || - left > core._PX_ / 32 + 1 || - top < -1 || - bottom > core._PY_ / 32 + 1 - ) { - continue; - } - } - ctx.fillStyle = color; - ctx.strokeStyle = border ?? color; - ctx.lineWidth = 1; - ctx.globalAlpha = 0.1; - ctx.fillRect(left * 32, top * 32, n * 32, n * 32); - ctx.globalAlpha = 0.6; - ctx.strokeRect(left * 32, top * 32, n * 32, n * 32); - } - } - } - ctx.restore(); - }; - }, - hero: function () { - /** - * 获取勇士在某一点的属性 - * @param {keyof HeroStatus | 'all'} name - * @param {number} x - * @param {number} y - * @param {FloorIds} floorId - */ - this.getHeroStatusOn = function (name, x, y, floorId) { - return this.getRealStatusOf(core.status.hero, name, x, y, floorId); - }; - - this.getHeroStatusOf = function (status, name, x, y, floorId) { - return getRealStatus(status, name, x, y, floorId); - }; - - function getRealStatus(status, name, x, y, floorId) { - if (name instanceof Array) { - return Object.fromEntries( - name.map(v => [ - v, - v !== 'all' && getRealStatus(status, v, x, y, floorId) - ]) - ); - } - - if (name === 'all') { - return Object.fromEntries( - Object.keys(core.status.hero).map(v => [ - v, - v !== 'all' && getRealStatus(status, v, x, y, floorId) - ]) - ); - } - - let s = status?.[name] ?? core.status.hero[name]; - if (s === null || s === void 0) { - throw new ReferenceError( - `Wrong hero status property name is delivered: ${name}` - ); - } - - x ??= core.status.hero.loc.x; - y ??= core.status.hero.loc.y; - floorId ??= core.status.floorId; - - // 永夜、极昼 - if (name === 'atk' || name === 'def') { - s += window.flags?.[`night_${floorId}`] ?? 0; - } - - // 技能 - if (flags.bladeOn && flags.blade) { - const level = core.getSkillLevel(2); - if (name === 'atk') { - s *= 1 + 0.1 * level; - } - if (name === 'def') { - s *= 1 - 0.1 * level; - } - } - if (flags.shield && flags.shieldOn) { - const level = core.getSkillLevel(10); - if (name === 'atk') { - s *= 1 - 0.1 * level; - } - if (name === 'def') { - s *= 1 + 0.1 * level; - } - } - - // buff - if (typeof s === 'number') s *= core.getBuff(name); - - // 取整 - if (typeof s === 'number') s = Math.floor(s); - return s; - } - }, pluginUtils: function () { - /** - * 滑动数组 - * @param {any[]} arr - * @param {number} delta - */ - this.slide = function (arr, delta) { - if (delta === 0) return arr; - delta %= arr.length; - if (delta > 0) { - arr.unshift(...arr.splice(arr.length - delta, delta)); - return arr; - } - if (delta < 0) { - arr.push(...arr.splice(0, -delta)); - return arr; - } - }; - - this.backDir = function (dir) { - return { - up: 'down', - down: 'up', - left: 'right', - right: 'left' - }[dir]; - }; - - this.has = function (v) { - return v !== null && v !== void 0; - }; - + // 保留这个函数,以保证main.js能够使用 this.maxGameScale = function (n = 0) { const index = core.domStyle.availableScale.indexOf( core.domStyle.scale diff --git a/script/compress.ts b/script/compress.ts index 567f6e9..4b003df 100644 --- a/script/compress.ts +++ b/script/compress.ts @@ -98,7 +98,9 @@ import { exec } from 'child_process'; // 3. 压缩js插件 try { exec( - 'babel ./dist/project/plugin --out-file ./dist/project/plugin.min.js' + `babel ${data.main.plugin + .map(v => `./dist/project/plugin/${v}.js`) + .join(' ')} --out-file ./dist/project/plugin.min.js` ).on('close', async () => { const main = await fs.readFile('./dist/main.js', 'utf-8'); await fs.writeFile( diff --git a/src/data/desc.json b/src/data/desc.json index d10b412..6c9bf79 100644 --- a/src/data/desc.json +++ b/src/data/desc.json @@ -51,17 +51,19 @@ "
", "游戏版本:V1.0.0", "
", + "游戏作者:古祠", + "
", "游戏开源地址:", "https://github.com/unanmed/HumanBreak", "
", - "游戏作者:古祠", - "
", "本塔遵循MIT开源协议。查看开源协议", "
", - "BGM来源:网易云音乐等", + "音乐来源:网易云音乐等", "
", "素材来源:大素材库、爱给网、网站素材库等", "
", + "特别说明:素材与音乐均来自网络,不得用于商业用途,仅用于参考与学习", + "
", "特别鸣谢:无名甲烷菌(提供部分特殊属性与机制想法)", "
", "测试:" @@ -450,14 +452,14 @@ }, "study": { "text": "学习", - "condition": "core.getSkillLevel(11) > 0", + "condition": "core.plugin.skillTree.getSkillLevel(11) > 0", "desc": [ "本条目会详细说明学习的机制与所有可以被学习的技能被学习后的效果。当前已经学习的技能会以与状态栏类似的盒子展示出来。", "
", "
", "首先,学习技能消耗的智慧点会越来越多,初始消耗的智慧点为500,每学习一次增加250。", "学习的技能可以持续5场战斗,在技能树界面每升级一次增加3场,", - "当前为${core.getSkillLevel(11) * 3 + 2}场。", + "当前为${core.plugin.skillTree.getSkillLevel(11) * 3 + 2}场。", "学习后对应属性的值,例如抱团怪增加的属性百分比,会与被学习的怪物相同。学习界面可以使用背包中的道具或点击状态栏打开。", "
", "
", diff --git a/src/data/skill.json b/src/data/skill.json index 4df2a5a..312cec5 100644 --- a/src/data/skill.json +++ b/src/data/skill.json @@ -8,7 +8,7 @@ }, "blade": { "text": "1:断灭之刃", - "opened": "core.getSkillLevel(2) > 0", + "opened": "core.plugin.skillTree.getSkillLevel(2) > 0", "desc": [ "快捷键1,开启后勇士攻击增加${level:2 * 10}%,", "同时防御减少${level:2 * 10}%。", @@ -31,7 +31,7 @@ }, "shield": { "text": "3:铸剑为盾", - "opened": "core.getSkillLevel(10) > 0", + "opened": "core.plugin.skillTree.getSkillLevel(10) > 0", "desc": [ "快捷键3,开启后勇士防御增加${level:10 * 10}%,", "同时攻击减少${level:10 * 10}%。", diff --git a/src/plugin/chase/chase1.ts b/src/plugin/chase/chase1.ts index de12532..8d8b1a5 100644 --- a/src/plugin/chase/chase1.ts +++ b/src/plugin/chase/chase1.ts @@ -576,7 +576,7 @@ export function para3(chase: Chase) { 'MT14', async () => { flags.finishChase1 = true; - core.autoFixRouteBoss(); + core.plugin.towerBoss.autoFixRouteBoss(); core.showStatusBar(); ani.time(750).apply('rect', 0); chase.end(); diff --git a/src/plugin/settings.ts b/src/plugin/settings.ts index b4d3209..147207e 100644 --- a/src/plugin/settings.ts +++ b/src/plugin/settings.ts @@ -105,16 +105,17 @@ function resetFlag() { } export async function triggerFullscreen() { + const { maxGameScale } = core.plugin.utils; if (document.fullscreenElement) { await document.exitFullscreen(); requestAnimationFrame(() => { - core.maxGameScale(1); + maxGameScale(1); }); fullscreen.value = false; } else { await document.body.requestFullscreen(); requestAnimationFrame(() => { - core.maxGameScale(); + maxGameScale(); }); fullscreen.value = true; } diff --git a/src/types/data.d.ts b/src/types/data.d.ts index c0df9bb..2e0410c 100644 --- a/src/types/data.d.ts +++ b/src/types/data.d.ts @@ -68,6 +68,8 @@ interface MainData { * 图片切分信息 */ readonly splitImages: SplitImageData; + + readonly plugin: string[]; } interface FirstData { diff --git a/src/types/plugin.d.ts b/src/types/plugin.d.ts index 37a9945..08e28ca 100644 --- a/src/types/plugin.d.ts +++ b/src/types/plugin.d.ts @@ -18,10 +18,20 @@ interface PluginDeclaration extends PluginUtils, PluginUis, PluginUse, - SkillTree, MiniMap, - HeroRealStatus, PluginAchievement { + /** + * + */ + utils: GamePluginUtils; + loopMap: GamePluginLoopMap; + towerBoss: GamePluginBoss; + skillTree: GamePluginSkillTree; + study: GamePluginStudy; + hero: GamePluginHeroRealStatus; + + skills: Record; + /** * 添加函数 例:添加弹出文字,像这个就可以使用core.addPop或core.plugin.addPop调用 * @param px 弹出的横坐标 @@ -83,6 +93,45 @@ interface PluginDeclaration resetFlagSettings(): void; } +interface GamePluginUtils { + /** + * 判定一个值是否不是undefined或null + * @param value 要判断的值 + */ + has(value: T): value is NonNullable; + + /** + * 滑动数组 + * @param arr 数组 + * @param delta 偏移量,正数表示向右滑动,负数表示向左滑动 + */ + slide(arr: T[], delta: number): T[]; + + /** + * 获取方向的反方向 + * @param dir 方向 + */ + backDir(dir: Dir): Dir; + + /** + * 最大化游戏缩放 + * @param n 最大缩放再少多少个缩放 + */ + maxGameScale(n?: number): void; +} + +interface GamePluginLoopMap { + checkLoopMap(): void; +} + +interface GamePluginBoss { + /** + * 自动修复特殊boss战的录像 + * @param isStart 是否要开始修剪录像 + */ + autoFixRouteBoss(isStart?: boolean); +} + interface PluginUtils { /** * 判定一个值是否不是undefined或null @@ -116,37 +165,6 @@ interface PluginUtils { * @param index 追逐战索引 */ startChase(index: number): Promise; - - /** - * 自动修复特殊boss战的录像 - * @param isStart 是否要开始修剪录像 - */ - autoFixRouteBoss(isStart?: boolean); - - /** - * 滑动数组 - * @param arr 数组 - * @param delta 偏移量,正数表示向右滑动,负数表示向左滑动 - */ - slide(arr: T[], delta: number): T[]; - - /** - * 获取方向的反方向 - * @param dir 方向 - */ - backDir(dir: Dir): Dir; - - /** - * 判断一个值是否不是undefined和null - * @param value 要判断的值 - */ - has(value: T): value is NonNullable; - - /** - * 最大化游戏缩放 - * @param n 最大缩放再少多少个缩放 - */ - maxGameScale(n?: number): void; } interface PluginUis { @@ -277,9 +295,7 @@ interface PluginUse { useUp(ele: HTMLElement, fn: DragFn): void; } -interface SkillTree { - skills: Record; - +interface GamePluginSkillTree { /** * 获取技能等级 * @param skill 技能索引 @@ -323,7 +339,7 @@ interface MiniMap { splitArea(): void; } -interface Study { +interface GamePluginStudy { /** * 学习一个怪物技能 * @param enemy 被学习的怪物 @@ -332,7 +348,7 @@ interface Study { studySkill(enemy: Enemy, num: number): void; } -interface HeroRealStatus { +interface GamePluginHeroRealStatus { /** * 获取勇士在某一点的属性 * @param name 要获取的勇士属性 diff --git a/src/ui/settings.vue b/src/ui/settings.vue index cbed829..8d2e7c7 100644 --- a/src/ui/settings.vue +++ b/src/ui/settings.vue @@ -77,7 +77,7 @@ 展示已学习技能:   {{ showStudied ? 'ON' : 'OFF' diff --git a/src/ui/skill.vue b/src/ui/skill.vue index 90f73ae..f060fb5 100644 --- a/src/ui/skill.vue +++ b/src/ui/skill.vue @@ -51,7 +51,10 @@ const content = computed(() => { } else return v; }) .join('') - .replace(/level:(\d+)/g, 'core.getSkillLevel($1)') + + .replace( + /level:(\d+)/g, + 'core.plugin.skillTree.getSkillLevel($1)' + ) + '`' ); }); diff --git a/src/ui/skillTree.vue b/src/ui/skillTree.vue index 5c86c71..3d1fbac 100644 --- a/src/ui/skillTree.vue +++ b/src/ui/skillTree.vue @@ -108,7 +108,7 @@ const mdef = ref(core.status.hero.mdef); const skill = computed(() => { update.value; - return core.getSkillFromIndex(selected.value); + return core.plugin.skillTree.getSkillFromIndex(selected.value); }); const skills = computed(() => { @@ -119,8 +119,9 @@ const desc = computed(() => { return eval( '`' + splitText(skill.value.desc).replace(/level(:\d+)?/g, (str, $1) => { - if ($1) return `core.getSkillLevel(${$1})`; - else return `core.getSkillLevel(${skill.value.index})`; + if ($1) return `core.plugin.skillTree.getSkillLevel(${$1})`; + else + return `core.plugin.skillTree.getSkillLevel(${skill.value.index})`; }) + '`' ); @@ -134,9 +135,10 @@ const effect = computed(() => { skill.value.effect .join('') .replace(/level(:\d+)?/g, (str, $1) => { - if ($1) return `(core.getSkillLevel(${$1}) + ${v})`; + if ($1) + return `(core.plugin.skillTree.getSkillLevel(${$1}) + ${v})`; else - return `(core.getSkillLevel(${skill.value.index}) + ${v})`; + return `(core.plugin.skillTree.getSkillLevel(${skill.value.index}) + ${v})`; }) + '`' ); @@ -154,20 +156,20 @@ const dict = computed(() => { const front = computed(() => { return skill.value.front.map(v => { - return `${core.getSkillLevel(v[0]) >= v[1] ? 'a' : 'b'}${v[1]}级 ${ - skills.value[dict.value[v[0]]].title - }`; + return `${ + core.plugin.skillTree.getSkillLevel(v[0]) >= v[1] ? 'a' : 'b' + }${v[1]}级 ${skills.value[dict.value[v[0]]].title}`; }); }); const consume = computed(() => { update.value; - return core.getSkillConsume(selected.value); + return core.plugin.skillTree.getSkillConsume(selected.value); }); const level = computed(() => { update.value; - return core.getSkillLevel(selected.value); + return core.plugin.skillTree.getSkillLevel(selected.value); }); function exit() { @@ -195,9 +197,9 @@ function draw() { ctx.lineTo( ...(s.loc.map(v => (v * 2 - 1) * per + per / 2) as LocArr) ); - if (core.getSkillLevel(s.index) < v.front[i][1]) + if (core.plugin.skillTree.getSkillLevel(s.index) < v.front[i][1]) ctx.strokeStyle = '#aaa'; - else if (core.getSkillLevel(s.index) === s.max) + else if (core.plugin.skillTree.getSkillLevel(s.index) === s.max) ctx.strokeStyle = '#ff0'; else ctx.strokeStyle = '#0f8'; ctx.lineWidth = devicePixelRatio; @@ -206,7 +208,7 @@ function draw() { }); skills.value.forEach(v => { const [x, y] = v.loc.map(v => v * 2 - 1); - const level = core.getSkillLevel(v.index); + const level = core.plugin.skillTree.getSkillLevel(v.index); // 技能图标 ctx.save(); ctx.lineWidth = per * 0.06; @@ -247,7 +249,7 @@ function click(e: MouseEvent) { } function upgrade(index: number) { - const success = core.upgradeSkill(index); + const success = core.plugin.skillTree.upgradeSkill(index); if (!success) tip('error', '升级失败!'); else { tip('success', '升级成功!'); diff --git a/src/ui/statusBar.vue b/src/ui/statusBar.vue index 1b52657..00afd65 100644 --- a/src/ui/statusBar.vue +++ b/src/ui/statusBar.vue @@ -149,7 +149,7 @@ const skill = ref('无'); const up = ref(0); const spring = ref(); const skillOpened = ref(core.getFlag('chapter', 0) > 0); -const studyOpened = ref(core.getSkillLevel(11) > 0); +const studyOpened = ref(core.plugin.skillTree.getSkillLevel(11) > 0); /** * 要展示的勇士属性 */ @@ -191,7 +191,7 @@ function update() { spring.value = 50 - flags.springCount; } skillOpened.value = core.getFlag('chapter', 0) > 0; - studyOpened.value = core.getSkillLevel(11) > 0; + studyOpened.value = core.plugin.skillTree.getSkillLevel(11) > 0; } function openSkillTree() {