From d3817ce86831296e70c99c2b81d17bb9783a4bd2 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Sat, 6 Jun 2020 16:01:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=87=BD=E6=95=B0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _server/CodeMirror/codeMirror.plugin.js | 33 +- _server/CodeMirror/defs.js | 1877 ++++++++++++++++++++++- _server/CodeMirror/tern.js | 1127 +------------- _server/editor_multi.js | 27 +- 4 files changed, 1892 insertions(+), 1172 deletions(-) diff --git a/_server/CodeMirror/codeMirror.plugin.js b/_server/CodeMirror/codeMirror.plugin.js index 15405db8..7ed4e91c 100644 --- a/_server/CodeMirror/codeMirror.plugin.js +++ b/_server/CodeMirror/codeMirror.plugin.js @@ -1866,13 +1866,13 @@ if (!error && (data.type.startsWith('fn(') || data.doc)) { var tip = elt("span", null, elt("strong", null, data.type || "not found")); if (data.doc) - tip.appendChild(document.createTextNode(" — " + data.doc)); /* - if (data.type.startsWith('fn(') && data.url) { - tip.appendChild(document.createTextNode(" ")); - var child = tip.appendChild(elt("a", null, "[docs]")); + tip.appendChild(document.createTextNode("\n" + data.doc.replace(//g,"\n"))); + if (data.url) { + tip.appendChild(document.createTextNode("\n")); + var child = tip.appendChild(elt("a", null, "[文档]")); child.href = data.url; child.target = "_blank"; - } */ + } tempTooltip(cm, tip, ts); } if (c) c(); @@ -2208,25 +2208,19 @@ if (cm.state.ternTooltip) remove(cm.state.ternTooltip); var where = cm.cursorCoords(); var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content); - function maybeClear() { - old = true; - if (!mouseOnTip) clear(); - } function clear() { + if (mouseOnTip) { + mouseOnTip = false; + setTimeout(clear, 50); + return; + } cm.state.ternTooltip = null; if (tip.parentNode) remove(tip) clearActivity() } - var mouseOnTip = false, old = false; + var mouseOnTip = false; CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; }); - CodeMirror.on(tip, "mouseout", function(e) { - var related = e.relatedTarget || e.toElement - if (!related || !CodeMirror.contains(tip, related)) { - if (old) clear(); - else mouseOnTip = false; - } - }); - setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700); + CodeMirror.on(tip, "mouseout", function(e) { mouseOnTip = false; }); var clearActivity = onEditorActivity(cm, clear) } @@ -2244,6 +2238,9 @@ } function makeTooltip(x, y, content) { + if (typeof content === 'string') { + content = content.replace(//g, '\n') + } var node = elt("div", cls + "tooltip", content); node.style.left = x + "px"; node.style.top = y + "px"; diff --git a/_server/CodeMirror/defs.js b/_server/CodeMirror/defs.js index 5a3854b5..0fffba07 100644 --- a/_server/CodeMirror/defs.js +++ b/_server/CodeMirror/defs.js @@ -604,10 +604,6 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ "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)" - }, - "Image": { - "!type": "fn(width?: number, height?: number) -> +HTMLImageElement", - "!doc": "Image Element constructor. Accepts two optional parameters: Image([unsigned long width, unsigned long height]). Returns an HTMLImageElement instance just as document.createElement('img') would." } }, { @@ -1182,6 +1178,21 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ { "!name": "core", "!define": { + "image": { + "!doc": "图片信息", + "width": "number", + "height": "number", + "src": "string" + }, + "audio": { + "!doc": "音乐音效信息", + "currentTime": "number", + "play": "fn()", + "pause": "fn()", + "paused": "bool", + "duration": "number", + "volume": "number", + }, "flag": { "!doc": "当前变量", "hard": { @@ -1278,6 +1289,12 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ "!doc": "当前是否全局不可瞬移" }, }, + "loc": { + "!doc": "勇士坐标", + "x": "number", + "y": "number", + "direction": "string" + }, "hero": { "!doc": "勇士当前属性", "image": { @@ -1370,18 +1387,7 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ }, "loc": { "!doc": "勇士当前坐标和朝向", - "x": { - "!type": "number", - "!doc": "当前x坐标" - }, - "y": { - "!type": "number", - "!doc": "当前y坐标" - }, - "direction": { - "!type": "number", - "!doc": "当前朝向" - }, + "!type": "loc", }, "flags": { "!type": "flag", @@ -1426,6 +1432,52 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ } } }, + "blockInfo": { + "!doc": "图块的更多信息", + "animate": { + "!type": "number", + "!doc": "动画帧数" + }, + "cls": { + "!type": "string", + "!doc": "图块类别" + }, + "faceIds": { + "!doc": "行走图朝向", + "up": "string", + "down": "string", + "left": "string", + "right": "string" + }, + "height": { + "!type": "number", + "!doc": "图块高度" + }, + "id": { + "!type": "string", + "!doc": "图块ID" + }, + "image": { + "!type": "image", + "!doc": "图块所在的图片" + }, + "name": { + "!type": "string", + "!doc": "图块名称" + }, + "number": { + "!type": "number", + "!doc": "图块使用的数字" + }, + "posX": { + "!type": "number", + "!doc": "图块在图片上的横坐标" + }, + "posY": { + "!type": "number", + "!doc": "图块在图片上的纵坐标" + }, + }, "enemy": { "!doc": "怪物信息", "id": { @@ -1968,13 +2020,1792 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ "!doc": "当前地图信息" } }, - "hasSpecial": { - "!type": "fn(special: ?, test: number) -> bool", - "!doc": "判定怪物是否拥有某种特殊属性;special - 怪物的ID、特殊属性值或怪物本身;test - 待检查的的属性编号;" - }, - "getBlock": { - "!type": "fn(x?: number, y?: number, floorId?: string, showDisable?: bool) -> {index: number, block: block}", - "_doc": "返回地图上某个点的图块信息" + "control": { + "!doc": "游戏控制", + "showStatusBar": { + "!doc": "显示状态栏", + "!type": "fn()" + }, + "startReplay": { + "!doc": "开始播放", + "!type": "fn(list: [string])" + }, + "triggerReplay": { + "!doc": "更改播放状态", + "!type": "fn()" + }, + "screenFlash": { + "!doc": "画面闪烁
例如:core.screenFlash([255, 0, 0, 1], 3); // 红屏一闪而过
color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若填负数则会被视为0)的颜色数组,必填
time: 单次闪烁时长,实际闪烁效果为先花其三分之一的时间渐变到目标色调,再花剩余三分之二的时间渐变回去
times: 闪烁的总次数,不填或填0都视为1
callback: 闪烁全部完毕后的回调函数,可选", + "!type": "fn(color: [number], time: number, times?: number, callback?: fn())" + }, + "setCurtain": { + "!doc": "更改画面色调,不计入存档。如需长期生效请使用core.events._action_setCurtain()函数
例如:core.setCurtain(); // 恢复画面色调,用时四分之三秒
color: 一行三列(第四列视为1)或一行四列(第四列若大于1则会被视为1,第四列若为负数则会被视为0)的颜色数组,不填视为[0, 0, 0, 0]
time: 渐变时间,单位为毫秒。不填视为750ms,负数视为0(无渐变,立即更改)
callback: 更改完毕后的回调函数,可选。事件流中常取core.doAction", + "!type": "fn(color?: [number], time?: number, callback?: fn())" + }, + "updateDamage": { + "!doc": "更新地图显伤
例如:core.updateDamage(); // 更新当前地图的显伤,绘制在显伤层(废话)
floorId: 地图id,不填视为当前地图。预览地图时填写
ctx: 绘制到的画布,如果填写了就会画在该画布而不是显伤层", + "!type": "fn(floorId?: string, ctx?: string|CanvasRenderingContext2D)" + }, + "nextX": { + "!doc": "获取主角面前第n格的横坐标
例如:core.closeDoor(core.nextX(), core.nextY(), 'yellowDoor', core.turnHero); // 在主角面前关上一扇黄门,然后主角顺时针旋转90°
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1", + "!type": "fn(n?: number) -> number" + }, + "nextY": { + "!doc": "获取主角面前第n格的纵坐标
例如:core.jumpHero(core.nextX(2), core.nextY(2)); // 主角向前跃过一格,即跳跃靴道具的使用效果
n: 目标格与主角的距离,面前为正数,背后为负数,脚下为0,不填视为1", + "!type": "fn(n?: number) -> number" + }, + "clearContinueAutomaticRoute": { + "!doc": "清空剩下的自动寻路列表", + "!type": "fn(callback?: fn())" + }, + "updateViewport": { + "!doc": "更新大地图的可见区域", + "!type": "fn()" + }, + "getMappedName": { + "!doc": "获得映射文件名", + "!type": "fn(name: string) -> string" + }, + "addFlag": { + "!doc": "增减一个flag变量,等价于 core.setFlag(name, core.getFlag(name, 0) + value)
例如:core.addFlag('hatred', 1); // 增加1点仇恨值
name: 变量名,支持中文
value: 变量的增量", + "!type": "fn(name: string, value: number)" + }, + "setFlag": { + "!doc": "设置一个flag变量
例如:core.setFlag('poison', true); // 令主角中毒
name: 变量名,支持中文
value: 变量的新值,不填或填null视为删除", + "!type": "fn(name: string, value: ?)" + }, + "viewMapReplay": { + "!doc": "回放录像时浏览地图", + "!type": "fn()" + }, + "stopSound": { + "!doc": "停止所有音频", + "!type": "fn()" + }, + "addGameCanvasTranslate": { + "!doc": "加减画布偏移", + "!type": "fn(x?: number, y?: number)" + }, + "addBuff": { + "!doc": "增减主角某个属性的百分比修正倍率,加减法叠加和抵消。等价于 core.setBuff(name, core.getBuff(name) + value)
例如:core.addBuff('atk', -0.1); // 主角获得一层“攻击力减一成”的负面效果
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 倍率的增量", + "!type": "fn(name: string, value: number)" + }, + "drawHero": { + "!doc": "绘制主角和跟随者并重置视野到以主角为中心
例如:core.drawHero(); // 原地绘制主角的静止帧(第一帧)并重置视野,这样调用一般就是用来重置视野
status: 绘制第几帧(默认支持1、2、4,推荐在project\\icons.js中把第三帧也注册了,这里预留了一个'midFoot'作为其枚举值),不填视为静止帧(第一帧)。
offset: 相对主角逻辑位置的偏移量,不填视为无偏移。用于绘制行走中的主角(正数表示前进,负数表示后退,但跟随者的后退很难看)或表现一些特殊的演出效果", + "!type": "fn(status?: string, offset?: number, frame?: number)" + }, + "pauseBgm": { + "!doc": "暂停背景音乐的播放", + "!type": "fn()" + }, + "setReplaySpeed": { + "!doc": "设置播放速度", + "!type": "fn(speed: number)" + }, + "pauseReplay": { + "!doc": "暂停播放", + "!type": "fn()" + }, + "doSL": { + "!doc": "实际进行存读档事件", + "!type": "fn(id?: string, type?: string)" + }, + "setStatus": { + "!doc": "设置主角的某个属性
例如:core.setStatus('atk', 100); // 设置攻击力为100
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.setHeroLoc(name, value),其他的会直接对 core.status.hero[name] 赋值
value: 属性的新值", + "!type": "fn(name: string, value: number)" + }, + "setAutomaticRoute": { + "!doc": "半自动寻路,用于鼠标或手指拖动
例如:core.setAutomaticRoute(0, 0, [{direction: \"right\", x: 4, y: 9}, {direction: \"right\", x: 5, y: 9}, {direction: \"right\", x: 6, y: 9}, {direction: \"up\", x: 6, y: 8}]);
destX: 鼠标或手指的起拖点横坐标
destY: 鼠标或手指的起拖点横坐标
stepPostfix: 拖动轨迹的数组表示,每项为一步的方向和目标点。", + "!type": "fn(destX: number, destY: number, stepPostfix: [loc])" + }, + "triggerHero": { + "!doc": "改变勇士的显隐状态", + "!type": "fn(type?: string, time?: number, callback?: fn())" + }, + "gatherFollowers": { + "!doc": "立刻聚集所有的跟随者", + "!type": "fn()" + }, + "getStatus": { + "!doc": "读取主角的某个属性,不包括百分比修正
例如:core.getStatus('atk'); // 读取主角的攻击力
name: 属性的英文名,其中'x'、'y'和'direction'会被特殊处理为 core.getHeroLoc(name),其他的会直接读取 core.status.hero[name]", + "!type": "fn(name: string) -> number" + }, + "setHeroLoc": { + "!doc": "设置勇士位置
值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;
如需立刻重新绘制地图还需调用:core.clearMap('hero'); core.drawHero(); 来对界面进行更新。
例如:core.setHeroLoc('x', 5) // 将勇士当前位置的横坐标设置为5。
name: 要设置的坐标属性
value: 新值
noGather: 是否聚集跟随者", + "!type": "fn(name: string, value: string|number, noGather?: bool)" + }, + "getLvName": { + "!doc": "根据级别的数字获取对应的名称,后者定义在全塔属性
例如:core.getLvName(); // 获取主角当前级别的名称,如“下级佣兵”
lv: 级别的数字,不填则视为主角当前的级别
返回值:级别的名称,如果不存在就还是返回数字", + "!type": "fn(lv?: number) -> string|number" + }, + "addStatus": { + "!doc": "增减主角的某个属性,等价于core.setStatus(name, core.getStatus(name) + value)
例如:core.addStatus('atk', 100'); // 给主角攻击力加100
name: 属性的英文名
value: 属性的增量", + "!type": "fn(name: string, value: number)" + }, + "speedUpReplay": { + "!doc": "加速播放", + "!type": "fn()" + }, + "loadData": { + "!doc": "从本地读档", + "!type": "fn(data?: ?, callback?: fn())" + }, + "debug": { + "!doc": "开启调试模式, 此模式下可以按Ctrl键进行穿墙, 并忽略一切事件。
此模式下不可回放录像和上传成绩。", + "!type": "fn()" + }, + "moveOneStep": { + "!doc": "每移动一格后执行的事件", + "!type": "fn(callback?: fn())" + }, + "clearStatus": { + "!doc": "清除游戏状态和数据", + "!type": "fn()" + }, + "updateFollowers": { + "!doc": "更新跟随者坐标", + "!type": "fn()" + }, + "waitHeroToStop": { + "!doc": "等待主角停下
例如:core.waitHeroToStop(core.vibrate); // 等待主角停下,然后视野左右抖动1秒
callback: 主角停止后的回调函数", + "!type": "fn(callback?: fn())" + }, + "hideStatusBar": { + "!doc": "隐藏状态栏", + "!type": "fn(showToolbox?: bool)" + }, + "getBuff": { + "!doc": "读取主角某个属性的百分比修正倍率,初始值为1
例如:core.getBuff('atk'); // 主角当前能发挥出多大比例的攻击力
name: 属性的英文名", + "!type": "fn(name: string) -> number" + }, + "setToolbarButton": { + "!doc": "改变工具栏为按钮1-8", + "!type": "fn(useButton?: bool)" + }, + "getSaves": { + "!doc": "获得某些存档内容", + "!type": "fn(ids?: ?, callback?: fn())" + }, + "replay": { + "!doc": "回放下一个操作", + "!type": "fn()" + }, + "getStatusOrDefault": { + "!doc": "从status中获得属性,如果不存在则从勇士属性中获取", + "!type": "fn(status?: ?, name?: string)" + }, + "unregisterReplayAction": { + "!doc": "注销一个录像行为", + "!type": "fn(name: string)" + }, + "setBuff": { + "!doc": "设置主角某个属性的百分比修正倍率,初始值为1,
倍率存放在flag: '__'+name+'_buff__' 中
例如:core.setBuff('atk', 0.5); // 主角能发挥出的攻击力减半
name: 属性的英文名,请注意只能用于数值类属性哦,否则随后的乘法会得到NaN
value: 新的百分比修正倍率,不填(效果上)视为1", + "!type": "fn(name: string, value: number)" + }, + "continueAutomaticRoute": { + "!doc": "继续剩下的自动寻路操作", + "!type": "fn()" + }, + "saveReplay": { + "!doc": "回放时存档", + "!type": "fn()" + }, + "setAutoHeroMove": { + "!doc": "连续行走
例如:core.setAutoHeroMove([{direction: \"up\", step: 1}, {direction: \"left\", step: 3}, {direction: \"right\", step: 3}, {direction: \"up\", step: 9}]); // 上左左左右右右上9
steps: 压缩的步伐数组,每项表示朝某方向走多少步", + "!type": "fn(steps: [?])" + }, + "fillPosWithPoint": { + "!doc": "显示离散的寻路点", + "!type": "fn(pos?: ?)" + }, + "unregisterResize": { + "!doc": "注销一个resize函数", + "!type": "fn(name: string)" + }, + "saveAndStopAutomaticRoute": { + "!doc": "保存剩下的寻路,并停止", + "!type": "fn()" + }, + "hideStartAnimate": { + "!doc": "淡出标题画面
例如:core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择
callback: 标题画面完全淡出后的回调函数", + "!type": "fn(callback?: fn())" + }, + "getAllSaves": { + "!doc": "获得所有存档内容", + "!type": "fn(callback?: fn())" + }, + "updateHeroIcon": { + "!doc": "更新状态栏的勇士图标", + "!type": "fn(name: string)" + }, + "setMusicBtn": { + "!doc": "设置音乐图标的显隐状态", + "!type": "fn()" + }, + "isPlaying": { + "!doc": "游戏是否已经开始", + "!type": "fn() -> bool" + }, + "triggerBgm": { + "!doc": "更改背景音乐的播放", + "!type": "fn()" + }, + "moveHero": { + "!doc": "连续前进,不撞南墙不回头
例如:core.moveHero(); // 连续前进
direction: 可选,如果设置了就会先转身到该方向
callback: 可选,如果设置了就只走一步", + "!type": "fn(direction?: string, callback?: fn())" + }, + "getRealStatusOrDefault": { + "!doc": "从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取", + "!type": "fn(status?: ?, name?: string)" + }, + "removeSave": { + "!doc": "删除某个存档", + "!type": "fn(index?: number, callback?: fn())" + }, + "registerAnimationFrame": { + "!doc": "注册一个 animationFrame
name: 名称,可用来作为注销使用
needPlaying: 是否只在游戏运行时才执行(在标题界面不执行)
func: 要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数", + "!type": "fn(name: string, needPlaying: bool, func?: fn(timestamp: number))" + }, + "getHeroLoc": { + "!doc": "读取主角的位置和/或朝向
例如:core.getHeroLoc(); // 读取主角的位置和朝向
name: 要读取横坐标还是纵坐标还是朝向还是都读取
返回值:name ? core.status.hero.loc[name] : core.status.hero.loc", + "!type": "fn(name: string) -> string|number" + }, + "stopAutomaticRoute": { + "!doc": "停止自动寻路操作", + "!type": "fn()" + }, + "setWeather": { + "!doc": "设置天气,不计入存档。如需长期生效请使用core.events._action_setWeather()函数
例如:core.setWeather('fog', 10); // 设置十级大雾天
type: 新天气的类型,不填视为晴天
level: 新天气(晴天除外)的级别,必须为不大于10的正整数,不填视为5", + "!type": "fn(type?: string, level?: number)" + }, + "updateStatusBar": { + "!doc": "立刻刷新状态栏和地图显伤", + "!type": "fn()" + }, + "autosave": { + "!doc": "自动存档", + "!type": "fn(removeLast?: bool)" + }, + "clearStatusBar": { + "!doc": "清空状态栏", + "!type": "fn()" + }, + "moveAction": { + "!doc": "尝试前进一步,如果面前不可被踏入就会直接触发该点事件
例如:core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理。常用于在事件流中让主角像自由行动时一样前进一步,可以照常触发moveOneStep(跑毒和计步)和面前的事件(包括但不限于阻激夹域捕)
callback: 走一步后的回调函数,可选", + "!type": "fn(callback?: fn())" + }, + "hasFlag": { + "!doc": "判定一个flag变量是否存在且不为false、0、''、null、undefined和NaN
例如:core.hasFlag('poison'); // 判断主角当前是否中毒
name: 变量名,支持中文
此函数等价于 !!core.getFlag(name)", + "!type": "fn(name: string) -> bool" + }, + "rewindReplay": { + "!doc": "回退", + "!type": "fn()" + }, + "toolboxReplay": { + "!doc": "回放录像时打开道具栏", + "!type": "fn()" + }, + "playBgm": { + "!doc": "播放背景音乐,中途开播但不计入存档且只会持续到下次场景切换。如需长期生效请将背景音乐的文件名赋值给flags.__bgm__
例如:core.playBgm('bgm.mp3', 30); // 播放bgm.mp3,并跳过前半分钟
bgm: 背景音乐的文件名,支持全塔属性中映射前的中文名
startTime: 跳过前多少秒,不填则不跳过", + "!type": "fn(bgm: string, startTime?: number)" + }, + "isReplaying": { + "!doc": "是否正在播放录像", + "!type": "fn() -> bool" + }, + "isMoving": { + "!doc": "当前是否正在移动", + "!type": "fn() -> bool" + }, + "getSaveIndexes": { + "!doc": "获得所有存在存档的存档位", + "!type": "fn(callback?: fn())" + }, + "unLockControl": { + "!doc": "解锁状态栏", + "!type": "fn()" + }, + "syncSave": { + "!doc": "同步存档到服务器", + "!type": "fn(type?: string)" + }, + "removeFlag": { + "!doc": "删除某个flag/变量", + "!type": "fn(name: string)" + }, + "registerResize": { + "!doc": "注册一个resize函数
name: 名称,可供注销使用
func: 可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。", + "!type": "fn(name: string, func: fn(obj: ?))" + }, + "stopReplay": { + "!doc": "停止播放", + "!type": "fn(force?: bool)" + }, + "bookReplay": { + "!doc": "回放时查看怪物手册", + "!type": "fn()" + }, + "turnHero": { + "!doc": "主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
例如:core.turnHero(); // 主角顺时针旋转90°,即单击主角或按下Z键的效果
direction: 主角的新朝向,可选", + "!type": "fn(direction?: string)" + }, + "resumeReplay": { + "!doc": "恢复播放", + "!type": "fn()" + }, + "resize": { + "!doc": "屏幕分辨率改变后重新自适应", + "!type": "fn()" + }, + "equipboxReplay": { + "!doc": "回放录像时打开装备栏", + "!type": "fn()" + }, + "getSave": { + "!doc": "获得某个存档内容", + "!type": "fn(index?: number, callback?: fn(data: ?))" + }, + "setViewport": { + "!doc": "设置视野范围", + "!type": "fn(x?: number, y?: number)" + }, + "chooseReplayFile": { + "!doc": "选择录像文件", + "!type": "fn()" + }, + "lockControl": { + "!doc": "锁定状态栏,常常用于事件处理", + "!type": "fn()" + }, + "updateCheckBlock": { + "!doc": "更新领域、夹击、阻击的伤害地图", + "!type": "fn(floorId?: string)" + }, + "checkBlock": { + "!doc": "检查并执行领域、夹击、阻击事件", + "!type": "fn()" + }, + "clearAutomaticRouteNode": { + "!doc": "清除自动寻路路线", + "!type": "fn(x?: number, y?: number)" + }, + "getFlag": { + "!doc": "读取一个flag变量
name: 变量名,支持中文
defaultValue: 当变量不存在时的返回值,可选(事件流中默认填0)。", + "!type": "fn(name: string, defaultValue?: ?)" + }, + "getNakedStatus": { + "!doc": "获得勇士原始属性(无装备和衰弱影响)", + "!type": "fn(name: string)" + }, + "nearHero": { + "!doc": "判定主角是否身处某个点的锯齿领域(即曼哈顿距离)
例如:core.nearHero(6, 6, 6); // 判定主角是否身处点(6,6)的半径为6的锯齿领域
x: 领域的中心横坐标
y: 领域的中心纵坐标
n: 领域的半径,不填视为1", + "!type": "fn(x: number, y: number, n?: number) -> bool" + }, + "stepReplay": { + "!doc": "单步播放", + "!type": "fn()" + }, + "hasSave": { + "!doc": "判断某个存档位是否存在存档", + "!type": "fn(index?: number) -> bool" + }, + "showStartAnimate": { + "!doc": "进入标题画面
例如:core.showStartAnimate(); // 重启游戏但不重置bgm
noAnimate: 可选,true表示不由黑屏淡入而是立即亮屏
callback: 可选,完全亮屏后的回调函数", + "!type": "fn(noAnimate?: bool, callback?: fn())" + }, + "moveViewport": { + "!doc": "移动视野范围", + "!type": "fn(steps?: ?, time?: number, callback?: fn())" + }, + "syncLoad": { + "!doc": "从服务器加载存档", + "!type": "fn()" + }, + "setHeroMoveInterval": { + "!doc": "设置行走的效果动画", + "!type": "fn(callback?: fn())" + }, + "registerReplayAction": { + "!doc": "注册一个录像行为
name: 自定义名称,可用于注销使用
func: 具体执行录像的函数,可为一个函数或插件中的函数名;
需要接受一个action参数,代表录像回放时的下一个操作
func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。", + "!type": "fn(name: string, func: fn(action?: string) -> bool)" + }, + "checkAutosave": { + "!doc": "实际将自动存档写入存储", + "!type": "fn()" + }, + "resumeBgm": { + "!doc": "恢复背景音乐的播放", + "!type": "fn(resumeTime?: number)" + }, + "setGameCanvasTranslate": { + "!doc": "设置大地图的偏移量", + "!type": "fn(ctx: string|CanvasRenderingContext2D, x: number, y: number)" + }, + "checkBgm": { + "!doc": "检查bgm状态", + "!type": "fn()" + }, + "speedDownReplay": { + "!doc": "减速播放", + "!type": "fn()" + }, + "getRealStatus": { + "!doc": "计算主角的某个属性,包括百分比修正
例如:core.getRealStatus('atk'); // 计算主角的攻击力,包括百分比修正。战斗使用的就是这个值
name: 属性的英文名,请注意只能用于数值类属性哦,否则乘法会得到NaN", + "!type": "fn(name: string)" + }, + "saveData": { + "!doc": "存档到本地", + "!type": "fn()" + }, + "unregisterAnimationFrame": { + "!doc": "注销一个animationFrame", + "!type": "fn(name: string)" + }, + "tryMoveDirectly": { + "!doc": "尝试瞬移,如果该点有图块/事件/阻激夹域捕则会瞬移到它旁边再走一步(不可踏入的话当然还是触发该点事件),这一步的方向优先和瞬移前主角的朝向一致
例如:core.tryMoveDirectly(6, 0); // 尝试瞬移到地图顶部的正中央,以样板0层为例,实际效果是瞬移到了上楼梯下面一格然后向上走一步并触发上楼事件
destX: 目标点的横坐标
destY: 目标点的纵坐标", + "!type": "fn(destX: number, destY: number)" + }, + "moveDirectly": { + "!doc": "瞬间移动", + "!type": "fn(destX?: number, destY?: number, ignoreSteps?: number)" + } + }, + "icons": { + "!doc": "图标信息", + "getTilesetOffset": { + "!doc": "根据图块数字或ID获得所在的tileset和坐标信息", + "!type": "fn(id?: string) -> {image: ?, x: number, y: number}" + }, + "getClsFromId": { + "!doc": "根据ID获得其图标类型", + "!type": "fn(id?: string) -> string" + }, + "getAllIconIds": { + "!doc": "获得所有图标的ID", + "!type": "fn() -> [string]" + }, + "getIcons": { + "!doc": "获得所有图标类型", + "!type": "fn()" + } + }, + "items": { + "!doc": "道具相关的函数", + "getEquip": { + "!doc": "检查主角某种类型的装备目前是什么
例如:core.getEquip(1) // 主角目前装备了什么盾牌
equipType: 装备类型,自然数
返回值:装备id,null表示未穿戴", + "!type": "fn(equipType: number) -> string" + }, + "loadEquip": { + "!doc": "尝试穿上某件装备并提示
例如:core.loadEquip('sword5') // 尝试装备上神圣剑,无回调
equipId: 装备id
callback: 穿戴成功或失败后的回调函数", + "!type": "fn(equipId: string, callback?: fn())" + }, + "itemCount": { + "!doc": "统计某种道具的持有量
例如:core.itemCount('yellowKey') // 持有多少把黄钥匙
itemId: 道具id
返回值:该种道具的持有量,不包括已穿戴的装备", + "!type": "fn(itemId: string) -> number" + }, + "getItems": { + "!doc": "获得所有道具", + "!type": "fn()" + }, + "canUseItem": { + "!doc": "检查能否使用某种道具
例如:core.canUseItem('pickaxe') // 能否使用破墙镐
itemId: 道具id
返回值:true表示可以使用", + "!type": "fn(itemId: string) -> bool" + }, + "hasItem": { + "!doc": "检查主角是否持有某种道具(不包括已穿戴的装备)
例如:core.hasItem('yellowKey') // 主角是否持有黄钥匙
itemId: 道具id
返回值:true表示持有", + "!type": "fn(itemId: string) -> bool" + }, + "addItem": { + "!doc": "静默增减某种道具的持有量 不会更新游戏画面或是显示提示
例如:core.addItem('yellowKey', -2) // 没收两把黄钥匙
itemId: 道具id
itemNum: 增加量,负数表示没收", + "!type": "fn(itemId: string, itemNum?: number)" + }, + "unloadEquip": { + "!doc": "脱下某个类型的装备
例如:core.unloadEquip(1) // 卸下盾牌,无回调
equipType: 装备类型编号,自然数
callback: 卸下装备后的回调函数", + "!type": "fn(equipType: number, callback?: fn())" + }, + "quickLoadEquip": { + "!doc": "快速换装
例如:core.quickLoadEquip(1) // 快速换上1号套装
index: 套装编号,自然数", + "!type": "fn(index: number)" + }, + "getItemEffect": { + "!doc": "即捡即用类的道具获得时的效果
例如:core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果
itemId: 道具id
itemNum: 道具数量,可选,默认为1", + "!type": "fn(itemId: string, itemNum?: number)" + }, + "quickSaveEquip": { + "!doc": "保存当前套装
例如:core.quickSaveEquip(1) // 将当前套装保存为1号套装
index: 套装编号,自然数", + "!type": "fn(index: number)" + }, + "setItem": { + "!doc": "设置某种道具的持有量
例如:core.setItem('shoes') // 没收绿鞋,重新启用passNet触发器
itemId: 道具id
itemNum: 新的持有量,可选,自然数,默认为0", + "!type": "fn(itemId: string, itemNum?: number)" + }, + "compareEquipment": { + "!doc": "比较两件(类型可不同)装备的优劣
例如:core.compareEquipment('sword5', 'shield5') // 比较神圣剑和神圣盾的优劣
compareEquipId: 装备甲的id
beComparedEquipId: 装备乙的id
返回值:两装备的各属性差,甲减乙,0省略", + "!type": "fn(compareEquipId: string, beComparedEquipId: string) -> {value: ?, percentage: ?}" + }, + "removeItem": { + "!doc": "删除某个物品", + "!type": "fn(itemId?: string, itemNum?: number)" + }, + "getEquipTypeById": { + "!doc": "判定某件装备的类型
例如:core.getEquipTypeById('shield5') // 1(盾牌)
equipId: 装备id
返回值:类型编号,自然数", + "!type": "fn(equipId: string) -> number" + }, + "getEquipTypeByName": { + "!doc": "根据类型获得一个可用的装备孔", + "!type": "fn(name?: string)" + }, + "useItem": { + "!doc": "使用一个道具
例如:core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调
itemId: 道具id
noRoute: 是否不计入录像,快捷键使用的请填true,否则可省略
callback: 道具使用完毕或使用失败后的回调函数", + "!type": "fn(itemId: string, noRoute?: bool, callback?: fn())" + }, + "hasEquip": { + "!doc": "检查主角是否穿戴着某件装备
例如:core.hasEquip('sword5') // 主角是否装备了神圣剑
itemId: 装备id
返回值:true表示已装备", + "!type": "fn(itemId: string) -> bool" + }, + "getItemEffectTip": { + "!doc": "即捡即用类的道具获得时的额外提示
例如:core.getItemEffectTip(redPotion) // (获得 红血瓶)',生命+100'
itemId: 道具id
返回值:图块属性itemEffectTip的内容", + "!type": "fn(itemId: string) -> string" + }, + "canEquip": { + "!doc": "检查能否穿上某件装备
例如:core.canEquip('sword5', true) // 主角可以装备神圣剑吗,如果不能会有提示
equipId: 装备id
hint: 无法穿上时是否提示(比如是因为未持有还是别的什么原因)
返回值:true表示可以穿上,false表示无法穿上", + "!type": "fn(equipId: string, hint?: bool) -> bool" + } + }, + "utils": { + "!doc": "工具类函数", + "scan": { + "!doc": "朝向到x,y映射", + "up": { + "x": "number", + "y": "number" + }, + "down": { + "x": "number", + "y": "number" + }, + "left": { + "x": "number", + "y": "number" + }, + "right": { + "x": "number", + "y": "number" + }, + }, + "clamp": { + "!doc": "将x限定在[a,b]区间内,注意a和b可交换
例如:core.clamp(1200, 1, 1000); // 1000
x: 原始值,!x为true时x一律视为0
a: 下限值,大于b将导致与b交换
b: 上限值,小于a将导致与a交换", + "!type": "fn(x: number, a: number, b: number) -> number" + }, + "rand": { + "!doc": "不支持SL的随机数
例如:1 + core.rand(6); // 随机生成一个小于7的正整数,模拟骰子的效果
num: 填正数表示生成小于num的随机自然数,否则生成小于1的随机正数
返回值:随机数,即使读档也不会改变结果", + "!type": "fn(num?: number) -> number" + }, + "clone": { + "!doc": "深拷贝一个对象(函数将原样返回)
例如:core.clone(core.status.hero, (name, value) => (name == 'items' || typeof value == 'number'), false); // 深拷贝主角的属性和道具
data: 待拷贝对象
filter: 过滤器,可选,表示data为数组或对象时拷贝哪些项或属性,true表示拷贝
recursion: 过滤器是否递归,可选。true表示过滤器也被递归
返回值:拷贝的结果,注意函数将原样返回", + "!type": "fn(data?: ?, filter?: fn(name: string, value: ?) -> bool, recursion?: bool)" + }, + "setLocalForage": { + "!doc": "往数据库写入一段数据", + "!type": "fn(key: string, value?: ?, successCallback?: fn(), errorCallback?: fn())" + }, + "getGlobal": { + "!doc": "读取一个全局存储,适用于global:xxx,支持录像。
例如:if (core.getGlobal('一周目已通关', false) === true) core.getItem('dagger'); // 二周目游戏进行到此处时会获得一把屠龙匕首
key: 全局变量名称,支持中文
defaultValue: 可选,当此全局变量不存在或值为null、undefined时,用此值代替
返回值:全局变量的值", + "!type": "fn(key: string, defaultValue?: ?)" + }, + "replaceText": { + "!doc": "将一段文字中的${}(表达式)进行替换。
例如:core.replaceText('衬衫的价格是${status:hp}镑${item:yellowKey}便士。'); // 把主角的生命值和持有的黄钥匙数量代入这句话
text: 模板字符串,可以使用${}计算js表达式,支持“状态、物品、变量、独立开关、全局存储、图块id、图块类型、敌人数据、装备id”等量参与运算
返回值:替换完毕后的字符串", + "!type": "fn(text: string, prefix?: string) -> string" + }, + "removeLocalStorage": { + "!doc": "移除本地存储", + "!type": "fn(key: string)" + }, + "unzip": { + "!doc": "解压一段内容", + "!type": "fn(blobOrUrl?: ?, success?: fn(data: ?), error?: fn(error: string), convertToText?: bool, onprogress?: fn(loaded: number, total: number))" + }, + "formatTime": { + "!doc": "格式化时间", + "!type": "fn(time: number) -> string" + }, + "readFile": { + "!doc": "尝试请求读取一个本地文件内容 [异步]
success: 成功后的回调
error: 失败后的回调
readType: 不设置则以文本读取,否则以DataUrl形式读取", + "!type": "fn(success?: fn(data: string), error?: fn(message: string), readType?: bool)" + }, + "readFileContent": { + "!doc": "文件读取完毕后的内容处理 [异步]", + "!type": "fn(content: string)" + }, + "formatDate": { + "!doc": "格式化日期为字符串", + "!type": "fn(date: ?) -> string" + }, + "download": { + "!doc": "弹窗请求下载一个文本文件
例如:core.download('route.txt', JSON.stringify(core.status.route)); // 弹窗请求下载录像
filename: 文件名
content: 文件内容", + "!type": "fn(filename: string, content: string)" + }, + "encodeBase64": { + "!doc": "base64加密
例如:core.encodeBase64('abcd'); // 'YWJjZA=='
str: 明文
返回值:密文", + "!type": "fn(str: string) -> string" + }, + "strlen": { + "!doc": "求字符串的国标码字节数,也可用于等宽字体下文本的宽度测算。请注意样板的默认字体Verdana不是等宽字体
例如:core.strlen('无敌ad'); // 6
str: 待测字符串
返回值:字符串的国标码字节数,每个汉字为2,每个ASCII字符为1", + "!type": "fn(str: string) -> number" + }, + "myprompt": { + "!doc": "让用户输入一段文字", + "!type": "fn(hint: string, value: string, callback?: fn(data?: string))" + }, + "getCookie": { + "!doc": "访问浏览器cookie", + "!type": "fn(name: string) -> string" + }, + "decodeRoute": { + "!doc": "录像解压的最后一步,即一压的逆过程
例如:core.decodeRoute(core.encodeRoute(core.status.route)); // 一压当前录像再解压-_-|
route: 录像解压倒数第二步的结果,即一压的结果
返回值:原始录像", + "!type": "fn(route: string) -> [string]" + }, + "formatDate2": { + "!doc": "格式化日期为最简字符串", + "!type": "fn(date: ?) -> string" + }, + "unshift": { + "!doc": "将b(可以是另一个数组)插入数组a的开头,此函数用于弥补a.unshift(b)中b只能是单项的不足。
例如:core.unshift(todo, {type: 'unfollow'}); // 在事件指令数组todo的开头插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新首项或前缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", + "!type": "fn(a: [?], b: ?) -> [?]" + }, + "same": { + "!doc": "判定深层相等, 会逐层比较每个元素
例如:core.same(['1', 2], ['1', 2]); // true", + "!type": "fn(a?: ?, b?: ?) -> bool" + }, + "setTwoDigits": { + "!doc": "两位数显示", + "!type": "fn(x: number) -> string" + }, + "splitImage": { + "!doc": "等比例切分一张图片
例如:core.splitImage(core.material.images.images['npc48.png'], 32, 48); // 把npc48.png切分成若干32×48px的小人
image: 图片名(支持映射前的中文名)或图片对象(参见上面的例子),获取不到时返回[]
width: 子图的宽度,单位为像素。原图总宽度必须是其倍数,不填视为32
height: 子图的高度,单位为像素。原图总高度必须是其倍数,不填视为正方形
返回值:子图组成的数组,在原图中呈先行后列,从左到右、从上到下排列。", + "!type": "fn(image?: string|image, width?: number, height?: number) -> [image]" + }, + "decompress": { + "!doc": "解压缩一个数据", + "!type": "fn(value: ?)" + }, + "showWithAnimate": { + "!doc": "动画显示某对象", + "!type": "fn(obj?: ?, speed?: number, callback?: fn())" + }, + "subarray": { + "!doc": "判定一个数组是否为另一个数组的前缀,用于录像接续播放。请注意函数名没有大写字母
例如:core.subarray(['ad', '米库', '小精灵', '小破草', '小艾'], ['ad', '米库', '小精灵']); // ['小破草', '小艾']
a: 可能的母数组,不填或比b短将返回null
b: 可能的前缀,不填或比a长将返回null
返回值:如果b不是a的前缀将返回null,否则将返回a去掉此前缀后的剩余数组", + "!type": "fn(a?: [?], b?: [?]) -> [?]|null" + }, + "turnDirection": { + "!doc": "转向某方向
turn: 转向的方向,可为 up,down,left,right,:left,:right,:back 七种
direction: 当前方向", + "!type": "fn(turn: string, direction?: string) -> string" + }, + "myconfirm": { + "!doc": "显示确认框,类似core.drawConfirmBox()
例如:core.myconfirm('重启游戏?', core.restart); // 弹窗询问玩家是否重启游戏
hint: 弹窗的内容
yesCallback: 确定后的回调函数
noCallback: 取消后的回调函数,可选", + "!type": "fn(hint: string, yesCallback?: fn(), noCallback?: fn())" + }, + "calValue": { + "!doc": "计算一个表达式的值,支持status:xxx等的计算。
例如:core.calValue('status:hp + status:def'); // 计算主角的生命值加防御力
value: 待求值的表达式
prefix: 独立开关前缀,一般可省略
返回值:求出的值", + "!type": "fn(value: string, prefix?: string)" + }, + "encodeRoute": { + "!doc": "录像压缩缩
例如:core.encodeRoute(core.status.route); // 压缩当前录像
route: 原始录像,自定义内容(不予压缩,原样写入)必须由0-9A-Za-z和下划线、冒号组成,所以中文和数组需要用JSON.stringify预处理再base64压缩才能交由一压
返回值:一压的结果", + "!type": "fn(route: [string]) -> string" + }, + "decodeBase64": { + "!doc": "base64解密
例如:core.decodeBase64('YWJjZA=='); // \"abcd\"
str: 密文
返回值:明文", + "!type": "fn(str: string) -> string" + }, + "http": { + "!doc": "发送一个HTTP请求 [异步]
type: 请求类型,只能为GET或POST
url: 目标地址
formData: 如果是POST请求则为表单数据
success: 成功后的回调
error: 失败后的回调", + "!type": "fn(type: string, url: string, formData: ?, success?: fn(data: string), error?: fn(message: string), mimeType?: string, responseType?: string, onprogress?: fn(loaded: number, total: number))" + }, + "getLocalStorage": { + "!doc": "获得本地存储", + "!type": "fn(key: string, defaultValue?: ?)" + }, + "arrayToRGB": { + "!doc": "颜色数组转字符串
例如:core.arrayToRGBA([102, 204, 255]); // \"rgba(102,204,255,1)\"
color: 一行三列或一行四列的数组,前三个元素必须为不大于255的自然数。第四个元素(如果有)必须为0或不大于1的数字,第四个元素不填视为1
返回值:该颜色的字符串表示", + "!type": "fn(color: [number]) -> string" + }, + "formatBigNumber": { + "!doc": "大数字格式化,单位为10000的倍数(w,e,z,j,g),末尾四舍五入
例如:core.formatBigNumber(123456789, false); // \"12346w\"
x: 原数字
onMap: 可选,true表示用于地图显伤,结果总字符数最多为5,否则最多为6
返回值:格式化结果", + "!type": "fn(x: number, onMap?: bool) -> string" + }, + "removeLocalForage": { + "!doc": "移除本地数据库的数据", + "!type": "fn(key: string, successCallback?: fn(), errorCallback?: fn())" + }, + "matchWildcard": { + "!doc": "通配符匹配,用于搜索图块等批量处理。
例如:core.playSound(core.matchWildcard('*Key', itemId) ? 'item.mp3' : 'door.mp3'); // 判断捡到的是钥匙还是别的道具,从而播放不同的音效
pattern: 模式串,每个星号表示任意多个(0个起)字符
string: 待测串
返回值:true表示匹配成功,false表示匹配失败", + "!type": "fn(pattern: string, string: string) -> bool" + }, + "setLocalStorage": { + "!doc": "设置本地存储", + "!type": "fn(key: string, value?: ?)" + }, + "hideWithAnimate": { + "!doc": "动画使某对象消失", + "!type": "fn(obj?: ?, speed?: number, callback?: fn())" + }, + "copy": { + "!doc": "尝试复制一段文本到剪切板。", + "!type": "fn(data: string) -> bool" + }, + "isset": { + "!doc": "判断一个值是否不为null,undefined和NaN
例如:core.isset(0/0); // false,因为0/0等于NaN
v: 待测值,可选
返回值:false表示待测值为null、undefined、NaN或未填写,true表示为其他值。", + "!type": "fn(v?: ?) -> bool" + }, + "replaceValue": { + "!doc": "对一个表达式中的特殊规则进行替换,如status:xxx等。
例如:core.replaceValue('status:atk+item:yellowKey'); // 把这两个冒号表达式替换为core.getStatus('hp')和core.itemCount('yellowKey')这样的函数调用
value: 模板字符串,注意独立开关不会被替换
返回值:替换完毕后的字符串", + "!type": "fn(value: string) -> string" + }, + "getLocalForage": { + "!doc": "从本地数据库读出一段数据", + "!type": "fn(key: string, defaultValue?: ?, successCallback?: fn(data: ?), errorCallback?: fn())" + }, + "inArray": { + "!doc": "判定array是不是一个数组,以及element是否在该数组中。
例如:core.inArray(core.material.enemys.greenSlime.special, 1); // 判定绿头怪除先攻外还有无其他特殊属性
array: 可能的数组,不为数组或不填将导致返回值为false
element: 待查找的元素
返回值:如果array为数组且具有element这项,就返回true,否则返回false", + "!type": "fn(array?: ?, element?: ?) -> bool" + }, + "setGlobal": { + "!doc": "设置一个全局存储,适用于global:xxx,录像播放时将忽略此函数。
例如:core.setBlobal('一周目已通关', true); // 设置全局存储“一周目已通关”为true,方便二周目游戏中的新要素。
key: 全局变量名称,支持中文
value: 全局变量的新值,不填或null表示清除此全局存储", + "!type": "fn(key: string, value?: ?)" + }, + "rand2": { + "!doc": "支持SL的随机数,并计入录像
例如:1 + core.rand2(6); // 随机生成一个小于7的正整数,模拟骰子的效果
num: 正整数,0或不填会被视为2147483648
返回值:属于 [0, num) 的随机数", + "!type": "fn(num?: number) -> number" + }, + "setStatusBarInnerHTML": { + "!doc": "填写非自绘状态栏
例如:core.setStatusBarInnerHTML('hp', core.status.hero.hp, 'color: #66CCFF'); // 更新状态栏中的主角生命,使用加载画面的宣传色
name: 状态栏项的名称,如'hp', 'atk', 'def'等。必须是core.statusBar中的一个合法项
value: 要填写的内容,大数字会被格式化为至多6个字符,无中文的内容会被自动设为斜体
css: 额外的css样式,可选。如更改颜色等", + "!type": "fn(name: string, value: ?, css?: string)" + }, + "matchRegex": { + "!doc": "是否满足正则表达式", + "!type": "fn(pattern: string, string: string) -> string" + }, + "push": { + "!doc": "将b(可以是另一个数组)插入数组a的末尾,此函数用于弥补a.push(b)中b只能是单项的不足。
例如:core.push(todo, {type: 'unfollow'}); // 在事件指令数组todo的末尾插入“取消所有跟随者”指令
a: 原数组
b: 待插入的新末项或后缀数组
返回值:插入完毕后的新数组,它是改变原数组a本身得到的", + "!type": "fn(a: [?], b: ?) -> [?]" + }, + "formatSize": { + "!doc": "格式化文件大小", + "!type": "fn(size: number) -> string" + } + }, + "actions": { + "!doc": "用户操作相关的函数", + "onup": { + "!doc": "当点击(触摸)事件放开时", + "!type": "fn(loc: {x: number, y: number, size: number})" + }, + "pressKey": { + "!doc": "按住某个键时", + "!type": "fn(keyCode: number)" + }, + "keyUp": { + "!doc": "根据放开键的code来执行一系列操作", + "!type": "fn(keyCode: number, altKey?: bool, fromReplay?: bool)" + }, + "ondown": { + "!doc": "点击(触摸)事件按下时", + "!type": "fn(loc: {x: number, y: number, size: number})" + }, + "registerAction": { + "!doc": "此函数将注册一个用户交互行为。
action: 要注册的交互类型,如 ondown, onclick, keyDown 等等。
name: 你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。
func: 执行函数。
如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。
priority: 优先级;优先级高的将会被执行。此项可不填,默认为0", + "!type": "fn(action: string, name: string, func: string|fn(params: ?), priority?: number)" + }, + "onkeyDown": { + "!doc": "按下某个键时", + "!type": "fn(e: Event)" + }, + "keyDown": { + "!doc": "根据按下键的code来执行一系列操作", + "!type": "fn(keyCode: number)" + }, + "onStatusBarClick": { + "!doc": "点击自绘状态栏时", + "!type": "fn(e?: Event)" + }, + "longClick": { + "!doc": "长按", + "!type": "fn(x: number, y: number, fromEvent?: bool)" + }, + "unregisterAction": { + "!doc": "注销一个用户交互行为", + "!type": "fn(action: string, name: string)" + }, + "keyDownCtrl": { + "!doc": "长按Ctrl键时", + "!type": "fn() -> bool" + }, + "onclick": { + "!doc": "具体点击屏幕上(x,y)点时,执行的操作", + "!type": "fn(x: number, y: number, stepPostfix?: ?)" + }, + "doRegisteredAction": { + "!doc": "执行一个用户交互行为", + "!type": "fn(action: string, params: ?)" + }, + "onkeyUp": { + "!doc": "放开某个键时", + "!type": "fn(e: Event)" + }, + "onmousewheel": { + "!doc": "滑动鼠标滚轮时的操作", + "!type": "fn(direct: number)" + }, + "onmove": { + "!doc": "当在触摸屏上滑动时", + "!type": "fn(loc: {x: number, y: number, size: number})" + } + }, + "loader": { + "!doc": "资源加载相关的函数", + "loadImages": { + "!doc": "加载一系列图片", + "!type": "fn(dir: string, names: [string], toSave: ?, callback?: fn()) " + }, + "loadImagesFromZip": { + "!doc": "从zip中加载一系列图片", + "!type": "fn(url: string, names: [string], toSave?: ?, onprogress?: ?, onfinished?: ?)" + }, + "loadBgm": { + "!doc": "加载一个bgm", + "!type": "fn(name: string)" + }, + "loadOneMusic": { + "!doc": "加载一个音乐或音效", + "!type": "fn(name: string)" + }, + "freeBgm": { + "!doc": "释放一个bgm的缓存", + "!type": "fn(name: string)" + }, + "loadOneSound": { + "!doc": "加载一个音效", + "!type": "fn(name: string)" + }, + "loadImage": { + "!doc": "加载某一张图片", + "!type": "fn(dir: name, imgName: name, callback?: fn())" + } + }, + "maps": { + "!doc": "地图处理相关的函数", + "noPass": { + "!doc": "判定某个点是否不可被踏入(不基于主角生命值和图块cannotIn属性)
例如:core.noPass(0, 0); // 判断地图左上角能否被踏入
x: 目标点的横坐标
y: 目标点的纵坐标
floorId: 目标点所在的地图id,不填视为当前地图
返回值:true表示可踏入", + "!type": "fn(x: number, y: number, floorId?: string) -> bool" + }, + "drawAnimate": { + "!doc": "播放动画,注意即使指定了主角的坐标也不会跟随主角移动,如有需要请使用core.drawHeroAnimate(name, callback)函数
例如:core.drawAnimate('attack', core.nextX(), core.nextY(), core.vibrate); // 在主角面前一格播放普攻动画,动画停止后视野左右抖动1秒
name: 动画文件名,不含后缀
x: 绝对横坐标
y: 绝对纵坐标
callback: 动画停止后的回调函数,可选
返回值:一个数字,可作为core.stopAnimate()的参数来立即停止播放(届时还可选择是否执行此次播放的回调函数)", + "!type": "fn(name: string, x: number, y: number, alignWindow: bool, callback?: fn()) -> number" + }, + "getBlockCls": { + "!doc": "判定某个点的图块类型
例如:if(core.getBlockCls(x1, y1) != 'enemys' && core.getBlockCls(x2, y2) != 'enemy48') core.openDoor(x3, y3); // 另一个简单的机关门事件,打败或炸掉这一对不同身高的敌人就开门
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否不返回null,true表示不返回null
返回值:图块类型,即“地形、四帧动画、矮敌人、高敌人、道具、矮npc、高npc、自动元件、额外地形”之一", + "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> string" + }, + "drawMap": { + "!doc": "地图重绘
例如:core.drawMap(); // 重绘当前地图,常用于更改贴图后的刷新
floorId: 地图id,建议省略
callback: 重绘完毕后的回调函数,可选", + "!type": "fn(floorId?: string)" + }, + "nearStair": { + "!doc": "当前位置是否在楼梯边", + "!type": "fn() -> bool" + }, + "turnBlock": { + "!doc": "事件转向", + "!type": "fn(direction?: string, x?: number, y?: number, floorId?: string)" + }, + "getMapArray": { + "!doc": "生成事件层矩阵
例如:core.getMapArray('MT0'); // 生成主塔0层的事件层矩阵,隐藏的图块视为0
floorId: 地图id,不填视为当前地图
showDisable: 可选,true表示隐藏的图块也会被表示出来
返回值:事件层矩阵,注意对其阵元的访问是[y][x]", + "!type": "fn(floorId?: string) -> [[number]]" + }, + "jumpBlock": { + "!doc": "跳跃图块
例如:core.jumpBlock(0, 0, 0, 0); // 令地图左上角的图块原地跳跃半秒,再花半秒淡出
sx: 起点的横坐标
sy: 起点的纵坐标
ex: 终点的横坐标
ey: 终点的纵坐标
time: 单步和淡出用时,单位为毫秒。不填视为半秒
keep: 是否不淡出,true表示不淡出
callback: 落地或淡出后的回调函数,可选", + "!type": "fn(sx: number, sy: number, ex: number, ey: number, time?: number, keep?: bool, callback?: fn())" + }, + "replaceBlock": { + "!doc": "批量替换图块
例如:core.replaceBlock(21, 22, core.floorIds); // 把游戏中地上当前所有的黄钥匙都变成蓝钥匙
fromNumber: 旧图块的数字
toNumber: 新图块的数字
floorId: 地图id或其数组,不填视为当前地图", + "!type": "fn(fromNumber: number, toNumber: number, floorId?: string|[string])" + }, + "drawBlock": { + "!doc": "绘制一个图块", + "!type": "fn(block?: block, animate?: number)" + }, + "resetMap": { + "!doc": "重置地图", + "!type": "fn(floorId?: string|[string])" + }, + "animateSetBlock": { + "!doc": "动画形式转变某点图块", + "!type": "fn(number: number|string, x: number, y: number, floorId?: string, time?: number, callback?: fn())" + }, + "animateSetBlocks": { + "!doc": "动画形式同时转变若干点图块", + "!type": "fn(number: number|string, locs: [?], floorId?: string, time?: number, callback?: fn())" + }, + "compressMap": { + "!doc": "压缩地图", + "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]" + }, + "enemyExists": { + "!doc": "某个点是否存在(指定的)怪物", + "!type": "fn(x: number, y: number, id?: string, floorId?: string) -> bool" + }, + "npcExists": { + "!doc": "某个点是否存在NPC", + "!type": "fn(x: number, y: number, floorId?: string) -> bool" + }, + "getBlockByNumber": { + "!doc": "根据数字获得图块", + "!type": "fn(number: number) -> block" + }, + "removeBlock": { + "!doc": "尝试删除一个图块,此函数会被打怪开门捡道具、“隐藏事件”指令和“移动/跳跃事件”指令的起点直接调用。
例如:core.removeBlock(0, 0); // 尝试删除地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + "!type": "fn(x: number, y: number, floorId?: string)" + }, + "hideBlock": { + "!doc": "隐藏(显示或隐藏的)图块,此函数不会被任何事件指令【直接】调用
例如:core.hideBlock(0, 0); // 隐藏地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + "!type": "fn(x: number, y: number, floorId?: string)" + }, + "removeBlockByIndex": { + "!doc": "根据block的索引删除该块", + "!type": "fn(index: number, floorId?: string)" + }, + "stairExists": { + "!doc": "某个点是否存在楼梯", + "!type": "fn(x: number, y: number, floorId?: string) -> bool" + }, + "decompressMap": { + "!doc": "解压缩地图", + "!type": "fn(mapArr: [[number]], floorId?: string) -> [[number]]" + }, + "automaticRoute": { + "!doc": "自动寻路
例如:core.automaticRoute(0, 0); // 自动寻路到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:每步走完后主角的loc属性组成的一维数组", + "!type": "fn(destX: number, destY: number) -> [loc]" + }, + "resizeMap": { + "!doc": "更改地图画布的尺寸", + "!type": "fn(floorId?: string)" + }, + "getFgNumber": { + "!doc": "判定前景层的一个位置是什么
例如:core.getFgNumber(core.status.hero.loc.x, core.status.hero.loc.y); // 判断主角脚下的前景层图块的数字
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: 步伐数组,前进和后退用于带朝向的npc
time: 单步和淡出用时,单位为毫秒。不填视为半秒
keep: 是否不淡出,true表示不淡出
callback: 移动或淡出后的回调函数,可选", + "!type": "fn(x: number, y: number, steps: [string], time?: number, keep?: bool, callback?: fn())" + }, + "getBgNumber": { + "!doc": "判定背景层的一个位置是什么,主要用于滑冰(167)
例如:core.getBgNumber(core.status.hero.loc.x, core.status.hero.loc.y); // 判断主角脚下的背景层图块的数字
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图
@param 可选,true表示不使用缓存", + "!type": "fn(x: number, y: number, floorId?: string, noCache?: bool) -> number" + }, + "getIdOfThis": { + "!doc": "获得当前事件点的ID", + "!type": "fn(id?: string) -> string" + }, + "searchBlock": { + "!doc": "搜索图块, 支持通配符和正则表达式
例如:core.searchBlock('*Door'); // 搜索当前地图的所有门
id: 图块id,支持星号表示任意多个(0个起)字符
floorId: 地图id,不填视为当前地图
showDisable: 隐藏点是否计入,true表示计入
返回值:一个详尽的数组,一般只用到其长度", + "!type": "fn(id: string, floorId?: string, showDisable?: bool) -> Array<{ floorId -> string, index -> number, x -> number, y -> number, block -> Block }>" + }, + "hideBgFgMap": { + "!doc": "隐藏前景/背景地图", + "!type": "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())" + }, + "getBlockInfo": { + "!doc": "获得某个图块或素材的信息,包括ID,cls,图片,坐标,faceIds等等", + "!type": "fn(block?: number|string|block) -> blockInfo" + }, + "canMoveDirectlyArray": { + "!doc": "获得某些点可否通行的信息", + "!type": "fn(locs?: [[number]])" + }, + "hideFloorImage": { + "!doc": "隐藏一个楼层贴图", + "!type": "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())" + }, + "extractBlocks": { + "!doc": "根据需求解析出blocks", + "!type": "fn(map?: [[number]], flags?: 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" + }, + "loadFloor": { + "!doc": "从文件或存档中加载某个楼层", + "!type": "fn(floorId?: string, map?: ?)" + }, + "generateMovableArray": { + "!doc": "可通行性判定
例如:core.generateMovableArray(); // 判断当前地图主角从各点能向何方向移动
floorId: 地图id,不填视为当前地图
x: 起点横坐标,不填视为挨个判定
y: 起点纵坐标,不填视为挨个判定
direction: 可选,必须和坐标一起使用。填写后将只检查是否可向该方向移动并返回布尔值
返回值:不设置坐标时为从各点可移动方向的三维数组,设置坐标但不设置方向时为该点可移动方向的一维数组,都设置时为布尔值", + "!type": "fn(floorId?: string, x?: number, y?: number, direction?: string)" + }, + "terrainExists": { + "!doc": "某个点是否存在(指定的)地形", + "!type": "fn(x: number, y: number, id?: string, floorId?: string) -> bool" + }, + "getBlockById": { + "!doc": "根据ID获得图块", + "!type": "fn(id: string) -> block" + }, + "drawBg": { + "!doc": "绘制背景层(含贴图,其与背景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawBg(); // 绘制当前地图的背景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", + "!type": "fn(floorId?: string, ctx?: CanvasRenderingContext2D)" + }, + "showBlock": { + "!doc": "显示(隐藏或显示的)图块,此函数将被“显示事件”指令和勾选了“不消失”的“移动/跳跃事件”指令(如阻击怪)的终点调用
例如:core.showBlock(0, 0); // 显示地图左上角的图块
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + "!type": "fn(x: number, y: number, floorId?: string)" + }, + "getMapBlocksObj": { + "!doc": "以x,y的形式返回每个点的事件", + "!type": "fn(floorId?: string, showDisable?: bool)" + }, + "removeGlobalAnimate": { + "!doc": "删除一个或所有全局动画", + "!type": "fn(x?: number, y?: number, name?: string)" + }, + "drawEvents": { + "!doc": "绘制事件层
例如:core.drawEvents(); // 绘制当前地图的事件层
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", + "!type": "fn(floorId?: string, blocks?: [block], ctx?: CanvasRenderingContext2D)" + }, + "canMoveDirectly": { + "!doc": "能否瞬移到某点,并求出节约的步数。此函数会无视可通行图块的script属性,如需使用该属性请手动禁止瞬移
例如:core.canMoveDirectly(0, 0); // 能否瞬移到地图左上角
destX: 目标点的横坐标
destY: 目标点的纵坐标
返回值:正数表示节约的步数,-1表示不可瞬移", + "!type": "fn(destX: number, destY: number) -> number" + }, + "saveMap": { + "!doc": "将当前地图重新变成数字,以便于存档", + "!type": "fn(floorId?: string)" + }, + "drawBoxAnimate": { + "!doc": "绘制UI层的box动画", + "!type": "fn()" + }, + "setBgFgBlock": { + "!doc": "转变图层块
例如:core.setBgFgBlock('bg', 167, 6, 6); // 把当前地图背景层的中心块改为滑冰
name: 背景还是前景
number: 新图层块的数字(也支持纯数字字符串如'1')或id
x: 横坐标
y: 纵坐标
floorId: 地图id,不填视为当前地图", + "!type": "fn(name: string, number: number|string, x: number, y: number, floorId?: string)" + }, + "drawFg": { + "!doc": "绘制前景层(含贴图,其与前景层矩阵的绘制顺序可通过复写此函数来改变)
例如:core.drawFg(); // 绘制当前地图的前景层
floorId: 地图id,不填视为当前地图
ctx: 某画布的ctx,用于绘制缩略图,一般不需要", + "!type": "fn(floorId?: string, ctx?: CanvasRenderingContext2D)" + }, + "getBlock": { + "!doc": "获得某个点的block", + "!type": "fn(x: number, y: number, floorId?: string, showDisable?: bool) -> block" + }, + "initBlock": { + "!doc": "初始化一个图块", + "!type": "fn(x: number, y: number, id: string|number, addInfo?: bool, eventFloor?: ?, flags?: ?) -> block" + }, + "addGlobalAnimate": { + "!doc": "添加一个全局动画", + "!type": "fn(block?: block)" + }, + "animateBlock": { + "!doc": "显示/隐藏某个块时的动画效果", + "!type": "fn(loc?: [number]|[[number]], type?: string, time?: number, callback?: fn())" + }, + "loadMap": { + "!doc": "将存档中的地图信息重新读取出来", + "!type": "fn(data?: ?, 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]]" + }, + "getBgMapArray": { + "!doc": "生成背景层矩阵
例如:core.getBgMapArray('MT0'); // 生成主塔0层的背景层矩阵,使用缓存
floorId: 地图id,不填视为当前地图
noCache: 可选,true表示不使用缓存
返回值:背景层矩阵,注意对其阵元的访问是[y][x]", + "!type": "fn(floorId?: string, noCache?: bool) -> [[number]]" + }, + "canMoveHero": { + "!doc": "单点单朝向的可通行性判定
@exmaple core.canMoveHero(); // 判断主角是否可以前进一步
x: 起点横坐标,不填视为主角当前的
y: 起点纵坐标,不填视为主角当前的
direction: 移动的方向,不填视为主角面对的方向
floorId: 地图id,不填视为当前地图
返回值:true表示可移动,false表示不可移动", + "!type": "fn(x?: number, y?: number, direction?: string, floorId?: string) -> bool" + }, + "drawThumbnail": { + "!doc": "绘制缩略图
例如:core.drawThumbnail(); // 绘制当前地图的缩略图
floorId: 地图id,不填视为当前地图
blocks: 一般不需要
options: 额外的绘制项,可选。可以增绘主角位置和朝向、采用不同于游戏中的主角行走图、增绘显伤、提供flags用于存读档
toDraw: 要绘制到的画布名或画布的ctx或还有其他信息,如起绘坐标、绘制大小、是否绘制全图、截取中心", + "!type": "fn(floorId?: string, blocks?: [block], options?: ?, toDraw?: string|CanvasRenderingContext2D|?)" + }, + "hideBlockByIndex": { + "!doc": "根据图块的索引来隐藏图块", + "!type": "fn(index?: number, floorId?: string)" + }, + "getNumberById": { + "!doc": "根据图块id得到数字(地图矩阵中的值)
例如:core.getNumberById('yellowWall'); // 1
id: 图块id
返回值:图块的数字,定义在project\\maps.js(请注意和project\\icons.js中的“图块索引”相区分!)", + "!type": "fn(id: string) -> number" + }, + "removeBlockByIndexes": { + "!doc": "一次性删除多个block", + "!type": "fn(indexes?: [number], floorId?: string)" + }, + "hideBlockByIndexes": { + "!doc": "一次性隐藏多个block", + "!type": "fn(indexes?: [number], floorId?: string)" + }, + "generateGroundPattern": { + "!doc": "生成groundPattern", + "!type": "fn(floorId?: string)" + }, + "showBgFgMap": { + "!doc": "显示前景/背景地图", + "!type": "fn(name?: string, loc?: [number]|[[number]], floorId?: string, callback?: fn())" + }, + "showFloorImage": { + "!doc": "显示一个楼层贴图", + "!type": "fn(loc?: [number]|[[number]], floorId?: string, callback?: fn())" + } + }, + "ui": { + "!doc": "UI绘制相关的函数", + "resizeCanvas": { + "!doc": "重新设置一个自定义画布的大小", + "!type": "fn(name: string, x: number, y: number)" + }, + "deleteCanvas": { + "!doc": "删除一个自定义画布", + "!type": "fn(name: string)" + }, + "drawSLPanel": { + "!doc": "绘制存档/读档界面", + "!type": "fn(index?: ?, refresh?: bool)" + }, + "drawKeyBoard": { + "!doc": "绘制虚拟键盘", + "!type": "fn()" + }, + "drawStorageRemove": { + "!doc": "绘制存档删除页面", + "!type": "fn()" + }, + "deleteAllCanvas": { + "!doc": "清空所有的自定义画布", + "!type": "fn()" + }, + "drawIcon": { + "!doc": "在某个canvas上绘制一个图标", + "!type": "fn(name: string|CanvasRenderingContext2D, id: string, x: number, y: number, w?: number, h?: number, frame?: number)" + }, + "drawFly": { + "!doc": "绘制楼层传送器", + "!type": "fn(page?: ?)" + }, + "setOpacity": { + "!doc": "设置某个canvas的透明度;尽量不要使用本函数,而是全部换成setAlpha实现", + "!type": "fn(name: string|CanvasRenderingContext2D, opacity: number)" + }, + "drawAbout": { + "!doc": "绘制“关于”界面", + "!type": "fn()" + }, + "getTextContentHeight": { + "!doc": "获得某段文字的预计绘制高度;参数说明详见 drawTextContent", + "!type": "fn(content: string, config?: ?)" + }, + "drawSwitchs": { + "!doc": "绘制系统设置界面", + "!type": "fn()" + }, + "drawSyncSelect": { + "!doc": "绘制存档同步选择页面", + "!type": "fn()" + }, + "drawArrow": { + "!doc": "在某个canvas上绘制一个箭头", + "!type": "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)" + }, + "drawReplay": { + "!doc": "绘制回放界面", + "!type": "fn()" + }, + "strokeEllipse": { + "!doc": "在某个canvas上绘制一个椭圆的边框", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string, lineWidth?: number)" + }, + "fillCircle": { + "!doc": "在某个canvas上绘制一个圆", + "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, style?: string)" + }, + "clearTip": { + "!doc": "清除左上角提示内容", + "!type": "fn()" + }, + "strokeRoundRect": { + "!doc": "在某个canvas上绘制一个圆角矩形的边框", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, lineWidth?: number, angle?: number)" + }, + "getContextByName": { + "!doc": "根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。
也可以传画布的context自身,则返回自己。", + "!type": "fn(canvas: string|CanvasRenderingContext2D) -> CanvasRenderingContext2D" + }, + "drawImage": { + "!doc": "在一个画布上绘制图片
后面的8个坐标参数与canvas的drawImage的八个参数完全相同。
name: 可以是系统画布之一,也可以是任意自定义动态创建的画布名 画布名称或者画布的context
image: 要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取;支持加':x',':y',':o'翻转),图片本身,或者一个画布。
angle:旋转角度", + "!url": "http://www.w3school.com.cn/html5/canvas_drawimage.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, image: string|image, x: number, y: number, w?: number, h?: number, x1?: number, y1?: number, w1?: number, h1?: number, angle?: number)" + }, + "drawTip": { + "!doc": "左上角绘制一段提示", + "!type": "fn(text: string, id?: string, frame?: number)" + }, + "drawBackground": { + "!doc": "绘制一个背景图,可绘制winskin或纯色背景;支持小箭头绘制", + "!type": "fn(left: string, top: string, right: string, bottom: string, posInfo?: {px: number, py: number, direction: string})" + }, + "fillEllipse": { + "!doc": "在某个canvas上绘制一个椭圆", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, a: number, b: number, angle?: number, style?: string)" + }, + "setFillStyle": { + "!doc": "设置某个canvas的绘制属性(如颜色等)", + "!url": "https://www.w3school.com.cn/tags/canvas_fillstyle.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" + }, + "drawText": { + "!doc": "地图中间绘制一段文字", + "!type": "fn(contents: string, callback?: fn())" + }, + "fillPolygon": { + "!doc": "在某个canvas上绘制一个多边形", + "!type": "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string)" + }, + "drawStatistics": { + "!doc": "绘制“数据统计”界面", + "!type": "fn(floorIds?: string)" + }, + "fillText": { + "!doc": "在某个画布上绘制一段文字
text: 要绘制的文本
style: 绘制的样式
font: 绘制的字体
最大宽度,超过此宽度会自动放缩", + "!url": "https://www.w3school.com.cn/tags/canvas_filltext.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, font?: string, maxWidth?: number)" + }, + "setTextBaseline": { + "!doc": "设置某个canvas的baseline", + "!url": "https://www.w3school.com.cn/tags/canvas_textbaseline.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, baseline: string)" + }, + "drawSettings": { + "!doc": "绘制系统菜单栏", + "!type": "fn()" + }, + "loadCanvas": { + "!doc": "加载某个canvas状态", + "!type": "fn(name: string|CanvasRenderingContext2D)" + }, + "splitLines": { + "!doc": "字符串自动换行的分割", + "!type": "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth?: number, font?: string)" + }, + "setAlpha": { + "!doc": "设置某个canvas的alpha值", + "!url": "https://www.w3school.com.cn/tags/canvas_globalalpha.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, alpha: number)" + }, + "setLineWidth": { + "!doc": "设置某个canvas的线宽度", + "!url": "https://www.w3school.com.cn/tags/canvas_linewidth.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, lineWidth: number)" + }, + "drawEquipbox": { + "!doc": "绘制装备界面", + "!type": "fn(index?: ?)" + }, + "drawTextBox": { + "!doc": "绘制一个对话框", + "!type": "fn(content: string, showAll?: bool)" + }, + "relocateCanvas": { + "!doc": "重新定位一个自定义画布", + "!type": "fn(name: string, x: number, y: number)" + }, + "closePanel": { + "!doc": "结束一切事件和绘制,关闭UI窗口,返回游戏进程", + "!type": "fn()" + }, + "textImage": { + "!doc": "文本图片化", + "!type": "fn(content: string, lineHeight?: number) -> image" + }, + "drawStatusBar": { + "!doc": "绘制状态栏", + "!type": "fn()" + }, + "setStrokeStyle": { + "!doc": "设置某个canvas边框属性", + "!url": "https://www.w3school.com.cn/tags/canvas_strokestyle.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, style: string)" + }, + "clearUI": { + "!doc": "清空UI层内容", + "!type": "fn()" + }, + "drawWindowSkin": { + "!doc": "绘制WindowSkin", + "!type": "fn(background: string, ctx: string|CanvasRenderingContext2D, x: number, y: number, w: string, h: string, direction?: string, px?: number, py?: number)" + }, + "drawGameInfo": { + "!doc": "绘制游戏信息界面", + "!type": "fn()" + }, + "fillRect": { + "!doc": "绘制一个矩形。style可选为绘制样式
text: 要绘制的文本
style: 绘制的样式
font: 绘制的字体", + "!url": "https://www.w3school.com.cn/tags/canvas_fillrect.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, text: string, x: number, y: number, style?: string, font?: string)" + }, + "drawScrollText": { + "!doc": "绘制滚动字幕", + "!type": "fn(content: string, time: number, lineHeight?: number, callback?: fn())" + }, + "strokePolygon": { + "!doc": "在某个canvas上绘制一个多边形的边框", + "!type": "fn(name: string|CanvasRenderingContext2D, nodes?: [[number]], style?: string, lineWidth?: number)" + }, + "strokeCircle": { + "!doc": "在某个canvas上绘制一个圆的边框", + "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: ?, style?: string, lineWidth?: number)" + }, + "drawLocalSaveSelect": { + "!doc": "绘制单存档界面", + "!type": "fn()" + }, + "drawWaiting": { + "!doc": "绘制等待界面", + "!type": "fn(text: string)" + }, + "setFont": { + "!doc": "设置某个canvas的文字字体", + "!url": "https://www.w3school.com.cn/tags/canvas_font.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, font: string)" + }, + "drawChoices": { + "!doc": "绘制一个选项界面", + "!type": "fn(content?: string, choices?: [?])" + }, + "setFontForMaxWidth": { + "!doc": "根据最大宽度自动缩小字体", + "!type": "fn(name: string|CanvasRenderingContext2D, text: string, maxWidth: number, font?: ?) -> string" + }, + "clearMap": { + "!doc": "清空某个画布图层
name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。(下同)
如果name也可以是'all',若为all则为清空所有系统画布。", + "!url": "https://www.w3school.com.cn/tags/canvas_clearrect.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, x?: number, y?: number, width?: number, height?: number)" + }, + "drawTextContent": { + "!doc": "绘制一段文字到某个画布上面
ctx: 要绘制到的画布
content: 要绘制的内容;转义字符不允许保留 \\t, \\b 和 \\f
config: 绘制配置项,目前暂时包含如下内容(均为可选)
left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右
fontSize:字体大小;lineHeight:行高;time:打字机间隔
返回值:绘制信息", + "!type": "fn(ctx: string|CanvasRenderingContext2D, content: string, config: ?)" + }, + "calWidth": { + "!doc": "计算某段文字的宽度", + "!url": "https://www.w3school.com.cn/tags/canvas_measuretext.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, text: string, font?: string) -> number" + }, + "fillArc": { + "!doc": "在某个canvas上绘制一个扇形", + "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string)" + }, + "drawWindowSelector": { + "!doc": "绘制选择光标", + "!type": "fn(background: ?, x: number, y: number, w: number, h: number)" + }, + "strokeArc": { + "!doc": "在某个canvas上绘制一段弧", + "!url": "https://www.w3school.com.cn/tags/canvas_arc.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, r: number, start: number, end: number, style?: string, lineWidth?: number)" + }, + "drawLine": { + "!doc": "在某个canvas上绘制一条线", + "!url": "https://www.w3school.com.cn/tags/canvas_lineto.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, style?: string, lineWidth?: number)" + }, + "drawPagination": { + "!doc": "绘制分页", + "!type": "fn(page?: ?, totalPage?: ?, y?: number)" + }, + "drawBookDetail": { + "!doc": "绘制怪物属性的详细信息", + "!type": "fn(index?: ?)" + }, + "drawToolbox": { + "!doc": "绘制道具栏", + "!type": "fn(index?: ?)" + }, + "drawHelp": { + "!doc": "绘制帮助页面", + "!type": "fn()" + }, + "drawQuickShop": { + "!doc": "绘制快捷商店选择栏", + "!type": "fn()" + }, + "drawCenterFly": { + "!doc": "绘制中心对称飞行器", + "!type": "fn()" + }, + "strokeRect": { + "!doc": "绘制一个矩形的边框
style: 绘制的样式", + "!url": "https://www.w3school.com.cn/tags/canvas_strokerect.asp", + "!type": "fn(name: CtxRefer, x: number, y: number, width: number, height: number, style: string)" + }, + "drawBook": { + "!doc": "绘制怪物手册", + "!type": "fn(index?: ?)" + }, + "fillRoundRect": { + "!doc": "在某个canvas上绘制一个圆角矩形", + "!type": "fn(name: string|CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, style?: string, angle?: number)" + }, + "fillBoldText": { + "!doc": "在某个画布上绘制一个描黑边的文字
text: 要绘制的文本
style: 绘制的样式
font: 绘制的字体", + "!type": "fn(name: CtxRefer, text: string, x: number, y: number, style: string, font: string)" + }, + "drawSyncSave": { + "!doc": "绘制存档同步界面", + "!type": "fn()" + }, + "saveCanvas": { + "!doc": "保存某个canvas状态", + "!type": "fn(name: string|CanvasRenderingContext2D)" + }, + "drawCursor": { + "!doc": "绘制键盘光标", + "!type": "fn()" + }, + "createCanvas": { + "!doc": "动态创建一个画布。name为要创建的画布名,如果已存在则会直接取用当前存在的。
x,y为创建的画布相对窗口左上角的像素坐标,width,height为创建的长宽。
zIndex为创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。
返回创建的画布的context,也可以通过core.dymCanvas[name]调用。
name:
x:
y:
width:
height:
zIndex:", + "!type": "fn(name: string, x: number, y: number, width: number, height: number, zIndex: number) -> CanvasRenderingContext2D" + }, + "setTextAlign": { + "!doc": "设置某个canvas的对齐", + "!url": "https://www.w3school.com.cn/tags/canvas_textalign.asp", + "!type": "fn(name: string|CanvasRenderingContext2D, align: string)" + }, + "drawMaps": { + "!doc": "绘制浏览地图界面", + "!type": "fn(index?: ?, x?: number, y?: number)" + } + }, + "enemys": { + "!doc": "怪物处理的相关函数", + "getSpecialHint": { + "!doc": "获得某种敌人的某种特殊属性的介绍
例如:core.getSpecialHint('bat', 1) // '先攻:怪物首先攻击'
enemy: 敌人id或敌人对象,用于确定属性的具体数值,否则可选
special: 属性编号,可以是该敌人没有的属性
返回值:属性的介绍,以属性名加中文冒号开头", + "!type": "fn(enemy: string|enemy, special: number) -> string" + }, + "getSpecialText": { + "!doc": "获得某种敌人的全部特殊属性名称
例如:core.getSpecialText('greenSlime') // ['先攻', '3连击', '破甲', '反击']
enemy: 敌人id或敌人对象,如core.material.enemys.greenSlime
返回值:字符串数组", + "!type": "fn(enemy: string|enemy) -> [string]" + }, + "hasSpecial": { + "!doc": "判定某种特殊属性的有无
例如:core.hasSpecial('greenSlime', 1) // 判定绿头怪有无先攻属性
special: 敌人id或敌人对象或正整数数组或自然数
test: 待检查的属性编号
", + "!type": "fn(special: number|[number]|string|number, test: number) -> bool" + }, + "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" + }, + "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}" + } + }, + "events": { + "!doc": "事件处理相关的函数", + "afterChangeFloor": { + "!doc": "转换楼层结束的事件", + "!type": "fn(floorId?: string)" + }, + "popEventLoc": { + "!doc": "将当前点坐标入栈", + "!type": "fn()" + }, + "afterOpenDoor": { + "!doc": "开一个门后触发的事件", + "!type": "fn(doorId?: string, x?: number, y?: number)" + }, + "checkLvUp": { + "!doc": "检查升级事件", + "!type": "fn()" + }, + "insertAction": { + "!doc": "插入事件
例如:core.insertAction('一段文字'); // 插入一个显示文章
action: 单个事件指令,或事件指令数组
x: 新的当前点横坐标,可选
y: 新的当前点纵坐标,可选
callback: 新的回调函数,可选
addToLast: 插入的位置,true表示插入到末尾,否则插入到开头", + "!type": "fn(action: string|?|[?], x?: number, y?: number, callback?: fn(), addToLast?: bool)" + }, + "unfollow": { + "!doc": "取消跟随", + "!type": "fn(name?: string)" + }, + "hasVisitedFloor": { + "!doc": "是否到达过某个楼层", + "!type": "fn(floorId?: string) -> bool" + }, + "startEvents": { + "!doc": "开始执行一系列自定义事件", + "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())" + }, + "setHeroIcon": { + "!doc": "更改主角行走图
例如:core.setHeroIcon('npc48.png', true); // 把主角从阳光变成样板0层左下角的小姐姐,但不立即刷新
name: 新的行走图文件名,可以是全塔属性中映射前的中文名。映射后会被存入core.status.hero.image
noDraw: true表示不立即刷新(刷新会导致大地图下视野重置到以主角为中心)", + "!type": "fn(name: string, noDraw?: bool)" + }, + "changingFloor": { + "!doc": "楼层转换中", + "!type": "fn(floorId?: string, heroLoc?: {x: number, y: number, direction: string})" + }, + "setEvents": { + "!doc": "直接设置事件列表", + "!type": "fn(list?: [?], x?: number, y?: number, callback?: fn())" + }, + "setValue": { + "!doc": "数值操作", + "!type": "fn(name: string, operator: string, value: ?, prefix?: string)" + }, + "precompile": { + "!doc": "预编辑事件", + "!type": "fn(data?: ?)" + }, + "vibrate": { + "!doc": "视野左右抖动
例如:core.vibrate(); // 视野左右抖动1秒
time: 抖动时长,单位为毫秒。必须为半秒的倍数,不填或小于1秒都视为1秒
callback: 抖动平息后的回调函数,可选", + "!type": "fn(time?: number, callback?: fn())" + }, + "confirmRestart": { + "!doc": "询问是否需要重新开始", + "!type": "fn()" + }, + "battle": { + "!doc": "战斗,如果填写了坐标就会删除该点的敌人并触发战后事件
例如:core.battle('greenSlime'); // 和从天而降的绿头怪战斗(如果打得过)
id: 敌人id,必填
x: 敌人的横坐标,可选
y: 敌人的纵坐标,可选
force: true表示强制战斗,可选
callback: 回调函数,可选", + "!type": "fn(id: string, x?: number, y?: number, force?: bool, callback?: fn())" + }, + "follow": { + "!doc": "跟随", + "!type": "fn(name: string)" + }, + "beforeBattle": { + "!doc": "战斗前触发的事件;返回false代表不进行战斗", + "!type": "fn(enemyId?: string, x?: number, y?: number) -> bool" + }, + "registerEvent": { + "!doc": "注册一个自定义事件
type: 事件类型
func: 事件的处理函数,可接受(data, x, y, prefix)参数
data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀", + "!type": "fn(type: string, func: fn(data: ?, x?: number, y?: number, prefix?: string))" + }, + "flyTo": { + "!doc": "飞往某一层", + "!type": "fn(toId?: string, callback?: fn()) -> bool" + }, + "afterGetItem": { + "!doc": "获得一个道具后的事件", + "!type": "fn(id?: string, x?: number, y?: number, isGentleClick?: bool)" + }, + "doAction": { + "!doc": "执行下一个事件指令,常作为回调
例如:core.setCurtain([0,0,0,1], undefined, core.doAction); // 事件中的原生脚本,配合勾选“不自动执行下一个事件”来达到此改变色调只持续到下次场景切换的效果
keepUI: true表示不清除UI画布和选择光标", + "!type": "fn(keepUI?: true)" + }, + "openBook": { + "!doc": "点击怪物手册时的打开操作", + "!type": "fn(fromUserAction?: bool)" + }, + "getNextItem": { + "!doc": "获得面前的物品(轻按)", + "!type": "fn(noRoute?: bool)" + }, + "hasAsync": { + "!doc": "当前是否有未处理完毕的异步事件", + "!type": "fn() -> bool" + }, + "openEquipbox": { + "!doc": "点击装备栏时的打开操作", + "!type": "fn(fromUserAction?: bool)" + }, + "recoverEvents": { + "!doc": "恢复一个事件", + "!type": "fn(data?: ?)" + }, + "setGlobalFlag": { + "!doc": "设置一个系统开关
例如:core.setGlobalFlag('steelDoorWithoutKey', true); // 使全塔的所有铁门都不再需要钥匙就能打开
name: 系统开关的英文名
value: 开关的新值,您可以用!core.flags[name]简单地表示将此开关反转", + "!type": "fn(name: string, value: bool)" + }, + "moveImage": { + "!doc": "移动一张图片并/或改变其透明度
例如:core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明
code: 图片编号
to: 新的左上角坐标,省略表示原地改变透明度
opacityVal: 新的透明度,省略表示不变
time: 移动用时,单位为毫秒。不填视为1秒
callback: 图片移动完毕后的回调函数,可选", + "!type": "fn(code: number, to?: [number], opacityVal?: number, time?: number, callback?: fn())" + }, + "openSettings": { + "!doc": "点击设置按钮时的操作", + "!type": "fn(fromUserAction?: bool)" + }, + "afterPushBox": { + "!doc": "推箱子后的事件", + "!type": "fn()" + }, + "unregisterSystemEvent": { + "!doc": "注销一个系统事件", + "!type": "fn(type: string)" + }, + "trigger": { + "!doc": "触发(x,y)点的事件", + "!type": "fn(x?: number, y?: number, callback?: fn())" + }, + "restart": { + "!doc": "重新开始游戏;此函数将回到标题页面", + "!type": "fn()" + }, + "doEvent": { + "!doc": "执行一个自定义事件", + "!type": "fn(data?: ?, x?: number, y?: number, prefix?: string)" + }, + "win": { + "!doc": "游戏获胜事件", + "!type": "fn(reason?: string, norank?: bool, noexit?: bool)" + }, + "setGlobalAttribute": { + "!doc": "设置全塔属性", + "!type": "fn(name: string, value: string)" + }, + "openToolbox": { + "!doc": "点击工具栏时的打开操作", + "!type": "fn(fromUserAction?: bool)" + }, + "setVolume": { + "!doc": "调节bgm的音量
例如:core.setVolume(0, 100, core.jumpHero); // 0.1秒内淡出bgm,然后主角原地跳跃半秒
value: 新的音量,为0或不大于1的正数。注意系统设置中是这个值的平方根的十倍
time: 渐变用时,单位为毫秒。不填或小于100毫秒都视为0
callback: 渐变完成后的回调函数,可选", + "!type": "fn(value: number, time?: number, callback?: fn())" + }, + "pushEventLoc": { + "!doc": "将当前点坐标入栈", + "!type": "fn(x?: number, y?: number, floorId?: string) -> bool" + }, + "openKeyBoard": { + "!doc": "点击虚拟键盘时的打开操作", + "!type": "fn(fromUserAction?: bool)" + }, + "insertCommonEvent": { + "!doc": "往当前事件列表之前或之后添加一个公共事件", + "!type": "fn(name?: string, args?: [?], x?: number, y?: number, callback?: fn(), addToLast?: bool)" + }, + "hideImage": { + "!doc": "隐藏一张图片
例如:core.hideImage(1, 1000, core.jumpHero); // 1秒内淡出1号图片,然后主角原地跳跃半秒
code: 图片编号
time: 淡出时间,单位为毫秒
callback: 图片完全消失后的回调函数,可选", + "!type": "fn(code: number, time?: number, callback?: fn())" + }, + "visitFloor": { + "!doc": "到达某楼层", + "!type": "fn(floorId?: string)" + }, + "openQuickShop": { + "!doc": "点击快捷商店按钮时的打开操作", + "!type": "fn(fromUserAction?: bool)" + }, + "afterBattle": { + "!doc": "战斗结束后触发的事件", + "!type": "fn(enemyId?: string, x?: number, y?: number)" + }, + "pushBox": { + "!doc": "推箱子", + "!type": "fn(data?: ?)" + }, + "autoEventExecuted": { + "!doc": "当前是否执行过某个自动事件", + "!type": "fn(symbol?: string, value?: ?) -> bool" + }, + "addValue": { + "!doc": "数值增减", + "!type": "fn(name: string, value: ?, prefix?: string)" + }, + "onSki": { + "!doc": "当前是否在冰上", + "!type": "fn(number?: number) -> bool" + }, + "showImage": { + "!doc": "显示一张图片
例如:core.showImage(1, core.material.images.images['winskin.png'], [0,0,128,128], [0,0,416,416], 0.5, 1000); // 裁剪winskin.png的最左边128×128px,放大到铺满整个视野,1秒内淡入到50%透明,编号为1
code: 图片编号,为不大于50的正整数,加上100后就是对应画布层的z值,较大的会遮罩较小的,注意色调层的z值为125,UI层为140
image: 图片文件名(可以是全塔属性中映射前的中文名)或图片对象(见上面的例子)
sloc: 一行且至多四列的数组,表示从原图裁剪的左上角坐标和宽高,可选
loc: 一行且至多四列的数组,表示图片在视野中的左上角坐标和宽高,可选
opacityVal: 不透明度,为小于1的正数。不填视为1
time: 淡入时间,单位为毫秒。不填视为0
callback: 图片完全显示出来后的回调函数,可选", + "!type": "fn(code: number, image: string|image, sloc?: [number], loc?: [number], opacityVal?: number, time?: number, callback?: fn())" + }, + "getItem": { + "!doc": "获得道具并提示,如果填写了坐标就会删除该点的该道具
例如:core.getItem('book'); // 获得敌人手册并提示
id: 道具id,必填
num: 获得的数量,不填视为1,填了就别填坐标了
x: 道具的横坐标,可选
y: 道具的纵坐标,可选
callback: 回调函数,可选", + "!type": "fn(id: string, num?: number, x?: number, y?: number, callback?: fn())" + }, + "registerSystemEvent": { + "!doc": "注册一个系统事件
type: 事件名
func: 为事件的处理函数,可接受(data,callback)参数", + "!type": "fn(type: string, func: fn(data?: ?, callback?: fn()))" + }, + "startGame": { + "!doc": "开始新游戏
例如:core.startGame('咸鱼乱撞', 0, ''); // 开始一局咸鱼乱撞难度的新游戏,随机种子为0
hard: 难度名,会显示在左下角(横屏)或右下角(竖屏)
seed: 随机种子,相同的种子保证了录像的可重复性
route: 经由base64压缩后的录像,用于从头开始的录像回放
callback: 回调函数,可选", + "!type": "fn(hard: string, seed: number, route: string, callback?: fn())" + }, + "doSystemEvent": { + "!doc": "执行一个系统事件", + "!type": "fn(type: string, data?: ?, callback?: fn())" + }, + "resetGame": { + "!doc": "初始化游戏", + "!type": "fn(hero?: ?, hard?: ?, floorId?: string, maps?: ?, values?: ?)" + }, + "setFloorInfo": { + "!doc": "设置一项楼层属性并刷新状态栏
例如:core.setFloorInfo('ratio', 2, 'MT0'); // 把主塔0层的血瓶和宝石变为双倍效果
name: 'title','name','canFlyTo','canUseQuickShop','cannotViewMap','cannotMoveDirectly','upFloor','downFloor','defaultGround','images','color','weather','bgm','ratio','underGround'之一
values: 属性的新值,可选。对'title'、'name'、'defaultGround'和'bgm'需要是字符串,对'underGround'和四个'canXxx'需要是布尔值,对两个'xxxFloor'需要是一行两列的自然数数组,对'ratio'需要是数字
floorId: 楼层id,不填视为当前层
prefix: 独立开关前缀,一般不需要,下同", + "!type": "fn(name: string, values: ?, floorId?: string, prefix?: string)" + }, + "openDoor": { + "!doc": "开门(包括三种基础墙)
例如:core.openDoor(0, 0, true, core.jumpHero); // 打开左上角的门,需要钥匙,然后主角原地跳跃半秒
x: 门的横坐标
y: 门的纵坐标
needKey: true表示需要钥匙,会导致机关门打不开
callback: 门完全打开后或打不开时的回调函数,可选", + "!type": "fn(x: number, y: number, needKey?: bool, callback?: fn())" + }, + "setEnemy": { + "!doc": "设置一项敌人属性并计入存档
例如:core.setEnemy('greenSlime', 'def', 0); // 把绿头怪的防御设为0
id: 敌人id
name: 属性的英文缩写
value: 属性的新值,可选
prefix: 独立开关前缀,一般不需要,下同", + "!type": "fn(id: string, name: string, value: ?, prefix?: string)" + }, + "autoEventExecuting": { + "!doc": "当前是否在执行某个自动事件", + "!type": "fn(symbol?: string, value?: ?) -> bool" + }, + "checkAutoEvents": { + "!doc": "检测自动事件", + "!type": "fn()" + }, + "showGif": { + "!doc": "绘制一张动图或擦除所有动图
例如:core.showGif(); // 擦除所有动图
name: 动图文件名,可以是全塔属性中映射前的中文名
x: 动图在视野中的左上角横坐标
y: 动图在视野中的左上角纵坐标", + "!type": "fn(name?: string, x?: number, y?: number)" + }, + "unregisterEvent": { + "!doc": "注销一个自定义事件", + "!type": "fn(type: string)" + }, + "jumpHero": { + "!doc": "主角跳跃,跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。
例如:core.jumpHero(); // 主角原地跳跃半秒
ex: 跳跃后的横坐标
ey: 跳跃后的纵坐标
time: 跳跃时长,单位为毫秒。不填视为半秒
callback: 跳跃完毕后的回调函数,可选", + "!type": "fn(ex?: number, ey?: number, time?: number, callback?: fn())" + }, + "closeDoor": { + "!doc": "关门,目标点必须为空地
例如:core.closeDoor(0, 0, 'yellowWall', core.jumpHero); // 在左上角关掉一堵黄墙,然后主角原地跳跃半秒
x: 横坐标
y: 纵坐标
id: 门的id,也可以用三种基础墙
callback: 门完全关上后的回调函数,可选", + "!type": "fn(x: number, y: number, id: string, callback?: fn())" + }, + "eventMoveHero": { + "!doc": "强制移动主角(包括后退),这个函数的作者已经看不懂这个函数了
例如:core.eventMoveHero(['forward'], 125, core.jumpHero); // 主角强制前进一步,用时1/8秒,然后主角原地跳跃半秒
steps: 步伐数组,注意后退时跟随者的行为会很难看
time: 每步的用时,单位为毫秒。0或不填则取主角的移速,如果后者也不存在就取0.1秒
callback: 移动完毕后的回调函数,可选", + "!type": "fn(steps: [step], time?: number, callback?: fn())" + }, + "changeFloor": { + "!doc": "场景切换
例如:core.changeFloor('MT0'); // 传送到主塔0层,主角坐标和朝向不变,黑屏时间取全塔属性中的值
floorId: 传送的目标地图id,可以填':before'和':after'分别表示楼下或楼上
stair: 传送的位置,可以填':now'(保持不变,可省略),':symmetry'(中心对称),':symmetry_x'(左右对称),':symmetry_y'(上下对称)或图块id(该图块最好在目标层唯一,一般为'downFloor'或'upFloor')
heroLoc: 传送的坐标(如果填写了,就会覆盖上述的粗略目标位置)和传送后主角的朝向(不填表示保持不变)
time: 传送的黑屏时间,单位为毫秒。可以填0(无黑屏)或不小于100的正整数,也可以省略(取全塔属性中的值)
callback: 黑屏结束后的回调函数,可选(这居然不是最后一个参数)
fromLoad: 本次场景切换是否为读档,读档会提示且没有黑屏,不会触发“每次到达事件”,也不会导致重生怪复活", + "!type": "fn(floorId: string, stair?: string, heroLoc?: {x?: number, y?: number, direction?: string}, time?: number, callback?: fn(), fromLoad?: bool)" + }, + "getCommonEvent": { + "!doc": "获得一个公共事件", + "!type": "fn(name: string) -> [?]" + }, + "lose": { + "!doc": "游戏失败事件", + "!type": "fn(reason?: string)" + }, + "gameOver": { + "!doc": "游戏结束
例如:core.gameOver(); // 游戏失败
ending: 结局名,省略表示失败
fromReplay: true表示在播放录像,可选
norank: true表示不计入榜单,可选", + "!type": "fn(ending?: string, fromReplay?: bool, norank?: bool)" + }, + "useFly": { + "!doc": "点击楼层传送器时的打开操作", + "!type": "fn(fromUserAction?: bool)" + }, + "tryUseItem": { + "!doc": "尝试使用一个道具
例如:core.tryUseItem('pickaxe'); // 尝试使用破墙镐
itemId: 道具id,其中敌人手册、传送器和飞行器会被特殊处理", + "!type": "fn(itemId: string)" + } } }, "hero": { diff --git a/_server/CodeMirror/tern.js b/_server/CodeMirror/tern.js index 0395bc67..7e823c4b 100644 --- a/_server/CodeMirror/tern.js +++ b/_server/CodeMirror/tern.js @@ -2492,7 +2492,7 @@ }); var getInstance = exports.getInstance = function(obj, ctor) { - if (ctor === false) return new Obj(obj); + if (ctor === false || obj == null) return new Obj(obj); if (!ctor) ctor = obj.hasCtor; if (!obj.instances) obj.instances = []; @@ -4305,531 +4305,6 @@ // Delayed initialization because of cyclic dependencies. def = exports.def = def.init({}, exports); }); -// Parses comments above variable declarations, function declarations, -// and object properties as docstrings and JSDoc-style type -// annotations. - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - return mod(require("../lib/infer"), require("../lib/tern"), require("../lib/comment"), - require("acorn"), require("acorn-walk")); - if (typeof define == "function" && define.amd) // AMD - return define(["../lib/infer", "../lib/tern", "../lib/comment", "acorn/dist/acorn", "acorn-walk/dist/walk"], mod); - mod(tern, tern, tern.comment, acorn, acorn.walk); -})(function(infer, tern, comment, acorn, walk) { - "use strict"; - - var WG_MADEUP = 1, WG_STRONG = 101; - - tern.registerPlugin("doc_comment", function(server, options) { - server.mod.jsdocTypedefs = Object.create(null); - server.on("reset", function() { - server.mod.jsdocTypedefs = Object.create(null); - }); - server.mod.docComment = { - weight: options && options.strong ? WG_STRONG : undefined, - fullDocs: options && options.fullDocs - }; - - server.on("postParse", postParse); - server.on("postInfer", postInfer); - server.on("postLoadDef", postLoadDef); - }); - - function postParse(ast, text) { - function attachComments(node) { comment.ensureCommentsBefore(text, node); } - - walk.simple(ast, { - VariableDeclaration: attachComments, - FunctionDeclaration: attachComments, - MethodDefinition: attachComments, - Property: attachComments, - AssignmentExpression: function(node) { - if (node.operator == "=") attachComments(node); - }, - CallExpression: function(node) { - if (isDefinePropertyCall(node)) attachComments(node); - }, - ExportNamedDeclaration: attachComments, - ExportDefaultDeclaration: attachComments, - ClassDeclaration: attachComments - }); - } - - function isDefinePropertyCall(node) { - return node.callee.type == "MemberExpression" && - node.callee.object.name == "Object" && - node.callee.property.name == "defineProperty" && - node.arguments.length >= 3 && - typeof node.arguments[1].value == "string"; - } - - function postInfer(ast, scope) { - jsdocParseTypedefs(ast.sourceFile.text, scope); - - walk.simple(ast, { - VariableDeclaration: function(node, scope) { - var decl = node.declarations[0].id; - if (node.commentsBefore && decl.type == "Identifier") - interpretComments(node, node.commentsBefore, scope, - scope.getProp(node.declarations[0].id.name)); - }, - FunctionDeclaration: function(node, scope) { - if (node.commentsBefore) - interpretComments(node, node.commentsBefore, scope, - scope.getProp(node.id.name), - node.scope.fnType); - }, - ClassDeclaration: function(node, scope) { - if (node.commentsBefore) - interpretComments(node, node.commentsBefore, scope, - scope.getProp(node.id.name), - node.objType); - }, - AssignmentExpression: function(node, scope) { - if (node.commentsBefore) - interpretComments(node, node.commentsBefore, scope, - infer.expressionType({node: node.left, state: scope})); - }, - ObjectExpression: function(node, scope) { - for (var i = 0; i < node.properties.length; ++i) { - var prop = node.properties[i]; - if (prop.type == 'SpreadElement') { continue; } - var name = infer.propName(prop); - if (name != "" && prop.commentsBefore) - interpretComments(prop, prop.commentsBefore, scope, node.objType.getProp(name)); - } - }, - Class: function(node, scope) { - if (!node.objType) return; - var proto = node.objType.getProp("prototype").getObjType(); - if (!proto) return; - for (var i = 0; i < node.body.body.length; i++) { - var method = node.body.body[i], name; - if (!method.commentsBefore) continue; - if (method.kind == "constructor") - interpretComments(method, method.commentsBefore, scope, node.objType); - else if ((name = infer.propName(method)) != "") - interpretComments(method, method.commentsBefore, scope, proto.getProp(name)); - } - }, - CallExpression: function(node, scope) { - if (node.commentsBefore && isDefinePropertyCall(node)) { - var type = infer.expressionType({node: node.arguments[0], state: scope}).getObjType(); - if (type && type instanceof infer.Obj) { - var prop = type.props[node.arguments[1].value]; - if (prop) interpretComments(node, node.commentsBefore, scope, prop); - } - } - }, - ExportNamedDeclaration: function(node, scope) { - if (node.commentsBefore && node.declaration && node.declaration.type === 'FunctionDeclaration') { - interpretComments(node.declaration, node.commentsBefore, scope, - scope.getProp(node.declaration.id.name), - node.declaration.scope.fnType); - } - }, - ExportDefaultDeclaration: function(node, scope) { - if (node.commentsBefore && node.declaration && node.declaration.type === 'FunctionDeclaration') { - interpretComments(node.declaration, node.commentsBefore, scope, - scope.getProp(node.declaration.id.name), - node.declaration.scope.fnType); - } - } - }, infer.searchVisitor, scope); - } - - function postLoadDef(data) { - var defs = data["!typedef"]; - var cx = infer.cx(), orig = data["!name"]; - if (defs) for (var name in defs) - cx.parent.mod.jsdocTypedefs[name] = - maybeInstance(infer.def.parse(defs[name], orig, name), name); - } - - // COMMENT INTERPRETATION - - function stripLeadingChars(lines) { - for (var head, i = 1; i < lines.length; i++) { - var line = lines[i], lineHead = line.match(/^[\s\*]*/)[0]; - if (lineHead != line) { - if (head == null) { - head = lineHead; - } else { - var same = 0; - while (same < head.length && head.charCodeAt(same) == lineHead.charCodeAt(same)) ++same; - if (same < head.length) head = head.slice(0, same); - } - } - } - lines = lines.map(function(line, i) { - line = line.replace(/\s+$/, ""); - if (i == 0 && head != null) { - for (var j = 0; j < head.length; j++) { - var found = line.indexOf(head.slice(j)); - if (found == 0) return line.slice(head.length - j); - } - } - if (head == null || i == 0) return line.replace(/^[\s\*]*/, ""); - if (line.length < head.length) return ""; - return line.slice(head.length); - }); - while (lines.length && !lines[lines.length - 1]) lines.pop(); - while (lines.length && !lines[0]) lines.shift(); - return lines; - } - - function interpretComments(node, comments, scope, aval, type) { - jsdocInterpretComments(node, scope, aval, comments); - var cx = infer.cx(); - - if (!type && aval instanceof infer.AVal && aval.types.length) { - type = aval.types[aval.types.length - 1]; - if (!(type instanceof infer.Obj) || type.origin != cx.curOrigin || type.doc) - type = null; - } - - for (var i = comments.length - 1; i >= 0; i--) { - var text = stripLeadingChars(comments[i].split(/\r\n?|\n/)).join("\n"); - if (text) { - if (aval instanceof infer.AVal) aval.doc = text; - if (type) type.doc = text; - break; - } - } - } - - // Parses a subset of JSDoc-style comments in order to include the - // explicitly defined types in the analysis. - - function skipSpace(str, pos) { - while (/\s/.test(str.charAt(pos))) ++pos; - return pos; - } - - function isIdentifier(string) { - if (!acorn.isIdentifierStart(string.charCodeAt(0))) return false; - for (var i = 1; i < string.length; i++) - if (!acorn.isIdentifierChar(string.charCodeAt(i))) return false; - return true; - } - - function parseLabelList(scope, str, pos, close) { - var labels = [], types = [], madeUp = false; - for (var first = true; ; first = false) { - pos = skipSpace(str, pos); - if (first && str.charAt(pos) == close) break; - var colon = str.indexOf(":", pos); - if (colon < 0) return null; - var label = str.slice(pos, colon); - if (!isIdentifier(label)) return null; - labels.push(label); - pos = colon + 1; - var type = parseType(scope, str, pos); - if (!type) return null; - pos = type.end; - madeUp = madeUp || type.madeUp; - types.push(type.type); - pos = skipSpace(str, pos); - var next = str.charAt(pos); - ++pos; - if (next == close) break; - if (next != ",") return null; - } - return {labels: labels, types: types, end: pos, madeUp: madeUp}; - } - - function parseTypeAtom(scope, str, pos) { - var result = parseTypeInner(scope, str, pos); - if (!result) return null; - if (str.slice(result.end, result.end + 2) == "[]") - return {madeUp: result.madeUp, end: result.end + 2, type: new infer.Arr(result.type)}; - else return result; - } - - function parseType(scope, str, pos) { - var type, union = false, madeUp = false; - for (;;) { - var inner = parseTypeAtom(scope, str, pos); - if (!inner) return null; - madeUp = madeUp || inner.madeUp; - if (union) inner.type.propagate(union); - else type = inner.type; - pos = skipSpace(str, inner.end); - if (str.charAt(pos) != "|") break; - pos++; - if (!union) { - union = new infer.AVal; - type.propagate(union); - type = union; - } - } - var isOptional = false; - if (str.charAt(pos) == "=") { - ++pos; - isOptional = true; - } - return {type: type, end: pos, isOptional: isOptional, madeUp: madeUp}; - } - - function parseTypeInner(scope, str, pos) { - pos = skipSpace(str, pos); - if (/[?!]/.test(str.charAt(pos))) pos++; - var type, madeUp = false; - - if (str.indexOf("function(", pos) == pos) { - var args = parseLabelList(scope, str, pos + 9, ")"), ret = infer.ANull; - if (!args) return null; - pos = skipSpace(str, args.end); - if (str.charAt(pos) == ":") { - ++pos; - var retType = parseType(scope, str, pos + 1); - if (!retType) return null; - pos = retType.end; - ret = retType.type; - madeUp = retType.madeUp; - } - type = new infer.Fn(null, infer.ANull, args.types, args.labels, ret); - } else if (str.charAt(pos) == "[") { - var inner = parseType(scope, str, pos + 1); - if (!inner) return null; - pos = skipSpace(str, inner.end); - madeUp = inner.madeUp; - if (str.charAt(pos) != "]") return null; - ++pos; - type = new infer.Arr(inner.type); - } else if (str.charAt(pos) == "{") { - var fields = parseLabelList(scope, str, pos + 1, "}"); - if (!fields) return null; - type = new infer.Obj(true); - for (var i = 0; i < fields.types.length; ++i) { - var field = type.defProp(fields.labels[i]); - field.initializer = true; - fields.types[i].propagate(field); - } - pos = fields.end; - madeUp = fields.madeUp; - } else if (str.charAt(pos) == "(") { - var inner = parseType(scope, str, pos + 1); - if (!inner) return null; - pos = skipSpace(str, inner.end); - if (str.charAt(pos) != ")") return null; - ++pos; - type = inner.type; - } else { - var start = pos; - if (!acorn.isIdentifierStart(str.charCodeAt(pos))) return null; - while (acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos; - if (start == pos) return null; - var word = str.slice(start, pos); - if (/^(number|integer)$/i.test(word)) type = infer.cx().num; - else if (/^bool(ean)?$/i.test(word)) type = infer.cx().bool; - else if (/^string$/i.test(word)) type = infer.cx().str; - else if (/^(null|undefined)$/i.test(word)) type = infer.ANull; - else if (/^array$/i.test(word)) { - var inner = null; - if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") { - var inAngles = parseType(scope, str, pos + 2); - if (!inAngles) return null; - pos = skipSpace(str, inAngles.end); - madeUp = inAngles.madeUp; - if (str.charAt(pos++) != ">") return null; - inner = inAngles.type; - } - type = new infer.Arr(inner); - } else if (/^object$/i.test(word)) { - type = new infer.Obj(true); - if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") { - var key = parseType(scope, str, pos + 2); - if (!key) return null; - pos = skipSpace(str, key.end); - if (str.charAt(pos++) != ",") return null; - var val = parseType(scope, str, pos); - if (!val) return null; - pos = skipSpace(str, val.end); - madeUp = key.madeUp || val.madeUp; - if (str.charAt(pos++) != ">") return null; - val.type.propagate(type.defProp("")); - } - } else { - while (str.charCodeAt(pos) == 46 || - acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos; - var path = str.slice(start, pos); - var cx = infer.cx(), defs = cx.parent && cx.parent.mod.jsdocTypedefs, found; - if (defs && (path in defs)) { - type = defs[path]; - } else if (found = infer.def.parsePath(path, scope).getObjType()) { - type = maybeInstance(found, path); - } else { - if (!cx.jsdocPlaceholders) cx.jsdocPlaceholders = Object.create(null); - if (!(path in cx.jsdocPlaceholders)) - type = cx.jsdocPlaceholders[path] = new infer.Obj(null, path); - else - type = cx.jsdocPlaceholders[path]; - madeUp = true; - } - } - } - - return {type: type, end: pos, madeUp: madeUp}; - } - - function maybeInstance(type, path) { - if (type instanceof infer.Fn && /(?:^|\.)[A-Z][^\.]*$/.test(path)) { - var proto = type.getProp("prototype").getObjType(); - if (proto instanceof infer.Obj) return infer.getInstance(proto); - } - return type; - } - - function parseTypeOuter(scope, str, pos) { - pos = skipSpace(str, pos || 0); - if (str.charAt(pos) != "{") return null; - var result = parseType(scope, str, pos + 1); - if (!result) return null; - var end = skipSpace(str, result.end); - if (str.charAt(end) != "}") return null; - result.end = end + 1; - return result; - } - - function jsdocInterpretComments(node, scope, aval, comments) { - var type, args, ret, foundOne, self, parsed; - - for (var i = 0; i < comments.length; ++i) { - var comment = comments[i]; - var decl = /(?:\n|\*)\s*@(type|param|arg(?:ument)?|returns?|this|class|constructor)(?:\s*?\n|\s+(.*))/g, m; - while (m = decl.exec(comment)) { - if (m[1] == "class" || m[1] == "constructor") { - self = foundOne = true; - continue; - } - - if (m[2] === undefined) continue; // to avoid tags that require a type argument. - - if (m[1] == "this" && (parsed = parseType(scope, m[2], 0))) { - self = parsed; - foundOne = true; - continue; - } - - if (!(parsed = parseTypeOuter(scope, m[2]))) continue; - foundOne = true; - - switch(m[1]) { - case "returns": case "return": - ret = parsed; break; - case "type": - type = parsed; break; - case "param": case "arg": case "argument": - // Possible jsdoc param name situations: - // employee - // [employee] - // [employee=John Doe] - // employee.name - // employees[].name - var name = m[2].slice(parsed.end).match(/^\s*(\[?)\s*([^\[\]\s=]+(\[\][^\[\]\s=]+)?)\s*(?:=[^\]]+\s*)?(\]?).*/); - if (!name) continue; - var argname = name[2] + (parsed.isOptional || (name[1] === '[' && name[4] === ']') ? "?" : ""); - - // Check to see if the jsdoc is indicating a property of a previously documented parameter - var isObjProp = false; - var parts = argname.split('.'); - if (args && parts.length == 2) { - var objname = parts[0]; - argname = parts[1]; - - // Go through each of the previously found parameter to find the - // object or array for which this new parameter should be a part - // of - var key, value; - for (key in args) { - value = args[key]; - - if (key === objname && value.type instanceof infer.Obj) { - isObjProp = true; - parsed.type.propagate(value.type.defProp(argname)); - } - else if (key + '[]' === objname && value.type instanceof infer.Arr) { - isObjProp = true; - parsed.type.propagate(value.type.getProp("").getType().defProp(argname)); - } - } - } - if (!isObjProp) { - (args || (args = Object.create(null)))[argname] = parsed; - } - break; - } - } - } - - if (foundOne) applyType(type, self, args, ret, node, aval); - } - - function jsdocParseTypedefs(text, scope) { - var cx = infer.cx(); - - var re = /\s@typedef\s+(.*)/g, m; - while (m = re.exec(text)) { - var parsed = parseTypeOuter(scope, m[1]); - var name = parsed && m[1].slice(parsed.end).match(/^\s*(\S+)/); - if (name && parsed.type instanceof infer.Obj) { - var rest = text.slice(m.index + m[0].length); - while (m = /\s+@prop(?:erty)?\s+(.*)/.exec(rest)) { - var propType = parseTypeOuter(scope, m[1]), propName; - if (propType && (propName = m[1].slice(propType.end).match(/^\s*(\S+)/))) - propType.type.propagate(parsed.type.defProp(propName[1])); - rest = rest.slice(m[0].length); - } - cx.parent.mod.jsdocTypedefs[name[1]] = parsed.type; - } - } - } - - function propagateWithWeight(type, target) { - var weight = infer.cx().parent.mod.docComment.weight; - type.type.propagate(target, weight || (type.madeUp ? WG_MADEUP : undefined)); - } - - function isFunExpr(node) { return node.type == "FunctionExpression" || node.type == "ArrowFunctionExpression" } - - function applyType(type, self, args, ret, node, aval) { - var fn; - if (node.type == "VariableDeclaration") { - var decl = node.declarations[0]; - if (decl.init && isFunExpr(decl.init)) fn = decl.init.scope.fnType; - } else if (node.type == "FunctionDeclaration") { - fn = node.scope.fnType; - } else if (node.type == "AssignmentExpression") { - if (isFunExpr(node.right)) - fn = node.right.scope.fnType; - } else if (node.type == "CallExpression" || node.type === "ClassDeclaration") { - } else { // An object property - if (isFunExpr(node.value)) fn = node.value.scope.fnType; - } - - if (fn && (args || ret || self)) { - if (args) for (var i = 0; i < fn.argNames.length; ++i) { - var name = fn.argNames[i], known = args[name]; - if (!known && (known = args[name + "?"])) - fn.argNames[i] += "?"; - if (known) propagateWithWeight(known, fn.args[i]); - } - if (ret) { - if (fn.retval == infer.ANull) fn.retval = new infer.AVal; - propagateWithWeight(ret, fn.retval); - } - if (self === true) { - var proto = fn.getProp("prototype").getObjType(); - self = proto && {type: infer.getInstance(proto, fn)}; - } - if (self) propagateWithWeight(self, fn.self); - } else if (type) { - propagateWithWeight(type, aval); - } - } -}); // When enabled, this plugin will gather (short) strings in your code, // and completing when inside a string will try to complete to // previously seen strings. Takes a single option, maxLength, which @@ -4890,603 +4365,3 @@ } }); - -(function(root, mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - return mod(exports, require("tern/lib/infer"), require("tern/lib/tern"), require("acorn/dist/walk")); - if (typeof define == "function" && define.amd) // AMD - return define(["exports", "tern/lib/infer", "tern/lib/tern", "acorn/dist/walk"], mod); - mod(root.tern || (root.tern = {}), tern, tern, acorn.walk); -})(this, function(exports, infer, tern, walk) { - "use strict"; - - var defaultRules = { - "UnknownProperty" : {"severity" : "warning"}, - "UnknownIdentifier" : {"severity" : "warning"}, - "NotAFunction" : {"severity" : "error"}, - "InvalidArgument" : {"severity" : "error"}, - "UnusedVariable" : {"severity" : "warning"}, - "UnknownModule" : {"severity" : "error"}, - "MixedReturnTypes": {"severity" : "warning"}, - "ObjectLiteral": {"severity" : "error"}, - "TypeMismatch": {"severity" : "warning"}, - "Array": {"severity" : "error"}, - "ES6Modules": {"severity" : "error"} - }; - - function makeVisitors(server, query, file, messages) { - - function addMessage(node, msg, severity) { - var error = makeError(node, msg, severity); - messages.push(error); - } - - function makeError(node, msg, severity) { - var error = {}; - var pos = getPosition(node); - - error.message = msg; - error.from = tern.outputPos(query, file, pos.start); - error.to = tern.outputPos(query, file, pos.end); - error.severity = severity; - - if (query.lineNumber) { - error.lineNumber = query.lineCharPositions - ? error.from.line - : tern.outputPos( - Object.extend({}, query, { lineCharPositions: true }), - file, - pos.start - ).line; - } - - if (!query.groupByFiles) error.file = file.name; - - return error; - } - - function getNodeName(node) { - if (node.callee) { - // This is a CallExpression node. - // We get the position of the function name. - return getNodeName(node.callee); - } else if (node.property) { - // This is a MemberExpression node. - // We get the name of the property. - return node.property.name; - } else { - return node.name; - } - } - - function getNodeValue(node) { - if (node.callee) { - // This is a CallExpression node. - // We get the position of the function name. - return getNodeValue(node.callee); - } else if (node.property) { - // This is a MemberExpression node. - // We get the value of the property. - return node.property.value; - } else { - if (node.type === "Identifier") { - var query = { - type: "definition", - start: node.start, - end: node.end - }; - var expr = tern.findQueryExpr(file, query); - var type = infer.expressionType(expr); - var objExpr = type.getType(); - if (objExpr && objExpr.originNode) return getNodeValue(objExpr.originNode); - return null; - } - return node.value; - } - } - - function getPosition(node) { - if(node.callee) { - // This is a CallExpression node. - // We get the position of the function name. - return getPosition(node.callee); - } - if(node.property) { - // This is a MemberExpression node. - // We get the position of the property. - return node.property; - } - return node; - } - - function getTypeName(type) { - if (!type) return "Unknown type"; - if (type.types) { - // multiple types - var types = type.types, s = ""; - for (var i = 0; i < types.length; i++) { - if (i > 0) s +="|"; - var t = getTypeName(types[i]); - if (t != "Unknown type") s+= t; - } - return s == "" ? "Unknown type" : s; - } - if (type.name) { - return type.name; - } - return (type.proto) ? type.proto.name : "Unknown type"; - } - - function hasProto(expectedType, name) { - if (!expectedType) return false; - if(!expectedType.proto) return false; - return expectedType.proto.name === name; - } - - function isRegexExpected(expectedType) { - return hasProto(expectedType, 'RegExp.prototype'); - } - - function isEmptyType(val) { - return (!val || (val.types && val.types.length == 0)); - } - - function compareType(expected, actual) { - if (isEmptyType(expected) || isEmptyType(actual)) return true; - if (expected.types) { - for (var i = 0; i < expected.types.length; i++) { - if (actual.types) { - for (var j = 0; j < actual.types.length; j++) { - if (compareType(expected.types[i], actual.types[j])) return true; - } - } else { - if (compareType(expected.types[i], actual.getType())) return true; - } - } - return false; - } else if (actual.types) { - for (var i = 0; i < actual.types.length; i++) { - if (compareType(expected.getType(), actual.types[i])) return true; - } - } - var expectedType = expected.getType(), actualType = actual.getType(); - if (!expectedType || !actualType) return true; - var currentProto = actualType.proto; - while(currentProto) { - if (expectedType.proto && expectedType.proto.name === currentProto.name) return true; - currentProto = currentProto.proto; - } - return false; - } - - function checkPropsInObject(node, expectedArg, actualObj, invalidArgument) { - var properties = node.properties, expectedObj = expectedArg.getType(); - for (var i = 0; i < properties.length; i++) { - var property = properties[i], key = property.key, prop = key && key.name, value = property.value; - if (prop) { - var expectedType = expectedObj.hasProp(prop); - if (!expectedType) { - // key doesn't exists - addMessage(key, "Invalid property at " + (i+1) + ": " + prop + " is not a property in " + getTypeName(expectedArg), invalidArgument.severity); - } else { - // test that each object literal prop is the correct type - var actualType = actualObj.props[prop]; - if (!compareType(expectedType, actualType)) { - addMessage(value, "Invalid property at " + (i+1) + ": cannot convert from " + getTypeName(actualType) + " to " + getTypeName(expectedType), invalidArgument.severity); - } - } - } - } - } - - function checkItemInArray(node, expectedArg, state, invalidArgument) { - var elements = node.elements, expectedType = expectedArg.hasProp(""); - for (var i = 0; i < elements.length; i++) { - var elt = elements[i], actualType = infer.expressionType({node: elt, state: state}); - if (!compareType(expectedType, actualType)) { - addMessage(elt, "Invalid item at " + (i+1) + ": cannot convert from " + getTypeName(actualType) + " to " + getTypeName(expectedType), invalidArgument.severity); - } - } - } - - function isObjectLiteral(type) { - var objType = type.getObjType(); - return objType && objType.proto && objType.proto.name == "Object.prototype"; - } - - function getFunctionLint(fnType) { - if (fnType.lint) return fnType.lint; - if (fnType.metaData) { - fnType.lint = getLint(fnType.metaData["!lint"]); - return fnType.lint; - }; - } - - function isFunctionType(type) { - if (type.types) { - for (var i = 0; i < type.types.length; i++) { - if (isFunctionType(type.types[i])) return true; - } - } - return type.proto && type.proto.name == "Function.prototype"; - } - - function validateCallExpression(node, state, c) { - var notAFunctionRule = getRule("NotAFunction"), invalidArgument = getRule("InvalidArgument"); - if (!notAFunctionRule && !invalidArgument) return; - var type = infer.expressionType({node: node.callee, state: state}); - if(type && !type.isEmpty()) { - // If type.isEmpty(), it is handled by MemberExpression/Identifier already. - - // An expression can have multiple possible (guessed) types. - // If one of them is a function, type.getFunctionType() will return it. - var fnType = type.getFunctionType(); - if(fnType == null) { - if (notAFunctionRule && !isFunctionType(type)) addMessage(node, "'" + getNodeName(node) + "' is not a function", notAFunctionRule.severity); - return; - } - var fnLint = getFunctionLint(fnType); - var continueLint = fnLint ? fnLint(node, addMessage, getRule) : true; - if (continueLint && fnType.args) { - // validate parameters of the function - if (!invalidArgument) return; - var actualArgs = node.arguments; - if (!actualArgs) return; - var expectedArgs = fnType.args; - for (var i = 0; i < expectedArgs.length; i++) { - var expectedArg = expectedArgs[i]; - if (actualArgs.length > i) { - var actualNode = actualArgs[i]; - if (isRegexExpected(expectedArg.getType())) { - var value = getNodeValue(actualNode); - if (value) { - try { - var regex = new RegExp(value); - } - catch(e) { - addMessage(actualNode, "Invalid argument at " + (i+1) + ": " + e, invalidArgument.severity); - } - } - } else { - var actualArg = infer.expressionType({node: actualNode, state: state}); - // if actual type is an Object literal and expected type is an object, we ignore - // the comparison type since object literal properties validation is done inside "ObjectExpression". - if (!(expectedArg.getObjType() && isObjectLiteral(actualArg))) { - if (!compareType(expectedArg, actualArg)) { - addMessage(actualNode, "Invalid argument at " + (i+1) + ": cannot convert from " + getTypeName(actualArg) + " to " + getTypeName(expectedArg), invalidArgument.severity); - } - } - } - } - } - } - } - } - - function validateAssignement(nodeLeft, nodeRight, rule, state) { - if (!nodeLeft || !nodeRight) return; - if (!rule) return; - var leftType = infer.expressionType({node: nodeLeft, state: state}), - rightType = infer.expressionType({node: nodeRight, state: state}); - if (!compareType(leftType, rightType)) { - addMessage(nodeRight, "Type mismatch: cannot convert from " + getTypeName(leftType) + " to " + getTypeName(rightType), rule.severity); - } - } - - function validateDeclaration(node, state, c) { - - function isUsedVariable(varNode, varState, file, srv) { - var name = varNode.name; - - for (var scope = varState; scope && !(name in scope.props); scope = scope.prev) {} - if (!scope) return false; - - var hasRef = false; - function searchRef(file) { - return function(node, scopeHere) { - if (node != varNode) { - hasRef = true; - throw new Error(); // throw an error to stop the search. - } - }; - } - - try { - if (scope.node) { - // local scope - infer.findRefs(scope.node, scope, name, scope, searchRef(file)); - } else { - // global scope - infer.findRefs(file.ast, file.scope, name, scope, searchRef(file)); - for (var i = 0; i < srv.files.length && !hasRef; ++i) { - var cur = srv.files[i]; - if (cur != file) infer.findRefs(cur.ast, cur.scope, name, scope, searchRef(cur)); - } - } - } catch(e) {}; - return hasRef; - } - - var unusedRule = getRule("UnusedVariable"), mismatchRule = getRule("TypeMismatch"); - if (!unusedRule && !mismatchRule) return; - switch(node.type) { - case "VariableDeclaration": - for (var i = 0; i < node.declarations.length; ++i) { - var decl = node.declarations[i], varNode = decl.id; - if (varNode.name != "✖") { - // unused variable - if (unusedRule && !isUsedVariable(varNode, state, file, server)) addMessage(varNode, "Unused variable '" + getNodeName(varNode) + "'", unusedRule.severity); - // type mismatch? - if (mismatchRule) validateAssignement(varNode, decl.init, mismatchRule, state); - } - } - break; - case "FunctionDeclaration": - if (unusedRule) { - var varNode = node.id; - if (varNode.name != "✖" && !isUsedVariable(varNode, state, file, server)) addMessage(varNode, "Unused function '" + getNodeName(varNode) + "'", unusedRule.severity); - } - break; - } - } - - function getArrType(type) { - if (type instanceof infer.Arr) { - return type.getObjType(); - } else if (type.types) { - for (var i = 0; i < type.types.length; i++) { - if (getArrType(type.types[i])) return type.types[i]; - } - } - } - - var visitors = { - VariableDeclaration: validateDeclaration, - FunctionDeclaration: validateDeclaration, - ReturnStatement: function(node, state, c) { - if (!node.argument) return; - var rule = getRule("MixedReturnTypes"); - if (!rule) return; - if (state.fnType && state.fnType.retval) { - var actualType = infer.expressionType({node: node.argument, state: state}), expectedType = state.fnType.retval; - if (!compareType(expectedType, actualType)) { - addMessage(node, "Invalid return type : cannot convert from " + getTypeName(actualType) + " to " + getTypeName(expectedType), rule.severity); - } - } - }, - // Detects expressions of the form `object.property` - MemberExpression: function(node, state, c) { - var rule = getRule("UnknownProperty"); - if (!rule) return; - var prop = node.property && node.property.name; - if (!prop || prop == "✖") return; - var type = infer.expressionType({node: node, state: state}); - var parentType = infer.expressionType({node: node.object, state: state}); - - if(node.computed) { - // Bracket notation. - // Until we figure out how to handle these properly, we ignore these nodes. - return; - } - - if(!parentType.isEmpty() && type.isEmpty()) { - // The type of the property cannot be determined, which means - // that the property probably doesn't exist. - - // We only do this check if the parent type is known, - // otherwise we will generate errors for an entire chain of unknown - // properties. - - // Also, the expression may be valid even if the parent type is unknown, - // since the inference engine cannot detect the type in all cases. - - var propertyDefined = false; - - // In some cases the type is unknown, even if the property is defined - if(parentType.types) { - // We cannot use parentType.hasProp or parentType.props - in the case of an AVal, - // this may contain properties that are not really defined. - parentType.types.forEach(function(potentialType) { - // Obj#hasProp checks the prototype as well - if(typeof potentialType.hasProp == 'function' && potentialType.hasProp(prop, true)) { - propertyDefined = true; - } - }); - } - - if(!propertyDefined) { - addMessage(node, "Unknown property '" + getNodeName(node) + "'", rule.severity); - } - } - }, - // Detects top-level identifiers, e.g. the object in - // `object.property` or just `object`. - Identifier: function(node, state, c) { - var rule = getRule("UnknownIdentifier"); - if (!rule) return; - var type = infer.expressionType({ - node: node, - state: state, - }); - - if (type.originNode != null || type.origin != null) { - // The node is defined somewhere (could be this node), - // regardless of whether or not the type is known. - } else if (type.isEmpty()) { - // The type of the identifier cannot be determined, - // and the origin is unknown. - addMessage(node, "Unknown identifier '" + getNodeName(node) + "'", rule.severity); - } else { - // Even though the origin node is unknown, the type is known. - // This is typically the case for built-in identifiers (e.g. window or document). - } - }, - // Detects function calls. - // `node.callee` is the expression (Identifier or MemberExpression) - // the is called as a function. - NewExpression: validateCallExpression, - CallExpression: validateCallExpression, - AssignmentExpression: function(node, state, c) { - var rule = getRule("TypeMismatch"); - validateAssignement(node.left, node.right, rule, state); - }, - ObjectExpression: function(node, state, c) { - // validate properties of the object literal - var rule = getRule("ObjectLiteral"); - if (!rule) return; - var actualType = node.objType; - var ctxType = infer.typeFromContext(file.ast, {node: node, state: state}), expectedType = null; - if (ctxType instanceof infer.Obj) { - expectedType = ctxType.getObjType(); - } else if (ctxType && ctxType.makeupType) { - var objType = ctxType.makeupType(); - if (objType && objType.getObjType()) { - expectedType = objType.getObjType(); - } - } - if (expectedType && expectedType != actualType) { - // expected type is known. Ex: config object of RequireJS - checkPropsInObject(node, expectedType, actualType, rule); - } - }, - ArrayExpression: function(node, state, c) { - // validate elements of the Arrray - var rule = getRule("Array"); - if (!rule) return; - //var actualType = infer.expressionType({node: node, state: state}); - var ctxType = infer.typeFromContext(file.ast, {node: node, state: state}), expectedType = getArrType(ctxType); - if (expectedType /*&& expectedType != actualType*/) { - // expected type is known. Ex: config object of RequireJS - checkItemInArray(node, expectedType, state, rule); - } - }, - ImportDeclaration: function(node, state, c) { - // Validate ES6 modules from + specifiers - var rule = getRule("ES6Modules"); - if (!rule) return; - var me = infer.cx().parent.mod.modules; - if (!me) return; // tern plugin modules.js is not loaded - var source = node.source; - if (!source) return; - // Validate ES6 modules "from" - var modType = me.getModType(source); - if (!modType || modType == infer.ANull) { - addMessage(source, "Invalid modules from '" + source.value + "'", rule.severity); - return; - } - // Validate ES6 modules "specifiers" - var specifiers = node.specifiers, specifier; - if (!specifiers) return; - for (var i = 0; i < specifiers.length; i++) { - var specifier = specifiers[i], imported = specifier.imported; - if (imported) { - var name = imported.name, type = modType.getType(); - if (type && !type.hasProp(name)) addMessage(imported, "Invalid modules specifier '" + getNodeName(imported) + "'", rule.severity); - } - } - } - }; - - return visitors; - } - - // Adapted from infer.searchVisitor. - // Record the scope and pass it through in the state. - // VariableDeclaration in infer.searchVisitor breaks things for us. - var scopeVisitor = walk.make({ - Function: function(node, _st, c) { - var scope = node.scope; - if (node.id) c(node.id, scope); - for (var i = 0; i < node.params.length; ++i) - c(node.params[i], scope); - c(node.body, scope, "ScopeBody"); - }, - Statement: function(node, st, c) { - c(node, node.scope || st) - } - }); - - // Validate one file - - var validateFile = exports.validateFile = function(server, query, file) { - try { - var messages = []; - var ast = file.ast; - var state = file.scope; - var visitors = makeVisitors(server, query, file, messages); - walk.simple(ast, visitors, infer.searchVisitor, state); - return { messages: messages }; - } catch(err) { - console.error(err.stack); - return { messages: [] }; - } - } - - tern.defineQueryType("lint", { - takesFile: true, - run: function(server, query, file) { - return validateFile(server, query, file); - } - }); - - // Validate the whole files of the server - - var validateFiles = exports.validateFiles = function(server, query) { - try { - var messages = [], files = server.files, groupByFiles = query.groupByFiles == true; - for (var i = 0; i < files.length; ++i) { - var messagesFile = groupByFiles ? [] : messages, file = files[i], ast = file.ast, state = file.scope; - var visitors = makeVisitors(server, query, file, messagesFile); - walk.simple(ast, visitors, infer.searchVisitor, state); - if (groupByFiles) messages.push({file:file.name, messages: messagesFile}); - } - return {messages: messages}; - } catch(err) { - console.error(err.stack); - return {messages: []}; - } - } - - tern.defineQueryType("lint-full", { - run: function(server, query) { - return validateFiles(server, query); - } - }); - - var lints = Object.create(null); - tern.registerLint = function(name, lint) { - lints[name] = lint; - }; - - var getLint = tern.getLint = function(name) { - if (!name) return null; - return lints[name]; - } - - tern.registerPlugin("lint", function(server, options) { - server._lint = { - rules: getRules(options) - }; - return { - passes: {}, - loadFirst: true - }; - }); - - function getRules(options) { - var rules = {}; - for(var ruleName in defaultRules) { - if (options && options.rules && options.rules[ruleName] && options.rules[ruleName].severity) { - if (options.rules[ruleName].severity != 'none') rules[ruleName] = options.rules[ruleName]; - } else { - rules[ruleName] = defaultRules[ruleName]; - } - } - return rules; - } - - function getRule(ruleName) { - var cx = infer.cx(), server = cx.parent, rules = server._lint.rules; - return rules[ruleName]; - } -}); \ No newline at end of file diff --git a/_server/editor_multi.js b/_server/editor_multi.js index 15c47e39..8239aa21 100644 --- a/_server/editor_multi.js +++ b/_server/editor_multi.js @@ -31,13 +31,13 @@ editor_multi = function () { }); Object.keys(core.material.bgms).forEach(function (name) { coredef.core.material.bgms[name] = { - "!type": "?", + "!type": "audio", "!doc": "背景音乐" } }); Object.keys(core.material.sounds).forEach(function (name) { coredef.core.material.sounds[name] = { - "!type": "?", + "!type": "audio", "!doc": "音效" } }); @@ -50,7 +50,7 @@ editor_multi = function () { Object.keys(core.material.images).forEach(function (name) { if (core.material.images[name] instanceof Image) { coredef.core.material.images[name] = { - "!type": "?", + "!type": "image", "!doc": "系统图片" } } else { @@ -59,7 +59,7 @@ editor_multi = function () { } for (var v in core.material.images[name]) { coredef.core.material.images[name][v] = { - "!type": "?", + "!type": "image", } } } @@ -74,7 +74,7 @@ editor_multi = function () { functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys.getSpecials().forEach(function (one) { var name = one[1]; if (name instanceof Function) name = name({}); - coredef.core.hasSpecial["!doc"] += name + "(" + one[0] + "); "; + coredef.core.enemys.hasSpecial["!doc"] += name + "(" + one[0] + "); "; }); Object.keys(core.canvas).forEach(function (name) { coredef.core.canvas[name] = { @@ -104,6 +104,23 @@ editor_multi = function () { Object.keys(core.status.textAttribute).forEach(function (id) { coredef.core.status.textAttribute[id] = {}; }); + // --- 转发函数 + for (var name in coredef.core) { + if (typeof coredef.core[name] === 'object') { + for (var funcname in coredef.core[name]) { + var one = coredef.core[name][funcname] || {}; + var type = one["!type"] || ""; + if (type.startsWith("fn(")) { + coredef.core[funcname] = { + "!type": one["!type"], + "!doc": one["!doc"] + "
(转发到 " + name + " 中)" + }; + if (one["!url"]) coredef.core[funcname]["!url"] = one["!url"]; + } + } + } + } + Object.keys(core.values).forEach(function (id) { var one = data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.values._data[id]; if (!one) return;