From 8f3c9b514f7782a882d14a9c693acd86027303dd Mon Sep 17 00:00:00 2001 From: strawberry42271 <2806566736@qq.com> Date: Thu, 30 Jan 2025 08:05:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9F=B3=E9=A2=91=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=92=8C=E6=A5=BC=E4=BC=A0=E7=9A=84=E5=B0=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _server/CodeMirror/defs.js | 5218 ++++++++++++----------- _server/MotaAction.g4 | 4 +- _server/editor_blocklyconfig.js | 10 +- project/animates/hand.animate | 2 +- project/data.js | 3 +- project/functions.js | 2874 +++++++------ project/items.js | 2 +- project/maps.js | 976 +++-- project/plugins.js | 6956 +++++++++++++++---------------- 9 files changed, 8629 insertions(+), 7416 deletions(-) diff --git a/_server/CodeMirror/defs.js b/_server/CodeMirror/defs.js index 1c16b25..ee517af 100644 --- a/_server/CodeMirror/defs.js +++ b/_server/CodeMirror/defs.js @@ -1,4130 +1,4526 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ { "!name": "browser", - "Node": { + Node: { "!type": "fn()", - "prototype": { - "nextSibling": { + prototype: { + nextSibling: { "!type": "+Element", - "!doc": "返回紧接其父节点的childNodes列表中指定节点之后的节点;如果指定节点是该列表中的最后一个节点,则返回null.", + "!doc": + "返回紧接其父节点的childNodes列表中指定节点之后的节点;如果指定节点是该列表中的最后一个节点,则返回null.", }, - "previousSibling": { + previousSibling: { "!type": "+Element", - "!doc": "返回紧接其父节点的childNodes列表中指定节点之前的节点,如果指定节点是该列表中的第一个节点,则返回null.", + "!doc": + "返回紧接其父节点的childNodes列表中指定节点之前的节点,如果指定节点是该列表中的第一个节点,则返回null.", }, - "lastChild": { + lastChild: { "!type": "+Element", - "!doc": "返回节点的最后一个孩子." + "!doc": "返回节点的最后一个孩子.", }, - "firstChild": { + firstChild: { "!type": "+Element", - "!doc": "返回树中该节点的第一个子节点;如果该节点为无子节点,则返回null.如果该节点是Document,则返回其直接子节点列表中的第一个节点.", + "!doc": + "返回树中该节点的第一个子节点;如果该节点为无子节点,则返回null.如果该节点是Document,则返回其直接子节点列表中的第一个节点.", }, - "childNodes": { + childNodes: { "!type": "+NodeList", - "!doc": "返回给定元素的子节点的集合." + "!doc": "返回给定元素的子节点的集合.", }, - "parentNode": { + parentNode: { "!type": "+Element", - "!doc": "返回DOM树中指定节点的父级." + "!doc": "返回DOM树中指定节点的父级.", }, - "tagName": { + tagName: { "!type": "string", - "!doc": "将当前节点的名称作为字符串返回." + "!doc": "将当前节点的名称作为字符串返回.", }, - "insertBefore": { + insertBefore: { "!type": "fn(newElt: +Element, before: +Element) -> +Element", "!doc": "将指定的节点插入到参考元素之前,作为当前节点的子级.", }, - "removeChild": { + removeChild: { "!type": "fn(oldNode: +Element) -> +Element", "!doc": "从DOM中删除一个子节点.返回已删除的节点.", }, - "appendChild": { + appendChild: { "!type": "fn(newNode: +Element) -> +Element", - "!doc": "将一个节点添加到指定父节点的子节点列表的末尾.如果该节点已经存在,则将其从当前父节点中删除,然后添加到新的父节点中.", + "!doc": + "将一个节点添加到指定父节点的子节点列表的末尾.如果该节点已经存在,则将其从当前父节点中删除,然后添加到新的父节点中.", }, - "cloneNode": { + cloneNode: { "!type": "fn(deep: bool) -> +Element", - "!doc": "返回在其上调用此方法的节点的副本." + "!doc": "返回在其上调用此方法的节点的副本.", }, - "addEventListener": { + addEventListener: { "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)", - "!doc": "在单个目标上注册单个事件侦听器.事件目标可以是文档中的单个元素,文档本身,窗口或XMLHttpRequest.", + "!doc": + "在单个目标上注册单个事件侦听器.事件目标可以是文档中的单个元素,文档本身,窗口或XMLHttpRequest.", }, - "removeEventListener": { + removeEventListener: { "!type": "fn(type: string, listener: fn(), capture: bool)", "!doc": "允许从事件目标中删除事件侦听器.", }, - "innerText": { + innerText: { "!type": "string", - "!doc": "获取或设置节点及其后代的文本内容." - } - }, - "!doc": "节点是一个接口,许多DOM类型都从该接口继承,并允许类似地对待(或测试)这些各种类型.", - }, - "Element": { - "!type": "fn()", - "prototype": { - "!proto": "Node.prototype", - "getAttribute": { - "!type": "fn(name: string) -> string", - "!doc": "返回指定元素上的命名属性的值.如果命名属性不存在,则返回的值将为null或\" \"(空字符串).", + "!doc": "获取或设置节点及其后代的文本内容.", }, - "setAttribute": { + }, + "!doc": + "节点是一个接口,许多DOM类型都从该接口继承,并允许类似地对待(或测试)这些各种类型.", + }, + Element: { + "!type": "fn()", + prototype: { + "!proto": "Node.prototype", + getAttribute: { + "!type": "fn(name: string) -> string", + "!doc": + '返回指定元素上的命名属性的值.如果命名属性不存在,则返回的值将为null或" "(空字符串).', + }, + setAttribute: { "!type": "fn(name: string, value: string)", "!doc": "在指定元素上添加新属性或更改现有属性的值.", }, - "removeAttribute": { + removeAttribute: { "!type": "fn(name: string)", "!doc": "从指定元素中删除属性.", }, - "getElementsByTagName": { + getElementsByTagName: { "!type": "fn(tagName: string) -> +NodeList", - "!doc": "返回具有给定标签名的元素列表.搜索指定元素下面的子树,不包括元素本身.返回的列表是活动的,这意味着它将自动使用DOM树进行更新.因此,无需使用相同的元素和参数多次调用element.getElementsByTagName." + "!doc": + "返回具有给定标签名的元素列表.搜索指定元素下面的子树,不包括元素本身.返回的列表是活动的,这意味着它将自动使用DOM树进行更新.因此,无需使用相同的元素和参数多次调用element.getElementsByTagName.", }, - "getElementsByClassName": { + getElementsByClassName: { "!type": "fn(name: string) -> +NodeList", - "!doc": "返回具有所有给定类名称的一组元素.在文档对象上调用时,将搜索整个文档,包括根节点.您还可以在任何元素上调用getElementsByClassName;它将仅返回元素,它们是具有给定类名的指定根元素的后代." + "!doc": + "返回具有所有给定类名称的一组元素.在文档对象上调用时,将搜索整个文档,包括根节点.您还可以在任何元素上调用getElementsByClassName;它将仅返回元素,它们是具有给定类名的指定根元素的后代.", }, - "children": { + children: { "!type": "+HTMLCollection", - "!doc": "返回给定元素的子元素的集合." + "!doc": "返回给定元素的子元素的集合.", }, - "className": { + 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属性的对象." + 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": { + classList: { "!type": "+DOMTokenList", - "!doc": "返回元素的class属性的标记列表." + "!doc": "返回元素的class属性的标记列表.", }, - "title": { + 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.\"," + "!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": { + width: { "!type": "number", - "!doc": "返回元素的布局宽度." + "!doc": "返回元素的布局宽度.", }, - "height": { + height: { "!type": "number", - "!doc": "元素相对于元素的offsetParent的高度." + "!doc": "元素相对于元素的offsetParent的高度.", }, - "getContext": { + getContext: { "!type": "fn(id: string) -> CanvasRenderingContext2D", - "!doc": " DOM画布元素公开了HTMLCanvasElement接口,该接口提供了用于操纵画布元素的布局和表示的属性和方法.HTMLCanvasElement接口继承了元素对象接口的属性和方法.", + "!doc": + " DOM画布元素公开了HTMLCanvasElement接口,该接口提供了用于操纵画布元素的布局和表示的属性和方法.HTMLCanvasElement接口继承了元素对象接口的属性和方法.", }, - "innerHTML": { + innerHTML: { "!type": "string", "!doc": "设置或获取描述元素后代的HTML语法.", - } + }, }, "!doc": "表示HTML或XML文档中的元素.", }, - "Document": { + Document: { "!type": "fn()", - "prototype": { + prototype: { "!proto": "Node.prototype", - "height": { + height: { "!type": "number", "!doc": "返回当前文档的元素的高度.", }, - "width": { + width: { "!type": "number", "!doc": "以像素为单位返回当前文档的元素的宽度.", }, - "body": { + body: { "!type": "+Element", "!doc": "返回当前文档的或节点.", }, - "cookie": { + cookie: { "!type": "string", "!doc": "获取并设置与当前文档关联的cookie.", }, - "URL": "string", - "title": { + URL: "string", + title: { "!type": "string", - "!doc": "获取或设置文档的标题." + "!doc": "获取或设置文档的标题.", }, - "getElementById": { + getElementById: { "!type": "fn(id: string) -> +Element", - "!doc": "通过元素ID返回对该元素的引用." + "!doc": "通过元素ID返回对该元素的引用.", }, - "getElementsByTagName": { + getElementsByTagName: { "!type": "fn(tagName: string) -> +NodeList", - "!doc": "返回具有给定标签名称的元素的NodeList.将搜索整个文档,包括根节点.返回的NodeList处于活动状态,这意味着它会自动更新自身以与DOM树保持同步,而无需再次调用document.getElementsByTagName." + "!doc": + "返回具有给定标签名称的元素的NodeList.将搜索整个文档,包括根节点.返回的NodeList处于活动状态,这意味着它会自动更新自身以与DOM树保持同步,而无需再次调用document.getElementsByTagName.", }, - "getElementsByName": { + getElementsByName: { "!type": "fn(name: string) -> +HTMLCollection", "!doc": "返回HTML文档中具有给定名称的元素列表.", }, - "getElementsByClassName": "Element.prototype.getElementsByClassName" + getElementsByClassName: "Element.prototype.getElementsByClassName", }, - "!doc": "浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素)." + "!doc": + "浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和
之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素).", }, - "document": { + document: { "!type": "+Document", - "!doc": "浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和
之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素)." + "!doc": + "浏览器中加载的每个网页都有其自己的文档对象.此对象用作网页内容(DOM树,包括诸如和
之类的元素)的入口点,并提供文档的全局功能(例如获取页面的URL和在文档中创建新元素).", }, - "Event": { + Event: { "!type": "fn()", - "prototype": { - "stopPropagation": { + prototype: { + stopPropagation: { "!type": "fn()", - "!doc": "防止当前事件进一步传播." + "!doc": "防止当前事件进一步传播.", }, - "preventDefault": { + preventDefault: { "!type": "fn()", - "!doc": "如果可以取消事件,则取消该事件,而不停止事件的进一步传播." + "!doc": "如果可以取消事件,则取消该事件,而不停止事件的进一步传播.", }, - "stopImmediatePropagation": { + stopImmediatePropagation: { "!type": "fn()", - "!doc": "防止同一事件的其他侦听器被调用." + "!doc": "防止同一事件的其他侦听器被调用.", }, - "type": { + type: { "!type": "string", - "!doc": "返回包含事件类型的字符串." + "!doc": "返回包含事件类型的字符串.", }, - "target": { + target: { "!type": "+Element", - "!doc": " EventTarget是由对象实现的DOM接口,这些对象可以接收DOM事件并具有侦听器.最常见的EventTarget是DOM元素,尽管其他对象也可以是EventTarget,例如文档,窗口,XMLHttpRequest,和别的." + "!doc": + " EventTarget是由对象实现的DOM接口,这些对象可以接收DOM事件并具有侦听器.最常见的EventTarget是DOM元素,尽管其他对象也可以是EventTarget,例如文档,窗口,XMLHttpRequest,和别的.", }, - "clientX": { + clientX: { "!type": "number", - "!doc": "返回事件发生的应用程序客户区域内的水平坐标(与页面内的坐标相反).例如,单击客户区域左上角将始终显示clientX值为0的鼠标事件,无论页面是否水平滚动." + "!doc": + "返回事件发生的应用程序客户区域内的水平坐标(与页面内的坐标相反).例如,单击客户区域左上角将始终显示clientX值为0的鼠标事件,无论页面是否水平滚动.", }, - "clientY": { + clientY: { "!type": "number", - "!doc": "返回事件发生在应用程序客户区中的垂直坐标(与页面中的坐标相反).例如,单击客户区左上角将始终显示不管页面是否垂直滚动,clientY值为0的鼠标事件." + "!doc": + "返回事件发生在应用程序客户区中的垂直坐标(与页面中的坐标相反).例如,单击客户区左上角将始终显示不管页面是否垂直滚动,clientY值为0的鼠标事件.", }, - "keyCode": { + keyCode: { "!type": "number", - "!doc": "返回按键事件中的非字符键或任何其他类型的键盘事件中的任何键的Unicode值.", + "!doc": + "返回按键事件中的非字符键或任何其他类型的键盘事件中的任何键的Unicode值.", }, - "charCode": { + charCode: { "!type": "number", - "!doc": "返回在按键事件期间按下的字符键的Unicode值." + "!doc": "返回在按键事件期间按下的字符键的Unicode值.", }, - "which": { + which: { "!type": "number", - "!doc": "返回所按下键的数字keyCode或所按下字母数字键的字符代码(charCode)." + "!doc": + "返回所按下键的数字keyCode或所按下字母数字键的字符代码(charCode).", }, - "button": { + button: { "!type": "number", - "!doc": "指示导致事件的鼠标按钮." + "!doc": "指示导致事件的鼠标按钮.", }, - "shiftKey": { + shiftKey: { "!type": "bool", "!doc": "指示事件触发时是否按下SHIFT键.", }, - "ctrlKey": { + ctrlKey: { "!type": "bool", "!doc": "指示事件触发时是否按下了CTRL键.", }, - "altKey": { + altKey: { "!type": "bool", "!doc": "指示事件触发时是否按下ALT键.", - } - } + }, + }, }, - "Storage": { - "length": { + Storage: { + length: { "!type": "number", - "!doc": "存储接口的length只读属性返回一个整数,该整数表示存储在存储对象中的数据项的数量.", + "!doc": + "存储接口的length只读属性返回一个整数,该整数表示存储在存储对象中的数据项的数量.", }, - "setItem": { + setItem: { "!type": "fn(name: string, value: string)", - "!doc": "存储接口的setItem()方法在传递键名称和值时,会将该键添加到存储中,或者更新该键的值(如果已存在).", + "!doc": + "存储接口的setItem()方法在传递键名称和值时,会将该键添加到存储中,或者更新该键的值(如果已存在).", }, - "getItem": { + getItem: { "!type": "fn(name: string) -> string", "!doc": "存储接口的getItem()方法在传递键名时将返回该键的值.", }, - "key": { + key: { "!type": "fn(index: number) -> string", - "!doc": "存储接口的key()方法传递数字n时,返回存储中第n个键的名称.键的顺序是用户代理定义的,因此您不应依赖它." + "!doc": + "存储接口的key()方法传递数字n时,返回存储中第n个键的名称.键的顺序是用户代理定义的,因此您不应依赖它.", }, - "removeItem": { + removeItem: { "!type": "fn(key: string)", "!doc": "存储接口的removeItem()方法在传递了键名后,将从存储中删除该键.", }, - "clear": { + clear: { "!type": "fn()", - "!doc": "存储接口的clear()方法在被调用时将从存储中清空所有键." - } + "!doc": "存储接口的clear()方法在被调用时将从存储中清空所有键.", + }, }, - "localStorage": { + localStorage: { "!type": "Storage", - "!doc": " localStorage属性允许您访问本地存储对象.localStorage与sessionStorage类似.唯一的区别是,虽然存储在localStorage中的数据没有到期时间,但是浏览会话时存储在sessionStorage中的数据将被清除.结束-也就是说,当浏览器关闭时.\ n \ n请注意,存储在localStorage或sessionStorage中的数据特定于页面协议." + "!doc": + " localStorage属性允许您访问本地存储对象.localStorage与sessionStorage类似.唯一的区别是,虽然存储在localStorage中的数据没有到期时间,但是浏览会话时存储在sessionStorage中的数据将被清除.结束-也就是说,当浏览器关闭时. n n请注意,存储在localStorage或sessionStorage中的数据特定于页面协议.", }, - "console": { - "assert": { + console: { + assert: { "!type": "fn(assertion: bool, text: string)", "!doc": "如果断言为false,则将错误消息写入控制台.", }, - "error": { + error: { "!type": "fn(...msg: ?)", "!doc": "将错误消息输出到Web控制台.", }, - "info": { + info: { "!type": "fn(...msg: ?)", "!doc": "将参考消息输出到Web控制台.", }, - "log": { + log: { "!type": "fn(...msg: ?)", "!doc": "将消息输出到Web控制台.", }, - "time": { + time: { "!type": "fn(label: string)", "!doc": "启动计时器,您可以使用该计时器来跟踪操作需要多长时间.", }, - "timeEnd": { + timeEnd: { "!type": "fn(label: string)", "!doc": "停止以前通过调用console.time()启动的计时器.", }, - "trace": { + trace: { "!type": "fn()", "!doc": "将堆栈跟踪输出到Web控制台.", }, - "warn": { + warn: { "!type": "fn(...msg: ?)", "!doc": "将警告消息输出到Web控制台.", }, - "!doc": "控制台对象提供对浏览器调试控制台的访问.其工作方式的细节因浏览器而异,但实际上提供了一组事实上的功能.", + "!doc": + "控制台对象提供对浏览器调试控制台的访问.其工作方式的细节因浏览器而异,但实际上提供了一组事实上的功能.", }, - "window": { + window: { "!type": "", "!doc": "窗口对象代表一个包含DOM文档的窗口.", }, - "self": { + self: { "!type": "", "!doc": "将对象引用返回到窗口对象.", }, - "devicePixelRatio": "number", - "requestAnimationFrame": { + devicePixelRatio: "number", + requestAnimationFrame: { "!type": "fn(callback: fn(timestamp: number)) -> number", - "!doc": " Window.requestAnimationFrame()方法告诉浏览器您希望执行动画,并请求浏览器在下一次重绘之前调用指定的函数来更新动画.该方法将回调作为参数在重新粉刷之前被调用." + "!doc": + " Window.requestAnimationFrame()方法告诉浏览器您希望执行动画,并请求浏览器在下一次重绘之前调用指定的函数来更新动画.该方法将回调作为参数在重新粉刷之前被调用.", }, - "cancelAnimationFrame": { + cancelAnimationFrame: { "!type": "fn(number)n", "!doc": "取消先前安排的动画帧请求.", }, - "alert": { + alert: { "!type": "fn(message: string)", - "!doc": "显示具有指定内容和确定按钮的警报对话框." + "!doc": "显示具有指定内容和确定按钮的警报对话框.", }, - "confirm": { + confirm: { "!type": "fn(message: string) -> bool", "!doc": "显示带有消息和两个按钮(确定和取消)的模式对话框.", }, - "prompt": { + prompt: { "!type": "fn(message: string, value: string) -> string", "!doc": "显示一个对话框,提示用户输入一些文本.", }, - "setTimeout": { + setTimeout: { "!type": "fn(f: fn(), ms: number) -> number", - "!doc": "在指定的延迟后调用函数或执行代码段." + "!doc": "在指定的延迟后调用函数或执行代码段.", }, - "clearTimeout": { + clearTimeout: { "!type": "fn(timeout: number)", "!doc": "清除window.setTimeout()设置的延迟.", }, - "setInterval": { + setInterval: { "!type": "fn(f: fn(), ms: number) -> number", - "!doc": "反复调用一个函数或执行代码段,每次调用该函数之间有固定的时间延迟.", + "!doc": + "反复调用一个函数或执行代码段,每次调用该函数之间有固定的时间延迟.", }, - "clearInterval": { + clearInterval: { "!type": "fn(interval: number)", "!doc": "取消使用setInterval设置的重复操作.", }, - "atob": { + atob: { "!type": "fn(encoded: string) -> string", - "!doc": "解码使用base-64编码编码的数据字符串." + "!doc": "解码使用base-64编码编码的数据字符串.", }, - "btoa": { + btoa: { "!type": "fn(data: string) -> string", "!doc": "从一串二进制数据创建一个base-64编码的ASCII字符串.", }, - "getComputedStyle": { + 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)" - } + 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": { + Infinity: { "!type": "number", - "!doc": "代表无穷大的数值." + "!doc": "代表无穷大的数值.", }, - "undefined": { + undefined: { "!type": "?", "!doc": "该值未定义.", }, - "NaN": { + NaN: { "!type": "number", - "!doc": "代表非数字的值." + "!doc": "代表非数字的值.", }, - "Object": { + Object: { "!type": "fn()", - "create": { + create: { "!type": "fn(proto: ?) -> !custom:Object_create", "!doc": "使用指定的原型对象和属性创建一个新对象.", }, - "defineProperty": { - "!type": "fn(obj: ?, prop: string, desc: propertyDescriptor) -> !custom:Object_defineProperty", - "!doc": "直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象.如果想了解如何将Object.defineProperty方法与类似二进制标志的语法一起使用,请参阅本文." + defineProperty: { + "!type": + "fn(obj: ?, prop: string, desc: propertyDescriptor) -> !custom:Object_defineProperty", + "!doc": + "直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象.如果想了解如何将Object.defineProperty方法与类似二进制标志的语法一起使用,请参阅本文.", }, - "keys": { + keys: { "!type": "fn(obj: ?) -> [string]", - "!doc": "返回一个给定对象自己的可枚举属性的数组,其顺序与for-in循环所提供的顺序相同(不同之处在于for-in循环也枚举了原型链中的属性). " + "!doc": + "返回一个给定对象自己的可枚举属性的数组,其顺序与for-in循环所提供的顺序相同(不同之处在于for-in循环也枚举了原型链中的属性). ", }, - "assign": { + assign: { "!type": "fn(target: ?, source: ?, source?: ?) -> !0", "!effects": ["copy !1 !0", "copy !2 !0", "copy !3 !0"], - "!doc": " Object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象.它将返回目标对象.,", + "!doc": + " Object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象.它将返回目标对象.,", }, - "prototype": { + prototype: { "!stdProto": "Object", - "toString": { + toString: { "!type": "fn() -> string", - "!doc": "返回表示对象的字符串." + "!doc": "返回表示对象的字符串.", }, - "hasOwnProperty": { + hasOwnProperty: { "!type": "fn(prop: string) -> bool", "!doc": "返回一个布尔值,指示对象是否具有指定的属性.", - } + }, }, }, - "Function": { + Function: { "!type": "fn(body: string) -> fn()", - "prototype": { + prototype: { "!stdProto": "Function", - "apply": { + apply: { "!type": "fn(this: ?, args: [?])", - "!effects": [ - "call and return !this this=!0 !1. !1. !1." - ], - "!doc": "调用具有给定值的函数,并以数组(或类似对象的数组)形式提供参数.", + "!effects": ["call and return !this this=!0 !1. !1. !1."], + "!doc": + "调用具有给定值的函数,并以数组(或类似对象的数组)形式提供参数.", }, - "call": { + call: { "!type": "fn(this: ?, args?: ?) -> !this.!ret", - "!effects": [ - "call and return !this this=!0 !1 !2 !3 !4" - ], + "!effects": ["call and return !this this=!0 !1 !2 !3 !4"], "!doc": "调用具有给定值和单独提供的参数的函数.", }, - "bind": { + bind: { "!type": "fn(this: ?, args?: ?) -> !custom:Function_bind", - "!doc": "创建一个新函数,该函数在被调用时将其this关键字设置为提供的值,并在调用新函数时提供给定的参数序列.", + "!doc": + "创建一个新函数,该函数在被调用时将其this关键字设置为提供的值,并在调用新函数时提供给定的参数序列.", }, - "prototype": "?" + prototype: "?", }, }, - "Array": { + Array: { "!type": "fn(size: number) -> !custom:Array_ctor", - "isArray": { + 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实例.,", + from: { + "!type": + "fn(arrayLike: ?, mapFn?: fn(elt: ?, i: number) -> ?, thisArg?: ?) -> [!0.]", + "!effects": ["call !1 this=!2 !0. number"], + "!doc": + " Array.from()方法从类似数组或可迭代的对象创建一个新的Array实例.,", }, - "of": { + of: { "!type": "fn(elementN: ?) -> [!0]", - "!doc": " Array.of()方法创建一个新的Array实例,该实例具有可变数量的参数,而不考虑参数的数量或类型.,", + "!doc": + " Array.of()方法创建一个新的Array实例,该实例具有可变数量的参数,而不考虑参数的数量或类型.,", }, - "prototype": { + prototype: { "!stdProto": "Array", - "length": { + length: { "!type": "number", "!doc": "一个无符号的32位整数,指定数组中的元素数.", }, - "concat": { + concat: { "!type": "fn(other: [?]) -> !this", "!doc": "返回一个新数组,该数组由该数组与其他数组和/或值组成.", }, - "join": { + join: { "!type": "fn(separator?: string) -> string", - "!doc": "将数组的所有元素连接到字符串中." + "!doc": "将数组的所有元素连接到字符串中.", }, - "splice": { + splice: { "!type": "fn(pos: number, amount: number, newelt?: ?) -> [?]", "!doc": "更改数组的内容,在删除旧元素的同时添加新元素.", }, - "pop": { + pop: { "!type": "fn() -> !this.", "!doc": "从数组中删除最后一个元素并返回该元素.", }, - "push": { + push: { "!type": "fn(newelt: ?) -> number", - "!effects": [ - "propagate !0 !this." - ], + "!effects": ["propagate !0 !this."], "!doc": "通过添加给定元素并返回数组的新长度来更改数组.", }, - "shift": { + shift: { "!type": "fn() -> !this.", "!doc": "从数组中删除第一个元素并返回该元素.此方法更改数组的长度.", }, - "unshift": { + unshift: { "!type": "fn(newelt: ?) -> number", - "!effects": [ - "propagate !0 !this." - ], + "!effects": ["propagate !0 !this."], "!doc": "将一个或多个元素添加到数组的开头,并返回数组的新长度.", }, - "slice": { + slice: { "!type": "fn(from?: number, to?: number) -> !this", - "!doc": "返回数组一部分的浅表副本." + "!doc": "返回数组一部分的浅表副本.", }, - "reverse": { + reverse: { "!type": "fn()", - "!doc": "就地反转数组.第一个数组元素变为最后一个,而最后一个数组变为第一个.", + "!doc": + "就地反转数组.第一个数组元素变为最后一个,而最后一个数组变为第一个.", }, - "sort": { + sort: { "!type": "fn(compare?: fn(a: ?, b: ?) -> number)", - "!effects": [ - "call !0 !this. !this." - ], - "!doc": "将数组中的元素排序并返回数组." + "!effects": ["call !0 !this. !this."], + "!doc": "将数组中的元素排序并返回数组.", }, - "indexOf": { + indexOf: { "!type": "fn(elt: ?, from?: number) -> number", - "!doc": "返回在数组中可以找到给定元素的第一个索引;如果不存在,则返回-1.", + "!doc": + "返回在数组中可以找到给定元素的第一个索引;如果不存在,则返回-1.", }, - "lastIndexOf": { + lastIndexOf: { "!type": "fn(elt: ?, from?: number) -> number", - "!doc": "返回在数组中找到给定元素的最后一个索引,如果不存在则返回-1.从fromIndex开始向后搜索数组.", + "!doc": + "返回在数组中找到给定元素的最后一个索引,如果不存在则返回-1.从fromIndex开始向后搜索数组.", }, - "filter": { - "!type": "fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> !this", - "!effects": [ - "call !0 this=!1 !this. number !this" - ], + filter: { + "!type": + "fn(test: fn(elt: ?, i: number, array: +Array) -> bool, context?: ?) -> !this", + "!effects": ["call !0 this=!1 !this. number !this"], "!doc": "创建一个新数组,其中包含所有通过提供的功能实现的测试的元素.", }, - "forEach": { + forEach: { "!type": "fn(f: fn(elt: ?, i: number, array: +Array), context?: ?)", - "!effects": [ - "call !0 this=!1 !this. number !this" - ], - "!doc": "每个数组元素执行一次提供的功能." + "!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" - ], + 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": "对一个累加器和数组的每个值(从左到右)应用一个函数,以将其减小为单个值.", + reduce: { + "!type": + "fn(combine: fn(sum: ?, elt: ?, i: number, array: +Array) -> ?, init?: ?) -> !0.!ret", + "!effects": ["call !0 !1 !this. number !this"], + "!doc": + "对一个累加器和数组的每个值(从左到右)应用一个函数,以将其减小为单个值.", }, - "fill": { + fill: { "!type": "fn(value: ?, start?: number, end?: number) -> !this", - "!doc": " fill()方法使用静态值填充数组的所有元素,从开始索引到结束索引.,", + "!doc": + " fill()方法使用静态值填充数组的所有元素,从开始索引到结束索引.,", }, - "find": { - "!type": "fn(callback: fn(element: ?, index: number, array: [?]) -> bool, thisArg?: ?) -> !this.", + find: { + "!type": + "fn(callback: fn(element: ?, index: number, array: [?]) -> bool, thisArg?: ?) -> !this.", "!effects": ["call !0 this=!2 !this. number"], - "!doc": "如果数组中的元素满足提供的测试功能,则find()方法将在数组中返回一个值.否则,返回undefined.,", + "!doc": + "如果数组中的元素满足提供的测试功能,则find()方法将在数组中返回一个值.否则,返回undefined.,", }, - "findIndex": { - "!type": "fn(callback: fn(element: ?, index: number, array: [?]), thisArg?: ?) -> number", + findIndex: { + "!type": + "fn(callback: fn(element: ?, index: number, array: [?]), thisArg?: ?) -> number", "!effects": ["call !0 this=!2 !this. number"], - "!doc": "如果数组中的元素满足提供的测试功能,则findIndex()方法将返回数组中的索引.否则返回-1.,", + "!doc": + "如果数组中的元素满足提供的测试功能,则findIndex()方法将返回数组中的索引.否则返回-1.,", }, - "keys": { + keys: { "!type": "fn() -> +iter[:t=number]", - "!doc": " keys()方法返回一个新的数组迭代器,其中包含数组中每个索引的键.,", + "!doc": + " keys()方法返回一个新的数组迭代器,其中包含数组中每个索引的键.,", }, - "values": { + values: { "!type": "fn() -> +iter[:t=!this.]", - "!doc": " values()方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的值.,", + "!doc": + " values()方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的值.,", }, - "includes": { + includes: { "!type": "fn(value: ?, fromIndex?: number) -> bool", "!doc": "确定数组是否包含某个元素,并根据需要返回true或false.,", - } + }, }, }, - "String": { + String: { "!type": "fn(value: ?) -> string", - "prototype": { + prototype: { "!stdProto": "String", - "length": { + length: { "!type": "number", - "!doc": "表示字符串的长度." + "!doc": "表示字符串的长度.", }, "": "string", - "charAt": { + charAt: { "!type": "fn(i: number) -> string", "!doc": "从字符串中返回指定的字符.", }, - "charCodeAt": { + charCodeAt: { "!type": "fn(i: number) -> number", - "!doc": "返回给定索引处字符的数字Unicode值(Unicode代码点> 0x10000除外).", + "!doc": + "返回给定索引处字符的数字Unicode值(Unicode代码点> 0x10000除外).", }, - "indexOf": { + indexOf: { "!type": "fn(char: string, from?: number) -> number", - "!doc": "返回指定值首次出现的调用String对象中的索引,从fromIndex开始搜索,\ n如果未找到该值,则返回-1.", + "!doc": + "返回指定值首次出现的调用String对象中的索引,从fromIndex开始搜索, n如果未找到该值,则返回-1.", }, - "lastIndexOf": { + lastIndexOf: { "!type": "fn(char: string, from?: number) -> number", - "!doc": "返回指定值最后一次出现的调用String对象内的索引,如果未找到则返回-1.从fromIndex开始向后搜索调用字符串.", + "!doc": + "返回指定值最后一次出现的调用String对象内的索引,如果未找到则返回-1.从fromIndex开始向后搜索调用字符串.", }, - "substring": { + substring: { "!type": "fn(from: number, to?: number) -> string", - "!doc": "返回一个索引与另一个索引之间或字符串末尾的字符串子集.
from为起始位置,to为终止位置.", + "!doc": + "返回一个索引与另一个索引之间或字符串末尾的字符串子集.
from为起始位置,to为终止位置.", }, - "substr": { + substr: { "!type": "fn(from: number, length?: number) -> string", - "!doc": "以指定的字符数返回从指定位置开始的字符串中的字符.
from为起始位置,length为长度", + "!doc": + "以指定的字符数返回从指定位置开始的字符串中的字符.
from为起始位置,length为长度", }, - "slice": { + slice: { "!type": "fn(from: number, to?: number) -> string", "!doc": "提取字符串的一部分并返回新的字符串.", }, - "padStart": { + padStart: { "!type": "fn(targetLength: number, padString?: string) -> string", - "!doc": "用另一个字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.", + "!doc": + "用另一个字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.", }, - "padEnd": { + padEnd: { "!type": "fn(targetLength: number, padString?: string) -> string", - "!doc": "用给定的字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.", + "!doc": + "用给定的字符串(如果需要,重复)填充当前字符串,以使结果字符串达到给定的长度.", }, - "trim": { + trim: { "!type": "fn() -> string", "!doc": "从字符串的两端删除空格.", }, - "trimStart": { + trimStart: { "!type": "fn() -> string", "!doc": "从字符串的开头删除空格.", }, - "trimEnd": { + trimEnd: { "!type": "fn() -> string", "!doc": "从字符串末尾删除空格.", }, - "toUpperCase": { + toUpperCase: { "!type": "fn() -> string", - "!doc": "返回转换为大写的调用字符串值." + "!doc": "返回转换为大写的调用字符串值.", }, - "toLowerCase": { + toLowerCase: { "!type": "fn() -> string", - "!doc": "返回转换为小写的调用字符串值." + "!doc": "返回转换为小写的调用字符串值.", }, - "split": { + split: { "!type": "fn(pattern?: string|+RegExp, limit?: number) -> [string]", "!doc": "通过将字符串分成子字符串,将String对象拆分为字符串数组.", }, - "concat": { + concat: { "!type": "fn(other: string) -> string", - "!doc": "将两个或多个字符串的文本合并,并返回一个新字符串." + "!doc": "将两个或多个字符串的文本合并,并返回一个新字符串.", }, - "match": { + match: { "!type": "fn(pattern: +RegExp) -> [string]", "!doc": "用于将字符串与正则表达式匹配时用于检索匹配.", }, - "replace": { + replace: { "!type": "fn(pattern: string|+RegExp, replacement: string) -> string", - "!doc": "返回一个新字符串,该字符串的某个或所有匹配项都由替换项替换.该模式可以是字符串或RegExp,并且替换项可以是字符串或每个匹配项将调用的函数. " + "!doc": + "返回一个新字符串,该字符串的某个或所有匹配项都由替换项替换.该模式可以是字符串或RegExp,并且替换项可以是字符串或每个匹配项将调用的函数. ", }, - "endsWith": { + endsWith: { "!type": "fn(searchString: string, position?: number) -> bool", - "!doc": " endsWith()方法确定一个字符串是否以另一个字符串的字符结尾,并根据需要返回true或false.,", + "!doc": + " endsWith()方法确定一个字符串是否以另一个字符串的字符结尾,并根据需要返回true或false.,", }, - "startsWith": { + startsWith: { "!type": "fn(searchString: string, position?: number) -> bool", - "!doc": " startsWith()方法确定一个字符串是否以另一个字符串的字符开头,并根据需要返回true或false.,", - } + "!doc": + " startsWith()方法确定一个字符串是否以另一个字符串的字符开头,并根据需要返回true或false.,", + }, }, }, - "Number": { + Number: { "!type": "fn(value: ?) -> number", - "MAX_VALUE": { + MAX_VALUE: { "!type": "number", - "!doc": " JavaScript中可表示的最大数值." + "!doc": " JavaScript中可表示的最大数值.", }, - "MIN_VALUE": { + MIN_VALUE: { "!type": "number", - "!doc": " JavaScript中可表示的最小正数值." + "!doc": " JavaScript中可表示的最小正数值.", }, - "POSITIVE_INFINITY": { + POSITIVE_INFINITY: { "!type": "number", - "!doc": "代表正无穷大值的值." + "!doc": "代表正无穷大值的值.", }, - "NEGATIVE_INFINITY": { + NEGATIVE_INFINITY: { "!type": "number", - "!doc": "代表负无穷大值的值." + "!doc": "代表负无穷大值的值.", }, - "prototype": { + prototype: { "!stdProto": "Number", - "toString": { + toString: { "!type": "fn(radix?: number) -> string", - "!doc": "返回代表指定Number对象的字符串" + "!doc": "返回代表指定Number对象的字符串", }, - "toFixed": { + toFixed: { "!type": "fn(digits: number) -> string", - "!doc": "使用定点符号格式化数字" + "!doc": "使用定点符号格式化数字", }, - "toExponential": { + toExponential: { "!type": "fn(digits: number) -> string", - "!doc": "返回以指数表示形式表示Number对象的字符串" + "!doc": "返回以指数表示形式表示Number对象的字符串", }, - "toPrecision": { + toPrecision: { "!type": "fn(digits: number) -> string", "!doc": " toPrecision()方法返回一个字符串,该数字表示指定精度的数字.", - } + }, }, - "EPSILON": { + EPSILON: { "!type": "number", - "!doc": " Number.EPSILON属性表示一个数值与可以表示为Number的最小值之间的差异.,", + "!doc": + " Number.EPSILON属性表示一个数值与可以表示为Number的最小值之间的差异.,", }, - "MAX_SAFE_INTEGER": { + MAX_SAFE_INTEGER: { "!type": "number", - "!doc": " Number.MAX_SAFE_INTEGER常量表示JavaScript中的最大安全整数(2 ^ 53-1).,", + "!doc": + " Number.MAX_SAFE_INTEGER常量表示JavaScript中的最大安全整数(2 ^ 53-1).,", }, - "MIN_SAFE_INTEGER": { + MIN_SAFE_INTEGER: { "!type": "number", - "!doc": " Number.MIN_SAFE_INTEGER常量表示JavaScript(-(2 ^ 53-1))中的最小安全整数.,", + "!doc": + " Number.MIN_SAFE_INTEGER常量表示JavaScript(-(2 ^ 53-1))中的最小安全整数.,", }, - "isFinite": { + isFinite: { "!type": "fn(testValue: ?) -> bool", "!doc": " Number.isFinite()方法确定传递的值是否为有限值.,", }, - "isInteger": { + isInteger: { "!type": "fn(testValue: ?) -> bool", "!doc": " Number.isInteger()方法确定传递的值是否为整数.,", }, - "isNaN": { + isNaN: { "!type": "fn(testValue: ?) -> bool", - "!doc": " Number.isNaN()方法确定传递的值是否为NaN.原始全局isNaN()的更可靠的版本.,", + "!doc": + " Number.isNaN()方法确定传递的值是否为NaN.原始全局isNaN()的更可靠的版本.,", }, - "isSafeInteger": { + isSafeInteger: { "!type": "fn(testValue: ?) -> bool", - "!doc": " Number.isSafeInteger()方法确定所提供的值是否是一个安全整数的数字.安全整数是该数字的整数.", + "!doc": + " Number.isSafeInteger()方法确定所提供的值是否是一个安全整数的数字.安全整数是该数字的整数.", }, - "parseFloat": { + parseFloat: { "!type": "fn(string: string) -> number", "!doc": " Number.parseFloat()方法解析字符串参数并返回浮点数.,", }, - "parseInt": { + parseInt: { "!type": "fn(string: string, radix?: number) -> number", - "!doc": " Number.parseInt()方法解析字符串参数并返回指定基数或基数的整数.,", + "!doc": + " Number.parseInt()方法解析字符串参数并返回指定基数或基数的整数.,", }, }, - "Boolean": { + Boolean: { "!type": "fn(value: ?) -> bool", - "prototype": { - "!stdProto": "Boolean" + 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": { + 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": { + prototype: { "!stdProto": "RegExp", - "exec": { + exec: { "!type": "fn(input: string) -> [string]", "!doc": "搜索指定字符串中的匹配项.返回结果数组,或者为null.", }, - "test": { + test: { "!type": "fn(input: string) -> bool", "!doc": "执行正则表达式和指定字符串之间的匹配搜索.返回true或false.", - } + }, }, "!doc": "创建正则表达式对象以将文本与模式匹配.", }, - "parseInt": { + parseInt: { "!type": "fn(string: string, radix?: number) -> number", - "!doc": "解析字符串参数并返回指定基数或基数的整数." + "!doc": "解析字符串参数并返回指定基数或基数的整数.", }, - "parseFloat": { + parseFloat: { "!type": "fn(string: string) -> number", - "!doc": "解析字符串参数并返回浮点数." + "!doc": "解析字符串参数并返回浮点数.", }, - "isNaN": { + isNaN: { "!type": "fn(value: number) -> bool", - "!doc": "确定值是否为NaN.请注意,此函数已损坏.您可能对ECMAScript 6 Number.isNaN感兴趣.", + "!doc": + "确定值是否为NaN.请注意,此函数已损坏.您可能对ECMAScript 6 Number.isNaN感兴趣.", }, - "isFinite": { + isFinite: { "!type": "fn(value: number) -> bool", - "!doc": "确定传递的值是否为有限数字." + "!doc": "确定传递的值是否为有限数字.", }, - "eval": { + eval: { "!type": "fn(code: string) -> ?", - "!doc": "评估以字符串形式表示的JavaScript代码." + "!doc": "评估以字符串形式表示的JavaScript代码.", }, - "encodeURI": { + encodeURI: { "!type": "fn(uri: string) -> string", - "!doc": "通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)(对于字符而言将仅是四个转义序列由两个\"代理\"字符组成).", + "!doc": + '通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)(对于字符而言将仅是四个转义序列由两个"代理"字符组成).', }, - "encodeURIComponent": { + encodeURIComponent: { "!type": "fn(uri: string) -> string", - "!doc": "通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)组件(对于由两个\"代理\"字符组成的字符).", + "!doc": + '通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例来编码统一资源标识符(URI)组件(对于由两个"代理"字符组成的字符).', }, - "decodeURI": { + decodeURI: { "!type": "fn(uri: string) -> string", "!doc": "解码以前由encodeURI或类似例程创建的统一资源标识符(URI).", }, - "decodeURIComponent": { + decodeURIComponent: { "!type": "fn(uri: string) -> string", - "!doc": "解码以前由encodeURIComponent或类似例程创建的统一资源标识符(URI)组件.", + "!doc": + "解码以前由encodeURIComponent或类似例程创建的统一资源标识符(URI)组件.", }, - "Math": { - "E": { + Math: { + E: { "!type": "number", - "!doc": "自然对数的底数,e约为2.718." + "!doc": "自然对数的底数,e约为2.718.", }, - "LN2": { + LN2: { "!type": "number", - "!doc": " 2的自然对数,大约为0.693." + "!doc": " 2的自然对数,大约为0.693.", }, - "LN10": { + LN10: { "!type": "number", - "!doc": " 10的自然对数,大约为2.302." + "!doc": " 10的自然对数,大约为2.302.", }, - "LOG2E": { + LOG2E: { "!type": "number", "!doc": " E的以2为底的对数(大约1.442).", }, - "LOG10E": { + LOG10E: { "!type": "number", - "!doc": " E的以10为底的对数(约0.434)." + "!doc": " E的以10为底的对数(约0.434).", }, - "SQRT1_2": { + SQRT1_2: { "!type": "number", - "!doc": " 1/2的平方根;等效于2的平方根上的1,大约为0.707." + "!doc": " 1/2的平方根;等效于2的平方根上的1,大约为0.707.", }, - "SQRT2": { + SQRT2: { "!type": "number", - "!doc": " 2的平方根,大约为1.414." + "!doc": " 2的平方根,大约为1.414.", }, - "PI": { + PI: { "!type": "number", - "!doc": "圆的周长与其直径之比,大约为3.14159." + "!doc": "圆的周长与其直径之比,大约为3.14159.", }, - "abs": { + abs: { "!type": "fn(number) -> number", - "!doc": "返回数字的绝对值." + "!doc": "返回数字的绝对值.", }, - "cos": { + cos: { "!type": "fn(number) -> number", - "!doc": "返回数字的余弦." + "!doc": "返回数字的余弦.", }, - "sin": { + sin: { "!type": "fn(number) -> number", - "!doc": "返回数字的正弦." + "!doc": "返回数字的正弦.", }, - "tan": { + tan: { "!type": "fn(number) -> number", - "!doc": "返回数字的正切值." + "!doc": "返回数字的正切值.", }, - "acos": { + acos: { "!type": "fn(number) -> number", - "!doc": "返回数字的反余弦(以弧度为单位)." + "!doc": "返回数字的反余弦(以弧度为单位).", }, - "asin": { + asin: { "!type": "fn(number) -> number", - "!doc": "返回数字的反正弦(以弧度为单位)." + "!doc": "返回数字的反正弦(以弧度为单位).", }, - "atan": { + atan: { "!type": "fn(number) -> number", - "!doc": "返回数字的反正切(以弧度为单位)." + "!doc": "返回数字的反正切(以弧度为单位).", }, - "atan2": { + atan2: { "!type": "fn(y: number, x: number) -> number", - "!doc": "返回其参数商的反正切值." + "!doc": "返回其参数商的反正切值.", }, - "ceil": { + ceil: { "!type": "fn(number) -> number", - "!doc": "返回大于或等于数字的最小整数." + "!doc": "返回大于或等于数字的最小整数.", }, - "floor": { + floor: { "!type": "fn(number) -> number", - "!doc": "返回小于或等于数字的最大整数." + "!doc": "返回小于或等于数字的最大整数.", }, - "round": { + round: { "!type": "fn(number) -> number", - "!doc": "返回四舍五入到最接近整数的数字的值." + "!doc": "返回四舍五入到最接近整数的数字的值.", }, - "exp": { + exp: { "!type": "fn(number) -> number", - "!doc": "返回E ^ x,其中x是自变量,E是欧拉常数,自然对数的底." + "!doc": "返回E ^ x,其中x是自变量,E是欧拉常数,自然对数的底.", }, - "log": { + log: { "!type": "fn(number) -> number", "!doc": "返回数字的自然对数(以E为底).", }, - "sqrt": { + sqrt: { "!type": "fn(number) -> number", - "!doc": "返回数字的平方根." + "!doc": "返回数字的平方根.", }, - "pow": { + pow: { "!type": "fn(number, number) -> number", - "!doc": "将基数返回指数幂,即baseexponent." + "!doc": "将基数返回指数幂,即baseexponent.", }, - "max": { + max: { "!type": "fn(number, number) -> number", - "!doc": "返回零个或多个数字中的最大值." + "!doc": "返回零个或多个数字中的最大值.", }, - "min": { + min: { "!type": "fn(number, number) -> number", - "!doc": "返回零个或多个数字中的最小值." + "!doc": "返回零个或多个数字中的最小值.", }, - "random": { + random: { "!type": "fn() -> number", - "!doc": "返回一个浮点伪随机数,范围为[0,1),即从0(包括)到不包括1(排除),然后您可以缩放到所需的值范围." + "!doc": + "返回一个浮点伪随机数,范围为[0,1),即从0(包括)到不包括1(排除),然后您可以缩放到所需的值范围.", }, - "log10": { + log10: { "!type": "fn(x: number) -> number", "!doc": " Math.log10()函数返回数字的以10为底的对数.", }, - "log2": { + log2: { "!type": "fn(x: number) -> number", "!doc": " Math.log2()函数返回数字的以2为底的对数.", }, - "sign": { + sign: { "!type": "fn(x: number) -> number", "!doc": " Math.sign()函数返回数字的符号,指示数字是正数,负数还是零.,", }, - "trunc": { + trunc: { "!type": "fn(x: number) -> number", - "!doc": " Math.trunc()函数通过删除任何小数位来返回数字的整数部分.它不舍入任何数字.该函数可以用floor()和ceil()函数表示: ,", + "!doc": + " Math.trunc()函数通过删除任何小数位来返回数字的整数部分.它不舍入任何数字.该函数可以用floor()和ceil()函数表示: ,", }, "!doc": "一个内置对象,具有用于数学常数和函数的属性和方法.", }, - "JSON": { - "parse": { - "!type": "fn(json: string, reviver?: fn(key: string, value: ?) -> ?) -> ?", + 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数组,则可以选择仅包括指定的属性.", + 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": + " JSON(JavaScript对象表示法)是一种数据交换格式.尽管它不是严格的子集,但它非常类似于JavaScript语法的子集.(有关详细信息,请参见JavaScript参考中的JSON.)在编写任何类型的基于JavaScript的应用程序(包括网站和浏览器扩展程序)时非常有用.例如,您可以将JSON格式的用户信息存储在cookie中,或者可以将扩展名首选项以JSON形式存储在字符串值的浏览器首选项中.", + }, }, { "!name": "core", "!define": { - "image": { + image: { "!doc": "图片信息", - "width": "number", - "height": "number", - "src": "string" + width: "number", + height: "number", + src: "string", }, - "audio": { + audio: { "!doc": "音乐音效信息", - "currentTime": "number", - "play": "fn()", - "pause": "fn()", - "paused": "bool", - "duration": "number", - "volume": "number", + currentTime: "number", + play: "fn()", + pause: "fn()", + paused: "bool", + duration: "number", + volume: "number", }, - "flag": { + flag: { "!doc": "当前变量", - "hard": { + hard: { "!type": "number", - "!doc": "当前难度编号" + "!doc": "当前难度编号", }, - "hatred": { + hatred: { "!type": "number", - "!doc": "当前仇恨值" + "!doc": "当前仇恨值", }, - "poison": { + poison: { "!type": "bool", - "!doc": "是否处于中毒状态" + "!doc": "是否处于中毒状态", }, - "weak": { + weak: { "!type": "number", - "!doc": "是否处于衰弱状态" + "!doc": "是否处于衰弱状态", }, - "curse": { + curse: { "!type": "number", - "!doc": "是否处于诅咒状态" + "!doc": "是否处于诅咒状态", }, - "no_zone": { + no_zone: { "!type": "bool", - "!doc": "无视领域伤害" + "!doc": "无视领域伤害", }, - "no_repulse": { + no_repulse: { "!type": "bool", - "!doc": "无视阻击伤害" + "!doc": "无视阻击伤害", }, - "no_lasel": { + no_lasel: { "!type": "bool", - "!doc": "无视激光伤害" + "!doc": "无视激光伤害", }, - "no_ambush": { + no_ambush: { "!type": "bool", - "!doc": "无视捕捉" + "!doc": "无视捕捉", }, - "__bgm__": { + __bgm__: { "!type": "string", - "!doc": "背景音乐" + "!doc": "背景音乐", }, - "__weather__": { - "!doc": "天气" + __weather__: { + "!doc": "天气", }, - "__color__": { - "!doc": "色调" + __color__: { + "!doc": "色调", }, - "__volume__": { + __volume__: { "!type": "number", - "!doc": "音量" + "!doc": "音量", }, - "skill": { + skill: { "!type": "number", - "!doc": "当前开启的技能编号" + "!doc": "当前开启的技能编号", }, - "skillName": { + skillName: { "!type": "string", - "!doc": "当前开启的技能名" + "!doc": "当前开启的技能名", }, - "input": { + input: { "!type": "string|number", - "!doc": "等待用户输入后的存放值" + "!doc": "等待用户输入后的存放值", }, - "type": { + type: { "!type": "number", - "!doc": "等待用户操作后获得的操作类型" + "!doc": "等待用户操作后获得的操作类型", }, - "keycode": { + keycode: { "!type": "number", - "!doc": "等待用户操作后用户按键的键值" + "!doc": "等待用户操作后用户按键的键值", }, - "x": { + x: { "!type": "number", - "!doc": "等待用户操作后用户点击的网格横坐标" + "!doc": "等待用户操作后用户点击的网格横坐标", }, - "y": { + y: { "!type": "number", - "!doc": "等待用户操作后用户点击的网格纵坐标" + "!doc": "等待用户操作后用户点击的网格纵坐标", }, - "px": { + px: { "!type": "number", - "!doc": "等待用户操作后用户点击的像素横坐标" + "!doc": "等待用户操作后用户点击的像素横坐标", }, - "py": { + py: { "!type": "number", - "!doc": "等待用户操作后用户点击的像素纵坐标" + "!doc": "等待用户操作后用户点击的像素纵坐标", }, - "__visited__": { - "!doc": "当前访问过的楼层" + __visited__: { + "!doc": "当前访问过的楼层", }, - "__leaveLoc__": { - "!doc": "每个楼层的离开位置,用于楼传平面塔模式" + __leaveLoc__: { + "!doc": "每个楼层的离开位置,用于楼传平面塔模式", }, - "cannotMoveDirectly": { + cannotMoveDirectly: { "!type": "bool", - "!doc": "当前是否全局不可瞬移" + "!doc": "当前是否全局不可瞬移", }, }, - "hero": { + hero: { "!doc": "勇士当前属性", - "image": { + image: { "!type": "string", - "!doc": "行走图" + "!doc": "行走图", }, - "animate": { + animate: { "!type": "bool", - "!doc": "是否开启帧动画" + "!doc": "是否开启帧动画", }, - "name": { + name: { "!type": "string", - "!doc": "勇士名" + "!doc": "勇士名", }, - "lv": { + lv: { "!type": "number", - "!doc": "勇士等级" + "!doc": "勇士等级", }, - "hpmax": { + hpmax: { "!type": "number", - "!doc": "勇士生命上限" + "!doc": "勇士生命上限", }, - "hp": { + hp: { "!type": "number", - "!doc": "勇士当前生命值" + "!doc": "勇士当前生命值", }, - "atk": { + atk: { "!type": "number", - "!doc": "勇士当前攻击力" + "!doc": "勇士当前攻击力", }, - "def": { + def: { "!type": "number", - "!doc": "勇士当前防御力" + "!doc": "勇士当前防御力", }, - "manamax": { + manamax: { "!type": "number", - "!doc": "勇士当前魔力上限,负数无效" + "!doc": "勇士当前魔力上限,负数无效", }, - "mana": { + mana: { "!type": "number", - "!doc": "勇士当前魔力值" + "!doc": "勇士当前魔力值", }, - "mdef": { + mdef: { "!type": "number", - "!doc": "勇士当前护盾值" + "!doc": "勇士当前护盾值", }, - "money": { + money: { "!type": "number", - "!doc": "勇士当前金币" + "!doc": "勇士当前金币", }, - "exp": { + exp: { "!type": "number", - "!doc": "勇士当前经验" + "!doc": "勇士当前经验", }, - "equipment": { + equipment: { "!type": "[string]", - "!doc": "勇士当前装备" + "!doc": "勇士当前装备", }, - "items": { + items: { "!doc": "勇士当前道具", - "constants": { - "!doc": "永久道具" + constants: { + "!doc": "永久道具", }, - "tools": { + tools: { "!doc": "消耗道具", - "yellowKey": { + yellowKey: { "!type": "number", - "!doc": "黄钥匙个数" + "!doc": "黄钥匙个数", }, - "blueKey": { + blueKey: { "!type": "number", - "!doc": "蓝钥匙个数" + "!doc": "蓝钥匙个数", }, - "redKey": { + redKey: { "!type": "number", - "!doc": "红钥匙个数" + "!doc": "红钥匙个数", }, - "greenKey": { + greenKey: { "!type": "number", - "!doc": "绿钥匙个数" + "!doc": "绿钥匙个数", }, - "steelKey": { + steelKey: { "!type": "number", - "!doc": "铁门钥匙个数" + "!doc": "铁门钥匙个数", }, }, - "equips": { - "!doc": "在背包中未装备上的装备" + equips: { + "!doc": "在背包中未装备上的装备", }, }, - "loc": { + loc: { "!doc": "勇士当前坐标和朝向", - "x": "number", - "y": "number", - "direction": { + x: "number", + y: "number", + direction: { "!doc": "朝向,只能为 up,down,left,right 之一", - "!type": "string" + "!type": "string", }, }, - "flags": { + flags: { "!type": "flag", - "!doc": "当前游戏中用到的变量" + "!doc": "当前游戏中用到的变量", }, - "followers": { + followers: { "!type": "[?]", - "!doc": "跟随者信息" + "!doc": "跟随者信息", }, - "steps": { + steps: { "!type": "number", - "!doc": "当前步数" - } + "!doc": "当前步数", + }, }, - "block": { + block: { "!doc": "地图图块信息", - "x": { + x: { "!type": "number", - "!doc": "图块的x坐标" + "!doc": "图块的x坐标", }, - "y": { + y: { "!type": "number", - "!doc": "图块的y坐标" + "!doc": "图块的y坐标", }, - "id": { + id: { "!type": "number", - "!doc": "图块的数字" + "!doc": "图块的数字", }, - "event": { + event: { "!doc": "图块上的事件信息", - "id": { + id: { "!type": "string", - "!doc": "图块的ID" + "!doc": "图块的ID", }, - "cls": { + cls: { "!type": "string", - "!doc": "图块的类别,一般为所在图片名去掉后缀" + "!doc": "图块的类别,一般为所在图片名去掉后缀", }, - "disabled": { + disabled: { "!type": "bool", - "!doc": "启用状态" - } - } + "!doc": "启用状态", + }, + }, }, - "blockInfo": { + blockInfo: { "!doc": "图块的更多信息", - "animate": { + animate: { "!type": "number", - "!doc": "动画帧数" + "!doc": "动画帧数", }, - "cls": { + cls: { "!type": "string", - "!doc": "图块类别" + "!doc": "图块类别", }, - "faceIds": { + faceIds: { "!doc": "行走图朝向", - "up": "string", - "down": "string", - "left": "string", - "right": "string" + up: "string", + down: "string", + left: "string", + right: "string", }, - "height": { + height: { "!type": "number", - "!doc": "图块高度" + "!doc": "图块高度", }, - "id": { + id: { "!type": "string", - "!doc": "图块ID" + "!doc": "图块ID", }, - "image": { + image: { "!type": "image", - "!doc": "图块所在的图片" + "!doc": "图块所在的图片", }, - "name": { + name: { "!type": "string", - "!doc": "图块名称" + "!doc": "图块名称", }, - "number": { + number: { "!type": "number", - "!doc": "图块使用的数字" + "!doc": "图块使用的数字", }, - "posX": { + posX: { "!type": "number", - "!doc": "图块在图片上的横坐标" + "!doc": "图块在图片上的横坐标", }, - "posY": { + posY: { "!type": "number", - "!doc": "图块在图片上的纵坐标" + "!doc": "图块在图片上的纵坐标", }, }, - "enemy": { + enemy: { "!doc": "怪物信息", - "id": { + id: { "!type": "string", - "!doc": "怪物ID" + "!doc": "怪物ID", }, - "name": { + name: { "!type": "string", - "!doc": "怪物名称" + "!doc": "怪物名称", }, - "displayIdInBook": { + displayIdInBook: { "!type": "string", - "!doc": "在怪物手册映射ID" + "!doc": "在怪物手册映射ID", }, - "hp": { + hp: { "!type": "number", - "!doc": "怪物生命值" + "!doc": "怪物生命值", }, - "atk": { + atk: { "!type": "number", - "!doc": "怪物攻击" + "!doc": "怪物攻击", }, - "def": { + def: { "!type": "number", - "!doc": "怪物防御" + "!doc": "怪物防御", }, - "money": { + money: { "!type": "number", - "!doc": "怪物金币" + "!doc": "怪物金币", }, - "exp": { + exp: { "!type": "number", - "!doc": "怪物经验" + "!doc": "怪物经验", }, - "special": { + special: { "!type": "[number]", - "!doc": "怪物特殊属性" + "!doc": "怪物特殊属性", }, - "point": { + point: { "!type": "number", - "!doc": "怪物加点" + "!doc": "怪物加点", }, - "value": { + value: { "!type": "number", - "!doc": "怪物特殊属性值:阻激夹域伤害值;吸血比例;光环增加生命比例" + "!doc": "怪物特殊属性值:阻激夹域伤害值;吸血比例;光环增加生命比例", }, - "zoneSquare": { + zoneSquare: { "!type": "bool", - "!doc": "领域怪是否九宫格伤害;区域光环是否九宫格范围" + "!doc": "领域怪是否九宫格伤害;区域光环是否九宫格范围", }, - "range": { + range: { "!type": "number", - "!doc": "领域伤害的范围;区域光环范围" + "!doc": "领域伤害的范围;区域光环范围", }, - "notBomb": { + notBomb: { "!type": "bool", - "!doc": "怪物不可炸" + "!doc": "怪物不可炸", }, - "n": { + n: { "!type": "number", - "!doc": "多连击的连击数;净化比例" + "!doc": "多连击的连击数;净化比例", }, - "add": { + add: { "!type": "bool", - "!doc": "吸血是否加到自身;光环是否叠加" + "!doc": "吸血是否加到自身;光环是否叠加", }, - "atkValue": { + atkValue: { "!type": "number", - "!doc": "反击比例;退化扣除攻击;光环增加攻击;" + "!doc": "反击比例;退化扣除攻击;光环增加攻击;", }, - "defValue": { + defValue: { "!type": "number", - "!doc": "破甲比例;退化扣除防御;光环增加防御" + "!doc": "破甲比例;退化扣除防御;光环增加防御", }, - "damage": { + damage: { "!type": "number", - "!doc": "固伤值" + "!doc": "固伤值", }, }, - "item": { + item: { "!doc": "道具信息", - "id": { + id: { "!type": "string", - "!doc": "道具ID" + "!doc": "道具ID", }, - "cls": { + cls: { "!type": "string", - "!doc": "道具类型" + "!doc": "道具类型", }, - "name": { + name: { "!type": "string", - "!doc": "道具名称" + "!doc": "道具名称", }, - "text": { + text: { "!type": "string", - "!doc": "道具描述" + "!doc": "道具描述", }, - "hideInToolbox": { + hideInToolbox: { "!type": "bool", - "!doc": "不显示在道具栏" + "!doc": "不显示在道具栏", }, - "equip": { + equip: { "!doc": "装备属性", - "type": { + type: { "!type": "number|string", - "!doc": "装备类型" + "!doc": "装备类型", }, - "animate": { + animate: { "!type": "string", - "!doc": "装备动画" + "!doc": "装备动画", }, - "value": { - "!doc": "数值加成" + value: { + "!doc": "数值加成", + }, + percentage: { + "!doc": "比例加成", }, - "percentage": { - "!doc": "比例加成" - } }, - "hideInReplay": { + hideInReplay: { "!type": "bool", - "!doc": "回放不绘制道具栏" + "!doc": "回放不绘制道具栏", }, }, - "floor": { + floor: { "!doc": "楼层信息", - "floorId": { + floorId: { "!type": "string", - "!doc": "楼层ID" + "!doc": "楼层ID", }, - "title": { + title: { "!type": "string", - "!doc": "楼层中文名" + "!doc": "楼层中文名", }, - "name": { + name: { "!type": "string", - "!doc": "状态栏显示值" + "!doc": "状态栏显示值", }, - "width": { + width: { "!type": "number", - "!doc": "地图宽" + "!doc": "地图宽", }, - "height": { + height: { "!type": "number", - "!doc": "地图高" + "!doc": "地图高", }, - "canFlyTo": { + canFlyTo: { "!type": "bool", - "!doc": "该楼是否可以楼传飞到" + "!doc": "该楼是否可以楼传飞到", }, - "canFlyFrom": { + canFlyFrom: { "!type": "bool", - "!doc": "该楼是否可以楼传飞出" + "!doc": "该楼是否可以楼传飞出", }, - "canUseQuickShop": { + canUseQuickShop: { "!type": "bool", - "!doc": "该楼是否可快捷商店" + "!doc": "该楼是否可快捷商店", }, - "cannotViewMap": { + cannotViewMap: { "!type": "bool", - "!doc": "该层是否不允许被浏览地图看到,也不统计" + "!doc": "该层是否不允许被浏览地图看到,也不统计", }, - "cannotMoveDirectly": { + cannotMoveDirectly: { "!type": "bool", - "!doc": "该层是否不允许瞬间移动" + "!doc": "该层是否不允许瞬间移动", }, - "upFloor": { + upFloor: { "!type": "[number]", - "!doc": "上楼点" + "!doc": "上楼点", }, - "downFloor": { + downFloor: { "!type": "[number]", - "!doc": "下楼点" + "!doc": "下楼点", }, - "flyPoint": { + flyPoint: { "!type": "[number]", - "!doc": "楼传落点" + "!doc": "楼传落点", }, - "color": { - "!doc": "楼层色调" + color: { + "!doc": "楼层色调", }, - "weather": { - "!doc": "楼层天气" + weather: { + "!doc": "楼层天气", }, - "bgm": { + bgm: { "!type": "string", - "!doc": "楼层背景音乐" + "!doc": "楼层背景音乐", }, - "ratio": { + ratio: { "!type": "number", - "!doc": "宝石/血瓶效果" + "!doc": "宝石/血瓶效果", }, - "map": { + map: { "!type": "[[number]]", - "!doc": "地图数据" + "!doc": "地图数据", }, - "blocks": { + blocks: { "!type": "[block]", - "!doc": "本层图块信息" - } + "!doc": "本层图块信息", + }, }, - "animate": { + animate: { "!doc": "动画信息", - "se": { + se: { "!type": "string", - "!doc": "动画音效" - } - } + "!doc": "动画音效", + }, + }, }, - "core": { + core: { "!doc": "核心游戏控制", - "__SIZE__": { + __SIZE__: { "!type": "number", - "!doc": "窗口宽度,为13或15" + "!doc": "窗口宽度,为13或15", }, - "__PIXELS__": { + __PIXELS__: { "!type": "number", - "!doc": "窗口像素宽度,为416或480" + "!doc": "窗口像素宽度,为416或480", }, - "__HALF_SIZE__": { + __HALF_SIZE__: { "!type": "number", - "!doc": "窗口宽度的一半,为6或7" + "!doc": "窗口宽度的一半,为6或7", }, - "floorIds": { + floorIds: { "!type": "[string]", - "!doc": "全部楼层ID列表" + "!doc": "全部楼层ID列表", }, - "floors": { - "!doc": "全部楼层信息" + floors: { + "!doc": "全部楼层信息", }, - "floorPartitions": { + floorPartitions: { "!type": "[[string]]", - "!doc": "楼层分区信息" + "!doc": "楼层分区信息", }, - "material": { + material: { "!doc": "游戏所用到的资源", - "animates": { - "!doc": "注册的动画" + animates: { + "!doc": "注册的动画", }, - "images": { - "!doc": "注册的图片" + images: { + "!doc": "注册的图片", }, - "bgms": { - "!doc": "注册的背景音乐" + bgms: { + "!doc": "注册的背景音乐", }, - "sounds": { - "!doc": "注册的音效" + sounds: { + "!doc": "注册的音效", }, - "enemys": { + enemys: { "!doc": "怪物定义", }, - "items": { - "!doc": "道具定义" - } + items: { + "!doc": "道具定义", + }, }, - "timeout": { - "!doc": "当前异步事件句柄" + timeout: { + "!doc": "当前异步事件句柄", }, - "interval": { - "!doc": "当前异步事件延时" + interval: { + "!doc": "当前异步事件延时", }, - "animateFrame": { - "!doc": "当前各个帧动画" + animateFrame: { + "!doc": "当前各个帧动画", }, - "musicStatus": { + musicStatus: { "!doc": "音乐音效状态", - "bgmStatus": { + bgmStatus: { "!type": "bool", - "!doc": "是否播放BGM" + "!doc": "是否播放BGM", }, - "soundStatus": { + soundStatus: { "!type": "bool", - "!doc": "是否播放SE" + "!doc": "是否播放SE", }, - "playingBgm": { + playingBgm: { "!type": "string", - "!doc": "正在播放的bgm" + "!doc": "正在播放的bgm", }, - "lastBgm": { + lastBgm: { "!type": "string", - "!doc": "上次播放的bgm" + "!doc": "上次播放的bgm", }, - "playingSounds": { - "!doc": "正在播放的SE" + playingSounds: { + "!doc": "正在播放的SE", }, - "volume": { + volume: { "!type": "number", - "!doc": "当前bgm音量" - } + "!doc": "当前bgm音量", + }, }, - "platform": { + platform: { "!doc": "平台信息", - "isPC": "bool", - "isAndroid": "bool", - "isIOS": "bool", + isPC: "bool", + isAndroid: "bool", + isIOS: "bool", }, - "domStyle": { + domStyle: { "!doc": "界面样式", - "scale": { + scale: { "!type": "number", "!doc": "当前界面放缩比例", }, - "ratio": { + ratio: { "!type": "number", - "!doc": "高清UI放缩比例" + "!doc": "高清UI放缩比例", }, - "hdCanvas": { + hdCanvas: { "!type": "[string]", - "!doc": "高清UI的系统画布" + "!doc": "高清UI的系统画布", }, - "availableScale": { + availableScale: { "!type": "[number]", - "!doc": "当前界面支持的放缩比例" + "!doc": "当前界面支持的放缩比例", }, - "isVertical": { + isVertical: { "!type": "bool", - "!doc": "当前是否是竖屏" + "!doc": "当前是否是竖屏", }, - "showStatusBar": { + showStatusBar: { "!type": "bool", - "!doc": "当前是否显示状态栏" + "!doc": "当前是否显示状态栏", }, - "toolbarBtn": { + toolbarBtn: { "!type": "bool", - "!doc": "当前工具栏是否是1-8的按钮" + "!doc": "当前工具栏是否是1-8的按钮", }, }, - "bigmap": { + bigmap: { "!doc": "大地图信息", - "canvas": { + canvas: { "!type": "[string]", - "!doc": "大地图的画布" + "!doc": "大地图的画布", }, - "width": { + width: { "!type": "number", - "!doc": "大地图高度" + "!doc": "大地图高度", }, - "height": { + height: { "!type": "number", - "!doc": "大地图宽度" + "!doc": "大地图宽度", }, - "offsetX": { + offsetX: { "!type": "number", - "!doc": "大地图视角横向偏移量" + "!doc": "大地图视角横向偏移量", }, - "offsetY": { + offsetY: { "!type": "number", - "!doc": "大地图视角纵向偏移量" + "!doc": "大地图视角纵向偏移量", }, - "posX": { + posX: { "!type": "number", - "!doc": "大地图视角横向基准格" + "!doc": "大地图视角横向基准格", }, - "posY": { + posY: { "!type": "number", - "!doc": "大地图视角纵向基准格" + "!doc": "大地图视角纵向基准格", }, - "v2": { + v2: { "!type": "bool", - "!doc": "是否是新版大地图绘制方式" + "!doc": "是否是新版大地图绘制方式", }, - "threshold": { + threshold: { "!type": "number", - "!doc": "新版大地图绘制方式的分界线" + "!doc": "新版大地图绘制方式的分界线", }, - "extend": { + extend: { "!type": "number", - "!doc": "新版大地图模式下向每一侧额外计算的数量" + "!doc": "新版大地图模式下向每一侧额外计算的数量", }, - "scale": { + scale: { "!type": "number", - "!doc": "缩略图的比例放缩" + "!doc": "缩略图的比例放缩", }, - "tempCanvas": { + tempCanvas: { "!type": "CanvasRenderingContext2D", - "!doc": "临时画布" - } + "!doc": "临时画布", + }, }, - "saves": { - "!doc": "当前存档信息" + saves: { + "!doc": "当前存档信息", }, - "dymCanvas": { - "!doc": "各个自定义画布" + dymCanvas: { + "!doc": "各个自定义画布", }, - "statusBar": { - "!doc": "状态栏信息" + statusBar: { + "!doc": "状态栏信息", }, - "canvas": { - "!doc": "系统画布" + canvas: { + "!doc": "系统画布", }, - "flags": { - "!doc": "系统开关" + flags: { + "!doc": "系统开关", }, - "values": { - "!doc": "全局数值,如毒衰效果" + values: { + "!doc": "全局数值,如毒衰效果", }, - "firstData": { - "!doc": "初始属性,如出生点" + firstData: { + "!doc": "初始属性,如出生点", }, - "status": { + status: { "!doc": "状态信息", - "hero": { + hero: { "!type": "hero", - "!doc": "勇士信息" + "!doc": "勇士信息", }, - "automaticRoute": { - "!doc": "自动寻路信息" + automaticRoute: { + "!doc": "自动寻路信息", }, - "bgmaps": { - "!doc": "各地图背景层" + bgmaps: { + "!doc": "各地图背景层", }, - "fgmaps": { - "!doc": "各地图前景层" + fgmaps: { + "!doc": "各地图前景层", }, - "mapBlockObjs": { - "!doc": "以<位置,block>存放的各地图图块信息" + mapBlockObjs: { + "!doc": "以<位置,block>存放的各地图图块信息", }, - "boxAnimateObjs": { - "!doc": "(手册和剧情文本的)帧动画对象" + boxAnimateObjs: { + "!doc": "(手册和剧情文本的)帧动画对象", }, - "checkBlock": { + checkBlock: { "!doc": "阻激夹域捕捉信息", - "damage": { - "!doc": "每个点的伤害信息" + damage: { + "!doc": "每个点的伤害信息", }, - "type": { - "!doc": "每个点的伤害类型" + type: { + "!doc": "每个点的伤害类型", }, - "repluse": { - "!doc": "每个点的阻击信息" + repluse: { + "!doc": "每个点的阻击信息", }, - "ambush": { - "!doc": "每个点的捕捉信息" + ambush: { + "!doc": "每个点的捕捉信息", }, - "needCache": { + needCache: { "!type": "bool", - "!doc": "该楼层是否需要计算缓存" + "!doc": "该楼层是否需要计算缓存", }, - "cache": { - "!doc": "每个点的光环缓存" + cache: { + "!doc": "每个点的光环缓存", }, }, - "damage": { + damage: { "!doc": "每个点的显伤信息", }, - "ctrlDown": { + ctrlDown: { "!type": "bool", - "!doc": "Ctrl键是否被按下" + "!doc": "Ctrl键是否被按下", }, - "curtainColor": { - "!doc": "当前画面色调" + curtainColor: { + "!doc": "当前画面色调", }, - "event": { + event: { "!doc": "当前事件", - "data": { - "!doc": "事件信息,如坐标等" + data: { + "!doc": "事件信息,如坐标等", }, - "id": { + id: { "!type": "string", - "!doc": "事件类型,如选择项/确认框" + "!doc": "事件类型,如选择项/确认框", }, - "interval": { + interval: { "!type": "number", - "!doc": "打字机效果的定时器" + "!doc": "打字机效果的定时器", }, - "selection": { + selection: { "!type": "number", - "!doc": "选择项和确认框的当前选中项" + "!doc": "选择项和确认框的当前选中项", + }, + ui: { + "!doc": "当前事件的界面信息,如楼传/手册/SL", }, - "ui": { - "!doc": "当前事件的界面信息,如楼传/手册/SL" - } }, - "floorAnimateObjs": { - "!doc": "楼层贴图的帧动画" + floorAnimateObjs: { + "!doc": "楼层贴图的帧动画", }, - "floorId": { + floorId: { "!type": "string", - "!doc": "当前楼层ID" + "!doc": "当前楼层ID", }, - "gameOver": { + gameOver: { "!type": "bool", - "!doc": "游戏是否已结束" + "!doc": "游戏是否已结束", }, - "globalAnimateObjs": { - "!doc": "各全局动画" + globalAnimateObjs: { + "!doc": "各全局动画", }, - "globalAnimateStatus": { + globalAnimateStatus: { "!type": "number", - "!doc": "全局动画的帧状态" + "!doc": "全局动画的帧状态", }, - "globalAttribute": { - "!doc": "全局css属性" + globalAttribute: { + "!doc": "全局css属性", }, - "hard": { + hard: { "!type": "string", - "!doc": "状态栏一角的难度名" + "!doc": "状态栏一角的难度名", }, - "downTime": { + downTime: { "!type": "number", - "!doc": "方向键已按下的时间" + "!doc": "方向键已按下的时间", }, - "heroCenter": { + heroCenter: { "!doc": "勇士中心像素坐标", - "px": { + px: { "!type": "number", - "!doc": "勇士中心的横坐标" + "!doc": "勇士中心的横坐标", }, - "py": { + py: { "!type": "number", - "!doc": "勇士中心的纵坐标" + "!doc": "勇士中心的纵坐标", }, }, - "heroMoving": { + heroMoving: { "!type": "number", - "!doc": "勇士行走的状态值" + "!doc": "勇士行走的状态值", }, - "heroStop": { + heroStop: { "!type": "bool", - "!doc": "勇士是否已停下" + "!doc": "勇士是否已停下", }, - "holdingKeys": { + holdingKeys: { "!type": "[number]", - "!doc": "当前按下的键" + "!doc": "当前按下的键", }, - "id2number": { - "!doc": "图块ID到数字的对应关系" + id2number: { + "!doc": "图块ID到数字的对应关系", }, - "lockControl": { + lockControl: { "!type": "bool", - "!doc": "当前是否是锁定操作状态" + "!doc": "当前是否是锁定操作状态", }, - "maps": { - "!doc": "当前各地图信息" + maps: { + "!doc": "当前各地图信息", }, - "number2Block": { - "!doc": "数字到图块对象的对应关系" + number2Block: { + "!doc": "数字到图块对象的对应关系", }, - "played": { + played: { "!type": "bool", - "!doc": "当前是否游戏中(不包括标题画面和录像回放)" + "!doc": "当前是否游戏中(不包括标题画面和录像回放)", }, - "replay": { + replay: { "!doc": "当前录像回放信息", - "animate": { + animate: { "!type": "bool", - "!doc": "回放是否正处于动画中" + "!doc": "回放是否正处于动画中", }, - "pausing": { + pausing: { "!type": "bool", - "!doc": "回放是否暂停中" + "!doc": "回放是否暂停中", }, - "replaying": { + replaying: { "!type": "bool", - "!doc": "当前是否回放中" + "!doc": "当前是否回放中", }, - "save": { + save: { "!type": "[]", - "!doc": "录像中的存档" + "!doc": "录像中的存档", }, - "speed": { + speed: { "!type": "number", - "!doc": "回放速度" + "!doc": "回放速度", }, - "steps": { + steps: { "!type": "number", - "!doc": "回放步数" + "!doc": "回放步数", }, - "toReplay": { + toReplay: { "!type": "[string]", - "!doc": "待回放的列表" + "!doc": "待回放的列表", }, - "totalList": { + totalList: { "!type": "[string]", - "!doc": "回放总列表" - } + "!doc": "回放总列表", + }, }, - "route": { + route: { "!type": "[string]", - "!doc": "当前录像内容" + "!doc": "当前录像内容", }, - "shops": { - "!doc": "全局商店列表" + shops: { + "!doc": "全局商店列表", }, - "textAttribute": { - "!doc": "当前剧情文本属性" + textAttribute: { + "!doc": "当前剧情文本属性", }, - "thisMap": { + thisMap: { "!type": "floor", - "!doc": "当前地图信息" - } + "!doc": "当前地图信息", + }, }, - "init": { + init: { "!doc": "初始化core", - "!type": "fn(coreData: ?, callback: fn())" + "!type": "fn(coreData: ?, callback: fn())", }, - "doFunc": { + doFunc: { "!doc": "执行一个函数;如果函数名是字符串则转发到插件中", - "!type": "fn(func: name|fn(), _this?: ?)" + "!type": "fn(func: name|fn(), _this?: ?)", }, - "control": { - "!doc": "负责整个游戏的核心控制系统,分为如下几个部分:
- requestAnimationFrame相关
- 标题界面,开始和重新开始游戏
- 自动寻路和人物行走相关
- 画布、位置、阻激夹域、显伤等相关
- 录像的回放相关
- 存读档,自动存档,同步存档等相关
- 人物属性和状态、位置、变量等相关
- 天气、色调、音乐和音效的播放
- 状态栏和工具栏相关
- 界面resize相关", - "showStatusBar": { + control: { + "!doc": + "负责整个游戏的核心控制系统,分为如下几个部分:
- requestAnimationFrame相关
- 标题界面,开始和重新开始游戏
- 自动寻路和人物行走相关
- 画布、位置、阻激夹域、显伤等相关
- 录像的回放相关
- 存读档,自动存档,同步存档等相关
- 人物属性和状态、位置、变量等相关
- 天气、色调、音乐和音效的播放
- 状态栏和工具栏相关
- 界面resize相关", + showStatusBar: { "!doc": "显示状态栏", - "!type": "fn()" + "!type": "fn()", }, - "startReplay": { + startReplay: { "!doc": "开始播放录像", - "!type": "fn(list: [string])" + "!type": "fn(list: [string])", }, - "triggerReplay": { + triggerReplay: { "!doc": "播放或暂停录像回放", - "!type": "fn()" + "!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())" + 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())" + 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)" + updateDamage: { + "!doc": + "注意!请勿使用该函数!请使用core.updateStatusBar()代替!!重算并绘制地图显伤
例如:core.updateDamage(); // 更新当前地图的显伤,绘制在显伤层(废话)
floorId: 地图id,不填视为当前地图。预览地图时填写
ctx: 绘制到的画布,如果填写了就会画在该画布而不是显伤层", + "!type": + "fn(floorId?: string, ctx?: string|CanvasRenderingContext2D)", }, - "drawDamage": { + drawDamage: { "!doc": "仅绘制地图显伤", - "!type": "fn(string|CanvasRenderingContext2D)" + "!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" + 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" + nextY: { + "!doc": + "获取主角面前第n格的纵坐标
例如:core.jumpHero(core.nextX(2), core.nextY(2)); // 主角向前跃过一格,即跳跃靴道具的使用效果
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1", + "!type": "fn(n?: number) -> number", }, - "clearContinueAutomaticRoute": { + clearContinueAutomaticRoute: { "!doc": "清空剩下的自动寻路列表", - "!type": "fn(callback?: fn())" + "!type": "fn(callback?: fn())", }, - "updateViewport": { + updateViewport: { "!doc": "更新大地图的可见区域", - "!type": "fn()" + "!type": "fn()", }, - "getMappedName": { + getMappedName: { "!doc": "获得映射文件名", - "!type": "fn(name: string) -> string" + "!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)" + 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: ?)" + 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" + 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)" + stopSound: { + "!doc": + "停止播放音效。如果未指定id则停止所有音效,否则只停止指定的音效。", + "!type": "fn(id?: number)", }, - "getPlayingSounds": { - "!doc": "获得当前正在播放的所有(指定)音效的id列表
name: 音效名,可用别名;不填代表返回正在播放的全部音效
返回值: 一个列表,每一项为一个正在播放的音效id;可用core.stopSound立刻停止播放", - "!type": "fn(name?: string) -> [number]" + getPlayingSounds: { + "!doc": + "获得当前正在播放的所有(指定)音效的id列表
name: 音效名,可用别名;不填代表返回正在播放的全部音效
返回值: 一个列表,每一项为一个正在播放的音效id;可用core.stopSound立刻停止播放", + "!type": "fn(name?: string) -> [number]", }, - "addGameCanvasTranslate": { + addGameCanvasTranslate: { "!doc": "加减画布偏移", - "!type": "fn(x?: number, y?: number)" + "!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)" + 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)" + drawHero: { + "!doc": + "绘制主角和跟随者并重置视野到以主角为中心
例如:core.drawHero(); // 原地绘制主角的静止帧并重置视野野
status: 只能为 stop, leftFoot 和 rightFoot,不填用stop。
offset: 相对主角逻辑位置的偏移量,不填视为无偏移。
frame: 绘制的第几帧", + "!type": "fn(status?: string, offset?: number, frame?: number)", }, - "pauseBgm": { + pauseBgm: { "!doc": "暂停背景音乐的播放", - "!type": "fn()" + "!type": "fn()", }, - "setBgmSpeed": { - "!doc": "设置背景音乐的播放速度和音调
speed: 播放速度,必须为30-300中间的值。100为正常速度。
usePitch: 是否同时改变音调(部分设备可能不支持)", - "!type": "fn(speed: number, usePitch?: bool)" + setBgmSpeed: { + "!doc": + "设置背景音乐的播放速度和音调
speed: 播放速度,必须为30-300中间的值。100为正常速度。
usePitch: 是否同时改变音调(部分设备可能不支持)", + "!type": "fn(speed: number, usePitch?: bool)", }, - "setReplaySpeed": { + setReplaySpeed: { "!doc": "设置播放速度", - "!type": "fn(speed: number)" + "!type": "fn(speed: number)", }, - "pauseReplay": { + pauseReplay: { "!doc": "暂停播放", - "!type": "fn()" + "!type": "fn()", }, - "doSL": { + doSL: { "!doc": "实际进行存读档事件", - "!type": "fn(id?: string, type?: string)" + "!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)" + 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}])" + 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": { + setHeroOpacity: { "!doc": "改变勇士的不透明度", - "!type": "fn(opacity?: number, moveMode?: string, time?: number, callback?: fn())" + "!type": + "fn(opacity?: number, moveMode?: string, time?: number, callback?: fn())", }, - "gatherFollowers": { + gatherFollowers: { "!doc": "立刻聚集所有的跟随者", - "!type": "fn()" + "!type": "fn()", }, - "getStatus": { - "!doc": "读取主角的某个属性,不包括百分比修正
例如:core.getStatus('atk'); // 读取主角的攻击力
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.getHeroLoc(name),其他的会直接读取 core.status.hero[name]", - "!type": "fn(name: string) -> number" + 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)" + 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" + getLvName: { + "!doc": + "根据级别的数字获取对应的名称,后者定义在全塔属性
例如:core.getLvName(); // 获取主角当前级别的名称,如“下级佣兵”
lv: 级别的数字,不填则视为主角当前的级别
返回值:级别的名称,如果不存在就还是返回数字", + "!type": "fn(lv?: number) -> string|number", }, - "getNextLvUpNeed": { - "!doc": "获得下次升级需要的经验值。
升级扣除模式下会返回经验差值;非扣除模式下会返回总共需要的经验值。
如果无法进行下次升级,返回null。", - "!type": "fn() -> 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)" + addStatus: { + "!doc": + "增减主角的某个属性,等价于core.setStatus(name, core.getStatus(name) + value)
例如:core.addStatus('atk', 100'); // 给主角攻击力加100
name: 属性的英文名
value: 属性的增量", + "!type": "fn(name: string, value: number)", }, - "speedUpReplay": { + speedUpReplay: { "!doc": "加速播放", - "!type": "fn()" + "!type": "fn()", }, - "loadData": { + loadData: { "!doc": "从本地读档", - "!type": "fn(data?: ?, callback?: fn())" + "!type": "fn(data?: ?, callback?: fn())", }, - "debug": { - "!doc": "开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。
此模式下不可回放录像和上传成绩。", - "!type": "fn()" + debug: { + "!doc": + "开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。
此模式下不可回放录像和上传成绩。", + "!type": "fn()", }, - "moveOneStep": { - "!doc": "每移动一格后执行的事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(callback?: fn())" + moveOneStep: { + "!doc": + "每移动一格后执行的事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", + "!type": "fn(callback?: fn())", }, - "clearStatus": { + clearStatus: { "!doc": "清除游戏状态和数据", - "!type": "fn()" + "!type": "fn()", }, - "updateFollowers": { + updateFollowers: { "!doc": "更新跟随者坐标", - "!type": "fn()" + "!type": "fn()", }, - "waitHeroToStop": { - "!doc": "等待主角停下
例如:core.waitHeroToStop(core.vibrate); // 等待主角停下,然后视野左右抖动1秒
callback: 主角停止后的回调函数", - "!type": "fn(callback?: fn())" + waitHeroToStop: { + "!doc": + "等待主角停下
例如:core.waitHeroToStop(core.vibrate); // 等待主角停下,然后视野左右抖动1秒
callback: 主角停止后的回调函数", + "!type": "fn(callback?: fn())", }, - "hideStatusBar": { + hideStatusBar: { "!doc": "隐藏状态栏
showToolbox: 是否不隐藏竖屏工具栏", - "!type": "fn(showToolbox?: bool)" + "!type": "fn(showToolbox?: bool)", }, - "getBuff": { - "!doc": "读取主角某个属性的百分比修正倍率,初始值为1
例如:core.getBuff('atk'); // 主角当前能发挥出多大比例的攻击力
name: 属性的英文名", - "!type": "fn(name: string) -> number" + 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])" + triggerDebuff: { + "!doc": + "获得或移除毒衰咒效果
action: 要获得还是移除,'get'为获得,'remove'为移除
type: 获得或移除的内容(poison/weak/curse),可以为字符串或数组", + "!type": "fn(action: string, type: string|[string])", }, - "setToolbarButton": { + setToolbarButton: { "!doc": "改变工具栏为按钮1-8", - "!type": "fn(useButton?: bool)" + "!type": "fn(useButton?: bool)", }, - "getSaves": { + getSaves: { "!doc": "获得某些存档内容", - "!type": "fn(ids?: ?, callback?: fn())" + "!type": "fn(ids?: ?, callback?: fn())", }, - "replay": { + replay: { "!doc": "回放下一个操作", - "!type": "fn()" + "!type": "fn()", }, - "getStatusOrDefault": { + getStatusOrDefault: { "!doc": "从status中获得属性,如果不存在则从勇士属性中获取", - "!type": "fn(status?: ?, name?: string)" + "!type": "fn(status?: ?, name?: string)", }, - "unregisterReplayAction": { + unregisterReplayAction: { "!doc": "注销一个录像行为", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "unregisterWeather": { + unregisterWeather: { "!doc": "注销一个天气", - "!type": "fn(name: string)" + "!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)" + setBuff: { + "!doc": + "设置主角某个属性的百分比修正倍率,初始值为1,
倍率存放在flag: '__'+name+'_buff__' 中
例如:core.setBuff('atk', 0.5); // 主角能发挥出的攻击力减半
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 新的百分比修正倍率,不填(效果上)视为1", + "!type": "fn(name: string, value: number)", }, - "continueAutomaticRoute": { + continueAutomaticRoute: { "!doc": "继续剩下的自动寻路操作", - "!type": "fn()" + "!type": "fn()", }, - "setAutoHeroMove": { - "!doc": "连续行走
例如:core.setAutoHeroMove([{direction: \"up\", step: 1}, {direction: \"left\", step: 3}]); // 上左左左
steps: 压缩的步伐数组,每项表示朝某方向走多少步", - "!type": "fn(steps: [?])" + setAutoHeroMove: { + "!doc": + '连续行走
例如:core.setAutoHeroMove([{direction: "up", step: 1}, {direction: "left", step: 3}]); // 上左左左
steps: 压缩的步伐数组,每项表示朝某方向走多少步', + "!type": "fn(steps: [?])", }, - "unregisterResize": { + unregisterResize: { "!doc": "注销一个resize函数", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "saveAndStopAutomaticRoute": { + saveAndStopAutomaticRoute: { "!doc": "保存剩下的寻路,并停止", - "!type": "fn()" + "!type": "fn()", }, - "hideStartAnimate": { - "!doc": "淡出标题画面
例如:core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择
callback: 标题画面完全淡出后的回调函数", - "!type": "fn(callback?: fn())" + hideStartAnimate: { + "!doc": + "淡出标题画面
例如:core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择
callback: 标题画面完全淡出后的回调函数", + "!type": "fn(callback?: fn())", }, - "getAllSaves": { + getAllSaves: { "!doc": "获得所有存档内容", - "!type": "fn(callback?: fn())" + "!type": "fn(callback?: fn())", }, - "updateHeroIcon": { + updateHeroIcon: { "!doc": "更新状态栏的勇士图标", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "setMusicBtn": { + setMusicBtn: { "!doc": "设置音乐图标的显隐状态", - "!type": "fn()" + "!type": "fn()", }, - "isPlaying": { + isPlaying: { "!doc": "游戏是否已经开始", - "!type": "fn() -> bool" + "!type": "fn() -> bool", }, - "triggerBgm": { + triggerBgm: { "!doc": "开启或关闭背景音乐的播放", - "!type": "fn()" + "!type": "fn()", }, - "moveHero": { - "!doc": "连续前进,不撞南墙不回头
例如:core.moveHero(); // 连续前进
direction: 可选,如果设置了就会先转身到该方向
callback: 可选,如果设置了就只走一步
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(direction?: string, callback?: fn())" + moveHero: { + "!doc": + "连续前进,不撞南墙不回头
例如:core.moveHero(); // 连续前进
direction: 可选,如果设置了就会先转身到该方向
callback: 可选,如果设置了就只走一步
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", + "!type": "fn(direction?: string, callback?: fn())", }, - "getRealStatusOrDefault": { - "!doc": "从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取", - "!type": "fn(status?: ?, name?: string)" + getRealStatusOrDefault: { + "!doc": + "从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取", + "!type": "fn(status?: ?, name?: string)", }, - "getStatusLabel": { + getStatusLabel: { "!doc": "获得某个状态的名字,如atk->攻击,def->防御等", - "!type": "fn(name: string) -> string" + "!type": "fn(name: string) -> string", }, - "removeSave": { + removeSave: { "!doc": "删除某个存档", - "!type": "fn(index?: number, callback?: fn())" + "!type": "fn(index?: number, callback?: fn())", }, - "registerAnimationFrame": { - "!doc": "注册一个 animationFrame
name: 名称,可用来作为注销使用
needPlaying: 是否只在游戏运行时才执行(在标题界面不执行)
func: 要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数", - "!type": "fn(name: string, needPlaying: bool, func?: fn(timestamp: number))" + 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" + getHeroLoc: { + "!doc": + "读取主角的位置和/或朝向
例如:core.getHeroLoc(); // 读取主角的位置和朝向
name: 要读取横坐标还是纵坐标还是朝向还是都读取
返回值:name ? core.status.hero.loc[name] : core.status.hero.loc", + "!type": "fn(name: string) -> string|number", }, - "stopAutomaticRoute": { + stopAutomaticRoute: { "!doc": "停止自动寻路操作", - "!type": "fn()" + "!type": "fn()", }, - "setWeather": { - "!doc": "设置天气,不计入存档。如需长期生效请使用core.events._action_setWeather()函数
例如:core.setWeather('fog', 10); // 设置十级大雾天
type: 新天气的类型,不填视为晴天
level: 新天气(晴天除外)的级别,必须为不大于10的正整数,不填视为5", - "!type": "fn(type?: string, level?: number)" + 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)" + updateStatusBar: { + "!doc": + "刷新状态栏和地图显伤
doNotCheckAutoEvents: 是否不检查自动事件", + "!type": "fn(doNotCheckAutoEvents?: bool, immediate?: bool)", }, - "autosave": { + autosave: { "!doc": "自动存档", - "!type": "fn(removeLast?: bool)" + "!type": "fn(removeLast?: bool)", }, - "clearStatusBar": { + clearStatusBar: { "!doc": "清空状态栏", - "!type": "fn()" + "!type": "fn()", }, - "moveAction": { - "!doc": "尝试前进一步,如果面前不可被踏入就会直接触发该点事件
请勿直接使用此函数,如有需要请使用「勇士前进一步或撞击」事件
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(callback?: 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" + hasFlag: { + "!doc": + "判定一个flag变量是否存在且不为false、0、''、null、undefined和NaN
例如:core.hasFlag('poison'); // 判断主角当前是否中毒
name: 变量名,支持中文
此函数等价于 !!core.getFlag(name)", + "!type": "fn(name: string) -> bool", }, - "rewindReplay": { + rewindReplay: { "!doc": "回退到上一个录像节点", - "!type": "fn()" + "!type": "fn()", }, - "playBgm": { - "!doc": "播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__
例如:core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟
bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名
startTime: 跳过前多少秒,不填则不跳过", - "!type": "fn(bgm: string, startTime?: number)" + playBgm: { + "!doc": + "播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__
例如:core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟
bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名
startTime: 跳过前多少秒,不填则不跳过", + "!type": "fn(bgm: string, startTime?: number)", }, - "isReplaying": { + isReplaying: { "!doc": "是否正在播放录像", - "!type": "fn() -> bool" + "!type": "fn() -> bool", }, - "isMoving": { + isMoving: { "!doc": "当前是否正在移动", - "!type": "fn() -> bool" + "!type": "fn() -> bool", }, - "getSaveIndexes": { + getSaveIndexes: { "!doc": "获得所有存在存档的存档位", - "!type": "fn(callback?: fn())" + "!type": "fn(callback?: fn())", }, - "unlockControl": { + unlockControl: { "!doc": "解锁用户控制行为", - "!type": "fn()" + "!type": "fn()", }, - "syncSave": { + syncSave: { "!doc": "同步存档到服务器", - "!type": "fn(type?: string)" + "!type": "fn(type?: string)", }, - "removeFlag": { + removeFlag: { "!doc": "删除某个flag/变量", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "registerResize": { - "!doc": "注册一个resize函数
name: 名称,可供注销使用
func: 可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。", - "!type": "fn(name: string, func: fn(obj: ?))" + 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))" + 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": { + stopReplay: { "!doc": "停止播放", - "!type": "fn(force?: bool)" + "!type": "fn(force?: bool)", }, - "turnHero": { - "!doc": "主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
例如:core.turnHero(); // 主角顺时针旋转90°,即单击主角或按下Z键的效果
direction: 主角的新朝向,可为 up, down, left, right, :left, :right, :back 七种之一", - "!type": "fn(direction?: string)" + turnHero: { + "!doc": + "主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
例如:core.turnHero(); // 主角顺时针旋转90°,即单击主角或按下Z键的效果
direction: 主角的新朝向,可为 up, down, left, right, :left, :right, :back 七种之一", + "!type": "fn(direction?: string)", }, - "resumeReplay": { + resumeReplay: { "!doc": "恢复播放", - "!type": "fn()" + "!type": "fn()", }, - "resize": { + resize: { "!doc": "屏幕分辨率改变后重新自适应", - "!type": "fn()" + "!type": "fn()", }, - "getSave": { + getSave: { "!doc": "获得某个存档内容", - "!type": "fn(index?: number, callback?: fn(data: ?))" + "!type": "fn(index?: number, callback?: fn(data: ?))", }, - "setViewport": { - "!doc": "设置视野范围
px,py: 左上角相对大地图的像素坐标,不需要为32倍数", - "!type": "fn(px?: number, py?: number)" + setViewport: { + "!doc": + "设置视野范围
px,py: 左上角相对大地图的像素坐标,不需要为32倍数", + "!type": "fn(px?: number, py?: number)", }, - "chooseReplayFile": { + chooseReplayFile: { "!doc": "选择录像文件", - "!type": "fn()" + "!type": "fn()", }, - "lockControl": { + lockControl: { "!doc": "锁定用户控制,常常用于事件处理", - "!type": "fn()" + "!type": "fn()", }, - "updateCheckBlock": { + updateCheckBlock: { "!doc": "更新领域、夹击、阻击的伤害地图", - "!type": "fn(floorId?: string)" + "!type": "fn(floorId?: string)", }, - "checkBlock": { + checkBlock: { "!doc": "检查并执行领域、夹击、阻击事件", - "!type": "fn()" + "!type": "fn()", }, - "clearAutomaticRouteNode": { + clearAutomaticRouteNode: { "!doc": "清除自动寻路路线", - "!type": "fn(x?: number, y?: number)" + "!type": "fn(x?: number, y?: number)", }, - "getFlag": { - "!doc": "读取一个flag变量
name: 变量名,支持中文
defaultValue: 当变量不存在时的返回值,可选(事件流中默认填0)。", - "!type": "fn(name: string, defaultValue?: ?)" + getFlag: { + "!doc": + "读取一个flag变量
name: 变量名,支持中文
defaultValue: 当变量不存在时的返回值,可选(事件流中默认填0)。", + "!type": "fn(name: string, defaultValue?: ?)", }, - "getNakedStatus": { + getNakedStatus: { "!doc": "获得勇士原始属性(无装备和衰弱影响)", - "!type": "fn(name: string)" + "!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" + nearHero: { + "!doc": + "判定主角是否身处某个点的锯齿领域(取曼哈顿距离)
例如:core.nearHero(6, 6, 6); // 判定主角是否身处点(6,6)的半径为6的锯齿领域
x: 领域的中心横坐标
y: 领域的中心纵坐标
n: 领域的半径,不填视为1", + "!type": "fn(x: number, y: number, n?: number) -> bool", }, - "stepReplay": { + stepReplay: { "!doc": "单步播放", - "!type": "fn()" + "!type": "fn()", }, - "hasSave": { + hasSave: { "!doc": "判断某个存档位是否存在存档", - "!type": "fn(index?: number) -> bool" + "!type": "fn(index?: number) -> bool", }, - "showStartAnimate": { - "!doc": "进入标题画面
例如:core.showStartAnimate(); // 重启游戏但不重置bgm
noAnimate: 可选,true表示不由黑屏淡入而是立即亮屏
callback: 可选,完全亮屏后的回调函数", - "!type": "fn(noAnimate?: bool, callback?: fn())" + showStartAnimate: { + "!doc": + "进入标题画面
例如:core.showStartAnimate(); // 重启游戏但不重置bgm
noAnimate: 可选,true表示不由黑屏淡入而是立即亮屏
callback: 可选,完全亮屏后的回调函数", + "!type": "fn(noAnimate?: bool, callback?: fn())", }, - "moveViewport": { + moveViewport: { "!doc": "移动视野范围", - "!type": "fn(x: number, y: number, moveMode?: string, time?: number, callback?: fn())" + "!type": + "fn(x: number, y: number, moveMode?: string, time?: number, callback?: fn())", }, - "syncLoad": { + syncLoad: { "!doc": "从服务器加载存档", - "!type": "fn()" + "!type": "fn()", }, - "setHeroMoveInterval": { + setHeroMoveInterval: { "!doc": "设置行走的效果动画", - "!type": "fn(callback?: fn())" + "!type": "fn(callback?: fn())", }, - "registerReplayAction": { - "!doc": "注册一个录像行为
name: 自定义名称,可用于注销使用
func: 具体执行录像的函数,可为一个函数或插件中的函数名;
需要接受一个action参数,代表录像回放时的下一个操作
func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。", - "!type": "fn(name: string, func: fn(action?: string) -> bool)" + registerReplayAction: { + "!doc": + "注册一个录像行为
name: 自定义名称,可用于注销使用
func: 具体执行录像的函数,可为一个函数或插件中的函数名;
需要接受一个action参数,代表录像回放时的下一个操作
func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。", + "!type": "fn(name: string, func: fn(action?: string) -> bool)", }, - "checkAutosave": { + checkAutosave: { "!doc": "实际将自动存档写入存储", - "!type": "fn()" + "!type": "fn()", }, - "resumeBgm": { + resumeBgm: { "!doc": "恢复背景音乐的播放
resumeTime: 从哪一秒开始恢复播放", - "!type": "fn(resumeTime?: number)" + "!type": "fn(resumeTime?: number)", }, - "setGameCanvasTranslate": { + setGameCanvasTranslate: { "!doc": "设置大地图的偏移量", - "!type": "fn(ctx: string|CanvasRenderingContext2D, x: number, y: number)" + "!type": + "fn(ctx: string|CanvasRenderingContext2D, x: number, y: number)", }, - "checkBgm": { + checkBgm: { "!doc": "检查bgm状态", - "!type": "fn()" + "!type": "fn()", }, - "setDisplayScale": { + setDisplayScale: { "!doc": "设置屏幕放缩", - "!type": "fn(delta: number)" + "!type": "fn(delta: number)", }, - "speedDownReplay": { + speedDownReplay: { "!doc": "减速播放", - "!type": "fn()" + "!type": "fn()", }, - "getRealStatus": { - "!doc": "计算主角的某个属性,包括百分比修正
例如:core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值
name: 属性的英文名,请注意只能用于数值类属性哦,否则乘法会得到NaN", - "!type": "fn(name: string)" + getRealStatus: { + "!doc": + "计算主角的某个属性,包括百分比修正
例如:core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值
name: 属性的英文名,请注意只能用于数值类属性哦,否则乘法会得到NaN", + "!type": "fn(name: string)", }, - "saveData": { + saveData: { "!doc": "存档到本地", - "!type": "fn()" + "!type": "fn()", }, - "unregisterAnimationFrame": { + unregisterAnimationFrame: { "!doc": "注销一个animationFrame", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "tryMoveDirectly": { - "!doc": "尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致
例如:core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件
destX: 目标点的横坐标
destY: 目标点的纵坐标", - "!type": "fn(destX: number, destY: number)" + tryMoveDirectly: { + "!doc": + "尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致
例如:core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件
destX: 目标点的横坐标
destY: 目标点的纵坐标", + "!type": "fn(destX: number, destY: number)", }, - "moveDirectly": { + moveDirectly: { "!doc": "瞬间移动", - "!type": "fn(destX?: number, destY?: number, ignoreSteps?: number)" + "!type": "fn(destX?: number, destY?: number, ignoreSteps?: number)", }, - "clearRouteFolding": { + clearRouteFolding: { "!doc": "清空录像折叠信息", - "!type": "fn()" + "!type": "fn()", }, - "checkRouteFolding": { + checkRouteFolding: { "!doc": "检查录像折叠信息", - "!type": "fn()" + "!type": "fn()", }, - "setSwitch": { + setSwitch: { "!doc": "设置某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string, value: ?)" + "!type": + "fn(x: number, y: number, floorId: string, name: string, value: ?)", }, - "getSwitch": { + getSwitch: { "!doc": "获得某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string, defaultValue?: ?)" + "!type": + "fn(x: number, y: number, floorId: string, name: string, defaultValue?: ?)", }, - "addSwitch": { + addSwitch: { "!doc": "增加某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string, value: number)" + "!type": + "fn(x: number, y: number, floorId: string, name: string, value: number)", }, - "removeSwitch": { + removeSwitch: { "!doc": "删除某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string)" + "!type": "fn(x: number, y: number, floorId: string, name: string)", }, - "removeSwitch": { + removeSwitch: { "!doc": "判定某个独立开关", - "!type": "fn(x: number, y: number, floorId: string, name: string) -> bool" - } + "!type": + "fn(x: number, y: number, floorId: string, name: string) -> bool", + }, }, - "icons": { + icons: { "!doc": "图标信息", - "getTilesetOffset": { + getTilesetOffset: { "!doc": "根据图块数字或ID获得所在的tileset和坐标信息", - "!type": "fn(id?: string) -> {image: ?, x: number, y: number}" + "!type": "fn(id?: string) -> {image: ?, x: number, y: number}", }, - "getClsFromId": { + getClsFromId: { "!doc": "根据ID获得其图标类型", - "!type": "fn(id?: string) -> string" + "!type": "fn(id?: string) -> string", }, - "getAllIconIds": { + getAllIconIds: { "!doc": "获得所有图标的ID", - "!type": "fn() -> [string]" + "!type": "fn() -> [string]", }, - "getIcons": { + getIcons: { "!doc": "获得所有图标类型", - "!type": "fn()" - } + "!type": "fn()", + }, }, - "items": { + items: { "!doc": "道具相关的函数", - "getEquip": { - "!doc": "检查主角某种类型的装备目前是什么
例如:core.getEquip(1) // 主角目前装备了什么盾牌
equipType: 装备类型,自然数
返回值:装备id,null表示未穿戴", - "!type": "fn(equipType: number) -> string" + 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())" + 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" + itemCount: { + "!doc": + "统计某种道具的持有量
例如:core.itemCount('yellowKey') // 持有多少把黄钥匙
itemId: 道具id
返回值:该种道具的持有量,不包括已穿戴的装备", + "!type": "fn(itemId: string) -> number", }, - "getItems": { + getItems: { "!doc": "获得所有道具", - "!type": "fn()" + "!type": "fn()", }, - "canUseItem": { - "!doc": "检查能否使用某种道具
例如:core.canUseItem('pickaxe') // 能否使用破墙镐
itemId: 道具id
返回值:true表示可以使用", - "!type": "fn(itemId: string) -> bool" + 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" + 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)" + 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())" + unloadEquip: { + "!doc": + "脱下某个类型的装备
例如:core.unloadEquip(1) // 卸下盾牌,无回调
equipType: 装备类型编号,自然数
callback: 卸下装备后的回调函数", + "!type": "fn(equipType: number, callback?: fn())", }, - "quickLoadEquip": { - "!doc": "快速换装
例如:core.quickLoadEquip(1) // 快速换上1号套装
index: 套装编号,自然数", - "!type": "fn(index: number)" + 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)" + 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)" + 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)" + 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: ?}" + compareEquipment: { + "!doc": + "比较两件(类型可不同)装备的优劣
例如:core.compareEquipment('sword5', 'shield5') // 比较神圣剑和神圣盾的优劣
compareEquipId: 装备甲的id
beComparedEquipId: 装备乙的id
返回值:两装备的各属性差,甲减乙,0省略", + "!type": + "fn(compareEquipId: string, beComparedEquipId: string) -> {value: ?, percentage: ?}", }, - "removeItem": { + removeItem: { "!doc": "删除某个物品", - "!type": "fn(itemId?: string, itemNum?: number)" + "!type": "fn(itemId?: string, itemNum?: number)", }, - "getEquipTypeById": { - "!doc": "判定某件装备的类型
例如:core.getEquipTypeById('shield5') // 1(盾牌)
equipId: 装备id
返回值:类型编号,自然数", - "!type": "fn(equipId: string) -> number" + getEquipTypeById: { + "!doc": + "判定某件装备的类型
例如:core.getEquipTypeById('shield5') // 1(盾牌)
equipId: 装备id
返回值:类型编号,自然数", + "!type": "fn(equipId: string) -> number", }, - "getEquipTypeByName": { + getEquipTypeByName: { "!doc": "根据类型获得一个可用的装备孔", - "!type": "fn(name?: string)" + "!type": "fn(name?: string)", }, - "useItem": { - "!doc": "使用一个道具
例如:core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调
itemId: 道具id
noRoute: 是否不计入录像,快捷键使用的请填true,否则可省略
callback: 道具使用完毕或使用失败后的回调函数", - "!type": "fn(itemId: string, noRoute?: bool, callback?: fn())" + 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" + 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" + 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" + 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)", }, - "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": { + utils: { "!doc": "工具函数库,里面有各个样板中使用到的工具函数。", - "scan": { + scan: { "!doc": "朝向到x,y映射", - "up": { - "x": "number", - "y": "number" + up: { + x: "number", + y: "number", }, - "down": { - "x": "number", - "y": "number" + down: { + x: "number", + y: "number", }, - "left": { - "x": "number", - "y": "number" + left: { + x: "number", + y: "number", + }, + right: { + x: "number", + y: "number", }, - "right": { - "x": "number", - "y": "number" - } }, - "applyEasing": { + applyEasing: { "!doc": "获得变速移动曲线", - "!type": "fn(mode?: string) -> fn(t: number) -> number" + "!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" + 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" + 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)" + 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]]" + cloneArray: { + "!doc": + "深拷贝一个1D或2D数组对象
例如:core.cloneArray(core.status.thisMap.map)", + "!type": "fn(data?: [number]|[[number]]) -> [number]|[[number]]", }, - "setLocalForage": { + setLocalForage: { "!doc": "往数据库写入一段数据", - "!type": "fn(key: string, value?: ?, successCallback?: fn(), errorCallback?: fn())" + "!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?: ?)" + 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" + replaceText: { + "!doc": + "将一段文字中的${}(表达式)进行替换。
例如:core.replaceText('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); // 把主角的生命值和持有的黄钥匙数量代入这句话
text: 模板字符串,可以使用${}计算js表达式,支持“状态、物品、变量、独立开关、全局存储、图块id、图块类型、敌人数据、装备id”等量参与运算
返回值:替换完毕后的字符串", + "!type": "fn(text: string, prefix?: string) -> string", }, - "removeLocalStorage": { + removeLocalStorage: { "!doc": "移除本地存储", - "!type": "fn(key: string)" + "!type": "fn(key: string)", }, - "unzip": { + unzip: { "!doc": "解压一段内容", - "!type": "fn(blobOrUrl?: ?, success?: fn(data: ?), error?: fn(error: string), convertToText?: bool, onprogress?: fn(loaded: number, total: number))" + "!type": + "fn(blobOrUrl?: ?, success?: fn(data: ?), error?: fn(error: string), convertToText?: bool, onprogress?: fn(loaded: number, total: number))", }, - "formatTime": { + formatTime: { "!doc": "格式化时间", - "!type": "fn(time: number) -> string" + "!type": "fn(time: number) -> string", }, - "readFile": { - "!doc": "尝试请求读取一个本地文件内容 [异步]
success: 成功后的回调
error: 失败后的回调
readType: 不设置则以文本读取,否则以DataUrl形式读取", - "!type": "fn(success?: fn(data: string), error?: fn(message: string), readType?: bool)" + readFile: { + "!doc": + "尝试请求读取一个本地文件内容 [异步]
success: 成功后的回调
error: 失败后的回调
readType: 不设置则以文本读取,否则以DataUrl形式读取", + "!type": + "fn(success?: fn(data: string), error?: fn(message: string), readType?: bool)", }, - "readFileContent": { + readFileContent: { "!doc": "文件读取完毕后的内容处理 [异步]", - "!type": "fn(content: string)" + "!type": "fn(content: string)", }, - "formatDate": { + formatDate: { "!doc": "格式化日期为字符串", - "!type": "fn(date: ?) -> string" + "!type": "fn(date: ?) -> string", }, - "download": { - "!doc": "弹窗请求下载一个文本文件
例如:core.download('route.txt', JSON.stringify(core.status.route)); // 弹窗请求下载录像
filename: 文件名
content: 文件内容", - "!type": "fn(filename: string, content: 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" + 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" + strlen: { + "!doc": + "求字符串的国标码字节数,也可用于等宽字体下文本的宽度测算。请注意样板的默认字体Verdana不是等宽字体
例如:core.strlen('无敌ad'); // 6
str: 待测字符串
返回值:字符串的国标码字节数,每个汉字为2,每个ASCII字符为1", + "!type": "fn(str: string) -> number", }, - "myprompt": { + myprompt: { "!doc": "让用户输入一段文字", - "!type": "fn(hint: string, value: string, callback?: fn(data?: string))" + "!type": + "fn(hint: string, value: string, callback?: fn(data?: string))", }, - "getCookie": { + getCookie: { "!doc": "访问浏览器cookie", - "!type": "fn(name: string) -> string" + "!type": "fn(name: string) -> string", }, - "decodeRoute": { - "!doc": "录像解压的最后一步,即一压的逆过程
例如:core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-|
route: 录像解压倒数第二步的结果,即一压的结果
返回值:原始录像", - "!type": "fn(route: string) -> [string]" + decodeRoute: { + "!doc": + "录像解压的最后一步,即一压的逆过程
例如:core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-|
route: 录像解压倒数第二步的结果,即一压的结果
返回值:原始录像", + "!type": "fn(route: string) -> [string]", }, - "formatDate2": { + formatDate2: { "!doc": "格式化日期为最简字符串", - "!type": "fn(date: ?) -> string" + "!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: ?) -> [?]" + 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" + same: { + "!doc": + "判定深层相等, 会逐层比较每个元素
例如:core.same(['1', 2], ['1', 2]); // true", + "!type": "fn(a?: ?, b?: ?) -> bool", }, - "setTwoDigits": { + setTwoDigits: { "!doc": "两位数显示", - "!type": "fn(x: number) -> string" + "!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]" + 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": { + decompress: { "!doc": "解压缩一个数据", - "!type": "fn(value: ?)" + "!type": "fn(value: ?)", }, - "showWithAnimate": { + showWithAnimate: { "!doc": "动画显示某对象", - "!type": "fn(obj?: ?, speed?: number, callback?: fn())" + "!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" + 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" + 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())" + 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)" + 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" + 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" + 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))" + 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": { + getGuid: { "!doc": "获得或生成浏览器唯一的guid", - "!type": "fn() -> string" + "!type": "fn() -> string", }, - "getLocalStorage": { + getLocalStorage: { "!doc": "获得本地存储", - "!type": "fn(key: string, defaultValue?: ?)" + "!type": "fn(key: string, defaultValue?: ?)", }, - "arrayToRGB": { - "!doc": "颜色数组转字符串
例如:core.arrayToRGB([102, 204, 255]); // \"#66ccff\"
color: 一行三列的数组,必须为不大于255的自然数
返回值:该颜色的#xxxxxx字符串表示", - "!type": "fn(color: [number]) -> string" + 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" + 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" + 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": { + removeLocalForage: { "!doc": "移除本地数据库的数据", - "!type": "fn(key: string, successCallback?: fn(), errorCallback?: fn())" + "!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" + matchWildcard: { + "!doc": + "通配符匹配,用于搜索图块等批量处理。
例如:core.playSound(core.matchWildcard('*Key', itemId) ? 'item.opus' : 'door.opus'); // 判断捡到的是钥匙还是别的道具,从而播放不同的音效
pattern: 模式串,每个星号表示任意多个(0个起)字符
string: 待测串
返回值:true表示匹配成功,false表示匹配失败", + "!type": "fn(pattern: string, string: string) -> bool", }, - "setLocalStorage": { + setLocalStorage: { "!doc": "设置本地存储", - "!type": "fn(key: string, value?: ?)" + "!type": "fn(key: string, value?: ?)", }, - "hideWithAnimate": { + hideWithAnimate: { "!doc": "动画使某对象消失", - "!type": "fn(obj?: ?, speed?: number, callback?: fn())" + "!type": "fn(obj?: ?, speed?: number, callback?: fn())", }, - "copy": { + copy: { "!doc": "尝试复制一段文本到剪切板。", - "!type": "fn(data: string) -> bool" + "!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" + 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" + replaceValue: { + "!doc": + "对一个表达式中的特殊规则进行替换,如status:xxx等。
例如:core.replaceValue('status:atk+item:yellowKey'); // 把这两个冒号表达式替换为core.getStatus('hp')和core.itemCount('yellowKey')这样的函数调用
value: 模板字符串,注意独立开关不会被替换
返回值:替换完毕后的字符串", + "!type": "fn(value: string) -> string", }, - "getLocalForage": { + getLocalForage: { "!doc": "从本地数据库读出一段数据", - "!type": "fn(key: string, defaultValue?: ?, successCallback?: fn(data: ?), errorCallback?: fn())" + "!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" + 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?: ?)" + 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" + 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)" + 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": { + matchRegex: { "!doc": "是否满足正则表达式", - "!type": "fn(pattern: string, string: string) -> string" + "!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: ?) -> [?]" + push: { + "!doc": + "将b(可以是另一个数组)插入数组a的末尾,此函数用于弥补a.push(b)中b只能是单项的不足。
例如:core.push(todo, {type: 'unfollow'}); // 在事件指令数组todo的末尾插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新末项或后缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", + "!type": "fn(a: [?], b: ?) -> [?]", }, - "formatSize": { + formatSize: { "!doc": "格式化文件大小", - "!type": "fn(size: number) -> string" - } + "!type": "fn(size: number) -> string", + }, }, - "actions": { + actions: { "!doc": "主要是处理一些和用户交互相关的内容。", - "onup": { + onup: { "!doc": "当点击(触摸)事件放开时", - "!type": "fn(loc: {x: number, y: number, size: number})" + "!type": "fn(loc: {x: number, y: number, size: number})", }, - "pressKey": { + pressKey: { "!doc": "按住某个键时", - "!type": "fn(keyCode: number)" + "!type": "fn(keyCode: number)", }, - "keyUp": { + keyUp: { "!doc": "根据放开键的code来执行一系列操作", - "!type": "fn(keyCode: number, altKey?: bool, fromReplay?: bool)" + "!type": "fn(keyCode: number, altKey?: bool, fromReplay?: bool)", }, - "ondown": { + ondown: { "!doc": "点击(触摸)事件按下时", - "!type": "fn(loc: {x: number, y: number, size: number})" + "!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)" + 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": { + onkeyDown: { "!doc": "按下某个键时", - "!type": "fn(e: Event)" + "!type": "fn(e: Event)", }, - "keyDown": { + keyDown: { "!doc": "根据按下键的code来执行一系列操作", - "!type": "fn(keyCode: number)" + "!type": "fn(keyCode: number)", }, - "onStatusBarClick": { + onStatusBarClick: { "!doc": "点击自绘状态栏时", - "!type": "fn(e?: Event)" + "!type": "fn(e?: Event)", }, - "longClick": { + longClick: { "!doc": "长按", - "!type": "fn(x: number, y: number, px: number, py: number, fromEvent?: bool)" + "!type": + "fn(x: number, y: number, px: number, py: number, fromEvent?: bool)", }, - "unregisterAction": { + unregisterAction: { "!doc": "注销一个用户交互行为", - "!type": "fn(action: string, name: string)" + "!type": "fn(action: string, name: string)", }, - "keyDownCtrl": { + keyDownCtrl: { "!doc": "长按Ctrl键时", - "!type": "fn() -> bool" + "!type": "fn() -> bool", }, - "onclick": { + onclick: { "!doc": "具体点击屏幕上(x,y)点时,执行的操作", - "!type": "fn(x: number, y: number, px: number, py: number, stepPostfix?: [?])" + "!type": + "fn(x: number, y: number, px: number, py: number, stepPostfix?: [?])", }, - "doRegisteredAction": { + doRegisteredAction: { "!doc": "执行一个用户交互行为", - "!type": "fn(action: string, params: ?)" + "!type": "fn(action: string, params: ?)", }, - "onkeyUp": { + onkeyUp: { "!doc": "放开某个键时", - "!type": "fn(e: Event)" + "!type": "fn(e: Event)", }, - "onmousewheel": { + onmousewheel: { "!doc": "滑动鼠标滚轮时的操作", - "!type": "fn(direct: number)" + "!type": "fn(direct: number)", }, - "onmove": { + onmove: { "!doc": "当在触摸屏上滑动时", - "!type": "fn(loc: {x: number, y: number, size: number})" - } + "!type": "fn(loc: {x: number, y: number, size: number})", + }, }, - "loader": { + loader: { "!doc": "资源加载相关的函数", - "loadImages": { + loadImages: { "!doc": "加载一系列图片", - "!type": "fn(dir: string, names: [string], toSave: ?, callback?: fn()) " + "!type": + "fn(dir: string, names: [string], toSave: ?, callback?: fn()) ", }, - "loadImagesFromZip": { + loadImagesFromZip: { "!doc": "从zip中加载一系列图片", - "!type": "fn(url: string, names: [string], toSave?: ?, onprogress?: ?, onfinished?: ?)" + "!type": + "fn(url: string, names: [string], toSave?: ?, onprogress?: ?, onfinished?: ?)", }, - "loadBgm": { + loadBgm: { "!doc": "加载一个bgm", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "loadOneMusic": { + loadOneMusic: { "!doc": "加载一个音乐或音效", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "freeBgm": { + freeBgm: { "!doc": "释放一个bgm的缓存", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "loadOneSound": { + loadOneSound: { "!doc": "加载一个音效", - "!type": "fn(name: string)" + "!type": "fn(name: string)", }, - "loadImage": { + loadImage: { "!doc": "加载某一张图片", - "!type": "fn(dir: name, imgName: name, callback?: fn())" - } + "!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" + 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" + 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" + 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)" + stopAnimate: { + "!doc": + "立刻停止一个动画播放
id: 播放动画的编号,即drawAnimate或drawHeroAnimate的返回值;不填视为所有动画br/>doCallback: 是否执行该动画的回调函数", + "!type": "fn(id?: number, doCallback?: bool)", }, - "getPlayingAnimates": { - "!doc": "获得当前正在播放的所有(指定)动画的id列表
name: 动画名;不填代表返回全部正在播放的动画
返回值: 一个数组,每一项为一个正在播放的动画;可用core.stopAnimate停止播放。", - "!type": "fn(name?: string) -> [number]" + 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" + 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)" + drawMap: { + "!doc": + "地图重绘
例如:core.drawMap(); // 重绘当前地图,常用于更改贴图或改变自动元件后的刷新
floorId: 地图id,可省略表示当前楼层
callback: 重绘完毕后的回调函数,可选", + "!type": "fn(floorId?: string)", }, - "nearStair": { + nearStair: { "!doc": "当前位置是否在楼梯边;在楼传平面塔模式下对箭头也有效", - "!type": "fn() -> bool" + "!type": "fn() -> bool", }, - "turnBlock": { + turnBlock: { "!doc": "事件转向", - "!type": "fn(direction?: string, x?: number, y?: number, floorId?: string)" + "!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]]" + getMapArray: { + "!doc": + "生成事件层矩阵
例如:core.getMapArray('MT0'); // 生成主塔0层的事件层矩阵,隐藏的图块视为0
floorId: 地图id,不填视为当前地图
showDisable: 可选,true表示隐藏的图块也会被表示出来
返回值:事件层矩阵,注意对其阵元的访问是[y][x]", + "!type": "fn(floorId?: string, noCache?: bool) -> [[number]]", }, - "getMapNumber": { + getMapNumber: { "!doc": "获得事件层某个点的数字", - "!type": "fn(x: number, y: number, floorId?: string, noCache?: bool) -> number" + "!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())" + 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])" + replaceBlock: { + "!doc": + "批量替换图块
例如:core.replaceBlock(21, 22, core.floorIds); // 把游戏中地上当前所有的黄钥匙都变成蓝钥匙
fromNumber: 旧图块的数字
toNumber: 新图块的数字
floorId: 地图id或其数组,不填视为当前地图", + "!type": + "fn(fromNumber: number, toNumber: number, floorId?: string|[string])", }, - "drawBlock": { + drawBlock: { "!doc": "绘制一个图块", - "!type": "fn(block?: block, animate?: number)" + "!type": "fn(block?: block, animate?: number)", }, - "resetMap": { + resetMap: { "!doc": "重置地图", - "!type": "fn(floorId?: string|[string])" + "!type": "fn(floorId?: string|[string])", }, - "animateSetBlock": { + animateSetBlock: { "!doc": "动画形式转变某点图块", - "!type": "fn(number: number|string, x: number, y: number, floorId?: string, time?: number, callback?: fn())" + "!type": + "fn(number: number|string, x: number, y: number, floorId?: string, time?: number, callback?: fn())", }, - "animateSetBlocks": { + animateSetBlocks: { "!doc": "动画形式同时转变若干点图块", - "!type": "fn(number: number|string, locs: [?], floorId?: string, time?: number, callback?: fn())" + "!type": + "fn(number: number|string, locs: [?], floorId?: string, time?: number, callback?: fn())", }, - "compressMap": { + compressMap: { "!doc": "压缩地图", - "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]" + "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]", }, - "enemyExists": { + enemyExists: { "!doc": "某个点是否存在(指定的)怪物", - "!type": "fn(x: number, y: number, id?: string, floorId?: string) -> bool" + "!type": + "fn(x: number, y: number, id?: string, floorId?: string) -> bool", }, - "npcExists": { + npcExists: { "!doc": "某个点是否存在NPC", - "!type": "fn(x: number, y: number, floorId?: string) -> bool" + "!type": "fn(x: number, y: number, floorId?: string) -> bool", }, - "getBlockByNumber": { + getBlockByNumber: { "!doc": "根据数字获得图块", - "!type": "fn(number: number) -> block" + "!type": "fn(number: number) -> block", }, - "removeBlock": { - "!doc": "删除一个图块,对应于「隐藏事件」并同时删除
例如:core.removeBlock(0, 0); // 尝试删除地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", - "!type": "fn(x: number, y: number, floorId?: string)" + 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)" + hideBlock: { + "!doc": + "隐藏一个图块,对应于「隐藏事件」且不删除
例如:core.hideBlock(0, 0); // 隐藏地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + "!type": "fn(x: number, y: number, floorId?: string)", }, - "removeBlockByIndex": { + removeBlockByIndex: { "!doc": "根据block的索引删除该块", - "!type": "fn(index: number, floorId?: string)" + "!type": "fn(index: number, floorId?: string)", }, - "stairExists": { + stairExists: { "!doc": "某个点是否存在楼梯", - "!type": "fn(x: number, y: number, floorId?: string) -> bool" + "!type": "fn(x: number, y: number, floorId?: string) -> bool", }, - "isMapBlockDisabled": { + isMapBlockDisabled: { "!doc": "某个点图块是否被强制启用或禁用", - "!type": "fn(floorId?: string, x?: number, y?: number, flags?: ?) -> bool" + "!type": + "fn(floorId?: string, x?: number, y?: number, flags?: ?) -> bool", }, - "setMapBlockDisabled": { + setMapBlockDisabled: { "!doc": "设置某个点图块的强制启用或禁用状态", - "!type": "fn(floorId?: string, x?: number, y?: number, disabled?: bool)" + "!type": + "fn(floorId?: string, x?: number, y?: number, disabled?: bool)", }, - "setBlockOpacity": { + setBlockOpacity: { "!doc": "设置某个点图块的不透明度", - "!type": "fn(opacity?: number, x?: number, y?: number, floorId?: string)" + "!type": + "fn(opacity?: number, x?: number, y?: number, floorId?: string)", }, - "setBlockFilter": { + setBlockFilter: { "!doc": "设置某个点图块的特效", - "!type": "fn(filter?: ?, x?: number, y?: number, floorId?: string)" + "!type": "fn(filter?: ?, x?: number, y?: number, floorId?: string)", }, - "decompressMap": { + decompressMap: { "!doc": "解压缩地图", - "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]" + "!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}]" + automaticRoute: { + "!doc": + "自动寻路
例如:core.automaticRoute(0, 0); // 自动寻路到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:每步走完后主角的loc属性组成的一维数组", + "!type": + "fn(destX: number, destY: number) -> [{x: number, y: number, direction: string}]", }, - "resizeMap": { + resizeMap: { "!doc": "更改地图画布的尺寸", - "!type": "fn(floorId?: string)" + "!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" + 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())" + 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" + getBgNumber: { + "!doc": + "判定某点的背景层的数字
例如:core.getBgNumber(); // 判断主角脚下的背景层图块的数字
x: 横坐标,不填为勇士坐标
y: 纵坐标,不填为勇士坐标
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存而强制重算", + "!type": + "fn(x?: number, y?: number, floorId?: string, noCache?: bool) -> number", }, - "getIdOfThis": { + getIdOfThis: { "!doc": "获得当前事件点的ID", - "!type": "fn(id?: string) -> string" + "!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}]" + 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}]" + 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": { + hideBgFgMap: { "!doc": "隐藏前景/背景地图", - "!type": "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())" + "!type": + "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())", }, - "getBlockInfo": { - "!doc": "获得某个图块或素材的信息,包括ID,cls,图片,坐标,faceIds等等", - "!type": "fn(block?: number|string|block) -> blockInfo" + getBlockInfo: { + "!doc": + "获得某个图块或素材的信息,包括ID,cls,图片,坐标,faceIds等等", + "!type": "fn(block?: number|string|block) -> blockInfo", }, - "getFaceDownId": { - "!doc": "获得某个图块对应行走图朝向向下的那一项的id;如果不存在行走图绑定则返回自身id。", - "!type": "fn(block?: string|number|block) -> string" + getFaceDownId: { + "!doc": + "获得某个图块对应行走图朝向向下的那一项的id;如果不存在行走图绑定则返回自身id。", + "!type": "fn(block?: string|number|block) -> string", }, - "canMoveDirectlyArray": { + canMoveDirectlyArray: { "!doc": "获得某些点可否通行的信息", - "!type": "fn(locs?: [[number]])" + "!type": "fn(locs?: [[number]])", }, - "hideFloorImage": { + hideFloorImage: { "!doc": "隐藏一个楼层贴图", - "!type": "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())" + "!type": + "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())", }, - "extractBlocks": { + extractBlocks: { "!doc": "根据需求解析出blocks", - "!type": "fn(map?: ?)" + "!type": "fn(map?: ?)", }, - "extractBlocksForUI": { + extractBlocksForUI: { "!doc": "根据需求为UI解析出blocks", - "!type": "fn(map?: ?, flags?: ?)" + "!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" + 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" + getBlockNumber: { + "!doc": + "判定某个点的图块数字
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块数字,该点无图块则返回null", + "!type": + "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> number", }, - "getBlockOpacity": { + getBlockOpacity: { "!doc": "获得某个点图块的不透明度", - "!type": "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> number" + "!type": + "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> number", }, - "getBlockFilter": { + getBlockFilter: { "!doc": "获得某个点图块的特效", - "!type": "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> ?" + "!type": + "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> ?", }, - "loadFloor": { + loadFloor: { "!doc": "从文件或存档中加载某个楼层", - "!type": "fn(floorId?: string, map?: ?)" + "!type": "fn(floorId?: string, map?: ?)", }, - "generateMovableArray": { - "!doc": "可通行性判定
例如:core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动
floorId: 地图id,不填视为当前地图
返回值:从各点可移动方向的三维数组", - "!type": "fn(floorId?: string) -> [[[string]]]" + generateMovableArray: { + "!doc": + "可通行性判定
例如:core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动
floorId: 地图id,不填视为当前地图
返回值:从各点可移动方向的三维数组", + "!type": "fn(floorId?: string) -> [[[string]]]", }, - "terrainExists": { + terrainExists: { "!doc": "某个点是否存在(指定的)地形", - "!type": "fn(x: number, y: number, id?: string, floorId?: string) -> bool" + "!type": + "fn(x: number, y: number, id?: string, floorId?: string) -> bool", }, - "getBlockById": { + getBlockById: { "!doc": "根据ID获得图块", - "!type": "fn(id: string) -> block" + "!type": "fn(id: string) -> block", }, - "drawBg": { - "!doc": "绘制背景层(含贴图,其与背景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawBg(); // 绘制当前地图的背景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", - "!type": "fn(floorId?: string, ctx?: CanvasRenderingContext2D)" + 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)" + showBlock: { + "!doc": + "显示(隐藏或显示的)图块,此函数将被“显示事件”指令和勾选了“不消失”的“移动/跳跃事件”指令(如阻击怪)的终点调用
例如:core.showBlock(0, 0); // 显示地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + "!type": "fn(x: number, y: number, floorId?: string)", }, - "getMapBlocksObj": { + getMapBlocksObj: { "!doc": "以x,y的形式返回每个点的事件", - "!type": "fn(floorId?: string, noCache?: bool)" + "!type": "fn(floorId?: string, noCache?: bool)", }, - "removeGlobalAnimate": { + removeGlobalAnimate: { "!doc": "删除一个或所有全局动画", - "!type": "fn(x?: number, y?: number, name?: string)" + "!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)" + 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" + canMoveDirectly: { + "!doc": + "能否瞬移到某点,并求出节约的步数。
例如:core.canMoveDirectly(0, 0); // 能否瞬移到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:正数表示节约的步数,-1表示不可瞬移", + "!type": "fn(destX: number, destY: number) -> number", }, - "saveMap": { + saveMap: { "!doc": "将当前地图重新变成数字,以便于存档", - "!type": "fn(floorId?: string)" + "!type": "fn(floorId?: string)", }, - "drawBoxAnimate": { + drawBoxAnimate: { "!doc": "绘制UI层的box动画", - "!type": "fn()" + "!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)" + 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)" + drawFg: { + "!doc": + "绘制前景层(含贴图,其与前景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawFg(); // 绘制当前地图的前景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", + "!type": "fn(floorId?: string, ctx?: CanvasRenderingContext2D)", }, - "getBlock": { + getBlock: { "!doc": "获得某个点的block", - "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> block" + "!type": + "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> block", }, - "initBlock": { + initBlock: { "!doc": "初始化一个图块", - "!type": "fn(x: number, y: number, id: string|number, addInfo?: bool, eventFloor?: ?) -> block" + "!type": + "fn(x: number, y: number, id: string|number, addInfo?: bool, eventFloor?: ?) -> block", }, - "addGlobalAnimate": { + addGlobalAnimate: { "!doc": "添加一个全局动画", - "!type": "fn(block?: block)" + "!type": "fn(block?: block)", }, - "animateBlock": { + animateBlock: { "!doc": "显示/隐藏某个块时的动画效果", - "!type": "fn(loc?: [number]|[[number]], type?: string|number, time?: number, callback?: fn())" + "!type": + "fn(loc?: [number]|[[number]], type?: string|number, time?: number, callback?: fn())", }, - "loadMap": { + loadMap: { "!doc": "将存档中的地图信息重新读取出来", - "!type": "fn(data?: ?, floorId?: string, flags?: ?)" + "!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)" + 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]]" + 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]]" + 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" + 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?: ?)" + drawThumbnail: { + "!doc": + "绘制缩略图
例如:core.drawThumbnail(); // 绘制当前地图的缩略图
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
options: 绘制信息,可选。可以增绘主角位置和朝向、采用不同于游戏中的主角行走图、增绘显伤、提供flags用于存读档,同时包含要绘制到的画布名或画布的ctx或还有其他信息,如起绘坐标、绘制大小、是否绘制全图、截取中心", + "!type": "fn(floorId?: string, blocks?: [block], options?: ?)", }, - "hideBlockByIndex": { + hideBlockByIndex: { "!doc": "根据图块的索引来隐藏图块", - "!type": "fn(index?: number, floorId?: string)" + "!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" + getNumberById: { + "!doc": + "根据图块id得到数字(地图矩阵中的值)
例如:core.getNumberById('yellowWall'); // 1
id: 图块id
返回值:图块的数字,定义在project\\maps.js(请注意和project\\icons.js中的“图块索引”相区分!)", + "!type": "fn(id: string) -> number", }, - "removeBlockByIndexes": { + removeBlockByIndexes: { "!doc": "一次性删除多个block", - "!type": "fn(indexes?: [number], floorId?: string)" + "!type": "fn(indexes?: [number], floorId?: string)", }, - "hideBlockByIndexes": { + hideBlockByIndexes: { "!doc": "一次性隐藏多个block", - "!type": "fn(indexes?: [number], floorId?: string)" + "!type": "fn(indexes?: [number], floorId?: string)", }, - "generateGroundPattern": { + generateGroundPattern: { "!doc": "生成groundPattern", - "!type": "fn(floorId?: string)" + "!type": "fn(floorId?: string)", }, - "showBgFgMap": { + showBgFgMap: { "!doc": "显示前景/背景地图", - "!type": "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())" + "!type": + "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())", }, - "showFloorImage": { + showFloorImage: { "!doc": "显示一个楼层贴图", - "!type": "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())" - } + "!type": + "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())", + }, }, - "ui": { - "!doc": "负责一切UI界面的绘制。主要包括三个部分:
- 设置某个画布的属性与在某个画布上绘制的相关API
- 具体的某个UI界面的绘制
- 动态创建画布相关的API", - "resizeCanvas": { + ui: { + "!doc": + "负责一切UI界面的绘制。主要包括三个部分:
- 设置某个画布的属性与在某个画布上绘制的相关API
- 具体的某个UI界面的绘制
- 动态创建画布相关的API", + resizeCanvas: { "!doc": "重新设置一个自定义画布的大小", - "!type": "fn(name: string, x?: number, y?: number, styleOnly?: bool, isTempCanvas?: bool)" + "!type": + "fn(name: string, x?: number, y?: number, styleOnly?: bool, isTempCanvas?: bool)", }, - "deleteCanvas": { - "!doc": "删除一个自定义画布
name: 画布名,也可以传入一个函数对所有画布进行筛选", - "!type": "fn(name: string|fn(name: string) -> bool)" + deleteCanvas: { + "!doc": + "删除一个自定义画布
name: 画布名,也可以传入一个函数对所有画布进行筛选", + "!type": "fn(name: string|fn(name: string) -> bool)", }, - "deleteAllCanvas": { + deleteAllCanvas: { "!doc": "清空所有的自定义画布", - "!type": "fn()" + "!type": "fn()", }, - "drawIcon": { + drawIcon: { "!doc": "在某个canvas上绘制一个图标", - "!type": "fn(name: string|CanvasRenderingContext2D, id: string, x: number, y: number, w?: number, h?: number, frame?: number)" + "!type": + "fn(name: string|CanvasRenderingContext2D, id: string, x: number, y: number, w?: number, h?: number, frame?: number)", }, - "drawFly": { + drawFly: { "!doc": "绘制楼层传送器", - "!type": "fn(page?: ?)" + "!type": "fn(page?: ?)", }, - "setOpacity": { - "!doc": "设置某个canvas整体的透明度;此函数直接改变画布本身,对已经绘制的内容也生效
如果仅想对接下来的绘制生效请使用setAlpha", - "!type": "fn(name: string|CanvasRenderingContext2D, opacity: number)" + setOpacity: { + "!doc": + "设置某个canvas整体的透明度;此函数直接改变画布本身,对已经绘制的内容也生效
如果仅想对接下来的绘制生效请使用setAlpha", + "!type": "fn(name: string|CanvasRenderingContext2D, opacity: number)", }, - "getTextContentHeight": { + getTextContentHeight: { "!doc": "获得某段文字的预计绘制高度;参数说明详见 drawTextContent", - "!type": "fn(content: string, config?: ?)" + "!type": "fn(content: string, config?: ?)", }, - "drawArrow": { + drawArrow: { "!doc": "在某个canvas上绘制一个箭头", - "!type": "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)", }, - "strokeEllipse": { + strokeEllipse: { "!doc": "在某个canvas上绘制一个椭圆的边框", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string, lineWidth?: number)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string, lineWidth?: number)", }, - "fillCircle": { + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, style?: string)", }, - "strokeRoundRect": { + strokeRoundRect: { "!doc": "在某个canvas上绘制一个圆角矩形的边框", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, lineWidth?: number, angle?: number)" + "!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" + getContextByName: { + "!doc": + "根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。
也可以传画布的context自身,则返回自己。", + "!type": + "fn(canvas: string|CanvasRenderingContext2D) -> CanvasRenderingContext2D", }, - "drawImage": { - "!doc": "在一个画布上绘制图片
后面的8个坐标参数与canvas的drawImage的八个参数完全相同。
name: 可以是系统画布之一,也可以是任意自定义动态创建的画布名 画布名称或者画布的context
image: 要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取;支持加':x',':y',':o'翻转),图片本身,或者一个画布。
angle:旋转角度", + 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)" + "!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)" + drawTip: { + "!doc": + "左上角绘制一段提示
text: 要提示的字符串,支持${}语法
id: 要绘制的图标ID
frame: 要绘制该图标的第几帧", + "!type": "fn(text: string, id?: string, frame?: number)", }, - "drawBackground": { + drawBackground: { "!doc": "绘制一个背景图,可绘制winskin或纯色背景;支持小箭头绘制", - "!type": "fn(left: string, top: string, right: string, bottom: string, posInfo?: {px: number, py: number, direction: string})" + "!type": + "fn(left: string, top: string, right: string, bottom: string, posInfo?: {px: number, py: number, direction: string})", }, - "fillEllipse": { + fillEllipse: { "!doc": "在某个canvas上绘制一个椭圆", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string)", }, - "setFillStyle": { + setFillStyle: { "!doc": "设置某个canvas的绘制属性(如颜色等)", "!url": "https://www.w3school.com.cn/tags/canvas_fillstyle.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" + "!type": "fn(name: string|CanvasRenderingContext2D, style: string)", }, - "drawText": { + drawText: { "!doc": "地图中间绘制一段文字", - "!type": "fn(contents: string, callback?: fn())" + "!type": "fn(contents: string, callback?: fn())", }, - "drawConfirmBox": { - "!doc": "绘制一个确认框
此项会打断事件流,如需不打断版本的请使用core.myconfirm()
text: 要绘制的内容,支持 ${} 语法
yesCallback: 点击确认后的回调
noCallback: 点击取消后的回调", - "!type": "fn(text: string, yesCallback?: fn(), noCallback?: 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)" + 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])" + clearUIEventSelector: { + "!doc": + "清除若干个自绘的选择光标
codes: 清除的光标编号;可以是单个编号或编号数组;不填则清除所有光标", + "!type": "fn(codes?: number|[number])", }, - "fillPolygon": { + fillPolygon: { "!doc": "在某个canvas上绘制一个多边形", - "!type": "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string)" + "!type": + "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string)", }, - "fillText": { - "!doc": "在某个画布上绘制一段文字
text: 要绘制的文本
style: 绘制的样式
font: 绘制的字体
最大宽度,超过此宽度会自动放缩", + 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)" + "!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", + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, baseline: string)", }, - "loadCanvas": { + loadCanvas: { "!doc": "加载某个canvas状态", - "!type": "fn(name: string|CanvasRenderingContext2D)" + "!type": "fn(name: string|CanvasRenderingContext2D)", }, - "splitLines": { + splitLines: { "!doc": "字符串自动换行的分割", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth?: number, font?: string)" + "!type": + "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth?: number, font?: string)", }, - "setAlpha": { - "!doc": "设置某个canvas接下来绘制的不透明度;不会影响已经绘制的内容
返回设置之前画布的不透明度
如果需要修改画布本身的不透明度请使用setOpacity", + setAlpha: { + "!doc": + "设置某个canvas接下来绘制的不透明度;不会影响已经绘制的内容
返回设置之前画布的不透明度
如果需要修改画布本身的不透明度请使用setOpacity", "!url": "https://www.w3school.com.cn/tags/canvas_globalalpha.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, alpha: number) -> number" + "!type": + "fn(name: string|CanvasRenderingContext2D, alpha: number) -> number", }, - "setFilter": { + setFilter: { "!doc": "设置某个canvas接下来绘制的filter", - "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" + "!type": "fn(name: string|CanvasRenderingContext2D, style: string)", }, - "setLineWidth": { + setLineWidth: { "!doc": "设置某个canvas的线宽度", "!url": "https://www.w3school.com.cn/tags/canvas_linewidth.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, lineWidth: number)" + "!type": + "fn(name: string|CanvasRenderingContext2D, lineWidth: number)", }, - "drawTextBox": { + drawTextBox: { "!doc": "绘制一个对话框", - "!type": "fn(content: string, showAll?: bool)" + "!type": "fn(content: string, showAll?: bool)", }, - "relocateCanvas": { + relocateCanvas: { "!doc": "重新定位一个自定义画布", - "!type": "fn(name: string, x: number, y: number, useDelta: bool)" + "!type": "fn(name: string, x: number, y: number, useDelta: bool)", }, - "rotateCanvas": { - "!doc": "设置一个自定义画布的旋转角度
centerX, centerY: 旋转中心(以屏幕像素为基准);不填视为图片正中心。", - "!type": "fn(name: string, angle: number, centerX?: number, centerY?: number)" + rotateCanvas: { + "!doc": + "设置一个自定义画布的旋转角度
centerX, centerY: 旋转中心(以屏幕像素为基准);不填视为图片正中心。", + "!type": + "fn(name: string, angle: number, centerX?: number, centerY?: number)", }, - "closePanel": { + closePanel: { "!doc": "结束一切事件和绘制,关闭UI窗口,返回游戏进程", - "!type": "fn()" + "!type": "fn()", }, - "textImage": { + textImage: { "!doc": "文本图片化", - "!type": "fn(content: string, lineHeight?: number) -> image" + "!type": "fn(content: string, lineHeight?: number) -> image", }, - "drawStatusBar": { + drawStatusBar: { "!doc": "绘制状态栏", - "!type": "fn()" + "!type": "fn()", }, - "setStrokeStyle": { + setStrokeStyle: { "!doc": "设置某个canvas边框属性", "!url": "https://www.w3school.com.cn/tags/canvas_strokestyle.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" + "!type": "fn(name: string|CanvasRenderingContext2D, style: string)", }, - "clearUI": { + clearUI: { "!doc": "清空UI层内容", - "!type": "fn()" + "!type": "fn()", }, - "drawWindowSkin": { + drawWindowSkin: { "!doc": "绘制WindowSkin", - "!type": "fn(background: string, ctx: string|CanvasRenderingContext2D, x: number, y: number, w: string, h: string, direction?: string, px?: number, py?: number)" + "!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度", + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, angle?: number)", }, - "drawScrollText": { + drawScrollText: { "!doc": "绘制滚动字幕", - "!type": "fn(content: string, time: number, lineHeight?: number, callback?: fn())" + "!type": + "fn(content: string, time: number, lineHeight?: number, callback?: fn())", }, - "strokePolygon": { + strokePolygon: { "!doc": "在某个canvas上绘制一个多边形的边框", - "!type": "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string, lineWidth?: number)" + "!type": + "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string, lineWidth?: number)", }, - "strokeCircle": { + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: ?, style?: string, lineWidth?: number)", }, - "drawWaiting": { + drawWaiting: { "!doc": "绘制等待界面", - "!type": "fn(text: string)" + "!type": "fn(text: string)", }, - "setFont": { + setFont: { "!doc": "设置某个canvas的文字字体", "!url": "https://www.w3school.com.cn/tags/canvas_font.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, font: string)" + "!type": "fn(name: string|CanvasRenderingContext2D, font: string)", }, - "drawChoices": { + drawChoices: { "!doc": "绘制一个选项界面", - "!type": "fn(content?: string, choices?: [?], width?: number, ctx?: string|CanvasRenderingContext2D)" + "!type": + "fn(content?: string, choices?: [?], width?: number, ctx?: string|CanvasRenderingContext2D)", }, - "setFontForMaxWidth": { + setFontForMaxWidth: { "!doc": "根据最大宽度自动缩小字体", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth: number, font?: ?) -> string" + "!type": + "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth: number, font?: ?) -> string", }, - "clearMap": { - "!doc": "清空某个画布图层
name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。
如果name也可以是'all',若为all则为清空所有系统画布。", + 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)" + "!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: ?)" + 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": { + calWidth: { "!doc": "计算某段文字的宽度", "!url": "https://www.w3school.com.cn/tags/canvas_measuretext.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, text: string, font?: string) -> number" + "!type": + "fn(name: string|CanvasRenderingContext2D, text: string, font?: string) -> number", }, - "fillArc": { + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string)", }, - "strokeArc": { + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string, lineWidth?: number)", }, - "drawLine": { + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)", }, - "drawPagination": { + drawPagination: { "!doc": "绘制分页", - "!type": "fn(page?: ?, totalPage?: ?, y?: number)" + "!type": "fn(page?: ?, totalPage?: ?, y?: number)", }, - "getToolboxItems": { + getToolboxItems: { "!doc": "获得所有应该在道具栏显示的某个类型道具", - "!type": "fn(cls: string) -> [string]" + "!type": "fn(cls: string) -> [string]", }, - "strokeRect": { - "!doc": "绘制一个矩形的边框
style: 绘制的样式
lineWidth: 线宽
angle: 旋转角度,弧度制,如Math.PI/2为90度", + 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)" + "!type": + "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, style?: string, lineWidth?: number, angle?: number)", }, - "drawBook": { + drawBook: { "!doc": "绘制怪物手册", - "!type": "fn(index?: ?)" + "!type": "fn(index?: ?)", }, - "fillRoundRect": { + fillRoundRect: { "!doc": "在某个canvas上绘制一个圆角矩形", - "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, angle?: number)" + "!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)" + 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": { + saveCanvas: { "!doc": "保存某个canvas状态", - "!type": "fn(name: string|CanvasRenderingContext2D)" + "!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" + 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": { + setTextAlign: { "!doc": "设置某个canvas的对齐", "!url": "https://www.w3school.com.cn/tags/canvas_textalign.asp", - "!type": "fn(name: string|CanvasRenderingContext2D, align: string)" + "!type": "fn(name: string|CanvasRenderingContext2D, align: string)", }, }, - "enemys": { + enemys: { "!doc": "定义了一系列和怪物相关的API函数。", - "getEnemys": { - "!doc": "获得所有怪物原始数据的一个副本。
请使用core.material.enemys获得当前各项怪物属性。", - "!type": "fn()" + getEnemys: { + "!doc": + "获得所有怪物原始数据的一个副本。
请使用core.material.enemys获得当前各项怪物属性。", + "!type": "fn()", }, - "getEnemyValue": { + getEnemyValue: { "!doc": "获得某个点上怪物的某个属性值", - "!type": "fn(enemy?: string|enemy, name: string, x?: number, y?: number, floorId?: string)" + "!type": + "fn(enemy?: string|enemy, name: string, x?: number, y?: number, floorId?: string)", }, - "getSpecials": { + getSpecials: { "!doc": "获得所有特殊属性的定义", - "!type": "fn() -> [[?]]" + "!type": "fn() -> [[?]]", }, - "getSpecialColor": { + getSpecialColor: { "!doc": "获得某个怪物所有特殊属性的颜色", - "!type": "fn(enemy: string|enemy) -> [string]" + "!type": "fn(enemy: string|enemy) -> [string]", }, - "getSpecialFlag": { - "!doc": "获得某个怪物所有特殊属性的额外标记。

例如,1为全图性技能,需要进行遍历全图(光环/支援等)", - "!type": "fn(enemy: string|enemy) -> number" + 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" + 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]" + 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" + 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]]" + 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" + 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" + 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: [?]}" + 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}" + 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" + 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}" + 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]" + 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", }, - "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": { + events: { + "!doc": + "events.js将处理所有和事件相关的操作,主要分为五个部分:
- 游戏的开始和结束
- 系统事件的处理
- 自定义事件的处理
- 点击状态栏图标所进行的操作
- 一些具体事件的执行内容", + afterChangeFloor: { "!doc": "转换楼层结束的事件", - "!type": "fn(floorId?: string)" + "!type": "fn(floorId?: string)", }, - "popEventLoc": { + popEventLoc: { "!doc": "将当前点坐标入栈", - "!type": "fn()" + "!type": "fn()", }, - "afterOpenDoor": { + afterOpenDoor: { "!doc": "开一个门后触发的事件", - "!type": "fn(doorId?: string, x?: number, y?: number)" + "!type": "fn(doorId?: string, x?: number, y?: number)", }, - "checkLvUp": { + checkLvUp: { "!doc": "检查升级事件", - "!type": "fn()" + "!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)" + 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": { + unfollow: { "!doc": "取消跟随
name: 取消跟随的行走图,不填则取消全部跟随者", - "!type": "fn(name?: string)" + "!type": "fn(name?: string)", }, - "hasVisitedFloor": { + hasVisitedFloor: { "!doc": "是否到达过某个楼层", - "!type": "fn(floorId?: string) -> bool" + "!type": "fn(floorId?: string) -> bool", }, - "startEvents": { + startEvents: { "!doc": "开始执行一系列自定义事件", - "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())" + "!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)" + setHeroIcon: { + "!doc": + "更改主角行走图
例如:core.setHeroIcon('npc48.png', true); // 把主角从阳光变成样板0层左下角的小姐姐,但不立即刷新
name: 新的行走图文件名,可以是全塔属性中映射前的中文名。映射后会被存入core.status.hero.image
noDraw: true表示不立即刷新(刷新会导致大地图下视野重置到以主角为中心)", + "!type": "fn(name: string, noDraw?: bool)", }, - "changingFloor": { + changingFloor: { "!doc": "楼层转换中", - "!type": "fn(floorId?: string, heroLoc?: {x: number, y: number, direction: string})" + "!type": + "fn(floorId?: string, heroLoc?: {x: number, y: number, direction: string})", }, - "setEvents": { + setEvents: { "!doc": "直接设置事件列表", - "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())" + "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())", }, - "setValue": { + setValue: { "!doc": "数值操作", - "!type": "fn(name: string, operator: string, value: ?, prefix?: string)" + "!type": + "fn(name: string, operator: string, value: ?, prefix?: string)", }, - "precompile": { + precompile: { "!doc": "预编辑事件", - "!type": "fn(data?: ?)" + "!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())" + 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": { + confirmRestart: { "!doc": "询问是否需要重新开始", - "!type": "fn()" + "!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())" + 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)" + follow: { + "!doc": + "跟随
name: 要跟随的一个合法的4x4的行走图名称,需要在全塔属性注册", + "!type": "fn(name: string)", }, - "beforeBattle": { + beforeBattle: { "!doc": "战斗前触发的事件;返回false代表不进行战斗", - "!type": "fn(enemyId?: string, x?: number, y?: number) -> bool" + "!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))" + 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": { + flyTo: { "!doc": "飞往某一层", - "!type": "fn(toId?: string, callback?: fn()) -> bool" + "!type": "fn(toId?: string, callback?: fn()) -> bool", }, - "afterGetItem": { + afterGetItem: { "!doc": "获得一个道具后的事件", - "!type": "fn(id?: string, x?: number, y?: number, isGentleClick?: bool)" + "!type": + "fn(id?: string, x?: number, y?: number, isGentleClick?: bool)", }, - "doAction": { - "!doc": "执行下一个事件指令,常作为回调
例如:core.setCurtain([0,0,0,1], null, null, core.doAction); // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果", - "!type": "fn()" + doAction: { + "!doc": + "执行下一个事件指令,常作为回调
例如:core.setCurtain([0,0,0,1], null, null, core.doAction); // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果", + "!type": "fn()", }, - "openBook": { + openBook: { "!doc": "点击怪物手册时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", }, - "save": { + save: { "!doc": "点击存档按钮时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", }, - "load": { + load: { "!doc": "点击读档按钮时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", }, - "getNextItem": { - "!doc": "轻按获得面前的物品或周围唯一物品
noRoute: 若为true则不计入录像", - "!type": "fn(noRoute?: bool)" + getNextItem: { + "!doc": + "轻按获得面前的物品或周围唯一物品
noRoute: 若为true则不计入录像", + "!type": "fn(noRoute?: bool)", }, - "hasAsync": { + hasAsync: { "!doc": "当前是否有未处理完毕的异步事件(不包含动画和音效)", - "!type": "fn() -> bool" + "!type": "fn() -> bool", }, - "stopAsync": { + stopAsync: { "!doc": "立刻停止所有正在进行的异步事件", - "!type": "fn()" + "!type": "fn()", }, - "openEquipbox": { + openEquipbox: { "!doc": "点击装备栏时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", }, - "recoverEvents": { + recoverEvents: { "!doc": "恢复一个事件", - "!type": "fn(data?: ?)" + "!type": "fn(data?: ?)", }, - "setGlobalFlag": { - "!doc": "设置一个系统开关
例如:core.setGlobalFlag('steelDoorWithoutKey', true); // 使全塔的所有铁门都不再需要钥匙就能打开
name: 系统开关的英文名
value: 开关的新值,您可以用!core.flags[name]简单地表示将此开关反转", - "!type": "fn(name: string, value: bool)" + 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())" + 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())" + rotateImage: { + "!doc": + "旋转一张图片
code: 图片编号
center: 旋转中心像素坐标(以屏幕为基准);不填视为图片本身中心
angle: 旋转角度;正数为顺时针,负数为逆时针
moveMode: 旋转模式
time: 旋转用时,单位为毫秒。不填视为1秒
callback: 图片旋转完毕后的回调函数,可选", + "!type": + "fn(code: number, center?: [number], angle?: number, moveMode?: string, time?: number, callback?: fn())", }, - "scaleImage": { + scaleImage: { "!doc": "放缩一张图片", - "!type": "fn(code: number, center?: [number], scale?: number, moveMode?: string, time?: number, callback?: fn())" + "!type": + "fn(code: number, center?: [number], scale?: number, moveMode?: string, time?: number, callback?: fn())", }, - "moveTextBox": { + moveTextBox: { "!doc": "移动对话框", - "!type": "fn(code: number, loc: [number], relative?: bool, moveMode?: string, time?: number, callback?: fn())" + "!type": + "fn(code: number, loc: [number], relative?: bool, moveMode?: string, time?: number, callback?: fn())", }, - "clearTextBox": { + clearTextBox: { "!doc": "清除对话框", - "!type": "fn(code: number)" + "!type": "fn(code: number)", }, - "openSettings": { + openSettings: { "!doc": "点击设置按钮时的操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", }, - "afterPushBox": { + afterPushBox: { "!doc": "推箱子后的事件", - "!type": "fn()" + "!type": "fn()", }, - "unregisterSystemEvent": { + unregisterSystemEvent: { "!doc": "注销一个系统事件", - "!type": "fn(type: string)" + "!type": "fn(type: string)", }, - "trigger": { - "!doc": "触发(x,y)点的系统事件;会执行该点图块的script属性,同时支持战斗(会触发战后)、道具(会触发道具后)、楼层切换等等
callback: 执行完毕的回调函数
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", - "!type": "fn(x?: number, y?: number, callback?: fn())" + trigger: { + "!doc": + "触发(x,y)点的系统事件;会执行该点图块的script属性,同时支持战斗(会触发战后)、道具(会触发道具后)、楼层切换等等
callback: 执行完毕的回调函数
【异步脚本,请勿在脚本中直接调用(而是使用对应的事件),否则可能导致录像出错】", + "!type": "fn(x?: number, y?: number, callback?: fn())", }, - "restart": { + restart: { "!doc": "重新开始游戏;此函数将回到标题页面", - "!type": "fn()" + "!type": "fn()", }, - "doEvent": { + doEvent: { "!doc": "执行一个自定义事件", - "!type": "fn(data?: ?, x?: number, y?: number, prefix?: string)" + "!type": "fn(data?: ?, x?: number, y?: number, prefix?: string)", }, - "win": { + win: { "!doc": "游戏获胜事件", - "!type": "fn(reason?: string, norank?: bool, noexit?: bool)" + "!type": "fn(reason?: string, norank?: bool, noexit?: bool)", }, - "setGlobalAttribute": { + setGlobalAttribute: { "!doc": "设置全塔属性", - "!type": "fn(name: string, value: string)" + "!type": "fn(name: string, value: string)", }, - "setNameMap": { + setNameMap: { "!doc": "设置文件别名", - "!type": "fn(name: string, value?: string)" + "!type": "fn(name: string, value?: string)", }, - "setTextAttribute": { + setTextAttribute: { "!doc": "设置剧情文本的属性", - "!type": "fn(data: ?)" + "!type": "fn(data: ?)", }, - "openToolbox": { + openToolbox: { "!doc": "点击工具栏时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!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())" + 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": { + pushEventLoc: { "!doc": "将当前点坐标入栈", - "!type": "fn(x?: number, y?: number, floorId?: string) -> bool" + "!type": "fn(x?: number, y?: number, floorId?: string) -> bool", }, - "openKeyBoard": { + openKeyBoard: { "!doc": "点击虚拟键盘时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!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)" + 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())" + hideImage: { + "!doc": + "隐藏一张图片
例如:core.hideImage(1, 1000, core.jumpHero); // 1秒内淡出1号图片,然后主角原地跳跃半秒
code: 图片编号
time: 淡出时间,单位为毫秒
callback: 图片完全消失后的回调函数,可选", + "!type": "fn(code: number, time?: number, callback?: fn())", }, - "visitFloor": { + visitFloor: { "!doc": "到达某楼层", - "!type": "fn(floorId?: string)" + "!type": "fn(floorId?: string)", }, - "openQuickShop": { + openQuickShop: { "!doc": "点击快捷商店按钮时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", }, - "afterBattle": { + afterBattle: { "!doc": "战斗结束后触发的事件", - "!type": "fn(enemyId?: string, x?: number, y?: number)" + "!type": "fn(enemyId?: string, x?: number, y?: number)", }, - "pushBox": { + pushBox: { "!doc": "推箱子", - "!type": "fn(data?: ?)" + "!type": "fn(data?: ?)", }, - "autoEventExecuted": { + autoEventExecuted: { "!doc": "当前是否执行过某个自动事件", - "!type": "fn(symbol?: string, value?: ?) -> bool" + "!type": "fn(symbol?: string, value?: ?) -> bool", }, - "onSki": { + onSki: { "!doc": "当前是否在冰上", - "!type": "fn(number?: number) -> bool" + "!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())" + 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())" + 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()))" + 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())" + startGame: { + "!doc": + "开始新游戏
例如:core.startGame('咸鱼乱撞', 0, ''); // 开始一局咸鱼乱撞难度的新游戏,随机种子为0
hard: 难度名,会显示在左下角(横屏)或右下角(竖屏)
seed: 随机种子,相同的种子保证了录像的可重复性
route: 经由base64压缩后的录像,用于从头开始的录像回放
callback: 回调函数,可选", + "!type": + "fn(hard: string, seed: number, route: string, callback?: fn())", }, - "doSystemEvent": { + doSystemEvent: { "!doc": "执行一个系统事件", - "!type": "fn(type: string, data?: ?, callback?: fn())" + "!type": "fn(type: string, data?: ?, callback?: fn())", }, - "resetGame": { + resetGame: { "!doc": "初始化游戏", - "!type": "fn(hero?: ?, hard?: ?, floorId?: string, maps?: ?, values?: ?)" + "!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)" + 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())" + 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)" + 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)" + 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": { + resetEnemyOnPoint: { "!doc": "重置某个点的怪物属性", - "!type": "fn(x: number, y: number, floorId?: string)" + "!type": "fn(x: number, y: number, floorId?: string)", }, - "moveEnemyOnPoint": { + moveEnemyOnPoint: { "!doc": "将某个点已经设置的敌人属性移动到其他点", - "!type": "fn(fromX: number, fromY: number, toX: number, toY: number, floorId?: string)" + "!type": + "fn(fromX: number, fromY: number, toX: number, toY: number, floorId?: string)", }, - "autoEventExecuting": { + autoEventExecuting: { "!doc": "当前是否在执行某个自动事件", - "!type": "fn(symbol?: string, value?: ?) -> bool" + "!type": "fn(symbol?: string, value?: ?) -> bool", }, - "checkAutoEvents": { + checkAutoEvents: { "!doc": "检测自动事件", - "!type": "fn()" + "!type": "fn()", }, - "showGif": { - "!doc": "绘制一张动图或擦除所有动图
例如:core.showGif(); // 擦除所有动图
name: 动图文件名,可以是全塔属性中映射前的中文名
x: 动图在视野中的左上角横坐标
y: 动图在视野中的左上角纵坐标", - "!type": "fn(name?: string, x?: number, y?: number)" + showGif: { + "!doc": + "绘制一张动图或擦除所有动图
例如:core.showGif(); // 擦除所有动图
name: 动图文件名,可以是全塔属性中映射前的中文名
x: 动图在视野中的左上角横坐标
y: 动图在视野中的左上角纵坐标", + "!type": "fn(name?: string, x?: number, y?: number)", }, - "unregisterEvent": { + unregisterEvent: { "!doc": "注销一个自定义事件", - "!type": "fn(type: string)" + "!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())" + 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())" + 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())" + 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())" + 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": { + getCommonEvent: { "!doc": "获得一个公共事件", - "!type": "fn(name: string) -> [?]" + "!type": "fn(name: string) -> [?]", }, - "lose": { + lose: { "!doc": "游戏失败事件", - "!type": "fn(reason?: string)" + "!type": "fn(reason?: string)", }, - "gameOver": { - "!doc": "游戏结束
例如:core.gameOver(); // 游戏失败
ending: 结局名,省略表示失败
fromReplay: true表示在播放录像,可选
norank: true表示不计入榜单,可选", - "!type": "fn(ending?: string, fromReplay?: bool, norank?: bool)" + gameOver: { + "!doc": + "游戏结束
例如:core.gameOver(); // 游戏失败
ending: 结局名,省略表示失败
fromReplay: true表示在播放录像,可选
norank: true表示不计入榜单,可选", + "!type": "fn(ending?: string, fromReplay?: bool, norank?: bool)", }, - "useFly": { + useFly: { "!doc": "点击楼层传送器时的打开操作", - "!type": "fn(fromUserAction?: bool)" + "!type": "fn(fromUserAction?: bool)", + }, + tryUseItem: { + "!doc": + "尝试使用一个道具
例如:core.tryUseItem('pickaxe'); // 尝试使用破墙镐
itemId: 道具id,其中敌人手册、传送器和飞行器会被特殊处理", + "!type": "fn(itemId: string)", }, - "tryUseItem": { - "!doc": "尝试使用一个道具
例如:core.tryUseItem('pickaxe'); // 尝试使用破墙镐
itemId: 道具id,其中敌人手册、传送器和飞行器会被特殊处理", - "!type": "fn(itemId: string)" - } }, - "plugin": { + 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)" + 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)" + openShop: { + "!doc": + "打开一个全局商店
shopId: 要开启的商店ID
noRoute: 打开行为是否不计入录像", + "!type": "fn(shopId: string, noRoute?: bool)", }, - "isShopVisited": { + isShopVisited: { "!doc": "某个全局商店是否被访问过", - "!type": "fn(id: string) -> bool" + "!type": "fn(id: string) -> bool", }, - "listShopIds": { + listShopIds: { "!doc": "列出所有应当显示的快捷商店列表", - "!type": "fn() -> [string]" + "!type": "fn() -> [string]", }, - "canOpenShop": { + canOpenShop: { "!doc": "当前能否打开某个商店", - "!type": "fn(id: string) -> bool" + "!type": "fn(id: string) -> bool", }, - "setShopVisited": { + setShopVisited: { "!doc": "设置某个商店的访问状态", - "!type": "fn(id: string, visited?: bool)" + "!type": "fn(id: string, visited?: bool)", }, - "canUseQuickShop": { - "!doc": "当前能否使用某个快捷商店
如果返回一个字符串,则代表不能,返回的字符串作为不能的提示;返回null表示可以使用", - "!type": "fn(id: string) -> string" + 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)" + 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)" + resumeMaps: { + "!doc": + '恢复某一些被删除楼层。
fromId: 开始恢复的楼层ID
toId: 恢复到的楼层编号;可选,不填则视为fromId
例如:core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层
core.resumeMaps("MT10") 只删恢复MT10层', + "!type": "fn(fromId: string, toId?: string)", }, - "autoRemoveMaps": { + autoRemoveMaps: { "!doc": "根据楼层分区信息自动砍层与恢复", - "!type": "fn(floorId: string)" + "!type": "fn(floorId: string)", }, - "openItemShop": { + openItemShop: { "!doc": "打开一个道具商店", - "!type": "fn(itemShopId: string)" - } - } + "!type": "fn(itemShopId: string)", + }, + }, }, - "lzw_encode": { + lzw_encode: { "!doc": "LZW压缩算法", "!url": "https://gist.github.com/revolunet/843889", - "!type": "fn(s: string) -> string" + "!type": "fn(s: string) -> string", }, - "lzw_decode": { + lzw_decode: { "!doc": "LZW解压缩算法", "!url": "https://gist.github.com/revolunet/843889", - "!type": "fn(s: string) -> string" + "!type": "fn(s: string) -> string", }, - "hero": { + hero: { "!type": "heroStatus", "!doc": "勇士信息,为 core.status.hero 的简写", }, - "flags": { + flags: { "!type": "flag", "!doc": "游戏中用到的变量,为 core.status.hero.flags 的简写", - } - } -]; \ No newline at end of file + }, + }, +]; diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index fdb313d..c488ffd 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -567,7 +567,7 @@ doorInfo_m /* doorInfo_m tooltip : 开门信息 -default : [160, 'door.mp3', 'door.mp3'] +default : [160, 'door.opus', 'door.opus'] helpUrl : /_docs/#/instruction EvalString_0 = EvalString_0 && (', "openSound": "' + EvalString_0 + '"'); EvalString_1 = EvalString_1 && (', "closeSound": "' + EvalString_1 + '"'); @@ -4626,7 +4626,7 @@ IdString FixedId_List : '生命'|'生命上限'|'攻击'|'防御'|'法强'|'魔攻比例'|'护盾比例'|'黄钥匙'|'蓝钥匙'|'红钥匙'|'金币'|'经验'|'魔力'|'魔力上限'|'当前横坐标'|'当前纵坐标'|'当前朝向'|'攻击增益'|'防御增益'|'护盾增益' - /*FixedId_List ['status:hp','status:hpmax','status:atk','status:def','status:spell','status:matk','status:mdef','item:yellowKey','item:blueKey','item:redKey','status:money','status:exp','status:mana','status:manamax','status:x','status:y','status:direction','buff:atk','buff:def','buff:mdef']*/; + /*FixedId_List ['status:hp','status:hpmax','status:atk','status:def','status:spell','status:matk','status:mdef','item:yellowKey','item:blueKey','item:redKey','status:money','status:exp','status:mana','status:manamax','status:x','status:y','status:direction','buff:atk','buff:def','buff:mdef']*/; Id_List : '变量' | '状态' | '物品' | '增益' | '独立开关' | '临时变量' |'全局存储' diff --git a/_server/editor_blocklyconfig.js b/_server/editor_blocklyconfig.js index 6c99618..b0f038c 100644 --- a/_server/editor_blocklyconfig.js +++ b/_server/editor_blocklyconfig.js @@ -132,8 +132,8 @@ editor_blocklyconfig = function () { MotaActionFunctions.actionParser.parse( { time: 160, - openSound: "door.mp3", - closeSound: "door.mp3", + openSound: "door.opus", + closeSound: "door.opus", keys: { yellowKey: 1, orangeKey: 1 }, }, "doorInfo" @@ -142,9 +142,9 @@ editor_blocklyconfig = function () { MotaActionBlocks["mainStyle_m"].xmlText(), MotaActionFunctions.actionParser.parse( { - 背景音乐: "bgm.mp3", - 确定: "confirm.mp3", - 攻击: "attack.mp3", + 背景音乐: "bgm.opus", + 确定: "confirm.opus", + 攻击: "attack.opus", 背景图: "bg.jpg", 领域: "zone", 文件名: "file.jpg", diff --git a/project/animates/hand.animate b/project/animates/hand.animate index 09c9514..2c4a168 100644 --- a/project/animates/hand.animate +++ b/project/animates/hand.animate @@ -1 +1 @@ -{"ratio":2,"se":"attack.mp3","bitmaps":["data:image/png;base64,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","data:image/png;base64,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","data:image/png;base64,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","","","","","","",""],"frame_max":8,"frames":[[[0,0,0,30,120]],[[0,0,0,50,255]],[[0,0,0,70,255]],[[1,0,0,80,255]],[[1,0,0,90,255]],[[2,0,0,90,120]],[],[]]} \ No newline at end of file +{"ratio":2,"se":"attack.opus","bitmaps":["data:image/png;base64,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","data:image/png;base64,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","data:image/png;base64,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","","","","","","",""],"frame_max":8,"frames":[[[0,0,0,30,120]],[[0,0,0,50,255]],[[0,0,0,70,255]],[[1,0,0,80,255]],[[1,0,0,90,255]],[[2,0,0,90,120]],[],[]]} \ No newline at end of file diff --git a/project/data.js b/project/data.js index c458804..1a1c452 100644 --- a/project/data.js +++ b/project/data.js @@ -1136,7 +1136,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = }, "followers": [], "steps": 0, - "matk": 0 + "matk": 0, + "spell": null }, "startCanvas": [ { diff --git a/project/functions.js b/project/functions.js index 770d92d..36fc93e 100644 --- a/project/functions.js +++ b/project/functions.js @@ -2,259 +2,286 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { "events": { "resetGame": function (hero, hard, floorId, maps, values) { - // 重置整个游戏;此函数将在游戏开始时,或者每次读档时最先被调用 - // hero:勇士信息;hard:难度;floorId:当前楼层ID;maps:地图信息;values:全局数值信息 + // 重置整个游戏;此函数将在游戏开始时,或者每次读档时最先被调用 + // hero:勇士信息;hard:难度;floorId:当前楼层ID;maps:地图信息;values:全局数值信息 - // 清除游戏数据 - // 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布 - core.clearStatus(); - // 初始化status - core.status = core.clone(core.initStatus, function (name) { - return name != 'hero' && name != 'maps'; - }); - core.control._bindRoutePush(); - core.status.played = true; - // 初始化人物,图标,统计信息 - core.status.hero = core.clone(hero); - window.hero = core.status.hero; - window.flags = core.status.hero.flags; - core.events.setHeroIcon(core.status.hero.image, true); - core.control._initStatistics(core.animateFrame.totalTime); - core.status.hero.statistics.totalTime = core.animateFrame.totalTime = - Math.max(core.status.hero.statistics.totalTime, core.animateFrame.totalTime); - core.status.hero.statistics.start = null; - // 初始难度 - core.status.hard = hard || ""; - // 初始化地图 - core.status.floorId = floorId; - core.status.maps = maps; - core.maps._resetFloorImages(); - // 初始化怪物和道具 - core.material.enemys = core.enemys.getEnemys(); - core.material.items = core.items.getItems(); - // 初始化全局数值和全局开关 - core.values = core.clone(core.data.values); - for (var key in values || {}) - core.values[key] = values[key]; - core.flags = core.clone(core.data.flags); - var globalFlags = core.getFlag("globalFlags", {}); - for (var key in globalFlags) - core.flags[key] = globalFlags[key]; - core._init_sys_flags(); - // 初始化界面,状态栏等 - core.resize(); - // 状态栏是否显示 - if (core.hasFlag('hideStatusBar')) - core.hideStatusBar(core.hasFlag('showToolbox')); - else - core.showStatusBar(); - // 隐藏右下角的音乐按钮 - core.dom.musicBtn.style.display = 'none'; - }, + // 清除游戏数据 + // 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布 + core.clearStatus(); + // 初始化status + core.status = core.clone(core.initStatus, function (name) { + return name != "hero" && name != "maps"; + }); + core.control._bindRoutePush(); + core.status.played = true; + // 初始化人物,图标,统计信息 + core.status.hero = core.clone(hero); + window.hero = core.status.hero; + window.flags = core.status.hero.flags; + core.events.setHeroIcon(core.status.hero.image, true); + core.control._initStatistics(core.animateFrame.totalTime); + core.status.hero.statistics.totalTime = core.animateFrame.totalTime = + Math.max( + core.status.hero.statistics.totalTime, + core.animateFrame.totalTime + ); + core.status.hero.statistics.start = null; + // 初始难度 + core.status.hard = hard || ""; + // 初始化地图 + core.status.floorId = floorId; + core.status.maps = maps; + core.maps._resetFloorImages(); + // 初始化怪物和道具 + core.material.enemys = core.enemys.getEnemys(); + core.material.items = core.items.getItems(); + // 初始化全局数值和全局开关 + core.values = core.clone(core.data.values); + for (var key in values || {}) core.values[key] = values[key]; + core.flags = core.clone(core.data.flags); + var globalFlags = core.getFlag("globalFlags", {}); + for (var key in globalFlags) core.flags[key] = globalFlags[key]; + core._init_sys_flags(); + // 初始化界面,状态栏等 + core.resize(); + // 状态栏是否显示 + if (core.hasFlag("hideStatusBar")) + core.hideStatusBar(core.hasFlag("showToolbox")); + else core.showStatusBar(); + // 隐藏右下角的音乐按钮 + core.dom.musicBtn.style.display = "none"; + }, "win": function (reason, norank, noexit) { - // 游戏获胜事件 - // 请注意,成绩统计时是按照hp进行上传并排名 - // 可以先在这里对最终分数进行计算,比如将2倍攻击和5倍黄钥匙数量加到分数上 - // core.status.hero.hp += 2 * core.getRealStatus('atk') + 5 * core.itemCount('yellowKey'); + // 游戏获胜事件 + // 请注意,成绩统计时是按照hp进行上传并排名 + // 可以先在这里对最终分数进行计算,比如将2倍攻击和5倍黄钥匙数量加到分数上 + // core.status.hero.hp += 2 * core.getRealStatus('atk') + 5 * core.itemCount('yellowKey'); - // 如果不退出,则临时存储数据 - if (noexit) { - core.status.extraEvent = core.clone(core.status.event); - } + // 如果不退出,则临时存储数据 + if (noexit) { + core.status.extraEvent = core.clone(core.status.event); + } - // 游戏获胜事件 - core.ui.closePanel(); - var replaying = core.isReplaying(); - if (replaying) core.stopReplay(); - core.waitHeroToStop(function () { - if (!noexit) { - core.clearMap('all'); // 清空全地图 - core.deleteAllCanvas(); // 删除所有创建的画布 - core.dom.gif2.innerHTML = ""; - } - reason = core.replaceText(reason); - core.drawText([ - "\t[" + (reason || "恭喜通关") + "]你的分数是${status:hp}。" - ], function () { - core.events.gameOver(reason || '', replaying, norank); - }) - }); -}, + // 游戏获胜事件 + core.ui.closePanel(); + var replaying = core.isReplaying(); + if (replaying) core.stopReplay(); + core.waitHeroToStop(function () { + if (!noexit) { + core.clearMap("all"); // 清空全地图 + core.deleteAllCanvas(); // 删除所有创建的画布 + core.dom.gif2.innerHTML = ""; + } + reason = core.replaceText(reason); + core.drawText( + ["\t[" + (reason || "恭喜通关") + "]你的分数是${status:hp}。"], + function () { + core.events.gameOver(reason || "", replaying, norank); + } + ); + }); + }, "lose": function (reason) { - // 游戏失败事件 - core.ui.closePanel(); - var replaying = core.isReplaying(); - core.stopReplay(); - core.waitHeroToStop(function () { - core.drawText([ - "\t[" + (reason || "结局1") + "]你死了。\n如题。" - ], function () { - core.events.gameOver(null, replaying); - }); - }) - }, + // 游戏失败事件 + core.ui.closePanel(); + var replaying = core.isReplaying(); + core.stopReplay(); + core.waitHeroToStop(function () { + core.drawText( + ["\t[" + (reason || "结局1") + "]你死了。\n如题。"], + function () { + core.events.gameOver(null, replaying); + } + ); + }); + }, "changingFloor": function (floorId, heroLoc) { - // 正在切换楼层过程中执行的操作;此函数的执行时间是“屏幕完全变黑“的那一刻 - // floorId为要切换到的楼层ID;heroLoc表示勇士切换到的位置 + // 正在切换楼层过程中执行的操作;此函数的执行时间是“屏幕完全变黑“的那一刻 + // floorId为要切换到的楼层ID;heroLoc表示勇士切换到的位置 - // ---------- 此时还没有进行切换,当前floorId还是原来的 ---------- // - var currentId = core.status.floorId || null; // 获得当前的floorId,可能为null - var fromLoad = core.hasFlag('__fromLoad__'); // 是否是读档造成的切换 - var isFlying = core.hasFlag('__isFlying__'); // 是否是楼传造成的切换 - if (!fromLoad && !(isFlying && currentId == floorId)) { - if (!core.hasFlag("__leaveLoc__")) core.setFlag("__leaveLoc__", {}); - if (currentId != null) core.getFlag("__leaveLoc__")[currentId] = core.clone(core.status.hero.loc); - } + // ---------- 此时还没有进行切换,当前floorId还是原来的 ---------- // + var currentId = core.status.floorId || null; // 获得当前的floorId,可能为null + var fromLoad = core.hasFlag("__fromLoad__"); // 是否是读档造成的切换 + var isFlying = core.hasFlag("__isFlying__"); // 是否是楼传造成的切换 + if (!fromLoad && !(isFlying && currentId == floorId)) { + if (!core.hasFlag("__leaveLoc__")) core.setFlag("__leaveLoc__", {}); + if (currentId != null) + core.getFlag("__leaveLoc__")[currentId] = core.clone( + core.status.hero.loc + ); + } - // 可以对currentId进行判定,比如删除某些自定义图层等 - // if (currentId == 'MT0') { - // core.deleteAllCanvas(); - // } + // 可以对currentId进行判定,比如删除某些自定义图层等 + // if (currentId == 'MT0') { + // core.deleteAllCanvas(); + // } - // 根据分区信息自动砍层与恢复 - if (core.autoRemoveMaps) core.autoRemoveMaps(floorId); + // 根据分区信息自动砍层与恢复 + if (core.autoRemoveMaps) core.autoRemoveMaps(floorId); - // 重置画布尺寸 - core.maps.resizeMap(floorId); - // 设置勇士的位置 - heroLoc.direction = core.turnDirection(heroLoc.direction); - core.status.hero.loc = heroLoc; - // 检查重生怪并重置 - if (!fromLoad) { - core.extractBlocks(floorId); - core.status.maps[floorId].blocks.forEach(function (block) { - if (block.disable && core.enemys.hasSpecial(block.event.id, 23)) { - block.disable = false; - core.setMapBlockDisabled(floorId, block.x, block.y, false); - core.maps._updateMapArray(floorId, block.x, block.y); - } - }); - core.control.gatherFollowers(); - } + // 重置画布尺寸 + core.maps.resizeMap(floorId); + // 设置勇士的位置 + heroLoc.direction = core.turnDirection(heroLoc.direction); + core.status.hero.loc = heroLoc; + // 检查重生怪并重置 + if (!fromLoad) { + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + if (block.disable && core.enemys.hasSpecial(block.event.id, 23)) { + block.disable = false; + core.setMapBlockDisabled(floorId, block.x, block.y, false); + core.maps._updateMapArray(floorId, block.x, block.y); + } + }); + core.control.gatherFollowers(); + } - // ---------- 重绘新地图;这一步将会设置core.status.floorId ---------- // - core.drawMap(floorId); + // ---------- 重绘新地图;这一步将会设置core.status.floorId ---------- // + core.drawMap(floorId); - // 切换楼层BGM - if (core.status.maps[floorId].bgm) { - var bgm = core.status.maps[floorId].bgm; - if (bgm instanceof Array) bgm = bgm[Math.floor(Math.random() * bgm.length)]; // 多个bgm则随机播放一个 - if (!core.hasFlag("__bgm__")) core.playBgm(bgm); - } else if (fromLoad && !core.hasFlag("__bgm__")) { - core.pauseBgm(); - } - // 更改画面色调 - var color = core.getFlag('__color__', null); - if (!color && core.status.maps[floorId].color) - color = core.status.maps[floorId].color; - core.clearMap('curtain'); - core.status.curtainColor = color; - if (color) core.fillRect('curtain', 0, 0, core._PX_ || core.__PIXELS__, core._PY_ || core.__PIXELS__, core.arrayToRGBA(color)); - // 更改天气 - var weather = core.getFlag('__weather__', null); - if (!weather && core.status.maps[floorId].weather) - weather = core.status.maps[floorId].weather; - if (weather) - core.setWeather(weather[0], weather[1]); - else core.setWeather(); + // 切换楼层BGM + if (core.status.maps[floorId].bgm) { + var bgm = core.status.maps[floorId].bgm; + if (bgm instanceof Array) + bgm = bgm[Math.floor(Math.random() * bgm.length)]; // 多个bgm则随机播放一个 + if (!core.hasFlag("__bgm__")) core.playBgm(bgm); + } else if (fromLoad && !core.hasFlag("__bgm__")) { + core.pauseBgm(); + } + // 更改画面色调 + var color = core.getFlag("__color__", null); + if (!color && core.status.maps[floorId].color) + color = core.status.maps[floorId].color; + core.clearMap("curtain"); + core.status.curtainColor = color; + if (color) + core.fillRect( + "curtain", + 0, + 0, + core._PX_ || core.__PIXELS__, + core._PY_ || core.__PIXELS__, + core.arrayToRGBA(color) + ); + // 更改天气 + var weather = core.getFlag("__weather__", null); + if (!weather && core.status.maps[floorId].weather) + weather = core.status.maps[floorId].weather; + if (weather) core.setWeather(weather[0], weather[1]); + else core.setWeather(); - // ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等 - -}, + // ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等 + }, "afterChangeFloor": function (floorId) { - // 转换楼层结束的事件;此函数会在整个楼层切换完全结束后再执行 - // floorId是切换到的楼层 + // 转换楼层结束的事件;此函数会在整个楼层切换完全结束后再执行 + // floorId是切换到的楼层 - // 如果是读档,则进行检查(是否需要恢复事件) - if (core.hasFlag('__fromLoad__')) { - core.events.recoverEvents(core.getFlag("__events__")); - core.removeFlag("__events__"); - } else { - // 每次抵达楼层执行的事件 - core.insertAction(core.floors[floorId].eachArrive); - core.ui.statusBar._update_map() - // 首次抵达楼层时执行的事件(后插入,先执行) - if (!core.hasVisitedFloor(floorId)) { - core.insertAction(core.floors[floorId].firstArrive); - core.visitFloor(floorId); - core.plugin.bfs(); - } - } -}, + // 如果是读档,则进行检查(是否需要恢复事件) + if (core.hasFlag("__fromLoad__")) { + core.events.recoverEvents(core.getFlag("__events__")); + core.removeFlag("__events__"); + } else { + // 每次抵达楼层执行的事件 + core.insertAction(core.floors[floorId].eachArrive); + core.ui.statusBar._update_map(); + // 首次抵达楼层时执行的事件(后插入,先执行) + if (!core.hasVisitedFloor(floorId)) { + core.insertAction(core.floors[floorId].firstArrive); + core.visitFloor(floorId); + core.plugin.bfs(); + } + } + }, "flyTo": function (toId, callback) { - // 楼层传送器的使用,从当前楼层飞往toId - // 如果不能飞行请返回false + // 楼层传送器的使用,从当前楼层飞往toId + // 如果不能飞行请返回false - var fromId = core.status.floorId; + var fromId = core.status.floorId; - // 检查能否飞行 - if (!core.status.maps[fromId].canFlyFrom || !core.status.maps[toId].canFlyTo || !core.hasVisitedFloor(toId)) { - core.playSound('操作失败'); - core.drawTip("无法飞往" + core.status.maps[toId].title + "!", 'fly'); - return false; - } + // 检查能否飞行 + if ( + !core.status.maps[fromId].canFlyFrom || + !core.status.maps[toId].canFlyTo || + !core.hasVisitedFloor(toId) + ) { + core.playSound("操作失败"); + core.drawTip("无法飞往" + core.status.maps[toId].title + "!", "fly"); + return false; + } - // 平面塔模式 - var stair = null, - loc = null; - if (core.flags.flyRecordPosition) { - loc = core.getFlag("__leaveLoc__", {})[toId] || null; - } - if (core.status.maps[toId].flyPoint != null && core.status.maps[toId].flyPoint.length == 2) { - stair = 'flyPoint'; - } - if (stair == null && loc == null) { - // 获得两个楼层的索引,以决定是上楼梯还是下楼梯 - var fromIndex = core.floorIds.indexOf(fromId), - toIndex = core.floorIds.indexOf(toId); - var stair = fromIndex <= toIndex ? "downFloor" : "upFloor"; - // 地下层:同层传送至上楼梯 - if (fromIndex == toIndex && core.status.maps[fromId].underGround) stair = "upFloor"; - } + // 平面塔模式 + var stair = null, + loc = null; + if (core.flags.flyRecordPosition) { + loc = core.getFlag("__leaveLoc__", {})[toId] || null; + } + if ( + core.status.maps[toId].flyPoint != null && + core.status.maps[toId].flyPoint.length == 2 + ) { + stair = "flyPoint"; + } + if (stair == null && loc == null) { + // 获得两个楼层的索引,以决定是上楼梯还是下楼梯 + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + var stair = fromIndex <= toIndex ? "downFloor" : "upFloor"; + // 地下层:同层传送至上楼梯 + if (fromIndex == toIndex && core.status.maps[fromId].underGround) + stair = "upFloor"; + } - // 记录录像 - core.status.route.push("fly:" + toId); - // 传送 - core.ui.closePanel(); - core.setFlag('__isFlying__', true); - core.changeFloor(toId, stair, loc, null, function () { - core.removeFlag("__isFlying__"); - if (callback) callback(); - }); + // 记录录像 + core.status.route.push("fly:" + toId); + // 传送 + core.ui.closePanel(); + core.setFlag("__isFlying__", true); + core.changeFloor(toId, stair, loc, null, function () { + core.removeFlag("__isFlying__"); + if (callback) callback(); + }); - return true; -}, + return true; + }, "beforeBattle": function (enemyId, x, y) { - // 战斗前触发的事件,可以加上一些战前特效(详见下面支援的例子) - // 此函数在“检测能否战斗和自动存档”【之后】执行。如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 - // 返回true则将继续战斗,返回false将不再战斗。 + // 战斗前触发的事件,可以加上一些战前特效(详见下面支援的例子) + // 此函数在“检测能否战斗和自动存档”【之后】执行。如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 + // 返回true则将继续战斗,返回false将不再战斗。 - // ------ 支援技能 ------ // - if (x != null && y != null) { - var index = x + "," + y, - cache = core.status.checkBlock.cache[index] || {}, - guards = cache.guards || []; - // 如果存在支援怪 - if (guards.length > 0) { - // 记录flag,当前要参与支援的怪物 - core.setFlag("__guards__" + x + "_" + y, guards); - var actions = [{ "type": "playSound", "name": "跳跃" }]; - // 增加支援的特效动画(图块跳跃) - guards.forEach(function (g) { - core.push(actions, { "type": "jump", "from": [g[0], g[1]], "to": [x, y], "time": 300, "keep": false, "async": true }); - }); - core.push(actions, [ - { "type": "waitAsync" }, // 等待所有异步事件执行完毕 - { "type": "setBlock", "number": enemyId, "loc": [[x, y]] }, // 重新设置怪物自身 - { "type": "battle", "loc": [x, y] } // 重要!重新触发本次战斗 - ]); - core.insertAction(actions); - return false; - } - } + // ------ 支援技能 ------ // + if (x != null && y != null) { + var index = x + "," + y, + cache = core.status.checkBlock.cache[index] || {}, + guards = cache.guards || []; + // 如果存在支援怪 + if (guards.length > 0) { + // 记录flag,当前要参与支援的怪物 + core.setFlag("__guards__" + x + "_" + y, guards); + var actions = [{ type: "playSound", name: "跳跃" }]; + // 增加支援的特效动画(图块跳跃) + guards.forEach(function (g) { + core.push(actions, { + type: "jump", + from: [g[0], g[1]], + to: [x, y], + time: 300, + keep: false, + async: true, + }); + }); + core.push(actions, [ + { type: "waitAsync" }, // 等待所有异步事件执行完毕 + { type: "setBlock", number: enemyId, loc: [[x, y]] }, // 重新设置怪物自身 + { type: "battle", loc: [x, y] }, // 重要!重新触发本次战斗 + ]); + core.insertAction(actions); + return false; + } + } - return true; - }, + return true; + }, "afterBattle": function (enemyId, x, y) { // 战斗结束后触发的事件 @@ -263,7 +290,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 播放战斗音效和动画 // 默认播放的动画;你也可以使用 - var animate = 'hand'; // 默认动画 + var animate = "hand"; // 默认动画 // 检查当前装备是否存在攻击动画 var equipId = core.getEquip(0); if (equipId && (core.material.items[equipId].equip || {}).animate) @@ -273,13 +300,11 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 检查该动画是否存在SE,如果不存在则使用默认音效 if (!(core.material.animates[animate] || {}).se) - core.playSound('attack.mp3'); + core.playSound("attack.opus"); // 播放动画;如果不存在坐标(强制战斗)则播放到勇士自身 - if (x != null && y != null) - core.drawAnimate(animate, x, y); - else - core.drawHeroAnimate(animate); + if (x != null && y != null) core.drawAnimate(animate, x, y); + else core.drawHeroAnimate(animate); // 获得战斗伤害信息 var damageInfo = core.getDamageInfo(enemyId, null, x, y) || {}; @@ -291,7 +316,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (damage == null || damage >= core.status.hero.hp) { core.status.hero.hp = 0; core.updateStatusBar(false, true); - core.events.lose('战斗失败'); + core.events.lose("战斗失败"); return; } @@ -311,8 +336,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = var money = guards.reduce(function (curr, g) { return curr + core.material.enemys[g[2]].money; }, core.getEnemyValue(enemy, "money", x, y)); - if (core.hasItem('coin')) money *= 2; // 幸运金币:双倍 - if (core.hasFlag('curse')) money = 0; // 诅咒效果 + if (core.hasItem("coin")) money *= 2; // 幸运金币:双倍 + if (core.hasFlag("curse")) money = 0; // 诅咒效果 core.status.hero.money += money; core.status.hero.statistics.money += money; @@ -320,32 +345,32 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = var exp = guards.reduce(function (curr, g) { return curr + core.material.enemys[g[2]].exp; }, core.getEnemyValue(enemy, "exp", x, y)); - if (core.hasFlag('curse')) exp = 0; + if (core.hasFlag("curse")) exp = 0; core.status.hero.exp += exp; core.status.hero.statistics.exp += exp; var hint = "打败 " + core.getEnemyValue(enemy, "name", x, y); - if (core.flags.statusBarItems.indexOf('enableMoney') >= 0) - hint += ',' + core.getStatusLabel('money') + '+' + money; // hint += ",金币+" + money; - if (core.flags.statusBarItems.indexOf('enableExp') >= 0) - hint += ',' + core.getStatusLabel('exp') + '+' + exp; // hint += ",经验+" + exp; + if (core.flags.statusBarItems.indexOf("enableMoney") >= 0) + hint += "," + core.getStatusLabel("money") + "+" + money; // hint += ",金币+" + money; + if (core.flags.statusBarItems.indexOf("enableExp") >= 0) + hint += "," + core.getStatusLabel("exp") + "+" + exp; // hint += ",经验+" + exp; core.drawTip(hint, enemy.id); // 中毒 if (core.enemys.hasSpecial(special, 12)) { - core.triggerDebuff('get', 'poison'); + core.triggerDebuff("get", "poison"); } // 衰弱 if (core.enemys.hasSpecial(special, 13)) { - core.triggerDebuff('get', 'weak'); + core.triggerDebuff("get", "weak"); } // 诅咒 if (core.enemys.hasSpecial(special, 14)) { - core.triggerDebuff('get', 'curse'); + core.triggerDebuff("get", "curse"); } // 仇恨怪物将仇恨值减半 if (core.enemys.hasSpecial(special, 17)) { - core.setFlag('hatred', Math.floor(core.getFlag('hatred', 0) / 2)); + core.setFlag("hatred", Math.floor(core.getFlag("hatred", 0) / 2)); } // 自爆 if (core.enemys.hasSpecial(special, 19)) { @@ -354,41 +379,45 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } // 退化 if (core.enemys.hasSpecial(special, 21)) { - core.status.hero.atk -= (enemy.atkValue || 0); - core.status.hero.def -= (enemy.defValue || 0); + core.status.hero.atk -= enemy.atkValue || 0; + core.status.hero.def -= enemy.defValue || 0; if (core.status.hero.atk < 0) core.status.hero.atk = 0; if (core.status.hero.def < 0) core.status.hero.def = 0; } // 增加仇恨值 - core.setFlag('hatred', core.getFlag('hatred', 0) + core.values.hatred); + core.setFlag("hatred", core.getFlag("hatred", 0) + core.values.hatred); // 战后的技能处理,比如扣除魔力值 - if (core.flags.statusBarItems.indexOf('enableSkill') >= 0) { + if (core.flags.statusBarItems.indexOf("enableSkill") >= 0) { // 检测当前开启的技能类型 - var skill = core.getFlag('skill', 0); - if (skill == 1) { // 技能1:二倍斩 + var skill = core.getFlag("skill", 0); + if (skill == 1) { + // 技能1:二倍斩 core.status.hero.mana -= 5; // 扣除5点魔力值 } // 关闭技能 - core.setFlag('skill', 0); - core.setFlag('skillName', '无'); + core.setFlag("skill", 0); + core.setFlag("skillName", "无"); } - // 事件的处理 var todo = []; // 加点事件 - var point = guards.reduce(function (curr, g) { - return curr + core.material.enemys[g[2]].point; - }, core.getEnemyValue(enemy, "point", x, y)) || 0; + var point = + guards.reduce(function (curr, g) { + return curr + core.material.enemys[g[2]].point; + }, core.getEnemyValue(enemy, "point", x, y)) || 0; if (core.flags.enableAddPoint && point > 0) { - core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]); + core.push(todo, [{ type: "insert", name: "加点事件", args: [point] }]); } // 战后事件 if (core.status.floorId != null) { - core.push(todo, core.floors[core.status.floorId].afterBattle[x + "," + y]); + core.push( + todo, + core.floors[core.status.floorId].afterBattle[x + "," + y] + ); } core.push(todo, enemy.afterBattle); @@ -405,7 +434,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (todo.length > 0) core.insertAction(todo, x, y); // 删除该点设置的怪物信息 - delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[x + "," + y]; + delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[ + x + "," + y + ]; // 因为removeBlock和hideBlock都会刷新状态栏,因此将删除部分移动到这里并保证刷新只执行一次,以提升效率 if (core.getBlock(x, y) != null) { @@ -420,382 +451,612 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } // 如果已有事件正在处理中 - if (core.status.event.id == null) - core.continueAutomaticRoute(); - else - core.clearContinueAutomaticRoute(); - + if (core.status.event.id == null) core.continueAutomaticRoute(); + else core.clearContinueAutomaticRoute(); }, "afterOpenDoor": function (doorId, x, y) { - // 开一个门后触发的事件 + // 开一个门后触发的事件 - var todo = []; - // 检查该点的开门后事件 - if (core.status.floorId) { - core.push(todo, core.floors[core.status.floorId].afterOpenDoor[x + "," + y]); - } - // 检查批量开门事件 - var door = core.getBlockById(doorId); - if (door && door.event.doorInfo) { - core.push(todo, door.event.doorInfo.afterOpenDoor); - } + var todo = []; + // 检查该点的开门后事件 + if (core.status.floorId) { + core.push( + todo, + core.floors[core.status.floorId].afterOpenDoor[x + "," + y] + ); + } + // 检查批量开门事件 + var door = core.getBlockById(doorId); + if (door && door.event.doorInfo) { + core.push(todo, door.event.doorInfo.afterOpenDoor); + } - if (todo.length > 0) core.insertAction(todo, x, y); + if (todo.length > 0) core.insertAction(todo, x, y); - if (core.status.event.id == null) - core.continueAutomaticRoute(); - else - core.clearContinueAutomaticRoute(); - }, + if (core.status.event.id == null) core.continueAutomaticRoute(); + else core.clearContinueAutomaticRoute(); + }, "afterGetItem": function (itemId, x, y, isGentleClick) { - // 获得一个道具后触发的事件 - // itemId:获得的道具ID;x和y是该道具所在的坐标 - // isGentleClick:是否是轻按触发的 - if (itemId.endsWith('Potion') && core.material.items[itemId].cls == 'items') - core.playSound('回血'); - else if (itemId.endsWith('Gem') && core.material.items[itemId].cls == 'items') - core.playSound('宝石') - else - core.playSound('获得道具'); + // 获得一个道具后触发的事件 + // itemId:获得的道具ID;x和y是该道具所在的坐标 + // isGentleClick:是否是轻按触发的 + if ( + itemId.endsWith("Potion") && + core.material.items[itemId].cls == "items" + ) + core.playSound("回血"); + else if ( + itemId.endsWith("Gem") && + core.material.items[itemId].cls == "items" + ) + core.playSound("宝石"); + else core.playSound("获得道具"); - var todo = []; - // 检查该点的获得道具后事件。 - if (core.status.floorId == null) return; - var event = core.floors[core.status.floorId].afterGetItem[x + "," + y]; - if (event && (event instanceof Array || !isGentleClick || !event.disableOnGentleClick)) { - if (event.data) event = event.data; - core.unshift(todo, event); - } + var todo = []; + // 检查该点的获得道具后事件。 + if (core.status.floorId == null) return; + var event = core.floors[core.status.floorId].afterGetItem[x + "," + y]; + if ( + event && + (event instanceof Array || + !isGentleClick || + !event.disableOnGentleClick) + ) { + if (event.data) event = event.data; + core.unshift(todo, event); + } - if (todo.length > 0) core.insertAction(todo, x, y); - }, + if (todo.length > 0) core.insertAction(todo, x, y); + }, "afterPushBox": function () { - // 推箱子后的事件 - if (core.searchBlock('box').length == 0) { - // 可以通过if语句来进行开门操作 - /* + // 推箱子后的事件 + if (core.searchBlock("box").length == 0) { + // 可以通过if语句来进行开门操作 + /* if (core.status.floorId=='xxx') { // 在某个楼层 core.insertAction([ // 插入一条事件 {"type": "openDoor", "loc": [x,y]} // 开门 ]) } */ - } - } + } + } }, "enemys": { "getSpecials": function () { - // 获得怪物的特殊属性,每一行定义一个特殊属性。 - // 分为五项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 - // 第四项为该特殊属性的颜色,可以写十六进制 #RRGGBB 或者 [r,g,b,a] 四元数组 - // 第五项为该特殊属性的标记;目前 1 代表是地图类技能(需要进行遍历全图) - // 名字和描述可以直接写字符串,也可以写个function将怪物传进去 - return [ - [1, "先攻", "怪物首先攻击", "#ffcc33"], - [3, "坚固", "怪物防御不小于角色攻击-1", "#c0b088"], - [6, function (enemy) { return (enemy.n || '') + "连击"; }, function (enemy) { return "怪物每回合攻击" + (enemy.n || 4) + "次"; }, "#ffee77"], - [7, "破甲", function (enemy) { return "战斗前,怪物附加角色防御的" + Math.floor(100 * (enemy.breakArmor || core.values.breakArmor || 0)) + "%作为伤害"; }, "#88c0ff"], - [8, "反击", function (enemy) { return "战斗时,怪物每回合附加角色攻击的" + Math.floor(100 * (enemy.counterAttack || core.values.counterAttack || 0)) + "%作为伤害,无视角色防御"; }, "#ffaa44"], - [9, "净化", function (enemy) { return "战斗前,怪物附加角色护盾的" + (enemy.purify || core.values.purify) + "倍作为伤害"; }, "#80eed6"], - [10, "模仿", "怪物的攻防和角色攻防相等", "#b0c0dd"], - [11, "吸血", function (enemy) { return "战斗前,怪物首先吸取角色的" + Math.floor(100 * enemy.vampire || 0) + "%生命(约" + Math.floor((enemy.vampire || 0) * core.getStatus('hp')) + "点)作为伤害" + (enemy.add ? ",并把伤害数值加到自身生命上" : ""); }, "#dd4448"], - [12, "中毒", "战斗后,角色陷入中毒状态,每一步损失生命" + core.values.poisonDamage + "点", "#99ee88"], - [13, "衰弱", "战斗后,角色陷入衰弱状态,攻防暂时下降" + (core.values.weakValue >= 1 ? core.values.weakValue + "点" : parseInt(core.values.weakValue * 100) + "%"), "#f0bbcc"], - [14, "诅咒", "战斗后,角色陷入诅咒状态,战斗无法获得金币和经验", "#bbeef0"], - [15, "领域", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "范围内" + (enemy.range || 1) + "格时自动减生命" + (enemy.zone || 0) + "点"; }, "#c677dd"], - [16, "夹击", "经过两只相同的怪物中间,角色生命值变成一半", "#bb99ee"], - [17, "仇恨", "战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得" + (core.values.hatred || 0) + "点仇恨值)", "#b0b666"], - [18, "阻击", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "时自动减生命" + (enemy.repulse || 0) + "点,同时怪物后退一格"; }, "#8888e6"], - [19, "自爆", "战斗后角色的生命值变成1", "#ff6666"], - [20, "无敌", "角色无法打败怪物,除非拥有十字架", "#aaaaaa"], - [21, "退化", function (enemy) { return "战斗后角色永久下降" + (enemy.atkValue || 0) + "点攻击和" + (enemy.defValue || 0) + "点防御"; }], - [22, "固伤", function (enemy) { return "战斗前,怪物对角色造成" + (enemy.damage || 0) + "点固定伤害,未开启负伤时无视角色护盾。"; }, "#ff9977"], - [23, "重生", "怪物被击败后,角色转换楼层则怪物将再次出现", "#a0e0ff"], - [24, "激光", function (enemy) { return "经过怪物同行或同列时自动减生命" + (enemy.laser || 0) + "点"; }, "#dda0dd"], - [25, "光环", function (enemy) { return (enemy.range != null ? ((enemy.haloSquare ? "该怪物九宫格" : "该怪物十字") + enemy.haloRange + "格范围内") : "同楼层所有") + "怪物生命提升" + (enemy.hpBuff || 0) + "%,攻击提升" + (enemy.atkBuff || 0) + "%,防御提升" + (enemy.defBuff || 0) + "%," + (enemy.haloAdd ? "可叠加" : "不可叠加"); }, "#e6e099", 1], - [26, "支援", "当周围一圈的怪物受到攻击时将上前支援,并组成小队战斗。", "#77c0b6", 1], - [27, "捕捉", function (enemy) { return "当走到怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "时会强制进行战斗。"; }, "#c0ddbb"] - ]; -}, + // 获得怪物的特殊属性,每一行定义一个特殊属性。 + // 分为五项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 + // 第四项为该特殊属性的颜色,可以写十六进制 #RRGGBB 或者 [r,g,b,a] 四元数组 + // 第五项为该特殊属性的标记;目前 1 代表是地图类技能(需要进行遍历全图) + // 名字和描述可以直接写字符串,也可以写个function将怪物传进去 + return [ + [1, "先攻", "怪物首先攻击", "#ffcc33"], + [3, "坚固", "怪物防御不小于角色攻击-1", "#c0b088"], + [ + 6, + function (enemy) { + return (enemy.n || "") + "连击"; + }, + function (enemy) { + return "怪物每回合攻击" + (enemy.n || 4) + "次"; + }, + "#ffee77", + ], + [ + 7, + "破甲", + function (enemy) { + return ( + "战斗前,怪物附加角色防御的" + + Math.floor( + 100 * (enemy.breakArmor || core.values.breakArmor || 0) + ) + + "%作为伤害" + ); + }, + "#88c0ff", + ], + [ + 8, + "反击", + function (enemy) { + return ( + "战斗时,怪物每回合附加角色攻击的" + + Math.floor( + 100 * (enemy.counterAttack || core.values.counterAttack || 0) + ) + + "%作为伤害,无视角色防御" + ); + }, + "#ffaa44", + ], + [ + 9, + "净化", + function (enemy) { + return ( + "战斗前,怪物附加角色护盾的" + + (enemy.purify || core.values.purify) + + "倍作为伤害" + ); + }, + "#80eed6", + ], + [10, "模仿", "怪物的攻防和角色攻防相等", "#b0c0dd"], + [ + 11, + "吸血", + function (enemy) { + return ( + "战斗前,怪物首先吸取角色的" + + Math.floor(100 * enemy.vampire || 0) + + "%生命(约" + + Math.floor((enemy.vampire || 0) * core.getStatus("hp")) + + "点)作为伤害" + + (enemy.add ? ",并把伤害数值加到自身生命上" : "") + ); + }, + "#dd4448", + ], + [ + 12, + "中毒", + "战斗后,角色陷入中毒状态,每一步损失生命" + + core.values.poisonDamage + + "点", + "#99ee88", + ], + [ + 13, + "衰弱", + "战斗后,角色陷入衰弱状态,攻防暂时下降" + + (core.values.weakValue >= 1 + ? core.values.weakValue + "点" + : parseInt(core.values.weakValue * 100) + "%"), + "#f0bbcc", + ], + [ + 14, + "诅咒", + "战斗后,角色陷入诅咒状态,战斗无法获得金币和经验", + "#bbeef0", + ], + [ + 15, + "领域", + function (enemy) { + return ( + "经过怪物周围" + + (enemy.zoneSquare ? "九宫格" : "十字") + + "范围内" + + (enemy.range || 1) + + "格时自动减生命" + + (enemy.zone || 0) + + "点" + ); + }, + "#c677dd", + ], + [16, "夹击", "经过两只相同的怪物中间,角色生命值变成一半", "#bb99ee"], + [ + 17, + "仇恨", + "战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得" + + (core.values.hatred || 0) + + "点仇恨值)", + "#b0b666", + ], + [ + 18, + "阻击", + function (enemy) { + return ( + "经过怪物周围" + + (enemy.zoneSquare ? "九宫格" : "十字") + + "时自动减生命" + + (enemy.repulse || 0) + + "点,同时怪物后退一格" + ); + }, + "#8888e6", + ], + [19, "自爆", "战斗后角色的生命值变成1", "#ff6666"], + [20, "无敌", "角色无法打败怪物,除非拥有十字架", "#aaaaaa"], + [ + 21, + "退化", + function (enemy) { + return ( + "战斗后角色永久下降" + + (enemy.atkValue || 0) + + "点攻击和" + + (enemy.defValue || 0) + + "点防御" + ); + }, + ], + [ + 22, + "固伤", + function (enemy) { + return ( + "战斗前,怪物对角色造成" + + (enemy.damage || 0) + + "点固定伤害,未开启负伤时无视角色护盾。" + ); + }, + "#ff9977", + ], + [23, "重生", "怪物被击败后,角色转换楼层则怪物将再次出现", "#a0e0ff"], + [ + 24, + "激光", + function (enemy) { + return "经过怪物同行或同列时自动减生命" + (enemy.laser || 0) + "点"; + }, + "#dda0dd", + ], + [ + 25, + "光环", + function (enemy) { + return ( + (enemy.range != null + ? (enemy.haloSquare ? "该怪物九宫格" : "该怪物十字") + + enemy.haloRange + + "格范围内" + : "同楼层所有") + + "怪物生命提升" + + (enemy.hpBuff || 0) + + "%,攻击提升" + + (enemy.atkBuff || 0) + + "%,防御提升" + + (enemy.defBuff || 0) + + "%," + + (enemy.haloAdd ? "可叠加" : "不可叠加") + ); + }, + "#e6e099", + 1, + ], + [ + 26, + "支援", + "当周围一圈的怪物受到攻击时将上前支援,并组成小队战斗。", + "#77c0b6", + 1, + ], + [ + 27, + "捕捉", + function (enemy) { + return ( + "当走到怪物周围" + + (enemy.zoneSquare ? "九宫格" : "十字") + + "时会强制进行战斗。" + ); + }, + "#c0ddbb", + ], + ]; + }, "getEnemyInfo": function (enemy, hero, x, y, floorId) { - // 获得某个怪物变化后的数据;该函数将被伤害计算和怪物手册使用 - // 例如:坚固、模仿、仿攻等等 - // - // 参数说明: - // enemy:该怪物信息 - // hero_hp,hero_atk,hero_def,hero_mdef:勇士的生命攻防护盾数据 - // x,y:该怪物的坐标(查看手册和强制战斗时为undefined) - // floorId:该怪物所在的楼层 - // 后面三个参数主要是可以在光环等效果上可以适用(也可以按需制作部分范围光环效果) - floorId = floorId || core.status.floorId; - var hero_hp = core.getRealStatusOrDefault(hero, 'hp'), - hero_atk = core.getRealStatusOrDefault(hero, 'atk'), - hero_def = core.getRealStatusOrDefault(hero, 'def'), - hero_mdef = core.getRealStatusOrDefault(hero, 'mdef'), - hero_speed = core.getRealStatusOrDefault(hero, 'speed'); + // 获得某个怪物变化后的数据;该函数将被伤害计算和怪物手册使用 + // 例如:坚固、模仿、仿攻等等 + // + // 参数说明: + // enemy:该怪物信息 + // hero_hp,hero_atk,hero_def,hero_mdef:勇士的生命攻防护盾数据 + // x,y:该怪物的坐标(查看手册和强制战斗时为undefined) + // floorId:该怪物所在的楼层 + // 后面三个参数主要是可以在光环等效果上可以适用(也可以按需制作部分范围光环效果) + floorId = floorId || core.status.floorId; + var hero_hp = core.getRealStatusOrDefault(hero, "hp"), + hero_atk = core.getRealStatusOrDefault(hero, "atk"), + hero_def = core.getRealStatusOrDefault(hero, "def"), + hero_mdef = core.getRealStatusOrDefault(hero, "mdef"), + hero_speed = core.getRealStatusOrDefault(hero, "speed"); - var mon_hp = core.getEnemyValue(enemy, 'hp', x, y, floorId), - mon_atk = core.getEnemyValue(enemy, 'atk', x, y, floorId), - mon_def = core.getEnemyValue(enemy, 'def', x, y, floorId), - mon_mdef = core.getEnemyValue(enemy, 'mdef', x, y, floorId), - mon_speed = core.getEnemyValue(enemy, 'speed', x, y, floorId), - mon_special = core.getEnemyValue(enemy, 'special', x, y, floorId); - var mon_money = core.getEnemyValue(enemy, 'money', x, y, floorId), - mon_exp = core.getEnemyValue(enemy, 'exp', x, y, floorId), - mon_point = core.getEnemyValue(enemy, 'point', x, y, floorId); - var mon_barrier = 0, - mon_absorb_damage = 0, - mon_magic = core.getEnemyValue(enemy, 'magic', x, y, floorId); - // 模仿 - if (core.hasSpecial(mon_special, 10)) { - mon_atk = hero_atk; - mon_def = hero_def; - } - // 坚固 - if (core.hasSpecial(mon_special, 3) && mon_def < hero_atk - 1) { - mon_def = hero_atk - 1; - } + var mon_hp = core.getEnemyValue(enemy, "hp", x, y, floorId), + mon_atk = core.getEnemyValue(enemy, "atk", x, y, floorId), + mon_def = core.getEnemyValue(enemy, "def", x, y, floorId), + mon_mdef = core.getEnemyValue(enemy, "mdef", x, y, floorId), + mon_speed = core.getEnemyValue(enemy, "speed", x, y, floorId), + mon_special = core.getEnemyValue(enemy, "special", x, y, floorId); + var mon_money = core.getEnemyValue(enemy, "money", x, y, floorId), + mon_exp = core.getEnemyValue(enemy, "exp", x, y, floorId), + mon_point = core.getEnemyValue(enemy, "point", x, y, floorId); + var mon_barrier = 0, + mon_absorb_damage = 0, + mon_magic = core.getEnemyValue(enemy, "magic", x, y, floorId); + // 模仿 + if (core.hasSpecial(mon_special, 10)) { + mon_atk = hero_atk; + mon_def = hero_def; + } + // 坚固 + if (core.hasSpecial(mon_special, 3) && mon_def < hero_atk - 1) { + mon_def = hero_atk - 1; + } - var guards = []; + var guards = []; - // 光环和支援检查 - if (!core.status.checkBlock) core.status.checkBlock = {}; + // 光环和支援检查 + if (!core.status.checkBlock) core.status.checkBlock = {}; - if (core.status.checkBlock.needCache) { - // 从V2.5.4开始,对光环效果增加缓存,以解决多次重复计算的问题,从而大幅提升运行效率。 - var hp_buff = 0, - atk_buff = 0, - def_buff = 0; - // 已经计算过的光环怪ID列表,用于判定叠加 - var usedEnemyIds = {}; - // 检查光环和支援的缓存 - var index = x != null && y != null ? (x + "," + y) : floorId; - if (!core.status.checkBlock.cache) core.status.checkBlock.cache = {}; - var cache = core.status.checkBlock.cache[index]; - if (!cache) { - // 没有该点的缓存,则遍历每个图块 - core.extractBlocks(floorId); - core.status.maps[floorId].blocks.forEach(function (block) { - if (!block.disable) { - // 获得该图块的ID - var id = block.event.id, - enemy = core.material.enemys[id]; - // 检查【光环】技能,数字25 - if (enemy && core.hasSpecial(enemy.special, 25)) { - // 检查是否是范围光环 - var inRange = enemy.haloRange == null; - if (enemy.haloRange != null && x != null && y != null) { - var dx = Math.abs(block.x - x), - dy = Math.abs(block.y - y); - // 检查十字和九宫格光环 - if (dx + dy <= enemy.haloRange) inRange = true; - if (enemy.haloSquare && dx <= enemy.haloRange && dy <= enemy.haloRange) inRange = true; - } - // 检查是否可叠加 - if (inRange && (enemy.haloAdd || !usedEnemyIds[enemy.id])) { - hp_buff += enemy.hpBuff || 0; - atk_buff += enemy.atkBuff || 0; - def_buff += enemy.defBuff || 0; - usedEnemyIds[enemy.id] = true; - } - } - // 检查【支援】技能,数字26 - if (enemy && core.hasSpecial(enemy.special, 26) && - // 检查支援条件,坐标存在,距离为1,且不能是自己 - // 其他类型的支援怪,比如十字之类的话.... 看着做是一样的 - x != null && y != null && Math.abs(block.x - x) <= 1 && Math.abs(block.y - y) <= 1 && !(x == block.x && y == block.y)) { - // 记录怪物的x,y,ID - guards.push([block.x, block.y, id]); - } + if (core.status.checkBlock.needCache) { + // 从V2.5.4开始,对光环效果增加缓存,以解决多次重复计算的问题,从而大幅提升运行效率。 + var hp_buff = 0, + atk_buff = 0, + def_buff = 0; + // 已经计算过的光环怪ID列表,用于判定叠加 + var usedEnemyIds = {}; + // 检查光环和支援的缓存 + var index = x != null && y != null ? x + "," + y : floorId; + if (!core.status.checkBlock.cache) core.status.checkBlock.cache = {}; + var cache = core.status.checkBlock.cache[index]; + if (!cache) { + // 没有该点的缓存,则遍历每个图块 + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + if (!block.disable) { + // 获得该图块的ID + var id = block.event.id, + enemy = core.material.enemys[id]; + // 检查【光环】技能,数字25 + if (enemy && core.hasSpecial(enemy.special, 25)) { + // 检查是否是范围光环 + var inRange = enemy.haloRange == null; + if (enemy.haloRange != null && x != null && y != null) { + var dx = Math.abs(block.x - x), + dy = Math.abs(block.y - y); + // 检查十字和九宫格光环 + if (dx + dy <= enemy.haloRange) inRange = true; + if ( + enemy.haloSquare && + dx <= enemy.haloRange && + dy <= enemy.haloRange + ) + inRange = true; + } + // 检查是否可叠加 + if (inRange && (enemy.haloAdd || !usedEnemyIds[enemy.id])) { + hp_buff += enemy.hpBuff || 0; + atk_buff += enemy.atkBuff || 0; + def_buff += enemy.defBuff || 0; + usedEnemyIds[enemy.id] = true; + } + } + // 检查【支援】技能,数字26 + if ( + enemy && + core.hasSpecial(enemy.special, 26) && + // 检查支援条件,坐标存在,距离为1,且不能是自己 + // 其他类型的支援怪,比如十字之类的话.... 看着做是一样的 + x != null && + y != null && + Math.abs(block.x - x) <= 1 && + Math.abs(block.y - y) <= 1 && + !(x == block.x && y == block.y) + ) { + // 记录怪物的x,y,ID + guards.push([block.x, block.y, id]); + } - // TODO:如果有其他类型光环怪物在这里仿照添加检查 - // 注:新增新的类光环属性(需要遍历全图的)需要在特殊属性定义那里的第五项写1,参见光环和支援的特殊属性定义。 - } - }); + // TODO:如果有其他类型光环怪物在这里仿照添加检查 + // 注:新增新的类光环属性(需要遍历全图的)需要在特殊属性定义那里的第五项写1,参见光环和支援的特殊属性定义。 + } + }); - core.status.checkBlock.cache[index] = { "hp_buff": hp_buff, "atk_buff": atk_buff, "def_buff": def_buff, "guards": guards }; - } else { - // 直接使用缓存数据 - hp_buff = cache.hp_buff; - atk_buff = cache.atk_buff; - def_buff = cache.def_buff; - guards = cache.guards; - } + core.status.checkBlock.cache[index] = { + hp_buff: hp_buff, + atk_buff: atk_buff, + def_buff: def_buff, + guards: guards, + }; + } else { + // 直接使用缓存数据 + hp_buff = cache.hp_buff; + atk_buff = cache.atk_buff; + def_buff = cache.def_buff; + guards = cache.guards; + } - // 增加比例;如果要增加数值可以直接在这里修改 - mon_hp *= (1 + hp_buff / 100); - mon_atk *= (1 + atk_buff / 100); - mon_def *= (1 + def_buff / 100); - } + // 增加比例;如果要增加数值可以直接在这里修改 + mon_hp *= 1 + hp_buff / 100; + mon_atk *= 1 + atk_buff / 100; + mon_def *= 1 + def_buff / 100; + } - // TODO:可以在这里新增其他的怪物数据变化 - // 比如仿攻(怪物攻击不低于勇士攻击): - // if (core.hasSpecial(mon_special, 27) && mon_atk < hero_atk) { - // mon_atk = hero_atk; - // } - // 也可以按需增加各种自定义内容 + // TODO:可以在这里新增其他的怪物数据变化 + // 比如仿攻(怪物攻击不低于勇士攻击): + // if (core.hasSpecial(mon_special, 27) && mon_atk < hero_atk) { + // mon_atk = hero_atk; + // } + // 也可以按需增加各种自定义内容 - return { - "hp": Math.floor(mon_hp), - "atk": Math.floor(mon_atk), - "def": Math.floor(mon_def), - "mdef": Math.floor(mon_mdef), - "speed": Math.floor(mon_speed), - "barrier": Math.floor(mon_barrier), - "absorb": Math.floor(mon_absorb_damage), - "money": Math.floor(mon_money), - "exp": Math.floor(mon_exp), - "point": Math.floor(mon_point), - "special": mon_special, - "magic": mon_magic, - "guards": guards, // 返回支援情况 - }; -}, + return { + hp: Math.floor(mon_hp), + atk: Math.floor(mon_atk), + def: Math.floor(mon_def), + mdef: Math.floor(mon_mdef), + speed: Math.floor(mon_speed), + barrier: Math.floor(mon_barrier), + absorb: Math.floor(mon_absorb_damage), + money: Math.floor(mon_money), + exp: Math.floor(mon_exp), + point: Math.floor(mon_point), + special: mon_special, + magic: mon_magic, + guards: guards, // 返回支援情况 + }; + }, "getDamageInfo": function (enemy, hero, x, y, floorId) { - // 获得战斗伤害信息(实际伤害计算函数) - // - // 参数说明: - // enemy:该怪物信息 - // hero:勇士的当前数据;如果对应项不存在则会从core.status.hero中取。 - // x,y:该怪物的坐标(查看手册和强制战斗时为undefined) - // floorId:该怪物所在的楼层 - // 后面三个参数主要是可以在光环等效果上可以适用 - floorId = floorId || core.status.floorId; + // 获得战斗伤害信息(实际伤害计算函数) + // + // 参数说明: + // enemy:该怪物信息 + // hero:勇士的当前数据;如果对应项不存在则会从core.status.hero中取。 + // x,y:该怪物的坐标(查看手册和强制战斗时为undefined) + // floorId:该怪物所在的楼层 + // 后面三个参数主要是可以在光环等效果上可以适用 + floorId = floorId || core.status.floorId; - var hero_hp = core.getRealStatusOrDefault(hero, 'hp'), - hero_atk = core.getRealStatusOrDefault(hero, 'atk'), - hero_def = core.getRealStatusOrDefault(hero, 'def'), - hero_mdef = core.getRealStatusOrDefault(hero, 'mdef'), - hero_speed = core.getRealStatusOrDefault(hero, 'speed'), - hero_magic = core.getRealStatusOrDefault(hero, 'magic'), - origin_hero_hp = core.getStatusOrDefault(hero, 'hp'), - origin_hero_atk = core.getStatusOrDefault(hero, 'atk'), - origin_hero_def = core.getStatusOrDefault(hero, 'def'); + var hero_hp = core.getRealStatusOrDefault(hero, "hp"), + hero_atk = core.getRealStatusOrDefault(hero, "atk"), + hero_def = core.getRealStatusOrDefault(hero, "def"), + hero_mdef = core.getRealStatusOrDefault(hero, "mdef"), + hero_speed = core.getRealStatusOrDefault(hero, "speed"), + hero_magic = core.getRealStatusOrDefault(hero, "magic"), + origin_hero_hp = core.getStatusOrDefault(hero, "hp"), + origin_hero_atk = core.getStatusOrDefault(hero, "atk"), + origin_hero_def = core.getStatusOrDefault(hero, "def"); + // 怪物的各项数据 + // 对坚固模仿等处理扔到了脚本编辑-getEnemyInfo之中 + var enemyInfo = core.enemys.getEnemyInfo(enemy, hero, x, y, floorId); + var mon_hp = enemyInfo.hp, + mon_atk = enemyInfo.atk, + mon_def = enemyInfo.def, + mon_mdef = enemyInfo.mdef, + mon_speed = enemyInfo.speed, + mon_special = enemyInfo.special, + mon_absorb_damage = enemyInfo.absorb, + mon_barrier = enemyInfo.barrier; + //---第一部分:静态属性修正--- + //此处写入静态影响勇士属性的勇士或怪物技能(静态影响怪物属性的技能于getEnemyInfo中写入) + // 技能的处理 + if (core.getFlag("skill", 0) == 1) { + // 开启了技能1:二倍斩 + hero_atk *= 2; // 计算时攻击力翻倍 + } + //勇士属性取整 + hero_atk = Math.max(0, Math.floor(hero_atk)); + hero_def = Math.max(0, Math.floor(hero_def)); + hero_mdef = Math.max(0, Math.floor(hero_mdef)); + hero_speed = Math.max(0, Math.floor(hero_speed)); - // 怪物的各项数据 - // 对坚固模仿等处理扔到了脚本编辑-getEnemyInfo之中 - var enemyInfo = core.enemys.getEnemyInfo(enemy, hero, x, y, floorId); - var mon_hp = enemyInfo.hp, - mon_atk = enemyInfo.atk, - mon_def = enemyInfo.def, - mon_mdef = enemyInfo.mdef, - mon_speed = enemyInfo.speed, - mon_special = enemyInfo.special, - mon_absorb_damage = enemyInfo.absorb, - mon_barrier = enemyInfo.barrier; + // 如果是无敌属性,且勇士未持有十字架 + if (core.hasSpecial(mon_special, 20) && !core.hasItem("cross")) + return null; // 不可战斗 - //---第一部分:静态属性修正--- - //此处写入静态影响勇士属性的勇士或怪物技能(静态影响怪物属性的技能于getEnemyInfo中写入) - // 技能的处理 - if (core.getFlag('skill', 0) == 1) { // 开启了技能1:二倍斩 - hero_atk *= 2; // 计算时攻击力翻倍 - } - //勇士属性取整 - hero_atk = Math.max(0, Math.floor(hero_atk)); - hero_def = Math.max(0, Math.floor(hero_def)); - hero_mdef = Math.max(0, Math.floor(hero_mdef)); - hero_speed = Math.max(0, Math.floor(hero_speed)); + // 战前造成的额外伤害(可被护盾抵消) + let init_damage = 0; - // 如果是无敌属性,且勇士未持有十字架 - if (core.hasSpecial(mon_special, 20) && !core.hasItem("cross")) - return null; // 不可战斗 + // 吸血 + if (core.hasSpecial(mon_special, 11)) { + let vampire_damage = hero_hp * enemy.vampire; - // 战前造成的额外伤害(可被护盾抵消) - let init_damage = 0; + // 如果有神圣盾免疫吸血等可以在这里写 + // 也可以用hasItem和hasEquip来判定装备 + // if (core.hasFlag('shield5')) vampire_damage = 0; - // 吸血 - if (core.hasSpecial(mon_special, 11)) { - let vampire_damage = hero_hp * enemy.vampire; + vampire_damage = Math.floor(vampire_damage) || 0; + // 加到自身 + if (enemy.add) + // 如果加到自身 + mon_hp += vampire_damage; - // 如果有神圣盾免疫吸血等可以在这里写 - // 也可以用hasItem和hasEquip来判定装备 - // if (core.hasFlag('shield5')) vampire_damage = 0; + init_damage += vampire_damage; + } - vampire_damage = Math.floor(vampire_damage) || 0; - // 加到自身 - if (enemy.add) // 如果加到自身 - mon_hp += vampire_damage; + //——第二部分:变量定义和初始赋值—— + // 每回合怪物对勇士造成的战斗伤害 + let per_damage = Math.max(mon_atk - hero_def, 0); + if (enemyInfo.magic) per_damage = Math.max(mon_atk - hero_mdef, 0); - init_damage += vampire_damage; - } + // + let hero_per_damage = Math.max(hero_atk - mon_def, 0); + if (hero_magic) hero_per_damage = Math.max(hero_atk - mon_mdef, 0); - //——第二部分:变量定义和初始赋值—— - // 每回合怪物对勇士造成的战斗伤害 - let per_damage = Math.max(mon_atk - hero_def, 0); - if (enemyInfo.magic) per_damage = Math.max(mon_atk - hero_mdef, 0); + let damage = 0, + hero_turn = 0, + mon_turn = 0; + //---第三部分:递归开始--- + let length = hero_speed * mon_speed; - // - let hero_per_damage = Math.max(hero_atk - mon_def, 0); - if (hero_magic) hero_per_damage = Math.max(hero_atk - mon_mdef, 0); + for ( + let now_mon_hp = mon_hp, + last_mon_hp = mon_hp, + mon_length = length, + hero_length = length, + mon_time = 0, + hero_time = 0; + now_mon_hp > 0; - let damage = 0, - hero_turn = 0, - mon_turn = 0; - //---第三部分:递归开始--- - let length = hero_speed * mon_speed; + ) { + //勇士和怪物的长度 + mon_time = mon_length / mon_speed; + hero_time = hero_length / hero_speed; + if ( + mon_time < hero_time || + ((mon_time = hero_time) && mon_speed > hero_speed) + ) { + //怪物攻击的回合 + //这里计算怪物攻击时发生的各种变化 - for (let now_mon_hp = mon_hp, last_mon_hp = mon_hp, mon_length = length, hero_length = length, mon_time = 0, hero_time = 0; now_mon_hp > 0;) { //勇士和怪物的长度 - mon_time = mon_length / mon_speed; - hero_time = hero_length / hero_speed; - if ((mon_time < hero_time) || ((mon_time = hero_time) && (mon_speed > hero_speed))) { //怪物攻击的回合 - //这里计算怪物攻击时发生的各种变化 + //伤害与回合增加 + damage += per_damage; + if (core.hasSpecial(mon_special, 6)) { + mon_turn += enemy.n; + } else { + mon_turn += 1; + } + //重新为长度赋值 + mon_length = length; + hero_length = hero_length - mon_time * hero_speed; + } else { + //勇士攻击的回合 + // 这里计算勇士攻击时发生的各种变化 - //伤害与回合增加 - damage += per_damage; - if (core.hasSpecial(mon_special, 6)) { - mon_turn += enemy.n; - } else { - mon_turn += 1; - } - //重新为长度赋值 - mon_length = length; - hero_length = hero_length - mon_time * hero_speed; - } else { //勇士攻击的回合 - // 这里计算勇士攻击时发生的各种变化 + // 伤害与回合数增加 + now_mon_hp -= hero_per_damage; + hero_turn += 1; //勇士回合+1,如果有勇士每回合多次攻击的情况,在这里写判断 + // 无法战斗计算 + if (hero_turn % 50 == 0) { + if (now_mon_hp >= last_mon_hp) return null; + last_mon_hp = now_mon_hp; + } + //重新为长度赋值 + hero_length = length; + mon_length = mon_length - hero_time * mon_speed; + } + } - // 伤害与回合数增加 - now_mon_hp -= hero_per_damage; - hero_turn += 1; //勇士回合+1,如果有勇士每回合多次攻击的情况,在这里写判断 - // 无法战斗计算 - if (hero_turn % 50 == 0) { - if (now_mon_hp >= last_mon_hp) return null; - last_mon_hp = now_mon_hp; - } - //重新为长度赋值 - hero_length = length; - mon_length = mon_length - hero_time * mon_speed; - } - } + //下面这些还没修改 + // 连击 + if (core.hasSpecial(mon_special, 6)) per_damage *= enemy.n || 2; - //下面这些还没修改 - // 连击 - if (core.hasSpecial(mon_special, 6)) per_damage *= (enemy.n || 2); + // 每回合的反击伤害;反击是按照勇士的攻击次数来计算回合 + let counterDamage = 0; + if (core.hasSpecial(mon_special, 8)) + counterDamage += Math.floor( + (enemy.counterAttack || core.values.counterAttack) * hero_atk + ); - // 每回合的反击伤害;反击是按照勇士的攻击次数来计算回合 - let counterDamage = 0; - if (core.hasSpecial(mon_special, 8)) - counterDamage += Math.floor((enemy.counterAttack || core.values.counterAttack) * hero_atk); + // 先攻 + if (core.hasSpecial(mon_special, 1)) init_damage += per_damage; - // 先攻 - if (core.hasSpecial(mon_special, 1)) init_damage += per_damage; + // 破甲 + if (core.hasSpecial(mon_special, 7)) + init_damage += Math.floor( + (enemy.breakArmor || core.values.breakArmor) * hero_def + ); - // 破甲 - if (core.hasSpecial(mon_special, 7)) - init_damage += Math.floor((enemy.breakArmor || core.values.breakArmor) * hero_def); + // 净化 + if (core.hasSpecial(mon_special, 9)) + init_damage += Math.floor( + (enemy.purify || core.values.purify) * hero_mdef + ); + //上面这些还没修改 + //勇士护盾计算 + let barrier = hero_mdef; + if (enemyInfo.magic) barrier = hero_def; - // 净化 - if (core.hasSpecial(mon_special, 9)) - init_damage += Math.floor((enemy.purify || core.values.purify) * hero_mdef); - //上面这些还没修改 - //勇士护盾计算 - let barrier = hero_mdef; - if (enemyInfo.magic) barrier = hero_def; - - // ------ 支援 ----- // - // 这个递归最好想明白为什么,flag:__extraTurn__是怎么用的 - /*var guards = core.getFlag("__guards__" + x + "_" + y, enemyInfo.guards); + // ------ 支援 ----- // + // 这个递归最好想明白为什么,flag:__extraTurn__是怎么用的 + /*var guards = core.getFlag("__guards__" + x + "_" + y, enemyInfo.guards); var guard_before_current_enemy = false; // ------ 支援怪是先打(true)还是后打(false)? turn += core.getFlag("__extraTurn__", 0); if (guards.length > 0) { @@ -823,42 +1084,43 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } } core.removeFlag("__extraTurn__");*/ - // ------ 支援END ------ // + // ------ 支援END ------ // - // 最终伤害:初始伤害 + 怪物对勇士造成的伤害 + 反击伤害 - damage += init_damage + hero_turn * counterDamage; - // 再扣去护盾 - damage -= barrier; + // 最终伤害:初始伤害 + 怪物对勇士造成的伤害 + 反击伤害 + damage += init_damage + hero_turn * counterDamage; + // 再扣去护盾 + damage -= barrier; - // 检查是否允许负伤 - if (!core.flags.enableNegativeDamage) - damage = Math.max(0, damage); + // 检查是否允许负伤 + if (!core.flags.enableNegativeDamage) damage = Math.max(0, damage); - // 最后处理仇恨和固伤(因为这两个不能被护盾减伤) - if (core.hasSpecial(mon_special, 17)) { // 仇恨 - damage += core.getFlag('hatred', 0); - } - if (core.hasSpecial(mon_special, 22)) { // 固伤 - damage += enemy.damage || 0; - } + // 最后处理仇恨和固伤(因为这两个不能被护盾减伤) + if (core.hasSpecial(mon_special, 17)) { + // 仇恨 + damage += core.getFlag("hatred", 0); + } + if (core.hasSpecial(mon_special, 22)) { + // 固伤 + damage += enemy.damage || 0; + } - return { - "mon_hp": Math.floor(mon_hp), - "mon_atk": Math.floor(mon_atk), - "mon_def": Math.floor(mon_def), - "mon_mdef": Math.floor(mon_mdef), - "init_damage": Math.floor(init_damage), - "per_damage": Math.floor(per_damage), - "hero_per_damage": Math.floor(hero_per_damage), - "turn": Math.floor(hero_turn), - "mon_turn": Math.floor(mon_turn), - "damage": Math.floor(damage) - }; - /*TODO:怪物手册的修改(需要修改这里return的内容以及一些战后判断) + return { + mon_hp: Math.floor(mon_hp), + mon_atk: Math.floor(mon_atk), + mon_def: Math.floor(mon_def), + mon_mdef: Math.floor(mon_mdef), + init_damage: Math.floor(init_damage), + per_damage: Math.floor(per_damage), + hero_per_damage: Math.floor(hero_per_damage), + turn: Math.floor(hero_turn), + mon_turn: Math.floor(mon_turn), + damage: Math.floor(damage), + }; + /*TODO:怪物手册的修改(需要修改这里return的内容以及一些战后判断) 1. 显示怪物是魔攻还是物攻(在怪物名字上做颜色变化,物攻是黄色,魔攻是蓝色) 2. 一防减伤是物防还是魔防(由怪物是物攻还是魔攻来转换) 3. 特殊战斗的怪物,在怪物手册里“伤害”写为“特殊战”*/ -} + } }, "actions": { "onKeyUp": function (keyCode, altKey) { @@ -871,7 +1133,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (core.isMoving()) return; // 商店长按时忽略 - if (core.status.onShopLongDown) return core.status.onShopLongDown = false; + if (core.status.onShopLongDown) + return (core.status.onShopLongDown = false); // Alt+0~9,快捷换上套装 if (altKey && keyCode >= 48 && keyCode <= 57) { @@ -888,8 +1151,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.openBook(true); break; case 71: // G:使用楼传器 - flags.canMoveFloor = core.canMoveFloor() - if (core.isPlaying()) core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + flags.canMoveFloor = core.canMoveFloor(); + core.useItem('fly', true) core.status.route.push("key:71"); break; case 65: // A:读取自动存档(回退) @@ -948,25 +1211,37 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.actions._clickGameInfo_openComments(); break; case 49: // 快捷键1: 破 - if (core.hasItem('pickaxe')) { + if (core.hasItem("pickaxe")) { core.status.route.push("key:49"); // 将按键记在录像中 - core.useItem('pickaxe', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + core.useItem("pickaxe", true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 } break; case 50: // 快捷键2: 炸 - if (core.hasItem('bomb')) { + if (core.hasItem("bomb")) { core.status.route.push("key:50"); // 将按键记在录像中 - core.useItem('bomb', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + core.useItem("bomb", true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 } break; case 51: // 快捷键3: 飞 - if (core.hasItem('centerFly')) { + if (core.hasItem("centerFly")) { core.ui._drawCenterFly(); } break; case 52: // 快捷键4:破冰/冰冻/地震/上下楼器/... 其他道具依次判断 { - var list = ["icePickaxe", "freezeBadge", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"]; + var list = [ + "icePickaxe", + "freezeBadge", + "earthquake", + "upFly", + "downFly", + "jumpShoes", + "lifeWand", + "poisonWine", + "weakWine", + "curseWine", + "superWine", + ]; for (var i = 0; i < list.length; i++) { var itemId = list[i]; if (core.canUseItem(itemId)) { @@ -992,9 +1267,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = break; case 70: // F:开启技能“二倍斩” // 检测是否拥有“二倍斩”这个技能道具 - if (core.hasItem('skill1')) { + if (core.hasItem("skill1")) { core.status.route.push("key:70"); - core.useItem('skill1', true); + core.useItem("skill1", true); } break; // 在这里可以任意新增或编辑已有的快捷键内容 @@ -1012,33 +1287,32 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = break; */ } - }, "onStatusBarClick": function (px, py, vertical) { - // 点击状态栏时触发的事件,仅在自绘状态栏开启时生效 - // px和py为点击的像素坐标 - // vertical为录像播放过程中的横竖屏信息 - // - // 横屏模式下状态栏的画布大小是 129*416 (开启拓展装备栏后是 129*457) - // 竖屏模式下状态栏的画布大小是 416*(32*rows+9) 其中rows为状态栏行数,即全塔属性中statusCanvasRowsOnMobile值 - // 可以使用 _isVertical() 来判定当前是否是竖屏模式 + // 点击状态栏时触发的事件,仅在自绘状态栏开启时生效 + // px和py为点击的像素坐标 + // vertical为录像播放过程中的横竖屏信息 + // + // 横屏模式下状态栏的画布大小是 129*416 (开启拓展装备栏后是 129*457) + // 竖屏模式下状态栏的画布大小是 416*(32*rows+9) 其中rows为状态栏行数,即全塔属性中statusCanvasRowsOnMobile值 + // 可以使用 _isVertical() 来判定当前是否是竖屏模式 - // 判定当前是否是竖屏模式。录像播放过程中可能会记录当时的横竖屏信息以覆盖。 - var _isVertical = function () { - if (core.isReplaying() && vertical != null) return vertical; - return core.domStyle.isVertical; - } + // 判定当前是否是竖屏模式。录像播放过程中可能会记录当时的横竖屏信息以覆盖。 + var _isVertical = function () { + if (core.isReplaying() && vertical != null) return vertical; + return core.domStyle.isVertical; + }; - // 如果正在执行事件,则忽略 - if (core.status.lockControl) return; - // 如果当前正在行走,则忽略;也可以使用 core.waitHeroToStop(callback) 来停止行走再回调执行脚本 - if (core.isMoving()) return; + // 如果正在执行事件,则忽略 + if (core.status.lockControl) return; + // 如果当前正在行走,则忽略;也可以使用 core.waitHeroToStop(callback) 来停止行走再回调执行脚本 + if (core.isMoving()) return; - // 判定px和py来执行自己的脚本内容.... 注意横竖屏 - // console.log("onStatusBarClick: ", px, py, _isVertical()); + // 判定px和py来执行自己的脚本内容.... 注意横竖屏 + // console.log("onStatusBarClick: ", px, py, _isVertical()); - // 样例一:点击某个区域后使用一个道具 - /* + // 样例一:点击某个区域后使用一个道具 + /* if (core.hasItem("pickaxe")) { if (_isVertical()) { // 竖屏模式下 @@ -1054,8 +1328,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } */ - // 样例二:点击某个区域后执行一段公共事件或脚本 - /* + // 样例二:点击某个区域后执行一段公共事件或脚本 + /* if (core.hasFlag("xxx")) { if (_isVertical()) { // 竖屏模式下 @@ -1080,625 +1354,809 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } } */ - - } + } }, "control": { "saveData": function () { - // 存档操作,此函数应该返回“具体要存档的内容” + // 存档操作,此函数应该返回“具体要存档的内容” - // 差异化存储values - var values = {}; - for (var key in core.values) { - if (!core.same(core.values[key], core.data.values[key])) - values[key] = core.clone(core.values[key]); - } + // 差异化存储values + var values = {}; + for (var key in core.values) { + if (!core.same(core.values[key], core.data.values[key])) + values[key] = core.clone(core.values[key]); + } - // 要存档的内容 - var data = { - 'floorId': core.status.floorId, - 'hero': core.clone(core.status.hero), - 'hard': core.status.hard, - 'maps': core.clone(core.maps.saveMap()), - 'route': core.encodeRoute(core.status.route), - 'values': values, - 'version': core.firstData.version, - 'guid': core.getGuid(), - "time": new Date().getTime() - }; - return data; -}, + // 要存档的内容 + var data = { + floorId: core.status.floorId, + hero: core.clone(core.status.hero), + hard: core.status.hard, + maps: core.clone(core.maps.saveMap()), + route: core.encodeRoute(core.status.route), + values: values, + version: core.firstData.version, + guid: core.getGuid(), + time: new Date().getTime(), + }; + return data; + }, "loadData": function (data, callback) { - // 读档操作;从存储中读取了内容后的行为 - const play = core.status.played - // 重置游戏和路线 - core.resetGame(data.hero, data.hard, data.floorId, core.maps.loadMap(data.maps, null, data.hero.flags), data.values); - core.status.route = core.decodeRoute(data.route); - core.control._bindRoutePush(); - // 文字属性,全局属性 - core.status.textAttribute = core.getFlag('textAttribute', core.status.textAttribute); - var toAttribute = core.getFlag('globalAttribute', core.status.globalAttribute); - if (!core.same(toAttribute, core.status.globalAttribute)) { - core.status.globalAttribute = toAttribute; - core.resize(); - } - // 重置音量 - core.events.setVolume(core.getFlag("__volume__", 1), 0); - // 加载勇士图标 - var icon = core.status.hero.image; - icon = core.getMappedName(icon); - if (core.material.images.images[icon]) { - core.material.images.hero = core.material.images.images[icon]; - core.material.icons.hero.width = core.material.images.images[icon].width / 4; - core.material.icons.hero.height = core.material.images.images[icon].height / 4; - } - core.setFlag('__fromLoad__', true); + // 读档操作;从存储中读取了内容后的行为 + const play = core.status.played; + // 重置游戏和路线 + core.resetGame( + data.hero, + data.hard, + data.floorId, + core.maps.loadMap(data.maps, null, data.hero.flags), + data.values + ); + core.status.route = core.decodeRoute(data.route); + core.control._bindRoutePush(); + // 文字属性,全局属性 + core.status.textAttribute = core.getFlag( + "textAttribute", + core.status.textAttribute + ); + var toAttribute = core.getFlag( + "globalAttribute", + core.status.globalAttribute + ); + if (!core.same(toAttribute, core.status.globalAttribute)) { + core.status.globalAttribute = toAttribute; + core.resize(); + } + // 重置音量 + core.events.setVolume(core.getFlag("__volume__", 1), 0); + // 加载勇士图标 + var icon = core.status.hero.image; + icon = core.getMappedName(icon); + if (core.material.images.images[icon]) { + core.material.images.hero = core.material.images.images[icon]; + core.material.icons.hero.width = + core.material.images.images[icon].width / 4; + core.material.icons.hero.height = + core.material.images.images[icon].height / 4; + } + core.setFlag("__fromLoad__", true); - // TODO:增加自己的一些读档处理 - core.ui.statusBar.clearItemInfo() - core.ui.statusBar.update(); - core.plugin.playing.clear() - // 切换到对应的楼层 - core.changeFloor(data.floorId, null, data.hero.loc, 0, function () { - // TODO:可以在这里设置读档后播放BGM - if (core.hasFlag("__bgm__")) { // 持续播放 - core.playBgm(core.getFlag("__bgm__")); - } + // TODO:增加自己的一些读档处理 + core.ui.statusBar.clearItemInfo(); + core.ui.statusBar.update(); + core.plugin.playing.clear(); + // 切换到对应的楼层 + core.changeFloor(data.floorId, null, data.hero.loc, 0, function () { + // TODO:可以在这里设置读档后播放BGM + if (core.hasFlag("__bgm__")) { + // 持续播放 + core.playBgm(core.getFlag("__bgm__")); + } - core.removeFlag('__fromLoad__'); - if (!play) core.insertCommonEvent('强制横屏') - if (callback) callback(); - }); - if (play) core.doAction() - - -}, + core.removeFlag("__fromLoad__"); + if (!play) core.insertCommonEvent("强制横屏"); + if (callback) callback(); + }); + if (play) core.doAction(); + }, "getStatusLabel": function (name) { - // 返回某个状态英文名的对应中文标签,如atk -> 攻击,def -> 防御等。 - // 请注意此项仅影响 libs/ 下的内容(如绘制怪物手册、数据统计等) - // 自行定义的(比如获得道具效果)中用到的“攻击+3”等需要自己去对应地方修改 + // 返回某个状态英文名的对应中文标签,如atk -> 攻击,def -> 防御等。 + // 请注意此项仅影响 libs/ 下的内容(如绘制怪物手册、数据统计等) + // 自行定义的(比如获得道具效果)中用到的“攻击+3”等需要自己去对应地方修改 - return { - name: "名称", - lv: "等级", - hpmax: "生命上限", - hp: "生命", - manamax: "魔力上限", - mana: "魔力", - atk: "攻击", - def: "防御", - spell: "法强", - matk: "魔攻比例", - mdef: "护盾比例", - speed: "速度", - money: "金币", - exp: "经验", - point: "加点", - steps: "步数", - } [name] || name; -}, + return ( + { + name: "名称", + lv: "等级", + hpmax: "生命上限", + hp: "生命", + manamax: "魔力上限", + mana: "魔力", + atk: "攻击", + def: "防御", + spell: "法强", + matk: "魔攻比例", + mdef: "护盾比例", + speed: "速度", + money: "金币", + exp: "经验", + point: "加点", + steps: "步数", + }[name] || name + ); + }, "triggerDebuff": function (action, type) { - // 毒衰咒效果的获得与解除 - // action:获得还是解除;'get'表示获得,'remove'表示解除 - // type:一个数组表示获得了哪些毒衰咒效果;poison, weak,curse - if (!(type instanceof Array)) type = [type]; + // 毒衰咒效果的获得与解除 + // action:获得还是解除;'get'表示获得,'remove'表示解除 + // type:一个数组表示获得了哪些毒衰咒效果;poison, weak,curse + if (!(type instanceof Array)) type = [type]; - if (action == 'get') { - if (core.inArray(type, 'poison') && !core.hasFlag("poison")) { - // 获得毒效果 - core.setFlag('poison', true); - } - if (core.inArray(type, 'weak') && !core.hasFlag('weak')) { - // 获得衰效果 - core.setFlag('weak', true); - if (core.values.weakValue >= 1) { - // >=1,直接扣数值 - core.addStatus('atk', -core.values.weakValue); - core.addStatus('def', -core.values.weakValue); - } else { - // <1,扣比例 - core.addBuff('atk', -core.values.weakValue); - core.addBuff('def', -core.values.weakValue); - } - } - if (core.inArray(type, 'curse') && !core.hasFlag('curse')) { - // 获得咒效果 - core.setFlag('curse', true); - } - } else if (action == 'remove') { - var success = false; - if (core.inArray(type, "poison") && core.hasFlag("poison")) { - success = true; - // 移除毒效果 - core.setFlag("poison", false); - } - if (core.inArray(type, "weak") && core.hasFlag("weak")) { - success = true; - // 移除衰效果 - core.setFlag("weak", false); - if (core.values.weakValue >= 1) { - // >=1,直接扣数值 - core.addStatus('atk', core.values.weakValue); - core.addStatus('def', core.values.weakValue); - } else { - // <1,扣比例 - core.addBuff('atk', core.values.weakValue); - core.addBuff('def', core.values.weakValue); - } - } - if (core.inArray(type, "curse") && core.hasFlag("curse")) { - success = true; - // 移除咒效果 - core.setFlag("curse", false); - } - if (success) core.playSound('回血'); - } - }, + if (action == "get") { + if (core.inArray(type, "poison") && !core.hasFlag("poison")) { + // 获得毒效果 + core.setFlag("poison", true); + } + if (core.inArray(type, "weak") && !core.hasFlag("weak")) { + // 获得衰效果 + core.setFlag("weak", true); + if (core.values.weakValue >= 1) { + // >=1,直接扣数值 + core.addStatus("atk", -core.values.weakValue); + core.addStatus("def", -core.values.weakValue); + } else { + // <1,扣比例 + core.addBuff("atk", -core.values.weakValue); + core.addBuff("def", -core.values.weakValue); + } + } + if (core.inArray(type, "curse") && !core.hasFlag("curse")) { + // 获得咒效果 + core.setFlag("curse", true); + } + } else if (action == "remove") { + var success = false; + if (core.inArray(type, "poison") && core.hasFlag("poison")) { + success = true; + // 移除毒效果 + core.setFlag("poison", false); + } + if (core.inArray(type, "weak") && core.hasFlag("weak")) { + success = true; + // 移除衰效果 + core.setFlag("weak", false); + if (core.values.weakValue >= 1) { + // >=1,直接扣数值 + core.addStatus("atk", core.values.weakValue); + core.addStatus("def", core.values.weakValue); + } else { + // <1,扣比例 + core.addBuff("atk", core.values.weakValue); + core.addBuff("def", core.values.weakValue); + } + } + if (core.inArray(type, "curse") && core.hasFlag("curse")) { + success = true; + // 移除咒效果 + core.setFlag("curse", false); + } + if (success) core.playSound("回血"); + } + }, "updateStatusBar": function () { - // 更新状态栏 - core.ui.statusBar.update(); - // 更新阻激夹域的伤害值 - core.updateCheckBlock(); - // 更新全地图显伤 - core.updateDamage(); -}, + // 更新状态栏 + core.ui.statusBar.update(); + // 更新阻激夹域的伤害值 + core.updateCheckBlock(); + // 更新全地图显伤 + core.updateDamage(); + }, "updateCheckBlock": function (floorId) { - // 领域、夹击、阻击等的伤害值计算 - floorId = floorId || core.status.floorId; - if (!floorId || !core.status.maps) return; + // 领域、夹击、阻击等的伤害值计算 + floorId = floorId || core.status.floorId; + if (!floorId || !core.status.maps) return; - var width = core.floors[floorId].width, - height = core.floors[floorId].height; - var blocks = core.getMapBlocksObj(floorId); + var width = core.floors[floorId].width, + height = core.floors[floorId].height; + var blocks = core.getMapBlocksObj(floorId); - var damage = {}, // 每个点的伤害值 - type = {}, // 每个点的伤害类型 - repulse = {}, // 每个点的阻击怪信息 - ambush = {}; // 每个点的捕捉信息 - var betweenAttackLocs = {}; // 所有可能的夹击点 - var needCache = false; - var canGoDeadZone = core.flags.canGoDeadZone; - core.flags.canGoDeadZone = true; + var damage = {}, // 每个点的伤害值 + type = {}, // 每个点的伤害类型 + repulse = {}, // 每个点的阻击怪信息 + ambush = {}; // 每个点的捕捉信息 + var betweenAttackLocs = {}; // 所有可能的夹击点 + var needCache = false; + var canGoDeadZone = core.flags.canGoDeadZone; + core.flags.canGoDeadZone = true; - // 计算血网和领域、阻击、激光的伤害,计算捕捉信息 - for (var loc in blocks) { - var block = blocks[loc], - x = block.x, - y = block.y, - id = block.event.id, - enemy = core.material.enemys[id]; - if (block.disable) continue; + // 计算血网和领域、阻击、激光的伤害,计算捕捉信息 + for (var loc in blocks) { + var block = blocks[loc], + x = block.x, + y = block.y, + id = block.event.id, + enemy = core.material.enemys[id]; + if (block.disable) continue; - type[loc] = type[loc] || {}; + type[loc] = type[loc] || {}; - // 血网 - // 如需调用当前楼层的ratio可使用 core.status.maps[floorId].ratio - if (id == 'lavaNet' && !core.hasItem('amulet')) { - damage[loc] = (damage[loc] || 0) + core.values.lavaDamage; - type[loc][(block.event.name || "血网") + "伤害"] = true; - } + // 血网 + // 如需调用当前楼层的ratio可使用 core.status.maps[floorId].ratio + if (id == "lavaNet" && !core.hasItem("amulet")) { + damage[loc] = (damage[loc] || 0) + core.values.lavaDamage; + type[loc][(block.event.name || "血网") + "伤害"] = true; + } - // 领域 - // 如果要防止领域伤害,可以直接简单的将 flag:no_zone 设为true - if (enemy && core.hasSpecial(enemy.special, 15) && !core.hasFlag('no_zone')) { - // 领域范围,默认为1 - var range = enemy.range || 1; - // 是否是九宫格领域 - var zoneSquare = false; - if (enemy.zoneSquare != null) zoneSquare = enemy.zoneSquare; - // 在范围内进行搜索,增加领域伤害值 - for (var dx = -range; dx <= range; dx++) { - for (var dy = -range; dy <= range; dy++) { - if (dx == 0 && dy == 0) continue; - var nx = x + dx, - ny = y + dy, - currloc = nx + "," + ny; - if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; - // 如果是十字领域,则还需要满足 |dx|+|dy|<=range - if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; - damage[currloc] = (damage[currloc] || 0) + (enemy.zone || 0); - type[currloc] = type[currloc] || {}; - type[currloc]["领域伤害"] = true; - } - } - } + // 领域 + // 如果要防止领域伤害,可以直接简单的将 flag:no_zone 设为true + if ( + enemy && + core.hasSpecial(enemy.special, 15) && + !core.hasFlag("no_zone") + ) { + // 领域范围,默认为1 + var range = enemy.range || 1; + // 是否是九宫格领域 + var zoneSquare = false; + if (enemy.zoneSquare != null) zoneSquare = enemy.zoneSquare; + // 在范围内进行搜索,增加领域伤害值 + for (var dx = -range; dx <= range; dx++) { + for (var dy = -range; dy <= range; dy++) { + if (dx == 0 && dy == 0) continue; + var nx = x + dx, + ny = y + dy, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + // 如果是十字领域,则还需要满足 |dx|+|dy|<=range + if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; + damage[currloc] = (damage[currloc] || 0) + (enemy.zone || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["领域伤害"] = true; + } + } + } - // 阻击 - // 如果要防止阻击伤害,可以直接简单的将 flag:no_repulse 设为true - if (enemy && core.hasSpecial(enemy.special, 18) && !core.hasFlag('no_repulse')) { - var scan = enemy.zoneSquare ? core.utils.scan2 : core.utils.scan; - for (var dir in scan) { - var nx = x + scan[dir].x, - ny = y + scan[dir].y, - currloc = nx + "," + ny; - if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; - damage[currloc] = (damage[currloc] || 0) + (enemy.repulse || 0); - type[currloc] = type[currloc] || {}; - type[currloc]["阻击伤害"] = true; + // 阻击 + // 如果要防止阻击伤害,可以直接简单的将 flag:no_repulse 设为true + if ( + enemy && + core.hasSpecial(enemy.special, 18) && + !core.hasFlag("no_repulse") + ) { + var scan = enemy.zoneSquare ? core.utils.scan2 : core.utils.scan; + for (var dir in scan) { + var nx = x + scan[dir].x, + ny = y + scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + damage[currloc] = (damage[currloc] || 0) + (enemy.repulse || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["阻击伤害"] = true; - var rdir = core.turnDirection(":back", dir); - // 检查下一个点是否存在事件(从而判定是否移动) - var rnx = x + scan[rdir].x, - rny = y + scan[rdir].y; - if (rnx < 0 || rnx >= width || rny < 0 || rny >= height) continue; - // 如需禁止阻击被推到已隐藏的事件处(如重生怪处),可将这一句的false改为true - if (core.getBlock(rnx, rny, floorId, false) != null) continue; - if (core.utils.scan[rdir] && !core.canMoveHero(x, y, rdir, floorId)) continue; - repulse[currloc] = (repulse[currloc] || []).concat([ - [x, y, id, rdir] - ]); - } - } + var rdir = core.turnDirection(":back", dir); + // 检查下一个点是否存在事件(从而判定是否移动) + var rnx = x + scan[rdir].x, + rny = y + scan[rdir].y; + if (rnx < 0 || rnx >= width || rny < 0 || rny >= height) continue; + // 如需禁止阻击被推到已隐藏的事件处(如重生怪处),可将这一句的false改为true + if (core.getBlock(rnx, rny, floorId, false) != null) continue; + if (core.utils.scan[rdir] && !core.canMoveHero(x, y, rdir, floorId)) + continue; + repulse[currloc] = (repulse[currloc] || []).concat([ + [x, y, id, rdir], + ]); + } + } - // 激光 - // 如果要防止激光伤害,可以直接简单的将 flag:no_laser 设为true - if (enemy && core.hasSpecial(enemy.special, 24) && !core.hasFlag("no_laser")) { - for (var nx = 0; nx < width; nx++) { - var currloc = nx + "," + y; - if (nx != x) { - damage[currloc] = (damage[currloc] || 0) + (enemy.laser || 0); - type[currloc] = type[currloc] || {}; - type[currloc]["激光伤害"] = true; - } - } - for (var ny = 0; ny < height; ny++) { - var currloc = x + "," + ny; - if (ny != y) { - damage[currloc] = (damage[currloc] || 0) + (enemy.laser || 0); - type[currloc] = type[currloc] || {}; - type[currloc]["激光伤害"] = true; - } - } - } + // 激光 + // 如果要防止激光伤害,可以直接简单的将 flag:no_laser 设为true + if ( + enemy && + core.hasSpecial(enemy.special, 24) && + !core.hasFlag("no_laser") + ) { + for (var nx = 0; nx < width; nx++) { + var currloc = nx + "," + y; + if (nx != x) { + damage[currloc] = (damage[currloc] || 0) + (enemy.laser || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["激光伤害"] = true; + } + } + for (var ny = 0; ny < height; ny++) { + var currloc = x + "," + ny; + if (ny != y) { + damage[currloc] = (damage[currloc] || 0) + (enemy.laser || 0); + type[currloc] = type[currloc] || {}; + type[currloc]["激光伤害"] = true; + } + } + } - // 捕捉 - // 如果要防止捕捉效果,可以直接简单的将 flag:no_ambush 设为true - if (enemy && core.enemys.hasSpecial(enemy.special, 27) && !core.hasFlag("no_ambush")) { - var scan = enemy.zoneSquare ? core.utils.scan2 : core.utils.scan; - // 给周围格子加上【捕捉】记号 - for (var dir in scan) { - var nx = x + scan[dir].x, - ny = y + scan[dir].y, - currloc = nx + "," + ny; - if (nx < 0 || nx >= width || ny < 0 || ny >= height || (core.utils.scan[dir] && !core.canMoveHero(x, y, dir, floorId))) continue; - ambush[currloc] = (ambush[currloc] || []).concat([ - [x, y, id, dir] - ]); - } - } + // 捕捉 + // 如果要防止捕捉效果,可以直接简单的将 flag:no_ambush 设为true + if ( + enemy && + core.enemys.hasSpecial(enemy.special, 27) && + !core.hasFlag("no_ambush") + ) { + var scan = enemy.zoneSquare ? core.utils.scan2 : core.utils.scan; + // 给周围格子加上【捕捉】记号 + for (var dir in scan) { + var nx = x + scan[dir].x, + ny = y + scan[dir].y, + currloc = nx + "," + ny; + if ( + nx < 0 || + nx >= width || + ny < 0 || + ny >= height || + (core.utils.scan[dir] && !core.canMoveHero(x, y, dir, floorId)) + ) + continue; + ambush[currloc] = (ambush[currloc] || []).concat([[x, y, id, dir]]); + } + } - // 夹击;在这里提前计算所有可能的夹击点,具体计算逻辑在下面 - // 如果要防止夹击伤害,可以简单的将 flag:no_betweenAttack 设为true - if (enemy && core.enemys.hasSpecial(enemy.special, 16) && !core.hasFlag('no_betweenAttack')) { - for (var dir in core.utils.scan) { - var nx = x + core.utils.scan[dir].x, - ny = y + core.utils.scan[dir].y, - currloc = nx + "," + ny; - if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; - betweenAttackLocs[currloc] = true; - } - } + // 夹击;在这里提前计算所有可能的夹击点,具体计算逻辑在下面 + // 如果要防止夹击伤害,可以简单的将 flag:no_betweenAttack 设为true + if ( + enemy && + core.enemys.hasSpecial(enemy.special, 16) && + !core.hasFlag("no_betweenAttack") + ) { + for (var dir in core.utils.scan) { + var nx = x + core.utils.scan[dir].x, + ny = y + core.utils.scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + betweenAttackLocs[currloc] = true; + } + } - // 检查地图范围类技能 - var specialFlag = core.getSpecialFlag(enemy); - if (specialFlag & 1) needCache = true; - if (core.status.event.id == 'viewMaps') needCache = true; - if ((core.status.event.id == 'book' || core.status.event.id == 'bool-detail') && core.status.event.ui) needCache = true; - } + // 检查地图范围类技能 + var specialFlag = core.getSpecialFlag(enemy); + if (specialFlag & 1) needCache = true; + if (core.status.event.id == "viewMaps") needCache = true; + if ( + (core.status.event.id == "book" || + core.status.event.id == "bool-detail") && + core.status.event.ui + ) + needCache = true; + } - // 对每个可能的夹击点计算夹击伤害 - for (var loc in betweenAttackLocs) { - var xy = loc.split(","), - x = parseInt(xy[0]), - y = parseInt(xy[1]); - // 夹击怪物的ID - var enemyId1 = null, - enemyId2 = null; - // 检查左右夹击 - var leftBlock = blocks[(x - 1) + "," + y], - rightBlock = blocks[(x + 1) + "," + y]; - var leftId = core.getFaceDownId(leftBlock), - rightId = core.getFaceDownId(rightBlock); - if (leftBlock && !leftBlock.disable && rightBlock && !rightBlock.disable && leftId == rightId) { - if (core.hasSpecial(leftId, 16)) - enemyId1 = leftId; - } - // 检查上下夹击 - var topBlock = blocks[x + "," + (y - 1)], - bottomBlock = blocks[x + "," + (y + 1)]; - var topId = core.getFaceDownId(topBlock), - bottomId = core.getFaceDownId(bottomBlock); - if (topBlock && !topBlock.disable && bottomBlock && !bottomBlock.disable && topId == bottomId) { - if (core.hasSpecial(topId, 16)) - enemyId2 = topId; - } + // 对每个可能的夹击点计算夹击伤害 + for (var loc in betweenAttackLocs) { + var xy = loc.split(","), + x = parseInt(xy[0]), + y = parseInt(xy[1]); + // 夹击怪物的ID + var enemyId1 = null, + enemyId2 = null; + // 检查左右夹击 + var leftBlock = blocks[x - 1 + "," + y], + rightBlock = blocks[x + 1 + "," + y]; + var leftId = core.getFaceDownId(leftBlock), + rightId = core.getFaceDownId(rightBlock); + if ( + leftBlock && + !leftBlock.disable && + rightBlock && + !rightBlock.disable && + leftId == rightId + ) { + if (core.hasSpecial(leftId, 16)) enemyId1 = leftId; + } + // 检查上下夹击 + var topBlock = blocks[x + "," + (y - 1)], + bottomBlock = blocks[x + "," + (y + 1)]; + var topId = core.getFaceDownId(topBlock), + bottomId = core.getFaceDownId(bottomBlock); + if ( + topBlock && + !topBlock.disable && + bottomBlock && + !bottomBlock.disable && + topId == bottomId + ) { + if (core.hasSpecial(topId, 16)) enemyId2 = topId; + } - if (enemyId1 != null || enemyId2 != null) { - var leftHp = core.status.hero.hp - (damage[loc] || 0); - if (leftHp > 1) { - // 夹击伤害值 - var value = Math.floor(leftHp / 2); - // 是否不超过怪物伤害值 - if (core.flags.betweenAttackMax) { - var enemyDamage1 = core.getDamage(enemyId1, x, y, floorId); - if (enemyDamage1 != null && enemyDamage1 < value) - value = enemyDamage1; - var enemyDamage2 = core.getDamage(enemyId2, x, y, floorId); - if (enemyDamage2 != null && enemyDamage2 < value) - value = enemyDamage2; - } - if (value > 0) { - damage[loc] = (damage[loc] || 0) + value; - type[loc] = type[loc] || {}; - type[loc]["夹击伤害"] = true; - } - } - } - } + if (enemyId1 != null || enemyId2 != null) { + var leftHp = core.status.hero.hp - (damage[loc] || 0); + if (leftHp > 1) { + // 夹击伤害值 + var value = Math.floor(leftHp / 2); + // 是否不超过怪物伤害值 + if (core.flags.betweenAttackMax) { + var enemyDamage1 = core.getDamage(enemyId1, x, y, floorId); + if (enemyDamage1 != null && enemyDamage1 < value) + value = enemyDamage1; + var enemyDamage2 = core.getDamage(enemyId2, x, y, floorId); + if (enemyDamage2 != null && enemyDamage2 < value) + value = enemyDamage2; + } + if (value > 0) { + damage[loc] = (damage[loc] || 0) + value; + type[loc] = type[loc] || {}; + type[loc]["夹击伤害"] = true; + } + } + } + } - // 取消注释下面这一段可以让护盾抵御阻激夹域伤害 - /* + // 取消注释下面这一段可以让护盾抵御阻激夹域伤害 + /* for (var loc in damage) { damage[loc] = Math.max(0, damage[loc] - core.getRealStatus('mdef')); } */ - core.flags.canGoDeadZone = canGoDeadZone; - core.status.checkBlock = { - damage: damage, - type: type, - repulse: repulse, - ambush: ambush, - needCache: needCache, - cache: {} // clear cache - }; - }, + core.flags.canGoDeadZone = canGoDeadZone; + core.status.checkBlock = { + damage: damage, + type: type, + repulse: repulse, + ambush: ambush, + needCache: needCache, + cache: {}, // clear cache + }; + }, "moveOneStep": function (callback) { - // 勇士每走一步后执行的操作。callback为行走完毕后的回调 - // 这个函数执行在“刚走完”的时候,即还没有检查该点的事件和领域伤害等。 - // 请注意:瞬间移动不会执行该函数。如果要控制能否瞬间移动有三种方法: - // 1. 将全塔属性中的cannotMoveDirectly这个开关勾上,即可在全塔中全程禁止使用瞬移。 - // 2, 将楼层属性中的cannotMoveDirectly这个开关勾上,即禁止在该层楼使用瞬移。 - // 3. 将flag:cannotMoveDirectly置为true,即可使用flag控制在某段剧情范围内禁止瞬移。 + // 勇士每走一步后执行的操作。callback为行走完毕后的回调 + // 这个函数执行在“刚走完”的时候,即还没有检查该点的事件和领域伤害等。 + // 请注意:瞬间移动不会执行该函数。如果要控制能否瞬间移动有三种方法: + // 1. 将全塔属性中的cannotMoveDirectly这个开关勾上,即可在全塔中全程禁止使用瞬移。 + // 2, 将楼层属性中的cannotMoveDirectly这个开关勾上,即禁止在该层楼使用瞬移。 + // 3. 将flag:cannotMoveDirectly置为true,即可使用flag控制在某段剧情范围内禁止瞬移。 - // 增加步数 - core.status.hero.steps++; - // 更新跟随者状态,并绘制 - core.updateFollowers(); - core.drawHero(); - // 检查中毒状态的扣血和死亡 - if (core.hasFlag('poison')) { - core.status.hero.statistics.poisonDamage += core.values.poisonDamage; - core.status.hero.hp -= core.values.poisonDamage; - if (core.status.hero.hp <= 0) { - core.status.hero.hp = 0; - core.updateStatusBar(false, true); - core.events.lose(); - return; - } else { - core.updateStatusBar(false, true); - } - } + // 增加步数 + core.status.hero.steps++; + // 更新跟随者状态,并绘制 + core.updateFollowers(); + core.drawHero(); + // 检查中毒状态的扣血和死亡 + if (core.hasFlag("poison")) { + core.status.hero.statistics.poisonDamage += core.values.poisonDamage; + core.status.hero.hp -= core.values.poisonDamage; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(false, true); + core.events.lose(); + return; + } else { + core.updateStatusBar(false, true); + } + } - // 从v2.7开始,每一步行走不会再刷新状态栏。 - // 如果有特殊要求(如每走一步都加buff之类),可手动取消注释下面这一句: - // core.updateStatusBar(true, true); + // 从v2.7开始,每一步行走不会再刷新状态栏。 + // 如果有特殊要求(如每走一步都加buff之类),可手动取消注释下面这一句: + // core.updateStatusBar(true, true); - // 检查自动事件 - core.checkAutoEvents(); + // 检查自动事件 + core.checkAutoEvents(); - // ------ 检查目标点事件 ------ // - // 无事件的道具(如血瓶)需要优先于阻激夹域判定 - var nowx = core.getHeroLoc('x'), - nowy = core.getHeroLoc('y'); - var block = core.getBlock(nowx, nowy); - var hasTrigger = false; - if (block != null && block.event.trigger == 'getItem' && - !core.floors[core.status.floorId].afterGetItem[nowx + "," + nowy]) { - hasTrigger = true; - core.trigger(nowx, nowy, callback); - } - // 执行目标点的阻激夹域事件 - core.checkBlock(); + // ------ 检查目标点事件 ------ // + // 无事件的道具(如血瓶)需要优先于阻激夹域判定 + var nowx = core.getHeroLoc("x"), + nowy = core.getHeroLoc("y"); + var block = core.getBlock(nowx, nowy); + var hasTrigger = false; + if ( + block != null && + block.event.trigger == "getItem" && + !core.floors[core.status.floorId].afterGetItem[nowx + "," + nowy] + ) { + hasTrigger = true; + core.trigger(nowx, nowy, callback); + } + // 执行目标点的阻激夹域事件 + core.checkBlock(); - // 执行目标点的script和事件 - if (!hasTrigger) - core.trigger(nowx, nowy, callback); + // 执行目标点的script和事件 + if (!hasTrigger) core.trigger(nowx, nowy, callback); - // 检查该点是否是滑冰 - if (core.onSki()) { - // 延迟到事件最后执行,因为这之前可能有阻激夹域动画 - core.insertAction({ "type": "moveAction" }, null, null, null, true); - } + // 检查该点是否是滑冰 + if (core.onSki()) { + // 延迟到事件最后执行,因为这之前可能有阻激夹域动画 + core.insertAction({ type: "moveAction" }, null, null, null, true); + } - // ------ 检查目标点事件 END ------ // + // ------ 检查目标点事件 END ------ // - // 如需强行终止行走可以在这里条件判定: - // core.stopAutomaticRoute(); -}, + // 如需强行终止行走可以在这里条件判定: + // core.stopAutomaticRoute(); + }, "moveDirectly": function (x, y, ignoreSteps) { - // 瞬间移动;x,y为要瞬间移动的点;ignoreSteps为减少的步数,可能之前已经被计算过 - // 返回true代表成功瞬移,false代表没有成功瞬移 + // 瞬间移动;x,y为要瞬间移动的点;ignoreSteps为减少的步数,可能之前已经被计算过 + // 返回true代表成功瞬移,false代表没有成功瞬移 - // 判定能否瞬移到该点 - if (ignoreSteps == null) ignoreSteps = core.canMoveDirectly(x, y); - if (ignoreSteps >= 0) { - // 中毒也允许瞬移 - if (core.hasFlag('poison')) { - var damage = ignoreSteps * core.values.poisonDamage; - if (damage >= core.status.hero.hp) return false; - core.status.hero.statistics.poisonDamage += damage; - core.status.hero.hp -= damage; - } + // 判定能否瞬移到该点 + if (ignoreSteps == null) ignoreSteps = core.canMoveDirectly(x, y); + if (ignoreSteps >= 0) { + // 中毒也允许瞬移 + if (core.hasFlag("poison")) { + var damage = ignoreSteps * core.values.poisonDamage; + if (damage >= core.status.hero.hp) return false; + core.status.hero.statistics.poisonDamage += damage; + core.status.hero.hp -= damage; + } - core.clearMap('hero'); - if ((core.bigmap.width * 32) === (core.bigmap.height * 32) && (core.bigmap.width * 32) === core.__PIXELS__) core.addPopMove(32 * core.status.hero.loc.x + 16, 32 * core.status.hero.loc.y + 16, 32 * x + 16, 32 * y + 16) - // 获得勇士最后的朝向 - var lastDirection = core.status.route[core.status.route.length - 1]; - if (['left', 'right', 'up', 'down'].indexOf(lastDirection) >= 0) - core.setHeroLoc('direction', lastDirection); - // 设置坐标,并绘制 - core.control._moveDirectyFollowers(x, y); - core.status.hero.loc.x = x; - core.status.hero.loc.y = y; - core.drawHero(); - // 记录录像 - core.status.route.push("move:" + x + ":" + y); - // 统计信息 - core.status.hero.statistics.moveDirectly++; - core.status.hero.statistics.ignoreSteps += ignoreSteps; - if (core.hasFlag('poison')) { - core.updateStatusBar(false, true); - } + core.clearMap("hero"); + if ( + core.bigmap.width * 32 === core.bigmap.height * 32 && + core.bigmap.width * 32 === core.__PIXELS__ + ) + core.addPopMove( + 32 * core.status.hero.loc.x + 16, + 32 * core.status.hero.loc.y + 16, + 32 * x + 16, + 32 * y + 16 + ); + // 获得勇士最后的朝向 + var lastDirection = core.status.route[core.status.route.length - 1]; + if (["left", "right", "up", "down"].indexOf(lastDirection) >= 0) + core.setHeroLoc("direction", lastDirection); + // 设置坐标,并绘制 + core.control._moveDirectyFollowers(x, y); + core.status.hero.loc.x = x; + core.status.hero.loc.y = y; + core.drawHero(); + // 记录录像 + core.status.route.push("move:" + x + ":" + y); + // 统计信息 + core.status.hero.statistics.moveDirectly++; + core.status.hero.statistics.ignoreSteps += ignoreSteps; + if (core.hasFlag("poison")) { + core.updateStatusBar(false, true); + } - core.checkRouteFolding(); - return true; - } - return false; -}, + core.checkRouteFolding(); + return true; + } + return false; + }, "parallelDo": function (timestamp) { - // 并行事件处理,可以在这里写任何需要并行处理的脚本或事件 - // 该函数将被系统反复执行,每次执行间隔视浏览器或设备性能而定,一般约为16.6ms一次 - // 参数timestamp为“从游戏资源加载完毕到当前函数执行时”的时间差,以毫秒为单位 + // 并行事件处理,可以在这里写任何需要并行处理的脚本或事件 + // 该函数将被系统反复执行,每次执行间隔视浏览器或设备性能而定,一般约为16.6ms一次 + // 参数timestamp为“从游戏资源加载完毕到当前函数执行时”的时间差,以毫秒为单位 - // 检查当前是否处于游戏开始状态 - if (!core.isPlaying()) return; + // 检查当前是否处于游戏开始状态 + if (!core.isPlaying()) return; - // 执行当前楼层的并行事件处理 - if (core.status.floorId) { - try { - eval(core.floors[core.status.floorId].parallelDo); - } catch (e) { - console.error(e); - } - } - } + // 执行当前楼层的并行事件处理 + if (core.status.floorId) { + try { + eval(core.floors[core.status.floorId].parallelDo); + } catch (e) { + console.error(e); + } + } + } }, "ui": { "getToolboxItems": function (cls) { - // 获得道具栏中当前某类型道具的显示项和显示顺序 - // cls为道具类型,只可能是 tools, constants 和 equips - // 返回一个数组,代表当前某类型道具的显示内容和顺序 - // 默认按id升序排列,您可以取消下面的注释改为按名称排列 + // 获得道具栏中当前某类型道具的显示项和显示顺序 + // cls为道具类型,只可能是 tools, constants 和 equips + // 返回一个数组,代表当前某类型道具的显示内容和顺序 + // 默认按id升序排列,您可以取消下面的注释改为按名称排列 - return Object.keys(core.status.hero.items[cls] || {}) - .filter(function (id) { return !core.material.items[id].hideInToolbox; }) - .sort( /*function (id1, id2) { return core.material.items[id1].name <= core.material.items[id2].name ? -1 : 1 }*/ ); -}, + return Object.keys(core.status.hero.items[cls] || {}) + .filter(function (id) { + return !core.material.items[id].hideInToolbox; + }) + .sort(/*function (id1, id2) { return core.material.items[id1].name <= core.material.items[id2].name ? -1 : 1 }*/); + }, "drawStatusBar": function () { - // 这真的是人能写出来的东西? - var ctx, fill = function (text, x, y, style) { - core.ui.setFont(ctx, (/\w+/.test(text) ? 'italic ' : '') + 'bold 18px Verdana'); - core.ui.fillBoldText(ctx, text, x, y, style); - }; - if (core.flags.statusCanvas) { // 系统开关「自绘状态栏」开启 - core.ui.clearMap(ctx = core.dom.statusCanvasCtx); // 清空状态栏 - core.ui.setFillStyle(ctx, core.status.globalAttribute.statusBarColor); - if (core.domStyle.isVertical) { // 竖屏 - core.drawImage(ctx, core.statusBar.icons.floor, 6, 6, 25, 25); - fill((core.status.thisMap || {}).name || "Loading", 42, 26); - core.drawImage(ctx, core.statusBar.icons.hp, 137, 6, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('hp')), 173, 26); - core.drawImage(ctx, core.statusBar.icons.atk, 268, 6, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('atk')), 304, 26); - core.drawImage(ctx, core.statusBar.icons.def, 6, 38, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('def')), 42, 58); - core.drawImage(ctx, core.statusBar.icons.mdef, 137, 38, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('mdef')), 173, 58); - core.drawImage(ctx, core.statusBar.icons.money, 268, 38, 25, 25); - fill(core.formatBigNumber(core.status.hero.money), 304, 58); - core.drawImage(ctx, core.statusBar.icons.exp, 6, 70, 25, 25); - fill(core.formatBigNumber(core.status.hero.exp), 42, 90); - } else if (!core.flags.hideLeftStatusBar) { // 横屏且未隐藏状态栏 - core.drawImage(ctx, core.statusBar.icons.floor, 6, 9, 25, 25); - fill((core.status.thisMap || {}).name || "Loading", 42, 29); - core.drawImage(ctx, core.statusBar.icons.hp, 6, 43, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('hp')), 42, 63); - core.drawImage(ctx, core.statusBar.icons.atk, 6, 77, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('atk')), 42, 97); - core.drawImage(ctx, core.statusBar.icons.def, 6, 111, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('def')), 42, 131); - core.drawImage(ctx, core.statusBar.icons.mdef, 6, 145, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('mdef')), 42, 165); - core.drawImage(ctx, core.statusBar.icons.money, 6, 179, 25, 25); - fill(core.formatBigNumber(core.status.hero.money), 42, 199); - core.drawImage(ctx, core.statusBar.icons.exp, 6, 213, 25, 25); - fill(core.formatBigNumber(core.status.hero.exp), 42, 233); - fill(core.setTwoDigits(core.itemCount('yellowKey')), 11, 267, '#FFCCAA'); - fill(core.setTwoDigits(core.itemCount('blueKey')), 46, 267, '#AAAADD'); - fill(core.setTwoDigits(core.itemCount('redKey')), 81, 267, '#FF8888'); - } - } else if (core.flags.hideLeftStatusBar && !core.domStyle.isVertical) { // 横屏且隐藏状态栏 - if (!core.dymCanvas['status']) - core.ui.createCanvas('status', 0, 0, core._PX_, core._PY_, 66); // 刚好盖过显伤层 - core.ui.clearMap(ctx = core.dymCanvas['status']); - core.ui.setFillStyle(ctx, core.status.globalAttribute.statusBarColor); - var offset = core.status.hero.loc.x - core.bigmap.offsetX / 32 >= core._HEIGHT_ ? 0 : core._PY_; - core.ui.setAlpha(ctx, 0.75); - core.ui.drawWindowSkin('winskin.webp', ctx, offset, 0, core._PX_ - core._PY_, core._PY_); - core.ui.setAlpha(ctx, 1); - core.drawImage(ctx, core.statusBar.icons.floor, 6 + offset, 9, 25, 25); - fill((core.status.thisMap || {}).name || "Loading", 42 + offset, 29); - core.drawImage(ctx, core.statusBar.icons.hp, 6 + offset, 43, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('hp')), 42 + offset, 63); - core.drawImage(ctx, core.statusBar.icons.atk, 6 + offset, 77, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('atk')), 42 + offset, 97); - core.drawImage(ctx, core.statusBar.icons.def, 6 + offset, 111, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('def')), 42 + offset, 131); - core.drawImage(ctx, core.statusBar.icons.mdef, 6 + offset, 145, 25, 25); - fill(core.formatBigNumber(core.getRealStatus('mdef')), 42 + offset, 165); - core.drawImage(ctx, core.statusBar.icons.money, 6 + offset, 179, 25, 25); - fill(core.formatBigNumber(core.status.hero.money), 42 + offset, 199); - core.drawImage(ctx, core.statusBar.icons.exp, 6 + offset, 213, 25, 25); - fill(core.formatBigNumber(core.status.hero.exp), 42 + offset, 233); - fill(core.setTwoDigits(core.itemCount('yellowKey')), 11 + offset, 267, '#FFCCAA'); - fill(core.setTwoDigits(core.itemCount('blueKey')), 46 + offset, 267, '#AAAADD'); - fill(core.setTwoDigits(core.itemCount('redKey')), 81 + offset, 267, '#FF8888'); - } -}, + // 这真的是人能写出来的东西? + var ctx, + fill = function (text, x, y, style) { + core.ui.setFont( + ctx, + (/\w+/.test(text) ? "italic " : "") + "bold 18px Verdana" + ); + core.ui.fillBoldText(ctx, text, x, y, style); + }; + if (core.flags.statusCanvas) { + // 系统开关「自绘状态栏」开启 + core.ui.clearMap((ctx = core.dom.statusCanvasCtx)); // 清空状态栏 + core.ui.setFillStyle(ctx, core.status.globalAttribute.statusBarColor); + if (core.domStyle.isVertical) { + // 竖屏 + core.drawImage(ctx, core.statusBar.icons.floor, 6, 6, 25, 25); + fill((core.status.thisMap || {}).name || "Loading", 42, 26); + core.drawImage(ctx, core.statusBar.icons.hp, 137, 6, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("hp")), 173, 26); + core.drawImage(ctx, core.statusBar.icons.atk, 268, 6, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("atk")), 304, 26); + core.drawImage(ctx, core.statusBar.icons.def, 6, 38, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("def")), 42, 58); + core.drawImage(ctx, core.statusBar.icons.mdef, 137, 38, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("mdef")), 173, 58); + core.drawImage(ctx, core.statusBar.icons.money, 268, 38, 25, 25); + fill(core.formatBigNumber(core.status.hero.money), 304, 58); + core.drawImage(ctx, core.statusBar.icons.exp, 6, 70, 25, 25); + fill(core.formatBigNumber(core.status.hero.exp), 42, 90); + } else if (!core.flags.hideLeftStatusBar) { + // 横屏且未隐藏状态栏 + core.drawImage(ctx, core.statusBar.icons.floor, 6, 9, 25, 25); + fill((core.status.thisMap || {}).name || "Loading", 42, 29); + core.drawImage(ctx, core.statusBar.icons.hp, 6, 43, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("hp")), 42, 63); + core.drawImage(ctx, core.statusBar.icons.atk, 6, 77, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("atk")), 42, 97); + core.drawImage(ctx, core.statusBar.icons.def, 6, 111, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("def")), 42, 131); + core.drawImage(ctx, core.statusBar.icons.mdef, 6, 145, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("mdef")), 42, 165); + core.drawImage(ctx, core.statusBar.icons.money, 6, 179, 25, 25); + fill(core.formatBigNumber(core.status.hero.money), 42, 199); + core.drawImage(ctx, core.statusBar.icons.exp, 6, 213, 25, 25); + fill(core.formatBigNumber(core.status.hero.exp), 42, 233); + fill( + core.setTwoDigits(core.itemCount("yellowKey")), + 11, + 267, + "#FFCCAA" + ); + fill( + core.setTwoDigits(core.itemCount("blueKey")), + 46, + 267, + "#AAAADD" + ); + fill(core.setTwoDigits(core.itemCount("redKey")), 81, 267, "#FF8888"); + } + } else if (core.flags.hideLeftStatusBar && !core.domStyle.isVertical) { + // 横屏且隐藏状态栏 + if (!core.dymCanvas["status"]) + core.ui.createCanvas("status", 0, 0, core._PX_, core._PY_, 66); // 刚好盖过显伤层 + core.ui.clearMap((ctx = core.dymCanvas["status"])); + core.ui.setFillStyle(ctx, core.status.globalAttribute.statusBarColor); + var offset = + core.status.hero.loc.x - core.bigmap.offsetX / 32 >= core._HEIGHT_ + ? 0 + : core._PY_; + core.ui.setAlpha(ctx, 0.75); + core.ui.drawWindowSkin( + "winskin.webp", + ctx, + offset, + 0, + core._PX_ - core._PY_, + core._PY_ + ); + core.ui.setAlpha(ctx, 1); + core.drawImage(ctx, core.statusBar.icons.floor, 6 + offset, 9, 25, 25); + fill((core.status.thisMap || {}).name || "Loading", 42 + offset, 29); + core.drawImage(ctx, core.statusBar.icons.hp, 6 + offset, 43, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("hp")), 42 + offset, 63); + core.drawImage(ctx, core.statusBar.icons.atk, 6 + offset, 77, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("atk")), 42 + offset, 97); + core.drawImage(ctx, core.statusBar.icons.def, 6 + offset, 111, 25, 25); + fill(core.formatBigNumber(core.getRealStatus("def")), 42 + offset, 131); + core.drawImage(ctx, core.statusBar.icons.mdef, 6 + offset, 145, 25, 25); + fill( + core.formatBigNumber(core.getRealStatus("mdef")), + 42 + offset, + 165 + ); + core.drawImage( + ctx, + core.statusBar.icons.money, + 6 + offset, + 179, + 25, + 25 + ); + fill(core.formatBigNumber(core.status.hero.money), 42 + offset, 199); + core.drawImage(ctx, core.statusBar.icons.exp, 6 + offset, 213, 25, 25); + fill(core.formatBigNumber(core.status.hero.exp), 42 + offset, 233); + fill( + core.setTwoDigits(core.itemCount("yellowKey")), + 11 + offset, + 267, + "#FFCCAA" + ); + fill( + core.setTwoDigits(core.itemCount("blueKey")), + 46 + offset, + 267, + "#AAAADD" + ); + fill( + core.setTwoDigits(core.itemCount("redKey")), + 81 + offset, + 267, + "#FF8888" + ); + } + }, "drawStatistics": function () { - // 浏览地图时参与的统计项目 + // 浏览地图时参与的统计项目 - return [ - 'yellowDoor', 'blueDoor', 'redDoor', 'greenDoor', 'steelDoor', - 'yellowKey', 'blueKey', 'redKey', 'greenKey', 'steelKey', - 'redGem', 'blueGem', 'greenGem', 'yellowGem', - 'redPotion', 'bluePotion', 'greenPotion', 'yellowPotion', 'superPotion', - 'pickaxe', 'bomb', 'centerFly', 'icePickaxe', 'freezeBadge', - 'earthquake', 'upFly', 'downFly', 'jumpShoes', 'lifeWand', - 'poisonWine', 'weakWine', 'curseWine', 'superWine', - 'sword1', 'sword2', 'sword3', 'sword4', 'sword5', - 'shield1', 'shield2', 'shield3', 'shield4', 'shield5', - // 在这里可以增加新的ID来进行统计个数,只能增加道具ID - ]; - }, + return [ + "yellowDoor", + "blueDoor", + "redDoor", + "greenDoor", + "steelDoor", + "yellowKey", + "blueKey", + "redKey", + "greenKey", + "steelKey", + "redGem", + "blueGem", + "greenGem", + "yellowGem", + "redPotion", + "bluePotion", + "greenPotion", + "yellowPotion", + "superPotion", + "pickaxe", + "bomb", + "centerFly", + "icePickaxe", + "freezeBadge", + "earthquake", + "upFly", + "downFly", + "jumpShoes", + "lifeWand", + "poisonWine", + "weakWine", + "curseWine", + "superWine", + "sword1", + "sword2", + "sword3", + "sword4", + "sword5", + "shield1", + "shield2", + "shield3", + "shield4", + "shield5", + // 在这里可以增加新的ID来进行统计个数,只能增加道具ID + ]; + }, "drawAbout": function () { - // 绘制“关于”界面 - core.ui.closePanel(); - core.lockControl(); - core.status.event.id = 'about'; + // 绘制“关于”界面 + core.ui.closePanel(); + core.lockControl(); + core.status.event.id = "about"; - var left = 48, - top = 36, - right = (core._PX_ || core.__PIXELS__) - 2 * left, - bottom = (core._PY_ || core.__PIXELS__) - 2 * top; + var left = 48, + top = 36, + right = (core._PX_ || core.__PIXELS__) - 2 * left, + bottom = (core._PY_ || core.__PIXELS__) - 2 * top; - core.setAlpha('ui', 0.85); - core.fillRect('ui', left, top, right, bottom, '#000000'); - core.setAlpha('ui', 1); - core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); + core.setAlpha("ui", 0.85); + core.fillRect("ui", left, top, right, bottom, "#000000"); + core.setAlpha("ui", 1); + core.strokeRect( + "ui", + left - 1, + top - 1, + right + 1, + bottom + 1, + "#FFFFFF", + 2 + ); - var text_start = left + 24; + var text_start = left + 24; - // 名称 - core.setTextAlign('ui', 'left'); - var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; - core.fillText('ui', "HTML5 魔塔样板", text_start, top + 35, globalAttribute.selectColor, "bold 22px " + globalAttribute.font); - core.fillText('ui', "版本: " + main.__VERSION__, text_start, top + 80, "#FFFFFF", "bold 17px " + globalAttribute.font); - core.fillText('ui', "作者: 艾之葵", text_start, top + 112); - core.fillText('ui', 'HTML5魔塔交流群:539113091', text_start, top + 112 + 32); - // TODO: 写自己的“关于”页面,每次增加32像素即可 - core.playSound('打开界面'); - } + // 名称 + core.setTextAlign("ui", "left"); + var globalAttribute = + core.status.globalAttribute || core.initStatus.globalAttribute; + core.fillText( + "ui", + "HTML5 魔塔样板", + text_start, + top + 35, + globalAttribute.selectColor, + "bold 22px " + globalAttribute.font + ); + core.fillText( + "ui", + "版本: " + main.__VERSION__, + text_start, + top + 80, + "#FFFFFF", + "bold 17px " + globalAttribute.font + ); + core.fillText("ui", "作者: 艾之葵", text_start, top + 112); + core.fillText( + "ui", + "HTML5魔塔交流群:539113091", + text_start, + top + 112 + 32 + ); + // TODO: 写自己的“关于”页面,每次增加32像素即可 + core.playSound("打开界面"); + } } } \ No newline at end of file diff --git a/project/items.js b/project/items.js index edc41cf..0507d29 100644 --- a/project/items.js +++ b/project/items.js @@ -332,7 +332,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "text": "可以自由往来去过的楼层", "hideInReplay": true, "hideInToolbox": true, - "useItemEffect": "//core.ui.drawFly(core.floorIds.indexOf(core.status.floorId));\nflags.canMoveFloor = core.canMoveFloor()\nif (core.isPlaying()) core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId));", + "useItemEffect": "//core.ui.drawFly(core.floorIds.indexOf(core.status.floorId));\nflags.canMoveFloor = core.canMoveFloor()\nif (!main.replayChecking && !core.isReplaying()) core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId));", "canUseItemEffect": "(function () {\n\tif (core.flags.flyNearStair && !core.nearStair() && !core.canMoveFloor())\n\t\treturn false;\n\treturn core.status.maps[core.status.floorId].canFlyFrom;\n})();" }, "coin": { diff --git a/project/maps.js b/project/maps.js index 407c757..67560d4 100644 --- a/project/maps.js +++ b/project/maps.js @@ -1,306 +1,670 @@ -var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = -{ - "1": {"cls":"animates","id":"yellowWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, - "2": {"cls":"animates","id":"whiteWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, - "3": {"cls":"animates","id":"blueWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, - "4": {"cls":"animates","id":"star","name":"星空"}, - "5": {"cls":"animates","id":"lava","name":"岩浆"}, - "6": {"cls":"animates","id":"ice","doorInfo":{"time":160,"openSound":"破冰镐","closeSound":"door.opus","keys":{"icePickaxe":1}},"animate":1}, - "7": {"cls":"terrains","id":"blueShopLeft"}, - "8": {"cls":"terrains","id":"blueShopRight"}, - "9": {"cls":"terrains","id":"pinkShopLeft"}, - "10": {"cls":"terrains","id":"pinkShopRight"}, - "11": {"cls":"animates","id":"lavaNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 血网的伤害效果移动到 checkBlock 中处理\n\n\t// 如果要做一次性血网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();","name":"血网"}, - "12": {"cls":"animates","id":"poisonNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'poison');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"毒网"}, - "13": {"cls":"animates","id":"weakNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'weak');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"衰网"}, - "14": {"cls":"animates","id":"curseNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'curse');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"咒网"}, - "15": {"cls":"animates","id":"blueLava"}, - "16": {"cls":"animates","id":"water"}, - "20": {"cls":"autotile","id":"autotile"}, - "21": {"cls":"items","id":"yellowKey"}, - "22": {"cls":"items","id":"blueKey"}, - "23": {"cls":"items","id":"redKey"}, - "24": {"cls":"items","id":"greenKey"}, - "25": {"cls":"items","id":"steelKey"}, - "26": {"cls":"items","id":"bigKey"}, - "27": {"cls":"items","id":"redGem"}, - "28": {"cls":"items","id":"blueGem"}, - "29": {"cls":"items","id":"greenGem"}, - "30": {"cls":"items","id":"yellowGem"}, - "31": {"cls":"items","id":"redPotion"}, - "32": {"cls":"items","id":"bluePotion"}, - "33": {"cls":"items","id":"greenPotion"}, - "34": {"cls":"items","id":"yellowPotion"}, - "35": {"cls":"items","id":"sword1"}, - "36": {"cls":"items","id":"shield1"}, - "37": {"cls":"items","id":"sword2"}, - "38": {"cls":"items","id":"shield2"}, - "39": {"cls":"items","id":"sword3"}, - "40": {"cls":"items","id":"shield3"}, - "41": {"cls":"items","id":"sword4"}, - "42": {"cls":"items","id":"shield4"}, - "43": {"cls":"items","id":"sword5"}, - "44": {"cls":"items","id":"shield5"}, - "45": {"cls":"items","id":"book"}, - "46": {"cls":"items","id":"fly"}, - "47": {"cls":"items","id":"pickaxe"}, - "48": {"cls":"items","id":"icePickaxe"}, - "49": {"cls":"items","id":"bomb"}, - "50": {"cls":"items","id":"centerFly"}, - "51": {"cls":"items","id":"upFly"}, - "52": {"cls":"items","id":"downFly"}, - "53": {"cls":"items","id":"coin"}, - "54": {"cls":"items","id":"freezeBadge"}, - "55": {"cls":"items","id":"cross"}, - "56": {"cls":"items","id":"superPotion"}, - "57": {"cls":"items","id":"earthquake"}, - "58": {"cls":"items","id":"poisonWine"}, - "59": {"cls":"items","id":"weakWine"}, - "60": {"cls":"items","id":"curseWine"}, - "61": {"cls":"items","id":"superWine"}, - "62": {"cls":"items","id":"dagger"}, - "63": {"cls":"items","id":"pack"}, - "64": {"cls":"items","id":"amulet"}, - "65": {"cls":"items","id":"hammer"}, - "68": {"cls":"items","id":"lifeWand"}, - "69": {"cls":"items","id":"jumpShoes"}, - "70": {"cls":"items","id":"sword0"}, - "71": {"cls":"items","id":"shield0"}, - "72": {"cls":"items","id":"skill1"}, - "73": {"cls":"items","id":"wand"}, - "81": {"cls":"animates","id":"yellowDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"yellowKey":1}},"name":"黄门"}, - "82": {"cls":"animates","id":"blueDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"blueKey":1}},"name":"蓝门"}, - "83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"redKey":1}},"name":"红门"}, - "84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"greenKey":1}},"name":"绿门"}, - "85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"specialKey":1}},"name":"机关门"}, - "86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"steelKey":1}},"name":"铁门"}, - "87": {"cls":"terrains","id":"upFloor","canPass":true}, - "88": {"cls":"terrains","id":"downFloor","canPass":true}, - "89": {"cls":"animates","id":"portal","canPass":true}, - "90": {"cls":"animates","id":"starPortal","canPass":true}, - "91": {"cls":"animates","id":"upPortal","canPass":true}, - "92": {"cls":"animates","id":"leftPortal","canPass":true}, - "93": {"cls":"animates","id":"downPortal","canPass":true}, - "94": {"cls":"animates","id":"rightPortal","canPass":true}, - "101": {"cls":"animates","id":"crystalUp"}, - "102": {"cls":"animates","id":"crystalBottom"}, - "103": {"cls":"animates","id":"fire"}, - "104": {"cls":"animates","id":"switch"}, - "109": {"cls":"animates","id":"magentaWall","canBreak":true,"animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{}}}, - "121": {"cls":"npcs","id":"man"}, - "122": {"cls":"npcs","id":"trader"}, - "123": {"cls":"npcs","id":"thief"}, - "124": {"cls":"npcs","id":"fairy"}, - "125": {"cls":"npcs","id":"wizard"}, - "126": {"cls":"npcs","id":"recluse"}, - "127": {"cls":"npcs","id":"king"}, - "128": {"cls":"npcs","id":"youngMan"}, - "129": {"cls":"npcs","id":"sign"}, - "130": {"cls":"npcs","id":"expShop"}, - "131": {"cls":"npcs","id":"moneyShop"}, - "132": {"cls":"npcs","id":"princess"}, - "133": {"cls":"npc48","id":"npc0","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, - "134": {"cls":"npc48","id":"npc1","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, - "135": {"cls":"npc48","id":"npc2","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, - "136": {"cls":"npc48","id":"npc3","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"},"animate":1}, - "137": {"cls":"npcs","id":"greenMan"}, - "138": {"cls":"npcs","id":"blueTrader"}, - "140": {"cls":"autotile","id":"autotile4"}, - "141": {"cls":"autotile","id":"autotile8"}, - "142": {"cls":"autotile","id":"autotile9"}, - "143": {"cls":"autotile","id":"autotile10"}, - "144": {"cls":"autotile","id":"autotile12"}, - "151": {"cls":"autotile","id":"autotile1"}, - "152": {"cls":"autotile","id":"autotile2"}, - "153": {"cls":"autotile","id":"autotile3"}, - "161": {"cls":"terrains","id":"arrowUp","canPass":true,"cannotOut":["left","right","down"],"cannotIn":["up"]}, - "162": {"cls":"terrains","id":"arrowDown","canPass":true,"cannotOut":["left","right","up"],"cannotIn":["down"]}, - "163": {"cls":"terrains","id":"arrowLeft","canPass":true,"cannotOut":["up","down","right"],"cannotIn":["left"]}, - "164": {"cls":"terrains","id":"arrowRight","canPass":true,"cannotOut":["up","down","left"],"cannotIn":["right"]}, - "165": {"cls":"terrains","id":"light","trigger":"null","canPass":true,"script":"(function () {\n\tcore.setBlock(core.getNumberById('darkLight'), core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();"}, - "166": {"cls":"terrains","id":"darkLight"}, - "167": {"cls":"terrains","id":"ski","trigger":"ski","canPass":true}, - "168": {"cls":"terrains","id":"flower","canPass":true}, - "169": {"cls":"terrains","id":"box","trigger":"pushBox"}, - "170": {"cls":"terrains","id":"boxed","trigger":"pushBox"}, - "201": {"cls":"enemys","id":"greenSlime"}, - "202": {"cls":"enemys","id":"redSlime"}, - "203": {"cls":"enemys","id":"blackSlime"}, - "204": {"cls":"enemys","id":"slimelord"}, - "205": {"cls":"enemys","id":"bat"}, - "206": {"cls":"enemys","id":"bigBat"}, - "207": {"cls":"enemys","id":"redBat"}, - "208": {"cls":"enemys","id":"vampire"}, - "209": {"cls":"enemys","id":"skeleton"}, - "210": {"cls":"enemys","id":"skeletonWarrior"}, - "211": {"cls":"enemys","id":"skeletonCaptain"}, - "212": {"cls":"enemys","id":"ghostSoldier"}, - "213": {"cls":"enemys","id":"zombie"}, - "214": {"cls":"enemys","id":"zombieKnight"}, - "215": {"cls":"enemys","id":"rock"}, - "216": {"cls":"enemys","id":"slimeman"}, - "217": {"cls":"enemys","id":"bluePriest"}, - "218": {"cls":"enemys","id":"redPriest"}, - "219": {"cls":"enemys","id":"brownWizard"}, - "220": {"cls":"enemys","id":"redWizard"}, - "221": {"cls":"enemys","id":"yellowGateKeeper"}, - "222": {"cls":"enemys","id":"blueGateKeeper"}, - "223": {"cls":"enemys","id":"redGateKeeper"}, - "224": {"cls":"enemys","id":"swordsman"}, - "225": {"cls":"enemys","id":"soldier"}, - "226": {"cls":"enemys","id":"yellowKnight"}, - "227": {"cls":"enemys","id":"redKnight"}, - "228": {"cls":"enemys","id":"darkKnight"}, - "229": {"cls":"enemys","id":"blackKing"}, - "230": {"cls":"enemys","id":"yellowKing"}, - "231": {"cls":"enemys","id":"greenKing"}, - "232": {"cls":"enemys","id":"blueKnight"}, - "233": {"cls":"enemys","id":"goldSlime"}, - "234": {"cls":"enemys","id":"poisonSkeleton"}, - "235": {"cls":"enemys","id":"poisonBat"}, - "236": {"cls":"enemys","id":"ironRock"}, - "237": {"cls":"enemys","id":"skeletonPriest"}, - "238": {"cls":"enemys","id":"skeletonKing"}, - "239": {"cls":"enemys","id":"skeletonPresbyter"}, - "240": {"cls":"enemys","id":"skeletonKnight"}, - "241": {"cls":"enemys","id":"evilHero"}, - "242": {"cls":"enemys","id":"devilWarrior"}, - "243": {"cls":"enemys","id":"demonPriest"}, - "244": {"cls":"enemys","id":"goldHornSlime"}, - "245": {"cls":"enemys","id":"redKing"}, - "246": {"cls":"enemys","id":"blueKing"}, - "247": {"cls":"enemys","id":"magicMaster"}, - "248": {"cls":"enemys","id":"silverSlime"}, - "249": {"cls":"enemys","id":"blademaster"}, - "250": {"cls":"enemys","id":"whiteHornSlime"}, - "251": {"cls":"enemys","id":"evilPrincess"}, - "252": {"cls":"enemys","id":"evilFairy"}, - "253": {"cls":"enemys","id":"yellowPriest"}, - "254": {"cls":"enemys","id":"redSwordsman"}, - "255": {"cls":"enemys","id":"whiteSlimeman"}, - "256": {"cls":"enemys","id":"poisonZombie"}, - "257": {"cls":"enemys","id":"dragon"}, - "258": {"cls":"enemys","id":"octopus"}, - "259": {"cls":"enemys","id":"fairyEnemy"}, - "260": {"cls":"enemys","id":"princessEnemy"}, - "261": {"cls":"enemy48","id":"angel"}, - "262": {"cls":"enemy48","id":"elemental"}, - "263": {"cls":"enemy48","id":"steelGuard"}, - "264": {"cls":"enemy48","id":"evilBat"}, - "265": {"cls":"enemys","id":"silverSlimelord"}, - "266": {"cls":"enemys","id":"goldSlimelord"}, - "267": {"cls":"enemys","id":"grayRock"}, - "270": {"cls":"enemys","id":"greenKnight"}, - "271": {"cls":"enemys","id":"bowman"}, - "272": {"cls":"enemys","id":"purpleBowman"}, - "275": {"cls":"enemys","id":"watcherSlime"}, - "277": {"cls":"enemys","id":"frostBat"}, - "278": {"cls":"enemys","id":"devilKnight"}, - "279": {"cls":"enemys","id":"grayPriest"}, - "280": {"cls":"enemys","id":"greenGateKeeper"}, - "300": {"cls":"terrains","id":"ground"}, - "301": {"cls":"terrains","id":"sWallT","name":"薄墙-上","cannotOut":["up"],"cannotIn":["up"]}, - "302": {"cls":"terrains","id":"sWallL","name":"薄墙-左","cannotOut":["left"],"cannotIn":["left"]}, - "303": {"cls":"terrains","id":"sWallR","name":"薄墙-右","cannotOut":["right"],"cannotIn":["right"]}, - "304": {"cls":"terrains","id":"sWallB","name":"薄墙-下","cannotOut":["down"],"cannotIn":["down"]}, - "305": {"cls":"terrains","id":"grass"}, - "306": {"cls":"terrains","id":"sWallTL","name":"薄墙-上左","cannotOut":["up","left"],"cannotIn":["up","left"]}, - "307": {"cls":"terrains","id":"sWallBR","name":"薄墙-下右","cannotOut":["down","right"],"cannotIn":["down","right"]}, - "308": {"cls":"terrains","id":"grass2"}, - "309": {"cls":"terrains","id":"sWallTR","name":"薄墙-上右","cannotOut":["up","right"],"cannotIn":["up","right"]}, - "310": {"cls":"terrains","id":"sWallBL","name":"薄墙-下左","cannotOut":["down","left"],"cannotIn":["down","left"]}, - "311": {"cls":"terrains","id":"ground2"}, - "312": {"cls":"terrains","id":"sWallTB","name":"薄墙-上下","cannotOut":["up","down"],"cannotIn":["up","down"]}, - "313": {"cls":"terrains","id":"ground3"}, - "314": {"cls":"terrains","id":"sWallLR","name":"薄墙-左右","cannotOut":["left","right"],"cannotIn":["left","right"]}, - "315": {"cls":"terrains","id":"sWallBLR","name":"薄墙-下左右","cannotOut":["down","left","right"],"cannotIn":["down","left","right"]}, - "316": {"cls":"terrains","id":"sWallTLR","name":"薄墙-上左右","cannotOut":["up","left","right"],"cannotIn":["up","left","right"]}, - "317": {"cls":"terrains","id":"sWallTBR","name":"薄墙-上下右","cannotOut":["up","down","right"],"cannotIn":["up","down","right"]}, - "318": {"cls":"terrains","id":"sWallTBL","name":"薄墙-上下左","cannotOut":["up","down","left"],"cannotIn":["up","down","left"]}, - "319": {"cls":"npc48","id":"tallYellowDoor","trigger":"openDoor","name":"高黄门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"yellowKey":1}}}, - "320": {"cls":"npc48","id":"tallBlueDoor","trigger":"openDoor","name":"高蓝门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"blueKey":1}}}, - "321": {"cls":"npc48","id":"tallRedDoor","trigger":"openDoor","name":"高红门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"redKey":1}}}, - "322": {"cls":"npc48","id":"tallGreenDoor","trigger":"openDoor","name":"高绿门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"greenKey":1}}}, - "323": {"cls":"npc48","id":"tallSpecialDoor","trigger":"openDoor","name":"高机关门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"specialKey":1}}}, - "324": {"cls":"npc48","id":"tallSteelDoor","trigger":"openDoor","name":"高铁门","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.opus","keys":{"steelKey":1}}}, - "325": {"cls":"enemys","id":"keiskeiFairy"}, - "326": {"cls":"enemys","id":"tulipFairy"}, - "327": {"cls":"enemy48","id":"bearDown"}, - "328": {"cls":"enemy48","id":"bearLeft"}, - "329": {"cls":"enemy48","id":"bearRight"}, - "330": {"cls":"enemy48","id":"bearUp"}, - "331": {"cls":"terrains","id":"T331"}, - "332": {"cls":"terrains","id":"T332"}, - "333": {"cls":"terrains","id":"T333"}, - "334": {"cls":"terrains","id":"T334"}, - "335": {"cls":"terrains","id":"T335"}, - "336": {"cls":"terrains","id":"T336"}, - "337": {"cls":"terrains","id":"T337"}, - "338": {"cls":"terrains","id":"T338"}, - "339": {"cls":"terrains","id":"T339"}, - "340": {"cls":"terrains","id":"T340"}, - "341": {"cls":"terrains","id":"T341"}, - "342": {"cls":"terrains","id":"T342"}, - "343": {"cls":"terrains","id":"T343"}, - "344": {"cls":"terrains","id":"T344"}, - "345": {"cls":"terrains","id":"T345","canPass":true}, - "346": {"cls":"terrains","id":"T346"}, - "347": {"cls":"terrains","id":"T347"}, - "348": {"cls":"terrains","id":"T348"}, - "349": {"cls":"terrains","id":"T349"}, - "350": {"cls":"terrains","id":"T350"}, - "351": {"cls":"terrains","id":"T351"}, - "352": {"cls":"terrains","id":"T352"}, - "353": {"cls":"terrains","id":"T353"}, - "354": {"cls":"terrains","id":"T354"}, - "355": {"cls":"terrains","id":"T355"}, - "356": {"cls":"terrains","id":"T356"}, - "357": {"cls":"terrains","id":"T357"}, - "358": {"cls":"terrains","id":"T358"}, - "359": {"cls":"terrains","id":"T359"}, - "360": {"cls":"terrains","id":"T360"}, - "361": {"cls":"terrains","id":"T361"}, - "362": {"cls":"terrains","id":"T362"}, - "363": {"cls":"terrains","id":"T363"}, - "364": {"cls":"terrains","id":"T364"}, - "365": {"cls":"terrains","id":"T365"}, - "376": {"cls":"terrains","id":"T376"}, - "377": {"cls":"terrains","id":"T377"}, - "378": {"cls":"terrains","id":"T378"}, - "379": {"cls":"terrains","id":"T379"}, - "380": {"cls":"terrains","id":"T380"}, - "381": {"cls":"terrains","id":"T381"}, - "382": {"cls":"terrains","id":"T382"}, - "383": {"cls":"terrains","id":"T383"}, - "384": {"cls":"terrains","id":"T384"}, - "385": {"cls":"terrains","id":"T385"}, - "386": {"cls":"terrains","id":"T386"}, - "387": {"cls":"terrains","id":"T387"}, - "388": {"cls":"terrains","id":"T388"}, - "389": {"cls":"terrains","id":"T389"}, - "390": {"cls":"terrains","id":"T390"}, - "391": {"cls":"terrains","id":"T391"}, - "392": {"cls":"terrains","id":"T392"}, - "393": {"cls":"terrains","id":"T393"}, - "394": {"cls":"terrains","id":"T394"}, - "395": {"cls":"terrains","id":"T395"}, - "20034": {"cls":"tileset","id":"X20034","canPass":true}, - "20154": {"cls":"tileset","id":"X20154","canPass":true}, - "20216": {"cls":"tileset","id":"X20216","canPass":true}, - "20217": {"cls":"tileset","id":"X20217","canPass":true}, - "40054": {"cls":"tileset","id":"X40054","canPass":false}, - "40090": {"cls":"tileset","id":"X40090","canPass":true}, - "50054": {"cls":"tileset","id":"X50054","canPass":false}, - "70505": {"cls":"tileset","id":"X70505","canPass":true}, - "71182": {"cls":"tileset","id":"X71182","canPass":false}, - "71183": {"cls":"tileset","id":"X71183","canPass":false}, - "71229": {"cls":"tileset","id":"X71229","canPass":true}, - "71818": {"cls":"tileset","id":"X71818","canPass":true}, - "71887": {"cls":"tileset","id":"X71887","canPass":true}, - "80003": {"cls":"tileset","id":"X80003","canPass":true}, - "80007": {"cls":"tileset","id":"X80007","canPass":true}, - "80010": {"cls":"tileset","id":"X80010","canPass":true}, - "80011": {"cls":"tileset","id":"X80011","canPass":true}, - "80020": {"cls":"tileset","id":"X80020","canPass":true}, - "90226": {"cls":"tileset","id":"X90226","canPass":false} -} \ No newline at end of file +var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = { + 1: { + cls: "animates", + id: "yellowWall", + canBreak: true, + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: {}, + }, + }, + 2: { + cls: "animates", + id: "whiteWall", + canBreak: true, + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: {}, + }, + }, + 3: { + cls: "animates", + id: "blueWall", + canBreak: true, + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: {}, + }, + }, + 4: { cls: "animates", id: "star", name: "星空" }, + 5: { cls: "animates", id: "lava", name: "岩浆" }, + 6: { + cls: "animates", + id: "ice", + doorInfo: { + time: 160, + openSound: "破冰镐", + closeSound: "door.opus", + keys: { icePickaxe: 1 }, + }, + animate: 1, + }, + 7: { cls: "terrains", id: "blueShopLeft" }, + 8: { cls: "terrains", id: "blueShopRight" }, + 9: { cls: "terrains", id: "pinkShopLeft" }, + 10: { cls: "terrains", id: "pinkShopRight" }, + 11: { + cls: "animates", + id: "lavaNet", + canPass: true, + trigger: "null", + script: + "(function () {\n\t// 血网的伤害效果移动到 checkBlock 中处理\n\n\t// 如果要做一次性血网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();", + name: "血网", + }, + 12: { + cls: "animates", + id: "poisonNet", + canPass: true, + trigger: "null", + script: + "(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'poison');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()", + name: "毒网", + }, + 13: { + cls: "animates", + id: "weakNet", + canPass: true, + trigger: "null", + script: + "(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'weak');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()", + name: "衰网", + }, + 14: { + cls: "animates", + id: "curseNet", + canPass: true, + trigger: "null", + script: + "(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'curse');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()", + name: "咒网", + }, + 15: { cls: "animates", id: "blueLava" }, + 16: { cls: "animates", id: "water" }, + 20: { cls: "autotile", id: "autotile" }, + 21: { cls: "items", id: "yellowKey" }, + 22: { cls: "items", id: "blueKey" }, + 23: { cls: "items", id: "redKey" }, + 24: { cls: "items", id: "greenKey" }, + 25: { cls: "items", id: "steelKey" }, + 26: { cls: "items", id: "bigKey" }, + 27: { cls: "items", id: "redGem" }, + 28: { cls: "items", id: "blueGem" }, + 29: { cls: "items", id: "greenGem" }, + 30: { cls: "items", id: "yellowGem" }, + 31: { cls: "items", id: "redPotion" }, + 32: { cls: "items", id: "bluePotion" }, + 33: { cls: "items", id: "greenPotion" }, + 34: { cls: "items", id: "yellowPotion" }, + 35: { cls: "items", id: "sword1" }, + 36: { cls: "items", id: "shield1" }, + 37: { cls: "items", id: "sword2" }, + 38: { cls: "items", id: "shield2" }, + 39: { cls: "items", id: "sword3" }, + 40: { cls: "items", id: "shield3" }, + 41: { cls: "items", id: "sword4" }, + 42: { cls: "items", id: "shield4" }, + 43: { cls: "items", id: "sword5" }, + 44: { cls: "items", id: "shield5" }, + 45: { cls: "items", id: "book" }, + 46: { cls: "items", id: "fly" }, + 47: { cls: "items", id: "pickaxe" }, + 48: { cls: "items", id: "icePickaxe" }, + 49: { cls: "items", id: "bomb" }, + 50: { cls: "items", id: "centerFly" }, + 51: { cls: "items", id: "upFly" }, + 52: { cls: "items", id: "downFly" }, + 53: { cls: "items", id: "coin" }, + 54: { cls: "items", id: "freezeBadge" }, + 55: { cls: "items", id: "cross" }, + 56: { cls: "items", id: "superPotion" }, + 57: { cls: "items", id: "earthquake" }, + 58: { cls: "items", id: "poisonWine" }, + 59: { cls: "items", id: "weakWine" }, + 60: { cls: "items", id: "curseWine" }, + 61: { cls: "items", id: "superWine" }, + 62: { cls: "items", id: "dagger" }, + 63: { cls: "items", id: "pack" }, + 64: { cls: "items", id: "amulet" }, + 65: { cls: "items", id: "hammer" }, + 68: { cls: "items", id: "lifeWand" }, + 69: { cls: "items", id: "jumpShoes" }, + 70: { cls: "items", id: "sword0" }, + 71: { cls: "items", id: "shield0" }, + 72: { cls: "items", id: "skill1" }, + 73: { cls: "items", id: "wand" }, + 81: { + cls: "animates", + id: "yellowDoor", + trigger: "openDoor", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { yellowKey: 1 }, + }, + name: "黄门", + }, + 82: { + cls: "animates", + id: "blueDoor", + trigger: "openDoor", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { blueKey: 1 }, + }, + name: "蓝门", + }, + 83: { + cls: "animates", + id: "redDoor", + trigger: "openDoor", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { redKey: 1 }, + }, + name: "红门", + }, + 84: { + cls: "animates", + id: "greenDoor", + trigger: "openDoor", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { greenKey: 1 }, + }, + name: "绿门", + }, + 85: { + cls: "animates", + id: "specialDoor", + trigger: "openDoor", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { specialKey: 1 }, + }, + name: "机关门", + }, + 86: { + cls: "animates", + id: "steelDoor", + trigger: "openDoor", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { steelKey: 1 }, + }, + name: "铁门", + }, + 87: { cls: "terrains", id: "upFloor", canPass: true }, + 88: { cls: "terrains", id: "downFloor", canPass: true }, + 89: { cls: "animates", id: "portal", canPass: true }, + 90: { cls: "animates", id: "starPortal", canPass: true }, + 91: { cls: "animates", id: "upPortal", canPass: true }, + 92: { cls: "animates", id: "leftPortal", canPass: true }, + 93: { cls: "animates", id: "downPortal", canPass: true }, + 94: { cls: "animates", id: "rightPortal", canPass: true }, + 101: { cls: "animates", id: "crystalUp" }, + 102: { cls: "animates", id: "crystalBottom" }, + 103: { cls: "animates", id: "fire" }, + 104: { cls: "animates", id: "switch" }, + 109: { + cls: "animates", + id: "magentaWall", + canBreak: true, + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: {}, + }, + }, + 121: { cls: "npcs", id: "man" }, + 122: { cls: "npcs", id: "trader" }, + 123: { cls: "npcs", id: "thief" }, + 124: { cls: "npcs", id: "fairy" }, + 125: { cls: "npcs", id: "wizard" }, + 126: { cls: "npcs", id: "recluse" }, + 127: { cls: "npcs", id: "king" }, + 128: { cls: "npcs", id: "youngMan" }, + 129: { cls: "npcs", id: "sign" }, + 130: { cls: "npcs", id: "expShop" }, + 131: { cls: "npcs", id: "moneyShop" }, + 132: { cls: "npcs", id: "princess" }, + 133: { + cls: "npc48", + id: "npc0", + faceIds: { down: "npc0", left: "npc1", right: "npc2", up: "npc3" }, + animate: 1, + }, + 134: { + cls: "npc48", + id: "npc1", + faceIds: { down: "npc0", left: "npc1", right: "npc2", up: "npc3" }, + animate: 1, + }, + 135: { + cls: "npc48", + id: "npc2", + faceIds: { down: "npc0", left: "npc1", right: "npc2", up: "npc3" }, + animate: 1, + }, + 136: { + cls: "npc48", + id: "npc3", + faceIds: { down: "npc0", left: "npc1", right: "npc2", up: "npc3" }, + animate: 1, + }, + 137: { cls: "npcs", id: "greenMan" }, + 138: { cls: "npcs", id: "blueTrader" }, + 140: { cls: "autotile", id: "autotile4" }, + 141: { cls: "autotile", id: "autotile8" }, + 142: { cls: "autotile", id: "autotile9" }, + 143: { cls: "autotile", id: "autotile10" }, + 144: { cls: "autotile", id: "autotile12" }, + 151: { cls: "autotile", id: "autotile1" }, + 152: { cls: "autotile", id: "autotile2" }, + 153: { cls: "autotile", id: "autotile3" }, + 161: { + cls: "terrains", + id: "arrowUp", + canPass: true, + cannotOut: ["left", "right", "down"], + cannotIn: ["up"], + }, + 162: { + cls: "terrains", + id: "arrowDown", + canPass: true, + cannotOut: ["left", "right", "up"], + cannotIn: ["down"], + }, + 163: { + cls: "terrains", + id: "arrowLeft", + canPass: true, + cannotOut: ["up", "down", "right"], + cannotIn: ["left"], + }, + 164: { + cls: "terrains", + id: "arrowRight", + canPass: true, + cannotOut: ["up", "down", "left"], + cannotIn: ["right"], + }, + 165: { + cls: "terrains", + id: "light", + trigger: "null", + canPass: true, + script: + "(function () {\n\tcore.setBlock(core.getNumberById('darkLight'), core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();", + }, + 166: { cls: "terrains", id: "darkLight" }, + 167: { cls: "terrains", id: "ski", trigger: "ski", canPass: true }, + 168: { cls: "terrains", id: "flower", canPass: true }, + 169: { cls: "terrains", id: "box", trigger: "pushBox" }, + 170: { cls: "terrains", id: "boxed", trigger: "pushBox" }, + 201: { cls: "enemys", id: "greenSlime" }, + 202: { cls: "enemys", id: "redSlime" }, + 203: { cls: "enemys", id: "blackSlime" }, + 204: { cls: "enemys", id: "slimelord" }, + 205: { cls: "enemys", id: "bat" }, + 206: { cls: "enemys", id: "bigBat" }, + 207: { cls: "enemys", id: "redBat" }, + 208: { cls: "enemys", id: "vampire" }, + 209: { cls: "enemys", id: "skeleton" }, + 210: { cls: "enemys", id: "skeletonWarrior" }, + 211: { cls: "enemys", id: "skeletonCaptain" }, + 212: { cls: "enemys", id: "ghostSoldier" }, + 213: { cls: "enemys", id: "zombie" }, + 214: { cls: "enemys", id: "zombieKnight" }, + 215: { cls: "enemys", id: "rock" }, + 216: { cls: "enemys", id: "slimeman" }, + 217: { cls: "enemys", id: "bluePriest" }, + 218: { cls: "enemys", id: "redPriest" }, + 219: { cls: "enemys", id: "brownWizard" }, + 220: { cls: "enemys", id: "redWizard" }, + 221: { cls: "enemys", id: "yellowGateKeeper" }, + 222: { cls: "enemys", id: "blueGateKeeper" }, + 223: { cls: "enemys", id: "redGateKeeper" }, + 224: { cls: "enemys", id: "swordsman" }, + 225: { cls: "enemys", id: "soldier" }, + 226: { cls: "enemys", id: "yellowKnight" }, + 227: { cls: "enemys", id: "redKnight" }, + 228: { cls: "enemys", id: "darkKnight" }, + 229: { cls: "enemys", id: "blackKing" }, + 230: { cls: "enemys", id: "yellowKing" }, + 231: { cls: "enemys", id: "greenKing" }, + 232: { cls: "enemys", id: "blueKnight" }, + 233: { cls: "enemys", id: "goldSlime" }, + 234: { cls: "enemys", id: "poisonSkeleton" }, + 235: { cls: "enemys", id: "poisonBat" }, + 236: { cls: "enemys", id: "ironRock" }, + 237: { cls: "enemys", id: "skeletonPriest" }, + 238: { cls: "enemys", id: "skeletonKing" }, + 239: { cls: "enemys", id: "skeletonPresbyter" }, + 240: { cls: "enemys", id: "skeletonKnight" }, + 241: { cls: "enemys", id: "evilHero" }, + 242: { cls: "enemys", id: "devilWarrior" }, + 243: { cls: "enemys", id: "demonPriest" }, + 244: { cls: "enemys", id: "goldHornSlime" }, + 245: { cls: "enemys", id: "redKing" }, + 246: { cls: "enemys", id: "blueKing" }, + 247: { cls: "enemys", id: "magicMaster" }, + 248: { cls: "enemys", id: "silverSlime" }, + 249: { cls: "enemys", id: "blademaster" }, + 250: { cls: "enemys", id: "whiteHornSlime" }, + 251: { cls: "enemys", id: "evilPrincess" }, + 252: { cls: "enemys", id: "evilFairy" }, + 253: { cls: "enemys", id: "yellowPriest" }, + 254: { cls: "enemys", id: "redSwordsman" }, + 255: { cls: "enemys", id: "whiteSlimeman" }, + 256: { cls: "enemys", id: "poisonZombie" }, + 257: { cls: "enemys", id: "dragon" }, + 258: { cls: "enemys", id: "octopus" }, + 259: { cls: "enemys", id: "fairyEnemy" }, + 260: { cls: "enemys", id: "princessEnemy" }, + 261: { cls: "enemy48", id: "angel" }, + 262: { cls: "enemy48", id: "elemental" }, + 263: { cls: "enemy48", id: "steelGuard" }, + 264: { cls: "enemy48", id: "evilBat" }, + 265: { cls: "enemys", id: "silverSlimelord" }, + 266: { cls: "enemys", id: "goldSlimelord" }, + 267: { cls: "enemys", id: "grayRock" }, + 270: { cls: "enemys", id: "greenKnight" }, + 271: { cls: "enemys", id: "bowman" }, + 272: { cls: "enemys", id: "purpleBowman" }, + 275: { cls: "enemys", id: "watcherSlime" }, + 277: { cls: "enemys", id: "frostBat" }, + 278: { cls: "enemys", id: "devilKnight" }, + 279: { cls: "enemys", id: "grayPriest" }, + 280: { cls: "enemys", id: "greenGateKeeper" }, + 300: { cls: "terrains", id: "ground" }, + 301: { + cls: "terrains", + id: "sWallT", + name: "薄墙-上", + cannotOut: ["up"], + cannotIn: ["up"], + }, + 302: { + cls: "terrains", + id: "sWallL", + name: "薄墙-左", + cannotOut: ["left"], + cannotIn: ["left"], + }, + 303: { + cls: "terrains", + id: "sWallR", + name: "薄墙-右", + cannotOut: ["right"], + cannotIn: ["right"], + }, + 304: { + cls: "terrains", + id: "sWallB", + name: "薄墙-下", + cannotOut: ["down"], + cannotIn: ["down"], + }, + 305: { cls: "terrains", id: "grass" }, + 306: { + cls: "terrains", + id: "sWallTL", + name: "薄墙-上左", + cannotOut: ["up", "left"], + cannotIn: ["up", "left"], + }, + 307: { + cls: "terrains", + id: "sWallBR", + name: "薄墙-下右", + cannotOut: ["down", "right"], + cannotIn: ["down", "right"], + }, + 308: { cls: "terrains", id: "grass2" }, + 309: { + cls: "terrains", + id: "sWallTR", + name: "薄墙-上右", + cannotOut: ["up", "right"], + cannotIn: ["up", "right"], + }, + 310: { + cls: "terrains", + id: "sWallBL", + name: "薄墙-下左", + cannotOut: ["down", "left"], + cannotIn: ["down", "left"], + }, + 311: { cls: "terrains", id: "ground2" }, + 312: { + cls: "terrains", + id: "sWallTB", + name: "薄墙-上下", + cannotOut: ["up", "down"], + cannotIn: ["up", "down"], + }, + 313: { cls: "terrains", id: "ground3" }, + 314: { + cls: "terrains", + id: "sWallLR", + name: "薄墙-左右", + cannotOut: ["left", "right"], + cannotIn: ["left", "right"], + }, + 315: { + cls: "terrains", + id: "sWallBLR", + name: "薄墙-下左右", + cannotOut: ["down", "left", "right"], + cannotIn: ["down", "left", "right"], + }, + 316: { + cls: "terrains", + id: "sWallTLR", + name: "薄墙-上左右", + cannotOut: ["up", "left", "right"], + cannotIn: ["up", "left", "right"], + }, + 317: { + cls: "terrains", + id: "sWallTBR", + name: "薄墙-上下右", + cannotOut: ["up", "down", "right"], + cannotIn: ["up", "down", "right"], + }, + 318: { + cls: "terrains", + id: "sWallTBL", + name: "薄墙-上下左", + cannotOut: ["up", "down", "left"], + cannotIn: ["up", "down", "left"], + }, + 319: { + cls: "npc48", + id: "tallYellowDoor", + trigger: "openDoor", + name: "高黄门", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { yellowKey: 1 }, + }, + }, + 320: { + cls: "npc48", + id: "tallBlueDoor", + trigger: "openDoor", + name: "高蓝门", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { blueKey: 1 }, + }, + }, + 321: { + cls: "npc48", + id: "tallRedDoor", + trigger: "openDoor", + name: "高红门", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { redKey: 1 }, + }, + }, + 322: { + cls: "npc48", + id: "tallGreenDoor", + trigger: "openDoor", + name: "高绿门", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { greenKey: 1 }, + }, + }, + 323: { + cls: "npc48", + id: "tallSpecialDoor", + trigger: "openDoor", + name: "高机关门", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { specialKey: 1 }, + }, + }, + 324: { + cls: "npc48", + id: "tallSteelDoor", + trigger: "openDoor", + name: "高铁门", + animate: 1, + doorInfo: { + time: 160, + openSound: "door.opus", + closeSound: "door.opus", + keys: { steelKey: 1 }, + }, + }, + 325: { cls: "enemys", id: "keiskeiFairy" }, + 326: { cls: "enemys", id: "tulipFairy" }, + 327: { cls: "enemy48", id: "bearDown" }, + 328: { cls: "enemy48", id: "bearLeft" }, + 329: { cls: "enemy48", id: "bearRight" }, + 330: { cls: "enemy48", id: "bearUp" }, + 331: { cls: "terrains", id: "T331" }, + 332: { cls: "terrains", id: "T332" }, + 333: { cls: "terrains", id: "T333" }, + 334: { cls: "terrains", id: "T334" }, + 335: { cls: "terrains", id: "T335" }, + 336: { cls: "terrains", id: "T336" }, + 337: { cls: "terrains", id: "T337" }, + 338: { cls: "terrains", id: "T338" }, + 339: { cls: "terrains", id: "T339" }, + 340: { cls: "terrains", id: "T340" }, + 341: { cls: "terrains", id: "T341" }, + 342: { cls: "terrains", id: "T342" }, + 343: { cls: "terrains", id: "T343" }, + 344: { cls: "terrains", id: "T344" }, + 345: { cls: "terrains", id: "T345", canPass: true }, + 346: { cls: "terrains", id: "T346" }, + 347: { cls: "terrains", id: "T347" }, + 348: { cls: "terrains", id: "T348" }, + 349: { cls: "terrains", id: "T349" }, + 350: { cls: "terrains", id: "T350" }, + 351: { cls: "terrains", id: "T351" }, + 352: { cls: "terrains", id: "T352" }, + 353: { cls: "terrains", id: "T353" }, + 354: { cls: "terrains", id: "T354" }, + 355: { cls: "terrains", id: "T355" }, + 356: { cls: "terrains", id: "T356" }, + 357: { cls: "terrains", id: "T357" }, + 358: { cls: "terrains", id: "T358" }, + 359: { cls: "terrains", id: "T359" }, + 360: { cls: "terrains", id: "T360" }, + 361: { cls: "terrains", id: "T361" }, + 362: { cls: "terrains", id: "T362" }, + 363: { cls: "terrains", id: "T363" }, + 364: { cls: "terrains", id: "T364" }, + 365: { cls: "terrains", id: "T365" }, + 376: { cls: "terrains", id: "T376" }, + 377: { cls: "terrains", id: "T377" }, + 378: { cls: "terrains", id: "T378" }, + 379: { cls: "terrains", id: "T379" }, + 380: { cls: "terrains", id: "T380" }, + 381: { cls: "terrains", id: "T381" }, + 382: { cls: "terrains", id: "T382" }, + 383: { cls: "terrains", id: "T383" }, + 384: { cls: "terrains", id: "T384" }, + 385: { cls: "terrains", id: "T385" }, + 386: { cls: "terrains", id: "T386" }, + 387: { cls: "terrains", id: "T387" }, + 388: { cls: "terrains", id: "T388" }, + 389: { cls: "terrains", id: "T389" }, + 390: { cls: "terrains", id: "T390" }, + 391: { cls: "terrains", id: "T391" }, + 392: { cls: "terrains", id: "T392" }, + 393: { cls: "terrains", id: "T393" }, + 394: { cls: "terrains", id: "T394" }, + 395: { cls: "terrains", id: "T395" }, + 20034: { cls: "tileset", id: "X20034", canPass: true }, + 20154: { cls: "tileset", id: "X20154", canPass: true }, + 20216: { cls: "tileset", id: "X20216", canPass: true }, + 20217: { cls: "tileset", id: "X20217", canPass: true }, + 40054: { cls: "tileset", id: "X40054", canPass: false }, + 40090: { cls: "tileset", id: "X40090", canPass: true }, + 50054: { cls: "tileset", id: "X50054", canPass: false }, + 70505: { cls: "tileset", id: "X70505", canPass: true }, + 71182: { cls: "tileset", id: "X71182", canPass: false }, + 71183: { cls: "tileset", id: "X71183", canPass: false }, + 71229: { cls: "tileset", id: "X71229", canPass: true }, + 71818: { cls: "tileset", id: "X71818", canPass: true }, + 71887: { cls: "tileset", id: "X71887", canPass: true }, + 80003: { cls: "tileset", id: "X80003", canPass: true }, + 80007: { cls: "tileset", id: "X80007", canPass: true }, + 80010: { cls: "tileset", id: "X80010", canPass: true }, + 80011: { cls: "tileset", id: "X80011", canPass: true }, + 80020: { cls: "tileset", id: "X80020", canPass: true }, + 90226: { cls: "tileset", id: "X90226", canPass: false }, +}; diff --git a/project/plugins.js b/project/plugins.js index 641697a..9d0ecff 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -1,264 +1,262 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { "init": function () { - this._afterLoadResources = function () { - // 本函数将在所有资源加载完毕后,游戏开启前被执行 - core.ui.statusBar.init(); - core.dom.playGame.style.fontFamily = "pala"; - core.dom.loadGame.style.fontFamily = "pala"; - core.dom.CGMode.style.fontFamily = "pala"; - core.dom.musicMode.style.fontFamily = "pala"; - core.dom.replayGame.style.fontFamily = "pala"; - core.registerEvent("changeMouse", function (data) { - if (!main.replayChecking && !core.isReplaying()) - core.changeMouse( - data.icon, - data.div, - data.translate[0], - data.translate[1], - data.scale[0], - data.scale[1], - data.angel, - data.px, - data.py - ); - core.doAction(); - }); - core.registerEvent("removeMouse", function (data) { - if (!main.replayChecking && !core.isReplaying()) - core.removeMouse(data.div); - core.doAction(); - }); - core.registerEvent("addPop", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - data.value = core.replaceText(data.value); - core.addPop( - data.value, - data.px, - data.py, - data.color, - data.boldColor, - data.left, - data.jump, - data.time, - data.show, - data.font, - data.speed - ); - } - core.doAction(); - }); - core.registerEvent("drawWarning", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - data.text = core.replaceText(data.text); - data.text2 = core.replaceText(data.text2); - core.drawWarning( - data.x, - data.y, - data?.text, - data?.text2, - data?.warning, - data.large, - data.size - ); + this._afterLoadResources = function () { + // 本函数将在所有资源加载完毕后,游戏开启前被执行 + core.ui.statusBar.init(); + core.dom.playGame.style.fontFamily = "pala"; + core.dom.loadGame.style.fontFamily = "pala"; + core.dom.CGMode.style.fontFamily = "pala"; + core.dom.musicMode.style.fontFamily = "pala"; + core.dom.replayGame.style.fontFamily = "pala"; + core.registerEvent("changeMouse", function (data) { + if (!main.replayChecking && !core.isReplaying()) + core.changeMouse( + data.icon, + data.div, + data.translate[0], + data.translate[1], + data.scale[0], + data.scale[1], + data.angel, + data.px, + data.py + ); + core.doAction(); + }); + core.registerEvent("removeMouse", function (data) { + if (!main.replayChecking && !core.isReplaying()) + core.removeMouse(data.div); + core.doAction(); + }); + core.registerEvent("addPop", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + data.value = core.replaceText(data.value); + core.addPop( + data.value, + data.px, + data.py, + data.color, + data.boldColor, + data.left, + data.jump, + data.time, + data.show, + data.font, + data.speed + ); + } + core.doAction(); + }); + core.registerEvent("drawWarning", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + data.text = core.replaceText(data.text); + data.text2 = core.replaceText(data.text2); + core.drawWarning( + data.x, + data.y, + data?.text, + data?.text2, + data?.warning, + data.large, + data.size + ); - setTimeout(() => core.doAction(), 3100); - } else { - core.doAction(); - } - }); + setTimeout(() => core.doAction(), 3100); + } else { + core.doAction(); + } + }); - core.registerEvent("over", function (data) { - let image = data.image ?? ""; - let time = data.time ?? 3000; - let sound = data.sound ?? ""; - let textColor = data.textColor ?? "#FFFFFF"; - let boldColor = data.boldColor ?? "#000000"; - let font = data.font ?? "bold 48px Verdana"; - let text = data.text ?? ""; - let hidetime = data.hidetime ?? 100; - if (!main.replayChecking && !core.isReplaying()) { - core.over( - image, - data.memory, - time, - hidetime, - sound, - textColor, - boldColor, - font, - text - ); - } else { - core.doAction(); - } - }); - core.registerEvent("changebg", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - core.changebg( - data.img1, - data.memory1, - data.img2, - data.memory2, - data.time, - data.style - ); - } else { - core.doAction(); - } - }); - core.registerEvent("overlist", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - core.overlist( - data.image, - data.memory, - data.hidetime || 30, - data.list || [ - { - text: "", - sound: "", - time: 50, - textColor: "#FFFFFF", - boldColor: "#000000", - font: "bold 48px Verdana", - frame: 0, - }, - ] - ); - } else { - core.doAction(); - } - }); - core.registerEvent("op", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - core.openvideo(); - } else { - core.doAction(); - } - }); - core.registerEvent("animationDrawable", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - core.animationDrawable( - data.allFarme, - data.color, - data.globalAlpha, - data.imageList, - data.soundList - ); - } else { - core.doAction(); - } - }); - core.registerEvent("setanimate", function (data) { - data.px = data.px ?? 0; - data.py = data.py ?? 0; - core.setanimate( - data.name, - data.px, - data.py, - data.width, - data.height, - data.allFarme, - data.imageList, - data.soundList - ); - core.doAction(); - }); - core.registerEvent("clearanimate", function (data) { - core.plugin.playing.clear(); + core.registerEvent("over", function (data) { + let image = data.image ?? ""; + let time = data.time ?? 3000; + let sound = data.sound ?? ""; + let textColor = data.textColor ?? "#FFFFFF"; + let boldColor = data.boldColor ?? "#000000"; + let font = data.font ?? "bold 48px Verdana"; + let text = data.text ?? ""; + let hidetime = data.hidetime ?? 100; + if (!main.replayChecking && !core.isReplaying()) { + core.over( + image, + data.memory, + time, + hidetime, + sound, + textColor, + boldColor, + font, + text + ); + } else { + core.doAction(); + } + }); + core.registerEvent("changebg", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + core.changebg( + data.img1, + data.memory1, + data.img2, + data.memory2, + data.time, + data.style + ); + } else { + core.doAction(); + } + }); + core.registerEvent("overlist", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + core.overlist( + data.image, + data.memory, + data.hidetime || 30, + data.list || [{ + text: "", + sound: "", + time: 50, + textColor: "#FFFFFF", + boldColor: "#000000", + font: "bold 48px Verdana", + frame: 0, + }, ] + ); + } else { + core.doAction(); + } + }); + core.registerEvent("op", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + core.openvideo(); + } else { + core.doAction(); + } + }); + core.registerEvent("animationDrawable", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + core.animationDrawable( + data.allFarme, + data.color, + data.globalAlpha, + data.imageList, + data.soundList + ); + } else { + core.doAction(); + } + }); + core.registerEvent("setanimate", function (data) { + data.px = data.px ?? 0; + data.py = data.py ?? 0; + core.setanimate( + data.name, + data.px, + data.py, + data.width, + data.height, + data.allFarme, + data.imageList, + data.soundList + ); + core.doAction(); + }); + core.registerEvent("clearanimate", function (data) { + core.plugin.playing.clear(); - core.doAction(); - }); - core.registerEvent("deleteanimate", function (data) { - core.deleteanimate(data.name); - core.doAction(); - }); - core.registerEvent("playanimate", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - data.x = data.x ?? 0; - data.y = data.y ?? 0; - data.scalex = data.scalex ?? 1; - data.scaley = data.scaley ?? 1; - core.playanimate( - data.name, - data.x, - data.y, - data.hero, - data.scalex, - data.scaley - ); - core.doAction(); - } else { - core.doAction(); - } - }); - core.registerEvent("cgtextList", function (data) { - core.ui.cgText.textList = core.plugin[data.textList]; - core.doAction(); - }); - core.registerEvent("cgtext", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - core.ui.cgText.image = data.bg; - core.ui.cgText.memory = data.memory; - core.ui.cgText.head = core.clone(data.head); - core.ui.cgText.index = data.index; - core.ui.cgText.name = core.ui.cgText.textList[data.index][0]; - core.ui.cgText.text = data.text - ? data.text - : core.ui.cgText.textList[data.index][1]; - core.ui.cgText.time = data.time; - core.ui.cgText.wait = data.wait; - core.ui.cgText.WindowSkin = data.WindowSkin; - core.ui.cgText.sound = data.sound || ""; - core.ui.cgText.bodyList = core.clone(data.bodyList); - main.dom.cgText.style.display = "block"; - core.ui.cgText.update(); - } else { - core.doAction(); - } - }); - core.registerEvent("introAndLoop", function (data) { - if (!main.replayChecking && !core.isReplaying()) { - core.plugin.introAndLoop(data.intro, data.time, data.loop); - core.doAction(); - } else { - core.doAction(); - } - }); - core.registerEvent("setq", function (data) { - core.setFlag("任务地点", data.id); + core.doAction(); + }); + core.registerEvent("deleteanimate", function (data) { + core.deleteanimate(data.name); + core.doAction(); + }); + core.registerEvent("playanimate", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + data.x = data.x ?? 0; + data.y = data.y ?? 0; + data.scalex = data.scalex ?? 1; + data.scaley = data.scaley ?? 1; + core.playanimate( + data.name, + data.x, + data.y, + data.hero, + data.scalex, + data.scaley + ); + core.doAction(); + } else { + core.doAction(); + } + }); + core.registerEvent("cgtextList", function (data) { + core.ui.cgText.textList = core.plugin[data.textList]; + core.doAction(); + }); + core.registerEvent("cgtext", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + core.ui.cgText.image = data.bg; + core.ui.cgText.memory = data.memory; + core.ui.cgText.head = core.clone(data.head); + core.ui.cgText.index = data.index; + core.ui.cgText.name = core.ui.cgText.textList[data.index][0]; + core.ui.cgText.text = data.text ? + data.text : + core.ui.cgText.textList[data.index][1]; + core.ui.cgText.time = data.time; + core.ui.cgText.wait = data.wait; + core.ui.cgText.WindowSkin = data.WindowSkin; + core.ui.cgText.sound = data.sound || ""; + core.ui.cgText.bodyList = core.clone(data.bodyList); + main.dom.cgText.style.display = "block"; + core.ui.cgText.update(); + } else { + core.doAction(); + } + }); + core.registerEvent("introAndLoop", function (data) { + if (!main.replayChecking && !core.isReplaying()) { + core.plugin.introAndLoop(data.intro, data.time, data.loop); + core.doAction(); + } else { + core.doAction(); + } + }); + core.registerEvent("setq", function (data) { + core.setFlag("任务地点", data.id); - core.doAction(); - }); + core.doAction(); + }); - core.registerEvent("setmusics", function (data) { - if ( - (core.getLocalStorage("musics") && - core.getLocalStorage("musics").length === 0) || - !core.getLocalStorage("musics") - ) - core.setLocalStorage("musics", ["theme.mp3"]); - let a = core.getLocalStorage("musics"); - if (!data.bgm) { - core.setLocalStorage("musics", ["theme.mp3"]); - } else { - if (!a.includes(data.bgm)) a.push(data.bgm); - core.setLocalStorage("musics", a); - } - core.doAction(); - }); - core.registerEvent("setcgs", function (data) { - if (!data.img) { - core.setLocalStorage("cgs", []); - } else { - let a = core.getLocalStorage("cgs") ?? []; - if (!a.includes(data.img)) a.push(data.img); - core.setLocalStorage("cgs", a); - } - core.doAction(); - }); - }; - }, + core.registerEvent("setmusics", function (data) { + if ( + (core.getLocalStorage("musics") && + core.getLocalStorage("musics").length === 0) || + !core.getLocalStorage("musics") + ) + core.setLocalStorage("musics", ["theme.mp3"]); + let a = core.getLocalStorage("musics"); + if (!data.bgm) { + core.setLocalStorage("musics", ["theme.mp3"]); + } else { + if (!a.includes(data.bgm)) a.push(data.bgm); + core.setLocalStorage("musics", a); + } + core.doAction(); + }); + core.registerEvent("setcgs", function (data) { + if (!data.img) { + core.setLocalStorage("cgs", []); + } else { + let a = core.getLocalStorage("cgs") ?? []; + if (!a.includes(data.img)) a.push(data.img); + core.setLocalStorage("cgs", a); + } + core.doAction(); + }); + }; +}, "drawLight": function () { // 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...) // 【参数说明】 @@ -2522,1218 +2520,1216 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = })(); }, "statusBar": function () { - main.dom.floorMsgGroup.style.display = "none"; - main.dom.statusBar.style.display = "none"; - main.dom.toolBar.style.display = "none"; - //所有数据*3是为了实现高清画布 - const GAMEVIEW_WIDTH = 676 * 3; //横屏画面宽度 - const GAMEVIEW_HEIGHT = 416 * 3; //横屏画面高度 + main.dom.floorMsgGroup.style.display = "none"; + main.dom.statusBar.style.display = "none"; + main.dom.toolBar.style.display = "none"; + //所有数据*3是为了实现高清画布 + const GAMEVIEW_WIDTH = 676 * 3; //横屏画面宽度 + const GAMEVIEW_HEIGHT = 416 * 3; //横屏画面高度 - const GAMEVIEW_WIDTH_VERTICAL = 416 * 3; //竖屏画面宽度 - const GAMEVIEW_HEIGHT_VERTICAL = 676 * 3; //竖屏画面高度 + const GAMEVIEW_WIDTH_VERTICAL = 416 * 3; //竖屏画面宽度 + const GAMEVIEW_HEIGHT_VERTICAL = 676 * 3; //竖屏画面高度 - const BAR_WIDTH = 130 * 3; //横屏左侧额外距离(即边栏宽度) - const BAR_HEIGHT_VERTICAL = 130 * 3; //竖屏上侧额外距离(即边栏高度) - const BORDER_WIDTH = 0; //游戏画面左侧偏移距离 - const BORDER_HEIGHT = 0; //游戏画面上侧偏移距离 + const BAR_WIDTH = 130 * 3; //横屏左侧额外距离(即边栏宽度) + const BAR_HEIGHT_VERTICAL = 130 * 3; //竖屏上侧额外距离(即边栏高度) + const BORDER_WIDTH = 0; //游戏画面左侧偏移距离 + const BORDER_HEIGHT = 0; //游戏画面上侧偏移距离 - const ITEM_BOX_LEFT = 549 * 3; //横屏道具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const ITEM_BOX_TOP = 155 * 3; //横屏道具栏上侧距离 - const ITEM_BOX_LEFT_VERTICAL = 160 * 3; //竖屏道具栏左侧距离 - const ITEM_BOX_TOP_VERTICAL = 549 * 3; //竖屏道具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const ITEM_BOX_LEFT = 549 * 3; //横屏道具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const ITEM_BOX_TOP = 155 * 3; //横屏道具栏上侧距离 + const ITEM_BOX_LEFT_VERTICAL = 160 * 3; //竖屏道具栏左侧距离 + const ITEM_BOX_TOP_VERTICAL = 549 * 3; //竖屏道具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const EQUIP_BLOCK_LEFT = 549 * 3; //横屏装备栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const EQUIP_BLOCK_TOP = 10 * 3; //横屏装备栏上侧距离 - const EQUIP_BLOCK_LEFT_VERTICAL = 10 * 3; //竖屏装备栏左侧距离 - const EQUIP_BLOCK_TOP_VERTICAL = 549 * 3; //竖屏装备栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const EQUIP_BLOCK_LEFT = 549 * 3; //横屏装备栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const EQUIP_BLOCK_TOP = 10 * 3; //横屏装备栏上侧距离 + const EQUIP_BLOCK_LEFT_VERTICAL = 10 * 3; //竖屏装备栏左侧距离 + const EQUIP_BLOCK_TOP_VERTICAL = 549 * 3; //竖屏装备栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const MAP_BLOCK_LEFT = 551 * 3; //横屏小地图左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const MAP_BLOCK_TOP = 0; //横屏小地图上侧距离 - const MAP_BLOCK_LEFT_VERTICAL = 0; //竖屏小地图左侧距离 - const MAP_BLOCK_TOP_VERTICAL = 551 * 3; //竖屏小地图上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const MAP_BLOCK_LEFT = 551 * 3; //横屏小地图左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const MAP_BLOCK_TOP = 0; //横屏小地图上侧距离 + const MAP_BLOCK_LEFT_VERTICAL = 0; //竖屏小地图左侧距离 + const MAP_BLOCK_TOP_VERTICAL = 551 * 3; //竖屏小地图上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT; //横屏钥匙栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const KEY_BLOCK_TOP = 110 * 3; //横屏钥匙栏上侧距离 - const KEY_BLOCK_LEFT_VERTICAL = 110 * 3; //竖屏钥匙栏左侧距离 - const KEY_BLOCK_TOP_VERTICAL = EQUIP_BLOCK_TOP_VERTICAL; //竖屏钥匙栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT; //横屏钥匙栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const KEY_BLOCK_TOP = 110 * 3; //横屏钥匙栏上侧距离 + const KEY_BLOCK_LEFT_VERTICAL = 110 * 3; //竖屏钥匙栏左侧距离 + const KEY_BLOCK_TOP_VERTICAL = EQUIP_BLOCK_TOP_VERTICAL; //竖屏钥匙栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const INFO_BLOCK_LEFT = 10 * 3; //横屏道具说明左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const INFO_BLOCK_TOP = 180 * 3; //横屏道具说明上侧距离 - const INFO_BLOCK_LEFT_VERTICAL = 113 * 3; //竖屏道具说明左侧距离 - const INFO_BLOCK_TOP_VERTICAL = 8 * 3; //竖屏道具说明上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const INFO_BLOCK_LEFT = 10 * 3; //横屏道具说明左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const INFO_BLOCK_TOP = 180 * 3; //横屏道具说明上侧距离 + const INFO_BLOCK_LEFT_VERTICAL = 113 * 3; //竖屏道具说明左侧距离 + const INFO_BLOCK_TOP_VERTICAL = 8 * 3; //竖屏道具说明上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT; //横屏工具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) - const TOOL_BOX_TOP = 348 * 3; //横屏工具栏上侧距离 - const TOOL_BOX_LEFT_VERTICAL = 348 * 3; //竖屏工具栏左侧距离 - const TOOL_BOX_TOP_VERTICAL = 549 * 3; //竖屏工具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) + const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT; //横屏工具栏左侧距离(右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT) + const TOOL_BOX_TOP = 348 * 3; //横屏工具栏上侧距离 + const TOOL_BOX_LEFT_VERTICAL = 348 * 3; //竖屏工具栏左侧距离 + const TOOL_BOX_TOP_VERTICAL = 549 * 3; //竖屏工具栏上侧距离(下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL) - const TOOL_ICON_OUTER_SIZE = 34 * 3; + const TOOL_ICON_OUTER_SIZE = 34 * 3; - const TEXT_COLOR = "#FFFFFF"; //默认文字颜色 - const globalAlpha = 0.7; //默认底框透明度 - const FORCE_COUNTABLE_ITEMS = ["centerFly"]; //常态显示数量的非永久道具,如果道具不在此数组中,则只有道具多余1时显示数量 + const TEXT_COLOR = "#FFFFFF"; //默认文字颜色 + const globalAlpha = 0.7; //默认底框透明度 + const FORCE_COUNTABLE_ITEMS = ["centerFly"]; //常态显示数量的非永久道具,如果道具不在此数组中,则只有道具多余1时显示数量 - const outerBackground = document.createElement("canvas"); //背景画布设置 - let globalAlphafloor = 0, - globalAlphafloorStatus = 4; - outerBackground.style.position = "absolute"; - outerBackground.style.zIndex = 5; - outerBackground.id = "outerBackground"; - main.dom.outerBackground = outerBackground; - main.dom.startPanel.insertAdjacentElement("afterend", outerBackground); + const outerBackground = document.createElement("canvas"); //背景画布设置 + let globalAlphafloor = 0, + globalAlphafloorStatus = 4; + outerBackground.style.position = "absolute"; + outerBackground.style.zIndex = 5; + outerBackground.id = "outerBackground"; + main.dom.outerBackground = outerBackground; + main.dom.startPanel.insertAdjacentElement("afterend", outerBackground); - const outerUI = document.createElement("canvas"); //额外ui画布设置(状态栏所有绘制、点击都在额外ui上) - outerUI.style.position = "absolute"; - outerUI.style.zIndex = 165; - outerUI.id = "outerUI"; + const outerUI = document.createElement("canvas"); //额外ui画布设置(状态栏所有绘制、点击都在额外ui上) + outerUI.style.position = "absolute"; + outerUI.style.zIndex = 165; + outerUI.id = "outerUI"; - main.dom.outerUI = outerUI; - outerBackground.insertAdjacentElement("afterend", outerUI); - setTimeout(function () { - // Should be executed immediately after init() - main.canvas.outerUI = outerUI.getContext("2d"); - }); - outerUI.onclick = function (e) { - try { - e.preventDefault(); - if (!core.isPlaying()) return false; - const left = core.dom.gameGroup.offsetLeft; - const top = core.dom.gameGroup.offsetTop; - const px = Math.floor((e.clientX - left) / core.domStyle.scale), - py = Math.floor((e.clientY - top) / core.domStyle.scale); - core.ui.statusBar.onclick(px * 3, py * 3); - } catch (ee) { - main.log(ee); - } - }; + main.dom.outerUI = outerUI; + outerBackground.insertAdjacentElement("afterend", outerUI); + setTimeout(function () { + // Should be executed immediately after init() + main.canvas.outerUI = outerUI.getContext("2d"); + }); + outerUI.onclick = function (e) { + try { + e.preventDefault(); + if (!core.isPlaying()) return false; + const left = core.dom.gameGroup.offsetLeft; + const top = core.dom.gameGroup.offsetTop; + const px = Math.floor((e.clientX - left) / core.domStyle.scale), + py = Math.floor((e.clientY - top) / core.domStyle.scale); + core.ui.statusBar.onclick(px * 3, py * 3); + } catch (ee) { + main.log(ee); + } + }; - const _resize_gameGroup = function (obj) { - //游戏画面自适应调节 - const gameGroup = core.dom.gameGroup; - gameGroup.style.width = obj.totalWidth + "px"; - gameGroup.style.height = obj.totalHeight + "px"; - gameGroup.style.left = (obj.clientWidth - obj.totalWidth) / 2 + "px"; - gameGroup.style.top = (obj.clientHeight - obj.totalHeight) / 2 + "px"; - //floorMsgGroup为切换楼层中生效,显示时间可通过‘全塔属性’——‘切换楼层时间’或游戏内设置调整 - //显示内容为游戏名/版本号/楼层名 - // floorMsgGroup - var floorMsgGroup = core.dom.floorMsgGroup; - var globalAttribute = - core.status.globalAttribute || core.initStatus.globalAttribute; - floorMsgGroup.style = globalAttribute.floorChangingStyle; - floorMsgGroup.style.height = floorMsgGroup.style.width = - (GAMEVIEW_HEIGHT / 3) * core.domStyle.scale + "px"; - floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; + const _resize_gameGroup = function (obj) { + //游戏画面自适应调节 + const gameGroup = core.dom.gameGroup; + gameGroup.style.width = obj.totalWidth + "px"; + gameGroup.style.height = obj.totalHeight + "px"; + gameGroup.style.left = (obj.clientWidth - obj.totalWidth) / 2 + "px"; + gameGroup.style.top = (obj.clientHeight - obj.totalHeight) / 2 + "px"; + //floorMsgGroup为切换楼层中生效,显示时间可通过‘全塔属性’——‘切换楼层时间’或游戏内设置调整 + //显示内容为游戏名/版本号/楼层名 + // floorMsgGroup + var floorMsgGroup = core.dom.floorMsgGroup; + var globalAttribute = + core.status.globalAttribute || core.initStatus.globalAttribute; + floorMsgGroup.style = globalAttribute.floorChangingStyle; + floorMsgGroup.style.height = floorMsgGroup.style.width = + (GAMEVIEW_HEIGHT / 3) * core.domStyle.scale + "px"; + floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; - if (core.domStyle.isVertical) { - floorMsgGroup.style.left = "0px"; - floorMsgGroup.style.top = - ((GAMEVIEW_HEIGHT_VERTICAL / 3 - GAMEVIEW_WIDTH_VERTICAL / 3) * - core.domStyle.scale) / - 2 + - "px"; - } else { - floorMsgGroup.style.left = - ((GAMEVIEW_WIDTH / 3 - GAMEVIEW_HEIGHT / 3) * core.domStyle.scale) / - 2 + - "px"; - floorMsgGroup.style.top = "0px"; - } - core.dom.musicBtn.style.right = - (obj.clientWidth - obj.totalWidth) / 2 + "px"; - core.dom.musicBtn.style.bottom = - (obj.clientHeight - obj.totalHeight) / 2 - 27 + "px"; - let startBackground = core.domStyle.isVertical - ? main.styles.startVerticalBackground || main.styles.startBackground - : main.styles.startBackground; - if (main.dom.startBackground.getAttribute("__src__") != startBackground) { - main.dom.startBackground.setAttribute("__src__", startBackground); - main.dom.startBackground.src = startBackground; - } - const span = document - .getElementById("startButtons") - .getElementsByTagName("span"); - let font = (GAMEVIEW_WIDTH / 100) * core.domStyle.scale; - if (core.domStyle.isVertical) - font = ((GAMEVIEW_WIDTH_VERTICAL * 2) / 100) * core.domStyle.scale; + if (core.domStyle.isVertical) { + floorMsgGroup.style.left = "0px"; + floorMsgGroup.style.top = + ((GAMEVIEW_HEIGHT_VERTICAL / 3 - GAMEVIEW_WIDTH_VERTICAL / 3) * + core.domStyle.scale) / + 2 + + "px"; + } else { + floorMsgGroup.style.left = + ((GAMEVIEW_WIDTH / 3 - GAMEVIEW_HEIGHT / 3) * core.domStyle.scale) / + 2 + + "px"; + floorMsgGroup.style.top = "0px"; + } + core.dom.musicBtn.style.right = + (obj.clientWidth - obj.totalWidth) / 2 + "px"; + core.dom.musicBtn.style.bottom = + (obj.clientHeight - obj.totalHeight) / 2 - 27 + "px"; + let startBackground = core.domStyle.isVertical ? + main.styles.startVerticalBackground || main.styles.startBackground : + main.styles.startBackground; + if (main.dom.startBackground.getAttribute("__src__") != startBackground) { + main.dom.startBackground.setAttribute("__src__", startBackground); + main.dom.startBackground.src = startBackground; + } + const span = document + .getElementById("startButtons") + .getElementsByTagName("span"); + let font = (GAMEVIEW_WIDTH / 100) * core.domStyle.scale; + if (core.domStyle.isVertical) + font = ((GAMEVIEW_WIDTH_VERTICAL * 2) / 100) * core.domStyle.scale; - core.dom.playGame.style.fontSize = font + "px"; - core.dom.loadGame.style.fontSize = font + "px"; - core.dom.CGMode.style.fontSize = font + "px"; - core.dom.musicMode.style.fontSize = font + "px"; - core.dom.replayGame.style.fontSize = font + "px"; - core.dom.startButtonGroup.style.padding = font * 0.3 + "px 25px"; - }; - const _resize_canvas = function (obj) { - //自适应画布 - main.dom.outerBackground.style.width = obj.totalWidth + "px"; - main.dom.outerBackground.style.height = obj.totalHeight + "px"; - main.dom.outerUI.style.width = obj.totalWidth + "px"; - main.dom.outerUI.style.height = obj.totalHeight + "px"; - if (main.dom.CGUI) { - main.dom.CGUI.style.width = obj.totalWidth + 3 + "px"; - main.dom.CGUI.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.music) { - main.dom.music.style.width = obj.totalWidth + 3 + "px"; - main.dom.music.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.cgText) { - main.dom.cgText.style.width = obj.totalWidth + 3 + "px"; - main.dom.cgText.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.logcanvas) { - main.dom.logcanvas.style.width = obj.totalWidth + 3 + "px"; - main.dom.logcanvas.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.over) { - main.dom.over.style.width = obj.totalWidth + 3 + "px"; - main.dom.over.style.height = obj.totalHeight + 3 + "px"; - } - if (main.dom.video) { - main.dom.video.style.width = obj.totalWidth + 3 + "px"; - main.dom.video.style.height = obj.totalHeight + 3 + "px"; - if (core.domStyle.isVertical) - main.dom.video.style.width = obj.totalHeight + 3 + "px"; - if (core.domStyle.isVertical) - main.dom.video.style.height = obj.totalWidth + 3 + "px"; - main.dom.video.style.top = "50%"; - main.dom.video.style.left = "50%"; + core.dom.playGame.style.fontSize = font + "px"; + core.dom.loadGame.style.fontSize = font + "px"; + core.dom.CGMode.style.fontSize = font + "px"; + core.dom.musicMode.style.fontSize = font + "px"; + core.dom.replayGame.style.fontSize = font + "px"; + core.dom.startButtonGroup.style.padding = font * 0.3 + "px 25px"; + }; + const _resize_canvas = function (obj) { + //自适应画布 + main.dom.outerBackground.style.width = obj.totalWidth + "px"; + main.dom.outerBackground.style.height = obj.totalHeight + "px"; + main.dom.outerUI.style.width = obj.totalWidth + "px"; + main.dom.outerUI.style.height = obj.totalHeight + "px"; + if (main.dom.CGUI) { + main.dom.CGUI.style.width = obj.totalWidth + 3 + "px"; + main.dom.CGUI.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.music) { + main.dom.music.style.width = obj.totalWidth + 3 + "px"; + main.dom.music.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.cgText) { + main.dom.cgText.style.width = obj.totalWidth + 3 + "px"; + main.dom.cgText.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.logcanvas) { + main.dom.logcanvas.style.width = obj.totalWidth + 3 + "px"; + main.dom.logcanvas.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.over) { + main.dom.over.style.width = obj.totalWidth + 3 + "px"; + main.dom.over.style.height = obj.totalHeight + 3 + "px"; + } + if (main.dom.video) { + main.dom.video.style.width = obj.totalWidth + 3 + "px"; + main.dom.video.style.height = obj.totalHeight + 3 + "px"; + if (core.domStyle.isVertical) + main.dom.video.style.width = obj.totalHeight + 3 + "px"; + if (core.domStyle.isVertical) + main.dom.video.style.height = obj.totalWidth + 3 + "px"; + main.dom.video.style.top = "50%"; + main.dom.video.style.left = "50%"; - main.dom.video.style.transform = "translate(-50%,-50%)"; + main.dom.video.style.transform = "translate(-50%,-50%)"; - if (core.domStyle.isVertical) - main.dom.video.style.transform = "translate(-50%,-50%) rotate(90deg)"; - } - if (main.dom.video1) { - main.dom.video1.style.width = obj.totalWidth + 3 + "px"; - main.dom.video1.style.height = obj.totalHeight + 3 + "px"; - } + if (core.domStyle.isVertical) + main.dom.video.style.transform = "translate(-50%,-50%) rotate(90deg)"; + } + if (main.dom.video1) { + main.dom.video1.style.width = obj.totalWidth + 3 + "px"; + main.dom.video1.style.height = obj.totalHeight + 3 + "px"; + } - const innerSize = obj.canvasWidth * core.domStyle.scale + "px"; - for (let i = 0; i < core.dom.gameCanvas.length; ++i) - core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[ - i - ].style.height = innerSize; - core.dom.gif.style.width = core.dom.gif.style.height = innerSize; - core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; + const innerSize = obj.canvasWidth * core.domStyle.scale + "px"; + for (let i = 0; i < core.dom.gameCanvas.length; ++i) + core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[ + i + ].style.height = innerSize; + core.dom.gif.style.width = core.dom.gif.style.height = innerSize; + core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; - core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = - innerSize; - core.dom.gameDraw.style.top = - obj.gameDrawBox.top * core.domStyle.scale + "px"; - core.dom.gameDraw.style.left = - obj.gameDrawBox.left * core.domStyle.scale + "px"; - // resize bigmap - core.bigmap.canvas.forEach(function (cn) { - const ratio = core.canvas[cn].canvas.hasAttribute("isHD") - ? core.domStyle.ratio - : 1; - core.canvas[cn].canvas.style.width = - (innerSize / ratio) * core.domStyle.scale + "px"; - core.canvas[cn].canvas.style.height = - (innerSize / ratio) * core.domStyle.scale + "px"; - }); - // resize dynamic canvas - for (const name in core.dymCanvas) { - const ctx = core.dymCanvas[name], - canvas = ctx.canvas; - const ratio = canvas.hasAttribute("isHD") ? core.domStyle.ratio : 1; - canvas.style.width = (innerSize / ratio) * core.domStyle.scale + "px"; - canvas.style.height = (innerSize / ratio) * core.domStyle.scale + "px"; - canvas.style.left = - parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; - canvas.style.top = - parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; - } - // resize next - main.dom.next.style.width = main.dom.next.style.height = - 5 * core.domStyle.scale + "px"; - main.dom.next.style.borderBottomWidth = - main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; - }; - const bgctx = main.dom.outerBackground.getContext("2d"); - const uictx = main.dom.outerUI.getContext("2d"); - let now = 0; - core.registerAnimationFrame("lightFloor", true, function (timestamp) { - if (timestamp - now > 1000 / 60) { - now = timestamp; - globalAlphafloor += globalAlphafloorStatus; - if (globalAlphafloor === 100) globalAlphafloorStatus = -2; - if (globalAlphafloor === 0) globalAlphafloorStatus = 2; + core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = + innerSize; + core.dom.gameDraw.style.top = + obj.gameDrawBox.top * core.domStyle.scale + "px"; + core.dom.gameDraw.style.left = + obj.gameDrawBox.left * core.domStyle.scale + "px"; + // resize bigmap + core.bigmap.canvas.forEach(function (cn) { + const ratio = core.canvas[cn].canvas.hasAttribute("isHD") ? + core.domStyle.ratio : + 1; + core.canvas[cn].canvas.style.width = + (innerSize / ratio) * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = + (innerSize / ratio) * core.domStyle.scale + "px"; + }); + // resize dynamic canvas + for (const name in core.dymCanvas) { + const ctx = core.dymCanvas[name], + canvas = ctx.canvas; + const ratio = canvas.hasAttribute("isHD") ? core.domStyle.ratio : 1; + canvas.style.width = (innerSize / ratio) * core.domStyle.scale + "px"; + canvas.style.height = (innerSize / ratio) * core.domStyle.scale + "px"; + canvas.style.left = + parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = + parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + } + // resize next + main.dom.next.style.width = main.dom.next.style.height = + 5 * core.domStyle.scale + "px"; + main.dom.next.style.borderBottomWidth = + main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; + }; + const bgctx = main.dom.outerBackground.getContext("2d"); + const uictx = main.dom.outerUI.getContext("2d"); + let now = 0; + core.registerAnimationFrame("lightFloor", true, function (timestamp) { + if (timestamp - now > 1000 / 60) { + now = timestamp; + globalAlphafloor += globalAlphafloorStatus; + if (globalAlphafloor === 100) globalAlphafloorStatus = -2; + if (globalAlphafloor === 0) globalAlphafloorStatus = 2; - if (core.domStyle.isVertical) { - core.clearMap( - uictx, - MAP_BLOCK_LEFT_VERTICAL, - MAP_BLOCK_TOP_VERTICAL, - 340, - 360 - ); - if (core.status.event.id === "viewMaps") { - core.ui.statusBar._update_map(core.status.event.data.floorId); - } else { - core.ui.statusBar._update_map(); - } + if (core.domStyle.isVertical) { + core.clearMap( + uictx, + MAP_BLOCK_LEFT_VERTICAL, + MAP_BLOCK_TOP_VERTICAL, + 340, + 360 + ); + if (core.status.event.id === "viewMaps") { + core.ui.statusBar._update_map(core.status.event.data.floorId); + } else { + core.ui.statusBar._update_map(); + } - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT_VERTICAL + 135, - MAP_BLOCK_TOP_VERTICAL + 170 - ); - uictx.globalAlpha = 1; - } else { - core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); - if (core.status.event.id === "viewMaps") { - core.ui.statusBar._update_map(core.status.event.data.floorId); - } else { - core.ui.statusBar._update_map(); - } - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT + 150, - MAP_BLOCK_TOP + 180 - ); - uictx.globalAlpha = 1; - } - } - }); + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT_VERTICAL + 135, + MAP_BLOCK_TOP_VERTICAL + 170 + ); + uictx.globalAlpha = 1; + } else { + core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); + if (core.status.event.id === "viewMaps") { + core.ui.statusBar._update_map(core.status.event.data.floorId); + } else { + core.ui.statusBar._update_map(); + } + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT + 150, + MAP_BLOCK_TOP + 180 + ); + uictx.globalAlpha = 1; + } + } + }); - core.control.resize = function () { - //自适应,可实现横竖屏切换 - if (main.mode == "editor") return; + core.control.resize = function () { + //自适应,可实现横竖屏切换 + if (main.mode == "editor") return; - const clientWidth = main.dom.body.clientWidth, - clientHeight = main.dom.body.clientHeight; - const canvasWidth = core.__PIXELS__; + const clientWidth = main.dom.body.clientWidth, + clientHeight = main.dom.body.clientHeight; + const canvasWidth = core.__PIXELS__; - const isVertical = clientHeight > clientWidth; - core.domStyle.isVertical = isVertical; + const isVertical = clientHeight > clientWidth; + core.domStyle.isVertical = isVertical; - const totalWidth = isVertical - ? GAMEVIEW_WIDTH_VERTICAL / 3 - : GAMEVIEW_WIDTH / 3, - totalHeight = isVertical - ? GAMEVIEW_HEIGHT_VERTICAL / 3 - : GAMEVIEW_HEIGHT / 3; + const totalWidth = isVertical ? + GAMEVIEW_WIDTH_VERTICAL / 3 : + GAMEVIEW_WIDTH / 3, + totalHeight = isVertical ? + GAMEVIEW_HEIGHT_VERTICAL / 3 : + GAMEVIEW_HEIGHT / 3; - const maxRatio = Math.min( - clientWidth / totalWidth, - clientHeight / totalHeight - ); + const maxRatio = Math.min( + clientWidth / totalWidth, + clientHeight / totalHeight + ); - core.domStyle.availableScale = []; - [1, 1.25, 1.5, 1.75, 2].forEach(function (v) { - if (maxRatio >= v) { - core.domStyle.availableScale.push(v); - } - }); + core.domStyle.availableScale = []; + [1, 1.25, 1.5, 1.75, 2].forEach(function (v) { + if (maxRatio >= v) { + core.domStyle.availableScale.push(v); + } + }); - if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { - core.domStyle.scale = Math.min(1, maxRatio); - } else if ( - core.getLocalStorage("scale") == null && - core.domStyle.availableScale.length >= 2 - ) { - core.domStyle.scale = - core.domStyle.availableScale[core.domStyle.availableScale.length - 2]; - core.setLocalStorage("scale", core.domStyle.scale); - } + if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { + core.domStyle.scale = Math.min(1, maxRatio); + } else if ( + core.getLocalStorage("scale") == null && + core.domStyle.availableScale.length >= 2 + ) { + core.domStyle.scale = + core.domStyle.availableScale[core.domStyle.availableScale.length - 2]; + core.setLocalStorage("scale", core.domStyle.scale); + } - const totalWidthScaled = totalWidth * core.domStyle.scale, - totalHeightScaled = totalHeight * core.domStyle.scale; + const totalWidthScaled = totalWidth * core.domStyle.scale, + totalHeightScaled = totalHeight * core.domStyle.scale; - const gameDrawBox = isVertical - ? { - left: BORDER_WIDTH / 3, - top: BAR_HEIGHT_VERTICAL / 3 + BORDER_HEIGHT / 3, - } - : { left: BAR_WIDTH / 3 + BORDER_WIDTH / 3, top: BORDER_HEIGHT / 3 }; + const gameDrawBox = isVertical ? { + left: BORDER_WIDTH / 3, + top: BAR_HEIGHT_VERTICAL / 3 + BORDER_HEIGHT / 3, + } : { left: BAR_WIDTH / 3 + BORDER_WIDTH / 3, top: BORDER_HEIGHT / 3 }; - const obj = { - clientWidth: clientWidth, - clientHeight: clientHeight, - canvasWidth: canvasWidth, - totalWidth: totalWidthScaled, - totalHeight: totalHeightScaled, - gameDrawBox: gameDrawBox, - globalAttribute: - core.status.globalAttribute || core.initStatus.globalAttribute, - }; + const obj = { + clientWidth: clientWidth, + clientHeight: clientHeight, + canvasWidth: canvasWidth, + totalWidth: totalWidthScaled, + totalHeight: totalHeightScaled, + gameDrawBox: gameDrawBox, + globalAttribute: core.status.globalAttribute || core.initStatus.globalAttribute, + }; - _resize_gameGroup(obj); - _resize_canvas(obj); + _resize_gameGroup(obj); + _resize_canvas(obj); - if (core.status.automaticRoute == null) core.status.automaticRoute = {}; - core.updateStatusBar(); - if (main.dom.CGUI && main.dom.CGUI.style.display === "block") - core.ui.CG.update(); - if (main.dom.music && main.dom.music.style.display === "block") - core.ui.music.update(); - if (main.dom.cgText && main.dom.cgText.style.display === "block") - core.ui.cgText.update(); - if (main.dom.logcanvas && main.dom.logcanvas.style.display === "block") - core.ui.cgText.update(); - }; + if (core.status.automaticRoute == null) core.status.automaticRoute = {}; + core.updateStatusBar(); + if (main.dom.CGUI && main.dom.CGUI.style.display === "block") + core.ui.CG.update(); + if (main.dom.music && main.dom.music.style.display === "block") + core.ui.music.update(); + if (main.dom.cgText && main.dom.cgText.style.display === "block") + core.ui.cgText.update(); + if (main.dom.logcanvas && main.dom.logcanvas.style.display === "block") + core.ui.cgText.update(); + }; - class StatusBar { - constructor() { - //道具栏列表 - this.itemMx = [ - //空位用‘none’填充,当前ui至多4列6行 - ["book", "wand", "none", "fly"], - ["cross", "superPotion", "pickaxe"], - ["bomb", "centerFly", "upFly"], - ["none", "none", "none"], - ["downFly", "knife", "snow"], - ["bigKey", "earthquake", "coin"], - ]; - } - //初始化内容(工具栏/录像操作执行函数) - init() { - this.toolbarAction = [ - [ - main.core.openKeyBoard, - main.core.openQuickShop, - core.openToolbox, - core.doSL, - ], - [main.core.openSettings, main.core.save, main.core.load, core.doSL], - ]; - this.replayAction = [ - [core.triggerReplay, core.stopReplay, core.rewindReplay], - [core.speedDownReplay, core.speedUpReplay, core.saveReplay], - ]; - } - //更新 - update() { - this._update_background(); //更新背景 - this._update_props(); //更新属性 - //this._update_items(); //更新道具 - //this._update_equips(); //更新装备 - //this._update_keys(); //更新钥匙 - //this._update_infoWindow(); //更新道具说明 - this._update_toolBox(); //更新工具栏 - this._redrawMap(); - } - _redrawMap() { - if (core.domStyle.isVertical) { - core.clearMap( - uictx, - MAP_BLOCK_LEFT_VERTICAL, - MAP_BLOCK_TOP_VERTICAL, - 340, - 360 - ); - this._update_map(); - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT_VERTICAL + 125, - MAP_BLOCK_TOP_VERTICAL + 170 - ); - uictx.globalAlpha = 1; - } else { - core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); - this._update_map(); - uictx.globalAlpha = globalAlphafloor / 100; - core.drawImage( - uictx, - "green.webp", - MAP_BLOCK_LEFT + 150, - MAP_BLOCK_TOP + 170 - ); - uictx.globalAlpha = 1; - } - } - //更新背景 - _update_background() { - if (core.domStyle.isVertical) { - bgctx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; - bgctx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; - uictx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; - uictx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; + class StatusBar { + constructor() { + //道具栏列表 + this.itemMx = [ + //空位用‘none’填充,当前ui至多4列6行 + ["book", "wand", "none", "fly"], + ["cross", "superPotion", "pickaxe"], + ["bomb", "centerFly", "upFly"], + ["none", "none", "none"], + ["downFly", "knife", "snow"], + ["bigKey", "earthquake", "coin"], + ]; + } + //初始化内容(工具栏/录像操作执行函数) + init() { + this.toolbarAction = [ + [ + main.core.openKeyBoard, + main.core.openQuickShop, + core.openToolbox, + core.doSL, + ], + [main.core.openSettings, main.core.save, main.core.load, core.doSL], + ]; + this.replayAction = [ + [core.triggerReplay, core.stopReplay, core.rewindReplay], + [core.speedDownReplay, core.speedUpReplay, core.saveReplay], + ]; + } + //更新 + update() { + this._update_background(); //更新背景 + this._update_props(); //更新属性 + //this._update_items(); //更新道具 + //this._update_equips(); //更新装备 + //this._update_keys(); //更新钥匙 + //this._update_infoWindow(); //更新道具说明 + this._update_toolBox(); //更新工具栏 + this._redrawMap(); + } + _redrawMap() { + if (core.domStyle.isVertical) { + core.clearMap( + uictx, + MAP_BLOCK_LEFT_VERTICAL, + MAP_BLOCK_TOP_VERTICAL, + 340, + 360 + ); + this._update_map(); + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT_VERTICAL + 125, + MAP_BLOCK_TOP_VERTICAL + 170 + ); + uictx.globalAlpha = 1; + } else { + core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360); + this._update_map(); + uictx.globalAlpha = globalAlphafloor / 100; + core.drawImage( + uictx, + "green.webp", + MAP_BLOCK_LEFT + 150, + MAP_BLOCK_TOP + 170 + ); + uictx.globalAlpha = 1; + } + } + //更新背景 + _update_background() { + if (core.domStyle.isVertical) { + bgctx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; + bgctx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; + uictx.canvas.width = GAMEVIEW_WIDTH_VERTICAL; + uictx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL; - const bg = core.material.images.images["status.webp"]; //竖屏背景(上) - bgctx.drawImage( - bg, - 0, - 0, - GAMEVIEW_WIDTH_VERTICAL, - BAR_HEIGHT_VERTICAL - ); - const bg2 = core.material.images.images["status.webp"]; //竖屏背景(下) - bgctx.drawImage( - bg2, - 0, - BAR_HEIGHT_VERTICAL + GAMEVIEW_WIDTH_VERTICAL, - GAMEVIEW_WIDTH_VERTICAL, - BAR_HEIGHT_VERTICAL - ); - bgctx.globalAlpha = globalAlpha; - bgctx.globalAlpha = 1; - core.setTextAlign("outerUI", "center"); - } else { - bgctx.canvas.width = GAMEVIEW_WIDTH; - bgctx.canvas.height = GAMEVIEW_HEIGHT; - uictx.canvas.width = GAMEVIEW_WIDTH; - uictx.canvas.height = GAMEVIEW_HEIGHT; + const bg = core.material.images.images["status.webp"]; //竖屏背景(上) + bgctx.drawImage( + bg, + 0, + 0, + GAMEVIEW_WIDTH_VERTICAL, + BAR_HEIGHT_VERTICAL + ); + const bg2 = core.material.images.images["status.webp"]; //竖屏背景(下) + bgctx.drawImage( + bg2, + 0, + BAR_HEIGHT_VERTICAL + GAMEVIEW_WIDTH_VERTICAL, + GAMEVIEW_WIDTH_VERTICAL, + BAR_HEIGHT_VERTICAL + ); + bgctx.globalAlpha = globalAlpha; + bgctx.globalAlpha = 1; + core.setTextAlign("outerUI", "center"); + } else { + bgctx.canvas.width = GAMEVIEW_WIDTH; + bgctx.canvas.height = GAMEVIEW_HEIGHT; + uictx.canvas.width = GAMEVIEW_WIDTH; + uictx.canvas.height = GAMEVIEW_HEIGHT; - const bg = core.material.images.images["status.webp"]; //横屏背景(左) - bgctx.drawImage(bg, 0, 0, BAR_WIDTH, GAMEVIEW_HEIGHT); - const bg2 = core.material.images.images["status.webp"]; //横屏背景(右) - bgctx.drawImage( - bg2, - BAR_WIDTH + GAMEVIEW_HEIGHT, - 0, - BAR_WIDTH, - GAMEVIEW_HEIGHT - ); - bgctx.globalAlpha = globalAlpha; + const bg = core.material.images.images["status.webp"]; //横屏背景(左) + bgctx.drawImage(bg, 0, 0, BAR_WIDTH, GAMEVIEW_HEIGHT); + const bg2 = core.material.images.images["status.webp"]; //横屏背景(右) + bgctx.drawImage( + bg2, + BAR_WIDTH + GAMEVIEW_HEIGHT, + 0, + BAR_WIDTH, + GAMEVIEW_HEIGHT + ); + bgctx.globalAlpha = globalAlpha; - bgctx.globalAlpha = 1; - core.setTextAlign("outerUI", "center"); - } - } - // 更新属性 - _update_props(updatedFloorTitle) { - if (!updatedFloorTitle && core.status.floorId) { - updatedFloorTitle = core.status.maps[core.status.floorId].title; - } - const statusList = ["hp", "atk", "def", "money"]; //属性列表,图标在函数复写core.statusBar.icons中声明,数字为project\materials\icons.png中的图标序号(可使用便捷ps追加,第一个序号为0) - const drawStatusList = (baseX, baseY) => { - let curh = baseY; - core.setTextAlign("outerUI", "right"); - statusList.forEach((item) => { - // 绘制图标 - core.drawIcon( - "outerUI", - item, - baseX - 95 * 3, - curh - 18 * 3, - 22 * 3, - 22 * 3 - ); + bgctx.globalAlpha = 1; + core.setTextAlign("outerUI", "center"); + } + } + // 更新属性 + _update_props(updatedFloorTitle) { + if (!updatedFloorTitle && core.status.floorId) { + updatedFloorTitle = core.status.maps[core.status.floorId].title; + } + const statusList = ["hp", "atk", "def", "money"]; //属性列表,图标在函数复写core.statusBar.icons中声明,数字为project\materials\icons.png中的图标序号(可使用便捷ps追加,第一个序号为0) + const drawStatusList = (baseX, baseY) => { + let curh = baseY; + core.setTextAlign("outerUI", "right"); + statusList.forEach((item) => { + // 绘制图标 + core.drawIcon( + "outerUI", + item, + baseX - 95 * 3, + curh - 18 * 3, + 22 * 3, + 22 * 3 + ); - // 四舍五入 - core.status.hero[item] = Math.round(core.status.hero[item]); - // 大数据格式化 - core.fillBoldText1( - "outerUI", - core.getRealStatus(item), - baseX, - curh, - TEXT_COLOR, - "#000000", - 6 - ); - curh += 24 * 3; - if (curh > 130 * 3 && core.domStyle.isVertical) { - curh = 24 * 3; - baseX += 105 * 3; - } - }); - core.setTextAlign("outerUI", "center"); - }; - if (core.domStyle.isVertical) { - core.clearMap("outerUI", 10 * 3, 0, 210 * 3, 120 * 3); - core.setFont("outerUI", "bold 42px Verdana"); - if (updatedFloorTitle) { - core.fillBoldText1( - "outerUI", - updatedFloorTitle, - 60 * 3, - 22 * 3, - TEXT_COLOR, - "#000000", - 6 - ); - } - //drawStatusList(96 * 3, 46 * 3); - //core.drawImage("outerUI", "lane1.png", 0, 0) - core.drawImage("outerUI", "cao.webp", 0, 0); - } else { - core.clearMap("outerUI", 10 * 3, 40 * 3, 105 * 3, 250 * 3); - core.setFont("outerUI", "bold 48px Verdana"); - if (updatedFloorTitle) { - core.fillBoldText1( - "outerUI", - updatedFloorTitle, - 62 * 3, - 41 * 3, - TEXT_COLOR, - "#000000", - 6 - ); - } - //drawStatusList(110 * 3, 93 * 3); - //core.drawImage("outerUI", "lane1.png", 0, 30) - core.drawImage( - "outerUI", - "cao.webp", - 0, - 0, - 400, - 350, - 0, - 30, - 360, - 315 - ); - } - } - _update_items() { - //更新道具栏 - const drawItemMx = (drawFn) => { - for (let i = 0; i < this.itemMx.length; i++) { - for (let j = 0; j < this.itemMx[i].length; j++) { - var item = this.itemMx[i][j]; - drawFn(i, j, item); - } - } - }; - const drawItem = (item, posx, posy) => { - const icon = core.material.icons.items[item], - image = core.material.images.items; - core.drawImage( - "outerUI", - image, - 0, - 32 * icon, - 32, - 32, - posx, - posy, - 30 * 3, - 30 * 3 - ); - const cnt = core.itemCount(item); - if ( - (core.items.items[item].cls === "tools" && cnt > 1) || - FORCE_COUNTABLE_ITEMS.includes(item) - ) { - core.fillText( - "outerUI", - cnt, - posx + 25 * 3, - posy + 28 * 3, - "#FFFFFF", - "bold 36px Verdana" - ); - } - }; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - ITEM_BOX_LEFT_VERTICAL, - ITEM_BOX_TOP_VERTICAL, - 185 * 3, - 125 * 3 - ); + // 四舍五入 + core.status.hero[item] = Math.round(core.status.hero[item]); + // 大数据格式化 + core.fillBoldText1( + "outerUI", + core.getRealStatus(item), + baseX, + curh, + TEXT_COLOR, + "#000000", + 6 + ); + curh += 24 * 3; + if (curh > 130 * 3 && core.domStyle.isVertical) { + curh = 24 * 3; + baseX += 105 * 3; + } + }); + core.setTextAlign("outerUI", "center"); + }; + if (core.domStyle.isVertical) { + core.clearMap("outerUI", 10 * 3, 0, 210 * 3, 120 * 3); + core.setFont("outerUI", "bold 42px Verdana"); + if (updatedFloorTitle) { + core.fillBoldText1( + "outerUI", + updatedFloorTitle, + 60 * 3, + 22 * 3, + TEXT_COLOR, + "#000000", + 6 + ); + } + //drawStatusList(96 * 3, 46 * 3); + //core.drawImage("outerUI", "lane1.png", 0, 0) + core.drawImage("outerUI", "cao.webp", 0, 0); + } else { + core.clearMap("outerUI", 10 * 3, 40 * 3, 105 * 3, 250 * 3); + core.setFont("outerUI", "bold 48px Verdana"); + if (updatedFloorTitle) { + core.fillBoldText1( + "outerUI", + updatedFloorTitle, + 62 * 3, + 41 * 3, + TEXT_COLOR, + "#000000", + 6 + ); + } + //drawStatusList(110 * 3, 93 * 3); + //core.drawImage("outerUI", "lane1.png", 0, 30) + core.drawImage( + "outerUI", + "cao.webp", + 0, + 0, + 400, + 350, + 0, + 30, + 360, + 315 + ); + } + } + _update_items() { + //更新道具栏 + const drawItemMx = (drawFn) => { + for (let i = 0; i < this.itemMx.length; i++) { + for (let j = 0; j < this.itemMx[i].length; j++) { + var item = this.itemMx[i][j]; + drawFn(i, j, item); + } + } + }; + const drawItem = (item, posx, posy) => { + const icon = core.material.icons.items[item], + image = core.material.images.items; + core.drawImage( + "outerUI", + image, + 0, + 32 * icon, + 32, + 32, + posx, + posy, + 30 * 3, + 30 * 3 + ); + const cnt = core.itemCount(item); + if ( + (core.items.items[item].cls === "tools" && cnt > 1) || + FORCE_COUNTABLE_ITEMS.includes(item) + ) { + core.fillText( + "outerUI", + cnt, + posx + 25 * 3, + posy + 28 * 3, + "#FFFFFF", + "bold 36px Verdana" + ); + } + }; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + ITEM_BOX_LEFT_VERTICAL, + ITEM_BOX_TOP_VERTICAL, + 185 * 3, + 125 * 3 + ); - drawItemMx((i, j, item) => { - if (core.hasItem(item)) { - const posx = ITEM_BOX_LEFT_VERTICAL + i * 30 * 3, - posy = ITEM_BOX_TOP_VERTICAL + j * 31 * 3; - drawItem(item, posx, posy); - } - }); - } else { - core.clearMap( - "outerUI", - ITEM_BOX_LEFT, - ITEM_BOX_TOP, - 125 * 3, - 185 * 3 - ); + drawItemMx((i, j, item) => { + if (core.hasItem(item)) { + const posx = ITEM_BOX_LEFT_VERTICAL + i * 30 * 3, + posy = ITEM_BOX_TOP_VERTICAL + j * 31 * 3; + drawItem(item, posx, posy); + } + }); + } else { + core.clearMap( + "outerUI", + ITEM_BOX_LEFT, + ITEM_BOX_TOP, + 125 * 3, + 185 * 3 + ); - drawItemMx((i, j, item) => { - if (core.hasItem(item)) { - const posx = ITEM_BOX_LEFT + j * 30 * 3, - posy = ITEM_BOX_TOP + i * 31 * 3; - drawItem(item, posx, posy); - } - }); - } - } + drawItemMx((i, j, item) => { + if (core.hasItem(item)) { + const posx = ITEM_BOX_LEFT + j * 30 * 3, + posy = ITEM_BOX_TOP + i * 31 * 3; + drawItem(item, posx, posy); + } + }); + } + } - _update_map(floorId = core.status.floorId) { - const x = core.domStyle.isVertical - ? MAP_BLOCK_LEFT_VERTICAL - : MAP_BLOCK_LEFT; - const y = core.domStyle.isVertical - ? MAP_BLOCK_TOP_VERTICAL - : MAP_BLOCK_TOP; + _update_map(floorId = core.status.floorId) { + const x = core.domStyle.isVertical ? + MAP_BLOCK_LEFT_VERTICAL : + MAP_BLOCK_LEFT; + const y = core.domStyle.isVertical ? + MAP_BLOCK_TOP_VERTICAL : + MAP_BLOCK_TOP; - if (!floorId) return; - const info = core.plugin.getMapDrawInfo(floorId, Infinity, true); - core.setTextAlign("outerUI", "center"); + if (!floorId) return; + const info = core.plugin.getMapDrawInfo(floorId, Infinity, true); + core.setTextAlign("outerUI", "center"); - core.plugin.drawSmallMap(uictx, info, floorId, x, y, 300, 300); - } + core.plugin.drawSmallMap(uictx, info, floorId, x, y, 300, 300); + } - _update_equips() { - return; - core.setFont("outerUI", "bold 48px Verdana"); - const drawEquip = (baseX, baseY, id, color, back) => { - if (!id) - core.fillText( - "outerUI", - back, - baseX + 20 * 3, - baseY + 22 * 3, - color - ); - else { - var icon = core.material.icons.items[id]; - core.drawImage( - "outerUI", - core.material.images.items, - 0, - 32 * icon, - 32, - 32, - baseX + 5 * 3, - baseY, - 32 * 3, - 32 * 3 - ); - } - }; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL, - 90 * 3, - 130 * 3 - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL, - core.getEquip(0), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, - EQUIP_BLOCK_TOP_VERTICAL, - core.getEquip(1), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, - core.getEquip(2), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, - EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, - core.getEquip(3), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL, - EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, - core.getEquip(4), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, - EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, - core.getEquip(5), - "#D1CEFF", - "无" - ); - } else { - core.clearMap( - "outerUI", - EQUIP_BLOCK_LEFT, - EQUIP_BLOCK_TOP, - 130 * 3, - 95 * 3 - ); - drawEquip( - EQUIP_BLOCK_LEFT, - EQUIP_BLOCK_TOP, - core.getEquip(0), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 42 * 3, - EQUIP_BLOCK_TOP, - core.getEquip(1), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 85 * 3, - EQUIP_BLOCK_TOP, - core.getEquip(2), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT, - EQUIP_BLOCK_TOP + 45 * 3, - core.getEquip(3), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 42 * 3, - EQUIP_BLOCK_TOP + 45 * 3, - core.getEquip(4), - "#D1CEFF", - "无" - ); - drawEquip( - EQUIP_BLOCK_LEFT + 85 * 3, - EQUIP_BLOCK_TOP + 45 * 3, - core.getEquip(5), - "#D1CEFF", - "无" - ); - } - } - _update_keys() { - const drawKeyList = (baseX, baseY) => { - const todraw = [], - keyList = ["yellowKey", "blueKey", "redKey", "greenKey"]; - let total = 0; - keyList.forEach(function (key, i) { - todraw[i] = core.itemCount(key); - total += todraw[i]; - }); + _update_equips() { + return; + core.setFont("outerUI", "bold 48px Verdana"); + const drawEquip = (baseX, baseY, id, color, back) => { + if (!id) + core.fillText( + "outerUI", + back, + baseX + 20 * 3, + baseY + 22 * 3, + color + ); + else { + var icon = core.material.icons.items[id]; + core.drawImage( + "outerUI", + core.material.images.items, + 0, + 32 * icon, + 32, + 32, + baseX + 5 * 3, + baseY, + 32 * 3, + 32 * 3 + ); + } + }; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL, + 90 * 3, + 130 * 3 + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL, + core.getEquip(0), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, + EQUIP_BLOCK_TOP_VERTICAL, + core.getEquip(1), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, + core.getEquip(2), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, + EQUIP_BLOCK_TOP_VERTICAL + 45 * 3, + core.getEquip(3), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL, + EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, + core.getEquip(4), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3, + EQUIP_BLOCK_TOP_VERTICAL + 90 * 3, + core.getEquip(5), + "#D1CEFF", + "无" + ); + } else { + core.clearMap( + "outerUI", + EQUIP_BLOCK_LEFT, + EQUIP_BLOCK_TOP, + 130 * 3, + 95 * 3 + ); + drawEquip( + EQUIP_BLOCK_LEFT, + EQUIP_BLOCK_TOP, + core.getEquip(0), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 42 * 3, + EQUIP_BLOCK_TOP, + core.getEquip(1), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 85 * 3, + EQUIP_BLOCK_TOP, + core.getEquip(2), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT, + EQUIP_BLOCK_TOP + 45 * 3, + core.getEquip(3), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 42 * 3, + EQUIP_BLOCK_TOP + 45 * 3, + core.getEquip(4), + "#D1CEFF", + "无" + ); + drawEquip( + EQUIP_BLOCK_LEFT + 85 * 3, + EQUIP_BLOCK_TOP + 45 * 3, + core.getEquip(5), + "#D1CEFF", + "无" + ); + } + } + _update_keys() { + const drawKeyList = (baseX, baseY) => { + const todraw = [], + keyList = ["yellowKey", "blueKey", "redKey", "greenKey"]; + let total = 0; + keyList.forEach(function (key, i) { + todraw[i] = core.itemCount(key); + total += todraw[i]; + }); - let dn = 3; - for (let i = 0; i <= dn; i++) { - let delta = i * 32 * 3; + let dn = 3; + for (let i = 0; i <= dn; i++) { + let delta = i * 32 * 3; - if (core.domStyle.isVertical) { - this.drawKey(keyList[i], baseX, baseY + delta); - } else { - this.drawKey(keyList[i], baseX + delta, baseY); - } + if (core.domStyle.isVertical) { + this.drawKey(keyList[i], baseX, baseY + delta); + } else { + this.drawKey(keyList[i], baseX + delta, baseY); + } - core.setFont("outerUI", "bold 48px Verdana"); - core.setTextAlign("outerUI", "left"); - if (core.domStyle.isVertical) { - core.fillText( - "outerUI", - todraw[i], - baseX + 20 * 3, - baseY + 14 * 3 + delta, - TEXT_COLOR - ); - } else { - core.fillText( - "outerUI", - todraw[i], - baseX + delta, - baseY + 32 * 3, - TEXT_COLOR - ); - } - } - }; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - KEY_BLOCK_LEFT_VERTICAL, - KEY_BLOCK_TOP_VERTICAL, - 45 * 3, - 130 * 3 - ); - drawKeyList( - KEY_BLOCK_LEFT_VERTICAL + 3 * 3, - KEY_BLOCK_TOP_VERTICAL + 5 * 3 - ); - } else { - core.clearMap( - "outerUI", - KEY_BLOCK_LEFT, - KEY_BLOCK_TOP, - 130 * 3, - 45 * 3 - ); - drawKeyList(KEY_BLOCK_LEFT + 10 * 3, KEY_BLOCK_TOP); - } - } - drawKey(key, x, y) { - let sx = 0, - sy = 0; + core.setFont("outerUI", "bold 48px Verdana"); + core.setTextAlign("outerUI", "left"); + if (core.domStyle.isVertical) { + core.fillText( + "outerUI", + todraw[i], + baseX + 20 * 3, + baseY + 14 * 3 + delta, + TEXT_COLOR + ); + } else { + core.fillText( + "outerUI", + todraw[i], + baseX + delta, + baseY + 32 * 3, + TEXT_COLOR + ); + } + } + }; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + KEY_BLOCK_LEFT_VERTICAL, + KEY_BLOCK_TOP_VERTICAL, + 45 * 3, + 130 * 3 + ); + drawKeyList( + KEY_BLOCK_LEFT_VERTICAL + 3 * 3, + KEY_BLOCK_TOP_VERTICAL + 5 * 3 + ); + } else { + core.clearMap( + "outerUI", + KEY_BLOCK_LEFT, + KEY_BLOCK_TOP, + 130 * 3, + 45 * 3 + ); + drawKeyList(KEY_BLOCK_LEFT + 10 * 3, KEY_BLOCK_TOP); + } + } + drawKey(key, x, y) { + let sx = 0, + sy = 0; - if (key == "yellowKey") sx += 13; - else if (key == "blueKey") sx += 26; - else if (key == "greenKey") sx += 39; + if (key == "yellowKey") sx += 13; + else if (key == "blueKey") sx += 26; + else if (key == "greenKey") sx += 39; - core.drawImage( - "outerUI", - "maba.webp", - sx, - sy, - 13, - 26, - x, - y, - 13 * 3, - 26 * 3 - ); - } - _update_infoWindow() { - const itemId = this.selectedItem; - let text = ""; - if (this.selectedItem) { - text = core.replaceText(core.material.items[itemId]?.text); - if (text[0] == "," || text[0] == ",") text = text.substring(1); - } - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - INFO_BLOCK_LEFT_VERTICAL, - INFO_BLOCK_TOP_VERTICAL, - 300 * 3, - 120 * 3 - ); + core.drawImage( + "outerUI", + "maba.webp", + sx, + sy, + 13, + 26, + x, + y, + 13 * 3, + 26 * 3 + ); + } + _update_infoWindow() { + const itemId = this.selectedItem; + let text = ""; + if (this.selectedItem) { + text = core.replaceText(core.material.items[itemId]?.text); + if (text[0] == "," || text[0] == ",") text = text.substring(1); + } + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + INFO_BLOCK_LEFT_VERTICAL, + INFO_BLOCK_TOP_VERTICAL, + 300 * 3, + 120 * 3 + ); - if (this.selectedItem) { - const icon = core.material.icons.items[itemId]; - core.setTextAlign("outerUI", "left"); - core.fillText( - "outerUI", - core.material.items[itemId].name, - INFO_BLOCK_LEFT_VERTICAL + 50 * 3, - INFO_BLOCK_TOP_VERTICAL + 27 * 3, - "#D1CEFF" - ); - core.drawImage( - "outerUI", - core.material.images.items, - 0, - 32 * icon, - 32, - 32, - INFO_BLOCK_LEFT_VERTICAL + 10 * 3, - INFO_BLOCK_TOP_VERTICAL + 8 * 3, - 32 * 3, - 32 * 3 - ); - core.ui.drawTextContent("outerUI", text, { - left: INFO_BLOCK_LEFT_VERTICAL + 10 * 3, - top: INFO_BLOCK_TOP_VERTICAL + 40 * 3, - maxWidth: 275 * 3, - color: "#D1CEFF", - fontSize: 36, - }); - } - } else { - core.clearMap( - "outerUI", - INFO_BLOCK_LEFT, - INFO_BLOCK_TOP, - 115 * 3, - 230 * 3 - ); + if (this.selectedItem) { + const icon = core.material.icons.items[itemId]; + core.setTextAlign("outerUI", "left"); + core.fillText( + "outerUI", + core.material.items[itemId].name, + INFO_BLOCK_LEFT_VERTICAL + 50 * 3, + INFO_BLOCK_TOP_VERTICAL + 27 * 3, + "#D1CEFF" + ); + core.drawImage( + "outerUI", + core.material.images.items, + 0, + 32 * icon, + 32, + 32, + INFO_BLOCK_LEFT_VERTICAL + 10 * 3, + INFO_BLOCK_TOP_VERTICAL + 8 * 3, + 32 * 3, + 32 * 3 + ); + core.ui.drawTextContent("outerUI", text, { + left: INFO_BLOCK_LEFT_VERTICAL + 10 * 3, + top: INFO_BLOCK_TOP_VERTICAL + 40 * 3, + maxWidth: 275 * 3, + color: "#D1CEFF", + fontSize: 36, + }); + } + } else { + core.clearMap( + "outerUI", + INFO_BLOCK_LEFT, + INFO_BLOCK_TOP, + 115 * 3, + 230 * 3 + ); - if (this.selectedItem) { - const icon = core.material.icons.items[itemId]; - core.setTextAlign("outerUI", "center"); - core.fillText( - "outerUI", - core.material.items[itemId].name, - INFO_BLOCK_LEFT + 60 * 3, - INFO_BLOCK_TOP + 25 * 3, - "#D1CEFF" - ); - core.drawImage( - "outerUI", - core.material.images.items, - 0, - 32 * icon, - 32, - 32, - INFO_BLOCK_LEFT + 45 * 3, - INFO_BLOCK_TOP + 30 * 3, - 32 * 3, - 32 * 3 - ); - core.ui.drawTextContent("outerUI", text, { - left: INFO_BLOCK_LEFT + 10 * 3, - top: INFO_BLOCK_TOP + 60 * 3, - maxWidth: 105 * 3, - color: "#D1CEFF", - fontSize: 36, - }); - } - } - } - showItemInfo(itemId) { - //展示道具说明 - this.selectedItem = itemId; - this._update_infoWindow(); - } - clearItemInfo() { - //清除道具说明 - this.selectedItem = null; - this._update_infoWindow(); - } - _update_toolBox() { - const tools = core.isReplaying() - ? [ - [core.status.replay.pausing ? "play" : "pause", "stop", "rewind"], - ["speedDown", "speedUp", "save"], - ] - : [ - ["keyboard", "shop", "pack", "T332"], - ["settings", "save", "load", "T331"], - ]; - if (core.domStyle.isVertical) { - core.clearMap( - "outerUI", - TOOL_BOX_LEFT_VERTICAL, - TOOL_BOX_TOP_VERTICAL, - 115, - 130 - ); + if (this.selectedItem) { + const icon = core.material.icons.items[itemId]; + core.setTextAlign("outerUI", "center"); + core.fillText( + "outerUI", + core.material.items[itemId].name, + INFO_BLOCK_LEFT + 60 * 3, + INFO_BLOCK_TOP + 25 * 3, + "#D1CEFF" + ); + core.drawImage( + "outerUI", + core.material.images.items, + 0, + 32 * icon, + 32, + 32, + INFO_BLOCK_LEFT + 45 * 3, + INFO_BLOCK_TOP + 30 * 3, + 32 * 3, + 32 * 3 + ); + core.ui.drawTextContent("outerUI", text, { + left: INFO_BLOCK_LEFT + 10 * 3, + top: INFO_BLOCK_TOP + 60 * 3, + maxWidth: 105 * 3, + color: "#D1CEFF", + fontSize: 36, + }); + } + } + } + showItemInfo(itemId) { + //展示道具说明 + this.selectedItem = itemId; + this._update_infoWindow(); + } + clearItemInfo() { + //清除道具说明 + this.selectedItem = null; + this._update_infoWindow(); + } + _update_toolBox() { + const tools = core.isReplaying() ? [ + [core.status.replay.pausing ? "play" : "pause", "stop", "rewind"], + ["speedDown", "speedUp", "save"], + ] : [ + ["keyboard", "shop", "pack", "T332"], + ["settings", "save", "load", "T331"], + ]; + if (core.domStyle.isVertical) { + core.clearMap( + "outerUI", + TOOL_BOX_LEFT_VERTICAL, + TOOL_BOX_TOP_VERTICAL, + 115, + 130 + ); - for (let i = 0; i < tools.length; i++) { - for (let j = 0; j < tools[i].length; j++) { - core.drawIcon( - "outerUI", - tools[i][j], - TOOL_BOX_LEFT_VERTICAL + i * 31 * 3, - TOOL_BOX_TOP_VERTICAL + j * 31 * 3, - 30 * 3, - 30 * 3 - ); - } - } - } else { - core.clearMap( - "outerUI", - TOOL_BOX_LEFT, - TOOL_BOX_TOP, - 130 * 3, - 80 * 3 - ); + for (let i = 0; i < tools.length; i++) { + for (let j = 0; j < tools[i].length; j++) { + core.drawIcon( + "outerUI", + tools[i][j], + TOOL_BOX_LEFT_VERTICAL + i * 31 * 3, + TOOL_BOX_TOP_VERTICAL + j * 31 * 3, + 30 * 3, + 30 * 3 + ); + } + } + } else { + core.clearMap( + "outerUI", + TOOL_BOX_LEFT, + TOOL_BOX_TOP, + 130 * 3, + 80 * 3 + ); - for (let i = 0; i < tools.length; i++) { - for (let j = 0; j < tools[i].length; j++) { - core.drawIcon( - "outerUI", - tools[i][j], - TOOL_BOX_LEFT + j * 31 * 3, - TOOL_BOX_TOP + i * 31 * 3, - 30 * 3, - 30 * 3 - ); - } - } - } - } - onclick(x, y) { - const makeBox = ([x, y], [w, h]) => { - return [ - [x, y], - [x + w, y + h], - ]; - }; - const gridify = ([x, y], [gw, gh]) => { - return [Math.floor(x / gw), Math.floor(y / gh)]; - }; - const useItem = (itemId) => { - if (!core.hasItem(itemId)) return; + for (let i = 0; i < tools.length; i++) { + for (let j = 0; j < tools[i].length; j++) { + core.drawIcon( + "outerUI", + tools[i][j], + TOOL_BOX_LEFT + j * 31 * 3, + TOOL_BOX_TOP + i * 31 * 3, + 30 * 3, + 30 * 3 + ); + } + } + } + } + onclick(x, y) { + const makeBox = ([x, y], [w, h]) => { + return [ + [x, y], + [x + w, y + h], + ]; + }; + const gridify = ([x, y], [gw, gh]) => { + return [Math.floor(x / gw), Math.floor(y / gh)]; + }; + const useItem = (itemId) => { + if (!core.hasItem(itemId)) return; - if (itemId != this.selectedItem) { - this.showItemInfo(itemId); - } else { - switch (itemId) { - case "centerFly": - core.ui._drawCenterFly(); - break; - case "book": - core.openBook(true); - break; - case "wand": - core.insertAction({ - type: "useItem", - id: itemId, - }); - break; - case "fly": - core.useItem(itemId); - break; - default: - core.useItem(itemId); - } - } - }; - const inRect = ([x, y], [[sx, sy], [dx, dy]]) => { - return sx <= x && x <= dx && sy <= y && y <= dy; - }; - const relativeTo = ([x, y], [ax, ay]) => { - return [x - ax, y - ay]; - }; - const pos = [x, y]; - if (core.domStyle.isVertical) { - const itemBox = makeBox( - [ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL], - [30 * 6 * 3, 31 * 4 * 3] - ); - if (inRect(pos, itemBox)) { - const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ - 30 * 3, - 31 * 3, - ]); - const itemId = this.itemMx[gx][gy]; - if ( - (core.status.event.id == "viewMaps" || - core.status.event.id == "fly") && - itemId === "book" - ) - core.openBook(true); - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - useItem(itemId); - return; - } - const toolBox = makeBox( - [TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL], - [31 * 2 * 3, 31 * 4 * 3] - ); - if (inRect(pos, toolBox)) { - const [col, row] = gridify(relativeTo(pos, toolBox[0]), [ - 31 * 3, - 31 * 3, - ]); - if (core.status.lockControl || core.isMoving()) return; - if (core.isReplaying()) { - this.replayAction[col][row].call(core); - } else if (core.isPlaying()) { - if (col === 0 && row === 3) { - core.doSL("autoSave", "load"); - } else if (col === 1 && row === 3) { - core.doSL("autoSave", "reload"); - } else { - this.toolbarAction[col][row].call(core, true); - } - } - return; - } - const mapBox = makeBox( - [MAP_BLOCK_LEFT_VERTICAL, MAP_BLOCK_TOP_VERTICAL], - [350, 350] - ); - if (inRect(pos, mapBox)) { - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - core.useItem("fly"); - return; - } - /*const equipBox = makeBox([EQUIP_BLOCK_LEFT_VERTICAL, EQUIP_BLOCK_TOP_VERTICAL], [90 * 3, 130 * 3]) + if (itemId != this.selectedItem) { + this.showItemInfo(itemId); + } else { + switch (itemId) { + case "centerFly": + core.ui._drawCenterFly(); + break; + case "book": + core.openBook(true); + break; + case "wand": + core.insertAction({ + type: "useItem", + id: itemId, + }); + break; + case "fly": + core.useItem(itemId); + break; + default: + core.useItem(itemId); + } + } + }; + const inRect = ([x, y], [ + [sx, sy], + [dx, dy] + ]) => { + return sx <= x && x <= dx && sy <= y && y <= dy; + }; + const relativeTo = ([x, y], [ax, ay]) => { + return [x - ax, y - ay]; + }; + const pos = [x, y]; + if (core.domStyle.isVertical) { + const itemBox = makeBox( + [ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL], + [30 * 6 * 3, 31 * 4 * 3] + ); + if (inRect(pos, itemBox)) { + const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ + 30 * 3, + 31 * 3, + ]); + const itemId = this.itemMx[gx][gy]; + if ( + (core.status.event.id == "viewMaps" || + core.status.event.id == "fly") && + itemId === "book" + ) + core.openBook(true); + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + useItem(itemId); + return; + } + const toolBox = makeBox( + [TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL], + [31 * 2 * 3, 31 * 4 * 3] + ); + if (inRect(pos, toolBox)) { + const [col, row] = gridify(relativeTo(pos, toolBox[0]), [ + 31 * 3, + 31 * 3, + ]); + if (core.status.lockControl || core.isMoving()) return; + if (core.isReplaying()) { + this.replayAction[col][row].call(core); + } else if (core.isPlaying()) { + if (col === 0 && row === 3) { + core.doSL("autoSave", "load"); + } else if (col === 1 && row === 3) { + core.doSL("autoSave", "reload"); + } else { + this.toolbarAction[col][row].call(core, true); + } + } + return; + } + const mapBox = makeBox( + [MAP_BLOCK_LEFT_VERTICAL, MAP_BLOCK_TOP_VERTICAL], + [350, 350] + ); + if (inRect(pos, mapBox)) { + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + core.useItem("fly"); + return; + } + /*const equipBox = makeBox([EQUIP_BLOCK_LEFT_VERTICAL, EQUIP_BLOCK_TOP_VERTICAL], [90 * 3, 130 * 3]) if (inRect(pos, equipBox)) { if (core.isReplaying() || core.status.lockControl || core.isMoving()) return; core.openEquipbox(true) return; }*/ - } else { - const mapBox = makeBox([MAP_BLOCK_LEFT, MAP_BLOCK_TOP], [350, 350]); - if (inRect(pos, mapBox)) { - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - core.useItem("fly"); - return; - } - /* + } else { + const mapBox = makeBox([MAP_BLOCK_LEFT, MAP_BLOCK_TOP], [350, 350]); + if (inRect(pos, mapBox)) { + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + core.useItem("fly"); + return; + } + /* const equipBox = makeBox([EQUIP_BLOCK_LEFT, EQUIP_BLOCK_TOP], [130, 95]) if (inRect(pos, equipBox)) { if (core.isReplaying() || core.status.lockControl || core.isMoving()) return; core.openEquipbox(true) return; }*/ - const itemBox = makeBox( - [ITEM_BOX_LEFT, ITEM_BOX_TOP], - [31 * 4 * 3, 30 * 6 * 3] - ); - if (inRect(pos, itemBox)) { - const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ - 31 * 3, - 30 * 3, - ]); - const itemId = this.itemMx[gy][gx]; - if ( - (core.status.event.id == "viewMaps" || - core.status.event.id == "fly") && - itemId === "book" - ) - core.openBook(true); - if ( - core.isReplaying() || - core.status.lockControl || - core.isMoving() - ) - return; - useItem(itemId); - return; - } - const toolBox = makeBox( - [TOOL_BOX_LEFT, TOOL_BOX_TOP], - [31 * 4 * 3, 31 * 2 * 3] - ); - if (inRect(pos, toolBox)) { - const [row, col] = gridify(relativeTo(pos, toolBox[0]), [ - 31 * 3, - 31 * 3, - ]); - if (core.status.lockControl || core.isMoving()) return; - if (core.isReplaying()) { - this.replayAction[col][row].call(core); - } else if (core.isPlaying()) { - if (col === 0 && row === 3) { - core.doSL("autoSave", "load"); - } else if (col === 1 && row === 3) { - core.doSL("autoSave", "reload"); - } else { - this.toolbarAction[col][row].call(core, true); - } - } - return; - } - } - } - } + const itemBox = makeBox( + [ITEM_BOX_LEFT, ITEM_BOX_TOP], + [31 * 4 * 3, 30 * 6 * 3] + ); + if (inRect(pos, itemBox)) { + const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [ + 31 * 3, + 30 * 3, + ]); + const itemId = this.itemMx[gy][gx]; + if ( + (core.status.event.id == "viewMaps" || + core.status.event.id == "fly") && + itemId === "book" + ) + core.openBook(true); + if ( + core.isReplaying() || + core.status.lockControl || + core.isMoving() + ) + return; + useItem(itemId); + return; + } + const toolBox = makeBox( + [TOOL_BOX_LEFT, TOOL_BOX_TOP], + [31 * 4 * 3, 31 * 2 * 3] + ); + if (inRect(pos, toolBox)) { + const [row, col] = gridify(relativeTo(pos, toolBox[0]), [ + 31 * 3, + 31 * 3, + ]); + if (core.status.lockControl || core.isMoving()) return; + if (core.isReplaying()) { + this.replayAction[col][row].call(core); + } else if (core.isPlaying()) { + if (col === 0 && row === 3) { + core.doSL("autoSave", "load"); + } else if (col === 1 && row === 3) { + core.doSL("autoSave", "reload"); + } else { + this.toolbarAction[col][row].call(core, true); + } + } + return; + } + } + } + } - core.ui.statusBar = new StatusBar(); + core.ui.statusBar = new StatusBar(); - core.control.clearStatusBar = function () { - core.clearMap("outerUI"); - }; - // init() called in `afterLoadResources`. - }, + core.control.clearStatusBar = function () { + core.clearMap("outerUI"); + }; + // init() called in `afterLoadResources`. +}, "override": function () { core.statusBar.icons = { floor: 0, @@ -8728,8 +8724,8 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }; }, "音频系统": function () { - // 在此增加新插件 - /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: + // 在此增加新插件 + /*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行: @@ -8737,2086 +8733,2084 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = */ - // 将__enable置为false将关闭插件 - let __enable = true; - if (!__enable || main.mode === "editor") return; - const { OggOpusDecoderWebWorker } = window["ogg-opus-decoder"]; - const { OggVorbisDecoderWebWorker } = window["ogg-vorbis-decoder"]; - const { CodecParser } = window.CodecParser; - const { Transition, linear } = core.plugin.animate; - - const audio = new Audio(); - const AudioStatus = { - Playing: 0, - Pausing: 1, - Paused: 2, - Stoping: 3, - Stoped: 4, - }; - const supportMap = new Map(); - const AudioType = { - Mp3: "audio/mpeg", - Wav: 'audio/wav; codecs="1"', - Flac: "audio/flac", - Opus: 'audio/ogg; codecs="opus"', - Ogg: 'audio/ogg; codecs="vorbis"', - Aac: "audio/aac", - }; - /** - * 检查一种音频类型是否能被播放 - * @param type 音频类型 AudioType - */ - function isAudioSupport(type) { - if (supportMap.has(type)) return supportMap.get(type); - else { - const support = audio.canPlayType(type); - const canPlay = support === "maybe" || support === "probably"; - supportMap.set(type, canPlay); - return canPlay; - } - } - - const typeMap = new Map([ - ["ogg", AudioType.Ogg], - ["mp3", AudioType.Mp3], - ["wav", AudioType.Wav], - ["flac", AudioType.Flac], - ["opus", AudioType.Opus], - ["aac", AudioType.Aac], - ]); - - /** - * 根据文件名拓展猜测其类型 - * @param file 文件名 string - */ - function guessTypeByExt(file) { - const ext = /\.[a-zA-Z\d]+$/.exec(file); - if (!ext?.[0]) return ""; - const type = ext[0].slice(1); - return typeMap.get(type.toLocaleLowerCase()) ?? ""; - } - - isAudioSupport(AudioType.Ogg); - isAudioSupport(AudioType.Mp3); - isAudioSupport(AudioType.Wav); - isAudioSupport(AudioType.Flac); - isAudioSupport(AudioType.Opus); - isAudioSupport(AudioType.Aac); - - function isNil(value) { - return value === void 0 || value === null; - } - - function sleep(time) { - return new Promise((res) => setTimeout(res, time)); - } - class AudioEffect { - constructor(ac) {} - /** - * 连接至其他效果器 - * @param target 目标输入 IAudioInput - * @param output 当前效果器输出通道 Number - * @param input 目标效果器的输入通道 Number - */ - connect(target, output, input) { - this.output.connect(target.input, output, input); - } - - /** - * 与其他效果器取消连接 - * @param target 目标输入 IAudioInput - * @param output 当前效果器输出通道 Number - * @param input 目标效果器的输入通道 Number - */ - disconnect(target, output, input) { - if (!target) { - if (!isNil(output)) { - this.output.disconnect(output); - } else { - this.output.disconnect(); - } - } else { - if (!isNil(output)) { - if (!isNil(input)) { - this.output.disconnect(target.input, output, input); - } else { - this.output.disconnect(target.input, output); - } - } else { - this.output.disconnect(target.input); - } - } - } - } - - class StereoEffect extends AudioEffect { - constructor(ac) { - super(ac); - const panner = ac.createPanner(); - this.input = panner; - this.output = panner; - } - - /** - * 设置音频朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 朝向x坐标 Number - * @param y 朝向y坐标 Number - * @param z 朝向z坐标 Number - */ - setOrientation(x, y, z) { - this.output.orientationX.value = x; - this.output.orientationY.value = y; - this.output.orientationZ.value = z; - } - /** - * 设置音频位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 位置x坐标 Number - * @param y 位置y坐标 Number - * @param z 位置z坐标 Number - */ - setPosition(x, y, z) { - this.output.positionX.value = x; - this.output.positionY.value = y; - this.output.positionZ.value = z; - } - end() {} - - start() {} - } - class VolumeEffect extends AudioEffect { - constructor(ac) { - super(ac); - const gain = ac.createGain(); - this.input = gain; - this.output = gain; - } - - /** - * 设置音量大小 - * @param volume 音量大小 Number - */ - setVolume(volume) { - this.output.gain.value = volume; - } - - /** - * 获取音量大小 Number - */ - getVolume() { - return this.output.gain.value; - } - - end() {} - - start() {} - } - class ChannelVolumeEffect extends AudioEffect { - /** 所有的音量控制节点 */ - - constructor(ac) { - super(ac); - /** 所有的音量控制节点 */ - this.gain = []; - const splitter = ac.createChannelSplitter(); - const merger = ac.createChannelMerger(); - this.output = merger; - this.input = splitter; - for (let i = 0; i < 6; i++) { - const gain = ac.createGain(); - splitter.connect(gain, i); - gain.connect(merger, 0, i); - this.gain.push(gain); - } - } - - /** - * 设置某个声道的音量大小 - * @param channel 要设置的声道,可填0-5 Number - * @param volume 这个声道的音量大小 Number - */ - setVolume(channel, volume) { - if (!this.gain[channel]) return; - this.gain[channel].gain.value = volume; - } - - /** - * 获取某个声道的音量大小,可填0-5 - * @param channel 要获取的声道 Number - */ - getVolume(channel) { - if (!this.gain[channel]) return 0; - return this.gain[channel].gain.value; - } - - end() {} - - start() {} - } - class DelayEffect extends AudioEffect { - constructor(ac) { - super(ac); - - const delay = ac.createDelay(); - this.input = delay; - this.output = delay; - } - - /** - * 设置延迟时长 - * @param delay 延迟时长,单位秒 Number - */ - setDelay(delay) { - this.output.delayTime.value = delay; - } - - /** - * 获取延迟时长 - */ - getDelay() { - return this.output.delayTime.value; - } - - end() {} - - start() {} - } - class EchoEffect extends AudioEffect { - constructor(ac) { - super(ac); - /** 当前增益 */ - this.gain = 0.5; - /** 是否正在播放 */ - this.playing = false; - const delay = ac.createDelay(); - const gain = ac.createGain(); - gain.gain.value = 0.5; - delay.delayTime.value = 0.05; - delay.connect(gain); - gain.connect(delay); - /** 延迟节点 */ - this.delay = delay; - /** 反馈增益节点 */ - this.gainNode = gain; - - this.input = gain; - this.output = gain; - } - - /** - * 设置回声反馈增益大小 - * @param gain 增益大小,范围 0-1,大于等于1的视为0.5,小于0的视为0 Number - */ - setFeedbackGain(gain) { - const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain; - this.gain = resolved; - if (this.playing) this.gainNode.gain.value = resolved; - } - - /** - * 设置回声间隔时长 - * @param delay 回声时长,范围 0.01-Infinity,小于0.01的视为0.01 Number - */ - setEchoDelay(delay) { - const resolved = delay < 0.01 ? 0.01 : delay; - this.delay.delayTime.value = resolved; - } - - /** - * 获取反馈节点增益 - */ - getFeedbackGain() { - return this.gain; - } - - /** - * 获取回声间隔时长 - */ - getEchoDelay() { - return this.delay.delayTime.value; - } - - end() { - this.playing = false; - const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10; - sleep(this.delay.delayTime.value * echoTime).then(() => { - if (!this.playing) this.gainNode.gain.value = 0; - }); - } - - start() { - this.playing = true; - this.gainNode.gain.value = this.gain; - } - } - - class StreamLoader { - constructor(url) { - /** 传输目标 Set*/ - this.target = new Set(); - this.loading = false; - } - - /** - * 将加载流传递给字节流读取对象 - * @param reader 字节流读取对象 IStreamReader - */ - pipe(reader) { - if (this.loading) { - console.warn( - "Cannot pipe new StreamReader object when stream is loading." - ); - return; - } - this.target.add(reader); - reader.piped(this); - return this; - } - - async start() { - if (this.loading) return; - this.loading = true; - const response = await window.fetch(this.url); - const stream = response.body; - if (!stream) { - console.error("Cannot get reader when fetching '" + this.url + "'."); - return; - } - // 获取读取器 - this.stream = stream; - const reader = response.body?.getReader(); - const targets = [...this.target]; - - await Promise.all(targets.map((v) => v.start(stream, this, response))); - if (reader && reader.read) { - // 开始流传输 - while (true) { - const { value, done } = await reader.read(); - await Promise.all( - targets.map((v) => v.pump(value, done, response)) - ); - if (done) break; - } - } else { - // 如果不支持流传输 - const buffer = await response.arrayBuffer(); - const data = new Uint8Array(buffer); - await Promise.all(targets.map((v) => v.pump(data, true, response))); - } - - this.loading = false; - targets.forEach((v) => v.end(true)); - - // - } - - cancel(reason) { - if (!this.stream) return; - this.stream.cancel(reason); - this.loading = false; - this.target.forEach((v) => v.end(false, reason)); - } - } - const fileSignatures = [ - [AudioType.Mp3, [0x49, 0x44, 0x33]], - [AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]], - [AudioType.Wav, [0x52, 0x49, 0x46, 0x46]], - [AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]], - [AudioType.Aac, [0xff, 0xf1]], - [AudioType.Aac, [0xff, 0xf9]], - ]; - const oggHeaders = [ - [AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]], - ]; - - function checkAudioType(data) { - let audioType = ""; - // 检查头文件获取音频类型,仅检查前256个字节 - const toCheck = data.slice(0, 256); - for (const [type, value] of fileSignatures) { - if (value.every((v, i) => toCheck[i] === v)) { - audioType = type; - break; - } - } - if (audioType === AudioType.Ogg) { - // 如果是ogg的话,进一步判断是不是opus - for (const [key, value] of oggHeaders) { - const has = toCheck.some((_, i) => { - return value.every((v, ii) => toCheck[i + ii] === v); - }); - if (has) { - audioType = key; - break; - } - } - } - - return audioType; - } - class AudioDecoder { - /** - * 注册一个解码器 - * @param type 要注册的解码器允许解码的类型 - * @param decoder 解码器对象 - */ - static registerDecoder(type, decoder) { - if (!this.decoderMap) this.decoderMap = new Map(); - if (this.decoderMap.has(type)) { - console.warn( - "Audio stream decoder for audio type '" + - type + - "' has already existed." - ); - return; - } - - this.decoderMap.set(type, decoder); - } - - /** - * 解码音频数据 - * @param data 音频文件数据 - * @param player AudioPlayer实例 - */ - static async decodeAudioData(data, player) { - // 检查头文件获取音频类型,仅检查前256个字节 - const toCheck = data.slice(0, 256); - const type = checkAudioType(data); - if (type === "") { - console.error( - "Unknown audio type. Header: '" + - [...toCheck] - .map((v) => v.toString().padStart(2, "0")) - .join(" ") - .toUpperCase() + - "'" - ); - return null; - } - if (isAudioSupport(type)) { - if (data.buffer instanceof ArrayBuffer) { - return player.ac.decodeAudioData(data.buffer); - } else { - return null; - } - } else { - const Decoder = this.decoderMap.get(type); - if (!Decoder) { - return null; - } else { - const decoder = new Decoder(); - await decoder.create(); - const decodedData = await decoder.decode(data); - if (!decodedData) return null; - const buffer = player.ac.createBuffer( - decodedData.channelData.length, - decodedData.channelData[0].length, - decodedData.sampleRate - ); - decodedData.channelData.forEach((v, i) => { - buffer.copyToChannel(v, i); - }); - decoder.destroy(); - return buffer; - } - } - } - } - - class VorbisDecoder { - /** - * 创建音频解码器 - */ - async create() { - this.decoder = new OggVorbisDecoderWebWorker(); - await this.decoder.ready; - } - /** - * 摧毁这个解码器 - */ - destroy() { - this.decoder?.free(); - } - /** - * 解码流数据 - * @param data 流数据 - */ - - async decode(data) { - return this.decoder?.decode(data); - } - /** - * 解码整个文件 - * @param data 文件数据 - */ - async decodeAll(data) { - return this.decoder?.decodeFile(data); - } - /** - * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 - */ - async flush() { - return this.decoder?.flush(); - } - } - - class OpusDecoder { - /** - * 创建音频解码器 - */ - async create() { - this.decoder = new OggOpusDecoderWebWorker(); - await this.decoder.ready; - } - /** - * 摧毁这个解码器 - */ - destroy() { - this.decoder?.free(); - } - /** - * 解码流数据 - * @param data 流数据 - */ - async decode(data) { - return this.decoder?.decode(data); - } - /** - * 解码整个文件 - * @param data 文件数据 - */ - async decodeAll(data) { - return this.decoder?.decodeFile(data); - } - /** - * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 - */ - async flush() { - return await this.decoder?.flush(); - } - } - const mimeTypeMap = { - [AudioType.Aac]: "audio/aac", - [AudioType.Flac]: "audio/flac", - [AudioType.Mp3]: "audio/mpeg", - [AudioType.Ogg]: "application/ogg", - [AudioType.Opus]: "application/ogg", - [AudioType.Wav]: "application/ogg", - }; - - function isOggPage(data) { - return !isNil(data.isFirstPage); - } - class AudioStreamSource { - route; - constructor(context) { - this.output = context.createBufferSource(); - /** 是否已经完全加载完毕 */ - this.loaded = false; - /** 是否正在播放 */ - this.playing = false; - /** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */ - this.buffered = 0; - /** 已经缓冲的采样点数量 */ - this.bufferedSamples = 0; - /** 歌曲时长,加载完毕之前保持为 0 */ - this.duration = 0; - /** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */ - this.bufferPlayDuration = 1; - /** 音频的采样率,未成功解析出之前保持为 0 */ - this.sampleRate = 0; - //是否循环播放 - this.loop = false; - /** 上一次播放是从何时开始的 */ - this.lastStartWhen = 0; - /** 开始播放时刻 */ - this.lastStartTime = 0; - /** 上一次播放的缓存长度 */ - this.lastBufferSamples = 0; - - /** 是否已经获取到头文件 */ - this.headerRecieved = false; - /** 音频类型 */ - this.audioType = ""; - /** 每多长时间组成一个缓存 Float32Array */ - this.bufferChunkSize = 10; - /** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */ - this.audioData = []; - - this.errored = false; - this.ac = context; - } - /** 当前已经播放了多长时间 */ - get currentTime() { - return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; - } - /** - * 设置每个缓存数据的大小,默认为10秒钟一个缓存数据 - * @param size 每个缓存数据的时长,单位秒 - */ - setChunkSize(size) { - if (this.controller?.loading || this.loaded) return; - this.bufferChunkSize = size; - } - - piped(controller) { - this.controller = controller; - } - - async pump(data, done) { - if (!data || this.errored) return; - if (!this.headerRecieved) { - // 检查头文件获取音频类型,仅检查前256个字节 - const toCheck = data.slice(0, 256); - this.audioType = checkAudioType(data); - if (!this.audioType) { - console.error( - "Unknown audio type. Header: '" + - [...toCheck] - .map((v) => v.toString(16).padStart(2, "0")) - .join(" ") - .toUpperCase() + - "'" - ); - return; - } - // 创建解码器 - const Decoder = AudioDecoder.decoderMap.get(this.audioType); - if (!Decoder) { - this.errored = true; - console.error( - "Cannot decode stream source type of '" + - this.audioType + - "', since there is no registered decoder for that type." - ); - return Promise.reject( - `Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.` - ); - } - this.decoder = new Decoder(); - // 创建数据解析器 - const mime = mimeTypeMap[this.audioType]; - const parser = new CodecParser(mime); - this.parser = parser; - await this.decoder.create(); - this.headerRecieved = true; - } - - const decoder = this.decoder; - const parser = this.parser; - if (!decoder || !parser) { - this.errored = true; - return Promise.reject( - "No parser or decoder attached in this AudioStreamSource" - ); - } - - await this.decodeData(data, decoder, parser); - if (done) await this.decodeFlushData(decoder, parser); - this.checkBufferedPlay(); - } - - /** - * 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告 - */ - checkSampleRate(info) { - for (const one of info) { - const frame = isOggPage(one) ? one.codecFrames[0] : one; - if (frame) { - const rate = frame.header.sampleRate; - if (this.sampleRate === 0) { - this.sampleRate = rate; - break; - } else { - if (rate !== this.sampleRate) { - console.warn("Sample rate in stream audio must be constant."); - } - } - } - } - } - - /** - * 解析音频数据 - */ - async decodeData(data, decoder, parser) { - // 解析音频数据 - const audioData = await decoder.decode(data); - if (!audioData) return; - // @ts-expect-error 库类型声明错误 - const audioInfo = [...parser.parseChunk(data)]; - - // 检查采样率 - this.checkSampleRate(audioInfo); - // 追加音频数据 - this.appendDecodedData(audioData, audioInfo); - } - - /** - * 解码剩余数据 - */ - async decodeFlushData(decoder, parser) { - const audioData = await decoder.flush(); - if (!audioData) return; - // @ts-expect-error 库类型声明错误 - const audioInfo = [...parser.flush()]; - - this.checkSampleRate(audioInfo); - this.appendDecodedData(audioData, audioInfo); - } - - /** - * 追加音频数据 - */ - appendDecodedData(data, info) { - const channels = data.channelData.length; - if (channels === 0) return; - if (this.audioData.length !== channels) { - this.audioData = []; - for (let i = 0; i < channels; i++) { - this.audioData.push([]); - } - } - // 计算出应该放在哪 - const chunk = this.sampleRate * this.bufferChunkSize; - const sampled = this.bufferedSamples; - const pushIndex = Math.floor(sampled / chunk); - const bufferIndex = sampled % chunk; - const dataLength = data.channelData[0].length; - let buffered = 0; - let nowIndex = pushIndex; - let toBuffer = bufferIndex; - while (buffered < dataLength) { - const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk; - - for (let i = 0; i < channels; i++) { - const audioData = this.audioData[i]; - if (!audioData[nowIndex]) { - audioData.push(new Float32Array(chunk)); - } - const toPush = data.channelData[i].slice(buffered, buffered + rest); - - audioData[nowIndex].set(toPush, toBuffer); - } - buffered += rest; - nowIndex++; - toBuffer = 0; - } - - this.buffered += - info.reduce((prev, curr) => prev + curr.duration, 0) / 1000; - this.bufferedSamples += info.reduce( - (prev, curr) => prev + curr.samples, - 0 - ); - } - - /** - * 检查已缓冲内容,并在未开始播放时播放 - */ - checkBufferedPlay() { - if (this.playing || this.sampleRate === 0) return; - const played = this.lastBufferSamples / this.sampleRate; - const dt = this.buffered - played; - if (this.loaded) { - this.playAudio(played); - return; - } - if (dt < this.bufferPlayDuration) return; - - this.lastBufferSamples = this.bufferedSamples; - // 需要播放 - this.mergeBuffers(); - if (!this.buffer) return; - if (this.playing) this.output.stop(); - this.createSourceNode(this.buffer); - this.output.loop = false; - this.output.start(0, played); - this.lastStartTime = this.ac.currentTime; - this.playing = true; - this.output.addEventListener("ended", () => { - this.playing = false; - this.checkBufferedPlay(); - }); - } - - mergeBuffers() { - const buffer = this.ac.createBuffer( - this.audioData.length, - this.bufferedSamples, - this.sampleRate - ); - const chunk = this.sampleRate * this.bufferChunkSize; - const bufferedChunks = Math.floor(this.bufferedSamples / chunk); - const restLength = this.bufferedSamples % chunk; - for (let i = 0; i < this.audioData.length; i++) { - const audio = this.audioData[i]; - const data = new Float32Array(this.bufferedSamples); - for (let j = 0; j < bufferedChunks; j++) { - data.set(audio[j], chunk * j); - } - if (restLength !== 0) { - data.set( - audio[bufferedChunks].slice(0, restLength), - chunk * bufferedChunks - ); - } - - buffer.copyToChannel(data, i, 0); - } - this.buffer = buffer; - } - - async start() { - delete this.buffer; - this.headerRecieved = false; - this.audioType = ""; - this.errored = false; - this.buffered = 0; - this.sampleRate = 0; - this.bufferedSamples = 0; - this.duration = 0; - this.loaded = false; - if (this.playing) this.output.stop(); - this.playing = false; - this.lastStartTime = this.ac.currentTime; - } - - end(done, reason) { - if (done && this.buffer) { - this.loaded = true; - delete this.controller; - this.mergeBuffers(); - - this.duration = this.buffered; - this.audioData = []; - this.decoder?.destroy(); - delete this.decoder; - delete this.parser; - } else { - console.warn( - "Unexpected end when loading stream audio, reason: '" + - (reason ?? "") + - "'" - ); - } - } - - playAudio(when) { - if (!this.buffer) return; - this.lastStartTime = this.ac.currentTime; - if (this.playing) this.output.stop(); - if (this.route.status !== AudioStatus.Playing) { - this.route.status = AudioStatus.Playing; - } - this.createSourceNode(this.buffer); - this.output.start(0, when); - this.playing = true; - - this.output.addEventListener("ended", () => { - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.status = AudioStatus.Stoped; - } - if (this.loop && !this.output.loop) this.play(0); - }); - } - /** - * 开始播放这个音频源 - */ - play(when) { - if (this.playing || this.errored) return; - if (this.loaded && this.buffer) { - this.playing = true; - this.playAudio(when); - } else { - this.controller?.start(); - } - } - - createSourceNode(buffer) { - if (!this.target) return; - const node = this.ac.createBufferSource(); - node.buffer = buffer; - if (this.playing) this.output.stop(); - this.playing = false; - this.output = node; - node.connect(this.target.input); - node.loop = this.loop; - } - /** - * 停止播放这个音频源 - * @returns 音频暂停的时刻 number - */ - stop() { - if (this.playing) this.output.stop(); - this.playing = false; - return this.ac.currentTime - this.lastStartTime; - } - /** - * 连接到音频路由图上,每次调用播放的时候都会执行一次 - * @param target 连接至的目标 IAudioInput - */ - connect(target) { - this.target = target; - } - /** - * 设置是否循环播放 - * @param loop 是否循环 boolean) - */ - setLoop(loop) { - this.loop = loop; - } - } - class AudioElementSource { - route; - constructor(context) { - const audio = new Audio(); - audio.preload = "none"; - this.output = context.createMediaElementSource(audio); - this.audio = audio; - this.ac = context; - audio.addEventListener("play", () => { - this.playing = true; - if (this.route.status !== AudioStatus.Playing) { - this.route.status = AudioStatus.Playing; - } - }); - audio.addEventListener("ended", () => { - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.status = AudioStatus.Stoped; - } - }); - } - get duration() { - return this.audio.duration; - } - get currentTime() { - return this.audio.currentTime; - } - /** - * 设置音频源的路径 - * @param url 音频路径 - */ - setSource(url) { - this.audio.src = url; - } - - play(when = 0) { - if (this.playing) return; - this.audio.currentTime = when; - this.audio.play(); - } - - stop() { - this.audio.pause(); - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.status = AudioStatus.Stoped; - } - return this.audio.currentTime; - } - - connect(target) { - this.output.connect(target.input); - } - - setLoop(loop) { - this.audio.loop = loop; - } - } - class AudioBufferSource { - route; - constructor(context) { - this.output = context.createBufferSource(); - /** 是否循环 */ - this.loop = false; - /** 上一次播放是从何时开始的 */ - this.lastStartWhen = 0; - /** 播放开始时刻 */ - this.lastStartTime = 0; - this.duration = 0; - this.ac = context; - } - get currentTime() { - return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; - } - - /** - * 设置音频源数据 - * @param buffer 音频源,可以是未解析的 ArrayBuffer,也可以是已解析的 AudioBuffer - */ - async setBuffer(buffer) { - if (buffer instanceof ArrayBuffer) { - this.buffer = await this.ac.decodeAudioData(buffer); - } else { - this.buffer = buffer; - } - this.duration = this.buffer.duration; - } - - play(when) { - if (this.playing || !this.buffer) return; - this.playing = true; - this.lastStartTime = this.ac.currentTime; - if (this.route.status !== AudioStatus.Playing) { - this.route.status = AudioStatus.Playing; - } - this.createSourceNode(this.buffer); - this.output.start(0, when); - this.output.addEventListener("ended", () => { - this.playing = false; - if (this.route.status === AudioStatus.Playing) { - this.route.status = AudioStatus.Stoped; - } - if (this.loop && !this.output.loop) this.play(0); - }); - } - - createSourceNode(buffer) { - if (!this.target) return; - const node = this.ac.createBufferSource(); - node.buffer = buffer; - this.output = node; - node.connect(this.target.input); - node.loop = this.loop; - } - - stop() { - this.output.stop(); - return this.ac.currentTime - this.lastStartTime; - } - - connect(target) { - this.target = target; - } - - setLoop(loop) { - this.loop = loop; - } - } - class AudioPlayer { - constructor() { - /** 音频播放上下文 */ - this.ac = new AudioContext(); - /** 音量节点 */ - this.gain = this.ac.createGain(); - this.gain.connect(this.ac.destination); - this.audioRoutes = new Map(); - } - /** - * 解码音频数据 - * @param data 音频数据 - */ - decodeAudioData(data) { - return AudioDecoder.decodeAudioData(data, this); - } - /** - * 设置音量 - * @param volume 音量 - */ - setVolume(volume) { - this.gain.gain.value = volume; - } - - /** - * 获取音量 - */ - getVolume() { - return this.gain.gain.value; - } - - /** - * 创建一个音频源 - * @param Source 音频源类 - */ - createSource(Source) { - return new Source(this.ac); - } - - /** - * 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况 - */ - createStreamSource() { - return new AudioStreamSource(this.ac); - } - - /** - * 创建一个通过 audio 元素播放的音频源 - */ - createElementSource() { - return new AudioElementSource(this.ac); - } - - /** - * 创建一个通过 AudioBuffer 播放的音频源 - */ - createBufferSource() { - return new AudioBufferSource(this.ac); - } - - /** - * 获取音频目的地 - */ - getDestination() { - return this.gain; - } - - /** - * 创建一个音频效果器 - * @param Effect 效果器类 - */ - createEffect(Effect) { - return new Effect(this.ac); - } - - /** - * 创建一个修改音量的效果器 - * ```txt - * |----------| - * Input ----> | GainNode | ----> Output - * |----------| - * ``` - */ - createVolumeEffect() { - return new VolumeEffect(this.ac); - } - - /** - * 创建一个立体声效果器 - * ```txt - * |------------| - * Input ----> | PannerNode | ----> Output - * |------------| - * ``` - */ - createStereoEffect() { - return new StereoEffect(this.ac); - } - - /** - * 创建一个修改单个声道音量的效果器 - * ```txt - * |----------| - * -> | GainNode | \ - * |--------------| / |----------| -> |------------| - * Input ----> | SplitterNode | ...... | MergerNode | ----> Output - * |--------------| \ |----------| -> |------------| - * -> | GainNode | / - * |----------| - * ``` - */ - createChannelVolumeEffect() { - return new ChannelVolumeEffect(this.ac); - } - - /** - * 创建一个延迟效果器 - * |-----------| - * Input ----> | DelayNode | ----> Output - * |-----------| - */ - createDelay() { - return new DelayEffect(this.ac); - } - - /** - * 创建一个回声效果器 - * ```txt - * |----------| - * Input ----> | GainNode | ----> Output - * ^ |----------| | - * | | - * | |------------| ↓ - * |-- | Delay Node | <-- - * |------------| - * ``` - */ - createEchoEffect() { - return new EchoEffect(this.ac); - } - - /** - * 创建一个音频播放路由 - * @param source 音频源 - */ - createRoute(source) { - return new AudioRoute(source, this); - } - - /** - * 添加一个音频播放路由,可以直接被播放 - * @param id 这个音频播放路由的名称 - * @param route 音频播放路由对象 - */ - addRoute(id, route) { - if (!this.audioRoutes) this.audioRoutes = new Map(); - if (this.audioRoutes.has(id)) { - console.warn( - "Audio route with id of '" + - id + - "' has already existed. New route will override old route." - ); - } - this.audioRoutes.set(id, route); - } - - /** - * 根据名称获取音频播放路由对象 - * @param id 音频播放路由的名称 - */ - getRoute(id) { - return this.audioRoutes.get(id); - } - /** - * 移除一个音频播放路由 - * @param id 要移除的播放路由的名称 - */ - removeRoute(id) { - this.audioRoutes.delete(id); - } - /** - * 播放音频 - * @param id 音频名称 - * @param when 从音频的哪个位置开始播放,单位秒 - */ - play(id, when) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot play audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - - route.play(when); - } - - /** - * 暂停音频播放 - * @param id 音频名称 - * @returns 当音乐真正停止时兑现 - */ - pause(id) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot pause audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - return route.pause(); - } - - /** - * 停止音频播放 - * @param id 音频名称 - * @returns 当音乐真正停止时兑现 - */ - stop(id) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot stop audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - return route.stop(); - } - - /** - * 继续音频播放 - * @param id 音频名称 - */ - resume(id) { - const route = this.getRoute(id); - if (!route) { - console.warn( - "Cannot pause audio route '" + - id + - "', since there is not added route named it." - ); - return; - } - route.resume(); - } - - /** - * 设置听者位置,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 位置x坐标 - * @param y 位置y坐标 - * @param z 位置z坐标 - */ - setListenerPosition(x, y, z) { - const listener = this.ac.listener; - listener.positionX.value = x; - listener.positionY.value = y; - listener.positionZ.value = z; - } - - /** - * 设置听者朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 朝向x坐标 - * @param y 朝向y坐标 - * @param z 朝向z坐标 - */ - setListenerOrientation(x, y, z) { - const listener = this.ac.listener; - listener.forwardX.value = x; - listener.forwardY.value = y; - listener.forwardZ.value = z; - } - - /** - * 设置听者头顶朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 - * @param x 头顶朝向x坐标 - * @param y 头顶朝向y坐标 - * @param z 头顶朝向z坐标 - */ - setListenerUp(x, y, z) { - const listener = this.ac.listener; - listener.upX.value = x; - listener.upY.value = y; - listener.upZ.value = z; - } - } - class AudioRoute { - constructor(source, player) { - source.route = this; - this.output = source.output; - - /** 效果器路由图 */ - this.effectRoute = []; - - /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ - this.endTime = 0; - /** 暂停时播放了多长时间 */ - this.pauseCurrentTime = 0; - /** 当前播放状态 */ - this.player = player; - this.status = AudioStatus.Stoped; - - this.shouldStop = false; - /** - * 每次暂停或停止时自增,用于判断当前正在处理的情况。 - * 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作 - */ - this.stopIdentifier = 0; - /** 暂停时刻 */ - this.pauseTime = 0; - this.source = source; - this.source.player = player; - } - /** 音频时长,单位秒 */ - get duration() { - return this.source.duration; - } - /** 当前播放了多长时间,单位秒 */ - get currentTime() { - if (this.status === AudioStatus.Paused) { - return this.pauseCurrentTime; - } else { - return this.source.currentTime; - } - } - set currentTime(time) { - this.source.stop(); - this.source.play(time); - } - /** - * 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。 - * @param time 暂停或停止时,经过多长时间之后才会结束音频的播放 - */ - setEndTime(time) { - this.endTime = time; - } - - /** - * 当音频播放时执行的函数,可以用于音频淡入效果 - * @param fn 音频开始播放时执行的函数 - */ - onStart(fn) { - this.audioStartHook = fn; - } - - /** - * 当音频暂停或停止时执行的函数,可以用于音频淡出效果 - * @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。 - * 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象 - */ - onEnd(fn) { - this.audioEndHook = fn; - } - - /** - * 开始播放这个音频 - * @param when 从音频的什么时候开始播放,单位秒 - */ - async play(when = 0) { - if (this.status === AudioStatus.Playing) return; - this.link(); - await this.player.ac.resume(); - if (this.effectRoute.length > 0) { - const first = this.effectRoute[0]; - this.source.connect(first); - const last = this.effectRoute.at(-1); - last.connect({ input: this.player.getDestination() }); - } else { - this.source.connect({ input: this.player.getDestination() }); - } - this.source.play(when); - this.status = AudioStatus.Playing; - this.pauseTime = 0; - this.audioStartHook?.(this); - this.startAllEffect(); - if (this.status !== AudioStatus.Playing) { - this.status = AudioStatus.Playing; - } - } - - /** - * 暂停音频播放 - */ - async pause() { - if (this.status !== AudioStatus.Playing) return; - this.status = AudioStatus.Pausing; - this.stopIdentifier++; - const identifier = this.stopIdentifier; - if (this.audioEndHook) { - this.audioEndHook(this.endTime, this); - await sleep(this.endTime); - } - if ( - this.status !== AudioStatus.Pausing || - this.stopIdentifier !== identifier - ) { - return; - } - this.pauseCurrentTime = this.source.currentTime; - const time = this.source.stop(); - this.pauseTime = time; - if (this.shouldStop) { - this.status = AudioStatus.Stoped; - this.endAllEffect(); - - this.shouldStop = false; - } else { - this.status = AudioStatus.Paused; - this.endAllEffect(); - } - this.endAllEffect(); - } - - /** - * 继续音频播放 - */ - resume() { - if (this.status === AudioStatus.Playing) return; - if ( - this.status === AudioStatus.Pausing || - this.status === AudioStatus.Stoping - ) { - this.audioStartHook?.(this); - - return; - } - if (this.status === AudioStatus.Paused) { - this.play(this.pauseTime); - } else { - this.play(0); - } - this.status = AudioStatus.Playing; - this.pauseTime = 0; - this.audioStartHook?.(this); - this.startAllEffect(); - } - - /** - * 停止音频播放 - */ - async stop() { - if (this.status !== AudioStatus.Playing) { - if (this.status === AudioStatus.Pausing) { - this.shouldStop = true; - } - return; - } - this.status = AudioStatus.Stoping; - this.stopIdentifier++; - const identifier = this.stopIdentifier; - if (this.audioEndHook) { - this.audioEndHook(this.endTime, this); - await sleep(this.endTime); - } - if ( - this.status !== AudioStatus.Stoping || - this.stopIdentifier !== identifier - ) { - return; - } - this.source.stop(); - this.status = AudioStatus.Stoped; - this.pauseTime = 0; - this.endAllEffect(); - } - - /** - * 添加效果器 - * @param effect 要添加的效果,可以是数组,表示一次添加多个 - * @param index 从哪个位置开始添加,如果大于数组长度,那么加到末尾,如果小于0,那么将会从后面往前数。默认添加到末尾 - */ - addEffect(effect, index) { - if (isNil(index)) { - if (effect instanceof Array) { - this.effectRoute.push(...effect); - } else { - this.effectRoute.push(effect); - } - } else { - if (effect instanceof Array) { - this.effectRoute.splice(index, 0, ...effect); - } else { - this.effectRoute.splice(index, 0, effect); - } - } - this.setOutput(); - if (this.source.playing) this.link(); - } - - /** - * 移除一个效果器 - * @param effect 要移除的效果 - */ - removeEffect(effect) { - const index = this.effectRoute.indexOf(effect); - if (index === -1) return; - this.effectRoute.splice(index, 1); - effect.disconnect(); - this.setOutput(); - if (this.source.playing) this.link(); - } - - setOutput() { - const effect = this.effectRoute.at(-1); - if (!effect) this.output = this.source.output; - else this.output = effect.output; - } - - /** - * 连接音频路由图 - */ - link() { - this.effectRoute.forEach((v) => v.disconnect()); - this.effectRoute.forEach((v, i) => { - const next = this.effectRoute[i + 1]; - if (next) { - v.connect(next); - } - }); - } - - startAllEffect() { - this.effectRoute.forEach((v) => v.start()); - } - - endAllEffect() { - this.effectRoute.forEach((v) => v.end()); - } - } - - const audioPlayer = new AudioPlayer(); - - class BgmController { - constructor(player) { - this.mainGain = player.createVolumeEffect(); - this.player = player; - /** bgm音频名称的前缀 */ - this.prefix = "bgms."; - /** 每个 bgm 的音量控制器 */ - this.gain = new Map(); - - /** 正在播放的 bgm */ - this.playingBgm = ""; - /** 是否正在播放 */ - this.playing = false; - - /** 是否已经启用 */ - this.enabled = true; - /** 是否屏蔽所有的音乐切换 */ - this.blocking = false; - /** 渐变时长 */ - this.transitionTime = 2000; - } - - /** - * 设置音频渐变时长 - * @param time 渐变时长 - */ - setTransitionTime(time) { - this.transitionTime = time; - for (const [, value] of this.gain) { - value.transition.time(time); - } - } - - /** - * 屏蔽音乐切换 - */ - blockChange() { - this.blocking = true; - } - - /** - * 取消屏蔽音乐切换 - */ - unblockChange() { - this.blocking = false; - } - - /** - * 设置总音量大小 - * @param volume 音量大小 - */ - setVolume(volume) { - this.mainGain.setVolume(volume); - this._volume = volume; - } - /** - * 获取总音量大小 - */ - getVolume() { - return this.mainGain.getVolume(); - } - /** - * 设置是否启用 - * @param enabled 是否启用 - */ - setEnabled(enabled) { - if (enabled) this.resume(); - else this.stop(); - this.enabled = enabled; - } - - /** - * 设置 bgm 音频名称的前缀 - */ - setPrefix(prefix) { - this.prefix = prefix; - } - - getId(name) { - return `${this.prefix}${name}`; - } - - /** - * 根据 bgm 名称获取其 AudioRoute 实例 - * @param id 音频名称 - */ - get(id) { - return this.player.getRoute(this.getId(id)); - } - - /** - * 添加一个 bgm - * @param id 要添加的 bgm 的名称 - * @param url 指定 bgm 的加载地址 - */ - addBgm(id, url = `project/bgms/${id}`) { - const type = guessTypeByExt(id); - if (!type) { - console.warn( - "Unknown audio extension name: '" + - id.split(".").slice(0, -1).join(".") + - "'" - ); - return; - } - const gain = this.player.createVolumeEffect(); - if (isAudioSupport(type)) { - const source = audioPlayer.createElementSource(); - source.setSource(url); - source.setLoop(true); - const route = new AudioRoute(source, audioPlayer); - route.addEffect([gain, this.mainGain]); - audioPlayer.addRoute(this.getId(id), route); - this.setTransition(id, route, gain); - } else { - const source = audioPlayer.createStreamSource(); - const stream = new StreamLoader(url); - stream.pipe(source); - source.setLoop(true); - const route = new AudioRoute(source, audioPlayer); - route.addEffect([gain, this.mainGain]); - audioPlayer.addRoute(this.getId(id), route); - this.setTransition(id, route, gain); - } - } - - /** - * 移除一个 bgm - * @param id 要移除的 bgm 的名称 - */ - removeBgm(id) { - this.player.removeRoute(this.getId(id)); - const gain = this.gain.get(id); - gain?.transition.ticker.destroy(); - this.gain.delete(id); - } - - setTransition(id, route, gain) { - const transition = new Transition(); - transition - .time(this.transitionTime) - .mode(linear()) - .transition("volume", 0); - - const tick = () => { - gain.setVolume(transition.value.volume); - }; - - /** - * @param expect 在结束时应该是正在播放还是停止 - */ - const setTick = async (expect) => { - transition.ticker.remove(tick); - transition.ticker.add(tick); - const identifier = route.stopIdentifier; - await sleep(this.transitionTime + 500); - if (route.status === expect && identifier === route.stopIdentifier) { - transition.ticker.remove(tick); - if (route.status === AudioStatus.Playing) { - gain.setVolume(1); - } else { - gain.setVolume(0); - } - } - }; - - route.onStart(async () => { - transition.transition("volume", 1); - setTick(AudioStatus.Playing); - }); - route.onEnd(() => { - transition.transition("volume", 0); - setTick(AudioStatus.Paused); - }); - route.setEndTime(this.transitionTime); - - this.gain.set(id, { effect: gain, transition }); - } - - /** - * 播放一个 bgm - * @param id 要播放的 bgm 名称 - */ - play(id, when) { - if (this.blocking) return; - if (id !== this.playingBgm && this.playingBgm) { - this.player.pause(this.getId(this.playingBgm)); - } - this.playingBgm = id; - if (!this.enabled) return; - this.player.play(this.getId(id), when); - this.playing = true; - } - - /** - * 继续当前的 bgm - */ - resume() { - if (this.blocking || !this.enabled || this.playing) return; - if (this.playingBgm) { - this.player.resume(this.getId(this.playingBgm)); - } - this.playing = true; - } - - /** - * 暂停当前的 bgm - */ - pause() { - if (this.blocking || !this.enabled) return; - if (this.playingBgm) { - this.player.pause(this.getId(this.playingBgm)); - } - this.playing = false; - } - - /** - * 停止当前的 bgm - */ - stop() { - if (this.blocking || !this.enabled) return; - if (this.playingBgm) { - this.player.stop(this.getId(this.playingBgm)); - } - this.playing = false; - } - } - const bgmController = new BgmController(audioPlayer); - - class SoundPlayer { - constructor(player) { - /** 每个音效的唯一标识符 */ - this.num = 0; - this.enabled = true; - this.gain = player.createVolumeEffect(); - /** 每个音效的数据 */ - this.buffer = new Map(); - /** 所有正在播放的音乐 */ - this.playing = new Set(); - this.player = player; - } - /** - * 设置是否启用音效 - * @param enabled 是否启用音效 - */ - setEnabled(enabled) { - if (!enabled) this.stopAllSounds(); - this.enabled = enabled; - } - - /** - * 设置音量大小 - * @param volume 音量大小 - */ - setVolume(volume) { - this.gain.setVolume(volume); - } - /** - * 获取音量大小 - */ - getVolume() { - return this.gain.getVolume(); - } - /** - * 添加一个音效 - * @param id 音效名称 - * @param data 音效的Uint8Array数据 - */ - async add(id, data) { - const buffer = await this.player.decodeAudioData(data); - if (!buffer) { - console.warn( - "Cannot decode sound '" + - id + - "', since audio file may not supported by 2.b." - ); - return; - } - this.buffer.set(id, buffer); - } - - /** - * 播放一个音效 - * @param id 音效名称 - * @param position 音频位置,[0, 0, 0]表示正中心,x轴指向水平向右,y轴指向水平向上,z轴指向竖直向上 - * @param orientation 音频朝向,[0, 1, 0]表示朝向前方 - */ - play(id, position = [0, 0, 0], orientation = [1, 0, 0]) { - if (!this.enabled) return -1; - const buffer = this.buffer.get(id); - if (!buffer) { - console.warn( - "Cannot play sound '" + - id + - "', since there is no added data named it." - ); - return -1; - } - const soundNum = this.num++; - - const source = this.player.createBufferSource(); - source.setBuffer(buffer); - const route = this.player.createRoute(source); - const stereo = this.player.createStereoEffect(); - stereo.setPosition(position[0], position[1], position[2]); - stereo.setOrientation(orientation[0], orientation[1], orientation[2]); - route.addEffect([stereo, this.gain]); - this.player.addRoute(`sounds.${soundNum}`, route); - route.play(); - source.output.addEventListener("ended", () => { - this.playing.delete(soundNum); - }); - this.playing.add(soundNum); - return soundNum; - } - - /** - * 停止一个音效 - * @param num 音效的唯一 id - */ - stop(num) { - const id = `sounds.${num}`; - const route = this.player.getRoute(id); - if (route) { - route.stop(); - this.player.removeRoute(id); - this.playing.delete(num); - } - } - - /** - * 停止播放所有音效 - */ - stopAllSounds() { - this.playing.forEach((v) => { - const id = `sounds.${v}`; - const route = this.player.getRoute(id); - if (route) { - route.stop(); - this.player.removeRoute(id); - } - }); - this.playing.clear(); - } - } - const soundPlayer = new SoundPlayer(audioPlayer); - - function loadAllBgm() { - const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; - for (const bgm of data.main.bgms) { - bgmController.addBgm(bgm); - } - } - loadAllBgm(); - AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder); - AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder); - - core.plugin.audioSystem = { - AudioType, - AudioDecoder, - AudioStatus, - checkAudioType, - isAudioSupport, - audioPlayer, - soundPlayer, - bgmController, - guessTypeByExt, - BgmController, - SoundPlayer, - EchoEffect, - DelayEffect, - ChannelVolumeEffect, - VolumeEffect, - StereoEffect, - AudioEffect, - AudioPlayer, - AudioRoute, - AudioStreamSource, - AudioElementSource, - AudioBufferSource, - loadAllBgm, - StreamLoader, - }; - //bgm相关复写 - control.prototype.playBgm = (bgm, when) => { - bgm = core.getMappedName(bgm); - bgmController.play(bgm, when); - core.setMusicBtn(); - }; - control.prototype.pauseBgm = () => { - bgmController.pause(); - core.setMusicBtn(); - }; - - control.prototype.resumeBgm = function () { - bgmController.resume(); - core.setMusicBtn(); - }; - control.prototype.checkBgm = function () { - core.playBgm(bgmController.playingBgm || main.startBgm); - }; - control.prototype.triggerBgm = function () { - core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; - if (bgmController.playing) bgmController.pause(); - else bgmController.resume(); - core.setMusicBtn(); - core.setLocalStorage("bgmStatus", core.musicStatus.bgmStatus); - }; - //sound相关复写 - control.prototype.playSound = function ( - sound, - _pitch, - callback, - position, - orientation - ) { - if (main.mode != "play" || !core.musicStatus.soundStatus) return; - const name = core.getMappedName(sound); - const num = soundPlayer.play(name, position, orientation); - const route = audioPlayer.getRoute(`sounds.${num}`); - if (!route) { - callback?.(); - return -1; - } else { - sleep(route.duration * 1000).then(() => callback?.()); - return num; - } - }; - control.prototype.stopSound = function (id) { - if (isNil(id)) { - soundPlayer.stopAllSounds(); - } else { - soundPlayer.stop(id); - } - }; - control.prototype.getPlayingSounds = function () { - return [...soundPlayer.playing]; - }; - //sound加载复写 - loader.prototype._loadOneSound_decodeData = function (name, data) { - if (data instanceof Blob) { - var blobReader = new zip.BlobReader(data); - blobReader.init(function () { - blobReader.readUint8Array(0, blobReader.size, function (uint8) { - //core.loader._loadOneSound_decodeData(name, uint8.buffer); - soundPlayer.add(name, uint8); - }); - }); - return; - } - if (data instanceof ArrayBuffer) { - const uint8 = new Uint8Array(data); - soundPlayer.add(name, uint8); - } - }; - //音量控制复写 - soundPlayer.setVolume( - core.musicStatus.userVolume * core.musicStatus.designVolume - ); - bgmController.setVolume( - core.musicStatus.userVolume * core.musicStatus.designVolume - ); - actions.prototype._clickSwitchs_sounds_userVolume = function (delta) { - var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); - if (value == 0 && delta < 0) return; - core.musicStatus.userVolume = core.clamp( - Math.pow(value + delta, 2) / 100, - 0, - 1 - ); - //audioContext 音效 不受designVolume 影响 - if (core.musicStatus.gainNode != null) - core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; - soundPlayer.setVolume( - core.musicStatus.userVolume * core.musicStatus.designVolume - ); - bgmController.setVolume( - core.musicStatus.userVolume * core.musicStatus.designVolume - ); - core.setLocalStorage("userVolume", core.musicStatus.userVolume); - core.playSound("确定"); - core.ui._drawSwitchs_sounds(); - }; - }, + // 将__enable置为false将关闭插件 + let __enable = true; + if (!__enable || main.mode === "editor") return; + const { OggOpusDecoderWebWorker } = window["ogg-opus-decoder"]; + const { OggVorbisDecoderWebWorker } = window["ogg-vorbis-decoder"]; + const { CodecParser } = window.CodecParser; + const { Transition, linear } = core.plugin.animate; + + const audio = new Audio(); + const AudioStatus = { + Playing: 0, + Pausing: 1, + Paused: 2, + Stoping: 3, + Stoped: 4, + }; + const supportMap = new Map(); + const AudioType = { + Mp3: "audio/mpeg", + Wav: 'audio/wav; codecs="1"', + Flac: "audio/flac", + Opus: 'audio/ogg; codecs="opus"', + Ogg: 'audio/ogg; codecs="vorbis"', + Aac: "audio/aac", + }; + /** + * 检查一种音频类型是否能被播放 + * @param type 音频类型 AudioType + */ + function isAudioSupport(type) { + if (supportMap.has(type)) return supportMap.get(type); + else { + const support = audio.canPlayType(type); + const canPlay = support === "maybe" || support === "probably"; + supportMap.set(type, canPlay); + return canPlay; + } + } + + const typeMap = new Map([ + ["ogg", AudioType.Ogg], + ["mp3", AudioType.Mp3], + ["wav", AudioType.Wav], + ["flac", AudioType.Flac], + ["opus", AudioType.Opus], + ["aac", AudioType.Aac], + ]); + + /** + * 根据文件名拓展猜测其类型 + * @param file 文件名 string + */ + function guessTypeByExt(file) { + const ext = /\.[a-zA-Z\d]+$/.exec(file); + if (!ext?.[0]) return ""; + const type = ext[0].slice(1); + return typeMap.get(type.toLocaleLowerCase()) ?? ""; + } + + isAudioSupport(AudioType.Ogg); + isAudioSupport(AudioType.Mp3); + isAudioSupport(AudioType.Wav); + isAudioSupport(AudioType.Flac); + isAudioSupport(AudioType.Opus); + isAudioSupport(AudioType.Aac); + + function isNil(value) { + return value === void 0 || value === null; + } + + function sleep(time) { + return new Promise((res) => setTimeout(res, time)); + } + class AudioEffect { + constructor(ac) {} + /** + * 连接至其他效果器 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + connect(target, output, input) { + this.output.connect(target.input, output, input); + } + + /** + * 与其他效果器取消连接 + * @param target 目标输入 IAudioInput + * @param output 当前效果器输出通道 Number + * @param input 目标效果器的输入通道 Number + */ + disconnect(target, output, input) { + if (!target) { + if (!isNil(output)) { + this.output.disconnect(output); + } else { + this.output.disconnect(); + } + } else { + if (!isNil(output)) { + if (!isNil(input)) { + this.output.disconnect(target.input, output, input); + } else { + this.output.disconnect(target.input, output); + } + } else { + this.output.disconnect(target.input); + } + } + } + } + + class StereoEffect extends AudioEffect { + constructor(ac) { + super(ac); + const panner = ac.createPanner(); + this.input = panner; + this.output = panner; + } + + /** + * 设置音频朝向,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 Number + * @param y 朝向y坐标 Number + * @param z 朝向z坐标 Number + */ + setOrientation(x, y, z) { + this.output.orientationX.value = x; + this.output.orientationY.value = y; + this.output.orientationZ.value = z; + } + /** + * 设置音频位置,x正方形水平向右,y正方形垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 Number + * @param y 位置y坐标 Number + * @param z 位置z坐标 Number + */ + setPosition(x, y, z) { + this.output.positionX.value = x; + this.output.positionY.value = y; + this.output.positionZ.value = z; + } + end() {} + + start() {} + } + class VolumeEffect extends AudioEffect { + constructor(ac) { + super(ac); + const gain = ac.createGain(); + this.input = gain; + this.output = gain; + } + + /** + * 设置音量大小 + * @param volume 音量大小 Number + */ + setVolume(volume) { + this.output.gain.value = volume; + } + + /** + * 获取音量大小 Number + */ + getVolume() { + return this.output.gain.value; + } + + end() {} + + start() {} + } + class ChannelVolumeEffect extends AudioEffect { + /** 所有的音量控制节点 */ + + constructor(ac) { + super(ac); + /** 所有的音量控制节点 */ + this.gain = []; + const splitter = ac.createChannelSplitter(); + const merger = ac.createChannelMerger(); + this.output = merger; + this.input = splitter; + for (let i = 0; i < 6; i++) { + const gain = ac.createGain(); + splitter.connect(gain, i); + gain.connect(merger, 0, i); + this.gain.push(gain); + } + } + + /** + * 设置某个声道的音量大小 + * @param channel 要设置的声道,可填0-5 Number + * @param volume 这个声道的音量大小 Number + */ + setVolume(channel, volume) { + if (!this.gain[channel]) return; + this.gain[channel].gain.value = volume; + } + + /** + * 获取某个声道的音量大小,可填0-5 + * @param channel 要获取的声道 Number + */ + getVolume(channel) { + if (!this.gain[channel]) return 0; + return this.gain[channel].gain.value; + } + + end() {} + + start() {} + } + class DelayEffect extends AudioEffect { + constructor(ac) { + super(ac); + + const delay = ac.createDelay(); + this.input = delay; + this.output = delay; + } + + /** + * 设置延迟时长 + * @param delay 延迟时长,单位秒 Number + */ + setDelay(delay) { + this.output.delayTime.value = delay; + } + + /** + * 获取延迟时长 + */ + getDelay() { + return this.output.delayTime.value; + } + + end() {} + + start() {} + } + class EchoEffect extends AudioEffect { + constructor(ac) { + super(ac); + /** 当前增益 */ + this.gain = 0.5; + /** 是否正在播放 */ + this.playing = false; + const delay = ac.createDelay(); + const gain = ac.createGain(); + gain.gain.value = 0.5; + delay.delayTime.value = 0.05; + delay.connect(gain); + gain.connect(delay); + /** 延迟节点 */ + this.delay = delay; + /** 反馈增益节点 */ + this.gainNode = gain; + + this.input = gain; + this.output = gain; + } + + /** + * 设置回声反馈增益大小 + * @param gain 增益大小,范围 0-1,大于等于1的视为0.5,小于0的视为0 Number + */ + setFeedbackGain(gain) { + const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain; + this.gain = resolved; + if (this.playing) this.gainNode.gain.value = resolved; + } + + /** + * 设置回声间隔时长 + * @param delay 回声时长,范围 0.01-Infinity,小于0.01的视为0.01 Number + */ + setEchoDelay(delay) { + const resolved = delay < 0.01 ? 0.01 : delay; + this.delay.delayTime.value = resolved; + } + + /** + * 获取反馈节点增益 + */ + getFeedbackGain() { + return this.gain; + } + + /** + * 获取回声间隔时长 + */ + getEchoDelay() { + return this.delay.delayTime.value; + } + + end() { + this.playing = false; + const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10; + sleep(this.delay.delayTime.value * echoTime).then(() => { + if (!this.playing) this.gainNode.gain.value = 0; + }); + } + + start() { + this.playing = true; + this.gainNode.gain.value = this.gain; + } + } + + class StreamLoader { + constructor(url) { + /** 传输目标 Set*/ + this.target = new Set(); + this.loading = false; + } + + /** + * 将加载流传递给字节流读取对象 + * @param reader 字节流读取对象 IStreamReader + */ + pipe(reader) { + if (this.loading) { + console.warn( + "Cannot pipe new StreamReader object when stream is loading." + ); + return; + } + this.target.add(reader); + reader.piped(this); + return this; + } + + async start() { + if (this.loading) return; + this.loading = true; + const response = await window.fetch(this.url); + const stream = response.body; + if (!stream) { + console.error("Cannot get reader when fetching '" + this.url + "'."); + return; + } + // 获取读取器 + this.stream = stream; + const reader = response.body?.getReader(); + const targets = [...this.target]; + + await Promise.all(targets.map((v) => v.start(stream, this, response))); + if (reader && reader.read) { + // 开始流传输 + while (true) { + const { value, done } = await reader.read(); + await Promise.all( + targets.map((v) => v.pump(value, done, response)) + ); + if (done) break; + } + } else { + // 如果不支持流传输 + const buffer = await response.arrayBuffer(); + const data = new Uint8Array(buffer); + await Promise.all(targets.map((v) => v.pump(data, true, response))); + } + + this.loading = false; + targets.forEach((v) => v.end(true)); + + // + } + + cancel(reason) { + if (!this.stream) return; + this.stream.cancel(reason); + this.loading = false; + this.target.forEach((v) => v.end(false, reason)); + } + } + const fileSignatures = [ + [AudioType.Mp3, [0x49, 0x44, 0x33]], + [AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]], + [AudioType.Wav, [0x52, 0x49, 0x46, 0x46]], + [AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]], + [AudioType.Aac, [0xff, 0xf1]], + [AudioType.Aac, [0xff, 0xf9]], + ]; + const oggHeaders = [ + [AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]], + ]; + + function checkAudioType(data) { + let audioType = ""; + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + for (const [type, value] of fileSignatures) { + if (value.every((v, i) => toCheck[i] === v)) { + audioType = type; + break; + } + } + if (audioType === AudioType.Ogg) { + // 如果是ogg的话,进一步判断是不是opus + for (const [key, value] of oggHeaders) { + const has = toCheck.some((_, i) => { + return value.every((v, ii) => toCheck[i + ii] === v); + }); + if (has) { + audioType = key; + break; + } + } + } + + return audioType; + } + class AudioDecoder { + /** + * 注册一个解码器 + * @param type 要注册的解码器允许解码的类型 + * @param decoder 解码器对象 + */ + static registerDecoder(type, decoder) { + if (!this.decoderMap) this.decoderMap = new Map(); + if (this.decoderMap.has(type)) { + console.warn( + "Audio stream decoder for audio type '" + + type + + "' has already existed." + ); + return; + } + + this.decoderMap.set(type, decoder); + } + + /** + * 解码音频数据 + * @param data 音频文件数据 + * @param player AudioPlayer实例 + */ + static async decodeAudioData(data, player) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + const type = checkAudioType(data); + if (type === "") { + console.error( + "Unknown audio type. Header: '" + [...toCheck] + .map((v) => v.toString().padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return null; + } + if (isAudioSupport(type)) { + if (data.buffer instanceof ArrayBuffer) { + return player.ac.decodeAudioData(data.buffer); + } else { + return null; + } + } else { + const Decoder = this.decoderMap.get(type); + if (!Decoder) { + return null; + } else { + const decoder = new Decoder(); + await decoder.create(); + const decodedData = await decoder.decode(data); + if (!decodedData) return null; + const buffer = player.ac.createBuffer( + decodedData.channelData.length, + decodedData.channelData[0].length, + decodedData.sampleRate + ); + decodedData.channelData.forEach((v, i) => { + buffer.copyToChannel(v, i); + }); + decoder.destroy(); + return buffer; + } + } + } + } + + class VorbisDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggVorbisDecoderWebWorker(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 解码整个文件 + * @param data 文件数据 + */ + async decodeAll(data) { + return this.decoder?.decodeFile(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return this.decoder?.flush(); + } + } + + class OpusDecoder { + /** + * 创建音频解码器 + */ + async create() { + this.decoder = new OggOpusDecoderWebWorker(); + await this.decoder.ready; + } + /** + * 摧毁这个解码器 + */ + destroy() { + this.decoder?.free(); + } + /** + * 解码流数据 + * @param data 流数据 + */ + async decode(data) { + return this.decoder?.decode(data); + } + /** + * 解码整个文件 + * @param data 文件数据 + */ + async decodeAll(data) { + return this.decoder?.decodeFile(data); + } + /** + * 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用 + */ + async flush() { + return await this.decoder?.flush(); + } + } + const mimeTypeMap = { + [AudioType.Aac]: "audio/aac", + [AudioType.Flac]: "audio/flac", + [AudioType.Mp3]: "audio/mpeg", + [AudioType.Ogg]: "application/ogg", + [AudioType.Opus]: "application/ogg", + [AudioType.Wav]: "application/ogg", + }; + + function isOggPage(data) { + return !isNil(data.isFirstPage); + } + class AudioStreamSource { + + constructor(context) { + this.output = context.createBufferSource(); + /** 是否已经完全加载完毕 */ + this.loaded = false; + /** 是否正在播放 */ + this.playing = false; + /** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */ + this.buffered = 0; + /** 已经缓冲的采样点数量 */ + this.bufferedSamples = 0; + /** 歌曲时长,加载完毕之前保持为 0 */ + this.duration = 0; + /** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */ + this.bufferPlayDuration = 1; + /** 音频的采样率,未成功解析出之前保持为 0 */ + this.sampleRate = 0; + //是否循环播放 + this.loop = false; + /** 上一次播放是从何时开始的 */ + this.lastStartWhen = 0; + /** 开始播放时刻 */ + this.lastStartTime = 0; + /** 上一次播放的缓存长度 */ + this.lastBufferSamples = 0; + + /** 是否已经获取到头文件 */ + this.headerRecieved = false; + /** 音频类型 */ + this.audioType = ""; + /** 每多长时间组成一个缓存 Float32Array */ + this.bufferChunkSize = 10; + /** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */ + this.audioData = []; + + this.errored = false; + this.ac = context; + } + /** 当前已经播放了多长时间 */ + get currentTime() { + return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; + } + /** + * 设置每个缓存数据的大小,默认为10秒钟一个缓存数据 + * @param size 每个缓存数据的时长,单位秒 + */ + setChunkSize(size) { + if (this.controller?.loading || this.loaded) return; + this.bufferChunkSize = size; + } + + piped(controller) { + this.controller = controller; + } + + async pump(data, done) { + if (!data || this.errored) return; + if (!this.headerRecieved) { + // 检查头文件获取音频类型,仅检查前256个字节 + const toCheck = data.slice(0, 256); + this.audioType = checkAudioType(data); + if (!this.audioType) { + console.error( + "Unknown audio type. Header: '" + [...toCheck] + .map((v) => v.toString(16).padStart(2, "0")) + .join(" ") + .toUpperCase() + + "'" + ); + return; + } + // 创建解码器 + const Decoder = AudioDecoder.decoderMap.get(this.audioType); + if (!Decoder) { + this.errored = true; + console.error( + "Cannot decode stream source type of '" + + this.audioType + + "', since there is no registered decoder for that type." + ); + return Promise.reject( + `Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.` + ); + } + this.decoder = new Decoder(); + // 创建数据解析器 + const mime = mimeTypeMap[this.audioType]; + const parser = new CodecParser(mime); + this.parser = parser; + await this.decoder.create(); + this.headerRecieved = true; + } + + const decoder = this.decoder; + const parser = this.parser; + if (!decoder || !parser) { + this.errored = true; + return Promise.reject( + "No parser or decoder attached in this AudioStreamSource" + ); + } + + await this.decodeData(data, decoder, parser); + if (done) await this.decodeFlushData(decoder, parser); + this.checkBufferedPlay(); + } + + /** + * 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告 + */ + checkSampleRate(info) { + for (const one of info) { + const frame = isOggPage(one) ? one.codecFrames[0] : one; + if (frame) { + const rate = frame.header.sampleRate; + if (this.sampleRate === 0) { + this.sampleRate = rate; + break; + } else { + if (rate !== this.sampleRate) { + console.warn("Sample rate in stream audio must be constant."); + } + } + } + } + } + + /** + * 解析音频数据 + */ + async decodeData(data, decoder, parser) { + // 解析音频数据 + const audioData = await decoder.decode(data); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.parseChunk(data)]; + + // 检查采样率 + this.checkSampleRate(audioInfo); + // 追加音频数据 + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 解码剩余数据 + */ + async decodeFlushData(decoder, parser) { + const audioData = await decoder.flush(); + if (!audioData) return; + // @ts-expect-error 库类型声明错误 + const audioInfo = [...parser.flush()]; + + this.checkSampleRate(audioInfo); + this.appendDecodedData(audioData, audioInfo); + } + + /** + * 追加音频数据 + */ + appendDecodedData(data, info) { + const channels = data.channelData.length; + if (channels === 0) return; + if (this.audioData.length !== channels) { + this.audioData = []; + for (let i = 0; i < channels; i++) { + this.audioData.push([]); + } + } + // 计算出应该放在哪 + const chunk = this.sampleRate * this.bufferChunkSize; + const sampled = this.bufferedSamples; + const pushIndex = Math.floor(sampled / chunk); + const bufferIndex = sampled % chunk; + const dataLength = data.channelData[0].length; + let buffered = 0; + let nowIndex = pushIndex; + let toBuffer = bufferIndex; + while (buffered < dataLength) { + const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk; + + for (let i = 0; i < channels; i++) { + const audioData = this.audioData[i]; + if (!audioData[nowIndex]) { + audioData.push(new Float32Array(chunk)); + } + const toPush = data.channelData[i].slice(buffered, buffered + rest); + + audioData[nowIndex].set(toPush, toBuffer); + } + buffered += rest; + nowIndex++; + toBuffer = 0; + } + + this.buffered += + info.reduce((prev, curr) => prev + curr.duration, 0) / 1000; + this.bufferedSamples += info.reduce( + (prev, curr) => prev + curr.samples, + 0 + ); + } + + /** + * 检查已缓冲内容,并在未开始播放时播放 + */ + checkBufferedPlay() { + if (this.playing || this.sampleRate === 0) return; + const played = this.lastBufferSamples / this.sampleRate; + const dt = this.buffered - played; + if (this.loaded) { + this.playAudio(played); + return; + } + if (dt < this.bufferPlayDuration) return; + + this.lastBufferSamples = this.bufferedSamples; + // 需要播放 + this.mergeBuffers(); + if (!this.buffer) return; + if (this.playing) this.output.stop(); + this.createSourceNode(this.buffer); + this.output.loop = false; + this.output.start(0, played); + this.lastStartTime = this.ac.currentTime; + this.playing = true; + this.output.addEventListener("ended", () => { + this.playing = false; + this.checkBufferedPlay(); + }); + } + + mergeBuffers() { + const buffer = this.ac.createBuffer( + this.audioData.length, + this.bufferedSamples, + this.sampleRate + ); + const chunk = this.sampleRate * this.bufferChunkSize; + const bufferedChunks = Math.floor(this.bufferedSamples / chunk); + const restLength = this.bufferedSamples % chunk; + for (let i = 0; i < this.audioData.length; i++) { + const audio = this.audioData[i]; + const data = new Float32Array(this.bufferedSamples); + for (let j = 0; j < bufferedChunks; j++) { + data.set(audio[j], chunk * j); + } + if (restLength !== 0) { + data.set( + audio[bufferedChunks].slice(0, restLength), + chunk * bufferedChunks + ); + } + + buffer.copyToChannel(data, i, 0); + } + this.buffer = buffer; + } + + async start() { + delete this.buffer; + this.headerRecieved = false; + this.audioType = ""; + this.errored = false; + this.buffered = 0; + this.sampleRate = 0; + this.bufferedSamples = 0; + this.duration = 0; + this.loaded = false; + if (this.playing) this.output.stop(); + this.playing = false; + this.lastStartTime = this.ac.currentTime; + } + + end(done, reason) { + if (done && this.buffer) { + this.loaded = true; + delete this.controller; + this.mergeBuffers(); + + this.duration = this.buffered; + this.audioData = []; + this.decoder?.destroy(); + delete this.decoder; + delete this.parser; + } else { + console.warn( + "Unexpected end when loading stream audio, reason: '" + + (reason ?? "") + + "'" + ); + } + } + + playAudio(when) { + if (!this.buffer) return; + this.lastStartTime = this.ac.currentTime; + if (this.playing) this.output.stop(); + if (this.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.playing = true; + + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + if (this.loop && !this.output.loop) this.play(0); + }); + } + /** + * 开始播放这个音频源 + */ + play(when) { + if (this.playing || this.errored) return; + if (this.loaded && this.buffer) { + this.playing = true; + this.playAudio(when); + } else { + this.controller?.start(); + } + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + if (this.playing) this.output.stop(); + this.playing = false; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + /** + * 停止播放这个音频源 + * @returns 音频暂停的时刻 number + */ + stop() { + if (this.playing) this.output.stop(); + this.playing = false; + return this.ac.currentTime - this.lastStartTime; + } + /** + * 连接到音频路由图上,每次调用播放的时候都会执行一次 + * @param target 连接至的目标 IAudioInput + */ + connect(target) { + this.target = target; + } + /** + * 设置是否循环播放 + * @param loop 是否循环 boolean) + */ + setLoop(loop) { + this.loop = loop; + } + } + class AudioElementSource { + + constructor(context) { + const audio = new Audio(); + audio.preload = "none"; + this.output = context.createMediaElementSource(audio); + this.audio = audio; + this.ac = context; + audio.addEventListener("play", () => { + this.playing = true; + if (this.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + }); + audio.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + }); + } + get duration() { + return this.audio.duration; + } + get currentTime() { + return this.audio.currentTime; + } + /** + * 设置音频源的路径 + * @param url 音频路径 + */ + setSource(url) { + this.audio.src = url; + } + + play(when = 0) { + if (this.playing) return; + this.audio.currentTime = when; + this.audio.play(); + } + + stop() { + this.audio.pause(); + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + return this.audio.currentTime; + } + + connect(target) { + this.output.connect(target.input); + } + + setLoop(loop) { + this.audio.loop = loop; + } + } + class AudioBufferSource { + + constructor(context) { + this.output = context.createBufferSource(); + /** 是否循环 */ + this.loop = false; + /** 上一次播放是从何时开始的 */ + this.lastStartWhen = 0; + /** 播放开始时刻 */ + this.lastStartTime = 0; + this.duration = 0; + this.ac = context; + } + get currentTime() { + return this.ac.currentTime - this.lastStartTime + this.lastStartWhen; + } + + /** + * 设置音频源数据 + * @param buffer 音频源,可以是未解析的 ArrayBuffer,也可以是已解析的 AudioBuffer + */ + async setBuffer(buffer) { + if (buffer instanceof ArrayBuffer) { + this.buffer = await this.ac.decodeAudioData(buffer); + } else { + this.buffer = buffer; + } + this.duration = this.buffer.duration; + } + + play(when) { + if (this.playing || !this.buffer) return; + this.playing = true; + this.lastStartTime = this.ac.currentTime; + if (this.route.status !== AudioStatus.Playing) { + this.route.status = AudioStatus.Playing; + } + this.createSourceNode(this.buffer); + this.output.start(0, when); + this.output.addEventListener("ended", () => { + this.playing = false; + if (this.route.status === AudioStatus.Playing) { + this.route.status = AudioStatus.Stoped; + } + if (this.loop && !this.output.loop) this.play(0); + }); + } + + createSourceNode(buffer) { + if (!this.target) return; + const node = this.ac.createBufferSource(); + node.buffer = buffer; + this.output = node; + node.connect(this.target.input); + node.loop = this.loop; + } + + stop() { + this.output.stop(); + return this.ac.currentTime - this.lastStartTime; + } + + connect(target) { + this.target = target; + } + + setLoop(loop) { + this.loop = loop; + } + } + class AudioPlayer { + constructor() { + /** 音频播放上下文 */ + this.ac = new AudioContext(); + /** 音量节点 */ + this.gain = this.ac.createGain(); + this.gain.connect(this.ac.destination); + this.audioRoutes = new Map(); + } + /** + * 解码音频数据 + * @param data 音频数据 + */ + decodeAudioData(data) { + return AudioDecoder.decodeAudioData(data, this); + } + /** + * 设置音量 + * @param volume 音量 + */ + setVolume(volume) { + this.gain.gain.value = volume; + } + + /** + * 获取音量 + */ + getVolume() { + return this.gain.gain.value; + } + + /** + * 创建一个音频源 + * @param Source 音频源类 + */ + createSource(Source) { + return new Source(this.ac); + } + + /** + * 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况 + */ + createStreamSource() { + return new AudioStreamSource(this.ac); + } + + /** + * 创建一个通过 audio 元素播放的音频源 + */ + createElementSource() { + return new AudioElementSource(this.ac); + } + + /** + * 创建一个通过 AudioBuffer 播放的音频源 + */ + createBufferSource() { + return new AudioBufferSource(this.ac); + } + + /** + * 获取音频目的地 + */ + getDestination() { + return this.gain; + } + + /** + * 创建一个音频效果器 + * @param Effect 效果器类 + */ + createEffect(Effect) { + return new Effect(this.ac); + } + + /** + * 创建一个修改音量的效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * |----------| + * ``` + */ + createVolumeEffect() { + return new VolumeEffect(this.ac); + } + + /** + * 创建一个立体声效果器 + * ```txt + * |------------| + * Input ----> | PannerNode | ----> Output + * |------------| + * ``` + */ + createStereoEffect() { + return new StereoEffect(this.ac); + } + + /** + * 创建一个修改单个声道音量的效果器 + * ```txt + * |----------| + * -> | GainNode | \ + * |--------------| / |----------| -> |------------| + * Input ----> | SplitterNode | ...... | MergerNode | ----> Output + * |--------------| \ |----------| -> |------------| + * -> | GainNode | / + * |----------| + * ``` + */ + createChannelVolumeEffect() { + return new ChannelVolumeEffect(this.ac); + } + + /** + * 创建一个延迟效果器 + * |-----------| + * Input ----> | DelayNode | ----> Output + * |-----------| + */ + createDelay() { + return new DelayEffect(this.ac); + } + + /** + * 创建一个回声效果器 + * ```txt + * |----------| + * Input ----> | GainNode | ----> Output + * ^ |----------| | + * | | + * | |------------| ↓ + * |-- | Delay Node | <-- + * |------------| + * ``` + */ + createEchoEffect() { + return new EchoEffect(this.ac); + } + + /** + * 创建一个音频播放路由 + * @param source 音频源 + */ + createRoute(source) { + return new AudioRoute(source, this); + } + + /** + * 添加一个音频播放路由,可以直接被播放 + * @param id 这个音频播放路由的名称 + * @param route 音频播放路由对象 + */ + addRoute(id, route) { + if (!this.audioRoutes) this.audioRoutes = new Map(); + if (this.audioRoutes.has(id)) { + console.warn( + "Audio route with id of '" + + id + + "' has already existed. New route will override old route." + ); + } + this.audioRoutes.set(id, route); + } + + /** + * 根据名称获取音频播放路由对象 + * @param id 音频播放路由的名称 + */ + getRoute(id) { + return this.audioRoutes.get(id); + } + /** + * 移除一个音频播放路由 + * @param id 要移除的播放路由的名称 + */ + removeRoute(id) { + this.audioRoutes.delete(id); + } + /** + * 播放音频 + * @param id 音频名称 + * @param when 从音频的哪个位置开始播放,单位秒 + */ + play(id, when) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot play audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + + route.play(when); + } + + /** + * 暂停音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + pause(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot pause audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + return route.pause(); + } + + /** + * 停止音频播放 + * @param id 音频名称 + * @returns 当音乐真正停止时兑现 + */ + stop(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot stop audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + return route.stop(); + } + + /** + * 继续音频播放 + * @param id 音频名称 + */ + resume(id) { + const route = this.getRoute(id); + if (!route) { + console.warn( + "Cannot pause audio route '" + + id + + "', since there is not added route named it." + ); + return; + } + route.resume(); + } + + /** + * 设置听者位置,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 位置x坐标 + * @param y 位置y坐标 + * @param z 位置z坐标 + */ + setListenerPosition(x, y, z) { + const listener = this.ac.listener; + listener.positionX.value = x; + listener.positionY.value = y; + listener.positionZ.value = z; + } + + /** + * 设置听者朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 朝向x坐标 + * @param y 朝向y坐标 + * @param z 朝向z坐标 + */ + setListenerOrientation(x, y, z) { + const listener = this.ac.listener; + listener.forwardX.value = x; + listener.forwardY.value = y; + listener.forwardZ.value = z; + } + + /** + * 设置听者头顶朝向,x正方向水平向右,y正方向垂直于地面向上,z正方向垂直屏幕远离用户 + * @param x 头顶朝向x坐标 + * @param y 头顶朝向y坐标 + * @param z 头顶朝向z坐标 + */ + setListenerUp(x, y, z) { + const listener = this.ac.listener; + listener.upX.value = x; + listener.upY.value = y; + listener.upZ.value = z; + } + } + class AudioRoute { + constructor(source, player) { + source.route = this; + this.output = source.output; + + /** 效果器路由图 */ + this.effectRoute = []; + + /** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */ + this.endTime = 0; + /** 暂停时播放了多长时间 */ + this.pauseCurrentTime = 0; + /** 当前播放状态 */ + this.player = player; + this.status = AudioStatus.Stoped; + + this.shouldStop = false; + /** + * 每次暂停或停止时自增,用于判断当前正在处理的情况。 + * 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作 + */ + this.stopIdentifier = 0; + /** 暂停时刻 */ + this.pauseTime = 0; + this.source = source; + this.source.player = player; + } + /** 音频时长,单位秒 */ + get duration() { + return this.source.duration; + } + /** 当前播放了多长时间,单位秒 */ + get currentTime() { + if (this.status === AudioStatus.Paused) { + return this.pauseCurrentTime; + } else { + return this.source.currentTime; + } + } + set currentTime(time) { + this.source.stop(); + this.source.play(time); + } + /** + * 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。 + * @param time 暂停或停止时,经过多长时间之后才会结束音频的播放 + */ + setEndTime(time) { + this.endTime = time; + } + + /** + * 当音频播放时执行的函数,可以用于音频淡入效果 + * @param fn 音频开始播放时执行的函数 + */ + onStart(fn) { + this.audioStartHook = fn; + } + + /** + * 当音频暂停或停止时执行的函数,可以用于音频淡出效果 + * @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。 + * 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象 + */ + onEnd(fn) { + this.audioEndHook = fn; + } + + /** + * 开始播放这个音频 + * @param when 从音频的什么时候开始播放,单位秒 + */ + async play(when = 0) { + if (this.status === AudioStatus.Playing) return; + this.link(); + await this.player.ac.resume(); + if (this.effectRoute.length > 0) { + const first = this.effectRoute[0]; + this.source.connect(first); + const last = this.effectRoute.at(-1); + last.connect({ input: this.player.getDestination() }); + } else { + this.source.connect({ input: this.player.getDestination() }); + } + this.source.play(when); + this.status = AudioStatus.Playing; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + if (this.status !== AudioStatus.Playing) { + this.status = AudioStatus.Playing; + } + } + + /** + * 暂停音频播放 + */ + async pause() { + if (this.status !== AudioStatus.Playing) return; + this.status = AudioStatus.Pausing; + this.stopIdentifier++; + const identifier = this.stopIdentifier; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + if ( + this.status !== AudioStatus.Pausing || + this.stopIdentifier !== identifier + ) { + return; + } + this.pauseCurrentTime = this.source.currentTime; + const time = this.source.stop(); + this.pauseTime = time; + if (this.shouldStop) { + this.status = AudioStatus.Stoped; + this.endAllEffect(); + + this.shouldStop = false; + } else { + this.status = AudioStatus.Paused; + this.endAllEffect(); + } + this.endAllEffect(); + } + + /** + * 继续音频播放 + */ + resume() { + if (this.status === AudioStatus.Playing) return; + if ( + this.status === AudioStatus.Pausing || + this.status === AudioStatus.Stoping + ) { + this.audioStartHook?.(this); + + return; + } + if (this.status === AudioStatus.Paused) { + this.play(this.pauseTime); + } else { + this.play(0); + } + this.status = AudioStatus.Playing; + this.pauseTime = 0; + this.audioStartHook?.(this); + this.startAllEffect(); + } + + /** + * 停止音频播放 + */ + async stop() { + if (this.status !== AudioStatus.Playing) { + if (this.status === AudioStatus.Pausing) { + this.shouldStop = true; + } + return; + } + this.status = AudioStatus.Stoping; + this.stopIdentifier++; + const identifier = this.stopIdentifier; + if (this.audioEndHook) { + this.audioEndHook(this.endTime, this); + await sleep(this.endTime); + } + if ( + this.status !== AudioStatus.Stoping || + this.stopIdentifier !== identifier + ) { + return; + } + this.source.stop(); + this.status = AudioStatus.Stoped; + this.pauseTime = 0; + this.endAllEffect(); + } + + /** + * 添加效果器 + * @param effect 要添加的效果,可以是数组,表示一次添加多个 + * @param index 从哪个位置开始添加,如果大于数组长度,那么加到末尾,如果小于0,那么将会从后面往前数。默认添加到末尾 + */ + addEffect(effect, index) { + if (isNil(index)) { + if (effect instanceof Array) { + this.effectRoute.push(...effect); + } else { + this.effectRoute.push(effect); + } + } else { + if (effect instanceof Array) { + this.effectRoute.splice(index, 0, ...effect); + } else { + this.effectRoute.splice(index, 0, effect); + } + } + this.setOutput(); + if (this.source.playing) this.link(); + } + + /** + * 移除一个效果器 + * @param effect 要移除的效果 + */ + removeEffect(effect) { + const index = this.effectRoute.indexOf(effect); + if (index === -1) return; + this.effectRoute.splice(index, 1); + effect.disconnect(); + this.setOutput(); + if (this.source.playing) this.link(); + } + + setOutput() { + const effect = this.effectRoute.at(-1); + if (!effect) this.output = this.source.output; + else this.output = effect.output; + } + + /** + * 连接音频路由图 + */ + link() { + this.effectRoute.forEach((v) => v.disconnect()); + this.effectRoute.forEach((v, i) => { + const next = this.effectRoute[i + 1]; + if (next) { + v.connect(next); + } + }); + } + + startAllEffect() { + this.effectRoute.forEach((v) => v.start()); + } + + endAllEffect() { + this.effectRoute.forEach((v) => v.end()); + } + } + + const audioPlayer = new AudioPlayer(); + + class BgmController { + constructor(player) { + this.mainGain = player.createVolumeEffect(); + this.player = player; + /** bgm音频名称的前缀 */ + this.prefix = "bgms."; + /** 每个 bgm 的音量控制器 */ + this.gain = new Map(); + + /** 正在播放的 bgm */ + this.playingBgm = ""; + /** 是否正在播放 */ + this.playing = false; + + /** 是否已经启用 */ + this.enabled = true; + /** 是否屏蔽所有的音乐切换 */ + this.blocking = false; + /** 渐变时长 */ + this.transitionTime = 2000; + } + + /** + * 设置音频渐变时长 + * @param time 渐变时长 + */ + setTransitionTime(time) { + this.transitionTime = time; + for (const [, value] of this.gain) { + value.transition.time(time); + } + } + + /** + * 屏蔽音乐切换 + */ + blockChange() { + this.blocking = true; + } + + /** + * 取消屏蔽音乐切换 + */ + unblockChange() { + this.blocking = false; + } + + /** + * 设置总音量大小 + * @param volume 音量大小 + */ + setVolume(volume) { + this.mainGain.setVolume(volume); + this._volume = volume; + } + /** + * 获取总音量大小 + */ + getVolume() { + return this.mainGain.getVolume(); + } + /** + * 设置是否启用 + * @param enabled 是否启用 + */ + setEnabled(enabled) { + if (enabled) this.resume(); + else this.stop(); + this.enabled = enabled; + } + + /** + * 设置 bgm 音频名称的前缀 + */ + setPrefix(prefix) { + this.prefix = prefix; + } + + getId(name) { + return `${this.prefix}${name}`; + } + + /** + * 根据 bgm 名称获取其 AudioRoute 实例 + * @param id 音频名称 + */ + get(id) { + return this.player.getRoute(this.getId(id)); + } + + /** + * 添加一个 bgm + * @param id 要添加的 bgm 的名称 + * @param url 指定 bgm 的加载地址 + */ + addBgm(id, url = `project/bgms/${id}`) { + const type = guessTypeByExt(id); + if (!type) { + console.warn( + "Unknown audio extension name: '" + + id.split(".").slice(0, -1).join(".") + + "'" + ); + return; + } + const gain = this.player.createVolumeEffect(); + if (isAudioSupport(type)) { + const source = audioPlayer.createElementSource(); + source.setSource(url); + source.setLoop(true); + const route = new AudioRoute(source, audioPlayer); + route.addEffect([gain, this.mainGain]); + audioPlayer.addRoute(this.getId(id), route); + this.setTransition(id, route, gain); + } else { + const source = audioPlayer.createStreamSource(); + const stream = new StreamLoader(url); + stream.pipe(source); + source.setLoop(true); + const route = new AudioRoute(source, audioPlayer); + route.addEffect([gain, this.mainGain]); + audioPlayer.addRoute(this.getId(id), route); + this.setTransition(id, route, gain); + } + } + + /** + * 移除一个 bgm + * @param id 要移除的 bgm 的名称 + */ + removeBgm(id) { + this.player.removeRoute(this.getId(id)); + const gain = this.gain.get(id); + gain?.transition.ticker.destroy(); + this.gain.delete(id); + } + + setTransition(id, route, gain) { + const transition = new Transition(); + transition + .time(this.transitionTime) + .mode(linear()) + .transition("volume", 0); + + const tick = () => { + gain.setVolume(transition.value.volume); + }; + + /** + * @param expect 在结束时应该是正在播放还是停止 + */ + const setTick = async (expect) => { + transition.ticker.remove(tick); + transition.ticker.add(tick); + const identifier = route.stopIdentifier; + await sleep(this.transitionTime + 500); + if (route.status === expect && identifier === route.stopIdentifier) { + transition.ticker.remove(tick); + if (route.status === AudioStatus.Playing) { + gain.setVolume(1); + } else { + gain.setVolume(0); + } + } + }; + + route.onStart(async () => { + transition.transition("volume", 1); + setTick(AudioStatus.Playing); + }); + route.onEnd(() => { + transition.transition("volume", 0); + setTick(AudioStatus.Paused); + }); + route.setEndTime(this.transitionTime); + + this.gain.set(id, { effect: gain, transition }); + } + + /** + * 播放一个 bgm + * @param id 要播放的 bgm 名称 + */ + play(id, when) { + if (this.blocking) return; + if (id !== this.playingBgm && this.playingBgm) { + this.player.pause(this.getId(this.playingBgm)); + } + this.playingBgm = id; + if (!this.enabled) return; + this.player.play(this.getId(id), when); + this.playing = true; + } + + /** + * 继续当前的 bgm + */ + resume() { + if (this.blocking || !this.enabled || this.playing) return; + if (this.playingBgm) { + this.player.resume(this.getId(this.playingBgm)); + } + this.playing = true; + } + + /** + * 暂停当前的 bgm + */ + pause() { + if (this.blocking || !this.enabled) return; + if (this.playingBgm) { + this.player.pause(this.getId(this.playingBgm)); + } + this.playing = false; + } + + /** + * 停止当前的 bgm + */ + stop() { + if (this.blocking || !this.enabled) return; + if (this.playingBgm) { + this.player.stop(this.getId(this.playingBgm)); + } + this.playing = false; + } + } + const bgmController = new BgmController(audioPlayer); + + class SoundPlayer { + constructor(player) { + /** 每个音效的唯一标识符 */ + this.num = 0; + this.enabled = true; + this.gain = player.createVolumeEffect(); + /** 每个音效的数据 */ + this.buffer = new Map(); + /** 所有正在播放的音乐 */ + this.playing = new Set(); + this.player = player; + } + /** + * 设置是否启用音效 + * @param enabled 是否启用音效 + */ + setEnabled(enabled) { + if (!enabled) this.stopAllSounds(); + this.enabled = enabled; + } + + /** + * 设置音量大小 + * @param volume 音量大小 + */ + setVolume(volume) { + this.gain.setVolume(volume); + } + /** + * 获取音量大小 + */ + getVolume() { + return this.gain.getVolume(); + } + /** + * 添加一个音效 + * @param id 音效名称 + * @param data 音效的Uint8Array数据 + */ + async add(id, data) { + const buffer = await this.player.decodeAudioData(data); + if (!buffer) { + console.warn( + "Cannot decode sound '" + + id + + "', since audio file may not supported by 2.b." + ); + return; + } + this.buffer.set(id, buffer); + } + + /** + * 播放一个音效 + * @param id 音效名称 + * @param position 音频位置,[0, 0, 0]表示正中心,x轴指向水平向右,y轴指向水平向上,z轴指向竖直向上 + * @param orientation 音频朝向,[0, 1, 0]表示朝向前方 + */ + play(id, position = [0, 0, 0], orientation = [1, 0, 0]) { + if (!this.enabled || !id) return -1; + const buffer = this.buffer.get(id); + if (!buffer) { + console.warn( + "Cannot play sound '" + + id + + "', since there is no added data named it." + ); + return -1; + } + const soundNum = this.num++; + + const source = this.player.createBufferSource(); + source.setBuffer(buffer); + const route = this.player.createRoute(source); + const stereo = this.player.createStereoEffect(); + stereo.setPosition(position[0], position[1], position[2]); + stereo.setOrientation(orientation[0], orientation[1], orientation[2]); + route.addEffect([stereo, this.gain]); + this.player.addRoute(`sounds.${soundNum}`, route); + route.play(); + source.output.addEventListener("ended", () => { + this.playing.delete(soundNum); + }); + this.playing.add(soundNum); + return soundNum; + } + + /** + * 停止一个音效 + * @param num 音效的唯一 id + */ + stop(num) { + const id = `sounds.${num}`; + const route = this.player.getRoute(id); + if (route) { + route.stop(); + this.player.removeRoute(id); + this.playing.delete(num); + } + } + + /** + * 停止播放所有音效 + */ + stopAllSounds() { + this.playing.forEach((v) => { + const id = `sounds.${v}`; + const route = this.player.getRoute(id); + if (route) { + route.stop(); + this.player.removeRoute(id); + } + }); + this.playing.clear(); + } + } + const soundPlayer = new SoundPlayer(audioPlayer); + + function loadAllBgm() { + const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + for (const bgm of data.main.bgms) { + bgmController.addBgm(bgm); + } + } + loadAllBgm(); + AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder); + AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder); + + core.plugin.audioSystem = { + AudioType, + AudioDecoder, + AudioStatus, + checkAudioType, + isAudioSupport, + audioPlayer, + soundPlayer, + bgmController, + guessTypeByExt, + BgmController, + SoundPlayer, + EchoEffect, + DelayEffect, + ChannelVolumeEffect, + VolumeEffect, + StereoEffect, + AudioEffect, + AudioPlayer, + AudioRoute, + AudioStreamSource, + AudioElementSource, + AudioBufferSource, + loadAllBgm, + StreamLoader, + }; + //bgm相关复写 + control.prototype.playBgm = (bgm, when) => { + bgm = core.getMappedName(bgm); + bgmController.play(bgm, when); + core.setMusicBtn(); + }; + control.prototype.pauseBgm = () => { + bgmController.pause(); + core.setMusicBtn(); + }; + + control.prototype.resumeBgm = function () { + bgmController.resume(); + core.setMusicBtn(); + }; + control.prototype.checkBgm = function () { + core.playBgm(bgmController.playingBgm || main.startBgm); + }; + control.prototype.triggerBgm = function () { + core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; + if (bgmController.playing) bgmController.pause(); + else bgmController.resume(); + core.setMusicBtn(); + core.setLocalStorage("bgmStatus", core.musicStatus.bgmStatus); + }; + //sound相关复写 + control.prototype.playSound = function ( + sound, + _pitch, + callback, + position, + orientation + ) { + if (main.mode != "play" || !core.musicStatus.soundStatus) return; + const name = core.getMappedName(sound); + const num = soundPlayer.play(name, position, orientation); + const route = audioPlayer.getRoute(`sounds.${num}`); + if (!route) { + callback?.(); + return -1; + } else { + sleep(route.duration * 1000).then(() => callback?.()); + return num; + } + }; + control.prototype.stopSound = function (id) { + if (isNil(id)) { + soundPlayer.stopAllSounds(); + } else { + soundPlayer.stop(id); + } + }; + control.prototype.getPlayingSounds = function () { + return [...soundPlayer.playing]; + }; + //sound加载复写 + loader.prototype._loadOneSound_decodeData = function (name, data) { + if (data instanceof Blob) { + var blobReader = new zip.BlobReader(data); + blobReader.init(function () { + blobReader.readUint8Array(0, blobReader.size, function (uint8) { + //core.loader._loadOneSound_decodeData(name, uint8.buffer); + soundPlayer.add(name, uint8); + }); + }); + return; + } + if (data instanceof ArrayBuffer) { + const uint8 = new Uint8Array(data); + soundPlayer.add(name, uint8); + } + }; + //音量控制复写 + soundPlayer.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + bgmController.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + actions.prototype._clickSwitchs_sounds_userVolume = function (delta) { + var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); + if (value == 0 && delta < 0) return; + core.musicStatus.userVolume = core.clamp( + Math.pow(value + delta, 2) / 100, + 0, + 1 + ); + //audioContext 音效 不受designVolume 影响 + if (core.musicStatus.gainNode != null) + core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; + soundPlayer.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + bgmController.setVolume( + core.musicStatus.userVolume * core.musicStatus.designVolume + ); + core.setLocalStorage("userVolume", core.musicStatus.userVolume); + core.playSound("确定"); + core.ui._drawSwitchs_sounds(); + }; +}, "怪物碎裂特效": function () { // 在此增加新插件 // -------------------- 安装说明 -------------------- // @@ -13906,7 +13900,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = ////// 点击楼层传送器时的打开操作 ////// events.prototype.useFly = function (fromUserAction) { - if (!core.isplaying()) return; + if (!core.isPlaying()) return; if (!core.status.maps[core.status.floorId].canFlyFrom) { core.drawTip(core.material.items["fly"].name + "好像失效了", "fly"); return; @@ -14079,7 +14073,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = px <= 317 && py >= 55 && py <= 317 && - core.isplaying() + core.isPlaying() ) { core.useFly(false); return;