From 0023a16007e1020710d2fde537d69e9c940f9ffd Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Tue, 26 Mar 2019 14:27:10 -0400 Subject: [PATCH 01/64] Update refactoring.md --- _server/refactoring.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/_server/refactoring.md b/_server/refactoring.md index 5d92d0f4..0dd94dd4 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -10,32 +10,34 @@ + [x] editor_blockly 图块化事件编辑器, 基本不改动 + [x] editor_multi 多行文本编辑器, 基本不改动 -+ [ ] editor_table 处理表格的生成, 及其响应的事件, 从原editor\_mode中分离 ++ [x] editor_table 处理表格的生成, 及其响应的事件, 从原editor\_mode中分离 + [ ] editor_file 调用fs.js编辑文件, 把原editor\_file模块化 + [ ] editor_game 处理来自core的数据, 导入为editor的数据, 从原editor中分离 -+ [ ] editor_util 生成guid等函数, 从editor分离 ++ [x] editor_util 生成guid等函数, 从editor分离 + [ ] editor 执行初始化流程加组合各组件 - + [ ] 原editor_mode 移除 + [ ] 原vm 移除 --- -对象结构 ++ [ ] 对象结构 ``` editor: { __proto__: { - blockly: 组件 - multi: 组件 - file: 组件 - table: 组件 - util: 组件 + fs + util + file + table + multi + blockly } game: 来自游戏的数据 config: 编辑器配置 mode: 当前的模式(左侧的选择) map: 当前编辑层的地图 + isMobile: 编辑器是否是手机端 + currentFloorData: 当前编辑的楼层数据 ... } ``` From 90407e94a09587ab654a600cf724b0abbb3c7262 Mon Sep 17 00:00:00 2001 From: oc Date: Fri, 29 Mar 2019 00:43:26 +0800 Subject: [PATCH 02/64] drawToolbox --- libs/actions.js | 85 +++++++++++----------- libs/ui.js | 182 +++++++++++++++++++++--------------------------- 2 files changed, 124 insertions(+), 143 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index c8e9baeb..31089b5b 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1234,49 +1234,47 @@ actions.prototype._keyUpQuickShop = function (keycode) { ////// 工具栏界面时的点击操作 ////// actions.prototype._clickToolbox = function (x, y) { // 装备栏 - if (x >= 10 && x <= 12 && y == 0) { + if (x >= this.LAST - 2 && y == 0) { core.ui.closePanel(); core.openEquipbox(); return; } - // 返回 - if (x >= 10 && x <= 12 && y == 12) { + if (x >= this.LAST - 2 && y == this.LAST) { core.ui.closePanel(); return; } + var toolsPage = core.status.event.data.toolsPage; var constantsPage = core.status.event.data.constantsPage; // 上一页 - if (x == 3 || x == 4) { - if (y == 7 && toolsPage > 1) { + if (x == this.HSIZE-2 || x == this.HSIZE-3) { + if (y == this.LAST - 5 && toolsPage > 1) { core.status.event.data.toolsPage--; core.ui.drawToolbox(core.status.event.selection); } - if (y == 12 && constantsPage > 1) { + if (y == this.LAST && constantsPage > 1) { core.status.event.data.toolsPage--; core.ui.drawToolbox(core.status.event.selection); } } // 下一页 - if (x == 8 || x == 9) { - if (y == 7 && toolsPage < Math.ceil(Object.keys(core.status.hero.items.tools).length / 12)) { + if (x == this.HSIZE+2 || x == this.HSIZE+3) { + if (y == this.LAST - 5 && toolsPage < Math.ceil(Object.keys(core.status.hero.items.tools).length / this.LAST)) { core.status.event.data.toolsPage++; core.ui.drawToolbox(core.status.event.selection); } - if (y == 12 && constantsPage < Math.ceil(Object.keys(core.status.hero.items.constants).length / 12)) { + if (y == this.LAST && constantsPage < Math.ceil(Object.keys(core.status.hero.items.constants).length / this.LAST)) { core.status.event.data.constantsPage++; core.ui.drawToolbox(core.status.event.selection); } } var index = parseInt(x / 2); - ; - if (y == 4) index += 0; - else if (y == 6) index += 6; - else if (y == 9) index += 12; - else if (y == 11) index += 18; + if (y == this.LAST - 8) index += 0; + else if (y == this.LAST - 6) index += this.HSIZE; + else if (y == this.LAST - 3) index += this.LAST; + else if (y == this.LAST - 1) index += this.LAST + this.HSIZE; else index = -1; - if (index >= 0) this._clickToolboxIndex(index); } @@ -1285,12 +1283,12 @@ actions.prototype._clickToolbox = function (x, y) { actions.prototype._clickToolboxIndex = function (index) { var items = null; var select; - if (index < 12) { - select = index + 12 * (core.status.event.data.toolsPage - 1); + if (index < this.LAST) { + select = index + this.LAST * (core.status.event.data.toolsPage - 1); items = Object.keys(core.status.hero.items.tools).sort(); } else { - select = index % 12 + 12 * (core.status.event.data.constantsPage - 1); + select = index % this.LAST + this.LAST * (core.status.event.data.constantsPage - 1); items = Object.keys(core.status.hero.items.constants).sort(); } if (items == null) return; @@ -1308,33 +1306,35 @@ actions.prototype._clickToolboxIndex = function (index) { actions.prototype._keyDownToolbox = function (keycode) { if (core.status.event.data == null) return; + var last_index = this.LAST - 1; + var tools = Object.keys(core.status.hero.items.tools).sort(); var constants = Object.keys(core.status.hero.items.constants).sort(); var index = core.status.event.selection; var toolsPage = core.status.event.data.toolsPage; var constantsPage = core.status.event.data.constantsPage; - var toolsTotalPage = Math.ceil(tools.length / 12); - var constantsTotalPage = Math.ceil(constants.length / 12); - var toolsLastIndex = toolsPage < toolsTotalPage ? 11 : (tools.length + 11) % 12; - var constantsLastIndex = 12 + (constantsPage < constantsTotalPage ? 11 : (constants.length + 11) % 12); + var toolsTotalPage = Math.ceil(tools.length / this.LAST); + var constantsTotalPage = Math.ceil(constants.length / this.LAST); + var toolsLastIndex = toolsPage < toolsTotalPage ? last_index : (tools.length + last_index) % this.LAST; + var constantsLastIndex = this.LAST + (constantsPage < constantsTotalPage ? last_index : (constants.length + last_index) % this.LAST); if (keycode == 37) { // left if (index == 0) { // 处理向前翻页 if (toolsPage > 1) { core.status.event.data.toolsPage--; - index = 11; + index = last_index; } else return; // 第一页不向前翻 } - else if (index == 12) { + else if (index == this.LAST) { if (constantsPage == 1) { if (toolsTotalPage == 0) return; core.status.event.data.toolsPage = toolsTotalPage; - index = (tools.length + 11) % 12; + index = (tools.length + last_index) % this.LAST; } else { core.status.event.data.constantsPage--; - index = 23; + index = 2 * this.LAST - 1; } } else index -= 1; @@ -1342,29 +1342,29 @@ actions.prototype._keyDownToolbox = function (keycode) { return; } if (keycode == 38) { // up - if (index >= 12 && index <= 17) { // 进入tools + if (index >= this.LAST && index < this.LAST + this.HSIZE) { // 进入tools if (toolsTotalPage == 0) return; - if (toolsLastIndex >= 6) index = Math.min(toolsLastIndex, index - 6); - else index = Math.min(toolsLastIndex, index - 12); + if (toolsLastIndex >= this.HSIZE) index = Math.min(toolsLastIndex, index - this.HSIZE); + else index = Math.min(toolsLastIndex, index - this.LAST); } - else if (index < 6) return; // 第一行没有向上 - else index -= 6; + else if (index < this.HSIZE) return; // 第一行没有向上 + else index -= this.HSIZE; this._clickToolboxIndex(index); return; } if (keycode == 39) { // right - if (toolsPage < toolsTotalPage && index == 11) { + if (toolsPage < toolsTotalPage && index == last_index) { core.status.event.data.toolsPage++; index = 0; } - else if (constantsPage < constantsTotalPage && index == 23) { + else if (constantsPage < constantsTotalPage && index == 2 * this.LAST - 1) { core.status.event.data.constantsPage++; - index = 12; + index = this.LAST; } else if (index == toolsLastIndex) { if (constantsTotalPage == 0) return; core.status.event.data.constantsPage = 1; - index = 12; + index = this.LAST; } else if (index == constantsLastIndex) // 一个物品无操作 return; @@ -1374,16 +1374,17 @@ actions.prototype._keyDownToolbox = function (keycode) { } if (keycode == 40) { // down var nextIndex = null; - if (index <= 5) { - if (toolsLastIndex > 5) nextIndex = Math.min(toolsLastIndex, index + 6); - else index += 6; + if (index < this.HSIZE) { + if (toolsLastIndex >= this.HSIZE) nextIndex = Math.min(toolsLastIndex, index + this.HSIZE); + else index += this.HSIZE; } - if (nextIndex == null && index <= 11) { + if (nextIndex == null && index < this.LAST) { if (constantsTotalPage == 0) return; - nextIndex = Math.min(index + 6, constantsLastIndex); + nextIndex = Math.min(index + this.HSIZE, constantsLastIndex); } - if (nextIndex == null && index <= 17) { - if (constantsLastIndex > 17) nextIndex = Math.min(constantsLastIndex, index + 6); + if (nextIndex == null && index < this.LAST + this.HSIZE) { + if (constantsLastIndex >= this.LAST + this.HSIZE) + nextIndex = Math.min(constantsLastIndex, index + this.HSIZE); } if (nextIndex != null) { this._clickToolboxIndex(nextIndex); diff --git a/libs/ui.js b/libs/ui.js index e145f275..294c2043 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1710,149 +1710,129 @@ ui.prototype._drawMaps_buildData = function (index, x, y) { ////// 绘制道具栏 ////// ui.prototype.drawToolbox = function(index) { - // 设定eventdata - if (!core.isset(core.status.event.data) || !core.isset(core.status.event.data.toolsPage) || !core.isset(core.status.event.data.constantsPage)) - core.status.event.data = {"toolsPage":1, "constantsPage":1, "selectId":null} + var info = this._drawToolbox_getInfo(index); + this._drawToolbox_drawBackground(); + // 绘制线 + core.setAlpha('ui', 1); + core.setStrokeStyle('ui', '#DDDDDD'); + core.canvas.ui.lineWidth = 2; + core.canvas.ui.strokeWidth = 2; + core.setTextAlign('ui', 'right'); + var line1 = this.PIXEL - 306; + this._drawToolbox_drawLine(line1, "消耗道具"); + var line2 = this.PIXEL - 146; + this._drawToolbox_drawLine(line2, "永久道具"); + + this._drawToolbox_drawDescription(info, line1); + + this._drawToolbox_drawContent(info, line1, info.tools, info.toolsPage, true); + this.drawPagination(info.toolsPage, info.toolsTotalPage, this.LAST - 5); + this._drawToolbox_drawContent(info, line2, info.constants, info.constantsPage); + this.drawPagination(info.constantsPage, info.constantsTotalPage); + + core.setTextAlign('ui', 'center'); + core.fillText('ui', '[装备栏]', this.PIXEL - 46, 25, '#DDDDDD', this._buildFont(15, true)); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13,'#DDDDDD'); +} + +ui.prototype._drawToolbox_getInfo = function (index) { + // 设定eventdata + if (!core.status.event.data || core.status.event.data.toolsPage == null) + core.status.event.data = {"toolsPage":1, "constantsPage":1, "selectId":null} // 获取物品列表 var tools = Object.keys(core.status.hero.items.tools).sort(); var constants = Object.keys(core.status.hero.items.constants).sort(); - // 处理页数 var toolsPage = core.status.event.data.toolsPage; var constantsPage = core.status.event.data.constantsPage; - var toolsTotalPage = Math.ceil(tools.length/12); - var constantsTotalPage = Math.ceil(constants.length/12); - + var toolsTotalPage = Math.ceil(tools.length/this.LAST); + var constantsTotalPage = Math.ceil(constants.length/this.LAST); // 处理index - if (!core.isset(index)) { - if (tools.length>0) index=0; - else if (constants.length>0) index=12; - else index=0; - } + if (index == null) + index = tools.length == 0 && constants.length > 0 ? this.LAST : 0; core.status.event.selection=index; - // 确认选择对象 - var select; - var selectId; - if (index<12) { - select = index + (toolsPage-1)*12; + var select, selectId; + if (index=tools.length) select=Math.max(0, tools.length-1); selectId = tools[select]; } else { - select = index%12 + (constantsPage-1)*12; + select = index%this.LAST + (constantsPage-1)*this.LAST; if (select>=constants.length) select=Math.max(0, constants.length-1); selectId = constants[select]; } if (!core.hasItem(selectId)) selectId=null; core.status.event.data.selectId=selectId; + return { + index: index, tools: tools, constants: constants, toolsPage: toolsPage, constantsPage: constantsPage, + toolsTotalPage: toolsTotalPage, constantsTotalPage: constantsTotalPage, selectId: selectId + }; +} +ui.prototype._drawToolbox_drawBackground = function () { // 绘制 core.clearMap('ui'); core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, 416, 416, '#000000'); - core.setAlpha('ui', 1); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000'); +} + +ui.prototype._drawToolbox_drawLine = function (yoffset, text) { core.setFillStyle('ui', '#DDDDDD'); - core.setStrokeStyle('ui', '#DDDDDD'); - core.canvas.ui.lineWidth = 2; - core.canvas.ui.strokeWidth = 2; - - var ydelta = 20; - - // 画线 core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 130-ydelta); - core.canvas.ui.lineTo(416, 130-ydelta); + core.canvas.ui.moveTo(0, yoffset); + core.canvas.ui.lineTo(this.PIXEL, yoffset); core.canvas.ui.stroke(); core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,129-ydelta); - core.canvas.ui.lineTo(416,105-ydelta); - core.canvas.ui.lineTo(416-72,105-ydelta); - core.canvas.ui.lineTo(416-102,129-ydelta); + core.canvas.ui.moveTo(this.PIXEL, yoffset-1); + core.canvas.ui.lineTo(this.PIXEL, yoffset-25); + core.canvas.ui.lineTo(this.PIXEL-72, yoffset-25); + core.canvas.ui.lineTo(this.PIXEL-102, yoffset-1); core.canvas.ui.fill(); + core.fillText('ui', text, this.PIXEL - 5, yoffset-6, '#333333', this._buildFont(16, true)); +} - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 290-ydelta); - core.canvas.ui.lineTo(416, 290-ydelta); - core.canvas.ui.stroke(); - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,289-ydelta); - core.canvas.ui.lineTo(416,265-ydelta); - core.canvas.ui.lineTo(416-72,265-ydelta); - core.canvas.ui.lineTo(416-102,289-ydelta); - core.canvas.ui.fill(); - - // 文字 - core.setTextAlign('ui', 'right'); - var globalFont = core.status.globalAttribute.font; - core.fillText('ui', "消耗道具", 411, 124-ydelta, '#333333', "bold 16px "+globalFont); - core.fillText('ui', "永久道具", 411, 284-ydelta); - +ui.prototype._drawToolbox_drawDescription = function (info, max_height) { core.setTextAlign('ui', 'left'); // 描述 - if (core.isset(selectId)) { - var item=core.material.items[selectId]; - core.fillText('ui', item.name, 10, 32, '#FFD700', "bold 20px "+globalFont) - + if (info.selectId) { + var item=core.material.items[info.selectId]; + core.fillText('ui', item.name, 10, 32, '#FFD700', this._buildFont(20, true)) var text = item.text||"该道具暂无描述。"; try { // 检查能否eval text = core.replaceText(text); } catch (e) {} - - var lines = core.splitLines('ui', text, 406, '17px '+globalFont); - - core.fillText('ui', lines[0], 10, 62, '#FFFFFF', '17px '+globalFont); - - if (lines.length==1) { - core.fillText('ui', '<继续点击该道具即可进行使用>', 10, 89, '#CCCCCC', '14px '+globalFont); + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(17, false)); + // --- 开始逐行绘制 + var curr = 62, line_height = 25; + core.setFillStyle('ui', '#FFFFFF'); + for (var i=0;i=max_height) break; } - else { - var leftText = text.substring(lines[0].length); - core.fillText('ui', leftText, 10, 89, '#FFFFFF', '17px '+globalFont); + if (curr < max_height) { + core.fillText('ui', '<继续点击该道具即可进行使用>', 10, curr, '#CCCCCC', this._buildFont(14, false)); } } +} +ui.prototype._drawToolbox_drawContent = function (info, line, items, page, drawCount) { core.setTextAlign('ui', 'right'); - var images = core.material.images.items; - - // 消耗道具 - for (var i=0;i<12;i++) { - var tool=tools[12*(toolsPage-1)+i]; - if (!core.isset(tool)) break; - var yoffset = 144 + Math.floor(i/6)*54 + 5 - ydelta; - var icon=core.material.icons.items[tool]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(4*(i%6)+1)+5, yoffset, 32, 32) - // 个数 - core.fillText('ui', core.itemCount(tool), 16*(4*(i%6)+1)+40, yoffset+33, '#FFFFFF', "bold 14px "+globalFont); - if (selectId == tool) - core.strokeRect('ui', 16*(4*(i%6)+1)+1, yoffset-4, 40, 40, '#FFD700'); + for (var i = 0; i < this.LAST; i++) { + var item = items[this.LAST * (page - 1) + i]; + if (!item) continue; + var yoffset = line + 54 * Math.floor(i / this.HSIZE) + 19; + var icon = core.material.icons.items[item], image = core.material.images.items; + core.drawImage('ui', image, 0, 32 * icon, 32, 32, 64 * (i % this.HSIZE) + 21, yoffset, 32, 32); + if (drawCount) + core.fillText('ui', core.itemCount(item), 64 * (i % this.HSIZE) + 56, yoffset + 33, '#FFFFFF', this._buildFont(14, true)); + if (info.selectId == item) + core.strokeRect('ui', 64 * (i % this.HSIZE) + 17, yoffset - 4, 40, 40, '#FFD700'); } - - // 永久道具 - for (var i=0;i<12;i++) { - var constant=constants[12*(constantsPage-1)+i]; - if (!core.isset(constant)) break; - var yoffset = 304+Math.floor(i/6)*54+5-ydelta; - var icon=core.material.icons.items[constant]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(4*(i%6)+1)+5, yoffset, 32, 32) - if (selectId == constant) - core.strokeRect('ui', 16*(4*(i%6)+1)+1, yoffset-4, 40, 40, '#FFD700'); - } - - // 分页 - this.drawPagination(toolsPage, toolsTotalPage, 7); - this.drawPagination(constantsPage, constantsTotalPage, 12); - - core.setTextAlign('ui', 'center'); - - // 装备栏 - // if (core.flags.equipment) - core.fillText('ui', '[装备栏]', 370, 25,'#DDDDDD', 'bold 15px '+globalFont); - // core.fillText('ui', '删除道具', 370, 32,'#DDDDDD', 'bold 15px '+globalFont); - // 退出 - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); } ////// 绘制装备界面 ////// From 46aa0c82788a8773876d753cca990c198786cbf4 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 00:30:56 +0800 Subject: [PATCH 03/64] drawSL --- libs/actions.js | 190 +++++++++++++++++++-------------------------- libs/control.js | 107 +++++++++++++++++++------ libs/core.js | 2 + libs/events.js | 6 +- libs/ui.js | 202 ++++++++++++++++-------------------------------- 5 files changed, 234 insertions(+), 273 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index 18e1f560..96008b98 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1578,11 +1578,9 @@ actions.prototype._keyUpEquipbox = function (keycode, altKey) { } ////// 存读档界面时的点击操作 ////// -actions.prototype._clickSL = function (x, y, px, py) { +actions.prototype._clickSL = function (x, y) { var page = core.status.event.data.page, offset = core.status.event.data.offset; - var index = page*10 + offset; - //var index = core.status.event.data; - //var page = parseInt(index / 10), offset = index % 10; + var index = page * 10 + offset; // 上一页 if ((x == this.HSIZE-2 || x == this.HSIZE-3) && y == this.LAST) { @@ -1596,13 +1594,11 @@ actions.prototype._clickSL = function (x, y, px, py) { } // 返回 if (x >= this.LAST-2 && y == this.LAST) { - if (core.events.recoverEvents(core.status.event.interval)) { + if (core.events.recoverEvents(core.status.event.interval)) return; - } core.ui.closePanel(); - if (!core.isPlaying()) { + if (!core.isPlaying()) core.showStartAnimate(true); - } return; } // 删除 @@ -1611,36 +1607,31 @@ actions.prototype._clickSL = function (x, y, px, py) { core.status.event.selection = !core.status.event.selection; core.ui.drawSLPanel(index); } - else {// 显示收藏 + else { // 显示收藏 core.status.event.data.mode = core.status.event.data.mode == 'all'?'fav':'all'; - core.saves.index = {}; - for(var i in core.saves.favorite){ - core.saves.index[i] = core.saves.favorite[i]; + if (core.status.event.data.mode == 'fav') + core.ui.drawSLPanel(1, true); + else { + page = parseInt((core.saves.saveIndex-1)/5); + offset = core.saves.saveIndex-5*page; + core.ui.drawSLPanel(10*page + offset); } - core.ui.drawSLPanel(index,true); } return; } - // 收藏 - var fav = null; - var centerX = parseInt(this.SIZE/2), leftX = 2, rightX = this.LAST-2; - - // 三个关键坐标: - var xLeft = parseInt(this.SIZE/3),xRight = parseInt(this.SIZE*2/3); - var topY1 = 0, topY2 = parseInt(this.SIZE/2); - + // 点存档名 + var xLeft = parseInt(this.SIZE/3), xRight = parseInt(this.SIZE*2/3); + var topY1 = 0, topY2 = this.HSIZE; if(y==topY1){ - if (x >= xLeft && x < xRight) fav = 5 * page + 1; - if (x >= xRight) fav = 5 * page + 2; + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 1); + if (x >= xRight) return this._clickSL_favorite(page, 2); } if(y==topY2){ - if (x < xLeft) fav = 5 * page + 3; - if (x >= xLeft && x < xRight) fav = 5 * page + 4; - if (x >= xRight) fav = 5 * page + 5; - } - if (fav != null){ - this._keyDownFav(page,fav%5); + if (x < xLeft) return this._clickSL_favorite(page, 3); + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 4); + if (x >= xRight) return this._clickSL_favorite(page, 5); } + var id = null; var topSpan = parseInt(this.SIZE/7); if (y >= topY1 + topSpan && y <= topY1 + topSpan + 3) { @@ -1655,30 +1646,52 @@ actions.prototype._clickSL = function (x, y, px, py) { } if (id != null) { if (core.status.event.selection) { - if (id == 'autoSave') { + if (id == 'autoSave') core.drawTip("无法删除自动存档!"); - } else { - // core.removeLocalStorage("save"+id); - core.removeLocalForage("save" + id, function () { - var idx = core.saves.favorite.indexOf(id); - core.saves.favorite.splice(idx,1); - delete core.saves.favName[id]; - core.ui._drawSLPanel_saveFav(function(){ - core.ui._drawSLPanel_flushIndex(); - core.ui.drawSLPanel(index, true)}); - }, function () { - core.drawTip("无法删除存档!"); - }) + core.removeSave(id, function () { + core.ui.drawSLPanel(index, true); + }); } } else { - if(core.status.event.data.mode=='fav')id = core.saves.favIndex[id]; + if(core.status.event.data.mode == 'fav' && id != 'autoSave') + id = core.saves.favorite[id - 1]; core.doSL(id, core.status.event.id); } } } +actions.prototype._clickSL_favorite = function (page, offset) { + if (offset == 0) return; + var index = 5 * page + offset; + if (core.status.event.data.mode == 'fav') { // 收藏模式下点击的下标直接对应favorite + index = core.saves.favorite[index - 1]; + core.myprompt("请输入想要显示的存档名(长度不超过5字符)", null, function (value) { + if(value && value.length <= 5){ + core.saves.favoriteName[index] = value; + core.control._updateFavoriteSaves(); + core.drawSLPanel(10 * page + offset); + } else if (value) { + alert("无效的输入!"); + } + }); + } else { + var v = core.saves.favorite.indexOf(index); + if (v >= 0) { // 已经处于收藏状态:取消收藏 + core.saves.favorite.splice(v, 1); + delete core.saves.favoriteName[index]; + } + else if (core.hasSave(index)) { // 存在存档则进行收藏 + core.saves.favorite.push(index); + core.saves.favorite = core.saves.favorite.sort(function (a,b) {return a-b;}); // 保证有序 + core.drawTip("收藏成功!"); + } + core.control._updateFavoriteSaves(); + core.ui.drawSLPanel(10 * page + offset); + } +} + ////// 存读档界面时,按下某个键的操作 ////// actions.prototype._keyDownSL = function (keycode) { @@ -1730,76 +1743,30 @@ actions.prototype._keyDownSL = function (keycode) { core.ui.drawSLPanel(10 * (page + 1) + offset); return; } - if (keycode == 70){ // F - this._keyDownFav(page,offset); - } -} -actions.prototype._keyDownFav = function(page, offset){ - var fav = page*5+offset; - var idx = fav; - var index = page*10 + offset; - if(core.status.event.data.mode=='fav'){//收藏模式下点击的下标直接对应favorite - fav = core.saves.favIndex[idx]; - var dataIdx = index; - core.myprompt("请输入想要显示的存档名(长度不超过5字符)", null, function (index) { - if(index && index.length<=5 && index.length>0){ - core.saves.favName[fav]=index; - core.ui._drawSLPanel_saveFav(function(){core.ui.drawSLPanel(dataIdx, false)}); - }else{ - alert("无效的输入!"); - } - }); - }else{ - idx = core.saves.favorite.indexOf(fav); - if(idx>=0){ - core.saves.favorite.splice(idx,1); - delete core.saves.favName[fav]; - }else{ - if(core.hasSave(fav)){ - core.saves.favorite.push(fav); - core.saves.favName[idx] = fav;//暂时的 收藏下标到名字的映射(实际存储的是收藏ID到名字的映射) - } - } - core.ui._drawSLPanel_saveFav(function(){ - core.ui._drawSLPanel_flushIndex(); - core.ui.drawSLPanel(index, false)}); - } } ////// 存读档界面时,放开某个键的操作 ////// actions.prototype._keyUpSL = function (keycode) { var page = core.status.event.data.page, offset = core.status.event.data.offset; - var index = page*10 + offset; + var index = page * 10 + offset; - if (keycode == 27 || keycode == 88 || (core.status.event.id == 'save' && keycode == 83) || (core.status.event.id == 'load' && keycode == 68)) { - if (core.events.recoverEvents(core.status.event.interval)) { - return; - } - core.ui.closePanel(); - if (!core.isPlaying()) { - core.showStartAnimate(true); - } + if (keycode == 27 || keycode == 88 || (core.status.event.id == 'save' && keycode == 83) + || (core.status.event.id == 'load' && keycode == 68)) { + this._clickSL(this.LAST, this.LAST); return; } if (keycode == 13 || keycode == 32 || keycode == 67) { - if (offset == 0) { + if (offset == 0) core.doSL("autoSave", core.status.event.id); - } else { var id = 5 * page + offset; - if(core.status.event.data.mode=='fav')id = core.saves.favIndex[id]; + if(core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; core.doSL(id, core.status.event.id); } return; } if (keycode == 69 && core.status.event.id != 'save') { // E 收藏切换 - core.status.event.data.mode = core.status.event.data.mode == 'all'?'fav':'all'; - core.saves.index = {}; - for(var i in core.saves.favorite){ - core.saves.index[i] = core.saves.favorite[i]; - } - core.ui.drawSLPanel(core.saves.saveIndex,true); - + this._clickSL(0, this.LAST); return; } if (keycode == 46) { @@ -1807,19 +1774,16 @@ actions.prototype._keyUpSL = function (keycode) { core.drawTip("无法删除自动存档!"); } else { - core.removeLocalForage("save" + (5 * page + offset), function () { - var id = 5 * page + offset; - var idx = core.saves.favorite.indexOf(id); - core.saves.favorite.splice(idx,1); - delete core.saves.favName[id]; - core.ui._drawSLPanel_saveFav(function(){ - core.ui._drawSLPanel_flushIndex(); - core.ui.drawSLPanel(index, true)}); - }, function () { - core.drawTip("无法删除存档!"); - }) + var id = 5 * page + offset; + if(core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.removeSave(id, function () { + core.ui.drawSLPanel(index, true); + }); } } + if (keycode == 70 && core.status.event.data.mode == 'all') { // F + this._clickSL_favorite(page, offset); + } } @@ -2098,7 +2062,7 @@ actions.prototype._clickLocalSaveSelect = function (x, y) { var selection = y - topIndex; core.status.event.selection = selection; if (selection < 2) { - core.control.getSaves(selection == 0 ? null : core.saves.saveIndex, function (saves) { + core.getAllSaves(selection == 0 ? null : core.saves.saveIndex, function (saves) { if (saves) { var content = { "name": core.firstData.name, @@ -2154,9 +2118,12 @@ actions.prototype._clickStorageRemove_all = function () { core.saves.autosave.data = null; core.saves.autosave.updated = false; core.ui.closePanel(); - core.drawText("\t[操作成功]你的所有存档已被清空。"); core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]你的所有存档已被清空。"); }; if (core.platform.useLocalForage) { core.ui.drawWaiting("正在清空,请稍后..."); @@ -2175,9 +2142,12 @@ actions.prototype._clickStorageRemove_current = function () { core.saves.autosave.data = null; core.saves.autosave.updated = false; core.ui.closePanel(); - core.drawText("\t[操作成功]当前塔的存档已被清空。"); core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]当前塔的存档已被清空。"); } if (core.platform.useLocalForage) { core.ui.drawWaiting("正在清空,请稍后..."); diff --git a/libs/control.js b/libs/control.js index 9b5272d9..7f9b5324 100644 --- a/libs/control.js +++ b/libs/control.js @@ -1608,7 +1608,7 @@ control.prototype._doSL_replayLoad_afterGet = function (id, data) { ////// 同步存档到服务器 ////// control.prototype.syncSave = function (type) { core.ui.drawWaiting("正在同步,请稍后..."); - core.control.getSaves(type=='all'?null:core.saves.saveIndex, function (saves) { + core.getAllSaves(type=='all'?null:core.saves.saveIndex, function (saves) { if (!saves) return core.drawText("没有要同步的存档"); core.control._syncSave_http(type, saves); }) @@ -1702,32 +1702,54 @@ control.prototype.loadData = function (data, callback) { return this.controldata.loadData(data, callback); } -control.prototype.getSaves = function (index, callback) { - if (index != null) { - core.getLocalForage("save"+index, null, function(data) { - if (callback) callback(data); - }, function(err) { - main.log(err); - if (callback) callback(null); - }) +control.prototype.getSave = function (index, callback) { + if (index == 0) { + // --- 自动存档先从缓存中获取 + if (core.saves.autosave.data != null) + callback(core.clone(core.saves.autosave.data)); + else { + core.getLocalForage("autoSave", null, function(data) { + callback(data); + }, function(err) { + main.log(err); + callback(null); + }); + } return; } - var ids = Object.keys(core.saves.ids).filter(function(x){return x!=0;}) - .sort(function(a,b) {return a-b;}), number = ids.length, saves = []; - var load = function (index, callback) { - if (index > number) { - if (callback) callback(saves); - return; - } - core.getLocalForage("save"+ids[index], null, function (data) { - saves.push(data); - load(index+1, callback); - }, function(err) { - main.log(err); - load(index+1, callback); - }) + core.getLocalForage("save"+index, null, function(data) { + if (callback) callback(data); + }, function(err) { + main.log(err); + if (callback) callback(null); + }); +} + +control.prototype.getSaves = function (ids, callback) { + if (!(ids instanceof Array)) return this.getSave(ids, callback); + var count = ids.length, data = {}; + for (var i = 0; i < ids.length; ++i) { + (function (i) { + core.getSave(ids[i], function (result) { + data[i] = result; + if (Object.keys(data).length == count) + callback(data); + }) + })(i); } - load(0, callback); +} + +control.prototype.getAllSaves = function (id, callback) { + if (id != null) return this.getSave(id, callback); + var ids = Object.keys(core.saves.ids).filter(function(x){return x!=0;}) + .sort(function(a,b) {return a-b;}), saves = []; + this.getSaves(ids, function (data) { + for (var i = 0; i < ids.length; ++i) { + if (data[i] != null) + saves.push(data[i]); + } + callback(saves); + }); } ////// 获得所有存在存档的存档位 ////// @@ -1761,6 +1783,43 @@ control.prototype.hasSave = function (index) { return core.saves.ids[index] || false; } +////// 删除一个或多个存档 +control.prototype.removeSave = function (index, callback) { + if (index == 0 || index == "autoSave") { + index = "autoSave"; + core.removeLocalForage(index, function () { + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + if (callback) callback(); + }); + return; + } + core.removeLocalForage("save" + index, function () { + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + delete core.saves.favoriteName[index]; + core.control._updateFavoriteSaves(); + if (callback) callback(); + }, function () { + core.drawTip("无法删除存档!"); + if (callback) callback(); + }); +} + +////// 读取收藏信息 +control.prototype._loadFavoriteSaves = function () { + core.saves.favorite = core.getLocalStorage("favorite", []); + // --- 移除不存在的收藏 + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + core.saves.favoriteName = core.getLocalStorage("favoriteName", {}); +} + +control.prototype._updateFavoriteSaves = function () { + core.setLocalStorage("favorite", core.saves.favorite); + core.setLocalStorage("favoriteName", core.saves.favoriteName); +} + +////// 加载某个存档 + // ------ 属性,状态,位置,buff,变量,锁定控制等 ------ // ////// 设置勇士属性 ////// diff --git a/libs/core.js b/libs/core.js index 4ef06a69..fb6d0077 100644 --- a/libs/core.js +++ b/libs/core.js @@ -67,6 +67,7 @@ function core() { 'isPC': true, // 是否是PC 'isAndroid': false, // 是否是Android 'isIOS': false, // 是否是iOS + 'string': 'PC', 'isWeChat': false, // 是否是微信 'isQQ': false, // 是否是QQ 'isChrome': false, // 是否是Chrome @@ -272,6 +273,7 @@ core.prototype._init_platform = function () { core.platform.isPC = false; } }); + core.platform.string = core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""; core.platform.supportCopy = document.queryCommandSupported || document.queryCommandSupported("copy"); var chrome = /Chrome\/(\d+)\./i.exec(navigator.userAgent); if (chrome && parseInt(chrome[1]) >= 50) core.platform.isChrome = true; diff --git a/libs/events.js b/libs/events.js index 7a0bef05..2751d87b 100644 --- a/libs/events.js +++ b/libs/events.js @@ -89,7 +89,7 @@ events.prototype._startGame_upload = function () { formData.append('type', 'people'); formData.append('name', core.firstData.name); formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""); + formData.append('platform', core.platform.string); formData.append('hard', core.encodeBase64(core.status.hard)); formData.append('hardCode', core.getFlag('hard', 0)); formData.append('base64', 1); @@ -171,7 +171,7 @@ events.prototype._gameOver_doUpload = function (username, ending, norank) { formData.append('type', 'score'); formData.append('name', core.firstData.name); formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""); + formData.append('platform', core.platform.string); formData.append('hard', core.encodeBase64(core.status.hard)); formData.append('username', core.encodeBase64(username || "")); formData.append('ending', core.encodeBase64(ending)); @@ -2205,7 +2205,7 @@ events.prototype.uploadCurrent = function (username) { formData.append('type', 'score'); formData.append('name', core.firstData.name); formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""); + formData.append('platform', core.platform.string); formData.append('hard', core.encodeBase64(core.status.hard)); formData.append('username', core.encodeBase64(username || "current")); formData.append('lv', core.status.hero.lv); diff --git a/libs/ui.js b/libs/ui.js index cc867ec4..4d924361 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -2022,8 +2022,9 @@ ui.prototype.drawEquipbox = function(index) { ////// 绘制存档/读档界面 ////// ui.prototype.drawSLPanel = function(index, refresh) { - if (!core.isset(index)) index=1; - if (index<0) index=0; + core.control._loadFavoriteSaves(); + if (index == null) index = 1; + if (index < 0) index = 0; var page = parseInt(index/10), offset=index%10; var max_page = main.savePages || 30; @@ -2031,53 +2032,35 @@ ui.prototype.drawSLPanel = function(index, refresh) { max_page = Math.ceil((core.saves.favorite||[]).length/5); if (page>=max_page) page=max_page - 1; if (offset>5) offset=5; - index=10*page+offset; + if (core.status.event.data && core.status.event.data.mode=='fav' && page == max_page - 1) { + offset = Math.min(offset, (core.saves.favorite||[]).length - 5 * page); + } var last_page = -1; var mode = 'all'; - if (core.isset(core.status.event.data)) { - //last_page = parseInt(core.status.event.data/10); + if (core.status.event.data) { last_page = core.status.event.data.page; mode = core.status.event.data.mode; } - - core.status.event.data={ - 'page':page, - 'offset':offset, - 'mode':mode - }; - if (!core.isset(core.status.event.ui)) + core.status.event.data={ 'page':page, 'offset':offset, 'mode':mode }; + core.status.event.ui = core.status.event.ui || []; + if (refresh || page != last_page) { core.status.event.ui = []; - - function drawAll() { - core.ui._drawSLPanel_drawBg(page,max_page); - core.ui._drawSLPanel_drawNRecords(6); + this._drawSLPanel_loadSave(page, function () { + core.ui._drawSLPanel_draw(page, max_page); + }); } - if (refresh || page!=last_page) { - core.status.event.ui = []; - this._drawSLPanel_loadFav( - function(){ - core.ui._drawSLPanel_loadSave(page, 0, drawAll); - } - ); - } - else drawAll(); + else this._drawSLPanel_draw(page, max_page); } -// 存档读档的背景 页码 -ui.prototype._drawSLPanel_drawBg = function(page, max_page) { - core.clearMap('ui'); - core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');//可改成背景图图 - core.setAlpha('ui', 1); - var globalFont = (core.status.globalAttribute||core.initStatus.globalAttribute).font; - - core.ui.drawPagination(page+1, max_page, null); +ui.prototype._drawSLPanel_draw = function (page, max_page) { + // --- 绘制背景 + this._drawSLPanel_drawBackground(); + // --- 绘制文字 + core.ui.drawPagination(page+1, max_page); core.setTextAlign('ui', 'center'); - // 退出 - //core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); var bottom = this.PIXEL-13; - core.fillText('ui', '返回游戏', this.PIXEL-48, bottom,'#DDDDDD', 'bold 15px '+globalFont); + core.fillText('ui', '返回游戏', this.PIXEL-48, bottom, '#DDDDDD', this._buildFont(15, true)); if (core.status.event.selection) core.setFillStyle('ui', '#FF6A6A'); @@ -2085,91 +2068,47 @@ ui.prototype._drawSLPanel_drawBg = function(page, max_page) { core.fillText('ui', '删除模式', 48, bottom); else{ if(core.status.event.data.mode=='all'){ - core.fillText('ui', '[E]显示收藏', 48, bottom); + core.fillText('ui', '[E]显示收藏', 52, bottom); }else{ - core.fillText('ui', '[E]显示全部', 48, bottom); + core.fillText('ui', '[E]显示全部', 52, bottom); } } + // --- 绘制记录 + this._drawSLPanel_drawRecords(); } -ui.prototype._drawSLPanel_flushIndex = function(){ - core.saves.favIndex = {}; - for(var i in core.saves.favorite){ - core.saves.favIndex[parseInt(i)+1]=core.saves.favorite[i]; +ui.prototype._drawSLPanel_drawBackground = function() { + core.clearMap('ui'); + core.setAlpha('ui', 0.85); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');//可改成背景图图 + core.setAlpha('ui', 1); +} + +ui.prototype._drawSLPanel_loadSave = function(page, callback) { + var ids = [0]; + for (var i = 1; i <= 5; ++i) { + var id = 5 * page + i; + if(core.status.event.data.mode=='fav') + id = core.saves.favorite[id - 1]; // 因为favorite第一个不是自动存档 所以要偏移1 + ids.push(id); } -} - -// 读取收藏信息到core.saves.favorite中 -ui.prototype._drawSLPanel_loadFav = function(callback){ - if(!core.saves.favorite || !core.saves.favName){ - core.getLocalForage("favorite", null, function(data) { - core.saves.favorite = data || []; - core.ui._drawSLPanel_flushIndex(); - core.getLocalForage("favName", null, function(data) { - core.saves.favName = data || {}; - callback(); - })}); - }else{ + core.getSaves(ids, function (data) { + for (var i = 0; i < ids.length; ++i) + core.status.event.ui[i] = data[i]; + core.saves.autosave.data = data[0]; callback(); - } -} -// 写入收藏信息 | 收藏信息包括收藏id列表favorite、id到收藏名的映射favName -ui.prototype._drawSLPanel_saveFav = function(callback){ - if(!core.saves.favorite){ - core.saves.favorite = []; - core.saves.favName = {}; - } - core.setLocalForage("favorite", core.saves.favorite, - function(){ - core.setLocalForage("favName", core.saves.favName,callback) - }); -} - - -// TODO:递归改并发 | 读取page页的i号存档数据到ui数组中 -ui.prototype._drawSLPanel_loadSave = function(page, i, callback) { - if (i==6) { - callback(); - return; - } - if (i==0) { - if (core.saves.autosave.data!=null) { - core.status.event.ui[i] = core.saves.autosave.data; - core.ui._drawSLPanel_loadSave(page, 1, callback); - } - else { - core.getLocalForage("autoSave", null, function(data) { - core.saves.autosave.data = data; - core.status.event.ui[i]=data; - core.ui._drawSLPanel_loadSave(page, i+1, callback); - }, function(err) {main.log(err);}); - } - } - else { - var id = 5*page+i; - if(core.status.event.data.mode=='fav'){ - id = core.saves.favorite[id-1];//因为favorite第一个不是自动存档 所以要偏移1 - } - core.getLocalForage("save"+id, null, function(data) { - core.status.event.ui[i]=data; - core.ui._drawSLPanel_loadSave(page, i+1, callback); - }, function(err) {main.log(err);}); - } + }); } // 在以x为中心轴 y为顶坐标 的位置绘制一条宽为size的记录 cho表示是否被选中 选中会加粗 highlight表示高亮标题 ✐ ui.prototype._drawSLPanel_drawRecord = function(title, data, x, y, size, cho, highLight){ - var globalFont = (core.status.globalAttribute||core.initStatus.globalAttribute).font; var strokeColor = '#FFD700'; if (core.status.event.selection) strokeColor = '#FF6A6A'; + if (!data || !data.floorId) highLight = false; - if (!core.isset(data) || !core.isset(data.floorId)) { //存在数据时才高亮 - highLight = false; - } - core.fillText('ui', title, x, y, highLight?'#FFD700':'#FFFFFF', this._buildFont(17,true));//"bold 17px "+globalFont);//名字 - + core.fillText('ui', title, x, y, highLight?'#FFD700':'#FFFFFF', this._buildFont(17, true)); core.strokeRect('ui', x-size/2, y+15, size, size, cho?strokeColor:'#FFFFFF', cho?6:2); - if (core.isset(data) && core.isset(data.floorId)) { + if (data && data.floorId) { core.drawThumbnail(data.floorId, core.maps.loadMap(data.maps, data.floorId).blocks, { heroLoc: data.hero.loc, heroIcon: data.hero.flags.heroIcon, flags: data.hero.flags }, { @@ -2177,45 +2116,36 @@ ui.prototype._drawSLPanel_drawRecord = function(title, data, x, y, size, cho, hi }); var v = core.formatBigNumber(data.hero.hp,true)+"/"+core.formatBigNumber(data.hero.atk,true)+"/"+core.formatBigNumber(data.hero.def,true); var v2 = "/"+core.formatBigNumber(data.hero.mdef,true); - if (v.length+v2.length<=21) v+=v2; - core.fillText('ui', v, x, y+30+size, '#FFD700', this._buildFont(10)); - core.fillText('ui', core.formatDate(new Date(data.time)), x, y+43+size, data.hero.flags.__consoleOpened__?'#FF6A6A':'#FFFFFF', this._buildFont(10));//'10px '+globalFont); + if (core.calWidth('ui', v + v2, this._buildFont(10, false)) <= size) v += v2; + core.fillText('ui', v, x, y+30+size, '#FFD700'); + core.fillText('ui', core.formatDate(new Date(data.time)), x, y+43+size, data.hero.flags.__consoleOpened__?'#FF6A6A':'#FFFFFF'); } else { core.fillRect('ui', x-size/2, y+15, size, size, '#333333', 2); - core.fillText('ui', '空', x, parseInt(y+15+size/2), '#FFFFFF', this._buildFont(30,true));//'bold 30px '+globalFont); + core.fillText('ui', '空', x, parseInt(y+15+size/2), '#FFFFFF', this._buildFont(30,true)); } } -// 绘制n条记录到画板 -ui.prototype._drawSLPanel_drawNRecords = function (n) -{ - var page = core.status.event.data.page;//Math.floor(core.status.event.data/10); - var offset = core.status.event.data.offset;//core.status.event.data%10; - var u = Math.floor(this.PIXEL/6), size = Math.floor(this.PIXEL/3-20);//118 - for (var i=0;i=0 || core.status.event.data.mode=='fav'; - if(highLight)title = '★ ' + title - else title = '☆ ' + title; - var fid = id; - if(core.status.event.data.mode=='fav' && i!=0){//收藏模式下显示修改符号以及自己标识的名字 - fid = core.saves.favIndex[id]; - if(!data && i>0){ - continue; - } -// name = core.saves.favName[fid] ? core.status.event.id=='save'?"S:":core.status.event.id=='load'?"L:":core.status.event.id=='replayLoad'?"re:":"" : name; - title = name + (core.saves.favName[fid]||fid) + '✐'; - }else{ -// name = core.saves.favName[fid] ? core.status.event.id=='save'?"S:":core.status.event.id=='load'?"L:":core.status.event.id=='replayLoad'?"re:":"" : name; - title += name + (core.saves.favName[fid]||fid); + var id = 5 * page + i; + var highLight = (i>0&&core.saves.favorite.indexOf(id)>=0) || core.status.event.data.mode=='fav'; + var title = (highLight?'★ ':'☆ ') + (core.saves.favoriteName[id] || (name + id)); + if (i != 0 && core.status.event.data.mode=='fav') { + if (!data) break; + var real_id = core.saves.favorite[id - 1]; + title = (core.saves.favoriteName[real_id] || (name + real_id)) + ' ✐'; } + var charSize = 32;// 字体占用像素范围 var topSpan = parseInt((this.PIXEL-charSize-2*(charSize*2 + size))/3);// Margin - var yTop1 = topSpan+parseInt(charSize/2);//文字的中心 + var yTop1 = topSpan+parseInt(charSize/2) + 8;//文字的中心 var yTop2 = yTop1+charSize*2+size+topSpan; if (i<3) { this._drawSLPanel_drawRecord(i==0?"自动存档":title, data, (2*i+1)*u, yTop1, size, i==offset, highLight); From f961cc25ece3ee9548fc15732c419d89b5db7da1 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 00:43:35 +0800 Subject: [PATCH 04/64] drawSL --- libs/actions.js | 11 ++++++----- libs/ui.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index 96008b98..8c30b831 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1622,24 +1622,23 @@ actions.prototype._clickSL = function (x, y) { // 点存档名 var xLeft = parseInt(this.SIZE/3), xRight = parseInt(this.SIZE*2/3); var topY1 = 0, topY2 = this.HSIZE; - if(y==topY1){ + if(y >= topY1 && y <= topY1 + 1) { if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 1); if (x >= xRight) return this._clickSL_favorite(page, 2); } - if(y==topY2){ + if(y >= topY2 && y <= topY2 + 1) { if (x < xLeft) return this._clickSL_favorite(page, 3); if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 4); if (x >= xRight) return this._clickSL_favorite(page, 5); } var id = null; - var topSpan = parseInt(this.SIZE/7); - if (y >= topY1 + topSpan && y <= topY1 + topSpan + 3) { + if (y >= topY1 + 2 && y < this.HSIZE - 1) { if (x < xLeft) id = "autoSave"; if (x >= xLeft && x < xRight) id = 5 * page + 1; if (x >= xRight) id = 5 * page + 2; } - if (y >= topY2+1 && y <= topY2+5) { + if (y >= topY2 + 2 && y < this.SIZE - 1) { if (x < xLeft) id = 5 * page + 3; if (x >= xLeft && x < xRight) id = 5 * page + 4; if (x >= xRight) id = 5 * page + 5; @@ -2115,6 +2114,7 @@ actions.prototype._clickStorageRemove = function (x, y) { actions.prototype._clickStorageRemove_all = function () { core.myconfirm("你确定要清除【全部塔】的所有本地存档?\n此行为不可逆!!!", function () { var done = function () { + core.saves.ids = {}; core.saves.autosave.data = null; core.saves.autosave.updated = false; core.ui.closePanel(); @@ -2139,6 +2139,7 @@ actions.prototype._clickStorageRemove_all = function () { actions.prototype._clickStorageRemove_current = function () { core.myconfirm("你确定要清除本塔的所有本地存档?\n此行为不可逆!!!", function () { var done = function () { + core.saves.ids = {}; core.saves.autosave.data = null; core.saves.autosave.updated = false; core.ui.closePanel(); diff --git a/libs/ui.js b/libs/ui.js index 4d924361..010c2cfe 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -2122,7 +2122,7 @@ ui.prototype._drawSLPanel_drawRecord = function(title, data, x, y, size, cho, hi } else { core.fillRect('ui', x-size/2, y+15, size, size, '#333333', 2); - core.fillText('ui', '空', x, parseInt(y+15+size/2), '#FFFFFF', this._buildFont(30,true)); + core.fillText('ui', '空', x, parseInt(y+22+size/2), '#FFFFFF', this._buildFont(30,true)); } } From f98c1b0916565188a8d0442afea46bd9821437e6 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 00:49:19 +0800 Subject: [PATCH 05/64] drawSL --- libs/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/actions.js b/libs/actions.js index 8c30b831..742cb09f 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1614,7 +1614,7 @@ actions.prototype._clickSL = function (x, y) { else { page = parseInt((core.saves.saveIndex-1)/5); offset = core.saves.saveIndex-5*page; - core.ui.drawSLPanel(10*page + offset); + core.ui.drawSLPanel(10*page + offset, true); } } return; From 25d0ce95acddabff7cc91ab410f5781dfa7a9b2e Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 01:13:40 +0800 Subject: [PATCH 06/64] Fix compress --- libs/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ui.js b/libs/ui.js index 010c2cfe..2d35c988 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1559,7 +1559,7 @@ ui.prototype.drawFly = function(page) { var middle = this.HPIXEL + 39; if (core.actions._getNextFlyFloor(1) != page) { core.fillText('ui', '▲', this.PIXEL - 60, middle - 64, null, this._buildFont(17, false)); - core.fillText('ui', '▲', this.PIXEL - 60, middle - 96,); + core.fillText('ui', '▲', this.PIXEL - 60, middle - 96); core.fillText('ui', '▲', this.PIXEL - 60, middle - 96 - 7); } if (core.actions._getNextFlyFloor(-1) != page) { From 254484541d7dcd3b65b90dfbdfde3e16a7bb0c12 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 16:46:02 +0800 Subject: [PATCH 07/64] drawEquipbox --- libs/actions.js | 91 +++++++------- libs/control.js | 4 +- libs/core.js | 11 +- libs/items.js | 25 ++-- libs/ui.js | 317 +++++++++++++++++++++-------------------------- project/icons.js | 2 +- project/items.js | 2 +- project/maps.js | 2 +- 8 files changed, 212 insertions(+), 242 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index 742cb09f..5ec68a78 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1416,64 +1416,65 @@ actions.prototype._keyUpToolbox = function (keycode) { ////// 装备栏界面时的点击操作 ////// actions.prototype._clickEquipbox = function (x, y) { // 道具栏 - if (x >= 10 && x <= 12 && y == 0) { + if (x >= this.LAST - 2 && y == 0) { core.ui.closePanel(); core.openToolbox(); return; } // 返回 - if (x >= 10 && x <= 12 && y == 12) { + if (x >= this.LAST - 2 && y == this.LAST) { core.ui.closePanel(); return; } - // 当前页面 - var page = core.status.event.data.page; - // 上一页 - if ((x == 3 || x == 4) && y == 12) { - if (page > 1) { + if ((x == this.HSIZE-2 || x == this.HSIZE-3) && y == this.LAST) { + if (core.status.event.data.page > 1) { core.status.event.data.page--; core.ui.drawEquipbox(core.status.event.selection); } return; } // 下一页 - if ((x == 8 || x == 9) && y == 12) { - var lastPage = Math.ceil(Object.keys(core.status.hero.items.equips).length / 12); - if (page < lastPage) { + if ((x == this.HSIZE+2 || x == this.HSIZE+3) && y == this.LAST) { + var lastPage = Math.ceil(Object.keys(core.status.hero.items.equips).length / this.LAST); + if (core.status.event.data.page < lastPage) { core.status.event.data.page++; core.ui.drawEquipbox(core.status.event.selection); } return; } - var index = parseInt(x / 2); - if (y == 4) index += 0; - else if (y == 6) index += 6; - else if (y == 9) index += 12; - else if (y == 11) index += 18; - else index = -1; - - if (index >= 0) { - if (index < 12) index = parseInt(index / 2); - this._clickEquipboxIndex(index); + var per_page = this.HSIZE - 3, v = this.SIZE / per_page; + if (y == this.LAST - 8) { + for (var i = 0; i < per_page; ++i) + if (x >= i * v && x <= (i + 1) * v) + return this._clickEquipboxIndex(i); } + else if (y == this.LAST - 6) { + for (var i = 0; i < per_page; ++i) + if (x >= i * v && x <= (i + 1) * v) + return this._clickEquipboxIndex(per_page + i); + } + else if (y == this.LAST - 3) + this._clickEquipboxIndex(this.LAST + parseInt(x / 2)) + else if (y == this.LAST - 1) + this._clickEquipboxIndex(this.LAST + this.HSIZE + parseInt(x / 2)); } ////// 选择装备栏界面中某个Index后的操作 ////// actions.prototype._clickEquipboxIndex = function (index) { - if (index < 6) { + if (index < this.LAST) { if (index >= core.status.globalAttribute.equipName.length) return; if (index == core.status.event.selection && core.status.hero.equipment[index]) { core.unloadEquip(index); core.status.route.push("unEquip:" + index); } } - else if (index >= 12) { + else { var equips = Object.keys(core.status.hero.items.equips || {}).sort(); if (index == core.status.event.selection) { - var equipId = equips[index - 12 + (core.status.event.data.page - 1) * 12]; + var equipId = equips[index - this.LAST + (core.status.event.data.page - 1) * this.LAST]; core.loadEquip(equipId); core.status.route.push("equip:" + equipId); } @@ -1483,21 +1484,23 @@ actions.prototype._clickEquipboxIndex = function (index) { ////// 装备栏界面时,按下某个键的操作 ////// actions.prototype._keyDownEquipbox = function (keycode) { - if (core.status.event.data != null) return; + if (core.status.event.data == null) return; + var last_index = this.LAST - 1; + var per_line = this.HSIZE - 3; var equipCapacity = core.status.globalAttribute.equipName.length; var ownEquipment = Object.keys(core.status.hero.items.equips).sort(); var index = core.status.event.selection; var page = core.status.event.data.page; - var totalPage = Math.ceil(ownEquipment.length / 12); - var totalLastIndex = 12 + (page < totalPage ? 11 : (ownEquipment.length + 11) % 12); + var totalPage = Math.ceil(ownEquipment.length / this.LAST); + var totalLastIndex = this.LAST + (page < totalPage ? last_index : (ownEquipment.length + last_index) % this.LAST); if (keycode == 37) { // left if (index == 0) return; - if (index == 12) { + if (index == this.LAST) { if (page > 1) { core.status.event.data.page--; - index = 23; + index = this.LAST + last_index; } else if (page == 1) index = equipCapacity - 1; @@ -1508,25 +1511,25 @@ actions.prototype._keyDownEquipbox = function (keycode) { return; } if (keycode == 38) { // up - if (index < 3) return; - else if (index < 6) index -= 3; - else if (index < 18) { - index = parseInt((index - 12) / 2); - if (equipCapacity > 3) index = Math.min(equipCapacity - 1, index + 3); + if (index < per_line) return; + else if (index < 2 * per_line) index -= per_line; + else if (index < this.LAST + this.HSIZE) { + index = parseInt((index - this.LAST) / 2); + if (equipCapacity > per_line) index = Math.min(equipCapacity - 1, index + per_line); else index = Math.min(equipCapacity - 1, index); } - else index -= 6; + else index -= this.HSIZE; this._clickEquipboxIndex(index); return; } if (keycode == 39) { // right - if (page < totalPage && index == 23) { + if (page < totalPage && index == this.LAST + last_index) { core.status.event.data.page++; - index = 12; + index = this.LAST; } else if (index == equipCapacity - 1) { if (totalPage == 0) return; - index = 12; + index = this.LAST; } else if (index == totalLastIndex) return; @@ -1535,19 +1538,19 @@ actions.prototype._keyDownEquipbox = function (keycode) { return; } if (keycode == 40) { // down - if (index < 3) { - if (equipCapacity > 3) index = Math.min(index + 3, equipCapacity - 1); + if (index < per_line) { + if (equipCapacity > per_line) index = Math.min(index + per_line, equipCapacity - 1); else { if (totalPage == 0) return; - index = Math.min(2 * index + 1 + 12, totalLastIndex); + index = Math.min(2 * index + 1 + this.LAST, totalLastIndex); } } - else if (index < 6) { + else if (index < 2 * per_line) { if (totalPage == 0) return; - index = Math.min(2 * (index - 3) + 1 + 12, totalLastIndex); + index = Math.min(2 * (index - per_line) + 1 + this.LAST, totalLastIndex); } - else if (index < 18) - index = Math.min(index + 6, totalLastIndex); + else if (index < this.LAST + this.HSIZE) + index = Math.min(index + this.HSIZE, totalLastIndex); else return; this._clickEquipboxIndex(index); return; diff --git a/libs/control.js b/libs/control.js index 7f9b5324..052cbed0 100644 --- a/libs/control.js +++ b/libs/control.js @@ -1865,12 +1865,12 @@ control.prototype.getRealStatusOrDefault = function (status, name) { ////// 设置某个属性的增幅值 ////// control.prototype.setBuff = function (name, value) { - this.setFlag('flag:__'+name+'_buff__', value); + this.setFlag('__'+name+'_buff__', value); } ////// 加减某个属性的增幅值 ////// control.prototype.addBuff = function (name, value) { - this.setFlag('flag:__'+name+'_buff__', this.getBuff(name) + value); + this.setFlag('__'+name+'_buff__', this.getBuff(name) + value); } ////// 获得某个属性的增幅值 ////// diff --git a/libs/core.js b/libs/core.js index fb6d0077..966aa4d0 100644 --- a/libs/core.js +++ b/libs/core.js @@ -103,7 +103,9 @@ function core() { "data": null, "time": 0, "updated": false, - } + }, + "favorite": [], + "favoriteName": {} } this.initStatus = { 'played': false, @@ -295,7 +297,7 @@ core.prototype._init_platform = function () { } core.prototype._init_checkLocalForage = function () { - core.platform.useLocalForage = core.getLocalStorage('useLocalForage', !core.platform.isIOS); + core.platform.useLocalForage = core.getLocalStorage('useLocalForage', true); var _error = function (e) { main.log(e); core.platform.useLocalForage = false; @@ -384,13 +386,16 @@ core.prototype._forwardFunc = function (name, funcname) { } if (core[funcname]) { - console.error("ERROR: Cannot forward function " + funcname + " from " + name + "!"); + console.error("ERROR: 无法转发 "+name+" 中的函数 "+funcname+" 到 core 中!同名函数已存在。"); return; } var parameterInfo = /^\s*function\s*[\w_$]*\(([\w_,$\s]*)\)\s*\{/.exec(core[name][funcname].toString()); var parameters = (parameterInfo == null ? "" : parameterInfo[1]).replace(/\s*/g, '').replace(/,/g, ', '); // core[funcname] = new Function(parameters, "return core."+name+"."+funcname+"("+parameters+");"); eval("core." + funcname + " = function (" + parameters + ") {\n\treturn core." + name + "." + funcname + "(" + parameters + ");\n}"); + if (name == 'plugin') { + main.log("插件函数转发:core."+funcname+" = core.plugin."+funcname); + } } core.prototype.doFunc = function (func, _this) { diff --git a/libs/items.js b/libs/items.js index 58faca5c..23056d8d 100644 --- a/libs/items.js +++ b/libs/items.js @@ -311,20 +311,17 @@ items.prototype.unloadEquip = function (equipType, callback) { } items.prototype.compareEquipment = function (compareEquipId, beComparedEquipId) { - var compareAtk = 0, compareDef = 0, compareMdef = 0; - if (compareEquipId) { - var compareEquip = core.material.items[compareEquipId]; - compareAtk += (compareEquip.equip || {}).atk || 0; - compareDef += (compareEquip.equip || {}).def || 0; - compareMdef += (compareEquip.equip || {}).mdef || 0; + var result = {}; + var first = core.material.items[compareEquipId], second = core.material.items[beComparedEquipId]; + for (var name in core.status.hero) { + if (typeof core.status.hero[name] == 'number') { + var ans = 0; + if (first) ans += (first.equip || {})[name] || 0; + if (second) ans -= (second.equip || {})[name] || 0; + if (ans != 0) result[name] = ans; + } } - if (beComparedEquipId) { - var beComparedEquip = core.material.items[beComparedEquipId]; - compareAtk -= (beComparedEquip.equip || {}).atk || 0; - compareDef -= (beComparedEquip.equip || {}).def || 0; - compareMdef -= (beComparedEquip.equip || {}).mdef || 0; - } - return {"atk": compareAtk, "def": compareDef, "mdef": compareMdef}; + return result; } ////// 实际换装的效果 ////// @@ -349,7 +346,7 @@ items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { var loadPercentage = loadEquip.equip.percentage, unloadPercentage = unloadEquip.equip.percentage; - if (loadPercentage != null && unloadPercentage != null && loadPercentage != unloadPercentage) { + if (loadId && unloadId && (loadPercentage || false) != (unloadPercentage || false)) { this.unloadEquip(type); this.loadEquip(loadId); if (callback) callback(); diff --git a/libs/ui.js b/libs/ui.js index 2d35c988..2d1ecf01 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1733,7 +1733,7 @@ ui.prototype.drawToolbox = function(index) { core.setTextAlign('ui', 'center'); core.fillText('ui', '[装备栏]', this.PIXEL - 46, 25, '#DDDDDD', this._buildFont(15, true)); - core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13,'#DDDDDD'); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13); } ui.prototype._drawToolbox_getInfo = function (index) { @@ -1795,28 +1795,25 @@ ui.prototype._drawToolbox_drawLine = function (yoffset, text) { } ui.prototype._drawToolbox_drawDescription = function (info, max_height) { - core.setTextAlign('ui', 'left'); - // 描述 - if (info.selectId) { - var item=core.material.items[info.selectId]; - core.fillText('ui', item.name, 10, 32, '#FFD700', this._buildFont(20, true)) - var text = item.text||"该道具暂无描述。"; - try { - // 检查能否eval - text = core.replaceText(text); - } catch (e) {} - var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(17, false)); - // --- 开始逐行绘制 - var curr = 62, line_height = 25; - core.setFillStyle('ui', '#FFFFFF'); - for (var i=0;i=max_height) break; - } - if (curr < max_height) { - core.fillText('ui', '<继续点击该道具即可进行使用>', 10, curr, '#CCCCCC', this._buildFont(14, false)); - } + if (!info.selectId) return; + var item=core.material.items[info.selectId]; + core.fillText('ui', item.name, 10, 32, '#FFD700', this._buildFont(20, true)) + var text = item.text||"该道具暂无描述。"; + try { + // 检查能否eval + text = core.replaceText(text); + } catch (e) {} + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(17, false)); + // --- 开始逐行绘制 + var curr = 62, line_height = 25; + core.setFillStyle('ui', '#FFFFFF'); + for (var i=0;i=max_height) break; + } + if (curr < max_height) { + core.fillText('ui', '<继续点击该道具即可进行使用>', 10, curr, '#CCCCCC', this._buildFont(14, false)); } } @@ -1837,187 +1834,155 @@ ui.prototype._drawToolbox_drawContent = function (info, line, items, page, drawC ////// 绘制装备界面 ////// ui.prototype.drawEquipbox = function(index) { - // 设定eventdata - if (!core.isset(core.status.event.data) || !core.isset(core.status.event.data.page)) - core.status.event.data = {"page":1, "selectId":null}; + var info = this._drawEquipbox_getInfo(index); + this._drawToolbox_drawBackground(); + core.setAlpha('ui', 1); + core.setStrokeStyle('ui', '#DDDDDD'); + core.canvas.ui.lineWidth = 2; + core.canvas.ui.strokeWidth = 2; + core.setTextAlign('ui', 'right'); + var line1 = this.PIXEL - 306; + this._drawToolbox_drawLine(line1, "当前装备"); + var line2 = this.PIXEL - 146; + this._drawToolbox_drawLine(line2, "拥有装备"); + + this._drawEquipbox_description(info, line1); + + this._drawEquipbox_drawEquiped(info, line1); + this._drawToolbox_drawContent(info, line2, info.ownEquipment, info.page, true); + this.drawPagination(info.page, info.totalPage); + + core.setTextAlign('ui', 'center'); + core.fillText('ui', '[道具栏]', this.PIXEL - 46, 25, '#DDDDDD', this._buildFont(15, true)); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13); +} + +ui.prototype._drawEquipbox_getInfo = function (index) { + if (!core.status.event.data || core.status.event.data.page == null) + core.status.event.data = {"page":1, "selectId":null}; var allEquips = core.status.globalAttribute.equipName; var equipLength = allEquips.length; - - if (!core.isset(core.status.hero.equipment)) core.status.hero.equipment = []; - + if (!core.status.hero.equipment) core.status.hero.equipment = []; var equipEquipment = core.status.hero.equipment; var ownEquipment = Object.keys(core.status.hero.items.equips).sort(); - var page = core.status.event.data.page; - var totalPage = Math.ceil(ownEquipment.length/12); - + var totalPage = Math.ceil(ownEquipment.length / this.LAST); // 处理index - if (!core.isset(index)) { - if (equipLength>0 && core.isset(equipEquipment[0])) index=0; - else if (ownEquipment.length>0) index=12; - else index=0; + if (index == null) { + if (equipLength > 0 && equipEquipment[0]) index = 0; + else if (ownEquipment.length > 0) index = this.LAST; + else index = 0; } - if (index>=12 && ownEquipment.length==0) index = 0; + if (index >= this.LAST && ownEquipment.length == 0) index = 0; var selectId=null; - if (index<12) { + if (index < this.LAST) { if (index >= equipLength) index=Math.max(0, equipLength - 1); - selectId = equipEquipment[index]||null; + selectId = equipEquipment[index] || null; } else { - if (page == totalPage) index = Math.min(index, (ownEquipment.length+11)%12+12); - selectId = ownEquipment[index-12 + (page-1)*12]; + if (page == totalPage) index = Math.min(index, (ownEquipment.length+this.LAST-1)%this.LAST+this.LAST); + selectId = ownEquipment[index - this.LAST + (page - 1) * this.LAST]; if (!core.hasItem(selectId)) selectId=null; } core.status.event.selection=index; core.status.event.data.selectId=selectId; + return { index: index, selectId: selectId, page: page, totalPage: totalPage, allEquips: allEquips, + equipLength: equipLength, equipEquipment: equipEquipment, ownEquipment: ownEquipment}; +} - core.clearMap('ui', 0, 0, 416, 416); - core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, 416, 416, '#000000'); - core.setAlpha('ui', 1); - core.setFillStyle('ui', '#DDDDDD'); - core.setStrokeStyle('ui', '#DDDDDD'); - core.canvas.ui.lineWidth = 2; - core.canvas.ui.strokeWidth = 2; - - var ydelta = 20; - - // 画线 - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 130-ydelta); - core.canvas.ui.lineTo(416, 130-ydelta); - core.canvas.ui.stroke(); - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,129-ydelta); - core.canvas.ui.lineTo(416,105-ydelta); - core.canvas.ui.lineTo(416-72,105-ydelta); - core.canvas.ui.lineTo(416-102,129-ydelta); - core.canvas.ui.fill(); - - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 290-ydelta); - core.canvas.ui.lineTo(416, 290-ydelta); - core.canvas.ui.stroke(); - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,289-ydelta); - core.canvas.ui.lineTo(416,265-ydelta); - core.canvas.ui.lineTo(416-72,265-ydelta); - core.canvas.ui.lineTo(416-102,289-ydelta); - core.canvas.ui.fill(); - - // 文字 - core.setTextAlign('ui', 'right'); - var globalFont = core.status.globalAttribute.font; - core.fillText('ui', "当前装备", 411, 124-ydelta, '#333333', "bold 16px "+globalFont); - core.fillText('ui', "拥有装备", 411, 284-ydelta); - +ui.prototype._drawEquipbox_description = function (info, max_height) { core.setTextAlign('ui', 'left'); + if (!info.selectId) return; + var equip=core.material.items[info.selectId]; + // --- 标题 + if (!equip.equip) equip.equip = {"type": 0}; + var equipType = equip.equip.type, equipString; + if (typeof equipType === 'string') { + equipString = equipType || "未知部位"; + equipType = core.items.getEquipTypeByName(equipType); + } + else equipString = info.allEquips[equipType] || "未知部位"; + core.fillText('ui', equip.name + "(" + equipString + ")", 10, 32, '#FFD700', this._buildFont(20, true)) + // --- 描述 + var text = equip.text || "该装备暂无描述。"; + try { + text = core.replaceText(text); + } catch (e) {} + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(17, false)); + var curr = 62, line_height = 25; + core.setFillStyle('ui', '#FFFFFF'); + for (var i = 0; i < lines.length; ++i) { + core.fillText('ui', lines[i], 10, curr); + curr += line_height; + if (curr >= max_height) break; + } + // --- 变化值 + if (curr >= max_height) return; + this._drawEquipbox_drawStatusChanged(info, curr, equip, equipType); +} - // 描述 - if (core.isset(selectId)) { - var equip=core.material.items[selectId]; - if (!core.isset(equip.equip)) equip.equip = {"type": 0}; - var equipType = equip.equip.type; - var equipString; - if (typeof equipType === 'string') { - equipString = equipType||"未知部位"; - equipType = core.items.getEquipTypeByName(equipType); - } - else equipString = allEquips[equipType]||"未知部位"; - - core.fillText('ui', equip.name + "(" + equipString + ")", 10, 32, '#FFD700', "bold 20px "+globalFont) - - var text = equip.text||"该装备暂无描述。"; - try { - text = core.replaceText(text); - } catch (e) {} - var lines = core.splitLines('ui', text, 406, '17px '+globalFont); - - core.fillText('ui', lines[0], 10, 62, '#FFFFFF', '17px '+globalFont); - - // 比较属性 - if (lines.length==1) { - var compare, differentMode = null; - if (index<12) compare = core.compareEquipment(null, selectId); - else { - if (equipType<0) { - differentMode = '<当前没有该装备的空位,请先卸下装备>'; - } - else { - var last = core.material.items[equipEquipment[equipType]]||{}; - // 检查是不是数值模式和比例模式之间的切换 - if (core.isset(last.equip) && (last.equip.percentage||false) != (equip.equip.percentage||false)) { - differentMode = '<数值和比例模式之间的切换不显示属性变化>'; - } - else { - compare = core.compareEquipment(selectId, equipEquipment[equipType]); - } - } - } - if (differentMode != null) { - core.fillText('ui', differentMode, 10, 89, '#CCCCCC', '14px '+globalFont); - } - else { - var drawOffset = 10; - [['攻击','atk'], ['防御','def'], ['魔防','mdef']].forEach(function (t) { - var title = t[0], name = t[1]; - if (!core.isset(compare[name]) || compare[name]==0) return; - var color = '#00FF00'; - if (compare[name]<0) color = '#FF0000'; - var nowValue = core.getStatus(name), newValue = nowValue + compare[name]; - if (equip.equip.percentage) { - var nowBuff = core.getBuff(name), newBuff = nowBuff+compare[name]/100; - nowValue = Math.floor(nowBuff*core.getStatus(name)); - newValue = Math.floor(newBuff*core.getStatus(name)); - } - var content = title + ' ' + nowValue + '->'; - core.fillText('ui', content, drawOffset, 89, '#CCCCCC', 'bold 14px '+globalFont); - drawOffset += core.calWidth('ui', content); - core.fillText('ui', newValue, drawOffset, 89, color); - drawOffset += core.calWidth('ui', newValue) + 15; - }) - } - } +ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipType) { + var compare, differentMode = null; + if (info.index < this.LAST) compare = core.compareEquipment(null, info.selectId); + else { + if (equipType<0) differentMode = '<当前没有该装备的空位,请先卸下装备>'; else { - var leftText = text.substring(lines[0].length); - core.fillText('ui', leftText, 10, 89, '#FFFFFF', '17px '+globalFont); + var last = core.material.items[info.equipEquipment[equipType]]||{}; + if (last.equip && (last.equip.percentage || false) != (equip.equip.percentage || false)) + differentMode = '<数值和比例模式之间的切换不显示属性变化>'; + else + compare = core.compareEquipment(info.selectId, info.equipEquipment[equipType]); } } + if (differentMode != null) { + core.fillText('ui', differentMode, 10, y, '#CCCCCC', this._buildFont(14, false)); + return; + } + var drawOffset = 10; + // --- 变化值... + core.setFont('ui', this._buildFont(14, true)); + for (var name in compare) { + var img = core.statusBar.icons[name]; + if (img) { // 绘制图标 + core.drawImage('ui', img, 0, 0, 32, 32, drawOffset, y - 13, 16, 16); + drawOffset += 20; + } + else { // 绘制文字 + core.fillText('ui', name + " ", drawOffset, y, '#CCCCCC'); + drawOffset += core.calWidth('ui', name + " "); + } + var nowValue = core.getStatus(name) * core.getBuff(name), newValue = (nowValue + compare[name]) * core.getBuff(name); + if (equip.equip.percentage) { + var nowBuff = core.getBuff(name), newBuff = nowBuff + compare[name] / 100; + nowValue = Math.floor(nowBuff * core.getStatus(name)); + newValue = Math.floor(newBuff * core.getStatus(name)); + } + nowValue = core.formatBigNumber(nowValue); + newValue = core.formatBigNumber(newValue); + core.fillText('ui', nowValue + "->", drawOffset, y, '#CCCCCC'); + drawOffset += core.calWidth('ui', nowValue + "->"); + core.fillText('ui', newValue, drawOffset, y, compare[name]>0?'#00FF00':'#FF0000'); + drawOffset += core.calWidth('ui', newValue) + 8; + } +} +ui.prototype._drawEquipbox_drawEquiped = function (info, line) { core.setTextAlign('ui', 'right'); - var images = core.material.images.items; - + var per_line = this.HSIZE - 3, width = Math.floor(this.PIXEL / (per_line + 0.25)); // 当前装备 - for (var i = 0 ; i < equipLength ; i++) { - var equipId = equipEquipment[i] || null; - if (core.isset(equipId)) { + for (var i = 0; i < info.equipLength ; i++) { + var equipId = info.equipEquipment[i] || null; + var offset_text = width * (i % per_line) + 56; + var offset_image = width * (i % per_line) + width * 2 / 3; + var y = line + 54 * Math.floor(i / per_line) + 19; + if (equipId) { var icon = core.material.icons.items[equipId]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(8*(i%3)+5)+5, 144+Math.floor(i/3)*54+5-ydelta, 32, 32); + core.drawImage('ui', core.material.images.items, 0, 32 * icon, 32, 32, offset_image, y, 32, 32); } - core.fillText('ui', allEquips[i]||"未知", 16*(8*(i%3)+1)+40, 144+Math.floor(i/3)*54+32-ydelta, '#FFFFFF', "bold 16px "+globalFont); - core.strokeRect('ui', 16*(8*(i%3)+5)+1, 144+Math.floor(i/3)*54+1-ydelta, 40, 40, index==i?'#FFD700':"#FFFFFF"); + core.fillText('ui', info.allEquips[i] || "未知", offset_text, y + 27, '#FFFFFF', this._buildFont(16, true)) + core.strokeRect('ui', offset_image - 4, y - 4, 40, 40, info.index==i?'#FFD700':"#FFFFFF"); } - - // 现有装备 - for (var i=0;i<12;i++) { - var ownEquip=ownEquipment[12*(page-1)+i]; - if (!core.isset(ownEquip)) continue; - var icon=core.material.icons.items[ownEquip]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(4*(i%6)+1)+5, 304+Math.floor(i/6)*54+5-ydelta, 32, 32) - // 个数 - if (core.itemCount(ownEquip)>1) - core.fillText('ui', core.itemCount(ownEquip), 16*(4*(i%6)+1)+40, 304+Math.floor(i/6)*54+38-ydelta, '#FFFFFF', "bold 14px "+globalFont); - if (index>=12 && selectId == ownEquip) - core.strokeRect('ui', 16*(4*(i%6)+1)+1, 304+Math.floor(i/6)*54+1-ydelta, 40, 40, '#FFD700'); - } - - this.drawPagination(page, totalPage, 12); - // 道具栏 - core.setTextAlign('ui', 'center'); - core.fillText('ui', '[道具栏]', 370, 25,'#DDDDDD', 'bold 15px '+globalFont); - // 退出按钮 - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); } ////// 绘制存档/读档界面 ////// diff --git a/project/icons.js b/project/icons.js index 71bce353..0fe339c2 100644 --- a/project/icons.js +++ b/project/icons.js @@ -259,7 +259,7 @@ var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = "hammer": 48, "jumpShoes": 49, "skill1": 30, - "I73": 10 + "wand": 10 }, "autotile": { "autotile": 0, diff --git a/project/items.js b/project/items.js index 83b236c7..efda5b71 100644 --- a/project/items.js +++ b/project/items.js @@ -303,7 +303,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "text": "可以打开或关闭主动技能二倍斩", "hideInReplay": true }, - "I73": { + "wand": { "cls": "items", "name": "新物品" } diff --git a/project/maps.js b/project/maps.js index 78a610d0..d631d0ba 100644 --- a/project/maps.js +++ b/project/maps.js @@ -67,7 +67,7 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "70": {"cls":"items","id":"sword0"}, "71": {"cls":"items","id":"shield0"}, "72": {"cls":"items","id":"skill1"}, - "73": {"cls":"items","id":"I73"}, + "73": {"cls":"items","id":"wand"}, "81": {"cls":"terrains","id":"yellowDoor","trigger":"openDoor"}, "82": {"cls":"terrains","id":"blueDoor","trigger":"openDoor"}, "83": {"cls":"terrains","id":"redDoor","trigger":"openDoor"}, From 67f98f2da634384870e77135dac909f2048ed9a6 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 17:04:48 +0800 Subject: [PATCH 08/64] toolbox textalign --- libs/ui.js | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/ui.js b/libs/ui.js index 2d1ecf01..8a569922 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1795,6 +1795,7 @@ ui.prototype._drawToolbox_drawLine = function (yoffset, text) { } ui.prototype._drawToolbox_drawDescription = function (info, max_height) { + core.setTextAlign('ui', 'left'); if (!info.selectId) return; var item=core.material.items[info.selectId]; core.fillText('ui', item.name, 10, 32, '#FFD700', this._buildFont(20, true)) From f7056d699f9ded847463716445c582adab038e95 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 17:15:12 +0800 Subject: [PATCH 09/64] fix status:x --- libs/control.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/control.js b/libs/control.js index 052cbed0..d85b2cf6 100644 --- a/libs/control.js +++ b/libs/control.js @@ -1841,7 +1841,7 @@ control.prototype.addStatus = function (name, value) { control.prototype.getStatus = function (name) { if (!core.status.hero) return null; if (name == 'x' || name == 'y' || name == 'direction') - return this.getHeroLoc('x'); + return this.getHeroLoc(name); if (name == 'exp') name = 'experience'; return core.status.hero[name]; } From 16980c527ab7f817a7e0201dfab5212679658d05 Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 19:18:57 +0800 Subject: [PATCH 10/64] quickCommonEvents --- _docs/event.md | 6 +++ _server/MotaAction.g4 | 17 +++++++++ _server/data.comment.js | 6 +++ _server/editor_blockly.js | 1 + libs/actions.js | 50 ++++++++++++++++++------- libs/control.js | 11 ++++++ libs/events.js | 27 +++++++++++++- libs/ui.js | 18 ++++++--- libs/utils.js | 7 +++- main.js | 2 +- project/data.js | 1 + project/events.js | 77 +++++++++++++++++++++++++++++++++++++++ project/functions.js | 2 +- 13 files changed, 200 insertions(+), 25 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index 3beb965a..57825ba7 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -682,6 +682,12 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ] ``` +### addToList:将本公共事件插入到快捷列表中 + +使用 `{"type": "addToList"}` 可以将当前公共事件插入到快捷商店列表中。 + +此项只能在公共事件中被执行。详见[公共事件](personalization#公共事件)。 + ### setBlock:设置某个图块 我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 1aa58715..c1c31050 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -240,6 +240,7 @@ action | insert_2_s | revisit_s | exit_s + | addToList_s | setBlock_s | showFloorImg_s | hideFloorImg_s @@ -693,6 +694,18 @@ var code = '{"type": "exit"},\n'; return code; */; +addToList_s + : '将本公共事件插入到快捷列表中' Newline + + +/* addToList_s +tooltip : addToList: 将本公共事件插入到快捷列表中 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=exit%EF%BC%9A%E7%AB%8B%E5%88%BB%E7%BB%93%E6%9D%9F%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 +colour : this.eventColor +var code = '{"type": "addToList"},\n'; +return code; +*/; + setBlock_s : '转变图块为' Int 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline @@ -2604,6 +2617,10 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['exit_s'].xmlText([ this.next]); break; + case "addToList": // 立刻结束事件 + this.next = MotaActionBlocks['addToList_s'].xmlText([ + this.next]); + break; case "animateImage": // 兼容 animateImage break; default: diff --git a/_server/data.comment.js b/_server/data.comment.js index c4ad3175..8df587e1 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -660,6 +660,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否在经过领域/夹击/路障等伤害后禁用快捷商店。" }, + "quickCommonEvents": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否使用自定义的公共事件列表来代替快捷商店列表。\n如果此项开启,则快捷商店列表中将会显示加入的公共事件。" + }, "checkConsole": { "_leaf": true, "_type": "checkbox", diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 9ebabaac..ee887b02 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -123,6 +123,7 @@ editor_blockly = function () { MotaActionBlocks['hideBgFgMap_s'].xmlText(), MotaActionBlocks['trigger_s'].xmlText(), MotaActionBlocks['insert_1_s'].xmlText(), + MotaActionBlocks['addToList_s'].xmlText(), MotaActionBlocks['insert_2_s'].xmlText(), MotaActionBlocks['move_s'].xmlText(), MotaActionBlocks['jump_s'].xmlText(), diff --git a/libs/actions.js b/libs/actions.js index 5ec68a78..ad1c3c1d 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1198,20 +1198,35 @@ actions.prototype._keyUpShop = function (keycode) { ////// 快捷商店界面时的点击操作 ////// actions.prototype._clickQuickShop = function (x, y) { - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { - return shopList[shopId].visited || !shopList[shopId].mustEnable - }); + var keys = []; + if (core.flags.quickCommonEvents) { + keys = core.getFlag("__commonEventList__", []); + } + else { + keys = Object.keys(core.status.shops).filter(function (shopId) { + return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable + }); + } + if (x >= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { var topIndex = this.HSIZE - parseInt(keys.length / 2); if (y >= topIndex && y < topIndex + keys.length) { - var reason = core.events.canUseQuickShop(keys[y - topIndex]); - if (!core.flags.enableDisabledShop && reason) { - core.drawText(reason); - return; + if (core.flags.quickCommonEvents) { + var name = keys[y - topIndex]; + core.ui.closePanel(); + core.status.route.push("common:" + core.encodeBase64(name)); + core.insertAction(name); + } + else { + var reason = core.events.canUseQuickShop(keys[y - topIndex]); + if (!core.flags.enableDisabledShop && reason) { + core.drawText(reason); + return; + } + core.events.openShop(keys[y - topIndex], true); + if (core.status.event.id == 'shop') + core.status.event.data.fromList = true; } - core.events.openShop(keys[y - topIndex], true); - if (core.status.event.id == 'shop') - core.status.event.data.fromList = true; } // 离开 else if (y == topIndex + keys.length) @@ -1225,10 +1240,17 @@ actions.prototype._keyUpQuickShop = function (keycode) { core.ui.closePanel(); return; } - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { - return shopList[shopId].visited || !shopList[shopId].mustEnable - }); - this._selectChoices(keys.length + 1, keycode, this._clickQuickShop); + var length = 0; + if (core.flags.quickCommonEvents) { + length = core.getFlag("__commonEventList__", []).length; + } + else { + var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { + return shopList[shopId].visited || !shopList[shopId].mustEnable + }); + length = keys.length; + } + this._selectChoices(length + 1, keycode, this._clickQuickShop); return; } diff --git a/libs/control.js b/libs/control.js index d85b2cf6..b980955b 100644 --- a/libs/control.js +++ b/libs/control.js @@ -33,6 +33,7 @@ control.prototype._init = function () { this.registerReplayAction("fly", this._replayAction_fly); this.registerReplayAction("shop", this._replayAction_shop); this.registerReplayAction("turn", this._replayAction_turn); + this.registerReplayAction("common", this._replayAction_common); this.registerReplayAction("getNext", this._replayAction_getNext); this.registerReplayAction("moveDirectly", this._replayAction_moveDirectly); this.registerReplayAction("key", this._replayAction_key); @@ -1441,6 +1442,16 @@ control.prototype._replayAction_turn = function (action) { return true; } +control.prototype._replayAction_common = function (action) { + if (action.indexOf("common:") != 0) return false; + var name = core.decodeBase64(action.substring(7)); + if (core.getFlag("__commonEventList__").indexOf(name) == -1) return false; + core.status.route.push(action); + core.insertAction(name); + setTimeout(core.replay); + return true; +} + control.prototype._replayAction_getNext = function (action) { if (action != "getNext") return false; if (!core.getNextItem()) return false; diff --git a/libs/events.js b/libs/events.js index 2751d87b..4a1be196 100644 --- a/libs/events.js +++ b/libs/events.js @@ -806,7 +806,10 @@ events.prototype.insertAction = function (action, x, y, callback, addToLast) { // ------ 判定commonEvent var commonEvent = this.getCommonEvent(action); - if (commonEvent instanceof Array) action = commonEvent; + if (commonEvent instanceof Array) { + this._addCommentEventToList(action, commonEvent); + action = commonEvent; + } if (!action) return; if (core.status.event.id != 'action') { @@ -827,6 +830,22 @@ events.prototype.getCommonEvent = function (name) { return this.commonEvent[name] || null; } +events.prototype._addCommentEventToList = function (name, list) { + if (list == null) list = this.getCommonEvent(name); + if (!list || !core.flags.quickCommonEvents) return; + var addToList = false; + for (var x in list) { + if (list[x].type == 'addToList') { + addToList = true; + break; + } + } + if (!addToList) return; + var obj = core.getFlag("__commonEventList__", []); + if (obj.indexOf(name) == -1) obj.push(name); + core.setFlag("__commonEventList__", obj); +} + ////// 恢复一个事件 ////// events.prototype.recoverEvents = function (data) { if (data) { @@ -1175,7 +1194,7 @@ events.prototype._action_insert = function (data, x, y, prefix) { } if (data.name) { // 公共事件 core.setFlag('arg0', data.name); - core.insertAction(this.getCommonEvent(data.name)); + core.insertAction(data.name); } else { var loc = this.__action_getLoc(data.loc, x, y, prefix); @@ -1188,6 +1207,10 @@ events.prototype._action_insert = function (data, x, y, prefix) { core.doAction(); } +events.prototype._action_addToList = function (data, x, y, prefix) { + core.doAction(); +} + events.prototype._action_playBgm = function (data, x, y, prefix) { core.playBgm(data.name); core.doAction(); diff --git a/libs/ui.js b/libs/ui.js index 8a569922..7529f589 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1118,12 +1118,18 @@ ui.prototype.drawSettings = function () { ////// 绘制快捷商店选择栏 ////// ui.prototype.drawQuickShop = function () { core.status.event.id = 'selectShop'; - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { - return shopList[shopId].visited || !shopList[shopId].mustEnable - }); - var choices = keys.map(function (shopId) { - return {"text": shopList[shopId].textInList, "color": shopList[shopId].visited?null:"#999999"}; - }); + var choices; + if (core.flags.quickCommonEvents) { + choices = core.clone(core.getFlag("__commonEventList__", [])); + } + else { + var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { + return shopList[shopId].visited || !shopList[shopId].mustEnable + }); + choices = keys.map(function (shopId) { + return {"text": shopList[shopId].textInList, "color": shopList[shopId].visited?null:"#999999"}; + }); + } choices.push("返回游戏"); this.drawChoices(null, choices); } diff --git a/libs/utils.js b/libs/utils.js index d3bac9d3..4dbd3e34 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -467,6 +467,8 @@ utils.prototype._encodeRoute_encodeOne = function (t) { return "P" + t.substring(6); else if (t.indexOf('input2:') == 0) return "Q" + t.substring(7) + ":"; + else if (t.indexOf('common:') == 0) + return "c" + t.substring(7) + ":"; else if (t == 'no') return 'N'; else if (t.indexOf('move:') == 0) @@ -525,7 +527,7 @@ utils.prototype._decodeRoute_number2id = function (number) { } utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { - var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't') ? + var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't' || c == 'c') ? this._decodeRoute_getString(decodeObj) : this._decodeRoute_getNumber(decodeObj); var mp = {"U": "up", "D": "down", "L": "left", "R": "right"}; @@ -570,6 +572,9 @@ utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { case "Q": decodeObj.ans.push("input2:" + nxt); break; + case "c": + decodeObj.ans.push("common:" + nxt); + break; case "N": decodeObj.ans.push("no"); break; diff --git a/main.js b/main.js index a6c60bba..9d26b266 100644 --- a/main.js +++ b/main.js @@ -675,7 +675,7 @@ window.onblur = function () { if (main.core && main.core.control) { try { main.core.control.checkAutosave(); - } catch (e) {main.log(e);} + } catch (e) {} } } diff --git a/project/data.js b/project/data.js index 878a6ca0..6173ffed 100644 --- a/project/data.js +++ b/project/data.js @@ -397,6 +397,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableMoveDirectly": true, "enableDisabledShop": true, "disableShopOnDamage": false, + "quickCommonEvents": false, "checkConsole": false } } \ No newline at end of file diff --git a/project/events.js b/project/events.js index 85eb07ca..b546b4a2 100644 --- a/project/events.js +++ b/project/events.js @@ -237,6 +237,83 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = ], "false": [] } + ], + "回收钥匙商店": [ + { + "type": "comment", + "text": "公共事件:回收钥匙商店" + }, + { + "type": "addToList" + }, + { + "type": "comment", + "text": "使用上述事件并在全塔属性打开quickCommonEvent开关" + }, + { + "type": "comment", + "text": "就可以在快捷列表(V键)中使用本公共事件" + }, + { + "type": "while", + "condition": "1", + "data": [ + { + "type": "choices", + "text": "\t[商人,woman]你有多余的钥匙想要出售吗?", + "choices": [ + { + "text": "黄钥匙(10金币)", + "color": [ + 255, + 255, + 0, + 1 + ], + "action": [ + { + "type": "if", + "condition": "item:yellowKey >= 1", + "true": [ + { + "type": "addValue", + "name": "item:yellowKey", + "value": "-1" + }, + { + "type": "addValue", + "name": "status:money", + "value": "10" + } + ], + "false": [ + "\t[商人,woman]你没有黄钥匙!" + ] + } + ] + }, + { + "text": "蓝钥匙(50金币)", + "color": [ + 0, + 0, + 255, + 1 + ], + "action": [] + }, + { + "text": "离开", + "action": [ + { + "type": "exit" + } + ] + } + ] + } + ] + } ] } } \ No newline at end of file diff --git a/project/functions.js b/project/functions.js index 64e05bf8..2044fe73 100644 --- a/project/functions.js +++ b/project/functions.js @@ -699,7 +699,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = gid = guards[i][2]; // 递归计算支援怪伤害信息,这里不传x,y保证不会重复调用 // 这里的mdef传0,因为护盾应该只会被计算一次 - var info = core.enemys.getDamageInfo(core.material.enemys[gid], origin_hero_hp, origin_hero_atk, origin_hero_def, 0); + var info = core.enemys.getDamageInfo(core.material.enemys[gid], { hp: origin_hero_hp, atk: origin_hero_atk, def: origin_hero_def, mdef: 0 }); if (info == null) { // 小队中任何一个怪物不可战斗,直接返回null return null; } From e822543bf1732e0dbbf8cf1e1f35a4480820b9ad Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 20:15:43 +0800 Subject: [PATCH 11/64] prev replays --- libs/control.js | 8 ++++++-- libs/maps.js | 13 ++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libs/control.js b/libs/control.js index b980955b..fa0eaf5a 100644 --- a/libs/control.js +++ b/libs/control.js @@ -1302,8 +1302,12 @@ control.prototype._replay_save = function () { control.prototype._replay_error = function (action) { core.status.replay.replaying = false; - main.log("录像文件出错,当前操作:" + action + - "\n接下来10个操作是:"+core.status.replay.toReplay.slice(0, 10).toString()); + var len = core.status.replay.toReplay.length; + var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1); + var nextList = core.status.replay.toReplay.slice(0, 10); + main.log("录像文件出错,当前操作:" + action); + main.log("之前的10个操作是:\n" + prevList.toString()); + main.log("接下来10个操作是:\n" + nextList.toString()); core.ui.drawConfirmBox("录像文件出错,你想回到上个节点吗?", function () { core.ui.closePanel(); if (core.status.replay.save.length > 0) { diff --git a/libs/maps.js b/libs/maps.js index f09d6b27..9d05a272 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -30,11 +30,14 @@ maps.prototype.loadFloor = function (floorId, map) { map = {"map": map}; } var content = {}; - ["floorId", "title", "name", "canFlyTo", "canUseQuickShop", "cannotViewMap", "cannotMoveDirectly", "color", "weather", - "defaultGround", "images", "item_ratio", "upFloor", "bgm", "downFloor", "underGround"].forEach(function (e) { - if (map[e] != null) content[e] = core.clone(map[e]); - else content[e] = core.clone(floor[e]); - }); + for (var name in floor) { + if (name != 'map' && name != 'bgmap' && name != 'fgmap' && floor[name] != null) + content[name] = core.clone(floor[name]); + } + for (var name in map) { + if (name != 'map' && name != 'bgmap' && name != 'fgmap' && map[name] != null) + content[name] = core.clone(map[name]); + } map = this.decompressMap(map.map, floorId); // 事件处理 content['blocks'] = this._mapIntoBlocks(map, floor, floorId); From 3b1c8b8a8095e673e54abc65c59c94e23dc72e5d Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 22:08:15 +0800 Subject: [PATCH 12/64] Fix animate async --- libs/actions.js | 4 ++-- libs/control.js | 23 ++++++++++------------- libs/events.js | 6 +++++- libs/maps.js | 16 ++++++---------- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index ad1c3c1d..90a42d46 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -717,7 +717,7 @@ actions.prototype._sys_keyDownCtrl = function () { } if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' && !core.status.event.data.current.noSkip) { - if (core.timeout.sleepTimeout && Object.keys(core.animateFrame.asyncId).length == 0) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { clearTimeout(core.timeout.sleepTimeout); core.timeout.sleepTimeout = null; core.doAction(); @@ -752,7 +752,7 @@ actions.prototype._sys_longClick_lockControl = function (x, y) { // 长按可以跳过等待事件 if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' && !core.status.event.data.current.noSkip) { - if (core.timeout.sleepTimeout && Object.keys(core.animateFrame.asyncId).length == 0) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { clearTimeout(core.timeout.sleepTimeout); core.timeout.sleepTimeout = null; core.doAction(); diff --git a/libs/control.js b/libs/control.js index fa0eaf5a..fce75b34 100644 --- a/libs/control.js +++ b/libs/control.js @@ -169,25 +169,22 @@ control.prototype._animationFrame_animate = function (timestamp) { if (timestamp - core.animateFrame.animateTime < 50 || !core.status.animateObjs || core.status.animateObjs.length == 0) return; core.clearMap('animate'); // 更新帧 - var animateObjs = []; - for (var i=0;i 0 || (core.status.animateObjs || []).length > 0; +} + ////// 跟随 ////// events.prototype.follow = function (name) { core.status.hero.followers = core.status.hero.followers || []; diff --git a/libs/maps.js b/libs/maps.js index 9d05a272..bc874952 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1832,18 +1832,17 @@ maps.prototype.drawAnimate = function (name, x, y, callback) { // 播放音效 core.playSound(animate.se); - var animateId = parseInt(Math.random() * 100000000); + var id = setTimeout(null); core.status.animateObjs.push({ + "id": id, "animate": animate, "centerX": centerX, "centerY": centerY, "index": 0, - "id": animateId, "callback": callback }); - core.animateFrame.asyncId[animateId] = true; - return animateId; + return id; } ////// 绘制动画的某一帧 ////// @@ -1881,7 +1880,6 @@ maps.prototype.stopAnimate = function (id, doCallback) { for (var i = 0; i < core.status.animateObjs.length; i++) { var obj = core.status.animateObjs[i]; if (obj.id == id) { - delete core.animateFrame.asyncId[obj.id]; if (doCallback) { (function (callback) { setTimeout(function () { @@ -1890,10 +1888,8 @@ maps.prototype.stopAnimate = function (id, doCallback) { })(obj.callback); } } - core.status.animateObjs.splice(i, 1); - if (core.status.animateObjs.length == 0) { - core.clearMap('animate'); - } - break; } + core.status.animateObjs = core.status.animateObjs.filter(function (x) { return x.id != id }); + if (core.status.animateObjs.length == 0) + core.clearMap('animate'); } From 6e5e8f46abd31cb18bca5eae974c47ac2c5dcf74 Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sat, 30 Mar 2019 11:13:11 -0400 Subject: [PATCH 13/64] shop_event & unknow_event --- _docs/event.md | 6 ---- _server/MotaAction.g4 | 71 ++++++++++++++++++++++++++++----------- _server/data.comment.js | 6 ---- _server/editor_blockly.js | 7 +++- project/data.js | 11 ++++++ project/events.js | 31 +++++++++++++---- 6 files changed, 93 insertions(+), 39 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index 57825ba7..3beb965a 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -682,12 +682,6 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ] ``` -### addToList:将本公共事件插入到快捷列表中 - -使用 `{"type": "addToList"}` 可以将当前公共事件插入到快捷商店列表中。 - -此项只能在公共事件中被执行。详见[公共事件](personalization#公共事件)。 - ### setBlock:设置某个图块 我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index c1c31050..1fa52eba 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -65,6 +65,7 @@ return code; shoplist : shopsub + | shopcommonevent | emptyshop ; @@ -77,6 +78,24 @@ var code = ' \n'; return code; */; +shopcommonevent + : '商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? '执行的公共事件 id' EvalString '参数列表' EvalString? + +/* shopcommonevent +tooltip : 全局商店, 执行一个公共事件 +helpUrl : https://h5mota.com/games/template/docs/#/ +default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] +var code = { + 'id': IdString_0, + 'textInList': EvalString_0, + 'mustEnable': Bool_0, + 'commonEvent': EvalString_1, + 'args': EvalString_2 +} +code=JSON.stringify(code,null,2)+',\n'; +return code; +*/; + shopsub : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '未开启状态则不显示在列表中' Bool BGNL? NewLine '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND @@ -240,7 +259,6 @@ action | insert_2_s | revisit_s | exit_s - | addToList_s | setBlock_s | showFloorImg_s | hideFloorImg_s @@ -304,6 +322,7 @@ action | callBook_s | callSave_s | callLoad_s + | unknow_s | function_s | pass_s ; @@ -694,18 +713,6 @@ var code = '{"type": "exit"},\n'; return code; */; -addToList_s - : '将本公共事件插入到快捷列表中' Newline - - -/* addToList_s -tooltip : addToList: 将本公共事件插入到快捷列表中 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=exit%EF%BC%9A%E7%AB%8B%E5%88%BB%E7%BB%93%E6%9D%9F%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 -colour : this.eventColor -var code = '{"type": "addToList"},\n'; -return code; -*/; - setBlock_s : '转变图块为' Int 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline @@ -1739,6 +1746,19 @@ var code = '{"type": "callLoad"},\n'; return code; */; +unknow_s + : '自定义事件' BGNL? RawEvalString + +/* unknow_s +tooltip : 通过脚本自定义的事件类型, 以及编辑器不识别的事件类型 +helpUrl : https://h5mota.com/games/template/docs/#/ +default : ['{"type":"eventType1"}'] +colour : this.dataColor +var tempobj={}; +eval("tempobj='"+RawEvalString_0+"'"); +var code = tempobj +',\n'; +return code; +*/; function_s : '自定义JS脚本' '不自动执行下一个事件' Bool BGNL? Newline RawEvalString Newline BEND Newline @@ -2127,10 +2147,21 @@ ActionParser.prototype.parse = function (obj,type) { obj.id,obj.name,obj.icon,obj.textInList,obj.commonTimes,obj.mustEnable,obj.use,obj.need,parser.EvalString(obj.text),text_choices,next ]); } + var buildcommentevent = function(obj,parser,next){ + return MotaActionBlocks['shopcommonevent'].xmlText([ + obj.id,parser.EvalString(obj.textInList),obj.mustEnable,parser.EvalString(obj.commonEvent),parser.EvalString(obj.args),next + ]); + } var next=null; if(!obj)obj=[]; while(obj.length){ - next=buildsub(obj.pop(),this,next); + var shopobj=obj.pop() + if(shopobj.choices) + next=buildsub(shopobj,this,next); + else if(shopobj.commonEvent) + next=buildcommentevent(shopobj,this,next); + else + throw new Error("[警告]出错啦!\n"+shopobj.id+" 无效的商店"); } return MotaActionBlocks['shop_m'].xmlText([next]); @@ -2617,14 +2648,16 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['exit_s'].xmlText([ this.next]); break; - case "addToList": // 立刻结束事件 - this.next = MotaActionBlocks['addToList_s'].xmlText([ - this.next]); - break; case "animateImage": // 兼容 animateImage break; default: - throw new Error("[警告]出错啦!\n"+data.type+" 事件不被支持..."); + var rawdata = JSON.stringify(data,function(k,v){ + if(typeof(v)=='string')return v.split('\n').join('\\n'); + else return v; + },2); + rawdata=rawdata.split('\n').join('\\n'); + this.next = MotaActionBlocks['unknow_s'].xmlText([ + rawdata,this.next]); } this.parseAction(); return; diff --git a/_server/data.comment.js b/_server/data.comment.js index 8df587e1..c4ad3175 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -660,12 +660,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否在经过领域/夹击/路障等伤害后禁用快捷商店。" }, - "quickCommonEvents": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否使用自定义的公共事件列表来代替快捷商店列表。\n如果此项开启,则快捷商店列表中将会显示加入的公共事件。" - }, "checkConsole": { "_leaf": true, "_type": "checkbox", diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index ee887b02..92ecfee2 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -37,6 +37,11 @@ editor_blockly = function () { {"text": "防御+4", "effect": "status:def+=4"}, {"text": "魔防+10", "effect": "status:mdef+=10"} ] + },{ + "id": "keyShop1", + "textInList": "回收钥匙商店", + "commonEvent": "回收钥匙商店", + "args": "" }],'shop'), MotaActionBlocks['afterBattle_m'].xmlText(), MotaActionBlocks['afterGetItem_m'].xmlText(), @@ -123,7 +128,6 @@ editor_blockly = function () { MotaActionBlocks['hideBgFgMap_s'].xmlText(), MotaActionBlocks['trigger_s'].xmlText(), MotaActionBlocks['insert_1_s'].xmlText(), - MotaActionBlocks['addToList_s'].xmlText(), MotaActionBlocks['insert_2_s'].xmlText(), MotaActionBlocks['move_s'].xmlText(), MotaActionBlocks['jump_s'].xmlText(), @@ -584,6 +588,7 @@ function omitedcheckUpdateFunction(event) { 'showTextImage_s': 'EvalString_0', 'function_s': 'RawEvalString_0', 'shopsub': 'EvalString_3', + 'unknow_s': 'RawEvalString_0', } var f = b ? textStringDict[b.type] : null; if (f) { diff --git a/project/data.js b/project/data.js index 6173ffed..5b612081 100644 --- a/project/data.js +++ b/project/data.js @@ -249,6 +249,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "name": "贪婪之神", "icon": "blueShop", "textInList": "1F金币商店", + "commonTimes": false, + "mustEnable": false, "use": "money", "need": "20+10*times*(times+1)", "text": "勇敢的武士啊,给我${need}金币就可以:", @@ -276,6 +278,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "name": "经验之神", "icon": "pinkShop", "textInList": "1F经验商店", + "commonTimes": false, + "mustEnable": false, "use": "experience", "need": "-1", "text": "勇敢的武士啊,给我若干经验就可以:", @@ -296,6 +300,13 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "effect": "status:def+=5" } ] + }, + { + "id": "keyShop1", + "textInList": "1F回收钥匙商店", + "mustEnable": false, + "commonEvent": "回收钥匙商店", + "args": "" } ], "levelUp": [ diff --git a/project/events.js b/project/events.js index b546b4a2..cfc58540 100644 --- a/project/events.js +++ b/project/events.js @@ -241,18 +241,15 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "回收钥匙商店": [ { "type": "comment", - "text": "公共事件:回收钥匙商店" - }, - { - "type": "addToList" + "text": "此事件在全局商店中被引用了(全局商店keyShop1)" }, { "type": "comment", - "text": "使用上述事件并在全塔属性打开quickCommonEvent开关" + "text": "解除引用前勿删除此事件" }, { "type": "comment", - "text": "就可以在快捷列表(V键)中使用本公共事件" + "text": "玩家在快捷列表(V键)中可以使用本公共事件" }, { "type": "while", @@ -300,7 +297,27 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = 255, 1 ], - "action": [] + "action": [ + { + "type": "if", + "condition": "item:blueKey >= 1", + "true": [ + { + "type": "addValue", + "name": "item:blueKey", + "value": "-1" + }, + { + "type": "addValue", + "name": "status:money", + "value": "50" + } + ], + "false": [ + "\t[商人,woman]你没有蓝钥匙!" + ] + } + ] }, { "text": "离开", From 48c1555e30a528010ff0f355fef211e9e769b38d Mon Sep 17 00:00:00 2001 From: oc Date: Sat, 30 Mar 2019 23:27:45 +0800 Subject: [PATCH 14/64] Add docs --- _docs/_sidebar.md | 1 - _docs/element.md | 91 +++++++++++------------------------------ _docs/img/keyboard.png | Bin 0 -> 54598 bytes _docs/index.md | 3 +- _docs/start.md | 19 ++++++++- 5 files changed, 43 insertions(+), 71 deletions(-) create mode 100644 _docs/img/keyboard.png diff --git a/_docs/_sidebar.md b/_docs/_sidebar.md index a7960592..039c2ea0 100644 --- a/_docs/_sidebar.md +++ b/_docs/_sidebar.md @@ -3,5 +3,4 @@ - [元件说明](element) - [事件](event) - [个性化](personalization) -- [V2.0版本介绍](V2.0) - [附录:API列表](api) diff --git a/_docs/element.md b/_docs/element.md index 8d352fef..4fed9b92 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -1,6 +1,6 @@ # 元件说明 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 @@ -56,6 +56,8 @@ type为该装备的类型,必填,和上面装备栏一一对应。例如,0 atk/def/mdef为该装备分别增加的攻防魔防数值(支持负数);如果不加也可省略不写。 +从V2.6开始,可以拓展到任何勇士的属性,如hpmax, atk, def, mdef, experience等等;自行添加的属性包括攻速speed也能使用。 + animate为该装备的攻击动画,仅对type为0时有效。具体可参见[动画和天气系统](#动画和天气系统)。 percentage为该装备是否按比例增加属性。 @@ -151,49 +153,13 @@ yellowWall, blueWall, whiteWall 怪物的特殊属性所对应的数字(special)在脚本编辑中的`getSpecials`中定义,请勿对已有的属性进行修改。 -``` js -function() { - // 获得怪物的特殊属性,每一行定义一个特殊属性。 - // 分为三项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 - // 可以直接写字符串,也可以写个function将怪物传进去 - return [ - [1, "先攻", "怪物首先攻击"], - [2, "魔攻", "怪物无视勇士的防御"], - [3, "坚固", "勇士每回合最多只能对怪物造成1点伤害"], - [4, "2连击", "怪物每回合攻击2次"], - [5, "3连击", "怪物每回合攻击3次"], - [6, function(enemy) {return (enemy.n||4)+"连击";}, function(enemy) {return "怪物每回合攻击"+(enemy.n||4)+"次";}], - [7, "破甲", "战斗前,怪物附加角色防御的"+Math.floor(100*core.values.breakArmor||0)+"%作为伤害"], - [8, "反击", "战斗时,怪物每回合附加角色攻击的"+Math.floor(100*core.values.counterAttack||0)+"%作为伤害,无视角色防御"], - [9, "净化", "战斗前,怪物附加勇士魔防的"+core.values.purify+"倍作为伤害"], - [10, "模仿", "怪物的攻防和勇士攻防相等"], - [11, "吸血", function (enemy) {return "战斗前,怪物首先吸取角色的"+Math.floor(100*enemy.value||0)+"%生命作为伤害"+(enemy.add?",并把伤害数值加到自身生命上":"");}], - [12, "中毒", "战斗后,勇士陷入中毒状态,每一步损失生命"+core.values.poisonDamage+"点"], - [13, "衰弱", "战斗后,勇士陷入衰弱状态,攻防暂时下降"+(core.values.weakValue>=1?core.values.weakValue+"点":parseInt(core.values.weakValue*100)+"%")], - [14, "诅咒", "战斗后,勇士陷入诅咒状态,战斗无法获得金币和经验"], - [15, "领域", function (enemy) {return "经过怪物周围"+(enemy.range||1)+"格时自动减生命"+(enemy.value||0)+"点";}], - [16, "夹击", "经过两只相同的怪物中间,勇士生命值变成一半"], - [17, "仇恨", "战斗前,怪物附加之前积累的仇恨值作为伤害"+(core.flags.hatredDecrease?";战斗后,释放一半的仇恨值":"")+"。(每杀死一个怪物获得"+(core.values.hatred||0)+"点仇恨值)"], - [18, "阻击", function (enemy) {return "经过怪物的十字领域时自动减生命"+(enemy.value||0)+"点,同时怪物后退一格";}], - [19, "自爆", "战斗后勇士的生命值变成1"], - [20, "无敌", "勇士无法打败怪物,除非拥有十字架"], - [21, "退化", function (enemy) {return "战斗后勇士永久下降"+(enemy.atkValue||0)+"点攻击和"+(enemy.defValue||0)+"点防御";}], - [22, "固伤", function (enemy) {return "战斗前,怪物对勇士造成"+(enemy.damage||0)+"点固定伤害,无视勇士魔防。";}], - [23, "重生", "怪物被击败后,角色转换楼层则怪物将再次出现"], - [24, "激光", function (enemy) {return "经过怪物同行或同列时自动减生命"+(enemy.value||0)+"点";}] - ]; -} -``` - 多属性可采用数组的写法,比如`'special': [1,3]`视为同时拥有先攻和坚固属性;`'special': [5,10,14,18]`视为拥有3连击、魔防、诅咒、阻击四个属性。 -怪物可以负伤,在`data.js`的全局变量`enableNegativeDamage`中指定。 - -下面的`getSpecialHint`函数则给定了每个特殊属性的详细描述。这个描述将在怪物手册中看到。 +怪物可以负伤,在全塔属性的全局变量`enableNegativeDamage`中指定。 打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](event#加点事件)。 -如果`data.js`中的enableExperience为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。 +如果全塔属性中的enableExperience为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。 拿到幸运金币后,打怪获得的金币将翻倍。 @@ -286,16 +252,12 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 ## 路障,楼梯,传送门 -血网的伤害数值、中毒后每步伤害数值、衰弱时暂时攻防下降的数值,都在 `data.js` 的values内定义。 +血网的伤害数值、中毒后每步伤害数值、衰弱时暂时攻防下降的数值,都在全塔属性的values内定义。 路障同样会尽量被自动寻路绕过。 有关楼梯和传送门,必须在该层样板的changeFloor里指定传送点的目标。 -![楼层转换](./img/changefloor.png) - -!> **请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。如(6,0)代表最上面一行的正中间一列。** - floorId指定的是目标楼层的唯一标识符(ID)。 也可以写`"floorId": ":before"`和`"floorId": ":next"`表示上一楼和下一楼。 @@ -347,7 +309,7 @@ floorId指定的是目标楼层的唯一标识符(ID)。 从V2.4开始,H5魔塔开始支持大地图。 -大地图在创建时可以指定宽高,要求**宽和高都不得小于13,且宽高之积不超过1000**。 +大地图在创建时可以指定宽高,要求**宽和高都不得小于13(15x15版本则是不小于15),且宽高之积不超过1000**。 大地图一旦创建成功则不得修改宽高数值。 @@ -355,13 +317,12 @@ floorId指定的是目标楼层的唯一标识符(ID)。 现在我们的H5魔塔支持播放动画,也支持天气系统了。 -要播放动画,你需要先使用“RM动画导出器”将动画导出,放在animates目录下,然后再data.js中定义。 +要播放动画,你需要先使用“RM动画导出器”将动画导出,放在animates目录下,然后在全塔属性的animates中定义。 ``` js -"animates": [// 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 - // 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 - "hand", "sword", "zone", "yongchang", "thunder" // 根据需求自行添加 -] +// 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 +// 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 +"animates": ["hand", "sword", "zone", "yongchang", "thunder"] ``` !> 动画必须是animate格式,名称不能使用中文,不能带空格或特殊字符。 @@ -376,9 +337,9 @@ floorId指定的是目标楼层的唯一标识符(ID)。 !> 播放录像时,将默认忽略所有动画。 -目前天气系统只支持雨和雪两种天气。 +目前天气系统支持雨和雪和雾两种天气。 -在每层楼的剧本文件里存在一个weather选项,表示该层楼的默认天气。 +在每层楼的楼层属性中存在一个weather选项,表示该层楼的默认天气。 ``` js // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain","snow"或"fog"代表雨雪雾,第二项为1-10之间的数代表强度。 @@ -394,24 +355,18 @@ floorId指定的是目标楼层的唯一标识符(ID)。 要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在全塔属性中进行定义 ``` js -"bgms": [ // 在此存放所有的bgm,和文件名一致。 - // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 - 'bgm.mp3' -]; -"sounds": [ // 在此存放所有的SE,和文件名一致 - // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 - 'floor.mp3', 'attack.mp3', 'door.mp3', 'item.mp3', 'zone.mp3' -] +// 在此存放所有的bgm,和文件名一致。 +// 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 +"bgms": ["bgm.mp3"] + +// 在此存放所有的SE,和文件名一致 +"sounds": ["floor.mp3", "attack.mp3", "door.mp3", "item.mp3", "zone.mp3"] ``` !> 音频名不能使用中文,不能带空格或特殊字符。 -目前BGM支持主流的音乐格式,如mp3, ogg,格式等。不支持mid格式的播放。 - 定义完毕后,我们可以调用`playBgm`/`playSound`事件来播放对应的音乐/音效,有关事件的详细介绍请参见[事件](event)。 **另外,考虑到用户的流量问题,将遵循如下规则:** @@ -443,7 +398,7 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 录像的回放主要有两种方式: 1. 保存成的录像文件(.h5route文件):在标题界面点录像回放,再选择文件即可。 -2. 游戏过程中时的当前录像:随时按R可以进行回放;手机端则长按任何位置3秒以上调出虚拟键盘,再按R。 +2. 游戏过程中时的当前录像:随时按R可以进行回放;手机端则可调出虚拟键盘,再按R。 录像播放过程中,可以进行如下操作: @@ -474,6 +429,9 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 ## 操作说明 +![](img/keyboard.png) + +   diff --git a/_docs/img/keyboard.png b/_docs/img/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..c2b9d2412fab08efa3397e4e09b7bdcdc915f79e GIT binary patch literal 54598 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 a==ki|Rk||P`-|>BC+o7c1Ej^$FY!OF+#~-0 literal 0 HcmV?d00001 diff --git a/_docs/index.md b/_docs/index.md index cb6a896c..d2f290e2 100644 --- a/_docs/index.md +++ b/_docs/index.md @@ -1,11 +1,10 @@ # HTML5 魔塔样板说明文档 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 但是,现在我们有了HTML5。 HTML5的画布(canvas)以及它被Android/iOS内置浏览器所支持的特性,可以让我们做出真正意义上的全平台覆盖的魔塔。 -事实上,在贴吧的试水发布也证明了,H5魔塔确实是可以成功的。两部即使是复刻的魔塔也受到了不少人的追捧,其流畅的手感和全平台支持的特性,也让很多没办法打开电脑的人爱不释手。 然而,一般而言使用非RMXP制作魔塔往往需要一定的编程技术,HTML5魔塔自然也不例外。但是,为了能让大家更加注重于“做塔”本身,而不用考虑做塔以外的各种脚本问题,我特意制作了这样一部HTML5的魔塔样板。 diff --git a/_docs/start.md b/_docs/start.md index a68ac244..004ea21f 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -1,6 +1,6 @@ # 快速上手 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! @@ -28,6 +28,7 @@ * “地图编辑器”允许你以可视化的方式进行编辑地图。 * “便捷PS工具”能让你很方便的对自定义素材进行添加。参见[自定义素材](personalization#自定义素材)。 * “地图生成器”能让你从已有的截图(如RMXP项目)中立刻生成可被本样板识别的地图数据。 +* “怪物数据导出”能让你从RMXP中导出怪物数据而被H5魔塔使用。 * “RM动画导出器”能让你从RMXP中导出动画而被H5魔塔使用。 * “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。 * “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。 @@ -54,6 +55,8 @@ ### 从RMXP导入已有的地图 +!> 注:现在已经不推荐此方法,如需从RM刻塔请使用 [RM转H5刻塔器使用教程](https://www.bilibili.com/video/av43125840) 进行操作。 + 如果我们想复刻一个现有的,已经被RMXP所制作的塔,也有很便捷的方式,那就是用到我们的“地图生成器”。 首先,我们打开RMXP和对应的项目,可以看到它的地图。 @@ -176,7 +179,7 @@ 之后刷新编辑器即可。 -对于怪物和道具,我们也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 +也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 @@ -230,6 +233,18 @@ HTML5的塔都是可以进行控制台调试的。 更多API和详细参数介绍可参见[API列表](api)。 +## 编辑器的基本操作 + +- **Alt+0~9, Ctrl+0~9** 保存和读取当前选中图块 +- **W/A/S/D** 移动大地图 +- **Ctrl+Z** 撤销上次绘图 +- **Ctrl+Y** 重做上次绘图 +- **PgUp/PgDn** 切换楼层 +- **Ctrl+S** 保存事件编辑器/脚本编辑器 +- **地图上单击** 选中该点 +- **地图上双击** 选中该点图块 +- **地图上右键** 弹出菜单栏,包括选中、复制、清除等操作 +- **事件编辑器中Ctrl+C, Ctrl+X, 右键等** 执行相应操作 ## 报错处理 From 8aca0760c090536d05dbf924840e65bcd2e1b67d Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 00:22:26 +0800 Subject: [PATCH 15/64] commonEventShop --- _server/MotaAction.g4 | 48 ++++++++++++++++++++++++-------------- _server/editor_blockly.js | 1 + libs/actions.js | 49 +++++++++++---------------------------- libs/control.js | 17 +++++--------- libs/events.js | 49 +++++++++++++-------------------------- libs/ui.js | 18 +++++--------- libs/utils.js | 7 +----- project/data.js | 6 ++--- 8 files changed, 77 insertions(+), 118 deletions(-) diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 1fa52eba..bb3e0f44 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -85,13 +85,25 @@ shopcommonevent tooltip : 全局商店, 执行一个公共事件 helpUrl : https://h5mota.com/games/template/docs/#/ default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] +if (EvalString_2) { + if (EvalString_2.indexOf('"')>=0) + throw new Error('请勿在此处使用双引号!尝试使用单引号吧~'); + // 检查是不是数组 + try { + EvalString_2 = JSON.parse(EvalString_2.replace(/'/g, '"')); + if (!(EvalString_2 instanceof Array)) throw new Error(); + } + catch (e) { + throw new Error('参数列表必须是个有效的数组!'); + } +} var code = { 'id': IdString_0, 'textInList': EvalString_0, 'mustEnable': Bool_0, - 'commonEvent': EvalString_1, - 'args': EvalString_2 + 'commonEvent': EvalString_1 } +if (EvalString_2) code.args = EvalString_2; code=JSON.stringify(code,null,2)+',\n'; return code; */; @@ -322,7 +334,7 @@ action | callBook_s | callSave_s | callLoad_s - | unknow_s + | unknown_s | function_s | pass_s ; @@ -1746,17 +1758,19 @@ var code = '{"type": "callLoad"},\n'; return code; */; -unknow_s +unknown_s : '自定义事件' BGNL? RawEvalString -/* unknow_s +/* unknown_s tooltip : 通过脚本自定义的事件类型, 以及编辑器不识别的事件类型 helpUrl : https://h5mota.com/games/template/docs/#/ -default : ['{"type":"eventType1"}'] +default : ['{"type":"test", "data": "这是自定义的参数"}'] colour : this.dataColor -var tempobj={}; -eval("tempobj='"+RawEvalString_0+"'"); -var code = tempobj +',\n'; +try { + var tempobj = JSON.parse(RawEvalString_0); +} catch (e) {throw new Error("不合法的JSON格式!");} +if (!tempobj.type) throw new Error("自定义事件需要一个type:xxx"); +var code = JSON.stringify(tempobj) +',\n'; return code; */; @@ -2148,8 +2162,13 @@ ActionParser.prototype.parse = function (obj,type) { ]); } var buildcommentevent = function(obj,parser,next){ + if (obj.args instanceof Array) { + try { obj.args = JSON.stringify(obj.args).replace(/"/g, "'"); } + catch (e) {obj.args = '';} + } + else obj.args = null; return MotaActionBlocks['shopcommonevent'].xmlText([ - obj.id,parser.EvalString(obj.textInList),obj.mustEnable,parser.EvalString(obj.commonEvent),parser.EvalString(obj.args),next + obj.id,parser.EvalString(obj.textInList),obj.mustEnable,parser.EvalString(obj.commonEvent),obj.args,next ]); } var next=null; @@ -2651,13 +2670,8 @@ ActionParser.prototype.parseAction = function() { case "animateImage": // 兼容 animateImage break; default: - var rawdata = JSON.stringify(data,function(k,v){ - if(typeof(v)=='string')return v.split('\n').join('\\n'); - else return v; - },2); - rawdata=rawdata.split('\n').join('\\n'); - this.next = MotaActionBlocks['unknow_s'].xmlText([ - rawdata,this.next]); + this.next = MotaActionBlocks['unknown_s'].xmlText([ + JSON.stringify(data),this.next]); } this.parseAction(); return; diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 92ecfee2..5aca5463 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -159,6 +159,7 @@ editor_blockly = function () { ], '原生脚本':[ MotaActionBlocks['function_s'].xmlText(), + MotaActionBlocks['unknown_s'].xmlText(), ], '值块':[ MotaActionBlocks['setValue_s'].xmlText([ diff --git a/libs/actions.js b/libs/actions.js index 90a42d46..917af40f 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1198,35 +1198,21 @@ actions.prototype._keyUpShop = function (keycode) { ////// 快捷商店界面时的点击操作 ////// actions.prototype._clickQuickShop = function (x, y) { - var keys = []; - if (core.flags.quickCommonEvents) { - keys = core.getFlag("__commonEventList__", []); - } - else { - keys = Object.keys(core.status.shops).filter(function (shopId) { - return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable - }); - } + var keys = Object.keys(core.status.shops).filter(function (shopId) { + return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable + }); if (x >= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { var topIndex = this.HSIZE - parseInt(keys.length / 2); if (y >= topIndex && y < topIndex + keys.length) { - if (core.flags.quickCommonEvents) { - var name = keys[y - topIndex]; - core.ui.closePanel(); - core.status.route.push("common:" + core.encodeBase64(name)); - core.insertAction(name); - } - else { - var reason = core.events.canUseQuickShop(keys[y - topIndex]); - if (!core.flags.enableDisabledShop && reason) { - core.drawText(reason); - return; - } - core.events.openShop(keys[y - topIndex], true); - if (core.status.event.id == 'shop') - core.status.event.data.fromList = true; + var reason = core.events.canUseQuickShop(keys[y - topIndex]); + if (!core.flags.enableDisabledShop && reason) { + core.drawText(reason); + return; } + core.events.openShop(keys[y - topIndex], true); + if (core.status.event.id == 'shop') + core.status.event.data.fromList = true; } // 离开 else if (y == topIndex + keys.length) @@ -1240,17 +1226,10 @@ actions.prototype._keyUpQuickShop = function (keycode) { core.ui.closePanel(); return; } - var length = 0; - if (core.flags.quickCommonEvents) { - length = core.getFlag("__commonEventList__", []).length; - } - else { - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { - return shopList[shopId].visited || !shopList[shopId].mustEnable - }); - length = keys.length; - } - this._selectChoices(length + 1, keycode, this._clickQuickShop); + var keys = Object.keys(core.status.shops).filter(function (shopId) { + return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable + }); + this._selectChoices(keys.length + 1, keycode, this._clickQuickShop); return; } diff --git a/libs/control.js b/libs/control.js index fce75b34..7a1de28b 100644 --- a/libs/control.js +++ b/libs/control.js @@ -33,7 +33,6 @@ control.prototype._init = function () { this.registerReplayAction("fly", this._replayAction_fly); this.registerReplayAction("shop", this._replayAction_shop); this.registerReplayAction("turn", this._replayAction_turn); - this.registerReplayAction("common", this._replayAction_common); this.registerReplayAction("getNext", this._replayAction_getNext); this.registerReplayAction("moveDirectly", this._replayAction_moveDirectly); this.registerReplayAction("key", this._replayAction_key); @@ -1412,6 +1411,12 @@ control.prototype._replayAction_shop = function (action) { if (selections.length == 0) return false; var shop=core.status.shops[shopId]; if (!shop || !shop.visited) return false; + // --- 判定commonEvent + if (shop.commonEvent) { + core.openShop(shopId, false); + setTimeout(core.replay); + return true; + } var choices = shop.choices; var topIndex = core.__HALF_SIZE__ - parseInt(choices.length / 2); core.status.event.selection = parseInt(selections.shift()); @@ -1443,16 +1448,6 @@ control.prototype._replayAction_turn = function (action) { return true; } -control.prototype._replayAction_common = function (action) { - if (action.indexOf("common:") != 0) return false; - var name = core.decodeBase64(action.substring(7)); - if (core.getFlag("__commonEventList__").indexOf(name) == -1) return false; - core.status.route.push(action); - core.insertAction(name); - setTimeout(core.replay); - return true; -} - control.prototype._replayAction_getNext = function (action) { if (action != "getNext") return false; if (!core.getNextItem()) return false; diff --git a/libs/events.js b/libs/events.js index 0c60d6aa..768cc35d 100644 --- a/libs/events.js +++ b/libs/events.js @@ -806,10 +806,7 @@ events.prototype.insertAction = function (action, x, y, callback, addToLast) { // ------ 判定commonEvent var commonEvent = this.getCommonEvent(action); - if (commonEvent instanceof Array) { - this._addCommentEventToList(action, commonEvent); - action = commonEvent; - } + if (commonEvent instanceof Array) action = commonEvent; if (!action) return; if (core.status.event.id != 'action') { @@ -830,22 +827,6 @@ events.prototype.getCommonEvent = function (name) { return this.commonEvent[name] || null; } -events.prototype._addCommentEventToList = function (name, list) { - if (list == null) list = this.getCommonEvent(name); - if (!list || !core.flags.quickCommonEvents) return; - var addToList = false; - for (var x in list) { - if (list[x].type == 'addToList') { - addToList = true; - break; - } - } - if (!addToList) return; - var obj = core.getFlag("__commonEventList__", []); - if (obj.indexOf(name) == -1) obj.push(name); - core.setFlag("__commonEventList__", obj); -} - ////// 恢复一个事件 ////// events.prototype.recoverEvents = function (data) { if (data) { @@ -1144,13 +1125,12 @@ events.prototype._action_useItem = function (data, x, y, prefix) { } events.prototype._action_openShop = function (data, x, y, prefix) { - if (core.isReplaying()) { // 正在播放录像,简单将visited置为true - core.status.shops[data.id].visited = true; - this.setEvents([]); - core.doAction(); - } - else + core.status.shops[data.id].visited = true; + this.setEvents([]); + if (!core.isReplaying()) this.openShop(data.id); + if (core.status.event.id == 'action') + core.doAction(); } events.prototype._action_disableShop = function (data, x, y, prefix) { @@ -1207,10 +1187,6 @@ events.prototype._action_insert = function (data, x, y, prefix) { core.doAction(); } -events.prototype._action_addToList = function (data, x, y, prefix) { - core.doAction(); -} - events.prototype._action_playBgm = function (data, x, y, prefix) { core.playBgm(data.name); core.doAction(); @@ -2109,9 +2085,9 @@ events.prototype.openShop = function (shopId, needVisited) { shop.times = shop.times || 0; if (shop.commonTimes) shop.times = core.getFlag('commonTimes', 0); if (needVisited && !shop.visited) { - if (!core.flags.enableDisabledShop) { - if (shop.times == 0) core.drawTip("该商店尚未开启"); - else core.drawTip("该商店已失效"); + if (!core.flags.enableDisabledShop || shop.commonEvent) { + if (shop.times == 0) core.drawTip("该项尚未开启"); + else core.drawTip("该项已失效"); return; } else { @@ -2119,6 +2095,13 @@ events.prototype.openShop = function (shopId, needVisited) { } } else shop.visited = true; + + // --- 商店 + if (shop.commonEvent) { + core.status.route.push("shop:"+shopId+":0"); + core.insertAction({"type": "insert", "name": shop.commonEvent, "args": shop.args}); + return; + } core.ui.drawShop(shopId); } diff --git a/libs/ui.js b/libs/ui.js index 7529f589..8a569922 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1118,18 +1118,12 @@ ui.prototype.drawSettings = function () { ////// 绘制快捷商店选择栏 ////// ui.prototype.drawQuickShop = function () { core.status.event.id = 'selectShop'; - var choices; - if (core.flags.quickCommonEvents) { - choices = core.clone(core.getFlag("__commonEventList__", [])); - } - else { - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { - return shopList[shopId].visited || !shopList[shopId].mustEnable - }); - choices = keys.map(function (shopId) { - return {"text": shopList[shopId].textInList, "color": shopList[shopId].visited?null:"#999999"}; - }); - } + var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { + return shopList[shopId].visited || !shopList[shopId].mustEnable + }); + var choices = keys.map(function (shopId) { + return {"text": shopList[shopId].textInList, "color": shopList[shopId].visited?null:"#999999"}; + }); choices.push("返回游戏"); this.drawChoices(null, choices); } diff --git a/libs/utils.js b/libs/utils.js index 4dbd3e34..d3bac9d3 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -467,8 +467,6 @@ utils.prototype._encodeRoute_encodeOne = function (t) { return "P" + t.substring(6); else if (t.indexOf('input2:') == 0) return "Q" + t.substring(7) + ":"; - else if (t.indexOf('common:') == 0) - return "c" + t.substring(7) + ":"; else if (t == 'no') return 'N'; else if (t.indexOf('move:') == 0) @@ -527,7 +525,7 @@ utils.prototype._decodeRoute_number2id = function (number) { } utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { - var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't' || c == 'c') ? + var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't') ? this._decodeRoute_getString(decodeObj) : this._decodeRoute_getNumber(decodeObj); var mp = {"U": "up", "D": "down", "L": "left", "R": "right"}; @@ -572,9 +570,6 @@ utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { case "Q": decodeObj.ans.push("input2:" + nxt); break; - case "c": - decodeObj.ans.push("common:" + nxt); - break; case "N": decodeObj.ans.push("no"); break; diff --git a/project/data.js b/project/data.js index 5b612081..096e2dc6 100644 --- a/project/data.js +++ b/project/data.js @@ -303,10 +303,9 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = }, { "id": "keyShop1", - "textInList": "1F回收钥匙商店", + "textInList": "回收钥匙商店", "mustEnable": false, - "commonEvent": "回收钥匙商店", - "args": "" + "commonEvent": "回收钥匙商店" } ], "levelUp": [ @@ -408,7 +407,6 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableMoveDirectly": true, "enableDisabledShop": true, "disableShopOnDamage": false, - "quickCommonEvents": false, "checkConsole": false } } \ No newline at end of file From b3fb8cf98a71f075962431b348323522411c25ff Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 00:35:35 +0800 Subject: [PATCH 16/64] remove double click unknow_s --- _server/editor_blockly.js | 1 - 1 file changed, 1 deletion(-) diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 5aca5463..595a55ff 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -589,7 +589,6 @@ function omitedcheckUpdateFunction(event) { 'showTextImage_s': 'EvalString_0', 'function_s': 'RawEvalString_0', 'shopsub': 'EvalString_3', - 'unknow_s': 'RawEvalString_0', } var f = b ? textStringDict[b.type] : null; if (f) { From 05f6b37121e67ae9ab9c9b3cf871ca463d2376fb Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sat, 30 Mar 2019 12:38:56 -0400 Subject: [PATCH 17/64] update refactoring.md --- _server/refactoring.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/_server/refactoring.md b/_server/refactoring.md index 0dd94dd4..9ae0e115 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -46,6 +46,10 @@ editor: { 某些注意到的点 ++ 插入公共事件的参数的转义处理 + ++ 转义改由editor.blockly处理,editor.multi原样接受和返回 + + 地图的编辑与其他(如全塔属性和楼层属性), 现在的文件操作的模式是完全不同的 楼层文件的储存与其他不同 @@ -62,7 +66,7 @@ editor: { + [ ] ? 表格折叠 变为四栏, 可以折叠展开 -+ [ ] blockly对于无法识别的图块原样返回 ++ [x] blockly对于无法识别的图块原样返回 + [ ] ? 简洁的事件方块注册 `editor.registerEvent('log',[['test','Int','测试',0],['floorId','Idstring','楼层','MT0']])` From 52abd1274025e4ff75f812afcb4232023b1d7505 Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sat, 30 Mar 2019 12:54:34 -0400 Subject: [PATCH 18/64] update refactoring.md --- _server/refactoring.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_server/refactoring.md b/_server/refactoring.md index 9ae0e115..90ae5681 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -59,6 +59,8 @@ editor: { ## 功能改进 ++ [ ] .g4中添加ObjectString, 要求其中的值可以JSON.parse, 生成的code中也是作为对象而不是字符串出现 + + [ ] 大地图 在切换时, 每次都回到最左上->每个楼层记录一个位置 四个箭头目前不能长按 From fa5fa402407400b83a4e0c9bd3bfc94a79424caf Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 01:01:13 +0800 Subject: [PATCH 19/64] loadFloor --- libs/maps.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/maps.js b/libs/maps.js index bc874952..445fc113 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -30,12 +30,14 @@ maps.prototype.loadFloor = function (floorId, map) { map = {"map": map}; } var content = {}; + var notCopy = ["firstArrive", "eachArrive", "parallelDo", "map", "bgmap", "fgmap", + "events", "changeFloor", "afterBattle", "afterGetItem", "afterOpenDoor", "cannotMove"]; for (var name in floor) { - if (name != 'map' && name != 'bgmap' && name != 'fgmap' && floor[name] != null) + if (notCopy.indexOf(name) == -1 && floor[name] != null) content[name] = core.clone(floor[name]); } for (var name in map) { - if (name != 'map' && name != 'bgmap' && name != 'fgmap' && map[name] != null) + if (notCopy.indexOf(name) == -1 && map[name] != null) content[name] = core.clone(map[name]); } map = this.decompressMap(map.map, floorId); From 11fe36a44edbb8dfbbfd5dd3e5e5babef6b8f54e Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sat, 30 Mar 2019 19:25:57 -0400 Subject: [PATCH 20/64] editor_game --- _server/editor.js | 119 ++------------------------------------- _server/editor_game.js | 124 +++++++++++++++++++++++++++++++++++++++++ editor-mobile.html | 1 + editor.html | 1 + 4 files changed, 131 insertions(+), 114 deletions(-) create mode 100644 _server/editor_game.js diff --git a/_server/editor.js b/_server/editor.js index 03b0cf01..3d50fa1f 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -43,24 +43,11 @@ editor.info editor.prototype.init = function (callback) { editor_util_wrapper(editor); + editor_game_wrapper(editor, main, core); editor_table_wrapper(editor); var afterMainInit = function () { - core.floors = JSON.parse(JSON.stringify(core.floors, function (k, v) { - if (v instanceof Function) { - return v.toString() - } else return v - })); - core.data = JSON.parse(JSON.stringify(core.data, function (k, v) { - if (v instanceof Function) { - return v.toString() - } else return v - })); - data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = JSON.parse(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, function (k, v) { - if (v instanceof Function) { - return v.toString() - } else return v - })); + editor.game.fixFunctionInGameData(); editor.main = main; editor.core = core; editor.fs = fs; @@ -78,10 +65,10 @@ editor.prototype.init = function (callback) { var afterCoreReset = function () { - editor.idsInit(core.maps, core.icons.icons); // 初始化图片素材信息 + editor.game.idsInit(core.maps, core.icons.icons); // 初始化图片素材信息 editor.drawInitData(core.icons.icons); // 初始化绘图 - editor.fetchMapFromCore(); + editor.game.fetchMapFromCore(); editor.updateMap(); editor.buildMark(); editor.drawEventBlock(); @@ -105,69 +92,6 @@ editor.prototype.init = function (callback) { afterMainInit(); } -editor.prototype.idsInit = function (maps, icons) { - editor.ids = [0]; - editor.indexs = []; - var MAX_NUM = 0; - var keys=Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); - for(var ii=0;iiMAX_NUM && v 32*32*3000){ - alert(imgName+'上的图块数量超过了3000,请修改后刷新页面'); - } - for (var id=startOffset; idMAX_NUM && v 32*32*3000){ + alert(imgName+'上的图块数量超过了3000,请修改后刷新页面'); + } + for (var id=startOffset; id + diff --git a/editor.html b/editor.html index 8f826432..0b5c4187 100644 --- a/editor.html +++ b/editor.html @@ -510,6 +510,7 @@ if (location.protocol.indexOf("http")!=0) { + From cfd3058b7e6ba46decc7b563873f00fa976b7d23 Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sat, 30 Mar 2019 19:35:10 -0400 Subject: [PATCH 21/64] update refactoring.md --- _server/refactoring.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/_server/refactoring.md b/_server/refactoring.md index 90ae5681..695e71ad 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -1,12 +1,11 @@ # 重构 -+ [ ] 按功能拆分文件 -+ [ ] 左侧页面模块化, 方便添加 -+ [ ] 不同的模式的文件操作尽可能模块化 ++ 按功能拆分文件 ++ 左侧页面模块化, 方便添加 ++ 不同的模式的文件操作尽可能模块化 ---- -文件结构 +## 文件结构 + [x] editor_blockly 图块化事件编辑器, 基本不改动 + [x] editor_multi 多行文本编辑器, 基本不改动 @@ -18,9 +17,8 @@ + [ ] 原editor_mode 移除 + [ ] 原vm 移除 ---- -+ [ ] 对象结构 +## 对象结构 ``` editor: { @@ -31,8 +29,8 @@ editor: { table multi blockly + game } - game: 来自游戏的数据 config: 编辑器配置 mode: 当前的模式(左侧的选择) map: 当前编辑层的地图 @@ -44,9 +42,9 @@ editor: { --- -某些注意到的点 +## 某些注意到的点&准备修改的内容 -+ 插入公共事件的参数的转义处理 ++ 插入公共事件的参数的转义处理, .g4中添加ObjectString, 要求其中的值可以JSON.parse, 生成的code中也是作为对象而不是字符串出现 + 转义改由editor.blockly处理,editor.multi原样接受和返回 @@ -57,9 +55,9 @@ editor: { + 目前editor.map中储存的是info\, 准备改为和core一致只储存数字 -## 功能改进 ++ editor.file在修改是不再返回obj和commentobj,只在查询时返回 -+ [ ] .g4中添加ObjectString, 要求其中的值可以JSON.parse, 生成的code中也是作为对象而不是字符串出现 +## 功能改进 + [ ] 大地图 在切换时, 每次都回到最左上->每个楼层记录一个位置 From 553ebe0deceeca3cce61913b8574237b60c4583d Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 11:10:16 +0800 Subject: [PATCH 22/64] fix ths --- libs/maps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/maps.js b/libs/maps.js index 445fc113..b2da2f6f 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1632,7 +1632,7 @@ maps.prototype.jumpBlock = function (sx, sy, ex, ey, time, keep, callback) { this.__playJumpSound(); - var jumpInfo = ths.__generateJumpInfo(sx, sy, ex, ey, time); + var jumpInfo = this.__generateJumpInfo(sx, sy, ex, ey, time); jumpInfo.keep = keep; this._jumpBlock_doJump(blockInfo, canvases, jumpInfo, callback); From 5eea4d33b43df7e4899c2d34652588ee1918d880 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Sun, 31 Mar 2019 14:34:45 +0800 Subject: [PATCH 23/64] add hint for syncSave --- libs/control.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/control.js b/libs/control.js index 7a1de28b..f348c10e 100644 --- a/libs/control.js +++ b/libs/control.js @@ -1635,7 +1635,7 @@ control.prototype._syncSave_http = function (type, saves) { else { core.drawText((type=='all'?"所有存档":"存档"+core.saves.saveIndex)+"同步成功!\n\n您的存档编号: " +response.code+"\n您的存档密码: "+response.msg - +"\n\n请牢记以上两个信息(如截图等),在从服务器\n同步存档时使用。") + +"\n\n请牢记以上两个信息(如截图等),在从服务器\n同步存档时使用。\n\r[yellow]另外请注意,存档同步只会保存一个月的时间。\r") } }, function (e) { core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+e); From 1747eef582c772be96ea398122ad490c1d4ae917 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Sun, 31 Mar 2019 15:01:43 +0800 Subject: [PATCH 24/64] Fix stopSound bug --- libs/control.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/control.js b/libs/control.js index f348c10e..a3afd4fe 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2203,8 +2203,8 @@ control.prototype.stopSound = function () { for (var i in core.musicStatus.playingSounds) { var source = core.musicStatus.playingSounds[i]; try { - if (source[i].stop) source[i].stop(); - else if (source[i].noteOff) source[i].noteOff(); + if (source.stop) source.stop(); + else if (source.noteOff) source.noteOff(); } catch (e) { main.log(e); From 99a4a7b2a2115475c786d86dcab91a420cb0f612 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 19:40:55 +0800 Subject: [PATCH 25/64] playSound stop previous --- _docs/event.md | 2 ++ _server/MotaAction.g4 | 9 +++++---- libs/events.js | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index 3beb965a..cb8f7b82 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1385,6 +1385,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。 +可以加`"stop": true`来停止之前正在播放的音效。 + ### stopSound:停止所有音效 使用`{"type": "stopSound"}`可以停止所有音效。 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index bb3e0f44..2555b855 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -1481,15 +1481,16 @@ return code; */; playSound_s - : '播放音效' EvalString Newline + : '播放音效' EvalString '停止之前音效' Bool? Newline /* playSound_s tooltip : playSound: 播放音效 helpUrl : https://h5mota.com/games/template/docs/#/event?id=playsound%EF%BC%9A%E6%92%AD%E6%94%BE%E9%9F%B3%E6%95%88 -default : ["item.mp3"] +default : ["item.mp3",false] colour : this.soundColor -var code = '{"type": "playSound", "name": "'+EvalString_0+'"},\n'; +Bool_0 = Bool_0 ? ', "stop": true' : ''; +var code = '{"type": "playSound", "name": "'+EvalString_0+'"'+Bool_0+'},\n'; return code; */; @@ -2495,7 +2496,7 @@ ActionParser.prototype.parseAction = function() { break; case "playSound": this.next = MotaActionBlocks['playSound_s'].xmlText([ - data.name,this.next]); + data.name,data.stop,this.next]); break; case "playBgm": this.next = MotaActionBlocks['playBgm_s'].xmlText([ diff --git a/libs/events.js b/libs/events.js index 768cc35d..bfabdb3c 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1213,6 +1213,7 @@ events.prototype._action_freeBgm = function (data, x, y, prefix) { } events.prototype._action_playSound = function (data, x, y, prefix) { + if (data.stop) core.stopSound(); core.playSound(data.name); core.doAction(); } From cf382a12714bebc485d459633791c9a07467a1c0 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 19:52:37 +0800 Subject: [PATCH 26/64] encode unknown route --- libs/utils.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/utils.js b/libs/utils.js index d3bac9d3..2aab843f 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -475,7 +475,7 @@ utils.prototype._encodeRoute_encodeOne = function (t) { return 'K' + t.substring(4); else if (t.indexOf('random:') == 0) return 'X' + t.substring(7); - return ''; + return '('+t+')'; } ////// 解密路线 ////// @@ -485,7 +485,7 @@ utils.prototype.decodeRoute = function (route) { // 解压缩 try { var v = LZString.decompressFromBase64(route); - if (/^[a-zA-Z0-9+\/=:]*$/.test(v)) { + if (/^[a-zA-Z0-9+\/=:()]*$/.test(v)) { route = v; } } catch (e) { @@ -525,6 +525,15 @@ utils.prototype._decodeRoute_number2id = function (number) { } utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) { + // --- 特殊处理自定义项 + if (c == '(') { + var idx = decodeObj.route.indexOf(')', decodeObj.index); + if (idx >= 0) { + decodeObj.ans.push(decodeObj.route.substring(decodeObj.index, idx)); + decodeObj.index = idx + 1; + return; + } + } var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't') ? this._decodeRoute_getString(decodeObj) : this._decodeRoute_getNumber(decodeObj); From 61e0c582a0475ec5a3bc756305f504ad5bc9b860 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 20:01:02 +0800 Subject: [PATCH 27/64] Fix decodeRoute --- libs/utils.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/utils.js b/libs/utils.js index 2aab843f..26c8b8e2 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -485,8 +485,9 @@ utils.prototype.decodeRoute = function (route) { // 解压缩 try { var v = LZString.decompressFromBase64(route); - if (/^[a-zA-Z0-9+\/=:()]*$/.test(v)) { - route = v; + if (v != null && /^[-_a-zA-Z0-9+\/=:()]*$/.test(v)) { + if (v != "" || route.length < 8) + route = v; } } catch (e) { } From b1f7f1a9c7f0e30c6823ae9176a65f6e063ac4d0 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 20:19:24 +0800 Subject: [PATCH 28/64] getBgNumber --- libs/control.js | 2 +- libs/maps.js | 22 +++++++++++++++++++--- project/events.js | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libs/control.js b/libs/control.js index a3afd4fe..1aa32609 100644 --- a/libs/control.js +++ b/libs/control.js @@ -659,7 +659,7 @@ control.prototype._moveAction_moving = function (callback) { core.updateStatusBar(); // 检查该点是否是滑冰 - if (core.getBgFgNumber('bg') == 167) { + if (core.getBgNumber() == 167) { core.insertAction("滑冰事件", null, null, null, true); } diff --git a/libs/maps.js b/libs/maps.js index b2da2f6f..04287c39 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -362,12 +362,28 @@ maps.prototype.getBgFgMapArray = function (name, floorId, noCache) { return arr; } +maps.prototype.getBgMapArray = function (floorId, noCache) { + return this.getBgFgMapArray('bg', floorId, noCache); +} + +maps.prototype.getFgMapArray = function (floorId, noCache) { + return this.getBgFgMapArray('fg', floorId, noCache); +} + maps.prototype.getBgFgNumber = function (name, x, y, floorId, noCache) { if (x == null) x = core.getHeroLoc('x'); if (y == null) y = core.getHeroLoc('y'); return this.getBgFgMapArray(name, floorId, noCache)[y][x]; } +maps.prototype.getBgNumber = function (x, y, floorId, noCache) { + return this.getBgFgNumber('bg', x, y, floorId, noCache); +} + +maps.prototype.getFgNumber = function (x, y, floorId, noCache) { + return this.getBgFgNumber('fg', x, y, floorId, noCache); +} + // ------ 当前能否朝某方向移动,能否瞬间移动 ------ // ////// 生成全图的当前可移动信息 ////// @@ -375,8 +391,8 @@ maps.prototype.generateMovableArray = function (floorId, x, y, direction) { floorId = floorId || core.status.floorId; if (!floorId) return null; var width = core.floors[floorId].width, height = core.floors[floorId].height; - var bgArray = this.getBgFgMapArray('bg', floorId), - fgArray = this.getBgFgMapArray('fg', floorId), + var bgArray = this.getBgMapArray(floorId), + fgArray = this.getFgMapArray(floorId), eventArray = this.getMapArray(floorId); var generate = function (x, y, direction) { @@ -489,7 +505,7 @@ maps.prototype._canMoveDirectly_bfs = function (sx, sy, ex, ey) { var canMoveArray = this.generateMovableArray(); var blocksObj = this.getMapBlocksObj(core.status.floorId); // 滑冰 - var bgMap = this.getBgFgMapArray('bg'); + var bgMap = this.getBgMapArray(); var visited = [], queue = []; visited[sx + "," + sy] = 0; diff --git a/project/events.js b/project/events.js index cfc58540..d8559846 100644 --- a/project/events.js +++ b/project/events.js @@ -223,7 +223,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = }, { "type": "if", - "condition": "core.getBgFgNumber('bg') == 167", + "condition": "core.getBgNumber() == 167", "true": [ { "type": "function", From 97386fa9cf0237e1265f9850ea6cdd4f429d489c Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sun, 31 Mar 2019 09:45:28 -0400 Subject: [PATCH 29/64] fix bug --- _server/vm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_server/vm.js b/_server/vm.js index fcf7f4e4..be273557 100644 --- a/_server/vm.js +++ b/_server/vm.js @@ -240,7 +240,7 @@ tip.infos=function(value){ document.getElementById('isAirwall-else').innerHTML=(tip.hasId?`

图块编号:${ value['idnum'] }

图块ID:${ value['id'] }

`:`

该图块无对应的数字或ID存在,请先前往icons.js和maps.js中进行定义!

`)+` -

图块所在素材:${ value['images'] + (tip.isAutotile ? '( '+infos['id']+' )' : '') } +

图块所在素材:${ value['images'] + (tip.isAutotile ? '( '+value['id']+' )' : '') }

图块索引:${ value['y'] }

` } From 2d81d7ce7441ce3d5c4407b044a68c134b968c6d Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sun, 31 Mar 2019 09:55:40 -0400 Subject: [PATCH 30/64] update regactoring.md --- _server/refactoring.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/_server/refactoring.md b/_server/refactoring.md index 695e71ad..55b02bfe 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -7,8 +7,8 @@ ## 文件结构 -+ [x] editor_blockly 图块化事件编辑器, 基本不改动 -+ [x] editor_multi 多行文本编辑器, 基本不改动 ++ [ ] editor_blockly 图块化事件编辑器 ++ [ ] editor_multi 多行文本编辑器 + [x] editor_table 处理表格的生成, 及其响应的事件, 从原editor\_mode中分离 + [ ] editor_file 调用fs.js编辑文件, 把原editor\_file模块化 + [ ] editor_game 处理来自core的数据, 导入为editor的数据, 从原editor中分离 @@ -16,7 +16,7 @@ + [ ] editor 执行初始化流程加组合各组件 + [ ] 原editor_mode 移除 + [ ] 原vm 移除 - ++ [ ] \*comment.js 表格注释与结构, 移至comment/\*comment.js ## 对象结构 @@ -61,7 +61,8 @@ editor: { + [ ] 大地图 在切换时, 每次都回到最左上->每个楼层记录一个位置 - 四个箭头目前不能长按 + 四个箭头支持长按 + ? 滚动条 + [ ] ? 表格折叠 变为四栏, 可以折叠展开 @@ -84,7 +85,12 @@ editor: { + [ ] 画地图也自动保存 ++ [ ] 修改系统的触发器(下拉菜单增加新项) + 在编辑器修改`comment.js`:现场发readFile请求读文件,然后开脚本编辑器进行编辑 ++ [ ] ? 删除注册项/修改图块ID + ++ [ ] ? 怪物和道具也能像其他类型那样查看“图块信息”(而不只是具体的怪物属性) ## 左侧页面模式 From f61ab6369310c722d3acc1494f12ad2685f7ef74 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 23:03:16 +0800 Subject: [PATCH 31/64] status:manamax & documents --- _docs/event.md | 682 +++++++++++++++------------------------ _docs/personalization.md | 451 ++++++-------------------- _server/data.comment.js | 5 + project/data.js | 1 + project/functions.js | 281 ++++++++-------- 5 files changed, 517 insertions(+), 903 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index cb8f7b82..7f1e6969 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1,6 +1,6 @@ # 事件 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 本章内将对样板所支持的事件进行介绍。 @@ -20,11 +20,13 @@ ## 关于V2.0的重要说明 -在V2.0版本中,所有事件均可以使用blockly来进行块的可视化编辑。 +在V2.0以后版本中,所有事件均可以使用blockly来进行块的可视化编辑。 它能通过拖动、复制粘贴等方式帮助你快速生成事件列表,而不用手动打大量字符。 -但是,仍然强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 +下述所说的都是在事件编辑器右边所展示的,该事件的代码化写法。 + +强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 ## 自定义事件 @@ -33,22 +35,18 @@ 所有自定义的事件都是如下的写法: ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - "trigger": "action", // 触发的trigger, action代表自定义事件 - "enable": true, // 该事件初始状态下是否处于启用状态 - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + "trigger": "action", // 触发的trigger, action代表自定义事件 + "enable": true, // 该事件初始状态下是否处于启用状态 + "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` -这里的`"x,y"`代表该点的横坐标为`x`,纵坐标为`y`;即从左到右第`x`列,从上到下的第`y`行(从0开始计算)。 - 我们上面提到,有很多系统已经默认的事件(例如开门、打怪等,相当于公共事件)。如果我们需要自定义一个事件,则需要`"trigger": "action"`,它表示该点是一个自定义事件。 !> **如果系统本身存在事件(如一个怪物),且你指定了`"trigger": "action"`,则原事件会被覆盖。** @@ -58,17 +56,15 @@ 如果该点本身不存在系统事件,则`"trigger":"action"`可被省略不写: ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - "enable": true, // 该事件初始状态下是否处于启用状态 - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + "enable": true, // 该事件初始状态下是否处于启用状态 + "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` @@ -77,17 +73,15 @@ 默认情况下`enable`是`true`,所以如果`enable`为`true`,该项也可以省略不写: ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false + "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` @@ -98,17 +92,15 @@ 因此,除非你想覆盖默认的可通行选项(比如将一个空地设为不可通行),否则该项可以忽略。 ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - // 除非你想覆盖系统默认的可通行状态,否则"noPass"项可以忽略 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false + // 除非你想覆盖系统默认的可通行状态,否则"noPass"项可以忽略 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` @@ -117,14 +109,12 @@ 如果大括号里只有`"data"`,则可以省略大括号和`"data"`,直接写中括号数组,换句话说,上面和下面这种写法也是等价的,可以进行一下比较: ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action", "enable"或"noPass"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] -} +// 如果大括号里只有"data"项(没有"action", "enable"或"noPass"),则可以省略到只剩下中括号 +[ + // 事件1 + // 事件2 + // ... +] ``` 这种简写方式可以极大方便地造塔者进行造塔。 @@ -136,15 +126,13 @@ `"data"`中,是由一系列的自定义事件类型组成。每个元素类似于: ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - {"type": "xxx", ...}, // 事件1 - {"type": "xxx", ...}, // 事件2 - // ... - // 按顺序写事件,直到结束 - ] -} +// 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 +[ + {"type": "xxx", ...}, // 事件1 + {"type": "xxx", ...}, // 事件2 + // ... + // 按顺序写事件,直到结束 +] ``` `"type"`为该自定义事件的类型;而后面的`...`则为具体的一些事件参数。 @@ -158,29 +146,23 @@ 使用`{"type": "text"}`可以显示一段文字。后面`"text"`可以指定文字内容。 ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件 - {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 - ] -} +[ + {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件 + {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 + // ... + // 按顺序写事件,直到结束 +] ``` 该项可以简写成直接的字符串的形式,即下面这种方式也是可以的: ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - "在界面上的一段文字",// 直接简写,和下面写法完全等价 - {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 - ] -} +[ + "在界面上的一段文字",// 直接简写,和下面写法完全等价 + {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 + // ... + // 按顺序写事件,直到结束 +] ``` 所有文字事件均可以进行简写,系统会自动转成`{"type": "text"}`的形式。 @@ -188,15 +170,12 @@ 值得注意的是,系统会自动对文字进行换行;不过我们也可以手动加入`\n`来换行。 ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 +[ "这一段文字特别特别长,但是系统可以对它进行自动换行,因此我们无需手动换行", "这是第一行\n这是第二行\n这是第三行", // ... // 按顺序写事件,直到结束 - ] -} +] ``` 我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以。 @@ -210,7 +189,7 @@ 从V2.5.2以后,新增了绘制大头像的功能。绘制大头像图的基本写法是`\t[1.png]`或者`\t[标题,1.png]`。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "一段普通文字", "\t[勇士,hero]这是一段勇士说的话", "\t[hero]如果使用勇士默认名称也可以直接简写hero", @@ -233,7 +212,7 @@ - `\b[up,x,y]` 显示在(x,y)点的上方(下方);x和y都为整数且在0到12之间。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "\b[up]这段文字显示在当前点上方", "\b[down]这段文字显示在当前点上方", "\t[hero]\b[up,hero]这是一段勇士说的话,会显示在勇士上方", @@ -246,7 +225,7 @@ 还可以使用`\r[...]`来调整剧情文本的颜色。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "这句话是默认颜色,\r[red]将颜色变成红色,\r[blue]将颜色变成蓝色", "\r[#FF00FF]还可以使用RGB值来控制颜色,\r如果不加中括号则回到默认颜色", "\t[hero]\b[up,hero]啊啊啊,别过来,\r[red]别过来!!!\n\r你到底是什么东西!" @@ -260,7 +239,7 @@ 需要注意的是,这个图片是绘制在UI层上的,下一个事件执行时即会擦除;同时如果使用了\t的图标动画效果,重叠的地方也会被图标动画给覆盖掉。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "\t[勇士]\b[up,hero]\f[1.png,100,100]以(100,100)为左上角绘制1.png图片", "\t[hero]\f[1.png,100,100]\f[2.png,300,300]同时绘制了两张图片", "\f[1.png,100,100,300,300]也可以填写宽高,这样会把图片强制进行放缩到指定的宽高值", @@ -273,7 +252,7 @@ 这里可以使用一个合法ID(32x48图块除外),或使用一个系统图标(`core.statusBar.icons`中的内容)。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "\t[勇士]\b[up,hero]这是一个飞行器\\i[fly],这是一个破墙镐\\i[pickaxe]", "\t[hero]也可以使用系统图标,比如这是存档\\i[save],这是工具栏\\i[toolbox]", ] @@ -286,7 +265,7 @@ 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "1+2=${1+2}, 4*5+6=${4*5+6}", // 显示"1+2=3, 4*5+6=26" ] ``` @@ -294,7 +273,7 @@ 我们可以使用 `status:xxx` 代表勇士的一个属性值;`item:xxx` 代表某个道具的个数;`flag:xxx` 代表某个自定义的变量或flag值。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "你当前的攻击力是${status:atk}, 防御是${status:def},坐标是(${status:x},${status:y})", "你的攻防和的十倍是${10*(status:atk+status:def)}", "你的红黄蓝钥匙总数为${item:yellowKey+item:blueKey+item:redKey}", @@ -306,18 +285,12 @@ - `item:xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 - `flag:xxx` 中的xxx为一个自定义的变量/Flag;如果没有对其进行赋值则默认值为false。 -另外,有个小`trick`。是否想立刻知道显示效果? - -你可以用Chrome浏览器打开游戏,按Ctrl+Shift+I打开开发者工具,找到Console(控制台),并中输入`core.drawText("...")` 即可立刻看到文字显示的效果。适当调整文字,使得显示效果满意后,再复制粘贴到你的剧情文本中。 - -![调试](./img/eventdebug.png) - ### autoText:自动剧情文本 使用`{"type": "autoText"}`可以使用剧情文本。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "autoText", "text": "一段自动显示的剧情文字", "time": 5000} ] ``` @@ -337,7 +310,7 @@ time为可选项,代表该自动文本的时间。可以不指定,不指定 使用`{"type": "scrollText"}`可以使用滚动剧情文本,即将一段文字从屏幕最下方滚动到屏幕最上方。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "scrollText", "text": "第一排\n第二牌\n\n空行后的一排", "time": 5000, "lineHeight": 1.4, "async": true}, ] ``` @@ -359,7 +332,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用`{"type": "setText"}`可以设置剧情文本的各项属性。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setText", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3], "time": 70}, {"type": "setText", "position": "up", "offset": 15, "bold": true, "titlefont": 26, "textfont": 17}, "这段话将显示在上方(距离顶端15像素),标题为红色,正文为黄色粗体,背景为透明度0.3的蓝色,标题26px,正文17px,70毫秒速度打字机效果", @@ -392,7 +365,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 `{"type": "tip"}`可以在左上角显示一段提示文字。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "tip", "text": "这段话将在左上角以气泡形式显示"} ] ``` @@ -404,7 +377,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 使用`{"type": "comment"}`可以添加一段注释 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "comment", "text": "这是一段会被跳过的注释内容"} ] ``` @@ -418,7 +391,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setValue", "name": "...", "value": "..."}, // 设置一个属性、道具或自定义Flag ] ``` @@ -430,7 +403,7 @@ name为你要修改的属性/道具/Flag,每次只能修改一个值。写法 value是一个表达式,将通过这个表达式计算出的结果赋值给name。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`的写法表示勇士当前属性,道具个数和某个变量/Flag值。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setValue", "name": "status:atk", "value": "status:atk+10" } // 攻击提高10点 {"type": "setValue", "name": "status:money", "value": "1000" } // 将金币数设为1000(不是+1000) {"type": "setValue", "name": "status:hp", "value": "status:hp*2" } // 生命值翻倍 @@ -450,7 +423,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam 即下面的写法是等价的: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setValue", "name": "status:atk", "value": "status:atk+10" } // 攻击提高10点 {"type": "addVakue", "name": "status:atk", "value": "10" } // 和上面写法等价 {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey-3" } // 黄钥匙个数-3 @@ -465,7 +438,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam 使用`{"type":"setFloor"}`可以设置某层楼的楼层属性。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setFloor", "name": "title", "value": "'主塔 0 层'" } // 设置当前楼层的中文名为主塔0层 {"type": "setFloor", "name": "canFlyTo", "floorId": "MT2", "value": "false" } // 设置MT2层不可飞行 {"type": "setFloor", "name": "cannotViewMap", "floorId": "MT0", "value": "true" } // 设置MT0层不可被浏览地图 @@ -476,8 +449,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam ] ``` -name为必填项,代表要修改的楼层属性。其和楼层属性中一一对应,目前只能为`"title", "name", "canFlyTo", "canUseQuickShop", "cannotViewMap", "cannotMoveDirectly", "color", "weather", -"defaultGround", "images", "item_ratio", "upFloor", "bgm", "downFloor", "underGround"`。 +name为必填项,代表要修改的楼层属性,和楼层属性中的一一对应。 floorId为可选项,代表要修改的楼层ID;可以省略代表当前楼层。 @@ -490,13 +462,12 @@ value为必填项,代表要修改到的数值。其应该和楼层属性中的 使用`{"type":"setGlobalAttribute"}`可以设置一个全局属性。 ``` js -"x,y": [ // 实际执行的事件列表 - {"type": "setGlobalAttribute", "name": "font", "value": "Verdana"}, // 设置字体为Verdana +[ + {"type": "setGlobalAttribute", "name": "font", "value": "Verdana"}, // 设置字体为Verdana ] ``` -name必填项,代表要修改的全局属性。目前只能为`"font", "statusLeftBackground", "statusTopBackground", "toolsBackground", -"borderColor", "statusBarColor", "hardLabelColor", "floorChangingBackground", "floorChangingTextColor"`。 +name必填项,代表要修改的全局属性。 value为必填项,代表要修改到的结果。此项无需再手动加单引号。 @@ -505,14 +476,12 @@ value为必填项,代表要修改到的结果。此项无需再手动加单引 使用`{"type":"setGlobalValue"}`可以设置一个全局数值。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setGlobalValue", "name": "lavaDamage", "value": 200}, // 设置血网伤害为200 ] ``` -name必填项,代表要修改的全局数值,其和全塔属性中的values一一对应。目前只能为`"lavaDamage", "poisonDamage", "weakValue", "redJewel", -"blueJewel", "greenJewel", "redPotion", "bluePotion", "yellowPotion", "greenPotion", "breakArmor", "counterAttack", -"purify", "hatred", "moveSpeed", "animateSpeed"`。 +name必填项,代表要修改的全局数值,其和全塔属性中的values一一对应。 value为必填项,代表要修改到的结果。该项必须是个数值。 @@ -521,15 +490,12 @@ value为必填项,代表要修改到的结果。该项必须是个数值。 使用`{"type":"setGlobalFlag"}`可以设置一个系统开关。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setGlobalFlag", "name": "enableMDef", "value": false}, // 不在状态栏显示魔防值 ] ``` -name必填项,代表要修改的系统开关,其是全塔属性中的flags中的一部分。目前只能为`"enableFloor", "enableName", "enableLv", -"enableHPMax", "enableMana", "enableMDef", "enableMoney", "enableExperience", "enableLevelUp", "levelUpLeftMode", -"enableKeys", "enablePZF", "enableDebuff", "enableSkill", "flyNearStair", "enableAddPoint", "enableNegativeDamage", -"useLoop", "enableGentleClick", "canGoDeadZone", "enableMoveDirectly", "disableShopOnDamage"`。 +name必填项,代表要修改的系统开关,其是全塔属性中的flags中的一部分。 value为必填项,只能为true或false,代表要修改到的结果。 @@ -540,7 +506,7 @@ value为必填项,只能为true或false,代表要修改到的结果。 使用`{"type":"show"}`可以将一个本身禁用的事件启用。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "show", "loc": [3,6], "floorId": "MT1", "time": 500}, // 启用MT1层[3,6]位置事件,动画500ms {"type": "show", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 {"type": "show", "loc": [3,6]}, // 如果不指定动画时间,则立刻显示,否则动画效果逐渐显示,time为动画时间 @@ -576,7 +542,7 @@ loc同样可以简单的写[x,y]表示单个点,或二维数组[[x1,y1],[x2,y2 NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}`,可以不写loc选项代表当前事件,可以指定time使NPC动画消失。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hide", "loc": [3,6], "floorId": "MT1", "time": 500}, // 禁用MT1层[3,6]位置事件,动画500ms {"type": "hide", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 {"type": "hide", "loc": [3,6]}, // 如果不指定动画时间,则立刻消失,否则动画效果逐渐消失,time为动画时间 @@ -596,7 +562,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` 其基本写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "trigger", "loc": [3,6]}, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 "执行trigger后,这段文字将不会再被显示" ] @@ -617,7 +583,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` 其基本写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "insert", "name": "加点事件", "args": [10] }, // 插入公共事件:加点事件,传入参数10 {"type": "insert", "name": "毒衰咒处理", "args": [0]}, // 插入公共事件:毒衰咒处理,传入参数0 {"type": "insert", "loc": [3,6]}, // 插入[3,6]点的事件并执行 @@ -657,7 +623,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` revisit和trigger完全相同,只不过是立刻触发的还是本地点的事件 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "revisit"}, // 立即触发本事件,等价于 {"type": "trigger", "loc": [x,y]} "执行revisit后,这段文字将不会再被显示" ] @@ -676,7 +642,7 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 例如玩家点击商人的"离开"选项,则可以调用exit返回游戏。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "exit" }, // 立即结束事件并恢复游戏,一切列表中的事件都将不再被执行 "执行exit后,这段文字将不会再被显示" ] @@ -687,7 +653,7 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setBlock", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层的(3,3)点变成数字233 {"type": "setBlock", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 {"type": "setBlock", "number": 57}, // loc也可省略,默认为当前点 @@ -715,7 +681,7 @@ number为**要更改到的数字**,有关“数字”的定义详见参见[素 有关贴图说明请参见[使用自己的图片作为某层楼的背景/前景素材](personalization#使用自己的图片作为某层楼的背景前景素材)。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hideFloorImg", "loc": [3,6], "floorId": "MT1"}, // 隐藏[3,6]的贴图 {"type": "hideFloorImg", "loc": [3,6]}, // 如果是当前层,则可以省略floorId项 {"type": "hideFloorImg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时隐藏多个贴图。 @@ -735,7 +701,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 其做法和参数,和隐藏贴图是完全一致的。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showFloorImg", "loc": [3,6], "floorId": "MT1"}, // 显示[3,6]的贴图 ] ``` @@ -747,7 +713,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 从V2.4.1开始,允许绘制三层图层(背景层,事件层和前景层)。使用`hideBgFgMap`可以隐藏背景或前景层中的图块。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hideBgFgMap", "name": "bg", "loc": [3,6], "floorId": "MT1"}, // 隐藏MT1层[3,6]的背景层图块 {"type": "hideBgFgMap", "name": "bg", "loc": [3,6]}, // 如果是当前层,则可以省略floorId项 {"type": "hideBgFgMap", "name": "fg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时隐藏多个贴图。 @@ -767,7 +733,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 其做法和参数,和隐藏贴图是完全一致的。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showBgFgMap", "name": "bg", "loc": [3,6], "floorId": "MT1"}, // 显示MT1层[3,6]的前景层图块 ] ``` @@ -777,7 +743,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 我们可以采用 `{"type": "setBgFgBlock"}` 来改变某个背景或前景层地图块。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setBgFgBlock", "name": "bg", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层背景层的(3,3)点变成数字233 {"type": "setBgFgBlock", "name": "bg", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 {"type": "setBgFgBlock", "name": "fg", "number": 57}, // loc也可省略,默认为当前点 @@ -797,7 +763,7 @@ loc为可选的,表示要更改地图块的坐标。如果忽略此项,则 使用`{"type": "setHeroIcon"}`可以更改角色行走图。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setHeroIcon", "name": "hero2.png"}, // 将勇士行走图改成hero2.png;必须在全塔属性的images中被定义过。 {"type": "setHeroIcon"}, // 如果不加name则恢复最初默认状态 {"type": "setValue", "name": "status:name", "value": "'可绒'"}, // 修改勇士名;请注意value必须加单引号。 @@ -839,7 +805,7 @@ name是可选的,代表目标行走图的文件名。 基本写法:`{"type": "sleep", "time": xxx}` ,其中xxx为指定的毫秒数。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "sleep", "time": 1000}, // 等待1000ms "等待1000ms后才开始执行这个事件", {"type": "sleep", "time": 2000, "noSkip": true}, // 等待2000毫秒,且不可被跳过 @@ -854,12 +820,10 @@ name是可选的,代表目标行走图的文件名。 调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 -例如,《宿命的旋律》中,一区有个骷髅队长,当你拿了它周围三个物品时,就会立刻触发强制战斗事件。这时候就可以用`{"type": "battle"}` 实现。 - 其基本写法是: `{"type": "battle", "id": xxx}`,其中xxx为怪物ID。 ``` js -"10,4": [ // 开门后走进去的事件:强制战斗 +[ "\t[blackKing]你终于还是来了。", "\t[hero]放开我们的公主!", "\t[blackKing]如果我不愿意呢?", @@ -877,8 +841,6 @@ name是可选的,代表目标行走图的文件名。 如果强制战斗失败,则会立刻生命归0并死亡,调用lose函数,接下来的事件不会再被执行。 -打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](#加点事件)。 - 强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。 ### openDoor:开门 @@ -886,7 +848,7 @@ name是可选的,代表目标行走图的文件名。 调用`{"type":"openDoor"}`可以打开一扇门。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门 {"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId {"type": "openDoor", "loc": [3,6], "needKey": true} // 打开此门需要钥匙 @@ -908,7 +870,7 @@ needKey是可选的,如果设置为true则需要钥匙才能打开此门。如 从V2.6开始提供了关门事件`{"type": "closeDoor"}`,拥有关门动画和对应的音效。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "closeDoor", "id": "yellowDoor", "loc": [3,6]}, // 给(3,6)点关上黄门 {"type": "closeDoor", "id": "specialDoor"}, // 不写loc则视为当前点 ] @@ -934,7 +896,7 @@ loc可选,为要关的位置,不填默认为当前点。 changeFloor的事件写法大致如下。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "changeFloor", "floorId": "sample0","loc": [10, 10], "direction": "left", "time": 1000 }, //后面几项依次为楼层id,楼层位置(这两项为必填);勇士方向可选,切换时间也是可选。 ] @@ -957,7 +919,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 这时候可以用changePos。其参数和changeFloor类似,但少了floorId和time两个选项。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "changePos", "loc": [10,10], "direction": "left"}, // 直接切换勇士的坐标,loc为目标地点,后面勇士换位后方向 {"type": "changePos", "loc", [10,10]}, // 如无需指定方向则direction可省略 {"type": "changePos", "direction": "left"} // loc也可省略,只指定direction;此时等价于当前勇士转向到某个方向。 @@ -969,7 +931,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 调用`{"type": "useItem"}`可以使用一个道具。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "changePos", "id": "pickaxe"}, // 尝试使用破 {"type": "changePos", "id": "bomb"}, // 尝试使用炸 {"type": "changePos", "id": "centerFly"} // 尝试使用飞 @@ -995,7 +957,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 使用 `{"type": "follow"}` 可以让一个npc加入跟随。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "follow", "name": "npc.png"}, // 将 npc.png 这个行走图加入跟随 {"type": "follow", "name": "hero.png"}, // 再将另一个行走图加入跟随 ] @@ -1010,7 +972,7 @@ name所指定的图片必须存在,在全塔属性中的images中被定义过 使用 `{"type": "unfollow"}` 来取消一个跟随。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "unfollow", "name": "npc.png"}, // 将 npc.png 这个行走图取消跟随 {"type": "follow"}, // 取消所有跟随 ] @@ -1037,7 +999,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 有关动画的详细介绍可参见[动画和天气系统](element#动画和天气系统)。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "animate", "name": "yongchang", "loc": [1,3]}, // 在(1,3)显示“咏唱魔法”动画 {"type": "animate", "name": "zone", "loc": "hero"}, // 在勇士位置显示“领域”动画 {"type": "animate", "name": "hand"}, // 可以不指定loc,则默认为当前事件点 @@ -1060,7 +1022,7 @@ loc可忽略,如果忽略则显示为事件当前点。 我们可以使用 `{"type": "showImage"}` 来显示一张图片。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showImage", "code": 1, "image": "bg.jpg", "loc": [231,297], "opacity": 1, "time" : 0}, // 在(231,297)显示bg.jpg {"type": "showImage", "code": 12, "image": "1.png", "loc": [209,267], "opacity": 0.5, "time" : 1000}, // 在(209,267)渐变显示1.png,渐变时间为1000毫秒,完成时不透明度为0.5,这张图片将遮盖上一张 {"type": "showImage", "code": 8, "image": "hero.png", "loc": [349,367], "opacity": 1, "time" : 500, "async": true}, // 在(209,267)渐变显示hero.png,渐变时间为500毫秒,异步执行;这张图片将被上一张遮盖 @@ -1087,7 +1049,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "showTextImage"}` 以图片的方式显示文本。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showTextImage", "code": 1, "text": "第一排\n第二排\n\n空行后的一排", "loc": [231,297], "opacity": 1, "time" : 0}, // 在(231,297)显示"第一排\n第二排\n\n空行后的一排" ] ``` @@ -1113,7 +1075,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "hideImage"}` 来清除一张图片。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hideImage", "code": 1, "time" : 0}, // 使1号图片消失 {"type": "hideImage", "code": 12, "time" : 1000}, // 使12号图片渐变消失,时间为1000毫秒 ] @@ -1130,7 +1092,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "showGif"}` 来显示一张图片。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showGif", "name": "timg.gif", "loc": [231,297]}, // 在(231,297)显示一张动图 {"type": "showGif"} // 如果不指定name则清除所有动图。 ] @@ -1147,7 +1109,7 @@ loc为动图左上角坐标,以像素为单位进行计算。 我们可以使用 `{"type": "moveImage"}` 来造成图片移动,淡入淡出等效果。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "moveImage", "code": 1, "to": [22,333], "opacity": 1, "time": 1000}, // 将1号图片移动到(22,333),动画时间为1000ms {"type": "moveImage", "code": 12, "opacity": 0.5, "time": 500}, // 将二号图片的透明度变为0.5,动画时间500ms {"type": "moveImage", "code": 1, "to": [109,167], "opacity": 0, "time": 300, "async": true}, // 将1号图片移动到(109,167),透明度设为0(不可见),动画时间300ms,异步执行 @@ -1169,7 +1131,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "setFg"}` 来更改画面色调。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setFg", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 {"type": "setFg", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 {"type": "setFg"} // 如果不指定color则恢复原样。 @@ -1191,7 +1153,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "screenFlash"}` 来进行画面闪烁。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "screenFlash", "color": [255,255,255,0.6], "time": 500, "times": 1}, // 闪光为白色,不透明度0.6,动画时间1000毫秒 {"type": "screenFlash", "color": [255,0,0,1], "time": 100, "times": 2, "async": true}, // 闪光为红色,强度最大,动画时间100毫秒,闪烁两次且异步执行 ] @@ -1212,7 +1174,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "setWeather"}` 来更改天气。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setWeather", "name": "rain", "level": 6}, // 更改为雨天,强度为6级 {"type": "setWeather", "name": "snow", "level": 3}, // 更改为雪天,强度为3级 {"type": "setWeather"} // 更改回晴天 @@ -1236,10 +1198,9 @@ level为天气的强度等级,在1-10之间。1级为最弱,10级为最强 下面是该事件常见的写法: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "move", "time": 750, "loc": [x,y], "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),loc为位置可选,steps为移动数组 - {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 - "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 + "right", "right", "down" // 向右两格,向下一格 ], "keep": true, "async":true }, // keep可选,如果为true则不消失,否则渐变消失;async可选,如果为true则异步执行。 ] ``` @@ -1248,9 +1209,7 @@ time选项必须指定,为每移动一步所需要用到的时间。 loc为需要移动的事件位置。可以省略,如果省略则移动本事件。 -steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}`,表示该步是向xxx方向移动n步。 - -如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。 +steps为一个数组,其每一项是`up, down, left, right`之一,表示这一步应该朝哪个方向走。 keep为一个可选项,代表该事件移动完毕后是否消失。如果该项指定了并为true,则移动完毕后将不消失,否则以动画效果消失。 @@ -1265,7 +1224,7 @@ keep为一个可选项,代表该事件移动完毕后是否消失。如果该 ``` js "4,3": [ // [4,3]是一个NPC,比如小偷 {"type": "move", "time": 750, "steps": [ // 向上移动两格,每步750毫秒 - {"direction": "up", "value": 2}, + "up", "up" ], "keep": true}, // 移动完毕后不消失 ], "4,1": { // [4,1]为目标地点 @@ -1289,13 +1248,14 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ``` js "x,y": [ // 实际执行的事件列表 {"type": "moveHero", "time": 750, "async": true, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组 - {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 - "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 + "down", "right", "forward", "backward" // 向下、右、前、后各走一步 ]}, ] ``` -可以看到,和上面的move事件几乎完全相同,除了不能指定loc,且少了immediateHide选项。 +可以看到,和上面的move事件几乎完全相同,除了不能指定loc,且少了keep选项。 + +勇士的steps也允许`forward`和`backward`,即前进和后退。 不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。 @@ -1308,7 +1268,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 下面是该事件常见的写法: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "keep": true, "async": true}, ] ``` @@ -1332,7 +1292,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 下面是该事件常见的写法: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "jump", "loc": [3,6], "time": 750, "async": true}, ] ``` @@ -1351,7 +1311,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 值得注意的是,额外添加进文件的背景音乐,需在main.js中this.bgms里加载它。 -目前支持mp3/ogg/wav/mid等多种格式的音乐播放。 +目前支持mp3/ogg/wav等多种格式的音乐播放。 有关BGM播放的详细说明参见[背景音乐](element#背景音乐) @@ -1385,7 +1345,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。 -可以加`"stop": true`来停止之前正在播放的音效。 +从V2.6开始,也可以加`"stop": true`来停止之前正在播放的音效。 ### stopSound:停止所有音效 @@ -1411,6 +1371,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 该事件会显示获胜页面,并重新游戏。 +可以增加`"norank": 1`来表示该结局不计入榜单。 + !> 如果`reason`不为空,则会以reason作为获胜的结局! ### lose:游戏失败 @@ -1442,7 +1404,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用`{"type": "input"}`可以接受用户的输入的数字。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "input", "text": "请输入一个数"}, // 显示一个弹窗让用户输入数字 "你刚刚输入的数是${flag:input}" // 输入结果将被赋值为flag:input ] @@ -1461,7 +1423,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 类似于input事件,使用`{"type": "input2"}`可以接受用户的输入的文本。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "input2", "text": "请输入你的ID"}, // 显示一个弹窗让用户输入文本 "你好,${flag:input},欢迎来到本塔" // 输入结果将被赋值为flag:input ] @@ -1482,7 +1444,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "if", "condition": "...", // 测试某个条件 "true": [ // 条件成立则执行true里面的事件 @@ -1503,7 +1465,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 例如下面这个例子,每次将检查你的攻击力是否大于500,不是的场合将给你的攻击力加100点。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "if", "condition": "status:atk>500", // 判断攻击力是否大于500 "true": [ // 条件成立则执行true里面的事件 "你的攻击力已经大于500了!", @@ -1511,7 +1473,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 ], "false": [ // 条件不成立则执行false里的事件 "你当前攻击力为${status:atk}, 不足500!\n给你增加100点攻击力!", - {"type": "setValue", "name": "status:atk", "value": "status:atk+100"}, // 攻击力加100, 接着会执行revisit事件 + {"type": "addValue", "name": "status:atk", "value": "100"}, // 攻击力加100, 接着会执行revisit事件 ] }, {"type", "revisit"}, // 立刻重启本事件, 直到攻击力大于500后结束 @@ -1521,7 +1483,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 需要额外注意的几点: - 给定的表达式(condition)一般需要返回true或false。 -- `flag:xxx` 可取用一个自定义变量或flag。如果从未设置过该flag,则其值默认为false。而JS中,`false==0`这个判断是成立的,因此我们可以简单使用 `"flag:npc_times==0"` 来判断某个NPC是否被访问过。 +- `flag:xxx` 可取用一个自定义变量或flag。如果从未设置过该flag,则其值默认为0。 - 即使成功失败的场合不执行事件,对应的true或false数组也需要存在,不过简单的留空就好。 - if可以不断进行嵌套,一层套一层;如成立的场合再进行另一个if判断等。 - if语句内的内容执行完毕后将接着其后面的语句继续执行。 @@ -1534,7 +1496,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "switch", "condition": "...", // 计算某个表达式 "caseList": [ {"case": "a", "action": [// 若表达式的值等于a则执行该处事件 @@ -1560,7 +1522,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 例如下面这个例子,将检查当前游戏难度并赠送不同属性。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "switch", "condition": "flag:hard", // 判断当前游戏难度 "caseList": [ {"case": "0", "action": [// 若表达式的值等于0则执行该处事件 @@ -1583,12 +1545,32 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 需要额外注意的几点: -- 各个条件分支的判断是顺序执行的,因此若多个分支的条件都满足,将只执行最靠前的分支,同理,请不要在`default`分支后添加分支,这些分支将不可能被执行。 +- 各个条件分支的判断是顺序执行的,因此若多个分支的条件都满足,将只执行最靠前的分支。 + - 同理,请不要在`default`分支后添加分支,这些分支将不可能被执行。 - `default`分支并不是必要的,如果删除,则在没有满足条件的分支时将不执行任何事件。 - 即使某个场合不执行事件,对应的action数组也需要存在,不过简单的留空就好。 -- switch可以不断进行嵌套,一层套一层;如某条件成立的场合再进行另一个switch判断等。 - switch语句内的内容执行完毕后将接着其后面的语句继续执行。 +由于`case`中的内容是会被计算的,因此如下写法也是合法的 + +```js +[ + {"type": "switch", "condition": "true", // 条件:某一项为真时 + "caseList": [ + {"case": "flag:a==1", "action": [ // 如果 flag:a == 1 + "走进了 flag:a==1 分支!" + ], + {"case": "flag:b>=3", "action": [ // 如果 flag:b >= 3 + "走进了 flag:b>=3 分支!" + ], + {"case": "default", "action": [ // 上述两条均布成立 + "上述两条均不成立" + ] + ] + }, +] +``` + ### choices:给用户提供选项 @@ -1601,17 +1583,16 @@ choices是一个很麻烦的事件,它将弹出一个列表供用户进行选 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "choices", "text": "...", // 提示文字 - "color": [255,0,0,1], // 颜色 "choices": [ {"text": "选项1文字", "action": [ // 选项1执行的事件 ]}, - {"text": "选项2文字", "action": [ + {"text": "选项2文字", "color": [255,0,0,1], "action": [ // 选项2执行的事件 ]}, - {"text": "选项3文字", "action": [ + {"text": "选项3文字", "icon": "fly", "action": [ // 选项3执行的事件 ]}, ] @@ -1629,67 +1610,10 @@ action为当用户选择了该选项时将执行的事件。 color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数组([255,0,0,1])。 +icon是可选的,如果设置则会在选项前绘制图标,其可以是一个有效的ID,或者`core.statusBar.icons`中的系统图标。 + 选项可以有任意多个,但一般不要超过6个,否则屏幕可能塞不下。 -下面是一个卖钥匙的事件,是一个比较复杂却也较为典型的if和choices合并使用的样例。 - -``` js -"10,11": [ // 商人事件,if语句和choices语句的写法 - // 这部分逻辑相对比较长,细心看,很容易看懂的。 - {"type": "if", "condition": "flag:woman_times==0", // 条件判断:是否从未访问过此商人。 - "true": [ // 如果从未访问过该商人,显示一段文字 - "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", - "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", - "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" - // 第一次访问结束 - ], - "false": [ // 如果已经访问过该商人 - {"type": "if", "condition": "flag:woman_times==8", // 条件判断:是否已经出售七把钥匙 - "true": [ // 如果已经出售过七把钥匙,则直接结束 - "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", - "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", - {"type": "setValue", "name": "item:bigKey", "value": "item:bigKey+1"}, // 获得一把大黄门钥匙 - "\t[老人,woman]我先走了,拜拜~", - {"type":"hide", "time": 500}, // 消失 - {"type":"exit"} // 立刻结束当前事件。下面的 setValue 和 revisit 都不会再执行。 - ], - "false": [ // 否则,显示选择页面 - {"type": "choices", "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", // 显示一个卖钥匙的选择页面 - "choices": [ // 提供四个选项:黄钥匙、蓝钥匙、红钥匙、离开。前三个选项显示需要的金额 - {"text": "黄钥匙(${9+flag:woman_times}金币)", "color": [255,255,0,1], "action": [ // 第一个选项,黄钥匙 - // 选择该选项的执行内容 - {"type": "if", "condition": "status:money>=9+flag:woman_times", // 条件判断:钱够不够 - "true": [ - {"type": "setValue", "name": "status:money", "value": "status:money-(9+flag:woman_times)"}, // 扣减金钱 - {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey+1"}, // 增加黄钥匙 - // 然后会继续执行下面的setValue来增加商人访问次数 - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - {"type": "revisit"} // 直接重新访问;不执行下面的setValue来增加访问次数 - ] - } - ]}, - {"text": "蓝钥匙(${18+2*flag:woman_times}金币)", "color": [0,0,255,1], "action": [ // 第二个选项:蓝钥匙 - // 逻辑和上面黄钥匙完全相同,略 - ]}, - {"text": "红钥匙(${36+4*flag:woman_times}金币)", "color": [255,0,0,1], "action": [ // 第三个选项:红钥匙 - // 逻辑和上面黄钥匙完全相同,略 - ]}, - {"text": "离开", "action": [ // 第四个选项:离开 - {"type": "exit"} // 立刻结束当前事件 - ]} - ] - } - ] - } - ] - }, - {"type": "setValue", "name": "flag:woman_times", "value": "flag:woman_times+1"}, // 增加该商人的访问次数。 - {"type": "revisit"} // 立即重新开始这个事件 -], -``` - ### while:循环处理 从2.2.1样板开始,我们提供了循环处理(while事件)。 @@ -1697,7 +1621,7 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "while", "condition": "...", // 循环测试某个条件 "data": [ // 条件成立则执行data里面的事件 @@ -1715,10 +1639,10 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 下面是一个输出1到10之间的数字,每隔1秒显示一个的例子。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type":"while", "condition": "flag:i<=10", // 循环处理;注意flag未设置则默认为0 "data":[ - {"type": "setValue", "name": "flag:i", "value": "flag:i+1"}, // 递增i + {"type": "addValue", "name": "flag:i", "value": "1"}, // 递增i "${flag:i}", // 输出i {"type": "sleep","time":1000}, // 等待1秒 ] @@ -1739,10 +1663,10 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 上面的输出例子也可以这么写: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type":"while", "condition": "true", // 循环处理;永远为真 "data":[ - {"type": "setValue", "name": "flag:i", "value": "flag:i+1"}, // 递增i + {"type": "addValue", "name": "flag:i", "value": "1"}, // 递增i "${flag:i}", // 输出i {"type": "sleep","time":1000}, // 等待1秒 {"type": "if", "condition": "flag:i<10", // 测试i是否小于10 @@ -1762,15 +1686,14 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 使用 `{"type": "wait"}` 可以等待用户进行操作(如点击、按键等)。 当用户执行操作后: -- 如果是键盘的按键操作,则会将flag:type置为0,并且把flag:keycode置为刚刚按键的keycode。 -- 如果是屏幕的点击操作,则会将flag:type置为1,并且设置flag:x和flag:y为刚刚的点击坐标(0-12之间),flag:px和flag:py置为刚刚的像素坐标(0-415之间)。 +- 如果是键盘的按键操作,则会将flag:type置为0,并且把flag:keycode置为按键的keycode。 +- 如果是屏幕的点击操作,则会将flag:type置为1,并且设置flag:x和flag:y为点击的位置坐标,flag:px和flag:py为点击的像素坐标。 下面是一个while事件和wait合并使用的例子,这个例子将不断接收用户的点击或按键行为,并输出该信息。 如果用户按下了ESC或者点击了屏幕正中心,则退出循环。 - ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "while", "condition": "true", // 永久循环 "data": [ {"type": "wait"}, // 等待用户操作 @@ -1793,7 +1716,6 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 ] } ] - ``` ### waitAsync:等待所有异步事件执行完毕 @@ -1814,7 +1736,7 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "function", "function": function(){ // 执行一段js脚本 // 这里写js代码 alert(core.getStatus("atk")); // 弹窗显示勇士的攻击力 @@ -1931,7 +1853,6 @@ core.insertAction([ } ``` - 总之,记住如下两点: - 可以使用setBlock来更改一个图块。 @@ -2015,31 +1936,13 @@ if (core.getFlag("door",0)==2) { 如果要对某个怪物进行加点操作,则首先需要修改该怪物的`point`数值,代表怪物本身的加点数值。 -然后在脚本编辑中找到加点事件,双击进行修改。它将返回一个choices事件。修改此函数为我们需要的加点项即可。 +从V2.5.5开始,加点事件移动到了[公共事件](personalization#公共事件)之中,会通过传参的形式来传递怪物的加点值。 -``` js -////// 加点事件 ////// -"addPoint" : function (enemy) { - // 加点事件 - var point = enemy.point; - if (!core.flags.enableAddPoint || !core.isset(point) || point<=0) return []; - - // 加点,返回一个choices事件 - return [ - {"type": "choices", - "choices": [ // 提供三个选项:对于每一点,攻击+1/防御+2/生命+200 - {"text": "攻击+"+(1*point), "action": [ - {"type": "setValue", "name": "status:atk", "value": "status:atk+"+(1*point)} - ]}, - {"text": "防御+"+(2*point), "action": [ - {"type": "setValue", "name": "status:def", "value": "status:def+"+(2*point)} - ]}, - {"text": "生命+"+(200*point), "action": [ - {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)} - ]}, - ] - } - ]; +```js +// 如果有加点 +var point = core.material.enemys[enemyId].point; +if (core.flags.enableAddPoint && point > 0) { + core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]); } ``` @@ -2062,42 +1965,14 @@ if (core.getFlag("door",0)==2) { "textInList": "1F金币商店", // 在快捷商店栏中显示的名称 "use": "money", // 商店所要使用的。只能是"money"或"experience"。 "commonTimes": true, // 是否使用全局次数 - "mustEnable": true, // 如果未开启则不显示在状态栏中 - "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times作为参数计算。 - // 这里用到的times为该商店的已经的访问次数。首次访问该商店时times的值为0。 - // 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式,只要以times作为参数即可。 - // 例如: "need": "25" 就是恒定需要25金币的商店; "need": "20+2*times" 就是第一次访问要20金币,以后每次递增2金币的商店。 - // 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。 - "text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字,需手动加换行符。可以使用${need}表示上面的need值。 + "mustEnable": false, // 如果未开启则不显示在状态栏中 + "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times(访问次数)作为参数计算。 + "text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字。可以使用${need}表示上面的need值。 "choices": [ // 商店的选项 - {"text": "生命+800", "effect": "status:hp+=800"}, - // 如果有多个effect以分号分开,参见下面的经验商店 - {"text": "攻击+4", "effect": "status:atk+=4"}, - {"text": "防御+4", "effect": "status:def+=4"}, - {"text": "魔防+10", "effect": "status:mdef+=10"} - // effect可以对status,item和flag进行操作。 - // 必须是X+=Y的形式,其中Y可以是一个表达式,以status:xxx, item:xxx或flag:xxx为参数 - // 其他effect样例: - // "item:yellowKey+=1" 黄钥匙+1 - // "item:pickaxe+=3" 破墙镐+3 - // "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍 - ] - }, - { - "id": "expShop1", // 商店唯一ID - "name": "经验之神", - "icon": "pinkShop", - "textInList": "1F经验商店", - "use": "experience", // 该商店使用的是经验进行计算 - "need": "-1", // 如果是对于每个选项所需要的数值不同,这里直接写-1,然后下面选项里给定具体数值 - "text": "勇敢的武士啊,给我若干经验就可以:", - "choices": [ - // 在choices中写need,可以针对每个选项都有不同的需求。 - // 这里的need同样可以以times作为参数,比如 "need": "100+20*times" - {"text": "等级+1", "need": "100", "effect": "status:lv+=1;status:hp+=1000;status:atk+=7;status:def+=7"}, - // 多个effect直接以分号分开即可。如上面的意思是生命+1000,攻击+7,防御+7。 - {"text": "攻击+5", "need": "30", "effect": "status:atk+=5"}, - {"text": "防御+5", "need": "30", "effect": "status:def+=5"}, + // effect可以对status,item和flag进行操作;必须是X+=Y的形式,其中Y可以是一个表达式 + {"text": "生命+800", "effect": "status:hp+=800"}, // 生命+800 + {"text": "攻击+4", "need": 30, "effect": "status:atk+=4"}, // 规定具体的数值 + {"text": "防御+2,魔防+4", "effect": "status:def+=2;status:mdef+=4"}, // 多个效果用分号分开 ] } ], @@ -2119,14 +1994,15 @@ if (core.getFlag("door",0)==2) { - choices 为商店的各个选项,是一个list,每一项是一个选项 - text为显示文字。请注意这里不支持 ${} 的表达式计算。 - effect 为该选项的效果;effect必须是 `status:xxx+=yyy`, `item:xxx+=yyy`或`flag:xxx+=yyy`的形式。即中间必须是+=符号。 - - 如有多个effect(例如升级全属性提升),使用分号分开,参见经验商店的写法。 + - 如有多个effect(例如升级全属性提升),使用分号分开。 像这样定义了全局商店后,即可在快捷栏中看到。 请注意,快捷商店默认是不可被使用的。直到至少调用一次自定义事件中的 `{"type": "openShop"}` 打开商店后,才能真正在快捷栏中被使用。 ``` js -"1,0": [ // 金币商店 +// 事件列表 +[ // 打开商店前,你也可以添加自己的剧情 // 例如,通过if来事件来判断是不是第一次访问商店,是的则显示一段文字(类似宿命的华音那样) {"type": "openShop", "id": "moneyShop1"}, // 这里的id要和data.js中你定义的商店ID完全一致 @@ -2143,6 +2019,27 @@ if (core.getFlag("door",0)==2) { 另外需要注意的一点就是,每层楼都有一个 canUseQuickShop 选项。如果该选项置为false则无法在该层使用快捷商店。 +**从V2.6开始,也提出了“公共事件化的全局商店”,即打开使用全局商店实际上是执行一个公共事件。** + +```js +"shops": [ + // 定义公共事件化的全局商店 + { + "id": "keyShop1", // 商店唯一ID + "textInList": "回收钥匙商店", // 在快捷商店栏中显示的名称 + "mustEnable": false, // 如果未开启则不显示在状态栏中 + "commonEvent": "回收钥匙商店", // 公共事件名 + "args": [], // 向该公共事件传递的参数 + } +] +``` + +`id`, `textInList`, `mustEnable`和上述完全相同。 + +`commonEvent`为公共事件名,即选择此项时要执行的公共事件。 + +`args`为向该公共事件传递的参数,参见[type:insert](#insert:插入公共事件或另一个地点的事件并执行)的说明。 + ## 系统引发的自定义事件 我们知道,所有自定义事件都是需要定义在`"x,y"`处,并且得让用户经过或撞上才能触发的。 @@ -2181,17 +2078,13 @@ if (core.getFlag("door",0)==2) { !> 多个机关门请分别设置开门变量如door1, door2等等。请勿存在两个机关门用相同的变量! -同样,为了实现类似于RMXP中,到达某一层后自动触发某段事件的效果,样板中还存在`firstArrive`事件。 - -当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。 +除此以外,每层楼还提供了`firstArrive`和`eachArrive`事件,分别为首次到达该楼层和每次到达该楼层时执行的事件。 ## 使用炸弹后的事件 上面的afterBattle事件只对和怪物进行战斗后才有会被处理。 -如果我们想在使用炸弹后也能触发一些事件(如开门),则可以在`functions.js`里面的`afterUseBomb`函数进行处理: - -!> V2.0版本可以直接在“脚本编辑 - 使用炸弹后的事件”中双击进行修改! +如果我们想在使用炸弹后也能触发一些事件(如开门),则可以在脚本编辑里面的`afterUseBomb`函数进行处理: ``` js ////// 使用炸弹/圣锤后的事件 ////// @@ -2207,47 +2100,42 @@ if (core.getFlag("door",0)==2) { } ``` -## 滑冰和推箱子事件 +## 滑冰事件 -最新的样板还支持滑冰和推箱子事件。 +从V2.6开始,滑冰事件被重写。现在的滑冰由公共事件执行。 -滑冰事件的数字是167,trigger为ski。 +在新版本中,冰面应该放在背景层上,上面可以放置道具、怪物、门等图块。 -当角色走上冰面时,将触发ski事件,并会一直向前滑行,直到撞上不可通行的块会触发事件(比如撞上怪物会触发battle,撞上门会触发openDoor等等),或者离开冰面为止。 +角色走上冰面后,将一直向前滑行,直到撞上不可通行的图块,或触发事件为止。 -!> 由于H5魔塔只有事件一层,因此滑冰的冰面上将无法摆放任何东西(如怪物,门或道具等);不过可以在战后/开门后/道具后的事件写转变图块成167,从而继续滑冰。 +如果撞上怪物将自动进行战斗,此战斗是强制的,打不过将直接死亡。 -!> 撞上怪物将触发battle进行战斗,该战斗是强制战斗,打不过将直接死亡。 +默认情况下,拾取冰面上道具后将停止滑冰行为。如果要继续滑冰,请在`afterGetItem`中插入公共事件:滑冰事件。打怪和开门同理。 + +!> 滑冰图块的数字是167,请勿修改此数字! + +## 推箱子事件 关于推箱子,存在三种状态:花(168),箱子(169)和已经推到花的箱子(170)。 !> 推箱子的前方不允许存在任何事件(花除外),包括已经禁用的自定义事件。 -推完箱子后将触发functions.js中的afterPushBox事件,你可以在这里进行开门判断。 +推完箱子后将触发脚本编辑中的afterPushBox函数,你可以在这里进行开门判断。 ``` js ////// 推箱子后的事件 ////// "afterPushBox" = function () { - - var noBoxLeft = function () { - // 地图上是否还存在未推到的箱子,如果不存在则返回true,存在则返回false - for (var i=0;i 如果reason不为空,则将会作为结局名! - -当失败(`{"type": "lose"}`,或者被怪强制战斗打死、被领域怪扣血死、中毒导致扣血死,路障导致扣血死等等)事件发生时,将调用`events.js`中的`lose`事件。其直接显示一段文字,并重新开始游戏。 - -``` js -////// 游戏失败事件 ////// -"lose": function(reason) { - core.ui.closePanel(); - var replaying = core.isReplaying(); - core.stopReplay(); - core.waitHeroToStop(function() { - core.drawText([ - "\t[结局1]你死了。\n如题。" - ], function () { - core.events.gameOver(null, replaying); - }); - }) -} -``` - -其参数reason为失败原因。你可以在这里修改失败界面时显示的文字。 - ========================================================================================== [继续阅读下一章:个性化](personalization) diff --git a/_docs/personalization.md b/_docs/personalization.md index f960eed9..80944d06 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -1,6 +1,6 @@ # 个性化 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 @@ -21,10 +21,14 @@ HTML5魔塔是使用画布(canvas)来绘制,存在若干个图层,它们 - route**[D]**:路线层;主要用来绘制勇士的行走路线图。 (z-index: 95) - paint**[D]**:绘图层;主要用来进行绘图模式。(z-index: 95) - curtain:色调层;用来控制当前楼层的画面色调 (z-index: 125) -- image1\~50**[D]**:图片层;用来绘制图片等操作。(z-index: 100+code, 101~150;也就是图片编号在1~25的在色调层之下,26~50的在色调层之上) -- ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 160) +- image1\~50**[D]**:图片层;用来绘制图片等操作。(z-index: 100+code, 101~150) +- ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 140) - data:数据层;用来绘制一些顶层的或更新比较快的数据,如左上角的提示,战斗界面中数据的变化等等。 (z-index: 170) +请注意:显示图片事件将自动创建一个图片层,z-index是100+图片编号。 + +而,色调层的z-index是25,ui层的z-index是140;因此,图片编号在1~24的将被色调层遮挡,25~40的将被ui层遮挡,41~50的将遮挡UI层。 + ### 动态创建canvas 从V2.5.3开始,可以在H5样板中任意动态创建canvas并进行使用。 @@ -104,11 +108,11 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); 从V2.5.4开始,贴图也允许进行帧动画,只要设置第五项的数值。 ``` js -"images": [[96,120,"bg.jpg",0]], // 背景图;你可以选择一张或多张图片来作为背景/前景素材。 -"images": [], // 无任何背景图 -"images": [[32,32,"house.png",0], [160,170,"bed.png",1]] // 在(32,32)放一个house.png在背景层,且(160,170)放bed.png在前景层 -"images": [[96,120,"tree.png",2]] // 如果写2,则会自动调节遮挡效果 -"images": [[64,0,"x.png",1,4]] // 这是一个前景层的4帧动画贴图 +[[96,120,"bg.jpg",0]] // 背景图;你可以选择一张或多张图片来作为背景/前景素材。 +[] // 无任何背景图 +[[32,32,"house.png",0], [160,170,"bed.png",1]] // 在(32,32)放一个house.png在背景层,且(160,170)放bed.png在前景层 +[[96,120,"tree.png",2]] // 如果写2,则会自动调节遮挡效果 +[[64,0,"x.png",1,4]] // 这是一个前景层的4帧动画贴图 ``` images为一个数组,代表当前层所有作为背景素材的图片信息。每一项为一个五元组,分别为该背景素材的x,y,图片名,遮挡方式和帧数。 @@ -129,18 +133,21 @@ images为一个数组,代表当前层所有作为背景素材的图片信息 关于楼层贴图和前景、背景层的层叠覆盖关系,默认是:**地板 - 背景贴图 - 背景图块 - 事件 - 勇士 - 前景贴图 - 前景图块**。 -可以通过修改`libs/maps.js`的`drawMap`函数中下面三行的顺序来改变其覆盖关系。 +可以通过修改`libs/maps.js`的`drawBg`和`drawFg`函数来改变其覆盖关系。 ``` js -// ----- 可以调整这三行的顺序来修改覆盖关系;同层画布上,后绘制的覆盖先绘制的 -// ----- ui.js的drawThumbnail函数也需要对应进行修改。 - -// 绘制楼层贴图 -core.maps.drawFloorImages(floorId, images); -// 绘制背景层图块 -core.maps.drawBgFgMap(floorId, core.canvas.bg, "bg", true); -// 绘制前景层图块 -core.maps.drawBgFgMap(floorId, core.canvas.fg, "fg", true); +////// 绘制背景层 ////// +maps.prototype.drawBg = function (floorId, ctx) { + var onMap = ctx == null; + if (onMap) { + ctx = core.canvas.bg; + core.clearMap(ctx); + } + this._drawBg_drawBackground(floorId, ctx); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + this._drawFloorImages(floorId, ctx, 'bg'); + this._drawBgFgMap(floorId, ctx, 'bg', onMap); +} ``` 楼层贴图可以被事件隐藏和显示,详见[隐藏贴图](event#hideFloorImg:隐藏贴图)的写法。 @@ -192,7 +199,7 @@ ID必须由数字字母下划线组成,数字在1000以内,且均不能和 之后刷新编辑器即可。 -对于怪物和道具,我们也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 +我们也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 @@ -206,85 +213,6 @@ ID必须由数字字母下划线组成,数字在1000以内,且均不能和 2. 下拉框选择autotile,然后点“追加” 3. 看到成功的提示后刷新编辑器即可。 - - - -### 地图生成器使用自定义素材 - -地图生成器是直接从js文件中读取数字-图标对应关系的。 - -因此,在你修改了icons.js和maps.js两个文件,也就是将素材添加到游戏后,地图生成器的对应关系也将同步更新。 - ### 额外素材 从V2.4.2开始,HTML5魔塔样板开始支持额外素材。 @@ -300,6 +228,7 @@ ID必须由数字字母下划线组成,数字在1000以内,且均不能和 **该素材的宽高必须都是32的倍数,且图片上的总图块数不超过1000(即最多有1000个32*32的图块在该图片上)。** ```js +// 在全塔属性中的tilesets导入素材 "tilesets": ["1.png", "2.png"] // 导入两个额外素材,文件名分别是1.png和2.png ``` @@ -350,19 +279,6 @@ core.status.hero.atk += core.values.redJewel + 2*ratio 具体过程比较复杂,需要一定的JS能力,在这里就不多说了,有需求可以找`艾之葵`进行了解。 -但值得一提的是,我们可以使用`core.hasItem(name)` 来判断是否某个道具是否存在。例如下面是passNet(通过路障处理)的一部分: - -``` js -/****** 经过路障 ******/ -events.prototype.passNet = function (data) { - // 有鞋子 - if (core.hasItem('shoes')) return; - if (data.event.id=='lavaNet') { // 血网 -// ... 下略 -``` - -我们进行了一个简单的判断,如果拥有绿鞋,则不进行任何路障的处理。 - ### 实战!拿到神圣盾后免疫吸血、领域、夹击效果 1. 在itemEffect中修改拿到神圣盾时的效果,标记一个自定义Flag。 @@ -444,86 +360,9 @@ control.prototype.useFly = function (need) { ``` 修改时,请先把`null`改成空字符串`""`,然后再双击进行编辑。 - - ## 自定义怪物属性 -如果你对现有的怪物不满意,想自行添加怪物属性也是可以的。具体参见脚本编辑-getSpecials。 +如果你对现有的怪物不满意,想自行添加怪物属性也是可以的。具体参见脚本编辑的getSpecials。 你需自己指定一个special数字,修改属性名和属性提示文字。后两者可以直接写字符串,或写个函数传入怪物。 @@ -552,7 +391,8 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 // ... 在这里写你要执行脚本 // **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为** if (core.hasItem('...')) { - core.useItem('...'); + core.status.route.push("key:0"); // 记录按键到录像中 + core.useItem('...', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 } break; @@ -560,6 +400,10 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 强烈建议所有新增的自定义快捷键均给个对应的永久道具可点击,以方便手机端的行为。 +使用`core.status.route.push("key:"+keyCode)`可以将这次按键记录在录像中。 + +!> 如果记录了按键,且使用道具的话,需要将useItem的第二个参数设为true,避免重复记录! + 可以使用altKey来判断Alt键是否被同时按下。 ## 公共事件 @@ -574,13 +418,9 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 ## 插件系统 -在H5中,提供了“插件”系统。具体参见“脚本编辑 - 插件编写”。 +在H5中,提供了“插件”系统。在V2.6中提供了一个插件下拉框,用户可以自行创建和写插件。 -![插件编写](./img/plugin.png) - -当我们在这上面定义了自己需要的函数(插件后),就可以通过任何方式进行调用。 - -在这个插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。 +在插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。 下面是一个很简单的例子,我编写一个插件函数,其效果是让勇士生命值变成原来的x倍,并令面前的图块消失。 @@ -599,11 +439,13 @@ this.myfunc = function(x) { 网站上也提供了一个[插件库](https://h5mota.com/plugins/),欢迎大家把自己写的插件进行共享。 +从V2.6开始,在插件中用`this.xxx`定义的函数将会被转发到core中。例如上述的`myfunc`除了`core.plugin.myfunc`外也可以直接`core.myfunc`调用。 + +详见[函数转发](api#函数转发)。 + ## 标题界面事件化 -从V2.5.3开始,我们可以将标题界面的绘制和游戏开始用事件来完成。可以通过绘制画布、 - -全塔属性,flags中的startUsingCanvas可以决定是否开启标题界面事件化。 +从V2.5.3开始,我们可以将标题界面的绘制和游戏开始用事件来完成。可以通过绘制画布、全塔属性,flags中的startUsingCanvas可以决定是否开启标题界面事件化。 然后就可以使用“事件流”的形式来绘制标题界面、提供选项等等。 @@ -619,36 +461,70 @@ this.myfunc = function(x) { 从V2.5.3以后,我们可以给手机端增加按键了,这样将非常有利于技能的释放。 -当用户在竖屏模式下点击工具栏,就会在工具栏按钮和快捷键模式之间进行切换。 +用户在菜单栏打开“拓展键盘”后,在竖屏模式下点击工具栏,就会在工具栏按钮和快捷键模式之间进行切换。 -切换到快捷键模式后,可以点1-7,分别等价于在电脑端按键1-7。 +切换到快捷键模式后,可以点1-8,分别等价于在电脑端按键1-8。 可以在脚本编辑的onKeyUp中定义每个快捷键的使用效果,比如使用道具或释放技能等。 -默认值下,1使用破,2使用炸,3使用飞,4使用其他存在的道具,5-7未定义。可以相应修改成自己的效果。 +默认值下,1使用破,2使用炸,3使用飞,4使用其他存在的道具,5-8未定义。可以相应修改成自己的效果。 -也可以替换icons.png中的对应图标,以及修改main.js中`main.statusBar.image.btn1~7`中的onclick事件来自定义按钮和对应按键。 +也可以替换icons.png中的对应图标,以及修改main.js中`main.statusBar.image.btn1~8`中的onclick事件来自定义按钮和对应按键。 非竖屏模式下、回放录像中、隐藏状态栏中,将不允许进行切换。 -## 自定义状态栏(新增显示项) +## 自绘状态栏 + +从V2.5.3开始允许自绘状态栏。要自绘状态栏,则应该打开全塔属性中的`statusCanvas`开关。 + +自绘模式下,全塔属性中的`statusCanvasRowsOnMobile`将控制竖屏模式下的状态栏行数。 + +开启自绘模式后,可以在脚本编辑的`drawStatusBar`中自行进行绘制。 + +横屏模式下的状态栏为`129x416`(15x15则是`149x480`);竖屏模式下的状态栏为`416*(32*rows+9)`(15x15是480)。 + +具体可详见脚本编辑的`drawStatusBar`函数。 + +## 自定义状态栏的显示项 在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - statusLeftBackground)等等。 但是,如果我们还想新增其他项目的显示,比如攻速或者暴击,该怎么办? -需要进行如下几个操作: +我们可以[自绘状态栏](#自绘状态栏),或者采用下面两个方式之一来新增。 + +### 利用已有项目 + +一个最为简单的方式是,直接利用已有项目。 + +例如,如果本塔中没有技能栏,则可以使用技能栏所对应的显示项。 + +1. 覆盖project/icons.png中技能的图标 +2. 打开全塔属性的enableSkill开关 +3. 在脚本编辑-updateStatusBar中可以直接替换技能栏的显示内容 + +``` + // 设置技能栏 + if (core.flags.enableSkill) { + // 替换成你想显示的内容,比如你定义的一个flag:abc。 + core.setStatusBarInnerHTML('skill', core.getFlag("abc", 0)); + } +``` + +### 额外新增新项目 + +如果是在需要给状态栏新定义项目,则需要进行如下几个操作: 1. 定义ID;比如攻速我就定义speed,暴击可以简单的定义baoji;你也可以定义其他的ID,但是不能和已有的重复。这里以speed为例。 -2. 在index.html的statusBar中(44行起),进行该状态栏项的定义。仿照其他几项,插在其应当显示的位置,注意替换掉相应的ID。 +2. 在index.html的statusBar中(46行起),进行该状态栏项的定义。仿照其他几项,插在其应当显示的位置,注意替换掉相应的ID。 ``` html

``` -3. 在editor.html中的statusBar(323行起),仿照第二点同样添加;这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。 -4. 使用便捷PS工具,打开icons.png,新增一行并将魔力的图标P上去;记下其索引比如37(从0开始数)。 +3. 在editor.html中的statusBar(383行起),仿照第二点同样添加;这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。 +4. 使用便捷PS工具,打开project/icons.png,新增一行并将魔力的图标P上去;记下其索引比如37(从0开始数)。 5. 在main.js的this.statusBar中增加图片、图标和内容的定义。 ``` js this.statusBar = { @@ -674,17 +550,15 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); ## 技能塔的支持 -其实,在HTML5上制作技能塔是完全可行的。 - 要支持技能塔,可能需要如下几个方面: +从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。 + - 魔力(和上限)的添加;技能的定义 - 状态栏的显示 - 技能的触发(按键与录像问题) - 技能的效果 -从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。 - ### 魔力的定义添加;技能的定义 从V2.5开始,提供了status:mana选项,可以直接代表当前魔力值。 @@ -697,6 +571,8 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); 如果flag:skill不为0,则代表当前处于某个技能开启状态,且状态栏显示flag:skillName值。伤害计算函数中只需要对flag:skill进行处理即可。 +!> 关于魔力上限:样板中默认没有提供status:manamax + ### 状态栏的显示 从V2.5开始,魔力值和技能名的状态栏项目已经被添加,可以直接使用。 @@ -706,9 +582,14 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); ``` js // 设置魔力值 if (core.flags.enableMana) { - // 也可以使用flag:manaMax来表示最大魔力值 - // core.status.hero.mana = Math.max(core.status.hero.mana, core.getFlag('manaMax', 10)); - // core.statusBar.mana.innerHTML = core.status.hero.mana + "/" + core.getFlag('manaMax', 10); + // status:manamax 只有在非负时才生效。 + if (core.status.hero.manamax != null && core.status.hero.manamax >= 0) { + core.status.hero.mana = Math.min(core.status.hero.mana, core.status.hero.manamax); + core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.status.hero.manamax); + } + else { + core.setStatusBarInnerHTML("mana", core.status.hero.mana); + } } // 设置技能栏 if (core.flags.enableSkill) { @@ -761,14 +642,15 @@ else { // 关闭技能 case 87: // W:开启技能“二倍斩” // 检测技能栏是否开启,是否拥有“二倍斩”这个技能道具 if (core.flags.enableSkill && core.hasItem('skill1')) { - core.useItem('skill1'); + core.status.route.push("key:87"); + core.useItem('skill1', true); } break; ``` 在勇士处于停止的条件下,按下W键时,判断技能的道具是否存在,如果存在再使用它。 -!> 1,2,3这三个键被默认绑定到了破炸飞;如果想用的话也是一样,只不过是把已有的实现进行替换。 +!> 由于现在手机端存在拓展键盘,也强烈建议直接覆盖1-8的使用效果,这样手机端使用也非常方便。 ### 技能的效果 @@ -816,141 +698,6 @@ if (core.flags.enableSkill) { 通过上述这几种方式,我们就能成功的让H5支持技能啦! -## 成就系统 - -我们还可以给HTML5魔塔增加成就系统。注意到成就是和游戏相关,因此需要使用getLocalStorage而不是getFlag判定。 - -可将下面的代码粘贴到脚本编辑 - 插件编写中。 - -``` js -// 所有成就项的定义 -this.achievements = [ - // 每行一个,分别定义flag、名称、描述、是否存在提示、成就点数 - {"flag": "a1", "name": "成就1", "text": "成就1的达成描述", "hint": false, "point": 1}, - // 可以继续往后新增其他的。 -]; - -// 达成成就;如 core.plugin.achieve("a1") 即达成a1对应的成就 -this.achieve = function (flag) { - // 获得已达成的成就;如果跟存档而不是跟游戏则改成getFlag - var achieved = core.getLocalStorage("achievements", []); - var point = core.getLocalStorage("achievePoint", 0); - // 已经获得该成就 - if (achieved.indexOf(flag)>=0) return; - // 尝试达成成就;找到对应的成就项 - this.achievements.forEach(function (one) { - if (one.flag == flag) { - // 执行达成成就的操作;也可以自行在上面加上达成成就后的事件 - core.insertAction("\t[达成成就:"+one.name+"]"+one.text); - point += one.point || 0; - } - }); - achieved.push(flag); - // 存入localStorage中;如果跟存档走则使用setFlag - core.setLocalStorage("achievements", achieved); - core.setLocalStorage("achievePoint", point); -} - -// 获得所有成就说明;这里简单使用两个insertAction,你也可以修改成自己的实现 -// 简单一点的可以使用insertAction+剧情文本;稍微复杂一点的可以使用图片化文本等;更复杂的可以自绘UI。 -this.getAchievements = function () { - var achieved = core.getLocalStorage("achievements", []); - var yes = [], no = []; - // 对所有成就进行遍历 - this.achievements.forEach(function (one) { - // 检测是否达成 - if (achieved.indexOf(one.flag)>=0) { - yes.push(one.name+":"+one.text); - } - else { - no.push(one.name+":"+(one.hint?one.text:"达成条件请自行探索")); - } - }); - core.insertAction([ - "\t[已达成的成就]"+(yes.length==0?"暂无":yes.join("\n")), - "\t[尚未达成的成就]"+(no.length==0?"暂无":no.join("\n")) - ]); -} -``` - - - -## 多角色的支持 - -其实,我们的样板还能支持多角色的制作。比如《黑·白·间》之类的塔也是完全可以刻的。 - -你只需要如下几步来达到多角色的效果。 - -1. 每个角色弄一张行走图。相关信息参见[自定义事件:setHeroIcon](event#setHeroIcon:更改角色行走图)。 -2. [覆盖楼传事件](#覆盖楼传事件),这样可以通过点工具栏的楼层传送按钮来切换角色。当然你也完全可以自己写一个道具,或[自定义快捷键](#自定义快捷键)来进行绑定。 -3. 将下述代码直接贴入脚本编辑 - 插件编写中。 - ``` js - // 所有需要保存的内容;这些保存的内容不会多角色共用,在切换时会进行恢复。 - // 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化, - // 多角色共用hp的话则删除hp,等等。总之,不共用的属性都在这里进行定义就好。 - var hero1 = { // 1号勇士(默认的是0号) - "floorId": "MT0", // 该角色楼层ID - "icon": "hero1.png", // 角色的行走图名称 - "name": "1号角色", - "lv": 1, - "hp": 1000, - "atk": 10, - "def": 10, - "mdef": 0, - "loc": {"x": 0, "y": 0, "direction": "up"}, - // 如果道具不共用就将下面这句话取消注释 - // "items": {"keys":{"yellowKey":0,"blueKey":0,"redKey":0},"tools":{},"constants":{}} - } - // 也可以类似新增其他勇士 - // var hero2 = { ... - - var heroCount = 2; // 包含默认的在内总共多少个勇士,该值需手动修改。 - - // 初始化该勇士 - this.initHeros = function () { - core.status.hero.icon = "hero.png"; - core.setFlag("hero1", core.clone(hero1)); // 将属性值存到变量中 - // core.setFlag("hero2", core.clone(hero2)); // 更多的勇士... - } - - // 切换勇士 - this.changeHero = function (toHeroId) { - var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID - if (!core.isset(toHeroId)) { - toHeroId = (currHeroId+1)%heroCount; - } - if (currHeroId == toHeroId) return; - - var saveList = Object.keys(hero1); - - // 保存当前内容 - var toSave = {}; - saveList.forEach(function(name) { - if (name=='floorId') toSave[name] = core.status.floorId; // 楼层单独设置 - else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象 - }) - - core.setFlag("hero"+currHeroId, toSave); // 将当前角色信息进行保存 - var data = core.getFlag("hero"+toHeroId); // 获得要切换的角色保存内容 - - // 设置角色的属性值 - saveList.forEach(function(name) { - if (name != 'floorId') - core.status.hero[name] = core.clone(data[name]); - }) - - // 插入事件:改变角色行走图并进行楼层切换 - core.insertAction([ - {"type": "setHeroIcon", "name": data.icon||"hero.png"}, // 改变行走图 - {"type": "changeFloor", "floorId": data.floorId, "loc": [data.loc.x, data.loc.y], - "direction": data.loc.direction, "time": 0} // 楼层切换事件 - ]) - core.setFlag("heroId", toHeroId); // 保存切换到的角色ID - } - ``` -3. 在脚本编辑 - setInitData中加上`core.plugin.initHeros()`来初始化新勇士。(写在`core.events.afterLoadData()`后,反大括号之前。) -4. 如果需要切换角色(包括事件、道具或者快捷键等),可以直接调用自定义JS脚本:`core.plugin.changeHero();`进行切换。也可以指定参数调用`core.plugin.changeHero(1)`来切换到某个具体的勇士上。 - ## 系统使用的flag变量 众所周知,自定义flag变量都可以任意定义并取用(未定义直接取用的flag默认值为0)。 diff --git a/_server/data.comment.js b/_server/data.comment.js index c4ad3175..c4b8b951 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -187,6 +187,11 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "textarea", "_data": "初始生命值" }, + "manamax": { + "_leaf": true, + "_type": "textarea", + "_data": "魔力上限;此项非负才会生效(null或小于0都不会生效)" + }, "mana": { "_leaf": true, "_type": "textarea", diff --git a/project/data.js b/project/data.js index 096e2dc6..ba2c9d99 100644 --- a/project/data.js +++ b/project/data.js @@ -78,6 +78,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "lv": 1, "hpmax": 9999, "hp": 1000, + "manamax": -1, "mana": 0, "atk": 100, "def": 100, diff --git a/project/functions.js b/project/functions.js index 2044fe73..07507e3d 100644 --- a/project/functions.js +++ b/project/functions.js @@ -753,152 +753,156 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // keyCode:当前按键的keyCode(每个键的keyCode自行百度) // altKey:Alt键是否被按下,为true代表同时按下了Alt键 // 可以在这里任意增加或编辑每个按键的行为 - + // 如果处于正在行走状态,则不处理 - if (!core.status.heroStop || core.status.heroMoving > 0) + if (core.isMoving()) return; // Alt+0~9,快捷换上套装 - if (altKey && keyCode>=48 && keyCode<=57) { - core.items.quickLoadEquip(keyCode-48); + if (altKey && keyCode >= 48 && keyCode <= 57) { + core.items.quickLoadEquip(keyCode - 48); return; } // 根据keyCode值来执行对应操作 switch (keyCode) { - case 27: // ESC:打开菜单栏 - core.openSettings(true); - break; - case 88: // X:使用怪物手册 - core.openBook(true); - break; - case 71: // G:使用楼传器 - core.useFly(true); - break; - case 65: // A:读取自动存档(回退) - core.doSL("autoSave", "load"); - break; - case 83: // S:存档 - core.save(true); - break; - case 68: // D:读档 - core.load(true); - break; - case 69: // E:打开光标 - core.ui.drawCursor(); - break; - case 84: // T:打开道具栏 - core.openToolbox(true); - break; - case 81: // Q:打开装备栏 - core.openEquipbox(true); - break; - case 90: // Z:转向 - core.turnHero(); - break; - case 75: case 86: // V:打开快捷商店列表 - core.openQuickShop(true); - break; - case 32: // SPACE:轻按 - core.getNextItem(); - break; - case 82: // R:回放录像 - core.actions._clickSyncSave_replay(); - break; - case 33: case 34: // PgUp/PgDn:浏览地图 - core.ui.drawMaps(); - break; - case 77: // M:绘图模式 - core.ui.drawPaint(); - break; - case 66: // B:打开数据统计 - core.ui.drawStatistics(); - break; - case 72: // H:打开帮助页面 - core.ui.drawHelp(); - break; - case 78: // N:重新开始 - core.confirmRestart(); - break; - case 79: // O:查看工程 - core.actions._clickGameInfo_openProject(); - break; - case 80: // P:游戏主页 - core.actions._clickGameInfo_openComments(); - break; - case 49: // 快捷键1: 破 - if (core.hasItem('pickaxe')) { - if (core.canUseItem('pickaxe')) { - core.useItem('pickaxe'); - } - else { - core.drawTip('当前不能使用破墙镐'); - } + case 27: // ESC:打开菜单栏 + core.openSettings(true); + break; + case 88: // X:使用怪物手册 + core.openBook(true); + break; + case 71: // G:使用楼传器 + core.useFly(true); + break; + case 65: // A:读取自动存档(回退) + core.doSL("autoSave", "load"); + break; + case 83: // S:存档 + core.save(true); + break; + case 68: // D:读档 + core.load(true); + break; + case 69: // E:打开光标 + core.ui.drawCursor(); + break; + case 84: // T:打开道具栏 + core.openToolbox(true); + break; + case 81: // Q:打开装备栏 + core.openEquipbox(true); + break; + case 90: // Z:转向 + core.turnHero(); + break; + case 75: + case 86: // V:打开快捷商店列表 + core.openQuickShop(true); + break; + case 32: // SPACE:轻按 + core.getNextItem(); + break; + case 82: // R:回放录像 + core.actions._clickSyncSave_replay(); + break; + case 33: + case 34: // PgUp/PgDn:浏览地图 + core.ui.drawMaps(); + break; + case 77: // M:绘图模式 + core.ui.drawPaint(); + break; + case 66: // B:打开数据统计 + core.ui.drawStatistics(); + break; + case 72: // H:打开帮助页面 + core.ui.drawHelp(); + break; + case 78: // N:重新开始 + core.confirmRestart(); + break; + case 79: // O:查看工程 + core.actions._clickGameInfo_openProject(); + break; + case 80: // P:游戏主页 + core.actions._clickGameInfo_openComments(); + break; + case 49: // 快捷键1: 破 + if (core.hasItem('pickaxe')) { + if (core.canUseItem('pickaxe')) { + core.status.route.push("key:49"); // 将按键记在录像中 + core.useItem('pickaxe', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + } else { + core.drawTip('当前不能使用破墙镐'); } - break; - case 50: // 快捷键2: 炸 - if (core.hasItem('bomb')) { - if (core.canUseItem('bomb')) { - core.useItem('bomb'); - } - else { - core.drawTip('当前不能使用炸弹'); - } + } + break; + case 50: // 快捷键2: 炸 + if (core.hasItem('bomb')) { + if (core.canUseItem('bomb')) { + core.status.route.push("key:50"); // 将按键记在录像中 + core.useItem('bomb', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 + } else { + core.drawTip('当前不能使用炸弹'); } - else if (core.hasItem('hammer')) { - if (core.canUseItem('hammer')) { - core.useItem('hammer'); - } - else { - core.drawTip('当前不能使用圣锤'); - } - - } - break; - case 51: // 快捷键3: 飞 - if (core.hasItem('centerFly')) { - core.ui.drawCenterFly(); - } - break; - case 52: // 快捷键4:破冰/冰冻/地震/上下楼器/... 其他道具依次判断 - { - var list = ["icePickaxe", "snow", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"]; - for (var i=0;i= 0) { + core.status.hero.mana = Math.min(core.status.hero.mana, core.status.hero.manamax); + core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.status.hero.manamax); + } + else { + core.setStatusBarInnerHTML("mana", core.status.hero.mana); + } } // 设置技能栏 if (core.flags.enableSkill) { From f65eab56f65d5462ae4cb7e751ca4a98034a6f4f Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 31 Mar 2019 23:58:30 +0800 Subject: [PATCH 32/64] api docs --- _docs/_api.md | 689 +++++++++++++++++++++++++++++++++++++++ _docs/_sidebar.md | 2 +- _docs/api.md | 50 ++- _docs/element.md | 2 +- _docs/event.md | 4 +- _docs/img/console.jpg | Bin 0 -> 18716 bytes _docs/personalization.md | 4 +- _docs/start.md | 2 +- 8 files changed, 744 insertions(+), 9 deletions(-) create mode 100644 _docs/_api.md create mode 100644 _docs/img/console.jpg diff --git a/_docs/_api.md b/_docs/_api.md new file mode 100644 index 00000000..64362e3b --- /dev/null +++ b/_docs/_api.md @@ -0,0 +1,689 @@ +# 附录: API列表 + +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * + +**这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** + +如有任何疑问,请联系小艾寻求帮助。 + +可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 + +**以下所有异步API都会加上[异步]的说明,存在此说明的请勿在事件处理的自定义脚本中使用。** + +!> 最常用的新手向命令,强烈建议每个人了解 + +``` text + +core.status.floorId +获得当前层的floorId。 + + +core.status.maps +获得所有楼层的地图信息。 + + +core.status.thisMap +获得当前楼层信息,其等价于core.status.maps[core.status.floorId]。 + + +core.floors +获得所有楼层的信息。例如core.floors[core.status.floorId].events可获得本楼层的所有自定义事件。 + + +core.status.hero +获得当前勇士状态信息。例如core.status.hero.atk就是当前勇士的攻击力数值。 + + +core.material.enemys +获得所有怪物信息。例如core.material.enemys.greenSlime就是获得绿色史莱姆的属性数据。 + + +core.material.items +获得所有道具的信息。 + + +core.debug() +开启调试模式。此模式下可以按Ctrl键进行穿墙,并忽略一切事件。 +此模式下不可回放录像和上传成绩。 + + +core.updateStatusBar() +立刻刷新状态栏和地图显伤。 + + +core.setStatus('atk', 1000) +将攻击力设置为1000;这里把atk可以改成hp, def, mdef, money, experience等等。 +本句等价于 core.status.hero.atk = 1000 + + +core.getStatus('atk') +返回当前攻击力数值。本句等价于 core.status.hero.atk。 + + +core.setHeroLoc('x', 5) +设置勇士位置。这句话的意思是将勇士当前位置的横坐标设置为5。 +同理可以设置勇士纵坐标 core.setHeroLoc('y', 3)。 +值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;如需立刻重新绘制地图还需调用: +core.clearMap('hero'); core.drawHero(); +来对界面进行更新。 + + +core.setItem('pickaxe', 10) +将破墙镐个数设置为10个。这里可以写任何道具的ID。 + + +core.addItem('pickaxe', 2) +将破墙镐的个数增加2个,无任何特效。这里可以写任何道具的ID。 + + +core.getItem('pickaxe', 4) +令勇士获得4个破墙镐。这里可以写任何道具的ID。 +和addItem相比,使用getItem会播放获得道具的音效,也会在左上角绘制获得提示。 + + +core.removeItem('pickaxe', 3) +删除3个破墙镐。第二项可忽略,默认值为1。 + + +core.itemCount('pickaxe') +返回当前破墙镐的个数。这里可以写任何道具的ID。 + + +core.hasItem('pickaxe') +返回当前是否存在某个道具。等价于 core.itemCount('pickaxe')>0 。 + + +core.getEquip(0) +获得0号装备类型(武器)的当前装备的itemId。如果不存在则返回null。 +这里可以写任意装备类型,从0开始和全塔属性中的equipName一一对应。 + + +core.hasEquip('sword1') +获得当前某个具体的装备是否处于正在被装备状态。 + + +core.setFlag('xyz', 2) +设置某个flag/变量的值为2。这里可以写任何的flag变量名。 + + +core.getFlag('xyz', 7) +获得某个flag/变量的值;如果该变量不存在,则返回第二个参数。 +比如 core.getFlag('point', 2) 则获得变量point的值;如果该变量从未定义过则返回2。 + + +core.hasFlag('xyz') +返回是否存在某个变量且不为0。等价于 core.getFlag('xyz', 0)!=0 。 + + +core.removeFlag('xyz') +删除某个flag/变量。 + + +core.insertAction(list, x, y, callback) +插入并执行一段自定义事件。在这里你可以写任意的自定义事件列表,有关详细写法请参见文档-事件。 +x和y如果设置则覆盖"当前事件点"的坐标,callback如果设置则覆盖事件执行完毕后的回调函数。 +例如: core.insertAction(["楼层切换", {"type":"changeFloor", "floorId": "MT3"}]) +将依次显示剧情文本,并执行一个楼层切换的自定义事件。 +-------- +从V2.5.4开始提出了“公共事件”的说法,这里也可以插入一个公共事件名。 +例如:core.insertAction("毒衰咒处理") 将插入公共事件“毒衰咒处理”。 + + +core.changeFloor(floorId, stair, heroLoc, time, callback) [异步] +立刻切换到指定楼层。 +floorId为目标楼层ID,stair为到达的目标楼梯,heroLoc为到达的指定点,time为动画时间,callback为切换完毕后的回调。 +例如: +core.changeFloor('MT2', 'upFloor', null, 600) 切换到MT2层的上楼点,动画事件600ms +core.changeFloor('MT5', null, {'x': 3, 'y': 6}, 0) 无动画切换到MT5层的(3,6)位置。 + + +core.resetMap() +重置当前楼层地图和楼层属性。 +此函数参数有三种形式: + - 不加任何参数,表示重置当前层:core.resetMap() + - 加上一个floorId,表示重置某一层:core.resetMap("MT1") + - 使用一个数组,表示重置若干层:core.resetMap(["MT1", "MT2", "MT3"]) +--------------------------- +** 说明:从V2.5.5开始存档方式发生了改变,在编辑器修改了地图后现在将直接生效,无需再重置地图。 + +R +录像回放的快捷键;这不是一个控制台命令,但是也把它放在这里供使用。 +录像回放在修改地图或新增数据后会很有用。 + +``` + +!> 一些相对高级的命令,针对有一定脚本经验的人 + +``` text + +========== 可直接从core中调用的,最常被使用的函数 ========== +core.js实际上是所有API的入口(路由),核心API的实现在其他几个文件中,core.js主要进行转发操作。 + + +core.nextX(n) +获得勇士面向的第n个位置的x坐标,n可以省略默认为1(即正前方) + + +core.nextY(n) +获得勇士面向的第n个位置的y坐标,n可以省略默认为1(即正前方) + + +core.nearHero(x, y) +判断某个点是否和勇士的距离不超过1。 + + +core.openDoor(id, x, y, needKey, callback) [异步] +尝试开门操作。id为目标点的ID,x和y为坐标,needKey表示是否需要使用钥匙,callback为开门完毕后的回调函数。 +id可为null代表使用地图上的值。 +例如:core.openDoor('yellowDoor', 10, 3, false, function() {console.log("1")}) +此函数返回true代表成功开门,并将执行callback回调;返回false代表无法开门,且不会执行回调函数。 + + +core.battle(id, x, y, force, callback) [异步] +执行战斗事件。id为怪物的id,x和y为坐标,force为bool值表示是否是强制战斗,callback为战斗完毕后的回调函数。 +id可为null代表使用地图上的值。 +例如:core.battle('greenSlime', null, null, true) + + +core.trigger(x, y) [异步] +触发某个地点的事件。 + + +core.isReplaying() +当前是否正在录像播放中 + + +core.drawBlock(block) +重绘某个图块。block应为core.status.thisMap.blocks中的一项。 + + +core.drawMap(floorId, callback) +重绘某一层的地图。floorId为要绘制楼层的floorId,callback为绘制完毕后的回调函数。 + + +core.terrainExists(x, y, id, floorId) +检测某个点是否存在(指定的)地形。 +x和y为坐标;id为地形ID,可为null表示任意地形;floorId为楼层ID,可忽略表示当前楼层。 + + +core.enemyExists(x, y, id, floorId) +检测某个点是否存在(指定的)怪物。 +x和y为坐标;id为怪物ID,可为null表示任意怪物;floorId为楼层ID,可忽略表示当前楼层。 + + +core.getBlock(x, y, floorId, showDisable) +获得某个点的当前图块信息。 +x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +showDisable如果为true,则对于禁用的点和事件也会进行返回。 +如果该点不存在图块,则返回null。 +否则,返回值如下: {"index": xxx, "block": xxx} +其中index为该点在该楼层blocks数组中的索引,block为该图块实际内容。 + + +core.getBlockId(x, y, floorId, showDisable) +获得某个点的图块ID。 +x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +showDisable如果为true,则对于禁用的点和事件也会进行返回。 +如果该点不存在图块,则返回null,否则返回该点的图块ID。 + + +core.getBlockCls(x, y, floorId, showDisable) +获得某个点的图块cls。 +x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +showDisable如果为true,则对于禁用的点和事件也会进行返回。 +如果该点不存在图块,则返回null,否则返回该点的图块cls。 + + +core.showBlock(x, y, floorId) +将某个点从禁用变成启用状态。 + + +core.hideBlock(x, y, floorId) +将某个点从启用变成禁用状态,但不会对其进行删除。 +此函数不会实际将该块从地图中进行删除,而是将该点设置为禁用,以供以后可能的启用事件。 + + +core.removeBlock(x, y, floorId) +将从启用变成禁用状态,并尽可能将其从地图上删除。 +和hideBlock相比,如果该点不存在自定义事件(比如门或普通的怪物),则将直接从地图中删除。 +如果存在自定义事件,则简单的禁用它,以供以后可能的启用事件。 + + +core.setBlock(number, x, y, floorId) +改变图块。number为要改变到的图块数字,x和y为坐标,floorId为楼层ID,可忽略表示当前楼层。 + + +core.useItem(itemId, noRoute, callback) +尝试使用某个道具。itemId为道具ID,noRoute如果为真则该道具的使用不计入录像。 +callback为成功或失败后的回调。 + + +core.canUseItem(itemId) +返回当前能否使用某个道具。 + + +core.loadEquip(itemId, callback) +装备上某个装备。itemId为装备的ID,callback为成功或失败后的回调。 + + +core.unloadEquip(equipType, callback) +卸下某个部位的装备。equipType为装备类型,从0开始;callback为成功或失败后的回调。 + + +core.getNextItem() +轻按。 + + +core.drawTip(text, itemIcon) +在左上角绘制一段提示信息,2秒后消失。itemIcon为道具图标的索引。 + + +core.drawText(contents, callback) [异步] +绘制一段文字。 +不推荐使用此函数,尽量使用core.insertAction(contents)来显示剧情文本。 + + +core.closePanel() +结束一切事件和绘制,关闭UI窗口,返回游戏进程。 + + +core.replaceText(text) +将一段文字中的${}进行计算并替换。 + + +core.calValue(value, prefix, need, times) +计算表达式的实际值。这个函数可以传入status:atk等这样的参数。 + + +core.getLocalStorage(key, defaultValue) +从localStorage中获得某个数据(已被parse);如果对应的key不存在则返回defaultValue。 + + +core.getLocalForage(key, defaultValue, successCallback, errorCallback) +从localForage中获得某个数据(已被parse),如果对应的key不存在则返回defaultValue。 +如果成功则通过successCallback回调,失败则通过errorCallback回调。 + + +core.hasSave(index) +判定当前某个存档位是否存在存档,返回true/false。 +index为存档编号,0代表自动存档,大于0则为正常的存档位。 + + +core.clone(data) +深拷贝某个对象。 + + +core.isset(x) +测试x是否不为null,不为undefined也不为NaN。 + + +core.rand(num) +使用伪种子生成伪随机数。该随机函数能被录像支持。 +num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到1之间的浮点数。 +此函数为伪随机算法,SL大法无效。(即多次SL后调用的该函数返回的值都是相同的。) + + +core.rand2(num) +使用系统的随机数算法得到的随机数。该随机函数能被录像支持。 +num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到2147483647之间的整数。 +此函数使用了系统的Math.random()函数,支持SL大法。 +但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 + + +core.restart() [异步] +返回标题界面。 + + +========== core.actions.XXX 和游戏控制相关的函数 ========== +actions.js主要用来进行用户交互行为的处理。 +所有用户行为,比如按键、点击、滑动等等,都会被此文件接收并进行操作。 + + +========== core.control.XXX 和游戏控制相关的函数 ========== +control.js主要用来进行游戏控制,比如行走控制、自动寻路、存读档等等游戏核心内容。 + +core.control.setGameCanvasTranslate(canvasId, x, y) +设置大地图的偏移量 + + +core.control.updateViewport() +更新大地图的可见区域 + + +core.control.gatherFollowers() +立刻聚集所有的跟随者 + + +core.control.replay() +回放下一个操作 + + +========== core.enemys.XXX 和怪物相关的函数 ========== +enemys.js主要用来进行怪物相关的内容,比如怪物的特殊属性,伤害和临界计算等。 + + +core.enemys.hasSpecial(special, test) +测试怪物是否含有某个特殊属性。 +常见用法: core.enemys.hasSpecial(monster.special, 3) ## 测试是否拥有坚固 + + +core.enemys.getSpecialText(enemyId) +返回一个列表,包含该怪物ID对应的所有特殊属性。 + + +core.enemys.getSpecialHint(enemy, special) +获得怪物某个(或全部)特殊属性的文字说明。 + + +core.enemys.canBattle(enemyId, x, y, floorId) +返回当前能否战胜某个怪物。 +后面三个参数是怪物坐标和楼层。 + + +core.enemys.getDamage(enemyId, x, y, floorId) +返回当前对某个怪物的战斗伤害。如果无法战斗,返回null。 +后面三个参数是怪物坐标和楼层。 + + +core.enemys.getExtraDamage(enemyId) +返回某个怪物会对勇士造成的额外伤害(不可被魔防抵消),例如仇恨、固伤等等。 + + +core.enemys.nextCriticals(enemyId, number, x, y, floorId) +返回一个列表,为接下来number(可忽略,默认为1)个该怪物的临界值和临界减伤。 +列表每一项类似 [x,y] 表示临界值为x,且临界减伤为y。 +如果无临界值,则返回空列表。 + + +core.enemys.getDefDamage(enemyId, k, x, y, floorId) +获得k(可忽略,默认为1)防减伤值。 + + +core.enemys.getDamageInfo(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) +获得实际战斗信息,比如伤害,回合数,每回合伤害等等。 +此函数是实际战斗过程的计算。 + + +core.enemys.calDamage(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) +获得在某个勇士属性下怪物伤害;实际返回的是上面getDamageInfo中伤害的数值。 + + +core.enemys.getCurrentEnemys(floorId) +获得某一层楼剩余所有怪物的信息(供怪物手册使用) + + +========== core.events.XXX 和事件相关的函数 ========== +events.js主要用来进行事件处理,比如自定义事件,以及某些条件下可能会被触发的事件。 +大多数事件API都在脚本编辑中存在,这里只列出部分比较重要的脚本编辑中不存在的API。 + + +core.events.gameOver(ending, fromReplay) +游戏结束并上传的事件。 +该函数将提问是否上传和是否下载录像,并返回标题界面。 + + +core.events.doEvents(list, x, y, callback) [异步] +开始执行某个事件。 +请不要执行此函数,尽量使用 core.insertAction(list, x, y, callback) 来开始执行一段事件。 + + +core.events.doAction() +执行下一个事件。此函数中将对所有自定义事件类型分别处理。 + + +core.events.getCommonEvent(name) +根据名称获得一个公共事件;如果不存在对应的公共事件则返回null。 + + +core.events.openShop(shopId, needVisited) [异步] +打开一个全局商店。needVisited表示是否需要该商店已被打开过。 + + +core.events.disableQuickShop(shopId) +禁用一个全局商店 + + +core.events.canUseQuickShop(shopId) +当前能否使用某个快捷商店 + + +core.events.setHeroIcon(name) +设置勇士行走图 + + +========== core.items.XXX 和道具相关的函数 ========== +items.js将处理和道具相关的内容,比如道具的使用,获取和删除等等。 + + +core.items.compareEquipment(equipId1, equipId2) +比较两个装备的属性变化值 + + +========== core.loader.XXX 和游戏加载相关的函数 ========== +loader.js将主要用来进行资源的加载,比如加载音乐、图片、动画等等。 + + +========== core.maps.XXX 和地图处理相关的函数 ========== +maps.js主要用来进行地图相关的的操作。包括绘制地图,获取地图上的点等等。 + + +core.maps.getNumberById(id) +根据ID来获得对应的数字。如果该ID不存在对应的数字则返回0。 + + +core.maps.canMoveHero(x,y,direction,floorId) +判断能否前往某个方向。x,y为坐标,可忽略为当前点;direction为方向,可忽略为当前方向。 +floorId为楼层ID,可忽略为当前楼层。 + + +core.maps.canMoveDirectly(destX, destY) +判断当前能否瞬间移动到某个点。 +该函数如果返回0则不可瞬间移动,大于0则可以瞬间移动,且返回值是跨度(即少走的步数)。 + + +core.maps.removeBlockById(index, floorId) +根据索引删除或禁用某块。 + + +core.maps.removeBlockByIds(floorId, ids) +根据索引删除或禁用若干块。 + + +core.maps.drawAnimate(name, x, y, callback) +播放一段动画,name为动画名(需在全塔属性注册),x和y为坐标(0-12之间),callback可选,为播放完毕的回调函数。 +播放过程是异步的,如需等待播放完毕请使用insertAction插入一条type:waitAsync事件。 +此函数将随机返回一个数字id,为此异步动画的唯一标识符。 + + +core.maps.stopAnimate(id, doCallback) +立刻停止一个异步动画。 +id为该动画的唯一标识符(由drawAnimate函数返回),doCallback可选,若为true则会执行该动画所绑定的回调函数。 + + +========== core.ui.XXX 和对话框绘制相关的函数 ========== +ui.js主要用来进行UI窗口的绘制,比如对话框、怪物手册、楼传器、存读档界面等等。 + + +core.ui.getContextByName(canvas) +根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。 +也可以传画布的context自身,则返回自己。 + + +core.clearMap(name) +清空某个画布图层。 +name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。(下同) +如果name也可以是'all',若为all则为清空所有系统画布。 + + +core.ui.fillText(name, text, x, y, style, font) +在某个画布上绘制一段文字。 +text为要绘制的文本,x,y为要绘制的坐标,style可选为绘制的样式,font可选为绘制的字体。(下同) + + +core.ui.fillBoldText(name, text, x, y, style, font) +在某个画布上绘制一个描黑边的文字。 + + +core.ui.fillRect(name, x, y, width, height, style) +绘制一个矩形。style可选为绘制样式。 + + +core.ui.strokeRect(name, x, y, width, height, style) +绘制一个矩形的边框。 + + +core.ui.drawLine(name, x1, y1, x2, y2, style, lineWidth) +绘制一条线。lineWidth可选为线宽。 + + +core.ui.drawArrow(name, x1, y1, x2, y2, style, lineWidth) +绘制一个箭头。 + + +core.ui.setFont(name, font) / core.ui.setLineWidth(name, lineWidth) +设置一个画布的字体/线宽。 + + +core.ui.setAlpha(name, font) / core.ui.setOpacity(name, font) +设置一个画布的绘制不透明度和画布本身的不透明度。 +两者区别如下: + - setAlpha是设置"接下来绘制的内容的不透明度",不会对已经绘制的内容产生影响。比如setAlpha('ui', 0.5)则会在接下来的绘制中使用0.5的不透明度。 + - setOpacity是设置"画布本身的不透明度",已经绘制的内容也会产生影响。比如我已经在UI层绘制了一段文字,再setOpacity则也会看起来变得透明。 +尽量不要对系统画布使用setOpacity(因为会对已经绘制的内容产生影响),自定义创建的画布则不受此限制。 + + +core.ui.setFillStyle(name, style) / core.ui.setStrokeStyle(name, style) +设置一个画布的填充样式/描边样式。 + + +core.ui.setTextAlign(name, align) +设置一个画布的文字对齐模式。 + + +core.ui.calWidth(name, text, font) +计算一段文字在画布上的绘制宽度 +font可选,如果存在则会先设置该画布上的字体。 + + +core.ui.drawImage(name, image, x, y, w, h, x1, y1, w1, h1) +在一个画布上绘制图片。 +name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。 +image为要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取),图片本身,或者一个画布。 +后面的8个坐标参数与canvas的drawImage的八个参数完全相同。 +请查看 http://www.w3school.com.cn/html5/canvas_drawimage.asp 了解更多。 + + +core.ui.createCanvas(name, x, y, width, height, zIndex) +动态创建一个画布。name为要创建的画布名,如果已存在则会直接取用当前存在的。 +x,y为创建的画布相对窗口左上角的像素坐标,width,height为创建的长宽。 +zIndex为创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。 +返回创建的画布的context,也可以通过core.dymCanvas[name]调用。 + + +core.ui.relocateCanvas(name, x, y) +重新定位一个自定义画布。 + + +core.ui.resizeCanvas(name, x, y) +重新设置一个自定义画布的大小。 + + +core.ui.deleteCanvas(name) +删除一个自定义画布。 + + +core.ui.deleteAllCanvas() +清空所有的自定义画布。 + + +core.ui.drawThumbnail(floorId, canvas, blocks, x, y, size, heroLoc, heroIcon) +绘制一个缩略图,比如楼传器界面,存读档界面等情况。 +floorId为目标楼层ID,canvas为要绘制到的图层,blocks为要绘制的所有图块。 +x,y为该图层开始绘制的起始点坐标,size为每一格的像素,heroLoc为勇士坐标,heroIcon为勇士图标。 + + +========== core.utils.XXX 工具类的辅助函数 ========== +utils.js主要用来进行一些辅助函数的计算。 + + +core.utils.splitLines(canvas, text, maxLength, font) +自动切分长文本的换行。 +canvas为图层,text为要自动换行的内容,maxLength为每行最长像素,font为文本的字体。 + + +core.utils.cropImage(image, size) +纵向对图片进行切分(裁剪)。 + + +core.utils.push(a,b) +向某个数组后插入另一个数组或元素 + + +core.utils.unshift(a, b) +向某个数组前插入另一个数组或元素 + + +core.utils.encodeBase64(str) +Base64加密字符串 + + +core.utils.decodeBase64(str) +Base64解密字符串 + + +core.utils.formatBigNumber(x, onMap) +大数据的格式化 + + +core.utils.subarray(a, b) +检查b是否是a的从头开始子串。 +如果是,则返回a删去b的一段;否则返回null。 + + +core.utils.same(a, b) +比较a和b两个对象是否相同 + + +core.utils.clamp(x, a, b) +将x限制在[a,b]之间的范围内 + + +core.utils.arrayToRGB(color) +将形如[255,0,0]之类的数组转成#FF0000这样的RGB形式。 + + +core.utils.arrayToRGBA(color) +将形如[255,0,0,1]之类的数组转成rgba(255,0,0,1)这样的RGBA形式。 + + +core.utils.encodeRoute(list) +压缩加密路线。可以使用core.encodeRoute(core.status.route)来压缩当前路线。 + + +core.utils.decodeRoute(route) +解压缩(解密)路线。 + + +core.utils.readFile(success, error, readType) [异步] +尝试请求读取一个本地文件内容。 +success和error为成功/失败后的回调,readType不设置则以文本读取,否则以DataUrl形式读取。 + + +core.utils.readFileContent(content) [异步] +文件读取完毕后的内容处理。 + + +core.utils.download(filename, content) +尝试生成并下载一个文件。 + + +core.utils.copy(data) +尝试复制一段文本到剪切板。 + + +core.utils.http(type, url, formData, success, error) [异步] +发送一个异步HTTP请求。 +type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 +success为成功后的回调,error为失败后的回调。 + +``` diff --git a/_docs/_sidebar.md b/_docs/_sidebar.md index 039c2ea0..f7c5a0a1 100644 --- a/_docs/_sidebar.md +++ b/_docs/_sidebar.md @@ -3,4 +3,4 @@ - [元件说明](element) - [事件](event) - [个性化](personalization) -- [附录:API列表](api) +- [脚本](api) diff --git a/_docs/api.md b/_docs/api.md index 88697135..f445d204 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1,6 +1,52 @@ -# 附录: API列表 +# 脚本 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * + +在V2.6版本中,基本对整个项目代码进行了重写,更加方便 + +## 控制台的使用 + +在Chrome浏览器中,按(Ctrl+Shift+I)可打开控制台。 + +![](img/console.jpg) + +控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。 + +有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。 + +### Console:命令行 + +### Sources:断点调试 + +### Elements:网页元素查看 + +## 整体项目架构 + +## 函数的转发,复写函数 + +## 附录:API列表 + +### core.js + +### actions.js + +### control.js + +### enemys.js + +### events.js + +### icons.js + +### items.js + +### loader.js + +### map.js + +### ui.js + +### utils.js **这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** diff --git a/_docs/element.md b/_docs/element.md index 4fed9b92..6f4551d3 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -85,7 +85,7 @@ percentage为该装备是否按比例增加属性。 使用`core.getEquip(equipType)`来获得某个装备类型的当前装备。 -更多相关API详见[附录:API列表](api)。 +更多相关API详见[附录:API列表](api#附录:API列表)。 ### 多重装备 diff --git a/_docs/event.md b/_docs/event.md index 7f1e6969..cac07dc8 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1746,11 +1746,11 @@ icon是可选的,如果设置则会在选项前绘制图标,其可以是一 `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统常见可能会被造塔所用到的的API都在[附录:API列表](api)中给出,请进行参照。 +系统常见可能会被造塔所用到的的API都在[API列表](api#附录:API列表)中给出,请进行参照。 **警告:自定义脚本中只能执行同步代码,不可执行任何异步代码,比如直接调用core.changeFloor(...)之类都是不行的。** -[附录:API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 +[API列表](api#附录:API列表)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 如果需要异步的代码都需要用事件(insertAction)来执行,这样事件处理过程和录像回放才不会出错。 diff --git a/_docs/img/console.jpg b/_docs/img/console.jpg new file mode 100644 index 0000000000000000000000000000000000000000..76d593787926c4c49dbe22a60a5fa3e9d3b2cdd4 GIT binary patch literal 18716 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 literal 0 HcmV?d00001 diff --git a/_docs/personalization.md b/_docs/personalization.md index 80944d06..246ffc33 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -51,7 +51,7 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); `core.deleteAllCanvas()`可以删除所有动态创建的画布,`core.relocateCanvas(name, x, y)`和`core.resizeCanvas(name, x, y)`可以对画布的位置和大小进行改变。 -更多详细API请参见[API列表](api)。 +更多详细API请参见[API列表](api#附录:API列表)。 ## 自定义素材 @@ -726,4 +726,4 @@ if (core.flags.enableSkill) { ========================================================================================== -[继续阅读附录:所有API列表](api) +[继续阅读脚本](api) diff --git a/_docs/start.md b/_docs/start.md index 004ea21f..f5b4fa12 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -231,7 +231,7 @@ HTML5的塔都是可以进行控制台调试的。 - `core.resetMap()` 重置当前层地图。 - …… -更多API和详细参数介绍可参见[API列表](api)。 +更多API和详细参数介绍可参见[API列表](api#附录:API列表)。 ## 编辑器的基本操作 From ffda8bdf974dbc6d66d19845bb708af695ad1840 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 1 Apr 2019 01:27:14 +0800 Subject: [PATCH 33/64] api.md --- _docs/api.md | 911 ++++++++++----------------------------- _docs/img/console1.jpg | Bin 0 -> 50176 bytes _docs/img/elements.jpg | Bin 0 -> 174704 bytes _docs/img/sources.jpg | Bin 0 -> 118547 bytes _docs/personalization.md | 22 +- 5 files changed, 229 insertions(+), 704 deletions(-) create mode 100644 _docs/img/console1.jpg create mode 100644 _docs/img/elements.jpg create mode 100644 _docs/img/sources.jpg diff --git a/_docs/api.md b/_docs/api.md index f445d204..873aa9a2 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -16,13 +16,236 @@ ### Console:命令行 +Console页为命令行。可以在这里输入一些命令进行调试。 + +比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。 + +更多的API可参见[附录:API列表](#附录:API列表)。 + +除此以外,游戏中的报错等信息也是可以在Console中进行查看的。 + +![](img/console1.jpg) + ### Sources:断点调试 +Sources页可以查看JS源代码,并进行断点调试等。 + +例如,如果相对脚本编辑中的伤害计算函数进行断点调试: +1. 在左边找到`project/functions.js`,单击打开文件 +2. 并找到对应的行(可以Ctrl+F搜索),比如搜索`getDamageInfo` +3. 在行号上点一下打断点,会出现一个蓝色标签 + +之后,当代码运行到你的断点处时,将自动停止运行。 + +![](img/sources.jpg) + +可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。 + +图中红色框内有几个按钮,从左到右分别是:**继续执行**,**执行到下一行**,**进入当前函数**,**跳出当前函数**,**单步执行**。 + +通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。 + +红圈下方是Call Stack,即当前的函数调用链(从哪些地方调用过来的)。 + +Sources还有更多有趣的功能,在此不做介绍,有兴趣的可自行网上搜索了解。 + ### Elements:网页元素查看 +Elements页可以查看网页的源代码,调整css布局等。 + +![](img/elements.jpg) + +不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。 + +手机模式下,左边可以对屏幕分辨率进行调整和模拟。 + +这可以很有效的帮我们进行测试样板在手机端的表现。 + ## 整体项目架构 -## 函数的转发,复写函数 +``` bash +├── /_server/ # 为可视化地图编辑器提供一些支持的目录 +├── /libs/ # ---- 系统库目录 ---- +│ ├─ /thirdparty/ # 游戏所用到的第三方库文件 +│ ├─ actions.js # 用户交互处理 +│ ├─ core.js # 系统核心文件(游戏入口,接口&转发) +│ ├─ control.js # 游戏逻辑控制 +│ ├─ data.js # 全塔属性等 +│ ├─ enemys.js # 怪物相关处理 +│ ├─ events.js # 各个事件的执行 +│ ├─ icons.js # 图标和素材 +│ ├─ items.js # 道具效果 +│ ├─ loader.js # 各个资源加载 +│ ├─ maps.js # 地图数据和绘制 +│ ├─ ui.js # UI窗口绘制 +│ └─ utils.js # 工具类函数 +├── /project/ # ---- 项目目录 ---- +│ ├─ /animates/ # 动画目录 +│ ├─ /floors/ # 楼层文件 +│ ├─ /images/ # 图片素材 +│ ├─ /sounds/ # bgm和音效 +│ ├─ data.js # 全塔属性 +│ ├─ enemys.js # 怪物属性 +│ ├─ events.js # 公共事件 +│ ├─ functions.js # 脚本编辑 +│ ├─ icons.js # 素材和ID的对应关系定义 +│ ├─ items.js # 道具的定义和效果 +│ ├─ maps.js # 地图和数字的对应关系 +│ └─ plugins.js # 自定义插件 +├── /常用工具/ # 辅助造塔的小工具 +├── editor.html # 地图编辑器 +├── editor-mobile.html # 手机版的地图编辑器 +├── index.html # 主程序,游戏的入口 +├── main.js # JS程序的入口,将动态对所需JS进行加载 +├── style.css # 游戏所需要用到的样式表 +└── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 +``` + +`_server`为****,里面存放了地图编辑器相关的各项内容。 + +`libs`为**系统库目录**,里面存放了各个系统核心函数。 + +从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数) + +`project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 + +## 函数的转发 + +在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。 + +例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的: + +```js +////// 获得某个自定义变量或flag ////// +control.prototype.getFlag = function(name, defaultValue) { + if (!core.status.hero) return defaultValue; + var value = core.status.hero.flags[name]; + return value != null ? value : defaultValue; +} +``` + +也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。 + +但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`,而不需要中间的control。 + +为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数,转发到core中执行**。 + +上述`getFlag`代码的转发实际上是增加了如下函数: + +```js +////// getFlag函数的转发 ////// +core.getFlag = function (name, defaultValue) { + return core.control.getFlag(name, defaultValue); +} +// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag() +``` + +转发是自动完成的,其满足如下两条规则: +- **在libs中其他文件定义的函数,如果不以下划线`_`开头,就会进行转发。** +- **如果core中已经存在同名函数,则会在控制台中打出一条报错信息,并不转发该函数。** + +具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。 + +!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发! + +例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。 + +## 复写函数 + +样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 + +在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: +- 不容易随着新样板来接档迁移 +- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 +- …… + +好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 + +如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` + +下面是几个例子,从简单到复杂。 + +### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 + +```js +// 重写ui.js中的_drawBook_drawBackground函数 +core.ui._drawBook_drawBackground = function () { + // 调用core.drawBackground函数来绘制winskin + // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 + core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); +} +``` + +### 重写点击楼传事件 + +```js +// 重写点击楼传事件 +core.events.useFly = function (fromUserAction) { + if (core.isMoving()) { + core.drawTip("请先停止勇士行动"); + return; + } + if (core.status.lockControl || core.status.event.id != null) return; + + if (core.canUseItem('fly')) core.useItem('fly'); + else core.drawTip("当前无法使用"+core.material.items.fly.name); +} +``` + +### 楼层切换时根据flag来播放不同的音效 + +```js +// 重写events.js中的_changeFloor_beforeChange,修改音效值 +core.events._changeFloor_beforeChange = function (info, callback) { + // 直接替换原始函数中的 core.playSound('floor.mp3'); + if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); + if (core.getFlag("floorSound") == 1) core.playSound('floor1.mp3'); + if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); + // ... + + // 下面是原始函数中的代码 + window.setTimeout(function () { + if (info.time == 0) + core.events._changeFloor_changing(info, callback); + else + core.show(core.dom.floorMsgGroup, info.time / 2, function () { + core.events._changeFloor_changing(info, callback); + }); + }, 25) +} +``` + +### 每次打开全局商店时播放一个音效 + +打开全局商店是在`events.js`中的`openShop`函数,因此需要对其进行重写。 + +然而,我们只需要在这个函数执行之前插一句音效播放,所以并不需要重写整个函数,而是直接插入一行就行。 + +```js +var openShop = core.events.openShop; // 先把原始函数用一个变量记录下来 +core.events.openShop = function (shopId, needVisited) { + core.playSound("shop.mp3"); // 播放一个音效 + return openShop(shopId, needVisited); // 直接调用原始函数 +} +``` + +### 每次绘制地图前在控制台打出一条信息 + +绘制地图在`maps.js`的`drawMap`函数,因此需要对其进行重写。 + +由于只需要额外在函数执行前增加一句控制台输出,所以直接插入一行即可。 + +但是需要注意的是,`drawMap`中使用了`this._drawMap_drawAll()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。 + +```js +var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来 +core.maps.drawMap = function (floorId, callback) { + console.log("drawMap..."); // 控制台打出一条信息 + drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps +} +``` + +详见[`call`或`apply`的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 ## 附录:API列表 @@ -47,689 +270,3 @@ ### ui.js ### utils.js - -**这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** - -如有任何疑问,请联系小艾寻求帮助。 - -可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 - -**以下所有异步API都会加上[异步]的说明,存在此说明的请勿在事件处理的自定义脚本中使用。** - -!> 最常用的新手向命令,强烈建议每个人了解 - -``` text - -core.status.floorId -获得当前层的floorId。 - - -core.status.maps -获得所有楼层的地图信息。 - - -core.status.thisMap -获得当前楼层信息,其等价于core.status.maps[core.status.floorId]。 - - -core.floors -获得所有楼层的信息。例如core.floors[core.status.floorId].events可获得本楼层的所有自定义事件。 - - -core.status.hero -获得当前勇士状态信息。例如core.status.hero.atk就是当前勇士的攻击力数值。 - - -core.material.enemys -获得所有怪物信息。例如core.material.enemys.greenSlime就是获得绿色史莱姆的属性数据。 - - -core.material.items -获得所有道具的信息。 - - -core.debug() -开启调试模式。此模式下可以按Ctrl键进行穿墙,并忽略一切事件。 -此模式下不可回放录像和上传成绩。 - - -core.updateStatusBar() -立刻刷新状态栏和地图显伤。 - - -core.setStatus('atk', 1000) -将攻击力设置为1000;这里把atk可以改成hp, def, mdef, money, experience等等。 -本句等价于 core.status.hero.atk = 1000 - - -core.getStatus('atk') -返回当前攻击力数值。本句等价于 core.status.hero.atk。 - - -core.setHeroLoc('x', 5) -设置勇士位置。这句话的意思是将勇士当前位置的横坐标设置为5。 -同理可以设置勇士纵坐标 core.setHeroLoc('y', 3)。 -值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;如需立刻重新绘制地图还需调用: -core.clearMap('hero'); core.drawHero(); -来对界面进行更新。 - - -core.setItem('pickaxe', 10) -将破墙镐个数设置为10个。这里可以写任何道具的ID。 - - -core.addItem('pickaxe', 2) -将破墙镐的个数增加2个,无任何特效。这里可以写任何道具的ID。 - - -core.getItem('pickaxe', 4) -令勇士获得4个破墙镐。这里可以写任何道具的ID。 -和addItem相比,使用getItem会播放获得道具的音效,也会在左上角绘制获得提示。 - - -core.removeItem('pickaxe', 3) -删除3个破墙镐。第二项可忽略,默认值为1。 - - -core.itemCount('pickaxe') -返回当前破墙镐的个数。这里可以写任何道具的ID。 - - -core.hasItem('pickaxe') -返回当前是否存在某个道具。等价于 core.itemCount('pickaxe')>0 。 - - -core.getEquip(0) -获得0号装备类型(武器)的当前装备的itemId。如果不存在则返回null。 -这里可以写任意装备类型,从0开始和全塔属性中的equipName一一对应。 - - -core.hasEquip('sword1') -获得当前某个具体的装备是否处于正在被装备状态。 - - -core.setFlag('xyz', 2) -设置某个flag/变量的值为2。这里可以写任何的flag变量名。 - - -core.getFlag('xyz', 7) -获得某个flag/变量的值;如果该变量不存在,则返回第二个参数。 -比如 core.getFlag('point', 2) 则获得变量point的值;如果该变量从未定义过则返回2。 - - -core.hasFlag('xyz') -返回是否存在某个变量且不为0。等价于 core.getFlag('xyz', 0)!=0 。 - - -core.removeFlag('xyz') -删除某个flag/变量。 - - -core.insertAction(list, x, y, callback) -插入并执行一段自定义事件。在这里你可以写任意的自定义事件列表,有关详细写法请参见文档-事件。 -x和y如果设置则覆盖"当前事件点"的坐标,callback如果设置则覆盖事件执行完毕后的回调函数。 -例如: core.insertAction(["楼层切换", {"type":"changeFloor", "floorId": "MT3"}]) -将依次显示剧情文本,并执行一个楼层切换的自定义事件。 --------- -从V2.5.4开始提出了“公共事件”的说法,这里也可以插入一个公共事件名。 -例如:core.insertAction("毒衰咒处理") 将插入公共事件“毒衰咒处理”。 - - -core.changeFloor(floorId, stair, heroLoc, time, callback) [异步] -立刻切换到指定楼层。 -floorId为目标楼层ID,stair为到达的目标楼梯,heroLoc为到达的指定点,time为动画时间,callback为切换完毕后的回调。 -例如: -core.changeFloor('MT2', 'upFloor', null, 600) 切换到MT2层的上楼点,动画事件600ms -core.changeFloor('MT5', null, {'x': 3, 'y': 6}, 0) 无动画切换到MT5层的(3,6)位置。 - - -core.resetMap() -重置当前楼层地图和楼层属性。 -此函数参数有三种形式: - - 不加任何参数,表示重置当前层:core.resetMap() - - 加上一个floorId,表示重置某一层:core.resetMap("MT1") - - 使用一个数组,表示重置若干层:core.resetMap(["MT1", "MT2", "MT3"]) ---------------------------- -** 说明:从V2.5.5开始存档方式发生了改变,在编辑器修改了地图后现在将直接生效,无需再重置地图。 - -R -录像回放的快捷键;这不是一个控制台命令,但是也把它放在这里供使用。 -录像回放在修改地图或新增数据后会很有用。 - -``` - -!> 一些相对高级的命令,针对有一定脚本经验的人 - -``` text - -========== 可直接从core中调用的,最常被使用的函数 ========== -core.js实际上是所有API的入口(路由),核心API的实现在其他几个文件中,core.js主要进行转发操作。 - - -core.nextX(n) -获得勇士面向的第n个位置的x坐标,n可以省略默认为1(即正前方) - - -core.nextY(n) -获得勇士面向的第n个位置的y坐标,n可以省略默认为1(即正前方) - - -core.nearHero(x, y) -判断某个点是否和勇士的距离不超过1。 - - -core.openDoor(id, x, y, needKey, callback) [异步] -尝试开门操作。id为目标点的ID,x和y为坐标,needKey表示是否需要使用钥匙,callback为开门完毕后的回调函数。 -id可为null代表使用地图上的值。 -例如:core.openDoor('yellowDoor', 10, 3, false, function() {console.log("1")}) -此函数返回true代表成功开门,并将执行callback回调;返回false代表无法开门,且不会执行回调函数。 - - -core.battle(id, x, y, force, callback) [异步] -执行战斗事件。id为怪物的id,x和y为坐标,force为bool值表示是否是强制战斗,callback为战斗完毕后的回调函数。 -id可为null代表使用地图上的值。 -例如:core.battle('greenSlime', null, null, true) - - -core.trigger(x, y) [异步] -触发某个地点的事件。 - - -core.isReplaying() -当前是否正在录像播放中 - - -core.drawBlock(block) -重绘某个图块。block应为core.status.thisMap.blocks中的一项。 - - -core.drawMap(floorId, callback) -重绘某一层的地图。floorId为要绘制楼层的floorId,callback为绘制完毕后的回调函数。 - - -core.terrainExists(x, y, id, floorId) -检测某个点是否存在(指定的)地形。 -x和y为坐标;id为地形ID,可为null表示任意地形;floorId为楼层ID,可忽略表示当前楼层。 - - -core.enemyExists(x, y, id, floorId) -检测某个点是否存在(指定的)怪物。 -x和y为坐标;id为怪物ID,可为null表示任意怪物;floorId为楼层ID,可忽略表示当前楼层。 - - -core.getBlock(x, y, floorId, showDisable) -获得某个点的当前图块信息。 -x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 -showDisable如果为true,则对于禁用的点和事件也会进行返回。 -如果该点不存在图块,则返回null。 -否则,返回值如下: {"index": xxx, "block": xxx} -其中index为该点在该楼层blocks数组中的索引,block为该图块实际内容。 - - -core.getBlockId(x, y, floorId, showDisable) -获得某个点的图块ID。 -x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 -showDisable如果为true,则对于禁用的点和事件也会进行返回。 -如果该点不存在图块,则返回null,否则返回该点的图块ID。 - - -core.getBlockCls(x, y, floorId, showDisable) -获得某个点的图块cls。 -x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 -showDisable如果为true,则对于禁用的点和事件也会进行返回。 -如果该点不存在图块,则返回null,否则返回该点的图块cls。 - - -core.showBlock(x, y, floorId) -将某个点从禁用变成启用状态。 - - -core.hideBlock(x, y, floorId) -将某个点从启用变成禁用状态,但不会对其进行删除。 -此函数不会实际将该块从地图中进行删除,而是将该点设置为禁用,以供以后可能的启用事件。 - - -core.removeBlock(x, y, floorId) -将从启用变成禁用状态,并尽可能将其从地图上删除。 -和hideBlock相比,如果该点不存在自定义事件(比如门或普通的怪物),则将直接从地图中删除。 -如果存在自定义事件,则简单的禁用它,以供以后可能的启用事件。 - - -core.setBlock(number, x, y, floorId) -改变图块。number为要改变到的图块数字,x和y为坐标,floorId为楼层ID,可忽略表示当前楼层。 - - -core.useItem(itemId, noRoute, callback) -尝试使用某个道具。itemId为道具ID,noRoute如果为真则该道具的使用不计入录像。 -callback为成功或失败后的回调。 - - -core.canUseItem(itemId) -返回当前能否使用某个道具。 - - -core.loadEquip(itemId, callback) -装备上某个装备。itemId为装备的ID,callback为成功或失败后的回调。 - - -core.unloadEquip(equipType, callback) -卸下某个部位的装备。equipType为装备类型,从0开始;callback为成功或失败后的回调。 - - -core.getNextItem() -轻按。 - - -core.drawTip(text, itemIcon) -在左上角绘制一段提示信息,2秒后消失。itemIcon为道具图标的索引。 - - -core.drawText(contents, callback) [异步] -绘制一段文字。 -不推荐使用此函数,尽量使用core.insertAction(contents)来显示剧情文本。 - - -core.closePanel() -结束一切事件和绘制,关闭UI窗口,返回游戏进程。 - - -core.replaceText(text) -将一段文字中的${}进行计算并替换。 - - -core.calValue(value, prefix, need, times) -计算表达式的实际值。这个函数可以传入status:atk等这样的参数。 - - -core.getLocalStorage(key, defaultValue) -从localStorage中获得某个数据(已被parse);如果对应的key不存在则返回defaultValue。 - - -core.getLocalForage(key, defaultValue, successCallback, errorCallback) -从localForage中获得某个数据(已被parse),如果对应的key不存在则返回defaultValue。 -如果成功则通过successCallback回调,失败则通过errorCallback回调。 - - -core.hasSave(index) -判定当前某个存档位是否存在存档,返回true/false。 -index为存档编号,0代表自动存档,大于0则为正常的存档位。 - - -core.clone(data) -深拷贝某个对象。 - - -core.isset(x) -测试x是否不为null,不为undefined也不为NaN。 - - -core.rand(num) -使用伪种子生成伪随机数。该随机函数能被录像支持。 -num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到1之间的浮点数。 -此函数为伪随机算法,SL大法无效。(即多次SL后调用的该函数返回的值都是相同的。) - - -core.rand2(num) -使用系统的随机数算法得到的随机数。该随机函数能被录像支持。 -num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到2147483647之间的整数。 -此函数使用了系统的Math.random()函数,支持SL大法。 -但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 - - -core.restart() [异步] -返回标题界面。 - - -========== core.actions.XXX 和游戏控制相关的函数 ========== -actions.js主要用来进行用户交互行为的处理。 -所有用户行为,比如按键、点击、滑动等等,都会被此文件接收并进行操作。 - - -========== core.control.XXX 和游戏控制相关的函数 ========== -control.js主要用来进行游戏控制,比如行走控制、自动寻路、存读档等等游戏核心内容。 - -core.control.setGameCanvasTranslate(canvasId, x, y) -设置大地图的偏移量 - - -core.control.updateViewport() -更新大地图的可见区域 - - -core.control.gatherFollowers() -立刻聚集所有的跟随者 - - -core.control.replay() -回放下一个操作 - - -========== core.enemys.XXX 和怪物相关的函数 ========== -enemys.js主要用来进行怪物相关的内容,比如怪物的特殊属性,伤害和临界计算等。 - - -core.enemys.hasSpecial(special, test) -测试怪物是否含有某个特殊属性。 -常见用法: core.enemys.hasSpecial(monster.special, 3) ## 测试是否拥有坚固 - - -core.enemys.getSpecialText(enemyId) -返回一个列表,包含该怪物ID对应的所有特殊属性。 - - -core.enemys.getSpecialHint(enemy, special) -获得怪物某个(或全部)特殊属性的文字说明。 - - -core.enemys.canBattle(enemyId, x, y, floorId) -返回当前能否战胜某个怪物。 -后面三个参数是怪物坐标和楼层。 - - -core.enemys.getDamage(enemyId, x, y, floorId) -返回当前对某个怪物的战斗伤害。如果无法战斗,返回null。 -后面三个参数是怪物坐标和楼层。 - - -core.enemys.getExtraDamage(enemyId) -返回某个怪物会对勇士造成的额外伤害(不可被魔防抵消),例如仇恨、固伤等等。 - - -core.enemys.nextCriticals(enemyId, number, x, y, floorId) -返回一个列表,为接下来number(可忽略,默认为1)个该怪物的临界值和临界减伤。 -列表每一项类似 [x,y] 表示临界值为x,且临界减伤为y。 -如果无临界值,则返回空列表。 - - -core.enemys.getDefDamage(enemyId, k, x, y, floorId) -获得k(可忽略,默认为1)防减伤值。 - - -core.enemys.getDamageInfo(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) -获得实际战斗信息,比如伤害,回合数,每回合伤害等等。 -此函数是实际战斗过程的计算。 - - -core.enemys.calDamage(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) -获得在某个勇士属性下怪物伤害;实际返回的是上面getDamageInfo中伤害的数值。 - - -core.enemys.getCurrentEnemys(floorId) -获得某一层楼剩余所有怪物的信息(供怪物手册使用) - - -========== core.events.XXX 和事件相关的函数 ========== -events.js主要用来进行事件处理,比如自定义事件,以及某些条件下可能会被触发的事件。 -大多数事件API都在脚本编辑中存在,这里只列出部分比较重要的脚本编辑中不存在的API。 - - -core.events.gameOver(ending, fromReplay) -游戏结束并上传的事件。 -该函数将提问是否上传和是否下载录像,并返回标题界面。 - - -core.events.doEvents(list, x, y, callback) [异步] -开始执行某个事件。 -请不要执行此函数,尽量使用 core.insertAction(list, x, y, callback) 来开始执行一段事件。 - - -core.events.doAction() -执行下一个事件。此函数中将对所有自定义事件类型分别处理。 - - -core.events.getCommonEvent(name) -根据名称获得一个公共事件;如果不存在对应的公共事件则返回null。 - - -core.events.openShop(shopId, needVisited) [异步] -打开一个全局商店。needVisited表示是否需要该商店已被打开过。 - - -core.events.disableQuickShop(shopId) -禁用一个全局商店 - - -core.events.canUseQuickShop(shopId) -当前能否使用某个快捷商店 - - -core.events.setHeroIcon(name) -设置勇士行走图 - - -========== core.items.XXX 和道具相关的函数 ========== -items.js将处理和道具相关的内容,比如道具的使用,获取和删除等等。 - - -core.items.compareEquipment(equipId1, equipId2) -比较两个装备的属性变化值 - - -========== core.loader.XXX 和游戏加载相关的函数 ========== -loader.js将主要用来进行资源的加载,比如加载音乐、图片、动画等等。 - - -========== core.maps.XXX 和地图处理相关的函数 ========== -maps.js主要用来进行地图相关的的操作。包括绘制地图,获取地图上的点等等。 - - -core.maps.getNumberById(id) -根据ID来获得对应的数字。如果该ID不存在对应的数字则返回0。 - - -core.maps.canMoveHero(x,y,direction,floorId) -判断能否前往某个方向。x,y为坐标,可忽略为当前点;direction为方向,可忽略为当前方向。 -floorId为楼层ID,可忽略为当前楼层。 - - -core.maps.canMoveDirectly(destX, destY) -判断当前能否瞬间移动到某个点。 -该函数如果返回0则不可瞬间移动,大于0则可以瞬间移动,且返回值是跨度(即少走的步数)。 - - -core.maps.removeBlockById(index, floorId) -根据索引删除或禁用某块。 - - -core.maps.removeBlockByIds(floorId, ids) -根据索引删除或禁用若干块。 - - -core.maps.drawAnimate(name, x, y, callback) -播放一段动画,name为动画名(需在全塔属性注册),x和y为坐标(0-12之间),callback可选,为播放完毕的回调函数。 -播放过程是异步的,如需等待播放完毕请使用insertAction插入一条type:waitAsync事件。 -此函数将随机返回一个数字id,为此异步动画的唯一标识符。 - - -core.maps.stopAnimate(id, doCallback) -立刻停止一个异步动画。 -id为该动画的唯一标识符(由drawAnimate函数返回),doCallback可选,若为true则会执行该动画所绑定的回调函数。 - - -========== core.ui.XXX 和对话框绘制相关的函数 ========== -ui.js主要用来进行UI窗口的绘制,比如对话框、怪物手册、楼传器、存读档界面等等。 - - -core.ui.getContextByName(canvas) -根据画布名找到一个画布的context;支持系统画布和自定义画布。如果不存在画布返回null。 -也可以传画布的context自身,则返回自己。 - - -core.clearMap(name) -清空某个画布图层。 -name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。(下同) -如果name也可以是'all',若为all则为清空所有系统画布。 - - -core.ui.fillText(name, text, x, y, style, font) -在某个画布上绘制一段文字。 -text为要绘制的文本,x,y为要绘制的坐标,style可选为绘制的样式,font可选为绘制的字体。(下同) - - -core.ui.fillBoldText(name, text, x, y, style, font) -在某个画布上绘制一个描黑边的文字。 - - -core.ui.fillRect(name, x, y, width, height, style) -绘制一个矩形。style可选为绘制样式。 - - -core.ui.strokeRect(name, x, y, width, height, style) -绘制一个矩形的边框。 - - -core.ui.drawLine(name, x1, y1, x2, y2, style, lineWidth) -绘制一条线。lineWidth可选为线宽。 - - -core.ui.drawArrow(name, x1, y1, x2, y2, style, lineWidth) -绘制一个箭头。 - - -core.ui.setFont(name, font) / core.ui.setLineWidth(name, lineWidth) -设置一个画布的字体/线宽。 - - -core.ui.setAlpha(name, font) / core.ui.setOpacity(name, font) -设置一个画布的绘制不透明度和画布本身的不透明度。 -两者区别如下: - - setAlpha是设置"接下来绘制的内容的不透明度",不会对已经绘制的内容产生影响。比如setAlpha('ui', 0.5)则会在接下来的绘制中使用0.5的不透明度。 - - setOpacity是设置"画布本身的不透明度",已经绘制的内容也会产生影响。比如我已经在UI层绘制了一段文字,再setOpacity则也会看起来变得透明。 -尽量不要对系统画布使用setOpacity(因为会对已经绘制的内容产生影响),自定义创建的画布则不受此限制。 - - -core.ui.setFillStyle(name, style) / core.ui.setStrokeStyle(name, style) -设置一个画布的填充样式/描边样式。 - - -core.ui.setTextAlign(name, align) -设置一个画布的文字对齐模式。 - - -core.ui.calWidth(name, text, font) -计算一段文字在画布上的绘制宽度 -font可选,如果存在则会先设置该画布上的字体。 - - -core.ui.drawImage(name, image, x, y, w, h, x1, y1, w1, h1) -在一个画布上绘制图片。 -name为画布名,可以是系统画布之一,也可以是任意自定义动态创建的画布名;还可以直接传画布的context本身。 -image为要绘制的图片,可以是一个全塔属性中定义的图片名(会从images中去获取),图片本身,或者一个画布。 -后面的8个坐标参数与canvas的drawImage的八个参数完全相同。 -请查看 http://www.w3school.com.cn/html5/canvas_drawimage.asp 了解更多。 - - -core.ui.createCanvas(name, x, y, width, height, zIndex) -动态创建一个画布。name为要创建的画布名,如果已存在则会直接取用当前存在的。 -x,y为创建的画布相对窗口左上角的像素坐标,width,height为创建的长宽。 -zIndex为创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。 -返回创建的画布的context,也可以通过core.dymCanvas[name]调用。 - - -core.ui.relocateCanvas(name, x, y) -重新定位一个自定义画布。 - - -core.ui.resizeCanvas(name, x, y) -重新设置一个自定义画布的大小。 - - -core.ui.deleteCanvas(name) -删除一个自定义画布。 - - -core.ui.deleteAllCanvas() -清空所有的自定义画布。 - - -core.ui.drawThumbnail(floorId, canvas, blocks, x, y, size, heroLoc, heroIcon) -绘制一个缩略图,比如楼传器界面,存读档界面等情况。 -floorId为目标楼层ID,canvas为要绘制到的图层,blocks为要绘制的所有图块。 -x,y为该图层开始绘制的起始点坐标,size为每一格的像素,heroLoc为勇士坐标,heroIcon为勇士图标。 - - -========== core.utils.XXX 工具类的辅助函数 ========== -utils.js主要用来进行一些辅助函数的计算。 - - -core.utils.splitLines(canvas, text, maxLength, font) -自动切分长文本的换行。 -canvas为图层,text为要自动换行的内容,maxLength为每行最长像素,font为文本的字体。 - - -core.utils.cropImage(image, size) -纵向对图片进行切分(裁剪)。 - - -core.utils.push(a,b) -向某个数组后插入另一个数组或元素 - - -core.utils.unshift(a, b) -向某个数组前插入另一个数组或元素 - - -core.utils.encodeBase64(str) -Base64加密字符串 - - -core.utils.decodeBase64(str) -Base64解密字符串 - - -core.utils.formatBigNumber(x, onMap) -大数据的格式化 - - -core.utils.subarray(a, b) -检查b是否是a的从头开始子串。 -如果是,则返回a删去b的一段;否则返回null。 - - -core.utils.same(a, b) -比较a和b两个对象是否相同 - - -core.utils.clamp(x, a, b) -将x限制在[a,b]之间的范围内 - - -core.utils.arrayToRGB(color) -将形如[255,0,0]之类的数组转成#FF0000这样的RGB形式。 - - -core.utils.arrayToRGBA(color) -将形如[255,0,0,1]之类的数组转成rgba(255,0,0,1)这样的RGBA形式。 - - -core.utils.encodeRoute(list) -压缩加密路线。可以使用core.encodeRoute(core.status.route)来压缩当前路线。 - - -core.utils.decodeRoute(route) -解压缩(解密)路线。 - - -core.utils.readFile(success, error, readType) [异步] -尝试请求读取一个本地文件内容。 -success和error为成功/失败后的回调,readType不设置则以文本读取,否则以DataUrl形式读取。 - - -core.utils.readFileContent(content) [异步] -文件读取完毕后的内容处理。 - - -core.utils.download(filename, content) -尝试生成并下载一个文件。 - - -core.utils.copy(data) -尝试复制一段文本到剪切板。 - - -core.utils.http(type, url, formData, success, error) [异步] -发送一个异步HTTP请求。 -type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 -success为成功后的回调,error为失败后的回调。 - -``` diff --git a/_docs/img/console1.jpg b/_docs/img/console1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46d1f5ba472e127f05f7489b8c607bd253de65fc GIT binary patch literal 50176 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 literal 0 HcmV?d00001 diff --git a/_docs/img/elements.jpg b/_docs/img/elements.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d32ebaf22bc0b4948c9bdd5ad965296112f15815 GIT binary patch literal 174704 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 literal 0 HcmV?d00001 diff --git a/_docs/img/sources.jpg b/_docs/img/sources.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7fce53b34473b8cd044c966c5beb19b4ecfe751d GIT binary patch literal 118547 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 literal 0 HcmV?d00001 diff --git a/_docs/personalization.md b/_docs/personalization.md index 246ffc33..6edfb2a6 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -337,28 +337,16 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { 对于特殊的塔,我们可以考虑修改楼传事件来完成一些特殊的要求,比如镜子可以按楼传来切换表里。 -要修改楼传事件,需要进行如下几步: +要修改楼传事件,需要进行如下两步: -1. 截获楼传的点击事件。在control.js中找到useFly函数,并将其替换成如下内容: -``` js -////// 点击楼层传送器时的打开操作 ////// -control.prototype.useFly = function (need) { - if (core.isMoving()) { - core.drawTip("请先停止勇士行动"); - return; - } - if (core.status.lockControl || core.status.event.id != null) return; - - if (core.canUseItem('fly')) core.useItem('fly'); - else core.drawTip("当前无法使用"+core.material.items.fly.name); -} -``` +1. 重写楼传的点击事件。在插件中对`core.control.useFly进行重写`。详细代码参见[重写点击楼传事件](api#重写点击楼传事件)。 2. 修改楼传的使用事件。和其他永久道具一样,在地图编辑器的图块属性中修改楼传的useItemEffect和canUseItemEffect两个内容。例如: ``` js "useItemEffect": "core.insertAction([...])" // 执行某段自定义事件,或者其他脚本 "canUseItemEffect": "true" // 任何时候可用 ``` -修改时,请先把`null`改成空字符串`""`,然后再双击进行编辑。 + +除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyboard`)即可。 ## 自定义怪物属性 @@ -441,7 +429,7 @@ this.myfunc = function(x) { 从V2.6开始,在插件中用`this.xxx`定义的函数将会被转发到core中。例如上述的`myfunc`除了`core.plugin.myfunc`外也可以直接`core.myfunc`调用。 -详见[函数转发](api#函数转发)。 +详见[函数的转发](api#函数的转发)。 ## 标题界面事件化 From e6e604ee7cd7e9ebb6803fe4b880325c489ff434 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Mon, 1 Apr 2019 20:37:08 +0800 Subject: [PATCH 34/64] statusBar Bottom, jump time, setAutomaticRoute --- libs/control.js | 20 ++++++++++++++------ libs/maps.js | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libs/control.js b/libs/control.js index 1aa32609..a77e7cf1 100644 --- a/libs/control.js +++ b/libs/control.js @@ -472,8 +472,14 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix)) return; if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix)) return; // 找寻自动寻路路线 - var moveStep = core.automaticRoute(destX, destY).concat(stepPostfix); - if (moveStep.length == 0) return core.deleteCanvas('route'); + var moveStep = core.automaticRoute(destX, destY); + if (moveStep.length == 0) { + if (destX != core.status.hero.loc.x || destY != core.status.hero.loc.y) + return; + moveStep = []; + } + var moveStep = moveStep.concat(stepPostfix); + if (moveStep.length == 0) return; core.status.automaticRoute.destX=destX; core.status.automaticRoute.destY=destY; this._setAutomaticRoute_drawRoute(moveStep); @@ -2353,10 +2359,11 @@ control.prototype.updateGlobalAttribute = function (name) { core.dom.statusBar.style.borderTop = border; core.dom.statusBar.style.borderLeft = border; core.dom.statusBar.style.borderRight = core.domStyle.isVertical?border:''; + core.dom.statusBar.style.borderBottom = core.domStyle.isVertical?'':border; core.dom.gameDraw.style.border = border; - core.dom.toolBar.style.borderBottom = border; core.dom.toolBar.style.borderLeft = border; core.dom.toolBar.style.borderRight = core.domStyle.isVertical?border:''; + core.dom.toolBar.style.borderBottom = core.domStyle.isVertical?border:''; break; } case 'statusBarColor': @@ -2588,7 +2595,7 @@ control.prototype._resize_statusBar = function (obj) { } else { statusBar.style.width = obj.BAR_WIDTH * core.domStyle.scale + "px"; - statusBar.style.height = obj.outerSize - 3 + "px"; + statusBar.style.height = obj.outerSize + "px"; statusBar.style.background = obj.globalAttribute.statusLeftBackground; // --- 计算文字大小 statusBar.style.fontSize = 16 * Math.min(1, (core.__HALF_SIZE__ + 3) / obj.count) * core.domStyle.scale + "px"; @@ -2596,6 +2603,7 @@ control.prototype._resize_statusBar = function (obj) { statusBar.style.display = 'block'; statusBar.style.borderTop = statusBar.style.borderLeft = obj.border; statusBar.style.borderRight = core.domStyle.isVertical ? obj.border : ''; + statusBar.style.borderBottom = core.domStyle.isVertical ? '' : obj.border; // 自绘状态栏 if (core.domStyle.isVertical) { core.dom.statusCanvas.style.width = obj.outerSize - 6 + "px"; @@ -2651,8 +2659,8 @@ control.prototype._resize_toolBar = function (obj) { toolBar.style.background = 'transparent'; } toolBar.style.display = 'block'; - toolBar.style.borderLeft = toolBar.style.borderBottom = obj.border; - toolBar.style.borderRight = core.domStyle.isVertical ? obj.border : ''; + toolBar.style.borderLeft = obj.border; + toolBar.style.borderRight = toolBar.style.borderBottom = core.domStyle.isVertical ? obj.border : ''; toolBar.style.fontSize = 16 * core.domStyle.scale + "px"; } diff --git a/libs/maps.js b/libs/maps.js index 04287c39..b2b5dbb6 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1660,7 +1660,7 @@ maps.prototype.__generateJumpInfo = function (sx, sy, ex, ey, time) { return { x: sx, y: sy, ex: ex, ey: ey, px: 32 * sx, py: 32 * sy, opacity: 1, jump_peak: jump_peak, jump_count: jump_count, - step: 0, per_time: time / 16 / core.status.replay.speed + step: 0, per_time: time / jump_count }; } From 075899df872d43c6846f054bf3742490e843628a Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Mon, 1 Apr 2019 20:43:11 +0800 Subject: [PATCH 35/64] norank --- libs/control.js | 10 +++------- libs/events.js | 4 ++-- libs/maps.js | 8 +------- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/libs/control.js b/libs/control.js index a77e7cf1..c72fabff 100644 --- a/libs/control.js +++ b/libs/control.js @@ -473,13 +473,9 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix)) return; // 找寻自动寻路路线 var moveStep = core.automaticRoute(destX, destY); - if (moveStep.length == 0) { - if (destX != core.status.hero.loc.x || destY != core.status.hero.loc.y) - return; - moveStep = []; - } - var moveStep = moveStep.concat(stepPostfix); - if (moveStep.length == 0) return; + if (moveStep.length == 0 && (destX != core.status.hero.loc.x || destY != core.status.hero.loc.y || stepPostfix.length == 0)) + return; + moveStep = moveStep.concat(stepPostfix); core.status.automaticRoute.destX=destX; core.status.automaticRoute.destY=destY; this._setAutomaticRoute_drawRoute(moveStep); diff --git a/libs/events.js b/libs/events.js index bfabdb3c..b8f10226 100644 --- a/libs/events.js +++ b/libs/events.js @@ -183,7 +183,7 @@ events.prototype._gameOver_doUpload = function (username, ending, norank) { formData.append('money', core.status.hero.money); formData.append('experience', core.status.hero.experience); formData.append('steps', core.status.hero.steps); - formData.append('norank', norank || 0); + formData.append('norank', norank ? 1 : 0); formData.append('seed', core.getFlag('__seed__')); formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime / 1000)); formData.append('route', core.encodeRoute(core.status.route)); @@ -2041,7 +2041,7 @@ events.prototype.jumpHero = function (ex, ey, time, callback) { var sx=core.status.hero.loc.x, sy=core.status.hero.loc.y; if (!core.isset(ex)) ex=sx; if (!core.isset(ey)) ey=sy; - core.maps.__playJumpSound(); + core.playSound('jump.mp3'); var jumpInfo = core.maps.__generateJumpInfo(sx, sy, ex, ey, time || 500); jumpInfo.icon = core.material.icons.hero[core.getHeroLoc('direction')]; jumpInfo.height = core.material.icons.hero.height; diff --git a/libs/maps.js b/libs/maps.js index b2b5dbb6..4a974ced 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1645,9 +1645,7 @@ maps.prototype.jumpBlock = function (sx, sy, ex, ey, time, keep, callback) { var block = blockArr[0], blockInfo = blockArr[1]; var canvases = this._initDetachedBlock(blockInfo, sx, sy, block.event.animate !== false); this._moveDetachedBlock(blockInfo, 32 * sx, 32 * sy, 1, canvases); - - this.__playJumpSound(); - + core.playSound('jump.mp3'); var jumpInfo = this.__generateJumpInfo(sx, sy, ex, ey, time); jumpInfo.keep = keep; @@ -1664,10 +1662,6 @@ maps.prototype.__generateJumpInfo = function (sx, sy, ex, ey, time) { }; } -maps.prototype.__playJumpSound = function () { - core.playSound('jump.mp3'); -} - maps.prototype._jumpBlock_doJump = function (blockInfo, canvases, jumpInfo, callback) { var animate = window.setInterval(function () { if (jumpInfo.jump_count > 0) From 9ecbd2aa42346faca04e4d0cc64e5293220ac2ab Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 2 Apr 2019 02:38:37 +0800 Subject: [PATCH 36/64] api for actions & control --- _docs/api.md | 714 +++++++++++++++++++++++++++++++++++++- _docs/event.md | 12 +- _docs/personalization.md | 2 +- _server/MotaAction.g4 | 29 +- _server/editor_blockly.js | 4 +- libs/actions.js | 22 +- libs/control.js | 43 ++- libs/core.js | 5 +- libs/events.js | 8 +- main.js | 8 +- project/floors/sample1.js | 4 +- 11 files changed, 777 insertions(+), 74 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index 873aa9a2..51e2b384 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -63,7 +63,7 @@ Elements页可以查看网页的源代码,调整css布局等。 ## 整体项目架构 -``` bash +``` text ├── /_server/ # 为可视化地图编辑器提供一些支持的目录 ├── /libs/ # ---- 系统库目录 ---- │ ├─ /thirdparty/ # 游戏所用到的第三方库文件 @@ -101,11 +101,11 @@ Elements页可以查看网页的源代码,调整css布局等。 └── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 ``` -`_server`为****,里面存放了地图编辑器相关的各项内容。 +`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 `libs`为**系统库目录**,里面存放了各个系统核心函数。 -从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数) +从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 `project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 @@ -167,10 +167,11 @@ core.getFlag = function (name, defaultValue) { ### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 +直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 + ```js // 重写ui.js中的_drawBook_drawBackground函数 core.ui._drawBook_drawBackground = function () { - // 调用core.drawBackground函数来绘制winskin // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); } @@ -178,8 +179,10 @@ core.ui._drawBook_drawBackground = function () { ### 重写点击楼传事件 +重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 + ```js -// 重写点击楼传事件 +// 重写events.js的useFly函数,即点击楼传按钮时的事件 core.events.useFly = function (fromUserAction) { if (core.isMoving()) { core.drawTip("请先停止勇士行动"); @@ -192,10 +195,14 @@ core.events.useFly = function (fromUserAction) { } ``` +其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 + ### 楼层切换时根据flag来播放不同的音效 +整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。 + ```js -// 重写events.js中的_changeFloor_beforeChange,修改音效值 +// 复制重写events.js中的_changeFloor_beforeChange,修改音效 core.events._changeFloor_beforeChange = function (info, callback) { // 直接替换原始函数中的 core.playSound('floor.mp3'); if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); @@ -203,7 +210,7 @@ core.events._changeFloor_beforeChange = function (info, callback) { if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); // ... - // 下面是原始函数中的代码 + // 下面是原始函数中的剩余代码,保持不变 window.setTimeout(function () { if (info.time == 0) core.events._changeFloor_changing(info, callback); @@ -245,16 +252,707 @@ core.maps.drawMap = function (floorId, callback) { } ``` -详见[`call`或`apply`的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 +详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 ## 附录:API列表 +这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 + +本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 + +如有任何疑问,请联系小艾寻求帮助。 + ### core.js +core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 + +但是,core中定义了很多游戏运行时的状态,这些状态很多都会被使用到。 + +``` text +core.__SIZE__, core.__PIXELS__ +游戏窗口大小;对于13x13的游戏而言这两个值分别是13和416,15x15来说分别是15和480。 + + +core.material +游戏中的所有资源列表,具体分为如下内容: +core.material.animates (动画) +core.material.bgms (背景音乐) +core.material.enemys (怪物信息,来自于 project/enemys.js) +core.material.icons (图标信息,来自于 project/icons.js) +core.material.images (图片素材,存放了各项素材图片如items.png等) + core.material.images.autotile (所有的自动元件图片) + core.material.images.tilesets (所有的额外素材图片) + core.material.images.images (用户引入的其他图片) +core.material.items (道具信息) +core.material.sounds (音效) + + +core.animateFrame +主要是记录和requestAnimationFrame相关的一些数据,常用的如下: +core.animateFrame.totalTime (游戏总的运行时时间) +core.animateFrame.weather (当前的天气信息) +core.animateFrame.asyncId (当前的异步处理事件的内容) + + +core.musicStatus +主要是记录和音效相关的内容,常用的如下: +core.musicStatus.bgmStatus (音乐开启状态) +core.musicStatus.soundStatus (音效开启状态) +core.musicStatus.playingBgm (当前正在播放的BGM) +core.musicStatus.lastBgm (最近一次尝试播放的BGM) +core.musicStatus.volume (当前的音量) +core.musicStatus.cachedBgms (背景音乐的缓存内从) +core.musicStatus.cacheBgmCount (背景音乐的缓存数量,默认值是4) + + +core.platform +游戏平台相关信息,常见的几个如下: +core.platform.isPC (是否是电脑端) +core.platform.isAndroid (是否是安卓端) +core.platform.isIOS (是否是iOS端) +core.platform.useLocalForage (是否开启了新版存档) +core.platform.extendKeyBoard (是否开启了拓展键盘) + + +core.domStyle +游戏的界面信息,包含如下几个: +core.domStyle.scale (当前的放缩比) +core.domStyle.isVertical (当前是否是竖屏状态) +core.domStyle.showStatusBar (当前是否显示状态栏) +core.domStyle.toolbarBtn (当前是否显示工具栏) + + +core.bigmap +当前的地图的尺寸信息,主要包含如下几个 +core.bigmap.width (当前地图的宽度) +core.bigmap.height (当前地图的高度) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素x) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素y) +core.bigmap.tempCanvas (一个临时画布,可以用来临时绘制很多东西) + + +core.saves +和存档相关的信息,包含如下几个: +core.saves.saveIndex (上次保存或读取的存档编号) +core.saves.ids (当前存在存档的编号列表) +core.saves.autosave (自动存档的信息) +core.saves.favorite (收藏的存档) +core.saves.favoriteNames (自定义存档的名称) + + +core.status +游戏的状态相关,是整个游戏中最重要的东西,其核心是如下几条: +请注意,每次重新开始、存档或读档时,core.status都会重新初始化。 +core.status.played (当前是否在游戏中) +core.status.gameOver (当前是否已经游戏结束,即win或lose) +core.status.hero (勇士信息;此项和全塔属性中的hero大体是对应的) + core.status.hero.name 勇士名 + core.status.hero.lv 当前等级 + core.status.hero.hpmax 当前生命上限 + core.status.hero.hp 当前生命值 + core.status.hero.manamax 当前魔力上限 + core.status.hero.mana 当前魔力值 + core.status.hero.atk 当前攻击力 + core.status.hero.def 当前防御力 + core.status.hero.mdef 当前魔防值 + core.status.hero.money 当前金币值 + core.status.hero.experience 当前经验值 + core.status.hero.loc 当前的位置信息 + core.status.hero.equipment 当前装上的装备 + core.status.hero.items 当前拥有的道具信息 + core.status.hero.flags 当前的各项flag信息 + core.status.hero.step 当前的步数值 + core.status.hero.statistics 当前的统计信息 +core.status.floorId (当前所在的楼层) +core.status.maps (所有的地图信息) +core.status.thisMap (当前的地图信息,等价于core.status.maps[core.status.floorId]) +core.status.bgmaps (所有背景层的信息) +core.status.fgmaps (所有的前景层的信息) +core.status.checkBlock (地图上的阻激夹域信息,也作为光环的缓存) +core.status.lockControl (当前是否是控制锁定状态) +core.status.automaticRoute (当前的自动寻路信息) +core.status.route (当前记录的录像) +core.status.replay (录像回放时要用到的信息) +core.status.shops (所有全局商店信息) +core.status.textAttribute (当前的文字属性,如颜色、背景等信息,和setText事件对应) +core.status.globalAttribute (当前的全局属性,如边框色、装备栏等) +core.status.curtainColor (当前色调层的颜色) +core.status.globalAnimateObjs (当前的全局帧动画效果) +core.status.floorAnimateObjs (当前的楼层贴图帧动画效果) +core.status.boxAnimateObjs (当前的盒子帧动画效果,例如怪物手册中的怪物) +core.status.autotileAnimateObjs (当前楼层的自动元件动画效果) +core.status.globalAnimateStatus (当前的帧动画的状态) +core.status.animateObjs (当前的播放动画信息) + + +core.floorIds +一个数组,表示所有的楼层ID,和全塔属性中的floorIds一致。 + + +core.floors +从楼层文件中读取全部的地图数据。 +和core.status.maps不同的是,后者在每次重新开始和读档时都会重置,也允许被修改(会存入存档)。 +而core.floors全程唯一,不允许被修改。 + + +core.statusBar +状态栏信息,例如状态栏图片,图标,以及各个内容的DOM定义等。 +core.statusBar.images (所有的系统图标,和icons.png对应) +core.statusBar.icons (状态栏中绘制的图标内容) + + +core.values +所有的全局数值信息,和全塔属性中的values一致。 +此项允许被直接修改,会存入存档。 + + +core.flags +所有的全塔开关,和全塔属性中的flags一致。 +此项不允许被直接修改,如有需要请使用“设置系统开关”事件,或者调用core.setGlobalFlag这个API。 + + +core.plugin +定义的插件函数。 + + +core.doFunc(func, _this) +执行一个函数,func为函数体或者插件中的函数名,_this为使用的this。 +如果func为一个字符串,则视为插件中的函数名,同时_this将被设置成core.plugin。 +此函数剩余参数将作为参数被传入func。 +``` + ### actions.js +actions.js主要是处理一些和用户交互相关的内容。 + +``` js +core.registerAction(action, name, func, priority) +注册一个用户交互行为。 +action:要注册的交互类型,如 ondown, onclick, keyDown 等等。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +priority:优先级;优先级高的被注册项将会被执行。此项可不填,默认为0。 +返回:如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 + + +core.unregisterAction(action, name) +注销一个用户交互行为。 + + +core.doRegisteredAction(action) +执行一个用户交互行为。 +此函数将在该交互行为所注册的所有函数中,按照优先级从高到底依次执行。 +此函数剩余的参数将会作为参数传入该执行函数中。 +当某个执行函数返回true时将终止这一过程。 + + +core.onkeyDown(e) +当按下某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.onkeyUp(e) +当放开某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.pressKey(keyCode) +当按住某个键不动时的操作,目前只对方向键有效。 +如果需要添加对于其他键的长按,请复写_sys_onkeyDown和_sys_onkeyUp。 +请勿直接覆盖或调用此函数,如有需要请注册一个"pressKey"的交互函数。 + + +core.keyDown(keyCode) +当按下某个键时的操作,参数为该键的keyCode值。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDown"的交互函数。 + + +core.keyUp(keyCode, altKey, fromReplay) +当按下某个键时的操作,参数为该键的keyCode值。 +altKey标志了Alt键是否同时被按下,fromReplay表示是否是从录像回放中调用的。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyUp"的交互函数。 + + +core.ondown(loc) +当点击屏幕时的操作。loc为点击的信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"ondown"的交互函数。 +注册的ondown交互函数需要接受x, y, px, py四个参数,代表点击的位置和像素坐标。 + + +core.onmove(loc) +当在屏幕上滑动时的操作。loc为当前的坐标信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmove"的交互函数。 +注册的onmove交互函数需要接受x, y, px, py四个参数,代表当前的的位置和像素坐标。 + + +core.onup() +当从屏幕上离开时的操作。请注意此函数是没有参数的。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onup"的交互函数。 + + +core.onclick(x, y) +当点击屏幕上的某点位置时执行的操作,请注意这里的x和y是位置坐标。 +一般而言,一个完整的ondown到onup将触发一个onclick事件。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onclick"的交互函数。 + + +core.onmousewheel(direct) +当滚动鼠标滑轮时执行的操作。direct为滑轮方向,上为1,下为-1。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmousewheel"的交互函数。 + + +core.keyDownCtrl() +当长按Ctrl键不动时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 + + +core.longClick() +当长按住屏幕时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 +注册的交互函数如果某一项返回true,则之后仍然会继续触发该长按, +如果全部返回false则将停止本次长按行为,直到手指离开屏幕并重新进行长按为止。 +``` + ### control.js +control.js将负责整个游戏的核心控制系统,分为如下几个部分: +- requestAnimationFrame相关 +- 标题界面,开始和重新开始游戏 +- 自动寻路和人物行走相关 +- 画布、位置、阻激夹域、显伤等相关 +- 录像的回放相关 +- 存读档,自动存档,同步存档等相关 +- 人物属性和状态、位置、变量等相关 +- 天气、色调、音乐和音效的播放 +- 状态栏和工具栏相关 +- 界面resize相关 + +```text +// ------ requestAnimationFrame 相关 ------ // + +core.registerAnimationFrame(name, needPlaying, func) +注册一个animationFrame。它将在每次浏览器的帧刷新时(约16.6ms)被执行。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +needPlaying:如果此项为true,则仅在游戏开始后才会被执行(标题界面不执行) +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +func可以接受一个timestamp作为参数,表示从整个页面加载完毕到当前时刻所经过的毫秒数。 +如果func执行报错,将在控制台打出一条信息,并自动进行注销。 + + +core.unregisterAnimationFrame(name) +注销一个animationFrame,参数是你的上面的自定义名称。 + +// ------ 开始界面相关 ------ // + +core.showStartAnimate(noAnimate, callback) +重置所有内容并显示游戏标题界面。 +noAnimate如果为true则不会有淡入动画,callback为执行完毕的回调。 + + +core.hideStartAnimate(callback) +淡出隐藏游戏标题界面,callback为执行完毕的回调。 + + +core.isPlaying() +当前是否正在游戏中。 + + +core.restart() +重新开始游戏。本质上就是播放标题界面的BGM并调用showStartAnimate。 + + +core.confirmRestart() +确认用户是否需要重新开始。 + + +core.clearStatus() +清除所有的游戏状态和数据,包括状态栏的显示。 + +// ------ 自动寻路、人物行走 ------ // + +core.stopAutomaticRoute() +停止自动寻路的操作 + + +core.saveAndStopAutomaticRoute() +保存剩下的寻路路线并停止自动寻路操作。主要用于打怪开门后继续寻路使用。 + + +core.continueAutomaticRoute() +继续剩下的自动寻路操作。主要用于打怪开门后继续寻路使用。 + + +core.clearContinueAutomaticRoute() +清空剩下的自动寻路操作。 + + +core.setAutomaticRoute(destX, destY, stepPostfix) +尝试开始进行一个自动寻路。stepPostfix是鼠标拖动的路径。 +此函数将检测是否在寻路中(在则停止或双击瞬移),检测是否点击自己(转身或轻按), +检测是否能单击瞬移,最后找寻自动寻路路线并开始寻路。 + + +core.setAutoHeroMove(steps) +设置勇士的自动行走路线,并立刻开始行走。 + + +core.setHeroMoveInterval(callback) +设置勇士行走动画。callback是每一步行走完毕后的回调。 + + +core.moveOneStep(x, y) +每走完一步后执行的操作,被转发到了脚本编辑中。 + + +core.moveAction(callback) +尝试执行单步行走。callback是执行完毕的回调。 +如果勇士面对的方向是noPass的,将直接触发事件并执行回调。 + + +core.moveHero(direction, callback) +令勇士朝一个方向行走。如果设置了callback,则只会行走一步,并执行回调。 +否则,将一直朝该方向行走,直到core.status.heroStop为true为止。 + + +core.isMoving() +当前是否正在处于行走状态 + + +core.waitHeroToStop(callback) +停止勇士的行走,等待行动结束后,再异步执行回调。 + + +core.turnHero(direction) +转向。如果设置了direction则会转到该方向,否则会右转。该函数会自动计入录像。 + + +core.moveDirectly(destX, destY) +尝试瞬间移动到某点,被转发到了脚本编辑中。 +此函数返回非负值代表成功进行瞬移,返回值是省略的步数;如果返回-1则代表没有成功瞬移。 + + +core.tryMoveDirectly(destX, destY) +尝试单击瞬移到某点。 +如果该点可被直接瞬间移动到,则直接瞬移到该点;否则尝试瞬移到相邻的上下左右点并行走一步。 + + +core.drawHero(status, offset) +绘制勇士。 +status可选,为'stop','leftFoot'和'rightFoot'之一,不填或null默认是'stop'。 +offset可选,表示具体当前格子的偏移量。不填默认为0。 +此函数将重新计算地图的偏移量,调整窗口位置,绘制勇士和跟随者信息。 + +// ------ 画布、位置、阻激夹域、显伤 ------ // + +core.setGameCanvasTranslate(canvas, x, y) +设置某个画布的偏移量 + + +core.addGameCanvasTranslate(x, y) +加减所有系统画布(ui和data除外)的偏移量。主要是被“画面震动”所使用。 + + +core.updateViewport() +根据大地图的偏移量来更新窗口的视野范围。 + + +core.nextX(n) / core.nextY(m) +获得勇士面对的第n个位置的横纵坐标。n可不填,默认为1。 + + +core.nearHero(x, y) +判定某个点是否和勇士的距离不大于1。 + + +core.gatherFollowers() +聚集所有的跟随者到勇士的位置。 + + +core.updateFollowers() +更新跟随者们的坐标。 + + +core.updateCheckBlock(floorId) +更新阻激夹域的信息,被转发到了脚本编辑中。 + + +core.checkBlock() +检查勇士坐标点的阻激夹域信息。 + + +core.updateDamage(floorId, ctx) +更新全地图的显伤。floorId可选,默认为当前楼层。 +ctx可选,为画布;如果不为空,则将会绘制到该画布上而不是damage层上。 + +// ------ 录像相关 ------ // + +core.chooseReplayFile() +弹出选择文件窗口,让用户选择录像文件。 + + +core.startReplay(list) +开始播放一段录像。list为录像的操作数组。 + + +core.triggerReplay() +播放或暂停录像,实际上是pauseReplay或resumeReplay之一。 + + +core.pauseReplay() / core.resumeReplay() +暂停和继续录像播放。 + + +core.speedUpReplay() / core.speedDownReplay() +加速和减速录像播放。 + + +core.setReplaySpeed(speed) +直接设置录像回放速度。 + + +core.stopReplay(force) +停止录像回放。如果force为true则强制停止。 + + +core.rewindReplay() +回退一个录像节点。 + + +core.saveReplay() / core.bookReplay() / core.viewMapReplay() +回放录像时的存档、查看怪物手册、浏览地图操作。 + + +core.isReplaying() +当前是否正在录像播放中。 + + +core.registerReplayAction(name, func) +注册一个自定义的录像行为。 +name:自定义名称,可用户注销使用。 +func:具体执行录像的函数,是一个函数体或者插件中的函数名。 +func需要接受action参数,代表录像回放时的当前操作行为。 +如果func返回true,则代表成功处理了此次操作,返回false代表没有进行处理。 +请注意回放录像时的二次记录问题(即回放时录像会重新记录路线)。 + + +core.unregisterReplayAction(name) +注销一个录像行为。此函数一般不应当被使用。 + +// ------ 存读档相关 ------ // + +core.autosave(remoreLast) +进行一个自动存档,实际上是加入到缓存之中。 +removeLast如果为true则会从路线中删除最后一项再存(打怪开门前的状态)。 +在事件处理中不允许调用本函数,如有需要请呼出存档页面。 + + +core.checkAutosave() +将缓存的自动存档写入存储中。平均每五秒钟,或在窗口失去焦点时被执行。 + + +core.doSL(id, type) +实际执行一个存读档事件。id为存档编号,自动存档为'autoSave'。 +type只能为'save', 'load', 'replayLoad'之一,代表存档、读档和从存档回放录像。 + + +core.syncSave(type) / core.syncLoad() +向服务器同步存档,从服务器加载存档。type如果为'all'则会向服务器同步所有存档。 + + +core.saveData() +获得要存档的内容,实际转发到了脚本编辑中。 + + +core.loadData(data, callback) +实际执行一次读档行为,data为读取到的数据,callback为执行完毕的回调。 +实际转发到了脚本编辑中。 + + +core.getSave(index, callback) +获得某个存档位的存档。index为存档编号,0代表自动存档。 + + +core.getSaves(ids, callback) +获得若干个存档位的存档。ids为一个存档编号数组,0代表自动存档。 + + +core.getAllSaves(callback) +获得全部的存档内容。目前仅被同步全部存档和下载全部存档所调用。 + + +core.getSaveIndexes(callback) +刷新全部的存档信息,将哪些档位有存档的记录到core.saves.ids中。 + + +core.hasSave(index) +判定某个存档位是否存在存档。index为存档编号,0代表自动存档。 + + +core.removeSave(index) +删除某个存档。index为存档编号,0代表自动存档。 + + +// ------ 属性、状态、位置、变量等 ------ // + +core.setStatus(name, value) +设置勇士当前的某个属性。 + + +core.addStatus(name, value) +加减勇士当前的某个属性。等价于 core.setStatus(name, core.getStatus(name) + value) + + +core.getStatus(name) +获得勇士的某个原始属性值。 + + +core.getStatusOrDefault(status, name) +尝试从status中获得某个原始属性值;如果status为null或不存在对应属性值则从勇士属性中获取。 +此项在伤害计算函数中使用较多,例如传递新的攻击和防御来计算临界和1防减伤。 + + +core.getRealStatus(name) +获得勇士的某个计算属性值。该属性值是在加成buff之后得到的。 +该函数等价于 core.getStatus(name) * core.getBuff(name) + + +core.getRealStatusOrDefault(status, name) +尝试从status中获得某个原始属性值再进行增幅,如果不存在则获取勇士本身的计算属性值。 + + +core.setBuff(name, value) +设置勇士的某个属性的增幅值。value为1代表无增幅。 + + +core.addBuff(name, value) +增减勇士的某个属性的增幅值。等价于 core.setBuff(name, core.getBuff(name) + value) + + +core.getBuff(name) +获得勇士的某个属性的增幅值。默认值是1。 + + +core.setHeroLoc(name, value, noGather) +设置勇士位置属性。name只能为'x', 'y'和'direction'之一。 +如果noGather为true,则不会聚集所有的跟随者。 + + +core.getHeroLoc(name) +获得勇士的某个位置属性。如果name为null则直接返回core.status.hero.loc。 + + +core.getLvName(lv) +获得某个等级对应的名称,其在全塔属性的levelUp中定义。如果不存在则返回原始数值。 + + +core.setFlag(name, value) +设置某个自定义变量或flag。如果value为null则会调用core.removeFlag进行删除。 + + +core.addFlag(name, value) +加减某个自定义的变量或flag。等价于 core.setFlag(name, core.getFlag(name, 0) + value) + + +core.getFlag(name, defaultValue) +获得某个自定义的变量或flag。如果该flag不存在(从未赋值过),则返回defaultValue值。 + + +core.hasFlag(name) +判定是否拥有某个自定义变量或flag。等价于 !!core.getFlag(name, 0) + + +core.removeFlag(name) +删除一个自定义变量或flag。 + + +core.lockControl() / core.unlockControl() +锁定和解锁控制。常常应用于事件处理。 + + +core.debug() +开启调试模式。此模式下可以按住Ctrl进行穿墙。 + +// ------ 天气,色调,音乐和音效 ------ // + +core.setWeather(type, level) +设置当前的天气。type只能为'rain', 'snow'或'fog',level为1-10之间代表强度信息。 + + +core.setCurtain(color, time, callback) +更改画面色调。color为更改到的色调,是个三元或四元组;time为渐变时间,0代表立刻切换。 + + +core.screenFlash(color, time, times, callback) +画面闪烁。color为色调,三元或四元组;time为单次闪烁时间,times为总闪烁次数。 + + +core.playBgm(bgm, startTime) +播放一个bgm。startTime可以控制开始时间,不填默认为0。 +如果bgm不存在、不被支持,或当前不允许播放背景音乐,则会跳过。 + + +core.pauseBgm() / core.resumeBgm() +暂停和恢复当前bgm的播放。 + + +core.triggerBgm() +更改当前bgm的播放状态。 + + +core.playSound(sound) / core.stopSound() +播放一个音效,停止全部音效。 +如果sound不存在、不被支持,或当前不允许播放音效,则会忽略。 + + +core.checkBgm() +检查bgm的状态。 +有的时候,刚打开页面时,浏览器是不允许自动播放标题界面bgm的,一定要经过一次用户操作行为。 +这时候我们可以给开始按钮增加core.checkBgm(),如果之前没有成功播放则重新播放。 + +// ------ 状态栏和工具栏相关 ------ // + +core.clearStatusBar() +清空状态栏的数据。 + + +core.updateStatusBar() +更新状态栏,被转发到了脚本编辑中。此函数还会根据是否在回放来设置工具栏的图标。 + + +core.showStatusBar() / core.hideStatusBar(showToolbox) +显示和隐藏状态栏。 +如果showToolbox为true,则在竖屏模式下不隐藏工具栏,方便手机存读档操作。 + + +core.updateHeroIcon() +更新状态栏上的勇士图标。 + + +core.updateGlobalAttribute() +更新全局属性,例如状态栏的背景图等。 + + +core.setToolbarButton(useButtom) +设置工具栏是否是拓展键盘。 + +// ------ resize 相关 ------ // + +core.registerResize(name, func) +注册一个resize函数。 +name为自定义名称,可供注销使用。 +func可以是一个函数,或插件中的函数名,可以接受一个obj作为参数。 +具体详见resize函数。 + + +core.unregisterResize(name) +注销一个resize函数。 + + +core.resize() +屏幕分辨率改变后的重新自适应。 +此函数将根据当前的屏幕分辨率信息,生成一个obj,并传入各个注册好的resize函数中执行。 +``` + ### enemys.js ### events.js diff --git a/_docs/event.md b/_docs/event.md index cac07dc8..fbd0b094 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -912,7 +912,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 **如果time指定为小于100,则视为没有楼层切换动画。** -### changePos:当前位置切换/勇士转向 +### changePos:当前位置切换/set勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 @@ -1126,15 +1126,15 @@ time为总移动的时间。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 -### setFg:更改画面色调 +### setCurtain:更改画面色调 -我们可以使用 `{"type": "setFg"}` 来更改画面色调。 +我们可以使用 `{"type": "setCurtain"}` 来更改画面色调。 ``` js [ - {"type": "setFg", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 - {"type": "setFg", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 - {"type": "setFg"} // 如果不指定color则恢复原样。 + {"type": "setCurtain", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 + {"type": "setCurtain", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 + {"type": "setCurtain"} // 如果不指定color则恢复原样。 ] ``` diff --git a/_docs/personalization.md b/_docs/personalization.md index 6edfb2a6..904b6e45 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -346,7 +346,7 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { "canUseItemEffect": "true" // 任何时候可用 ``` -除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyboard`)即可。 +除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyBoard`)即可。 ## 自定义怪物属性 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 2555b855..b7e93322 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -305,8 +305,8 @@ action | moveImage_s | showGif_0_s | showGif_1_s - | setFg_0_s - | setFg_1_s + | setCurtain_0_s + | setCurtain_1_s | screenFlash_s | setWeather_s | move_s @@ -1277,35 +1277,35 @@ var code = '{"type": "moveImage", "code": '+Int_0+toloc+EvalString_0+',"time": ' return code; */; -setFg_0_s +setCurtain_0_s : '更改画面色调' EvalString Colour '动画时间' Int? '不等待执行完毕' Bool Newline -/* setFg_0_s -tooltip : setFg: 更改画面色调,动画时间可不填 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setfg%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 +/* setCurtain_0_s +tooltip : setCurtain: 更改画面色调,动画时间可不填 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=setcurtain%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 default : ["255,255,255,1",'rgba(255,255,255,1)',500,false] colour : this.soundColor var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/; if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); Int_0 = Int_0!=='' ?(', "time": '+Int_0):''; var async = Bool_0?', "async": true':''; -var code = '{"type": "setFg", "color": ['+EvalString_0+']'+Int_0 +async+'},\n'; +var code = '{"type": "setCurtain", "color": ['+EvalString_0+']'+Int_0 +async+'},\n'; return code; */; -setFg_1_s +setCurtain_1_s : '恢复画面色调' '动画时间' Int? '不等待执行完毕' Bool Newline -/* setFg_1_s -tooltip : setFg: 恢复画面色调,动画时间可不填 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setfg%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 +/* setCurtain_1_s +tooltip : setCurtain: 恢复画面色调,动画时间可不填 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=setcurtain%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 default : [500,false] colour : this.soundColor Int_0 = Int_0!=='' ?(', "time": '+Int_0):''; var async = Bool_0?', "async": true':''; -var code = '{"type": "setFg"'+Int_0 +async+'},\n'; +var code = '{"type": "setCurtain"'+Int_0 +async+'},\n'; return code; */; @@ -2433,11 +2433,12 @@ ActionParser.prototype.parseAction = function() { } break; case "setFg": // 颜色渐变 + case "setCurtain": if(this.isset(data.color)){ - this.next = MotaActionBlocks['setFg_0_s'].xmlText([ + this.next = MotaActionBlocks['setCurtain_0_s'].xmlText([ data.color,'rgba('+data.color+')',data.time||0,data.async||false,this.next]); } else { - this.next = MotaActionBlocks['setFg_1_s'].xmlText([ + this.next = MotaActionBlocks['setCurtain_1_s'].xmlText([ data.time||0,data.async||false,this.next]); } break; diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 595a55ff..08fc8c74 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -141,8 +141,8 @@ editor_blockly = function () { MotaActionBlocks['animate_s'].xmlText(), MotaActionBlocks['showStatusBar_s'].xmlText(), MotaActionBlocks['hideStatusBar_s'].xmlText(), - MotaActionBlocks['setFg_0_s'].xmlText(), - MotaActionBlocks['setFg_1_s'].xmlText(), + MotaActionBlocks['setCurtain_0_s'].xmlText(), + MotaActionBlocks['setCurtain_1_s'].xmlText(), MotaActionBlocks['screenFlash_s'].xmlText(), MotaActionBlocks['setWeather_s'].xmlText(), MotaActionBlocks['playBgm_s'].xmlText(), diff --git a/libs/actions.js b/libs/actions.js index 917af40f..6b288e15 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -105,7 +105,7 @@ actions.prototype.doRegisteredAction = function (action) { return false; } -actions.prototype.checkReplaying = function () { +actions.prototype._checkReplaying = function () { if (core.isReplaying() && core.status.event.id != 'save' && (core.status.event.id || "").indexOf('book') != 0 && core.status.event.id != 'viewMaps') return true; @@ -114,7 +114,7 @@ actions.prototype.checkReplaying = function () { ////// 检查是否在录像播放中,如果是,则停止交互 actions.prototype._sys_checkReplay = function () { - if (this.checkReplaying()) return true; + if (this._checkReplaying()) return true; } ////// 按下某个键时 ////// @@ -145,7 +145,7 @@ actions.prototype.onkeyUp = function (e) { } actions.prototype._sys_onkeyUp_replay = function (e) { - if (this.checkReplaying()) { + if (this._checkReplaying()) { if (e.keyCode == 27) // ESCAPE core.stopReplay(); else if (e.keyCode == 90) // Z @@ -286,7 +286,7 @@ actions.prototype.keyUp = function (keyCode, altKey, fromReplay) { } actions.prototype._sys_keyUp_replay = function (keyCode, altKey, fromReplay) { - if (!fromReplay && this.checkReplaying()) return true; + if (!fromReplay && this._checkReplaying()) return true; } actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) { @@ -389,7 +389,7 @@ actions.prototype._sys_keyUp = function (keyCode, altKey) { if (core.status.automaticRoute && core.status.automaticRoute.autoHeroMove) { core.stopAutomaticRoute(); } - core.stopHero(); + core.status.heroStop = true; return true; } @@ -539,8 +539,8 @@ actions.prototype._sys_onup = function () { return true; } -////// 获得点击事件相对左上角的坐标(0到12之间) ////// -actions.prototype.getClickLoc = function (x, y) { +////// 获得点击事件相对左上角的坐标 ////// +actions.prototype._getClickLoc = function (x, y) { var statusBar = {'x': 0, 'y': 0}; var size = 32; @@ -663,7 +663,7 @@ actions.prototype.onmousewheel = function (direct) { actions.prototype._sys_onmousewheel = function (direct) { // 向下滚动是 -1 ,向上是 1 - if (this.checkReplaying()) { + if (this._checkReplaying()) { // 滚轮控制速度 if (direct == 1) core.speedUpReplay(); if (direct == -1) core.speedDownReplay(); @@ -2065,7 +2065,7 @@ actions.prototype._clickLocalSaveSelect = function (x, y) { var selection = y - topIndex; core.status.event.selection = selection; if (selection < 2) { - core.getAllSaves(selection == 0 ? null : core.saves.saveIndex, function (saves) { + var callback = function (saves) { if (saves) { var content = { "name": core.firstData.name, @@ -2074,7 +2074,9 @@ actions.prototype._clickLocalSaveSelect = function (x, y) { } core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5save", JSON.stringify(content)); } - }) + }; + if (selection == 0) core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); } core.status.event.selection = 2; diff --git a/libs/control.js b/libs/control.js index c72fabff..df0d7796 100644 --- a/libs/control.js +++ b/libs/control.js @@ -371,7 +371,6 @@ control.prototype.confirmRestart = function (fromSettings) { }); } - ////// 清除游戏状态和数据 ////// control.prototype.clearStatus = function() { // 停止各个Timeout和Interval @@ -424,7 +423,7 @@ control.prototype.stopAutomaticRoute = function () { core.status.automaticRoute.destX=null; core.status.automaticRoute.destY=null; core.status.automaticRoute.lastDirection = null; - core.stopHero(); + core.status.heroStop = true; if (core.status.automaticRoute.moveStepBeforeStop.length==0) core.deleteCanvas('route'); } @@ -751,11 +750,6 @@ control.prototype.waitHeroToStop = function(callback) { } } -////// 停止勇士的移动状态 ////// -control.prototype.stopHero = function () { - core.status.heroStop = true; -} - ////// 转向 ////// control.prototype.turnHero = function(direction) { if (direction) { @@ -1274,7 +1268,12 @@ control.prototype._doReplayAction = function (action) { control.prototype._replay_finished = function () { core.status.replay.replaying = false; core.status.event.selection = 0; - core.ui.drawConfirmBox("录像播放完毕,你想退出播放吗?", function () { + var str = "录像播放完毕,你想退出播放吗?"; + if (core.status.route.length != core.status.replay.totalList.length + || core.subarray(core.status.route, core.status.replay.totalList) == null) { + str = "录像播放完毕,但记录不一致。\n请检查录像播放时的二次记录问题。\n你想退出播放吗?"; + } + core.ui.drawConfirmBox(str, function () { core.ui.closePanel(); core.stopReplay(true); }, function () { @@ -1462,6 +1461,7 @@ control.prototype._replayAction_moveDirectly = function (action) { // 忽略连续的瞬移事件 while (core.status.replay.toReplay.length>0 && core.status.replay.toReplay[0].indexOf('move:')==0) { + core.status.route.push(action); action = core.status.replay.toReplay.shift(); } @@ -1617,13 +1617,15 @@ control.prototype._doSL_replayLoad_afterGet = function (id, data) { ////// 同步存档到服务器 ////// control.prototype.syncSave = function (type) { core.ui.drawWaiting("正在同步,请稍后..."); - core.getAllSaves(type=='all'?null:core.saves.saveIndex, function (saves) { - if (!saves) return core.drawText("没有要同步的存档"); + var callback = function (saves) { core.control._syncSave_http(type, saves); - }) + } + if (type == 'all') core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); } control.prototype._syncSave_http = function (type, saves) { + if (!saves) return core.drawText("没有要同步的存档"); var formData = new FormData(); formData.append('type', 'save'); formData.append('name', core.firstData.name); @@ -1748,8 +1750,7 @@ control.prototype.getSaves = function (ids, callback) { } } -control.prototype.getAllSaves = function (id, callback) { - if (id != null) return this.getSave(id, callback); +control.prototype.getAllSaves = function (callback) { var ids = Object.keys(core.saves.ids).filter(function(x){return x!=0;}) .sort(function(a,b) {return a-b;}), saves = []; this.getSaves(ids, function (data) { @@ -1792,7 +1793,7 @@ control.prototype.hasSave = function (index) { return core.saves.ids[index] || false; } -////// 删除一个或多个存档 +////// 删除某个存档 control.prototype.removeSave = function (index, callback) { if (index == 0 || index == "autoSave") { index = "autoSave"; @@ -1827,8 +1828,6 @@ control.prototype._updateFavoriteSaves = function () { core.setLocalStorage("favoriteName", core.saves.favoriteName); } -////// 加载某个存档 - // ------ 属性,状态,位置,buff,变量,锁定控制等 ------ // ////// 设置勇士属性 ////// @@ -2019,7 +2018,7 @@ control.prototype._setWeather_createNodes = function (type, level) { } ////// 更改画面色调 ////// -control.prototype.setFg = function(color, time, callback) { +control.prototype.setCurtain = function(color, time, callback) { if (time == null) time=750; if (time<=0) time=0; if (!core.status.curtainColor) @@ -2037,10 +2036,10 @@ control.prototype.setFg = function(color, time, callback) { return; } - this._setFg_animate(core.status.curtainColor, color, time, callback); + this._setCurtain_animate(core.status.curtainColor, color, time, callback); } -control.prototype._setFg_animate = function (nowColor, color, time, callback) { +control.prototype._setCurtain_animate = function (nowColor, color, time, callback) { var per_time = 10, step = parseInt(time / per_time); var animate = setInterval(function() { nowColor = [ @@ -2068,8 +2067,8 @@ control.prototype.screenFlash = function (color, time, times, callback) { times = times || 1; time = time / 3; var nowColor = core.clone(core.status.curtainColor); - core.setFg(color, time, function() { - core.setFg(nowColor, time * 2, function() { + core.setCurtain(color, time, function() { + core.setCurtain(nowColor, time * 2, function() { if (times > 1) core.screenFlash(color, time * 3, times - 1, callback); else { @@ -2381,7 +2380,7 @@ control.prototype.updateGlobalAttribute = function (name) { } } -////// 改变工具栏为按钮1-7 ////// +////// 改变工具栏为按钮1-8 ////// control.prototype.setToolbarButton = function (useButton) { if (!core.domStyle.showStatusBar) { // 隐藏状态栏时检查竖屏 diff --git a/libs/core.js b/libs/core.js index 966aa4d0..c3040df4 100644 --- a/libs/core.js +++ b/libs/core.js @@ -16,8 +16,7 @@ function core() { 'ground': null, 'items': {}, 'enemys': {}, - 'icons': {}, - 'events': {} + 'icons': {} } this.timeout = { 'tipTimeout': null, @@ -84,8 +83,8 @@ function core() { this.domStyle = { scale: 1.0, isVertical: false, - toolbarBtn: false, showStatusBar: true, + toolbarBtn: false, } this.bigmap = { canvas: ["bg", "event", "event2", "fg", "damage"], diff --git a/libs/events.js b/libs/events.js index b8f10226..5b7ff3b9 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1068,13 +1068,17 @@ events.prototype._action_moveImage = function (data, x, y, prefix) { } events.prototype._action_setFg = function (data, x, y, prefix) { + return this._action_setCurtain(data, x, y, prefix); +} + +events.prototype._action_setCurtain = function (data, x, y, prefix) { if (data.async) { - core.setFg(data.color, data.time); + core.setCurtain(data.color, data.time); core.setFlag('__color__', data.color || null); core.doAction(); } else { - core.setFg(data.color, data.time, function () { + core.setCurtain(data.color, data.time, function () { core.setFlag('__color__', data.color || null); core.doAction(); }); diff --git a/main.js b/main.js index 9d26b266..6091c580 100644 --- a/main.js +++ b/main.js @@ -364,7 +364,7 @@ main.dom.body.onselectstart = function () { main.dom.data.onmousedown = function (e) { try { e.stopPropagation(); - var loc = main.core.getClickLoc(e.clientX, e.clientY); + var loc = main.core.actions._getClickLoc(e.clientX, e.clientY); if (loc == null) return; main.core.ondown(loc); } catch (ee) { main.log(ee); } @@ -374,7 +374,7 @@ main.dom.data.onmousedown = function (e) { main.dom.data.onmousemove = function (e) { try { e.stopPropagation(); - var loc = main.core.getClickLoc(e.clientX, e.clientY); + var loc = main.core.actions._getClickLoc(e.clientX, e.clientY); if (loc == null) return; main.core.onmove(loc); }catch (ee) { main.log(ee); } @@ -401,7 +401,7 @@ main.dom.data.onmousewheel = function(e) { main.dom.data.ontouchstart = function (e) { try { e.preventDefault(); - var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); + var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; main.core.ondown(loc); }catch (ee) { main.log(ee); } @@ -411,7 +411,7 @@ main.dom.data.ontouchstart = function (e) { main.dom.data.ontouchmove = function (e) { try { e.preventDefault(); - var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); + var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; main.core.onmove(loc); }catch (ee) { main.log(ee); } diff --git a/project/floors/sample1.js b/project/floors/sample1.js index a7d256df..92b3a0e8 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -151,7 +151,7 @@ main.floors.sample1= "type": "hide" }, { - "type": "setFg", + "type": "setCurtain", "color": [ 0, 0, @@ -184,7 +184,7 @@ main.floors.sample1= "2,11": [ "\t[杰克,thief]喂!醒醒!快醒醒!", { - "type": "setFg", + "type": "setCurtain", "time": 1500 }, "\t[hero]额,我这是在什么地方?", From 1ec4994f3cd23467e80f16cfa63bd883a6fc8ad1 Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 2 Apr 2019 11:17:17 +0800 Subject: [PATCH 37/64] drawBook textAlign center --- libs/ui.js | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/ui.js b/libs/ui.js index 8a569922..6c887f92 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1254,6 +1254,7 @@ ui.prototype.drawBook = function (index) { core.drawBoxAnimate(); this.drawPagination(page, totalPage); + core.setTextAlign('ui', 'center'); core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13,'#DDDDDD', this._buildFont(15, true)); } From 2fc11cab08aa82bad271fb6d2cfdc209fced4465 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Tue, 2 Apr 2019 18:30:13 +0800 Subject: [PATCH 38/64] toolBox PgUp & setMusicBtn & docs --- _docs/api.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ libs/actions.js | 2 +- libs/control.js | 1 + libs/enemys.js | 16 ++++--- 4 files changed, 133 insertions(+), 7 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index 51e2b384..41942f38 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -955,8 +955,129 @@ core.resize() ### enemys.js +enemys.js中定义了一系列和怪物相关的API函数。 + +```js +core.hasSpecial(special, test) +判断是否含有某个特殊属性。test为要检查的特殊属性编号。 +special为要测试的内容,允许接收如下类型参数: + - 一个数字:将直接和test进行判等。 + - 一个数组:将检查test是否在该数组之中存在。 + - 一个怪物信息:将检查test是否在该怪物的特殊属性中存在 + - 一个字符串:视为怪物ID,将检查该怪物的特殊属性 + + +core.getSpecials() +获得所有特殊属性的列表。实际上被转发到了脚本编辑中。 + + +core.getSpecialText(enemy) +获得某个怪物的全部特殊属性名称。enemy可以是怪物信息或怪物ID。 +将返回一个数组,每一项是该怪物所拥有的一个特殊属性的名称。 + + +core.getSpecialHint(enemy, special) +获得怪物的某个特殊属性的描述。enemy可以是怪物信息或怪物ID,special为该特殊属性编号。 + + +core.canBattle(enemy, x, y, floorId) +判定当前能否战胜某个怪物。 +enemy可以是怪物信息或怪物ID,x,y,floorId为当前坐标和楼层。(下同) +能战胜返回true,不能战胜返回false。 + + +core.getDamage(enemy, x, y, floorId) +获得某个怪物的全部伤害值。 +如果没有破防或无法战斗则返回null,否则返回具体的伤害值。 + + +core.getExtraDamage(enemy, x, y, floorId) +获得某个怪物的额外伤害值(不可被魔防减伤)。 +目前暂时只包含了仇恨和固伤两者,如有需要可复写该函数。 + + +core.getDamageString(enemy, x, y, floorId) +获得某个怪物伤害字符串和颜色信息,以便于在地图上绘制显伤。 + + +core.nextCriticals(enemy, number, x, y, floorId) +获得接下来的N个临界值和临界减伤。enemy可以是怪物信息或怪物ID,x,y,floorId为当前坐标和楼层。 +number为要计算的临界值数量,不填默认为1。 +如果全塔属性中的useLoop开关被开启,则将使用循环法或二分法计算临界,否则使用回合法计算临界。 +返回一个二维数组 [[x1,y1],[x2,y2],...] 表示接下来的每个临界值和减伤值。 + + +core.getDefDamage(enemy, k, x, y, floorId) +获得某个怪物的k防减伤值。k可不填默认为1。 + + +core.getEnemyInfo(enemy, hero, x, y, floorId) +获得某个怪物的实际计算时的属性。该函数实际被转发到了脚本编辑中。 +hero可为null或一个对象,具体将使用core.getRealStatusOrDefault(hero, "atk")来获得攻击力数值。 +该函数应当返回一个对象,记录了怪物的实际计算时的属性。 + + +core.getDamageInfo(enemy, hero, x, y, floorId) +获得某个怪物的战斗信息。该函数实际被转发到了脚本编辑中。 +hero可为null或一个对象,具体将使用core.getRealStatusOrDefault(hero, "atk")来获得攻击力数值。 +如果该函数返回null,则代表不可战斗(如没有破防,或无敌等)。 +否则,该函数应该返回一个对象,记录了战斗伤害信息,如战斗回合数等。 +从V2.5.5开始,该函数也允许直接返回一个数字,代表战斗伤害值,此时回合数将视为0。 + + +core.updateEnemys() +更新怪物数据。该函数实际被转发到了脚本编辑中。详见文档-事件-更新怪物数据。 + + +core.getCurrentEnemys(floorId) +获得某个楼层不重复的怪物信息,floorId不填默认为当前楼层。该函数会被怪物手册所调用。 +该函数将返回一个列表,每一项都是一个不同的怪物,按照伤害值从小到大排序。 +另外值得注意的是,如果设置了某个怪物的displayIdInBook,则会返回对应的怪物。 + + +core.hasEnemyLeft(floorId) +检查某个楼层是否还有剩余的怪物。等价于 core.getCurrentEnemys(floorId).length > 0 +``` + ### events.js +events.js将处理所有和事件相关的操作。 + +```js +core.resetGame(hero, hard, floorId, maps, values) +重置整个游戏。该函数实际被转发到了脚本编辑中。 + + +core.startGame(hard, seed, route, callback) +开始新游戏。 +hard为难度字符串,会被设置为core.status.hard。 +seed为开始时要设置的的种子,route为要开始播放的录像,callback为回调函数。 +该函数将重置整个游戏,调用setInitData,执行startText事件,上传游戏人数统计信息等。 + + +core.setInitData() +根据难度分歧来初始化难度,包括设置flag:hard,设置初始属性等。 +该函数实际被转发到了脚本编辑中。 + + +core.win(reason, norank) +游戏胜利,reason为结局名,norank如果为真则该结局不计入榜单。 +该函数实际被转发到了脚本编辑中。 + + +core.lose(reason) +游戏失败,reason为结局名。该函数实际被转发到了脚本编辑中。 + + +core.gameOver(ending, fromReplay, norank) +游戏结束。ending为获胜结局名,null代表失败;fromReplay标识是否是录像触发的。 +此函数将询问是否上传成绩(如果ending不是null),是否下载录像等,并重新开始。 + + + +``` + + ### icons.js ### items.js diff --git a/libs/actions.js b/libs/actions.js index 6b288e15..e6f3aff0 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1255,7 +1255,7 @@ actions.prototype._clickToolbox = function (x, y) { core.ui.drawToolbox(core.status.event.selection); } if (y == this.LAST && constantsPage > 1) { - core.status.event.data.toolsPage--; + core.status.event.data.constantsPage--; core.ui.drawToolbox(core.status.event.selection); } } diff --git a/libs/control.js b/libs/control.js index df0d7796..b932169b 100644 --- a/libs/control.js +++ b/libs/control.js @@ -326,6 +326,7 @@ control.prototype._showStartAnimate_resetDom = function () { core.clearMap('all'); core.deleteAllCanvas(); core.dom.musicBtn.style.display = 'block'; + core.setMusicBtn(); // 重置音量 core.events.setVolume(1, 0); core.updateStatusBar(); diff --git a/libs/enemys.js b/libs/enemys.js index 75f64ede..2b7e551d 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -107,13 +107,13 @@ enemys.prototype.canBattle = function (enemy, x, y, floorId) { ////// 获得某个怪物的伤害 ////// enemys.prototype.getDamage = function (enemy, x, y, floorId) { if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; - var damage = this.calDamage(enemy, null, x, y, floorId); + var damage = this._calDamage(enemy, null, x, y, floorId); if (damage == null) return null; - return damage + this.getExtraDamage(enemy); + return damage + this.getExtraDamage(enemy, x, y, floorId); } ////// 获得某个怪物的额外伤害 ////// -enemys.prototype.getExtraDamage = function (enemy) { +enemys.prototype.getExtraDamage = function (enemy, x, y, floorId) { if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; var extra_damage = 0; if (this.hasSpecial(enemy.special, 17)) { // 仇恨 @@ -265,8 +265,8 @@ enemys.prototype._nextCriticals_useTurn = function (enemy, info, number, x, y, f enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId) { if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; k = k || 1; - var nowDamage = this.calDamage(enemy, null, x, y, floorId); - var nextDamage = this.calDamage(enemy, {"def": core.status.hero.def + k}, x, y, floorId); + var nowDamage = this._calDamage(enemy, null, x, y, floorId); + var nextDamage = this._calDamage(enemy, {"def": core.status.hero.def + k}, x, y, floorId); if (nowDamage == null || nextDamage == null) return "???"; return nowDamage - nextDamage; } @@ -284,7 +284,7 @@ enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) { } ////// 获得在某个勇士属性下怪物伤害 ////// -enemys.prototype.calDamage = function (enemy, hero, x, y, floorId) { +enemys.prototype._calDamage = function (enemy, hero, x, y, floorId) { if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; var info = this.getDamageInfo(enemy, hero, x, y, floorId); @@ -357,4 +357,8 @@ enemys.prototype._getCurrentEnemys_sort = function (enemys) { } return a.damage - b.damage; }); +} + +enemys.prototype.hasEnemyLeft = function (floorId) { + return core.getCurrentEnemys(floorId).length > 0; } \ No newline at end of file From e280308da51801dd25f70044fda3650b4a3e531d Mon Sep 17 00:00:00 2001 From: oc Date: Wed, 3 Apr 2019 00:23:12 +0800 Subject: [PATCH 39/64] docs for events/enemys/items/maps --- _docs/api.md | 778 +++++++++++++++++++++++++++++++++++++++++++++++- libs/actions.js | 2 +- libs/control.js | 23 -- libs/core.js | 2 +- libs/events.js | 162 +++++----- libs/items.js | 42 +-- libs/maps.js | 126 ++++---- libs/utils.js | 2 +- 8 files changed, 946 insertions(+), 191 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index 41942f38..8d085971 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -301,7 +301,7 @@ core.musicStatus.soundStatus (音效开启状态) core.musicStatus.playingBgm (当前正在播放的BGM) core.musicStatus.lastBgm (最近一次尝试播放的BGM) core.musicStatus.volume (当前的音量) -core.musicStatus.cachedBgms (背景音乐的缓存内从) +core.musicStatus.cachedBgms (背景音乐的缓存内容) core.musicStatus.cacheBgmCount (背景音乐的缓存数量,默认值是4) @@ -557,14 +557,6 @@ core.isPlaying() 当前是否正在游戏中。 -core.restart() -重新开始游戏。本质上就是播放标题界面的BGM并调用showStartAnimate。 - - -core.confirmRestart() -确认用户是否需要重新开始。 - - core.clearStatus() 清除所有的游戏状态和数据,包括状态栏的显示。 @@ -1041,9 +1033,17 @@ core.hasEnemyLeft(floorId) ### events.js -events.js将处理所有和事件相关的操作。 +events.js将处理所有和事件相关的操作,主要分为五个部分: +- 游戏的开始和结束 +- 系统事件的处理 +- 自定义事件的处理 +- 点击状态栏图标所进行的操作 +- 一些具体事件的执行内容 + ```js +// ------ 游戏的开始和结束 ------ // + core.resetGame(hero, hard, floorId, maps, values) 重置整个游戏。该函数实际被转发到了脚本编辑中。 @@ -1074,18 +1074,774 @@ core.gameOver(ending, fromReplay, norank) 此函数将询问是否上传成绩(如果ending不是null),是否下载录像等,并重新开始。 +core.restart() +重新开始游戏。本质上就是播放标题界面的BGM并调用showStartAnimate。 + +core.confirmRestart() +确认用户是否需要重新开始。 + +// ------ 系统事件处理 ------ // + +core.registerSystemEvent(type, func) +注册一个系统事件,即通过图块的默认触发器所触发的事件。 +type为一个要注册的事件类型,func为要执行的函数体或插件中的函数名。 +func需要接受(data, callback)作为参数,分别是触发点的图块信息,和执行完毕时的回调。 +如果注册一个已经存在的系统事件,比如openDoor,则会覆盖系统的默认函数。 + + +core.unregisterSystemEvent(type) +注销一个系统事件。type是上面你注册的事件类型。 + + +core.doSystemEvent(type, data, callback) +执行一个系统事件。type为事件类型,data为该事件点的图块信息,callback为执行完毕的回调。 + + +core.battle(id, x, y, force, callback) +和怪物进行战斗。 +id为怪物的ID,x和y为怪物坐标,force如果为真将强制战斗,callback为执行完毕的回调。 +如果填写了怪物坐标,则会删除对应点的图块并执行该点战后事件。 +如果是在事件流的执行过程中调用此函数,则不会进行自动存档,且会强制战斗。 + + +core.beforeBattle(enemyId, x, y) +战前事件。实际被转发到了脚本编辑中,可以在这里加上一些战前特效。 +此函数在“检测能否战斗和自动存档”【之后】执行。 +如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 +此函数返回true则将继续本次战斗,返回false将不再战斗。 + + +core.afterBattle(enemyId, x, y, callback) +战后事件,将执行扣血、加金币经验、特殊属性处理、战后事件处理等操作。 +实际被转发到了脚本编辑中。 + + +core.openDoor(x, y, needKey, callback) +尝试开一个门。x和y为门的坐标,needKey表示是否需要钥匙,callback为执行完毕的回调。 +如果不是一个有效的门,需要钥匙且未持有等,均会忽略此事件并直接执行callback。 + + +core.afterOpenDoor(doorId, x, y, callback) +开完一个门后执行的事件,实际被转发到了脚本编辑中。 + + +core.getItem(id, num, x, y, callback) +获得若干个道具。itemId为道具ID,itemNum为获得的道具个数,不填默认为1。 +x和y为道具点的坐标,如果设置则会擦除地图上的该点。 + + +core.afterGetItem(id, x, y, callback) +获得一个道具后执行的事件,实际被转发到了脚本编辑中。 + + +core.getNextItem(noRoute) +轻按,即获得面对的道具。如果noRoute为真则这个轻按行为不会计入录像。 + + +core.changeFloor(floorId, stair, heroLoc, time, callback, fromLoad) +楼层切换。floorId为目标楼层ID,stair为是什么楼梯,heroLoc为目标点坐标。 +time为切换时间,callback为切换完毕的回调,fromLoad标志是否是从读档造成的切换。 +floorId也可以填":before"和":next"表示前一层和后一层。 +heroLoc为{"x": 0, "y": 0, "direction": "up"}的形式。不存在则从勇士位置取。 +如果stair不为null,则会在该楼层中找对应的图块作为目标点的坐标并覆盖heroLoc。 +一般设置的是"upFloor"和"downFloor",但也可以用任何其他的图块ID。 + + +core.changingFloor(floorId, heroLoc, fromLoad) +正在执行楼层切换中执行的操作,实际被转发到了脚本编辑中。 + + +core.hasVisitedFloor(floorId) +是否曾经到达过某一层。 + + +core.visitFloor(floorId) +标记曾经到达了某一层。 + + +core.passNet(data) +执行一个路障处理。这里只有毒衰咒网的处理,血网被移动到了updateCheckBlock中。 + + +core.pushBox(data) +执行一个推箱子事件。 + + +core.afterPushBox() +推箱子之后触发的事件,实际被转发到了脚本编辑中。 + + +core.changeLight(id, x, y) +踩灯后的事件。 + +// ------ 自定义事件的处理 ------ // + +core.registerEvent(type, func) +注册一个自定义事件。type为事件名,func为执行事件的函数体或插件中的函数名。 +func可以接受(data, x, y, prefix)参数,其中data为事件内容,x和y为该点坐标,prefix为该点前缀。 +同名注册的事件将进行覆盖。 +请记得在自定义处理事件完毕后调用core.doAction()再继续执行下一个事件! + + +core.unregisterEvent(type) +注销一个自定义事件。 + + +core.doEvent(data, x, y, prefix) +执行一个自定义事件。data为事件内容,将根据data.type去注册的事件列表中查找对应的执行函数。 +x和y为该点坐标,prefix为该点前缀。执行事件时也会把(data, x, y, prefix)传入执行函数。 + + +core.setEvents(list, x, y, callback) +设置自定义事件的执行列表,坐标和回调函数。 + + +core.startEvents(list, x, y, callback) +开始执行一系列的自定义事件。list为事件列表,x和y为事件坐标,callback为执行完毕的回调。 +此函数将调用core.setEvents,然后停止勇士,再执行core.doAction()。 + + +core.doAction() +执行下一个自定义事件。 +此函数将检测事件列表是否全部执行完毕,如果是则执行回调函数。 +否则,将从事件列表中弹出下一个事件,并调用core.doEvent进行执行。 + + +core.insertAction(action, x, y, callback, addToLast) +向当前的事件列表中插入一个或多个事件并执行。 +如果当前并不是在事件执行流中,则会调用core.startEvents()开始执行事件,否则仅仅执行插入操作。 +action为要插入的事件,可以是一个单独的事件,或者是一个事件列表。 +x,y,callback如果设置了且不为null,则会覆盖当前的坐标和回调函数。 +addToLast如果为真,则会插入到事件执行列表的尾部,否则是插入到执行列表的头部。 + + +core.getCommonEvent(name) +根据名称获得某个公共事件内容。 + + +core.recoverEvents(data) +恢复事件现场。一般用于呼出怪物手册、呼出存读档页面等时,恢复事件执行流。 + +// ------ 点击状态栏图标时执行的一些操作 ------ // + +core.openBook(fromUserAction) +尝试打开怪物手册。fromUserAction标志是否是从用户的行为触发,如按键或点击状态栏。(下同) +不建议复写此函数,否则【呼出怪物手册】事件会出问题。 + + +core.useFly(fromUserAction) +尝试使用楼传器。可以安全的复写此函数,参见文档-个性化-覆盖楼传事件。 + + +core.flyTo(toId, callback) +尝试飞行到某个楼层,被转发到了脚本编辑中。 +如果此函数返回true代表成功进行了飞行,false代表不能进行飞行。 + + +core.openEquipbox(fromUserAction) / core.openToolbox(fromUserAction) +尝试打开道具栏和装备栏。可以安全复写这两个函数。 + + +core.openQuickShop(fromUserAction) / core.openKeyBoard(fromUserAction) +尝试打开快捷商店和虚拟键盘。可以安全复写这两个函数。 + + +core.save(fromUserAction) / core.load(fromUserAction) +尝试打开存读档页面。 +不建议复写这两个函数,否则【呼出存读档页面】事件会出问题。 + + +core.openSettings(fromUserAction) +尝试打开系统菜单。不建议复写此函数。 + + +// ------ 一些具体事件的执行内容 ------ // + +core.hasAsync() +当前是否存在未执行完毕的异步事件。请注意正在播放的动画也算异步事件。 + + +core.follow(name) / core.unfollow(name) +跟随勇士/取消跟随。name为行走图名称。 +在取消跟随时如果指定了name,则会从跟随列表中选取一个该行走图取消,否则取消所有跟随。 +跟随和取消跟随都会调用core.gatherFollowers()来聚集所有的跟随者。 + + +core.setValue(name, value, prefix) / core.addValue(name, value, prefix) +设置/增减某个数值。name可以是status:xxx,item:xxx或flag:xxx。 +value可以是一个表达式,将调用core.calValue()计算。prefix为前缀,独立开关使用。 + + +core.doEffect(effect, need, times) +执行一个effect操作。该函数目前仅被全局商店的status:xxx+=yyy所调用。 + + +core.setFloorInfo(name, values, floorId, prefix) +设置某层楼的楼层属性。 + + +core.setGlobalAttribute(name, value) +设置一个全局属性,如边框颜色等。 + + +core.setGlobalFlag(name, value) +设置一个全局开关,如enableXXX等。 +如果需要设置一个全局数值如红宝石数值,可以直接简单的修改core.values,因此没有单独列出函数。 + + +core.closeDoor(x, y, id, callback) +执行一个关门事件。如果不是一个合法的门,或者该点不为空地,则会忽略本事件。 + + +core.showImage(code, image, sloc, loc, opacityVal, time, callback) +显示一张图片。code为图片编号,image为图片内容或图片名。 +sloc为[x,y,w,h]形式,表示在原始图片上裁剪的区域,也可直接设为null表示整张图片。 +loc为[x,y,w,h]形式,表示在界面上绘制的位置和大小,w和h可忽略表示使用绘制大小。 +opacityVal为绘制的不透明度,time为淡入时间。 +此函数将创建一个画布,其z-index是100+code,即图片编号为1则是101,编号50则是150。 +请注意,curtain层的z-index是125,UI层的z-index是140;因此可以通过图片编号来调整覆盖关系。 + + +core.hideImage(code, time, callback) +隐藏一张图片。code为图片编号,time为淡出时间。 + + +core.moveImage(code, to, opacityVal, time, callback) +移动一张图片。code为图片编号,to为[x,y]表示目标位置,opacityVal目标不透明度,time为移动时间。 + + +core.showGif(name, x, y) +绘制一张gif图片或取消所有绘制内容。如果name不设置则视为取消。x和y为左上角像素坐标。 + + +core.setVolume(value, time, callback) +设置音量。value为目标音量大小,在0到1之间。time为音量渐变的时间。 + + +core.vibrate(time, callback) +画面震动。time为震动时间。 +请注意,画面震动时间必须是500的倍数,系统也会自动把time调整为上整的500倍数值。 + + +core.eventMoveHero(steps, time, callback) +使用事件移动勇士。time为每步的移动时间。 +steps为移动数组,可以接受'up','down','left','right','forward'和'backward'项。 +使用事件移动勇士将不会触发任何地图上的事件。 + + +core.jumpHero(ex, ey, time, callback) +跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。 + + +core.openShop(shopId, needVisited) +打开一个全局商店。needVisited表示是否需要该商店原本就是启用状态。 +如果该商店对应的实际上是一个全局事件,则会直接插入并执行。 + + +core.disableQuickShop(shopId) +禁用一个全局商店,即把一个商店从启用变成禁用状态。 + + +core.canUseQuickShop(shopId) +当前能否使用某个全局商店,实际被转发到了脚本编辑中。 +如果此函数返回null则表示可以使用,返回一个字符串表示不可以,该字符串表示不可以的原因。 + + +core.setHeroIcon(name, noDraw) +设置勇士的行走图。 +name为行走图名称,noDraw如果为真则不会调用core.drawHero()函数进行刷新。 + + +core.checkLvUp() +检查升级事件。该函数将判定当前是否升级(或连续升级),然后执行升级事件。 + + +core.tryUseItem(itemId) +尝试使用一个道具。 +对于怪物手册和楼传器,将分别调用core.openBook()和core.useFly()函数。 +对于中心对称飞行器,则会调用core.drawCenterFly()函数。 +对于其他的道具,将检查是否拥有,能否使用,并且进行使用。 + + +core.afterUseBomb() +使用炸弹或圣锤后的事件。实际被转发到了脚本编辑中。 ``` - ### icons.js +icons.js主要是负责素材相关信息,比如某个素材在对应的图片上的位置。 + +```js +core.getClsFromId(id) +根据某个素材的ID获得该素材的cls + + +core.getTilesetOffset(id) +根据某个素材来获得对应的tileset和坐标信息。 +如果该素材不是tileset,则返回null。 +``` + ### items.js +items.js主要负责一切和道具相关的内容。 + +```js +core.getItemEffect(itemId, itemNum) +即捡即用类的道具获得时的效果。实际对应道具图块属性中的itemEffect框。 + + +core.getItemEffectTip(itemId) +即捡即用类的道具获得时的额外提示,比如“,攻击+100”。 +实际对应道具图块属性中的itemEffectTip框。 + + +core.useItem(itemId, noRoute, callback) +尝试使用一个道具。实际对应道具图块属性中的useItemEffect框。 +此函数也会调用一遍core.canUseItem(),如果无法使用将直接返回。 +noRoute如果为真,则这次使用道具的过程不会被计入录像。 +使用道具完毕后,对于消耗道具将自动扣除,永久道具不会扣除。 + + +core.canUseItem(itemId) +当前能否使用某个道具。 +有些系统道具如破炸和上下楼器等,会在计算出目标点的坐标后存入core.status.event.ui。 +使用道具时将直接从core.status.event.ui调用,不会重新计算。 + + +core.itemCount(itemId) +获得某个道具的个数。 + + +core.hasItem(itemId) +当前是否拥有某个道具。等价于 core.itemCount(itemId) > 0 +请注意,装备上的装备不视为拥有该道具,即core.hasEquip()和core.hasItem()是完全不同的。 + + +core.hasEquip(itemId) +当前是否装备上某个装备。 +请注意,装备上的装备不视为拥有该道具,即core.hasEquip()和core.hasItem()是完全不同的。 + + +core.getEquip(equipType) +获得某个装备位的当前装备。equipType为装备类型,从0开始。 +如果该装备位没有装备则返回null,否则返回当前装备的ID。 + + +core.setItem(itemId, itemNum) +设置某个道具的个数。 + + +core.addItem(itemId, itemNum) +增减某个道具的个数,itemNum可不填默认为1。 + + +core.getEquipTypeByName(name) +根据装备位名称来找到一个空的装备孔,适用于多重装备。 +如果没有一个装备孔是该装备名称,则返回-1。 + + +core.getEquipTypeById(equipId) +获得某个装备的装备类型。 +如果其type写的是装备名(多重装备),则调用core.getEquipTypeByName()函数。 + + +core.canEquip(equipId, hint) +当前能否穿上某个装备。如果hint为真,则不可装备时会气泡提示原因。 + + +core.loadEquip(equipId, callback) +穿上某个装备。 + + +core.unloadEquip(equipType, callback) +脱下某个装备孔的装备。 + + +core.compareEquipment(compareEquipId, beComparedEquipId) +比较两个套装的差异。 +此函数将对所有的勇士属性包括生命魔力攻防魔防金币等进行比较。 +如果存在差异的,将作为一个对象返回其差异内容。 + + +core.quickSaveEquip(index) +保存当前套装。index为保存的套装编号。 + + +core.quickLoadEquip() +读取当前套装。index为读取的套装编号。 +``` + ### loader.js +loader.js主要负责资源加载相关的内容。 + +```js +core.loadImage(imgName, callback) +从 project/images/ 中加载一张图片。imgName为图片名。 +callback为执行完毕的回调函数,接收(imgName, image)即图片名和图片内容作为参数。 +如果图片不存在或加载失败则会在控制台打出一条错误日志,不会执行回调。 + + +core.loadImages(names, toSave, callback) +从 project/images/ 中加载若干张图片。 +names为一个图片名的列表,toSave为加载并存到的对象。 +callback为全部加载完毕执行的回调。 + + +core.loadOneMusic(name) +从 project/sounds/ 或第三方中加载一个音乐,并存入core.material.bgms中。name为音乐名。 + + +core.loadOneSound(name) +从 project/sounds/ 中加载一个音效,并存入core.material.sounds中。name为音效名。 + + +core.loadBgm(name) +预加载一个bgm并加入缓存列表core.musicStatus.cachedBgms。 +此函数将会检查bgm的缓存,预加载和静音播放。 +如果缓存列表溢出(core.musicStatus.cacheBgmCount)则通过LRU算法选择一个bgm并调用core.freeBgm()。 + + +core.freeBgm(name) +释放一个bgm的内存并移出缓存列表。如果该bgm正在播放则也会立刻停止。 +``` + ### map.js +maps.js负责一切和地图相关的处理内容,包括如下几个方面: +- 地图的初始化,保存和读取,地图数组的生成 +- 是否可移动或瞬间移动的判定 +- 地图的绘制 +- 获得某个点的图块信息 +- 启用和禁用图块,改变图块 +- 移动/跳跃图块,淡入淡出图块 +- 全局动画控制,动画的绘制 + +```js +// ------ 地图的初始化,保存和读取,地图数组的生成 ------ // + +core.loadFloor(floorId, map) +从楼层或者存档中生成core.status.maps的内容。 +map为存档信息,如果某项在map中不存在则会从core.floors中读取。 + + +core.getNumberById(id) +给定一个图块ID,找到对应的数字。 + + +core.initBlock(x, y, id, addInfo, eventFloor) +给定一个数字,初始化一个图块信息。 +x和y为坐标,id为数字或者可以:t或:f结尾表示初始是启用还是禁用状态。 +addInfo如果为true则会填充上图块的默认信息,比如给怪物添加battle触发器。 +eventFloor如果设置为某个楼层信息,则会填充上该点的自定义或楼层切换事件。 + + +core.compressMap(mapArr, floorId) +压缩地图。mapArr为要压缩的二维数组,floorId为对应的楼层。 +此函数将把mapArr和对应的楼层中的数组进行比较,并只取差异值进行存储。 +通过这种压缩地图的方式,不仅节省了存档空间,还支持了任意修改地图的接档。 + + +core.decompressMap(mapArr, floorId) +解压缩地图。mapArr为压缩后的地图,floorId为对应的楼层。 +此函数返回解压后的二维数组。 + + +core.saveMap(floorId) +将某层楼的数据生成存档所保存的内容。在core.saveData()中被调用。 + + +core.loadMap(data, floorId) +从data中读取楼层数据,并调用core.loadFloor()进行初始化。 + + +core.resizeMap(floorId) +根据某层楼的地图大小来调整大地图的画布大小。floorId可为null表示当前层。 + + +core.getMapArray(floorId, showDisable) +生成某层楼的二维数组。floorId可不填代表当前楼层。 +showDisable若为真,则对于禁用的点会加上:f表示,否则视为0。 + + +core.getMapBlocksObj(floorId, showDisable) +以x,y的形式返回每个点的图块信息。floorId可不填表示当前楼层。 +此函数将返回 {"0,0": {...}, "0,1": {...}} 这样的结构,其中内部为对应点的block信息。 + + +core.getBgMapArray(floorId, noCache) +获得某层楼的背景层的二维数组。floorId可不填表示当前楼层。 +如果noCache为真则重新从剧本中读取而不使用缓存数据。 + + +core.getFgMapArray(floorId, noCache) +获得某层楼的前景层的二维数组。floorId可不填表示当前楼层。 +如果noCache为真则重新从剧本中读取而不使用缓存数据。 + + +core.getBgNumber(x, y, floorId, noCache) +获得某层楼的背景层中某个点的数字。floorId可不填表示当前楼层。 +如果noCache为真则重新从剧本中读取而不使用缓存数据。 +本函数实际等价于 core.getBgMapArray(floorId, noCache)[y][x] + + +core.getBgNumber(x, y, floorId, noCache) +获得某层楼的前景层中某个点的数字。参数和方法同上。 + +// ------ 是否可移动或瞬间移动的判定 ------ // + +core.generateMovableArray(floorId, x, y, direction) +生成全图或某个点的可通行方向数组。floorId为楼层Id,可不填默认为当前点。 +这里的可通行方向数组,指的是["up","down","left","right"]中的一个或多个组成的数组。 + - 如果不设置x和y,则会返回一个三维数组,其中每个点都是一个该点可通行方向的数组。 + - 如果设置了x和y但没有设置direction,则只会返回该点的可通行方向数组, + - 如果设置了x和y以及direction,则会判定direction是否在该点可通行方向数组中,并返回true或false。 +可以使用core.inArray()来判定某个方向是否在可通行方向数组中。 + + +core.canMoveHero(x, y, direction, floorId) +某个点是否可朝某个方向移动。x和y可选,不填或为null则默认为勇士当前点。 +direction可选,不填或为null则默认勇士当前朝向。floorId不填则默认为当前楼层。 +此函数将直接调用 core.generateMovableArray() 进行判定。 + + +core.canMoveDirectly(destX, destY) +当前能否瞬间移动到某个点。 +如果可以瞬移则返回非负数,其值为该次瞬移所少走的步数;如果不能瞬移则返回-1。 + + +core.automaticRoute(destX, destY) +找寻到目标点的一条自动寻路路径。 + +// ------ 绘制地图相关 ------ // + +core.drawBlock(block, animate) +重新绘制一个图块,block为图块信息。 +如果animate不为null则代表是通过全局动画的绘制,其值为当前的帧数。 + + +core.generateGroundPattern(floorId) +生成某个楼层的地板信息。floorId不填默认为当前楼层。 +该函数可被怪物手册、对话框帧动画等地方使用。 + + +core.drawMap(floorId, callback) +绘制某层楼的地图。floorId为目标楼层ID,可不填表示当前楼层。 +此函数会将core.status.floorId设置为floorId,并设置core.status.thisMap。 +将依次调用core.drawBg(), core.drawEvents()和core.drawFg()函数,最后绘制勇士和更新地图显伤。 + + +core.drawBg(floorId, ctx) +绘制背景层。floorId为目标楼层ID,可不填表示当前楼层。 +如果ctx不为null,则背景层将绘制在该画布上而不是bg层上(drawThumbnail使用)。 +可以通过复写该函数,调整_drawFloorImages和_drawBgFgMap的顺序来调整背景图块和贴图的遮挡顺序。 + + +core.drawEvents(floorId, blocks, ctx) +绘制事件层。floorId为目标楼层ID,可不填表示当前楼层。 +block表示要绘制的图块列表,可不填使用当前楼层的图块列表。 +如果ctx不为null,则背景层将绘制在该画布上而不是event层上(drawThumbnail使用)。 + + +core.drawFg(floorId, ctx) +绘制前景层。floorId为目标楼层ID,可不填表示当前楼层。 +如果ctx不为null,则背景层将绘制在该画布上而不是fg层上(drawThumbnail使用)。 +可以通过复写该函数,调整_drawFloorImages和_drawBgFgMap的顺序来调整前景图块和贴图的遮挡顺序。 + + +core.drawThumbnail(floorId, blocks, options, toDraw) +绘制一个楼层的缩略图。floorId为目标楼层ID,可不填表示当前楼层。 +block表示要绘制的图块列表,可不填使用当前楼层的图块列表。 +options为绘制选项(可为null),包括: + heroLoc: 勇士位置;heroIcon:勇士图标(默认当前勇士);damage:是否绘制显伤; + flags:当前的flags(在存读档时使用) +toDraw为要绘制到的信息(可为null,或为一个画布名),包括: + ctx:要绘制到的画布(名);x,y:起点横纵坐标(默认0);size:绘制大小(默认416/480); + all:是否绘制全图(默认false);centerX,centerY:截取中心(默认为地图正中心) + +// ------ 获得某个点的图块信息 ------ // + +core.noPass(x, y, floorId) +判定某个点是否有noPass的图块。 + + +core.npcExists(x, y, floorId) +判定某个点是否有NPC的存在。 + + +core.terrainExists(x, y, id, floorId) +判定某个点是否有(指定的)地形存在。 +如果id为null,则只要存在terrains即为真,否则还会判定对应点的ID。 + + +core.stairExists(x, y, floorId) +判定某个点是否存在楼梯。 + + +core.nearStair() +判定当前勇士是否在楼梯上或旁边(距离不超过1)。 + + +core.enemyExists(x, y, id, floorId) +判定某个点是否有(指定的)怪物存在。 +如果id为null,则只要存在怪物即为真,否则还会判定对应点的怪物ID。 +请注意,如果需要判定某个楼层是否存在怪物请使用core.hasEnemyLeft()函数。 + + +core.getBlock(x, y, floorId, showDisable) +获得某个点的当前图块信息。x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +showDisable如果为true,则对于禁用的点和事件也会进行返回。 +如果该点不存在图块,则返回null。 +否则,返回值如下: {"index": xxx, "block": xxx} +其中index为该点在该楼层blocks数组中的索引,block为该图块实际内容。 + + +core.getBlockId(x, y, floorId, showDisable) +获得某个点的图块ID。如果该点不存在图块则返回null。 + + +core.getBlockCls(x, y, floorId, showDisable) +获得某个点的图块类型。如果该点不存在图块则返回null。 + + +core.getBlockInfo(block) +根据某个的图块信息获得其详细的素材信息。 +如果参数block为字符串,则视为图块ID;如果参数为数字,则视为图块的数字。 +此函数将返回一个非常详尽的素材信息,目前包括如下几项: +number:素材数字;id:素材id;cls:素材类型;image:素材所在的素材图片;animate:素材的帧数。 +posX, posY:素材在该素材图片上的位置;height:素材的高度;faceIds:NPC朝向记录。 + + +core.searchBlock(id, floorId, showDisable) +搜索一个图块出现过的所有位置。id为图块ID,也可以传入图块的数字。 +floorId为要搜索的楼层,可以是一个楼层ID,或者一个楼层数组。如果floorId不填则只搜索当前楼层。 +showDisable如果为真,则对于禁用的图块也会返回。 +此函数将返回一个数组,每一项为一个搜索到的结果: +{"floorId": ..., "index": ..., "block": {...}, "x": ..., "y": ...} +即包含该图块所在的楼层ID,在该楼层的blocks数组的索引,图块内容,和横纵坐标。 + + +// ------ 启用和禁用图块,改变图块 ------ // + +core.showBlock(x, y, floorId) +将某个点从禁用变成启用状态。floorId可不填或null表示当前楼层。 + + +core.hideBlock(x, y, floorId) +将某个点从启用变成禁用状态,但不会对其进行删除。floorId可不填或null表示当前楼层。 +此函数不会实际将该块从地图中进行删除,而是将该点设置为禁用,以供以后可能的启用事件。 + + +core.removeBlock(x, y, floorId) +将从启用变成禁用状态,并尽可能将其从地图上删除。 +和hideBlock相比,如果该点不存在自定义事件(比如门或普通的怪物),则将直接从地图中删除。 +如果存在自定义事件,则简单的禁用它,以供以后可能的启用事件。 + + +core.removeBlockById(index, floorId) +根据索引从地图的block数组中尽可能删除一个图块。floorId可不填或null表示当前楼层。 + + +core.removeBlockByIds(floorId, ids) +根据索引数组从地图的block数组中尽可能删除一系列图块。floorId可不填或null表示当前楼层。 + + +core.canRemoveBlock(block, floorId) +判定当前能否完全删除某个图块。floorId可不填或null表示当前楼层。 +如果该点存在自定义事件,或者是重生怪,则不可进行删除。 + + +core.showBgFgMap(name, loc, floorId, callback) +显示某层楼中某个背景/前景层的图块。name只能为'bg'或'fg'表示背景或前景层。 +loc为该点坐标,floorId可不填默认为当前楼层。callback为执行完毕的回调。 + + +core.hideBgFgMap(name, loc, floorId, callback) +隐藏某层楼中某个背景/前景层的图块。name只能为'bg'或'fg'表示背景或前景层。 +loc为该点坐标,floorId可不填默认为当前楼层。callback为执行完毕的回调。 + + +core.showFloorImage(loc, floorId, callback) +显示某层楼中的某个楼层贴图。loc为该贴图的左上角坐标。floorId可省略表示当前楼层。 + + +core.hideFloorImage(loc, floorId, callback) +隐藏某层楼中的某个楼层贴图。loc为该贴图的左上角坐标。floorId可省略表示当前楼层。 + + +core.setBlock(number, x, y, floorId) +改变某个楼层的某个图块。 +number为要改变到的数字,也可以传入图块id(将调用core.getNumberById()来获得数字)。 +x,y和floorId为目标点坐标和楼层,可忽略为当前点和当前楼层。 + + +core.replaceBlock(fromNumber, toNumber, floorId) +将某个或某些楼层中的所有某个图块替换成另一个图块 +fromNumber和toNumber为要被替换和替换到的数字。 +floorId可为某个楼层ID,或者一个楼层数组;如果不填只视为当前楼层。 +值得注意的是,使用此函数转了的点上的自定义事件可能无法被执行。 +如有需要,再对那些存在事件的点执行core.setBlock()即可 + + +core.setBgFgBlock(name, number, x, y, floorId) +设置前景/背景层的某个图块。name只能为'bg'或'fg'表示前景或背景层。 +number为要设置到的图块数字,x,y和floorId为目标点坐标和楼层,可忽略为当前点和当前楼层。 + + +core.resetMap(floorId) +重置某层或若干层的地图和楼层属性。 +floorId可为某个楼层ID,或者一个楼层数组(同时重置若干层);如果不填则只重置当前楼层。 + +// ------ 移动/跳跃图块,淡入淡出图块 ------ // + +core.moveBlock(x, y, steps, time, keep, callback) +移动一个图块,x和y为图块的坐标。 +steps为移动的数组,每一项只能是"up","down","left","right"之一。 +time为每一步的移动时间,不填默认为500ms。 +如果keep为真,则在移动完毕后将自动调用一个setBlock事件,改变目标点的图块(即不消失), +否则会按照time时间来淡出消失。callback会执行完毕后的回调。 + + +core.jumpBlock(sx, sy, ex, ey, time, keep, callback) +跳跃一个图块,sx和sy为图块的坐标,ex和ey为目标坐标。time为整个跳跃过程中的全程用时,不填默认500。 +如果keep为真,则在移动完毕后将自动调用一个setBlock事件,改变目标点的图块(即不消失), +否则会按照time时间来淡出消失。callback会执行完毕后的回调。 + + +core.animateBlock(loc, type, time, callback) +淡入/淡出一个或多个图块。 +loc为一个图块坐标,或者一个二维数组表示一系列图块坐标(将同时显示和隐藏)。 +type只能为'show'或'hide'表示是淡入但是淡出。time为动画时间,callback为执行完毕的回调。 + +// ------ 全局动画控制,动画的绘制 ------ // + +core.addGlobalAnimate(block) +添加一个全局帧动画。 + + +core.removeGlobalAnimate(x, y, name) +删除一个或全部的全局帧动画。name可为'bg',null或'fg'表示某个图层。 +x和y如果为null,则会删除全部的全局帧动画,否则只会删除该点的该层的帧动画。 + + +core.drawBoxAnimate() +绘制UI层的box动画,如怪物手册和对话框中的帧动画等。 + + +core.drawAnimate(name, x, y, callback) +绘制一个动画。name为动画名,x和y为绘制的基准坐标,callback为绘制完毕的回调函数。 +此函数将播放动画音效,并异步开始绘制该动画。 +此函数会返回一个动画id,可以通过core.stopAnimate()立刻停止该动画的播放。 + + +core.stopAnimate(id, doCallback) +立刻停止某个动画的播放。id为上面core.drawAnimate的返回值。 +如果doCallback为真,则会执行该动画所对应的回调函数。 +``` + ### ui.js + + ### utils.js diff --git a/libs/actions.js b/libs/actions.js index e6f3aff0..dfc55820 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1920,7 +1920,7 @@ actions.prototype._clickSettings = function (x, y) { core.ui.drawGameInfo(); break; case 6: - return core.confirmRestart(true); + return core.confirmRestart(); case 7: core.ui.closePanel(); break; diff --git a/libs/control.js b/libs/control.js index b932169b..d97498d8 100644 --- a/libs/control.js +++ b/libs/control.js @@ -349,29 +349,6 @@ control.prototype.isPlaying = function() { return core.status.played; } -////// 重新开始游戏;此函数将回到标题页面 ////// -control.prototype.restart = function() { - this.showStartAnimate(); - core.playBgm(main.startBgm); -} - -////// 询问是否需要重新开始 ////// -control.prototype.confirmRestart = function (fromSettings) { - core.status.event.selection = 1; - core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () { - core.ui.closePanel(); - core.restart(); - }, function () { - if (fromSettings) { - core.status.event.selection = 3; - core.ui.drawSettings(); - } - else { - core.ui.closePanel(); - } - }); -} - ////// 清除游戏状态和数据 ////// control.prototype.clearStatus = function() { // 停止各个Timeout和Interval diff --git a/libs/core.js b/libs/core.js index c3040df4..7a42dc4f 100644 --- a/libs/core.js +++ b/libs/core.js @@ -341,7 +341,7 @@ core.prototype._init_others = function () { core.prototype._afterLoadResources = function (callback) { // 初始化地图 - core.initStatus.maps = core.maps.initMaps(core.floorIds); + core.initStatus.maps = core.maps._initMaps(); core.control._setRequestAnimationFrame(); core._initPlugins(); core.showStartAnimate(); diff --git a/libs/events.js b/libs/events.js index 5b7ff3b9..9917f4df 100644 --- a/libs/events.js +++ b/libs/events.js @@ -234,6 +234,23 @@ events.prototype._gameOver_askRate = function (ending) { }); } +////// 重新开始游戏;此函数将回到标题页面 ////// +events.prototype.restart = function() { + core.showStartAnimate(); + core.playBgm(main.startBgm); +} + +////// 询问是否需要重新开始 ////// +events.prototype.confirmRestart = function () { + core.status.event.selection = 1; + core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () { + core.ui.closePanel(); + core.restart(); + }, function () { + core.ui.closePanel(); + }); +} + // ------ 系统事件的处理 ------ // ////// 注册一个系统事件 ////// @@ -279,10 +296,7 @@ events.prototype._trigger = function (x, y) { if (trigger == 'changeFloor' && !noPass && this._trigger_ignoreChangeFloor(block)) return; core.status.automaticRoute.moveDirectly = false; - this.doSystemEvent(trigger, block, function () { - if (trigger == 'openDoor' || trigger == 'changeFloor') - core.replay(); - }) + this.doSystemEvent(trigger, block); } } @@ -341,16 +355,20 @@ events.prototype.afterBattle = function (enemyId, x, y, callback) { } events.prototype._sys_openDoor = function (data, callback) { - this.openDoor(data.event.id, data.x, data.y, true, callback); + this.openDoor(data.x, data.y, true, function () { + core.replay(); + if (callback) callback(); + }); } ////// 开门 ////// -events.prototype.openDoor = function (id, x, y, needKey, callback) { - id = id || core.getBlockId(x, y); +events.prototype.openDoor = function (x, y, needKey, callback) { + var id = core.getBlockId(x, y); core.saveAndStopAutomaticRoute(); if (!this._openDoor_check(id, x, y, needKey)) { + var locked = core.status.lockControl; core.waitHeroToStop(function () { - core.unLockControl(); + if (!locked) core.unLockControl(); if (callback) callback(); }); return; @@ -376,7 +394,7 @@ events.prototype._openDoor_check = function (id, x, y, needKey) { core.clearContinueAutomaticRoute(); return false; } - core.autosave(true); + if (!core.status.event.id) core.autosave(true); core.removeItem(key); } return true; @@ -386,6 +404,7 @@ events.prototype._openDoor_animate = function (id, x, y, callback) { var door = core.material.icons.animates[id]; var speed = id.endsWith("Door") ? 30 : 70; + var locked = core.status.lockControl; core.lockControl(); core.status.replay.animate = true; var state = 0; @@ -394,7 +413,7 @@ events.prototype._openDoor_animate = function (id, x, y, callback) { if (state == 4) { clearInterval(animate); core.removeBlock(x, y); - core.unLockControl(); + if (!locked) core.unLockControl(); core.status.replay.animate = false; core.events.afterOpenDoor(id, x, y, callback); return; @@ -414,29 +433,33 @@ events.prototype._sys_getItem = function (data, callback) { } ////// 获得某个物品 ////// -events.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) { - itemNum = itemNum || 1; - var itemCls = core.material.items[itemId].cls; - core.items.getItemEffect(itemId, itemNum); - core.removeBlock(itemX, itemY); - var text = '获得 ' + core.material.items[itemId].name; - if (itemNum > 1) text += "x" + itemNum; - if (itemCls === 'items') text += core.items.getItemEffectTip(itemId); - core.drawTip(text, core.material.icons.items[itemId]); +events.prototype.getItem = function (id, num, x, y, callback) { + num = num || 1; + var itemCls = core.material.items[id].cls; + core.items.getItemEffect(id, num); + core.removeBlock(x, y); + var text = '获得 ' + core.material.items[id].name; + if (num > 1) text += "x" + num; + if (itemCls === 'items') text += core.items.getItemEffectTip(id); + core.drawTip(text, core.material.icons.items[id]); core.updateStatusBar(); - this.eventdata.afterGetItem(itemId, itemX, itemY, callback); + this.afterGetItem(id, x, y, callback); +} + +events.prototype.afterGetItem = function (id, x, y, callback) { + this.eventdata.afterGetItem(id, x, y, callback); } ////// 获得面前的物品(轻按) ////// -events.prototype.getNextItem = function () { +events.prototype.getNextItem = function (noRoute) { if (core.isMoving() || !core.canMoveHero() || !core.flags.enableGentleClick) return false; var nextX = core.nextX(), nextY = core.nextY(); var block = core.getBlock(nextX, nextY); if (block == null) return false; if (block.block.event.trigger == 'getItem') { - core.status.route.push("getNext"); + if (!noRoute) core.status.route.push("getNext"); this.getItem(block.block.event.id, 1, nextX, nextY); return true; } @@ -449,7 +472,10 @@ events.prototype._sys_changeFloor = function (data, callback) { if (data.loc) heroLoc = {'x': data.loc[0], 'y': data.loc[1]}; if (data.direction) heroLoc.direction = data.direction; if (core.status.event.id != 'action') core.status.event.id = null; - core.changeFloor(data.floorId, data.stair, heroLoc, data.time, callback); + core.changeFloor(data.floorId, data.stair, heroLoc, data.time, function () { + core.replay(); + if (callback) callback(); + }); } ////// 楼层切换 ////// @@ -461,6 +487,7 @@ events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback } info.fromLoad = fromLoad; floorId = info.floorId; + info.locked = core.status.lockControl; core.dom.floorNameLabel.innerHTML = core.status.maps[floorId].title; core.lockControl(); @@ -553,7 +580,7 @@ events.prototype._changeFloor_changing = function (info, callback) { } events.prototype._changeFloor_afterChange = function (info, callback) { - core.unLockControl(); + if (!info.locked) core.unLockControl(); core.status.replay.animate = false; core.events.afterChangeFloor(info.floorId, info.fromLoad); @@ -661,11 +688,6 @@ events.prototype._sys_changeLight = function (data, callback) { events.prototype.changeLight = function (id, x, y) { if (id != null && id != 'light') return; core.setBlock(core.getNumberById('darkLight'), x, y); - this.afterChangeLight(x, y); -} - -////// 改变亮灯之后,可以触发的事件 ////// -events.prototype.afterChangeLight = function (x, y) { return this.eventdata.afterChangeLight(x, y); } @@ -724,8 +746,19 @@ events.prototype.doEvent = function (data, x, y, prefix) { core.doAction(); } +events.prototype.setEvents = function (list, x, y, callback) { + var data = core.status.event.data || {}; + if (list) + data.list = [{todo: core.clone(list), total: core.clone(list), condition: "false"}]; + if (x != null) data.x = x; + if (y != null) data.y = y; + if (callback) data.callback = callback; + core.status.event.id = 'action'; + core.status.event.data = data; +} + ////// 开始执行一系列自定义事件 ////// -events.prototype.doEvents = function (list, x, y, callback) { +events.prototype.startEvents = function (list, x, y, callback) { if (!list) return; if (!(list instanceof Array)) { list = [list]; @@ -738,17 +771,6 @@ events.prototype.doEvents = function (list, x, y, callback) { }); } -events.prototype.setEvents = function (list, x, y, callback) { - var data = core.status.event.data || {}; - if (list) - data.list = [{todo: core.clone(list), total: core.clone(list), condition: "false"}]; - if (x != null) data.x = x; - if (y != null) data.y = y; - if (callback) data.callback = callback; - core.status.event.id = 'action'; - core.status.event.data = data; -} - ////// 执行当前自定义事件列表中的下一个事件 ////// events.prototype.doAction = function () { // 清空boxAnimate和UI层 @@ -799,7 +821,7 @@ events.prototype._popEvents = function (current, prefix) { return false; } -////// 往当前事件列表之前添加一个或多个事件 ////// +////// 往当前事件列表之前或之后添加一个或多个事件 ////// events.prototype.insertAction = function (action, x, y, callback, addToLast) { if (core.hasFlag("__statistics__")) return; if (core.status.gameOver) return; @@ -810,7 +832,7 @@ events.prototype.insertAction = function (action, x, y, callback, addToLast) { if (!action) return; if (core.status.event.id != 'action') { - this.doEvents(action, x, y, callback); + this.startEvents(action, x, y, callback); } else { if (addToLast) @@ -1022,10 +1044,7 @@ events.prototype._action_jumpHero = function (data, x, y, prefix) { events.prototype._action_changeFloor = function (data, x, y, prefix) { var loc = this.__action_getHeroLoc(data.loc, prefix); var heroLoc = {x: loc[0], y: loc[1], direction: data.direction}; - core.changeFloor(data.floorId || core.status.floorId, null, heroLoc, data.time, function () { - core.lockControl(); - core.doAction(); - }); + core.changeFloor(data.floorId || core.status.floorId, null, heroLoc, data.time, core.doAction); } events.prototype._action_changePos = function (data, x, y, prefix) { @@ -1101,10 +1120,7 @@ events.prototype._action_openDoor = function (data, x, y, prefix) { var loc = this.__action_getLoc(data.loc, x, y, prefix); var floorId = data.floorId || core.status.floorId; if (floorId == core.status.floorId) { - core.openDoor(null, loc[0], loc[1], data.needKey, function () { - core.lockControl(); - core.doAction(); - }); + core.openDoor(loc[0], loc[1], data.needKey, core.doAction); } else { core.removeBlock(loc[0], loc[1], floorId); @@ -1152,14 +1168,10 @@ events.prototype._action_trigger = function (data, x, y, prefix) { if (block != null && block.block.event.trigger) { block = block.block; this.setEvents([], block.x, block.y); - var _callback = function () { - core.lockControl(); - core.doAction(); - } if (block.event.trigger == 'action') this.setEvents(block.event.data); else { - core.doSystemEvent(block.event.trigger, block, _callback); + core.doSystemEvent(block.event.trigger, block, core.doAction); return; } } @@ -1703,24 +1715,6 @@ events.prototype.unfollow = function (name) { core.drawHero(); } -////// 绘制或取消一张gif图片 ////// -events.prototype.showGif = function (name, x, y) { - var image = core.material.images.images[name]; - if (image) { - var gif = new Image(); - gif.src = image.src; - gif.style.position = 'absolute'; - gif.style.left = x * core.domStyle.scale + "px"; - gif.style.top = y * core.domStyle.scale + "px"; - gif.style.width = image.width * core.domStyle.scale + "px"; - gif.style.height = image.height * core.domStyle.scale + "px"; - core.dom.gif2.appendChild(gif); - } - else { - core.dom.gif2.innerHTML = ""; - } -} - ////// 数值操作 ////// events.prototype.setValue = function (name, value, prefix, add) { var value = core.calValue(value, prefix); @@ -1924,6 +1918,24 @@ events.prototype._moveImage_moving = function (name, moveInfo, callback) { core.animateFrame.asyncId[animate] = true; } +////// 绘制或取消一张gif图片 ////// +events.prototype.showGif = function (name, x, y) { + var image = core.material.images.images[name]; + if (image) { + var gif = new Image(); + gif.src = image.src; + gif.style.position = 'absolute'; + gif.style.left = x * core.domStyle.scale + "px"; + gif.style.top = y * core.domStyle.scale + "px"; + gif.style.width = image.width * core.domStyle.scale + "px"; + gif.style.height = image.height * core.domStyle.scale + "px"; + core.dom.gif2.appendChild(gif); + } + else { + core.dom.gif2.innerHTML = ""; + } +} + ////// 淡入淡出音乐 ////// events.prototype.setVolume = function (value, time, callback) { var set = function (value) { @@ -2214,7 +2226,7 @@ events.prototype.afterUseBomb = function () { } ////// 上传当前数据 ////// -events.prototype.uploadCurrent = function (username) { +events.prototype._uploadCurrent = function (username) { var formData = new FormData(); formData.append('type', 'score'); diff --git a/libs/items.js b/libs/items.js index 23056d8d..8a89e7a7 100644 --- a/libs/items.js +++ b/libs/items.js @@ -196,20 +196,6 @@ items.prototype.setItem = function (itemId, itemNum) { core.updateStatusBar(); } -////// 删除某个物品 ////// -items.prototype.removeItem = function (itemId, itemNum) { - if (itemNum == null) itemNum = 1; - if (!core.hasItem(itemId)) return false; - var itemCls = core.material.items[itemId].cls; - core.status.hero.items[itemCls][itemId] -= itemNum; - if (core.status.hero.items[itemCls][itemId] <= 0) { - if (itemCls != 'keys') delete core.status.hero.items[itemCls][itemId]; - else core.status.hero.items[itemCls][itemId] = 0; - } - core.updateStatusBar(); - return true; -} - ////// 增加某个物品的个数 ////// items.prototype.addItem = function (itemId, itemNum) { if (itemNum == null) itemNum = 1; @@ -230,15 +216,22 @@ items.prototype.addItem = function (itemId, itemNum) { core.updateStatusBar(); } -// ---------- 装备相关 ------------ // - -items.prototype.getEquipTypeById = function (equipId) { - var type = core.material.items[equipId].equip.type; - if (typeof type == 'string') - type = this.getEquipTypeByName(type); - return type; +////// 删除某个物品 ////// +items.prototype.removeItem = function (itemId, itemNum) { + if (itemNum == null) itemNum = 1; + if (!core.hasItem(itemId)) return false; + var itemCls = core.material.items[itemId].cls; + core.status.hero.items[itemCls][itemId] -= itemNum; + if (core.status.hero.items[itemCls][itemId] <= 0) { + if (itemCls != 'keys') delete core.status.hero.items[itemCls][itemId]; + else core.status.hero.items[itemCls][itemId] = 0; + } + core.updateStatusBar(); + return true; } +// ---------- 装备相关 ------------ // + items.prototype.getEquipTypeByName = function (name) { var names = core.status.globalAttribute.equipName; for (var i = 0; i < names.length; ++i) { @@ -249,6 +242,13 @@ items.prototype.getEquipTypeByName = function (name) { return -1; } +items.prototype.getEquipTypeById = function (equipId) { + var type = core.material.items[equipId].equip.type; + if (typeof type == 'string') + type = this.getEquipTypeByName(type); + return type; +} + // 当前能否撞上某装备 items.prototype.canEquip = function (equipId, hint) { // 装备是否合法 diff --git a/libs/maps.js b/libs/maps.js index 4a974ced..f03bfe60 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -155,7 +155,8 @@ maps.prototype._addEvent = function (block, x, y, event) { } ////// 初始化所有地图 ////// -maps.prototype.initMaps = function (floorIds) { +maps.prototype._initMaps = function () { + var floorIds = core.floorIds; var maps = {}; for (var i = 0; i < floorIds.length; i++) { var floorId = floorIds[i]; @@ -242,7 +243,7 @@ maps.prototype.saveMap = function (floorId) { return map; } var map = maps[floorId], floor = core.floors[floorId]; - var blocks = this.getMapArray(map.blocks, floor.width, floor.height, true); + var blocks = this._getMapArrayFromBlocks(map.blocks, floor.width, floor.height, true); if (main.mode == 'editor') return blocks; var thisFloor = this._compressFloorData(map, floor); @@ -297,7 +298,13 @@ maps.prototype.resizeMap = function (floorId) { } ////// 将当前地图重新变成二维数组形式 ////// -maps.prototype.getMapArray = function (blockArray, width, height, checkDisable) { +maps.prototype.getMapArray = function (floorId, showDisable) { + floorId = floorId || core.status.floorId; + return this._getMapArrayFromBlocks(core.status.maps[floorId].blocks, + core.floors[floorId].width, core.floors[floorId].height, showDisable); +} + +maps.prototype._getMapArrayFromBlocks = function (blockArray, width, height, showDisable) { if (typeof blockArray == 'string') { var floorId = blockArray; blockArray = core.status.maps[floorId].blocks; @@ -313,11 +320,11 @@ maps.prototype.getMapArray = function (blockArray, width, height, checkDisable) blockArray.forEach(function (block) { var x = block.x, y = block.y; if (block.disable) { - if (checkDisable) blocks[y][x] = block.id + ":f"; + if (showDisable) blocks[y][x] = block.id + ":f"; } else { blocks[y][x] = block.id; - if (checkDisable && block.disable === false) + if (showDisable && block.disable === false) blocks[y][x] = block.id + ":t"; } }); @@ -336,7 +343,7 @@ maps.prototype.getMapBlocksObj = function (floorId, showDisable) { } ////// 将背景前景层变成二维数组的形式 ////// -maps.prototype.getBgFgMapArray = function (name, floorId, noCache) { +maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { floorId = floorId || core.status.floorId; if (!floorId) return []; var width = core.floors[floorId].width; @@ -363,25 +370,25 @@ maps.prototype.getBgFgMapArray = function (name, floorId, noCache) { } maps.prototype.getBgMapArray = function (floorId, noCache) { - return this.getBgFgMapArray('bg', floorId, noCache); + return this._getBgFgMapArray('bg', floorId, noCache); } maps.prototype.getFgMapArray = function (floorId, noCache) { - return this.getBgFgMapArray('fg', floorId, noCache); + return this._getBgFgMapArray('fg', floorId, noCache); } -maps.prototype.getBgFgNumber = function (name, x, y, floorId, noCache) { +maps.prototype._getBgFgNumber = function (name, x, y, floorId, noCache) { if (x == null) x = core.getHeroLoc('x'); if (y == null) y = core.getHeroLoc('y'); - return this.getBgFgMapArray(name, floorId, noCache)[y][x]; + return this._getBgFgMapArray(name, floorId, noCache)[y][x]; } maps.prototype.getBgNumber = function (x, y, floorId, noCache) { - return this.getBgFgNumber('bg', x, y, floorId, noCache); + return this._getBgFgNumber('bg', x, y, floorId, noCache); } maps.prototype.getFgNumber = function (x, y, floorId, noCache) { - return this.getBgFgNumber('fg', x, y, floorId, noCache); + return this._getBgFgNumber('fg', x, y, floorId, noCache); } // ------ 当前能否朝某方向移动,能否瞬间移动 ------ // @@ -701,7 +708,7 @@ maps.prototype._drawMap_drawAll = function (floorId) { maps.prototype._drawMap_drawBlockInfo = function (ctx, block, blockInfo, arr, onMap) { if (blockInfo == null) return; if (blockInfo.cls == 'autotile') { // Autotile单独处理 - this.drawAutotile(ctx, arr, block, 32, 0, 0); + this._drawAutotile(ctx, arr, block, 32, 0, 0); if (onMap) this.addGlobalAnimate(block); return; } @@ -716,15 +723,16 @@ maps.prototype._drawMap_drawBlockInfo = function (ctx, block, blockInfo, arr, on ////// 绘制背景层 ////// maps.prototype.drawBg = function (floorId, ctx) { + floorId = floorId || core.status.floorId; var onMap = ctx == null; if (onMap) { ctx = core.canvas.bg; core.clearMap(ctx); } - this._drawBg_drawBackground(floorId, ctx); + core.maps._drawBg_drawBackground(floorId, ctx); // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 - this._drawFloorImages(floorId, ctx, 'bg'); - this._drawBgFgMap(floorId, ctx, 'bg', onMap); + core.maps._drawFloorImages(floorId, ctx, 'bg'); + core.maps._drawBgFgMap(floorId, ctx, 'bg', onMap); } maps.prototype._drawBg_drawBackground = function (floorId, ctx) { @@ -744,7 +752,7 @@ maps.prototype._drawBg_drawBackground = function (floorId, ctx) { maps.prototype.drawEvents = function (floorId, blocks, ctx) { floorId = floorId || core.status.floorId; if (!blocks) blocks = core.status.maps[floorId].blocks; - var arr = this.getMapArray(blocks, core.floors[floorId].width, core.floors[floorId].height); + var arr = this._getMapArrayFromBlocks(blocks, core.floors[floorId].width, core.floors[floorId].height); var onMap = ctx == null; if (onMap) ctx = core.canvas.event; blocks.filter(function (block) { @@ -757,6 +765,7 @@ maps.prototype.drawEvents = function (floorId, blocks, ctx) { ////// 绘制前景层 ////// maps.prototype.drawFg = function (floorId, ctx) { + floorId = floorId || core.status.floorId; var onMap = ctx == null; if (onMap) ctx = core.canvas.fg; // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 @@ -774,7 +783,7 @@ maps.prototype._drawBgFgMap = function (floorId, ctx, name, onMap) { if (!core.status[name + "maps"]) core.status[name + "maps"] = {}; - var arr = this.getBgFgMapArray(name, floorId, true); + var arr = this._getBgFgMapArray(name, floorId, true); for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var block = this.initBlock(x, y, arr[y][x], true); @@ -866,7 +875,7 @@ maps.prototype._drawFloorImage = function (ctx, name, type, image, offsetX, widt } ////// 绘制Autotile ////// -maps.prototype.drawAutotile = function (ctx, mapArr, block, size, left, top, status) { +maps.prototype._drawAutotile = function (ctx, mapArr, block, size, left, top, status) { var indexArrs = [ //16种组合的图块索引数组; // 将autotile分割成48块16*16的小块; 数组索引即对应各个小块 // +----+----+----+----+----+----+ [10, 9, 4, 3], //0 bin:0000 | 1 | 2 | 3 | 4 | 5 | 6 | @@ -1231,7 +1240,7 @@ maps.prototype.searchBlock = function (id, floorId, showDisable) { for (var i = 0; i < core.status.maps[floorId].blocks.length; ++i) { var block = core.status.maps[floorId].blocks[i]; if (block.event.id == id && (showDisable || !block.disable)) - result.push({floorId: floorId, index: i, block: block}); + result.push({floorId: floorId, index: i, block: block, x: block.x, y: block.y}); } return result; } @@ -1317,19 +1326,6 @@ maps.prototype.removeBlockById = function (index, floorId) { } } -////// 能否彻底从地图中删除一个图块 ////// -maps.prototype.canRemoveBlock = function (block, floorId) { - var x = block.x, y = block.y; - // 检查该点是否存在事件 - if (core.floors[floorId].events[x + "," + y] || core.floors[floorId].changeFloor[x + "," + y]) - return false; - // 检查是否存在重生 - if (block.event && block.event.cls.indexOf('enemy') == 0 && core.hasSpecial(block.event.id, 23)) - return false; - - return true; -} - ////// 一次性删除多个block ////// maps.prototype.removeBlockByIds = function (floorId, ids) { floorId = floorId || core.status.floorId; @@ -1341,24 +1337,17 @@ maps.prototype.removeBlockByIds = function (floorId, ids) { }); } -////// 将地图中所有某个图块替换成另一个图块 ////// -maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) { - floorId = floorId || core.status.floorId; - if (floorId instanceof Array) { - floorId.forEach(function (floorId) { - core.replaceBlock(fromNumber, toNumber, floorId); - }); - return; - } - var toBlock = this.initBlock(0, 0, toNumber, true); - core.status.maps[floorId].blocks.forEach(function (block) { - if (block.id == fromNumber) { - block.id = toNumber; - for (var one in toBlock.event) { - block.event[one] = core.clone(toBlock.event[one]); - } - } - }); +////// 能否彻底从地图中删除一个图块 ////// +maps.prototype.canRemoveBlock = function (block, floorId) { + var x = block.x, y = block.y; + // 检查该点是否存在事件 + if (core.floors[floorId].events[x + "," + y] || core.floors[floorId].changeFloor[x + "," + y]) + return false; + // 检查是否存在重生 + if (block.event && block.event.cls.indexOf('enemy') == 0 && core.hasSpecial(block.event.id, 23)) + return false; + + return true; } ////// 显示前景/背景地图 ////// @@ -1436,6 +1425,7 @@ maps.prototype.setBlock = function (number, x, y, floorId) { floorId = floorId || core.status.floorId; if (!floorId || number == null || x == null || y == null) return; if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return; + if (typeof number == 'string') number = core.getNumberById(number); var originBlock = core.getBlock(x, y, floorId, true); var block = this.initBlock(x, y, number, true, core.floors[floorId]); @@ -1463,6 +1453,26 @@ maps.prototype.setBlock = function (number, x, y, floorId) { } } +////// 将地图中所有某个图块替换成另一个图块 ////// +maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) { + floorId = floorId || core.status.floorId; + if (floorId instanceof Array) { + floorId.forEach(function (floorId) { + core.replaceBlock(fromNumber, toNumber, floorId); + }); + return; + } + var toBlock = this.initBlock(0, 0, toNumber, true); + core.status.maps[floorId].blocks.forEach(function (block) { + if (block.id == fromNumber) { + block.id = toNumber; + for (var one in toBlock.event) { + block.event[one] = core.clone(toBlock.event[one]); + } + } + }); +} + ////// 改变前景背景的图块 ////// maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) { floorId = floorId || core.status.floorId; @@ -1776,16 +1786,16 @@ maps.prototype._animateBlock_drawList = function (list, opacity) { // ------ 全局动画控制,动画绘制 ------ // ////// 添加一个全局动画 ////// -maps.prototype.addGlobalAnimate = function (b) { - if (!b.event || b.event.animate == null) return; - if (b.event.cls == 'autotile') { - var id = b.event.id, img = core.material.images.autotile[id]; +maps.prototype.addGlobalAnimate = function (block) { + if (!block.event || block.event.animate == null) return; + if (block.event.cls == 'autotile') { + var id = block.event.id, img = core.material.images.autotile[id]; if (!img || img.width == 96) return; - core.status.autotileAnimateObjs.blocks.push(b); + core.status.autotileAnimateObjs.blocks.push(block); } else { - if (!b.event.animate || b.event.animate == 1) return; - core.status.globalAnimateObjs.push(b); + if (!block.event.animate || block.event.animate == 1) return; + core.status.globalAnimateObjs.push(block); } } diff --git a/libs/utils.js b/libs/utils.js index 26c8b8e2..d1a9f0c7 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -1080,7 +1080,7 @@ utils.prototype._export = function (floorIds) { // map var content = floorIds.length + "\n" + core.__SIZE__ + " " + core.__SIZE__ + "\n\n"; floorIds.forEach(function (floorId) { - var arr = core.maps.getMapArray(core.status.maps[floorId].blocks); + var arr = core.maps._getMapArrayFromBlocks(core.status.maps[floorId].blocks); content += arr.map(function (x) { // check monster x.forEach(function (t) { From b91e40c03763a9ab8a3db04e7f57818dc4aa8c38 Mon Sep 17 00:00:00 2001 From: oc Date: Wed, 3 Apr 2019 22:33:30 +0800 Subject: [PATCH 40/64] initPlugins & musicBtn & clearStatus --- libs/control.js | 5 ++--- libs/core.js | 2 +- libs/events.js | 5 ----- project/functions.js | 1 + 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/libs/control.js b/libs/control.js index d97498d8..c4b93ffa 100644 --- a/libs/control.js +++ b/libs/control.js @@ -323,8 +323,6 @@ control.prototype._showStartAnimate_resetDom = function () { core.dom.levelChooseButtons.style.display = 'none'; core.status.played = false; core.clearStatus(); - core.clearMap('all'); - core.deleteAllCanvas(); core.dom.musicBtn.style.display = 'block'; core.setMusicBtn(); // 重置音量 @@ -362,6 +360,7 @@ control.prototype.clearStatus = function() { } core.status = {}; core.clearStatusBar(); + core.clearMap('all'); core.deleteAllCanvas(); core.status.played = false; } @@ -2524,7 +2523,7 @@ control.prototype._resize_gameGroup = function (obj) { floorMsgGroup.style.color = obj.globalAttribute.floorChangingTextColor; // musicBtn if (core.domStyle.isVertical || core.domStyle.scale < 1) { - core.dom.musicBtn.style.right = core.dom.musicBtn.style.height = "3px"; + core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px"; } else { core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px"; diff --git a/libs/core.js b/libs/core.js index 7a42dc4f..97069384 100644 --- a/libs/core.js +++ b/libs/core.js @@ -219,6 +219,7 @@ core.prototype.init = function (coreData, callback) { this._init_flags(); this._init_platform(); this._init_others(); + this._initPlugins(); core.loader._load(function () { core._afterLoadResources(callback); @@ -343,7 +344,6 @@ core.prototype._afterLoadResources = function (callback) { // 初始化地图 core.initStatus.maps = core.maps._initMaps(); core.control._setRequestAnimationFrame(); - core._initPlugins(); core.showStartAnimate(); if (callback) callback(); } diff --git a/libs/events.js b/libs/events.js index 9917f4df..63438d3a 100644 --- a/libs/events.js +++ b/libs/events.js @@ -52,9 +52,6 @@ events.prototype._startGame_start = function (hard, seed, route, callback) { } else core.utils.__init_seed(); this.setInitData(); - - core.clearMap('all'); - core.deleteAllCanvas(); core.clearStatusBar(); var todo = []; @@ -1657,8 +1654,6 @@ events.prototype.load = function (fromUserAction) { if (!core.isPlaying()) { core.dom.startPanel.style.display = 'none'; core.clearStatus(); - core.clearMap('all'); - core.deleteAllCanvas(); core.status.event = {'id': 'load', 'data': null}; core.status.lockControl = true; core.ui.drawSLPanel(10*page+offset); diff --git a/project/functions.js b/project/functions.js index 07507e3d..4e4c0762 100644 --- a/project/functions.js +++ b/project/functions.js @@ -6,6 +6,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // hero:勇士信息;hard:难度;floorId:当前楼层ID;maps:地图信息;values:全局数值信息 // 清除游戏数据 + // 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布 core.clearStatus(); // 初始化status core.status = core.clone(core.initStatus); From 7fd843bf44389fb3e3d3a689d9d687f10c8480c7 Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 01:50:50 +0800 Subject: [PATCH 41/64] api docs --- _docs/api.md | 493 ++++++++++++++++++++++++++++++++++++++++++++- libs/actions.js | 10 +- libs/control.js | 4 +- libs/core.js | 2 + libs/events.js | 11 +- libs/items.js | 4 +- libs/ui.js | 121 ++++++----- libs/utils.js | 82 +++----- project/plugins.js | 22 +- 9 files changed, 625 insertions(+), 124 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index 8d085971..efe18684 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -215,7 +215,7 @@ core.events._changeFloor_beforeChange = function (info, callback) { if (info.time == 0) core.events._changeFloor_changing(info, callback); else - core.show(core.dom.floorMsgGroup, info.time / 2, function () { + core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { core.events._changeFloor_changing(info, callback); }); }, 25) @@ -724,6 +724,8 @@ name:自定义名称,可用户注销使用。 func:具体执行录像的函数,是一个函数体或者插件中的函数名。 func需要接受action参数,代表录像回放时的当前操作行为。 如果func返回true,则代表成功处理了此次操作,返回false代表没有进行处理。 +自己添加的录像项只能由数字、大小写、下划线线、冒号等符号组成,否则无法正常压缩和解压缩。 +对于自定义内容(比如中文文本或数组)请使用JSON.stringify再core.encodeBase64处理。 请注意回放录像时的二次记录问题(即回放时录像会重新记录路线)。 @@ -1842,6 +1844,495 @@ core.stopAnimate(id, doCallback) ### ui.js +ui.js负责一切UI界面的绘制。主要包括三个部分: +- 设置某个画布的属性的相关API +- 具体的某个UI界面的绘制 +- 动态创建画布相关的API +```js +// ------ 设置某个画布的属性的相关API ------// +这系列函数的name一般都是画布名,可以是系统画布或动态创建的画布。 +但也同时也允许直接传画布的context本身,将返回自身。 + + +core.getContextByName(name) +根据画布名找到一个画布的context;支持系统画布和自定义画布。 +如果不存在画布此函数返回null。 +该参数也可以直接传画布的context自身,则返回自己。 + + +core.clearMap(name) +清空某个画布图层。 +该函数的name也可以是'all',若为'all'则为清空所有系统画布。 + + +core.fillText(name, text, x, y, style, font) +在某个画布上绘制一段文字。 +text为要绘制的文本,x,y为要绘制的坐标,style可选为绘制的样式,font可选为绘制的字体。(下同) +请注意textAlign和textBaseline将决定绘制的左右对齐和上下对齐方式。 +具体可详见core.setTextAlign()和core.setTextBaseline()函数。 + + +core.fillBoldText(name, text, x, y, style, font) +在某个画布上绘制一个描黑边的文字。 + + +core.fillRect(name, x, y, width, height, style) +绘制一个矩形。style可选为绘制样式。如果设置将调用core.setFillStyle()。(下同) + + +core.strokeRect(name, x, y, width, height, style, lineWidth) +绘制一个矩形的边框。style可选为绘制样式,如果设置将调用core.setStrokeStyle()。 +lineWidth如果设置将调用core.setLineWidth()。(下同) + + +core.drawLine(name, x1, y1, x2, y2, style, lineWidth) +绘制一条线。 + + +core.drawArrow(name, x1, y1, x2, y2, style, lineWidth) +绘制一个箭头。 + + +core.setFont(name, font) / core.setLineWidth(name, lineWidth) +设置一个画布的字体/线宽。 + + +core.setAlpha(name, font) / core.setOpacity(name, font) +设置一个画布的绘制不透明度和画布本身的不透明度。 +两者区别如下: + - setAlpha是设置"接下来绘制的内容的不透明度",不会对已经绘制的内容产生影响。 + > 比如setAlpha('ui', 0.5)则会在接下来的绘制中使用0.5的不透明度。 + - setOpacity是设置"画布本身的不透明度",已经绘制的内容也会产生影响。 + > 比如我已经在UI层绘制了一段文字,再setOpacity则也会让已经绘制的文字变得透明。 +尽量不要对系统画布使用setOpacity(因为会对已经绘制的内容产生影响),自定义创建的画布则不受此限制。 + + +core.setFillStyle(name, style) / core.setStrokeStyle(name, style) +设置一个画布的填充样式/描边样式。 + + +core.setTextAlign(name, align) +设置一个画布的文字横向对齐模式,这里的align只能为'left', 'right'和'center'。 +默认为'left'。 + + +core.setTextBaseline(name, baseline) +设置一个画布的纵向对齐模式。有关textBaseline的说明可参见如下资料: +http://www.runoob.com/tags/canvas-textbaseline.html +默认值是'alphabetic'。 +请注意,系统画布在绘制前都是没有设置过textBaseline的,都是按照alphabetic进行坐标绘制。 +因此,如果你修改了系统画布的textBaseline来绘图,记得再绘制完毕后修改回来。 + + +core.calWidth(name, text, font) +计算一段文字在某个画布上的绘制宽度,此函数其实是ctx.measureText()的包装。 +font可选,如果存在则会先设置该画布上的字体。 +此函数不会对文字进行换行,如需换行版本请使用core.splitLines()函数。 + + +core.splitLines(name, text, maxWidth, font) +计算一段文字在某画布上换行分割的结果。 +text为文字内容,maxWidth为最大宽度,可为null表示不自动换行。 +font可选,如果存在则会先设置该画布上的字体。 +此函数将返回一个换行完毕的文本列表。 + + +core.drawImage(name, image, x, y, w, h, x1, y1, w1, h1) +在一张画布上绘制一张图片,此函数其实是ctx.drawImage()的包装。 +可以查看下面的文档以了解各项参数的信息: +http://www.w3school.com.cn/html5/canvas_drawimage.asp +这里的image允许传一个图片,画布。也允许传递图片名,将从你导入的图片中获取图片内容。 + +// ------ 具体的某个UI界面的绘制 ------ // +core.closePanel() +结束一切事件和UI绘制,关闭UI窗口,返回游戏。 +此函数将以此调用core.clearUI(),core.unlockControl(),并清空core.status.event里面的内容。 + + +core.clearUI() +重置UI窗口。此函数将清掉所有的UI帧动画和光标,清空UI画布,并将alpha设为1。 + + +core.drawTip(text, id) +在左上角以气泡的形式绘制一段提示。 +text为文字内容,仅支持${}的表达式计算,不支持换行和变色。 +id可选,为同时绘制的图标ID,如果不为null则会同时绘制该图标(仅对32x32的素材有效)。 + + +core.drawText(content, callback) +绘制一段文字。contents为一个字符串或一个字符串数组,callback为全部绘制完毕的回调。 +支持所有的文字效果(如\n,${},\r,\\i等),也支持\t和\b的语法。 +如果当前在事件处理中或录像回放中,则会自动转成core.insertAction处理。 +不建议使用该函数,如有绘制文字的需求请尽量使用core.insertAction()插入剧情文本事件。 + + +core.drawWindowSelector(background, x, y, w, h) +绘制一个WindowSkin的闪烁光标。background可为winskin的图片名或图片本身。 +x,y,w,h为绘制的左上角位置和宽高,都是像素为单位。 + + +core.drawWindowSkin(background, ctx, x, y, w, h, direction, px, py) +绘制一个WindowSkin。background可为winskin的图片名或图片本身。 +ctx为画布名或画布本身,x,y,w,h为左上角位置和宽高,都是像素为单位。 +direction, px, py可选,如果设置则会绘制一个对话框箭头。 + + +core.drawBackground(left, top, right, bottom, posInfo) +绘制一个背景图。此函数将使用你在【剧情文本设置】中设置的背景,即用纯色或WindowSkin来绘制。 +left, top, right, bottom为你要绘制的左上角和右下角的坐标。 +posInfo如果不为null则是一个含position, px和py的对象,表示一个对话框箭头的绘制。 + + +core.drawTextContent(ctx, content, config) +根据配置在某个画布上绘制一段文字。此函数会被core.drawTextBox()所调用。 +ctx为画布名或画布本身,如果不设置则会忽略该函数。 +content为要绘制的文字内容,支持所有的文字效果(如\n,${},\r,\\i等),但不支持支持\t和\b的语法。 +config为绘制的配置项,目前可以包括如下几项: + - left, top:在该画布上绘制的左上角像素位置,不设置默认为(0,0)。 + > 该函数绘制时会将textBaseline设置为'top',因此只需要考虑第一个字的左上角位置。 + - maxWidth:单行最大宽度,超过此宽度将自动换行,不设置不会自动换行。 + - color:默认颜色,为#XXXXXX形式。如果不设置则使用剧情文本设置中的正文颜色。 + - bold:是否粗体。如果不设置默认为false。 + - align:文字对齐方式,仅在maxWidth设置时有效,默认为'left'。 + - fontSize:字体大小,如果不设置则使用剧情文本设置中的正文字体大小。 + - lineHeight:绘制的行距值,如果不设置则使用fontSize*1.3(即1.3被行距)。 + - time:打字机效果。如果此项不为0,则会用打字机效果逐个字进行绘制,并设置core.status.event.interval定时器。 + + +core.drawTextBox(content, showAll) +绘制一个对话框。content为一个字符串或一个字符串数组。 +支持所有的文字效果(如\n,${},\r,\\i等),也支持\t和\b的语法。 +该函数将使用用户在剧情文本设置中的配置项进行绘制。 +实际执行时,会计算文本框宽度并绘制背景,绘制标题和头像,再调用core.drawTextContent()绘制正文内容。 +showAll可选,如果为true则不会使用打字机效果而全部显示,主要用于打字机效果的点击显示全部。 + + +core.drawScrollText(content, time, lineHeight, callback) +绘制一个滚动字幕。content为绘制内容,time为总滚动时间(默认为5000),lineHeight为行距比例(默认为1.4)。 +滚动字幕将绘制在UI上,支持所有的文字效果(如\n,${},\r,\\i等),但不支持\t和\b效果。 +可以通过剧情文本设置中的align控制是否居中绘制,offset控制其距离左边的偏移量。 + + +core.textImage(content, lineHeight) +将文本图片化。content为绘制内容,lineHeight为行距比例(默认为1.4)。 +可以通过剧情文本设置中的align控制是否居中绘制。 +此函数将返回一个临时画布的canvas,供转绘到其他画布上使用。 + + +core.drawChoices(content, choices) +绘制一个选项框。 +content可选,为选项上方的提示文字,支持所有的文字效果(如\n,${},\r,\\i等),也支持\t效果。 +choices必选,为要绘制的选项内容,是一个列表。其中的每一项: + - 可以是一个字符串,表示选项文字,将使用剧情文本设置中的正文颜色来绘制,仅支持${}表达式计算。 + - 或者是一个包含text, color和icon的对象。 + > text必选,为要绘制的文字内容,仅支持${}的表达式计算, + > color为要绘制的选项颜色,可以是#XXXXXX的格式或RGBA数组。不设置则使用正文颜色。 + > icon为要绘制的图标ID,支持使用素材的ID,或者系统图标。 + + +core.drawConfirmBox(text, yesCallback, noCallback) +绘制一个确认框。text为确认文字,支持\n换行(但不支持自动换行)和${}表达式计算。 +yesCallback和noCallback分别为确定和取消的回调函数。 +可以在调用此函数前设置core.status.event.selection来控制默认的选中项(0确定1取消)。 +此函数和core.myconfirm的区别主要在于: + - 此函数会清掉UI层原有的绘制信息,core.myconfirm不会清除。 + - 此函数可以用键盘进行操作,core.myconfirm必须用鼠标点击。 +另外请注意:本函数和事件流的执行不兼容,选择也不会进录像。 +如果需要在事件流中调用请使用提供选择项。 + + +core.drawWaiting(text) +绘制一个等待界面,一般用于同步存档之类的操作。 +调用此函数后,需要再调用core.closePanel()才会恢复游戏。 + + +core.drawPagination(page, totalPage, y) +绘制一个分页。y如果不设置则绘制在最后一行。 + + +core.drawBook(index) / core.drawBookDetail(index) +绘制怪物手册,绘制怪物的详细信息。 + + +core.drawFly(page) / core.drawCenterFly() / core.drawShop(shopId) +绘制楼传页面,中心对称飞行器,绘制商店 + + +core.drawToolbox(index) / core.drawEquipbox(index) / core.drawKeyBoard() +绘制道具栏,绘制装备栏,绘制虚拟键盘。 + + +core.drawMaps(index, x, y) / core.drawSLPanel(index, refresh) +绘制浏览地图,绘制存读档界面。 + + +core.drawStatusBar() +自定义绘制状态栏,仅在状态栏canvas化开启时有效,实际被转发到了脚本编辑中。 + + +core.drawStatistics() +绘制数据统计。将从脚本编辑中获得要统计的数据列表,再遍历所有地图进行统计。 + + +core.drawAbout() / core.drawPaint() / core.drawHelp() +绘制关于界面,绘图模式,帮助界面。 + +// ------ 动态创建画布相关的API ------ // + + +core.ui.createCanvas(name, x, y, width, height, z) +动态创建一个自定义画布。name为要创建的画布名,如果已存在则会直接取用当前存在的。 +x,y为创建的画布相对窗口左上角的像素坐标,width,height为创建的长宽。 +z值为创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。 +返回创建的画布的context,也可以通过core.dymCanvas[name]或core.getContextByName获得。 + + +core.ui.relocateCanvas(name, x, y) +重新定位一个自定义画布。x和y为画布的左上角坐标。 + + +core.ui.resizeCanvas(name, width, height) +重新设置一个自定义画布的大小。width和height为新设置的宽高。此操作会清空画布。 + + +core.ui.deleteCanvas(name) +删除一个自定义画布。 + + +core.ui.deleteAllCanvas() +删除所有的自定义画布。 +``` ### utils.js + +utils.js是一个工具函数库,里面有各个样板中使用到的工具函数。 + +```js +core.replayText(text, need, times) +将一段文字中的${}(表达式)进行替换。need和time一般可以直接忽略。 + + +core.calValue(value, prefix, need, time) +计算一个表达式的值,支持status:xxx等的计算。 +prefix为前缀(switch:xxx的独立开关使用),need和time一般可以直接忽略。 + + +core.unshift(a, b) / core.push(a, b) +将b插入到列表a之前或之后。b可以是一个元素或者一个数组。 + + +core.decompress(value) +解压缩一个字符串并进行JSON解析。value可能会被LZString或LZW算法进行压缩。 +返回解压后的JSON格式内容,如果无法解压则返回null。 + + +core.setLocalStorage(key, value) +将一个键值对存入localStorage中,会立刻返回结果。 +此函数会自动给key加上它的name作为前缀,以便于不同塔之间的区分。 +value为要存储的内容,存储前会被JSON转成字符串形式,并LZW算法进行压缩。 +如果value为null,则实际会调用core.removeLocalStorage()函数清除该key。 +localStorage为浏览器的默认存储,和存档无关,只有5M的空间大小。 +此函数立刻返回true或false,如果为false则代表存储失败,一般指的是空间满了。 + + +core.getLocalStorage(key, defaultValue) +从localStorage中获得一个键的值,会立刻返回结果。 +此函数会自动给key加上它的name作为前缀,以便于不同塔之间的区分。 +如果对应的键值不存在或为null,则会返回defaultValue。 +返回的结果会被调用core.decompress进行解压缩和JSON解析。 + + +core.removeLocalStorage(key) +从localStorage中删除一个键的值,会立刻返回。 +此函数会自动给key加上它的name作为前缀,以便于不同塔之间的区分。 + + +core.setLocalForage(key, value, successCallback, errorCallback) +将一个键值对存入localForage中,异步返回结果。 +如果用户没有开启【新版存档】开关,则仍然会使用core.setLocalStorage()。 +localForage为一个开源库,项目地址 https://github.com/localForage/localForage +一般存入的是indexedDB,这是一个浏览器的自带数据库,没有空间限制。 +successCallback和errorCallback均可选,表示该次存储成功或失败的回调, +此函数为异步的,只能通过回调函数来获得存储的成功或失败信息。 + + +core.getLocalForage(key, defaultValue, successCallback, errorCallback) +从localForage中获得一个键的值,异步返回结果。 +如果对应的键值不存在,则会使用defaultValue。 +如果获得成功,则会将core.decompress后的结果传入successCallback回调函数执行。 +errorCallback可选,如果失败,则会将错误信息传入errorCallback()。 +此函数是异步的,只能通过回调函数来获得读取的结果或错误信息。 + + +core.clone(data) +深拷贝一个对象。有关浅拷贝,深拷贝,基本类型和引用类型等相关知识可参见: +https://zhuanlan.zhihu.com/p/26282765 + + +core.splitImage(image, width, height) +等比例切分一张图片。width和height为每张子图片的宽高。 +请确保原始图片的宽度和高度都是是width和height的倍数。 +此函数将返回一个一维数组,每一项都是一张切分好的图片,横向再纵向排列。 +例如4x3的图片按1x1切分得到一个长度为12的数组,按如下方式进行排列: +0 1 2 3 +4 5 6 7 +8 9 10 11 +可以将很多需要的图片拼在一张大图上,然后在插件的_afterLoadResources中切分。 +切分好的图片再存入core.material.images.images中,这样可以少很多IO请求。 + + +core.formatDate(date) / core.formatDate2(date) / core.formatTime(time) +格式化日期和时间。 + + +core.setTwoDigits(x) +将x变成两位数。其实就是 parseInt(x) < 10 ? "0" + x : x + + +core.formatBigNumber(x, onMap) +大数据格式化。x为要格式化的内容,如果不合法会返回???。 +onMap标记是否在地图上调用,如果为真则尝试格式化成六位,否则五位。 + + +core.arrayToRGB(color) / core.arrayToRGBA(color) +将一个颜色数组,例如[255,0,0,1]转成#FF0000或rgba(255,0,0,1)的形式。 + + +core.encodeRoute(route) +录像压缩和解压缩。route为要压缩的路线数组。 +此函数将尽可能对录像进行压缩。对于无法识别的项目(比如自己添加的),将不压缩而原样放入。 +例如,["up","up","left","move:3:5","test:2333","getNext","item:bomb","down"] +将被压缩成"U2LM3:5(test:2333)GIbomb:D"。 +自己添加的录像项只能由数字、大小写、下划线线、冒号等符号组成,否则无法正常压缩和解压缩。 +对于自定义内容(比如中文文本或数组)请使用JSON.stringify再core.encodeBase64处理。 +压缩的结果将再次进行LZString.compressToBase64()的压缩以进一步节省空间。 + + +core.decodeRoute(route) +解压缩一个录像,返回解压完毕的路线数组。 + + +core.isset(v) +判定v是不是null, undefined或NaN。 +请尽量避免使用此函数,而是直接判定 v == null (请注意 null==undefined !) + + +core.subarray(a, b) +判定数组b是不是数组a的一个前缀子数组。 +如果是,则返回a中除去b后的剩余数组,否则返回null。 + + +core.inArray(array, element) +判定array是不是一个数组,以及element是否在该数组中。 + + +core.clamp(x, a, b) +将x限定在[a,b]区间内。 + + +core.getCookie(name) +获得一个cookie值,如果不存在该cookie则返回null。 + + +core.setStatusBarInnerHTML(name, value, css) +设置一个状态栏的innerHTML。此函数会自动设置状态栏的文字放缩和斜体等效果。 +name为状态栏的名称,如atk, def等。需要是core.statusBar中的一个合法项。 +value为要设置到的数值,如果是数字则会先core.formatBigNumber()进行格式化。 +css可选,为增添的额外css内容,比如可以设定颜色等。 + + +core.strlen(str) +计算某个字符串的实际长度。每个字符的长度,ASCII码视为1,中文等视为2。 + + +core.reverseDirection(direction) +翻转方向,即"up"转成"down", "left"转成"right"等。 + + +core.encodeBase64(str) / core.decodeBase64(str) +将字符串进行base64加密或解密。 + + +core.convertBase(str, fromBase, toBase) +任意进制转换。此函数可能执行的非常慢,慎用。 + + +core.rand(num) +使用伪种子生成伪随机数。该随机函数能被录像支持。 +num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到1之间的浮点数。 +此函数为伪随机算法,SL大法无效。(即多次SL后调用的该函数返回的值都是相同的。) + + +core.rand2(num) +使用系统的随机数算法得到的随机数。该随机函数能被录像支持。 +num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到2147483647之间的整数。 +此函数使用了系统的Math.random()函数,支持SL大法。 +但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 +对于需要大量生成随机数,但又想使用真随机支持SL大法的(例如随机生成地图等),可以采用如下方式: + var x = core.rand2(100); for (var i = 0; i < x; i++) core.rand() +即先生成一个真随机数,根据该数来推进伪随机的种子,这样就可以放心调用core.rand()啦。 + + +core.readFile(success, error) +读取一个本地文件内容。success和error分别为读取成功或失败的回调函数。 +iOS平台暂不支持读取文件操作。 + + +core.readFileContent(content) +读取到的文件内容。此函数会被APP等调用,来传递文件的具体内容。 + + +core.download(filename, content) +生成一个文件并下载。filename为文件名,content为具体的文件内容。 +iOS平台暂不支持下载文件操作。 + + +core.copy(data) +将一段内容拷贝到剪切板。 + + +core.myconfirm(hint, yesCallback, noCallback) +弹窗绘制一段提示信息并让用户确认。hint为提示信息。 +yesCallback和noCallback分别为确定和取消的回调函数。 +此函数和core.drawConfirmBox的区别主要在于: + - drawConfirmBox会清掉UI层原有的绘制信息,此函数不会清除。 + - drawConfirmBox可以用键盘进行操作,此函数必须用鼠标点击。 +另外请注意:本函数的选择也不会进录像,一般用于全局的提示。 +如果需要在事件流中调用请使用显示确认框或显示选择项。 + + +core.myprompt(hint, value, callback) +弹窗让用户输入一段内容。hint为提示信息,value为框内的默认填写内容。 +callback为用户点击确认或取消后的回调。 +如果用户点击了确认,则会把框内的内容(可能是空串)传递给callback,否则把null传递给callback。 + + +core.showWithAnimate(obj, speed, callback) / core.hideWithAnimate(obj, speed, callback) +动画淡入或淡出一个对象。 + + +core.consoleOpened() +检测当前的控制台是否处于开启状态。仅在全塔属性中的检查控制台开关开启时有效。 +此函数有可能会存在误伤行为,即没开过控制台仍认为开启过。 + + +core.hashCode(obj) +计算一个对象的哈希值。 + + +core.same(a, b) +判定a和b是否相同,包括类型相同和值相同。 +如果a和b都是数组,则会递归依次比较数组中的值;如果都是对象亦然。 + + +core.utils.http(type, url, formData, success, error, mimeType, responseType) +发送一个异步HTTP请求。 +type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 +success为成功后的回调,error为失败后的回调。 +mimeType和responseType如果设置将会覆盖默认值。 + + +lzw_encode(s) / lzw_decode(s) +LZW压缩算法,来自https://gist.github.com/revolunet/843889 +``` diff --git a/libs/actions.js b/libs/actions.js index dfc55820..2d516b52 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1904,11 +1904,11 @@ actions.prototype._clickSettings = function (x, y) { core.ui.drawKeyBoard(); break; case 2: - core.clearSelector(); + core.clearUI(); core.ui.drawMaps(); break; case 3: - core.clearSelector(); + core.clearUI(); core.ui.drawPaint(); break; case 4: @@ -2211,7 +2211,7 @@ actions.prototype._clickReplay_fromBeginning = function () { actions.prototype._clickReplay_fromLoad = function () { core.status.event.id = 'replayLoad'; core.status.event.selection = null; - core.ui.clearSelector(); + core.clearUI(); var saveIndex = core.saves.saveIndex; var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; core.ui.drawSLPanel(10 * page + offset); @@ -2450,7 +2450,7 @@ actions.prototype._onupPaint = function () { core.status.event.data.x = null; core.status.event.data.y = null; // 保存 - core.paint[core.status.floorId] = lzw_encode(core.utils.encodeCanvas(core.dymCanvas.paint).join(",")); + core.paint[core.status.floorId] = lzw_encode(core.utils._encodeCanvas(core.dymCanvas.paint).join(",")); } actions.prototype.setPaintMode = function (mode) { @@ -2498,7 +2498,7 @@ actions.prototype.loadPaint = function () { core.clearMap('paint'); var value = core.paint[core.status.floorId]; if (value) value = lzw_decode(value).split(","); - core.utils.decodeCanvas(value, 32 * core.bigmap.width, 32 * core.bigmap.height); + core.utils._decodeCanvas(value, 32 * core.bigmap.width, 32 * core.bigmap.height); core.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0); core.drawTip("读取绘图文件成功"); diff --git a/libs/control.js b/libs/control.js index c4b93ffa..f0de8ee1 100644 --- a/libs/control.js +++ b/libs/control.js @@ -308,7 +308,7 @@ control.prototype.showStartAnimate = function (noAnimate, callback) { this._showStartAnimate_resetDom(); if (core.flags.startUsingCanvas || noAnimate) return this._showStartAnimate_finished(core.flags.startUsingCanvas, callback); - core.hide(core.dom.startTop, 20, function () { + core.hideWithAnimate(core.dom.startTop, 20, function () { core.control._showStartAnimate_finished(false, callback); }); } @@ -339,7 +339,7 @@ control.prototype._showStartAnimate_finished = function (start, callback) { ////// 隐藏游戏开始界面 ////// control.prototype.hideStartAnimate = function (callback) { - core.hide(core.dom.startPanel, 20, callback); + core.hideWithAnimate(core.dom.startPanel, 20, callback); } ////// 游戏是否已经开始 ////// diff --git a/libs/core.js b/libs/core.js index 97069384..3f1165b6 100644 --- a/libs/core.js +++ b/libs/core.js @@ -344,6 +344,8 @@ core.prototype._afterLoadResources = function (callback) { // 初始化地图 core.initStatus.maps = core.maps._initMaps(); core.control._setRequestAnimationFrame(); + if (core.plugin._afterLoadResources) + core.plugin._afterLoadResources(); core.showStartAnimate(); if (callback) callback(); } diff --git a/libs/events.js b/libs/events.js index 63438d3a..6b3d5beb 100644 --- a/libs/events.js +++ b/libs/events.js @@ -438,7 +438,7 @@ events.prototype.getItem = function (id, num, x, y, callback) { var text = '获得 ' + core.material.items[id].name; if (num > 1) text += "x" + num; if (itemCls === 'items') text += core.items.getItemEffectTip(id); - core.drawTip(text, core.material.icons.items[id]); + core.drawTip(text, id); core.updateStatusBar(); this.afterGetItem(id, x, y, callback); @@ -559,7 +559,7 @@ events.prototype._changeFloor_beforeChange = function (info, callback) { if (info.time == 0) core.events._changeFloor_changing(info, callback); else - core.show(core.dom.floorMsgGroup, info.time / 2, function () { + core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { core.events._changeFloor_changing(info, callback); }); }, 25) @@ -571,7 +571,7 @@ events.prototype._changeFloor_changing = function (info, callback) { if (info.time == 0) this._changeFloor_afterChange(info, callback); else - core.hide(core.dom.floorMsgGroup, info.time / 4, function () { + core.hideWithAnimate(core.dom.floorMsgGroup, info.time / 4, function () { core.events._changeFloor_afterChange(info, callback); }); } @@ -771,10 +771,7 @@ events.prototype.startEvents = function (list, x, y, callback) { ////// 执行当前自定义事件列表中的下一个事件 ////// events.prototype.doAction = function () { // 清空boxAnimate和UI层 - core.status.boxAnimateObjs = []; - clearInterval(core.status.event.interval); - core.status.event.interval = null; - core.clearSelector(); + core.clearUI(); // 判定是否执行完毕 if (this._doAction_finishEvents()) return; // 当前点坐标和前缀 diff --git a/libs/items.js b/libs/items.js index 8a89e7a7..6c13e824 100644 --- a/libs/items.js +++ b/libs/items.js @@ -365,8 +365,8 @@ items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { core.status.hero.equipment[type] = loadId || null; // --- 提示 - if (loadId) core.drawTip("已装备上" + loadEquip.name, core.material.icons.items[loadId]); - else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, core.material.icons.items[unloadId]); + if (loadId) core.drawTip("已装备上" + loadEquip.name, loadId); + else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, unloadId); if (callback) callback(); } diff --git a/libs/ui.js b/libs/ui.js index 6c887f92..6c4501e2 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -23,12 +23,13 @@ ui.prototype._init = function () { ////////////////// 地图设置 -ui.prototype.getContextByName = function (canvas) { - if (typeof canvas == 'string') { - if (core.canvas[canvas]) - canvas = core.canvas[canvas]; - else if (core.dymCanvas[canvas]) - canvas = core.dymCanvas[canvas]; +ui.prototype.getContextByName = function (name) { + var canvas = name; + if (typeof name == 'string') { + if (core.canvas[name]) + canvas = core.canvas[name]; + else if (core.dymCanvas[name]) + canvas = core.dymCanvas[name]; } if (canvas && canvas.canvas) { return canvas; @@ -190,6 +191,36 @@ ui.prototype.calWidth = function (name, text, font) { return 0; } +////// 字符串自动换行的分割 ////// +ui.prototype.splitLines = function (name, text, maxWidth, font) { + var ctx = this.getContextByName(name); + if (!ctx) return; + if (font) core.setFont(name, font); + + var contents = []; + var last = 0; + for (var i = 0; i < text.length; i++) { + if (text.charAt(i) == '\n') { + contents.push(text.substring(last, i)); + last = i + 1; + } + else if (text.charAt(i) == '\\' && text.charAt(i + 1) == 'n') { + contents.push(text.substring(last, i)); + last = i + 2; + } + else { + var toAdd = text.substring(last, i + 1); + var width = core.calWidth(name, toAdd); + if (maxWidth && width > maxWidth) { + contents.push(text.substring(last, i)); + last = i; + } + } + } + contents.push(text.substring(last)); + return contents; +} + ////// 绘制一张图片 ////// ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1) { var ctx = this.getContextByName(name); @@ -233,25 +264,23 @@ ui.prototype.clearUI = function () { clearInterval(core.status.event.interval); core.status.event.interval = null; core.status.boxAnimateObjs = []; - core.clearSelector(); - core.clearMap('ui'); - core.setAlpha('ui', 1); -} - -////// 清除光标 ////// -ui.prototype.clearSelector = function () { if (core.dymCanvas._selector) core.deleteCanvas("_selector"); core.clearMap('ui'); core.setAlpha('ui', 1); } ////// 左上角绘制一段提示 ////// -ui.prototype.drawTip = function (text, itemIcon) { +ui.prototype.drawTip = function (text, id) { var textX, textY, width, height; clearInterval(core.interval.tipAnimate); core.setFont('data', "16px Arial"); core.setTextAlign('data', 'left'); - if (!itemIcon) { + if (id != null) { + var info = core.getBlockInfo(id); + if (info == null || !info.image || info.height != 32) id = null; + else id = info; + } + if (!id) { textX = 16; textY = 18; width = textX + core.calWidth('data', text) + 16; @@ -263,10 +292,10 @@ ui.prototype.drawTip = function (text, itemIcon) { width = textX + core.calWidth('data', text) + 8; height = 42; } - this._drawTip_animate(text, itemIcon, textX, textY, width, height); + this._drawTip_animate(text, id, textX, textY, width, height); } -ui.prototype._drawTip_animate = function (text, itemIcon, textX, textY, width, height) { +ui.prototype._drawTip_animate = function (text, info, textX, textY, width, height) { var alpha = 0, hide = false; core.interval.tipAnimate = window.setInterval(function () { if (hide) alpha -= 0.1; @@ -274,8 +303,8 @@ ui.prototype._drawTip_animate = function (text, itemIcon, textX, textY, width, h core.clearMap('data', 5, 5, core.ui.PIXEL, height); core.setAlpha('data', alpha); core.fillRect('data', 5, 5, width, height, '#000'); - if (itemIcon) - core.drawImage('data', core.material.images.items, 0, itemIcon * 32, 32, 32, 10, 8, 32, 32); + if (info) + core.drawImage('data', info.image, info.posX * 32, info.posY * 32, 32, 32, 10, 8, 32, 32); core.fillText('data', text, textX + 5, textY + 15, '#fff'); core.setAlpha('data', 1); if (alpha > 0.6 || alpha < 0) { @@ -507,13 +536,13 @@ ui.prototype.drawBackground = function (left, top, right, bottom, posInfo) { } ////// 计算有效文本框的宽度 -ui.prototype.calTextBoxWidth = function (ctx, content, min_width, max_width, font) { +ui.prototype._calTextBoxWidth = function (ctx, content, min_width, max_width, font) { // 无限长度自动换行 var allLines = core.splitLines(ctx, content, null, font); // 如果不存在手动换行,尽量调成半行形式 if (allLines.length == 1) { - var w = core.calWidth(ctx, allLines[0]); + var w = core.calWidth(ctx, allLines[0]) + 5; if (w 1) - core.fillText('ui', '上一页', this.HPIXEL - 80, top*32+19); + core.fillText('ui', '上一页', this.HPIXEL - 80, y*32+19); if (page < totalPage) - core.fillText('ui', '下一页', this.HPIXEL + 80, top*32+19); + core.fillText('ui', '下一页', this.HPIXEL + 80, y*32+19); } ////// 绘制键盘光标 ////// @@ -1633,7 +1662,7 @@ ui.prototype.drawMaps = function (index, x, y) { var offsetX = 32 * (data.x - this.HSIZE), offsetY = 32 * (data.y - this.HSIZE); var value = core.paint[data.floorId]; if (value) value = lzw_decode(value).split(","); - core.utils.decodeCanvas(value, 32 * data.mw, 32 * data.mh); + core.utils._decodeCanvas(value, 32 * data.mw, 32 * data.mh); core.drawImage('ui', core.bigmap.tempCanvas.canvas, offsetX * 32, offsetY * 32, this.PIXEL, this.PIXEL, 0, 0, this.PIXEL, this.PIXEL); } @@ -2359,13 +2388,13 @@ ui.prototype._drawPaint_draw = function () { core.status.event.id = 'paint'; core.status.event.data = {"x": null, "y": null, "erase": false}; - core.clearSelector(); + core.clearUI(); core.createCanvas('paint', -core.bigmap.offsetX, -core.bigmap.offsetY, 32*core.bigmap.width, 32*core.bigmap.height, 95); // 将已有的内容绘制到route上 var value = core.paint[core.status.floorId]; if (core.isset(value)) value = lzw_decode(value).split(","); - core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); + core.utils._decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); core.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0); core.setLineWidth('paint', 3); @@ -2384,7 +2413,7 @@ ui.prototype._drawPaint_draw = function () { ////// 绘制帮助页面 ////// ui.prototype.drawHelp = function () { - core.clearSelector(); + core.clearUI(); if (core.material.images.keyboard) { core.status.event.id = 'help'; core.lockControl(); diff --git a/libs/utils.js b/libs/utils.js index d1a9f0c7..d491197e 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -75,35 +75,6 @@ utils.prototype.calValue = function (value, prefix, need, times) { return value; } -////// 字符串自动换行的分割 ////// -utils.prototype.splitLines = function (canvas, text, maxLength, font) { - if (font) core.setFont(canvas, font); - - var contents = []; - var last = 0; - for (var i = 0; i < text.length; i++) { - - if (text.charAt(i) == '\n') { - contents.push(text.substring(last, i)); - last = i + 1; - } - else if (text.charAt(i) == '\\' && text.charAt(i + 1) == 'n') { - contents.push(text.substring(last, i)); - last = i + 2; - } - else { - var toAdd = text.substring(last, i + 1); - var width = core.calWidth(canvas, toAdd); - if (maxLength && width > maxLength) { - contents.push(text.substring(last, i)); - last = i; - } - } - } - contents.push(text.substring(last)); - return contents; -} - ////// 向某个数组前插入另一个数组或元素 ////// utils.prototype.unshift = function (a, b) { if (!(a instanceof Array) || b == null) return; @@ -128,6 +99,28 @@ utils.prototype.push = function (a, b) { return a; } +utils.prototype.decompress = function (value) { + try { + var output = lzw_decode(value); + if (output) return JSON.parse(output); + } + catch (e) { + } + try { + var output = LZString.decompress(value); + if (output) return JSON.parse(output); + } + catch (e) { + } + try { + return JSON.parse(value); + } + catch (e) { + main.log(e); + } + return null; +} + ////// 设置本地存储 ////// utils.prototype.setLocalStorage = function (key, value) { try { @@ -163,28 +156,6 @@ utils.prototype.setLocalStorage = function (key, value) { } } -utils.prototype.decompress = function (value) { - try { - var output = lzw_decode(value); - if (output) return JSON.parse(output); - } - catch (e) { - } - try { - var output = LZString.decompress(value); - if (output) return JSON.parse(output); - } - catch (e) { - } - try { - return JSON.parse(value); - } - catch (e) { - main.log(e); - } - return null; -} - ////// 获得本地存储 ////// utils.prototype.getLocalStorage = function (key, defaultValue) { var res = this.decompress(localStorage.getItem(core.firstData.name + "_" + key)); @@ -628,6 +599,7 @@ utils.prototype.getCookie = function (name) { ////// 设置statusBar的innerHTML,会自动斜体和放缩,也可以增加自定义css ////// utils.prototype.setStatusBarInnerHTML = function (name, value, css) { + if (!core.statusBar[name]) return; if (typeof value == 'number') value = this.formatBigNumber(value); // 判定是否斜体 var italic = /^[-a-zA-Z0-9`~!@#$%^&*()_=+\[{\]}\\|;:'",<.>\/?]*$/.test(value); @@ -931,7 +903,7 @@ utils.prototype.myprompt = function (hint, value, callback) { } ////// 动画显示某对象 ////// -utils.prototype.show = function (obj, speed, callback) { +utils.prototype.showWithAnimate = function (obj, speed, callback) { obj.style.display = 'block'; if (!speed && main.mode != 'play') { obj.style.opacity = 1; @@ -951,7 +923,7 @@ utils.prototype.show = function (obj, speed, callback) { } ////// 动画使某对象消失 ////// -utils.prototype.hide = function (obj, speed, callback) { +utils.prototype.hideWithAnimate = function (obj, speed, callback) { if (!speed || main.mode != 'play') { obj.style.display = 'none'; if (callback) callback(); @@ -970,7 +942,7 @@ utils.prototype.hide = function (obj, speed, callback) { }, speed); } -utils.prototype.encodeCanvas = function (ctx) { +utils.prototype._encodeCanvas = function (ctx) { var list = []; var width = ctx.canvas.width, height = ctx.canvas.height; ctx.mozImageSmoothingEnabled = false; @@ -997,7 +969,7 @@ utils.prototype.encodeCanvas = function (ctx) { } ////// 解析arr数组,并绘制到tempCanvas上 ////// -utils.prototype.decodeCanvas = function (arr, width, height) { +utils.prototype._decodeCanvas = function (arr, width, height) { // 清空tempCanvas var tempCanvas = core.bigmap.tempCanvas; tempCanvas.canvas.width = width; diff --git a/project/plugins.js b/project/plugins.js index 3f0afa16..85ca3bfa 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -1,18 +1,28 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { "init": function () { - // 在这里写的代码,在所有模块加载完毕后,游戏开始前会被执行 + console.log("插件编写测试"); - // 可以写一些其他的被直接执行的代码 + // 可以写一些直接执行的代码 + // 在这里写的代码将会在【资源加载前】被执行,此时图片等资源尚未被加载。 + // 请勿在这里对包括bgm,图片等资源进行操作。 - this.test = function () { - console.log("插件函数执行测试"); - console.log(this); + this._afterLoadResources = function () { + // 本函数将在所有资源加载完毕后,游戏开启前被执行 + // 可以在这个函数里面对资源进行一些操作,比如切分图片等。 + + // 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。 + // var arr = core.splitImage("assets.png", 32, 32); + // for (var i = 0; i < arr.length; i++) { + // core.material.images.images["asset"+i+".png", arr[i]); + // } + } - // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + // 从V2.6开始,插件中用this.XXX方式定义的函数也会被转发到core中,详见文档-脚本-函数的转发。 }, "drawLight": function () { From bfa3f3e075c57e9bc109d75116de91fbb1b557e8 Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Wed, 3 Apr 2019 13:54:19 -0400 Subject: [PATCH 42/64] confirm_s --- _server/MotaAction.g4 | 22 ++++++++++++++++++++++ _server/editor_blockly.js | 2 ++ 2 files changed, 24 insertions(+) diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index bb3e0f44..9fd002f3 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -331,6 +331,7 @@ action | input_s | input2_s | choices_s + | confirm_s | callBook_s | callSave_s | callLoad_s @@ -1658,6 +1659,20 @@ var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\ return code; */; +confirm_s + : '显示确认框' ':' EvalString BGNL? '确定' ':' BGNL? Newline action+ '取消' ':' BGNL? Newline action+ BEND Newline + +/* confirm_s +tooltip : 弹出确认框 +helpUrl : https://h5mota.com/games/template/docs/#/ +default : ["确认要???吗?"] +var code = ['{"type": "confirm", "text": "',EvalString_0,'",\n', + '"yes": [\n',action_0,'],\n', + '"no": [\n',action_1,']\n', +'},\n'].join(''); +return code; +*/; + while_s : '循环处理' ':' '当' expression '时' BGNL? Newline action+ BEND Newline @@ -2570,6 +2585,13 @@ ActionParser.prototype.parseAction = function() { this.insertActionList(data["false"]), this.next]); break; + case "confirm": // 显示确认框 + this.next = MotaActionBlocks['confirm_s'].xmlText([ + this.EvalString(data.text), + this.insertActionList(data["yes"]), + this.insertActionList(data["no"]), + this.next]); + break; case "switch": // 多重条件分歧 var case_caseList = null; for(var ii=data.caseList.length-1,caseNow;caseNow=data.caseList[ii];ii--) { diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 595a55ff..0f05135b 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -76,6 +76,7 @@ editor_blockly = function () { ]) ]) ]), + MotaActionBlocks['confirm_s'].xmlText(), ], '数据相关':[ MotaActionBlocks['setValue_s'].xmlText([ @@ -589,6 +590,7 @@ function omitedcheckUpdateFunction(event) { 'showTextImage_s': 'EvalString_0', 'function_s': 'RawEvalString_0', 'shopsub': 'EvalString_3', + 'confirm_s': 'EvalString_0', } var f = b ? textStringDict[b.type] : null; if (f) { From d9002596ab249120c5395f5fb17596967c3d646e Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 01:59:31 +0800 Subject: [PATCH 43/64] Fix plugin --- project/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.js b/project/plugins.js index 85ca3bfa..d09e9944 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -16,7 +16,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = // 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。 // var arr = core.splitImage("assets.png", 32, 32); // for (var i = 0; i < arr.length; i++) { - // core.material.images.images["asset"+i+".png", arr[i]); + // core.material.images.images["asset"+i+".png"] = arr[i]; // } } From a46d322e0c55462fd46d6cce03a4c9b9fa3db6c3 Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 02:03:38 +0800 Subject: [PATCH 44/64] replace ```js --- _docs/api.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index efe18684..9703764b 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -425,7 +425,7 @@ core.doFunc(func, _this) actions.js主要是处理一些和用户交互相关的内容。 -``` js +```text core.registerAction(action, name, func, priority) 注册一个用户交互行为。 action:要注册的交互类型,如 ondown, onclick, keyDown 等等。 @@ -951,7 +951,7 @@ core.resize() enemys.js中定义了一系列和怪物相关的API函数。 -```js +```text core.hasSpecial(special, test) 判断是否含有某个特殊属性。test为要检查的特殊属性编号。 special为要测试的内容,允许接收如下类型参数: @@ -1043,7 +1043,7 @@ events.js将处理所有和事件相关的操作,主要分为五个部分: - 一些具体事件的执行内容 -```js +```text // ------ 游戏的开始和结束 ------ // core.resetGame(hero, hard, floorId, maps, values) @@ -1374,7 +1374,7 @@ core.afterUseBomb() icons.js主要是负责素材相关信息,比如某个素材在对应的图片上的位置。 -```js +```text core.getClsFromId(id) 根据某个素材的ID获得该素材的cls @@ -1388,7 +1388,7 @@ core.getTilesetOffset(id) items.js主要负责一切和道具相关的内容。 -```js +```text core.getItemEffect(itemId, itemNum) 即捡即用类的道具获得时的效果。实际对应道具图块属性中的itemEffect框。 @@ -1478,7 +1478,7 @@ core.quickLoadEquip() loader.js主要负责资源加载相关的内容。 -```js +```text core.loadImage(imgName, callback) 从 project/images/ 中加载一张图片。imgName为图片名。 callback为执行完毕的回调函数,接收(imgName, image)即图片名和图片内容作为参数。 @@ -1520,7 +1520,7 @@ maps.js负责一切和地图相关的处理内容,包括如下几个方面: - 移动/跳跃图块,淡入淡出图块 - 全局动画控制,动画的绘制 -```js +```text // ------ 地图的初始化,保存和读取,地图数组的生成 ------ // core.loadFloor(floorId, map) @@ -1849,7 +1849,7 @@ ui.js负责一切UI界面的绘制。主要包括三个部分: - 具体的某个UI界面的绘制 - 动态创建画布相关的API -```js +```text // ------ 设置某个画布的属性的相关API ------// 这系列函数的name一般都是画布名,可以是系统画布或动态创建的画布。 但也同时也允许直接传画布的context本身,将返回自身。 @@ -2108,7 +2108,7 @@ core.ui.deleteAllCanvas() utils.js是一个工具函数库,里面有各个样板中使用到的工具函数。 -```js +```text core.replayText(text, need, times) 将一段文字中的${}(表达式)进行替换。need和time一般可以直接忽略。 From 77e4e405b817c81725988deb8f95917a077291b1 Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 02:05:20 +0800 Subject: [PATCH 45/64] plugin --- _docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/api.md b/_docs/api.md index 9703764b..4783aaaa 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -2,7 +2,7 @@ ?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * -在V2.6版本中,基本对整个项目代码进行了重写,更加方便 +在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。 ## 控制台的使用 From 9b5acbcdc4ef4b33fa16bbebbf531a679cb2c87a Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Wed, 3 Apr 2019 18:21:48 -0400 Subject: [PATCH 46/64] nobreak --- _server/MotaAction.g4 | 9 +++++---- _server/editor_blockly.js | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 9fd002f3..05014715 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -1639,13 +1639,13 @@ return code; */; choicesContext - : '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour BGNL? Newline action+ + : '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour '不跳出' Bool BGNL? Newline action+ /* choicesContext tooltip : 选项的选择 helpUrl : https://h5mota.com/games/template/docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 -default : ["提示文字:红钥匙","",""] +default : ["提示文字:红钥匙","","",null,false] colour : this.subColor if (EvalString_1) { var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/; @@ -1655,7 +1655,8 @@ if (EvalString_1) { EvalString_1 = ', "color": "'+EvalString_1+'"'; } IdString_0 = IdString_0?(', "icon": "'+IdString_0+'"'):''; -var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']},\n'; +var nobreak = Bool_0?', "nobreak": true':''; +var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']'+nobreak+'},\n'; return code; */; @@ -2607,7 +2608,7 @@ ActionParser.prototype.parseAction = function() { var text_choices = null; for(var ii=data.choices.length-1,choice;choice=data.choices[ii];ii--) { text_choices=MotaActionBlocks['choicesContext'].xmlText([ - choice.text,choice.icon,choice.color,'rgba('+choice.color+')',this.insertActionList(choice.action),text_choices]); + choice.text,choice.icon,choice.color,'rgba('+choice.color+')',choice.nobreak,this.insertActionList(choice.action),text_choices]); } this.next = MotaActionBlocks['choices_s'].xmlText([ this.isset(data.text)?this.EvalString(data.text):null,'','',text_choices,this.next]); diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 0f05135b..6aaa3df7 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -70,9 +70,9 @@ editor_blockly = function () { MotaActionBlocks['lose_s'].xmlText(), MotaActionBlocks['choices_s'].xmlText([ '选择剑或者盾','流浪者','man',MotaActionBlocks['choicesContext'].xmlText([ - '剑','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), + '剑','','',null,null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), MotaActionBlocks['choicesContext'].xmlText([ - '盾','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]), + '盾','','',null,null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]), ]) ]) ]), From 15bc4d1cef8cbd13282f6631eeb71ad00ff34004 Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 13:00:01 +0800 Subject: [PATCH 47/64] script & api docs --- _docs/_sidebar.md | 3 +- _docs/api.md | 278 ++-------------------------------- _docs/element.md | 2 +- _docs/event.md | 4 +- _docs/img/plugin.jpg | Bin 0 -> 32911 bytes _docs/personalization.md | 6 +- _docs/script.md | 313 +++++++++++++++++++++++++++++++++++++++ _docs/start.md | 2 +- libs/ui.js | 4 +- 9 files changed, 336 insertions(+), 276 deletions(-) create mode 100644 _docs/img/plugin.jpg create mode 100644 _docs/script.md diff --git a/_docs/_sidebar.md b/_docs/_sidebar.md index f7c5a0a1..914ee883 100644 --- a/_docs/_sidebar.md +++ b/_docs/_sidebar.md @@ -3,4 +3,5 @@ - [元件说明](element) - [事件](event) - [个性化](personalization) -- [脚本](api) +- [脚本](script) +- [附录:API列表](api) diff --git a/_docs/api.md b/_docs/api.md index 4783aaaa..346f4bd6 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1,268 +1,14 @@ -# 脚本 +# 附录:API列表 ?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * -在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。 - -## 控制台的使用 - -在Chrome浏览器中,按(Ctrl+Shift+I)可打开控制台。 - -![](img/console.jpg) - -控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。 - -有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。 - -### Console:命令行 - -Console页为命令行。可以在这里输入一些命令进行调试。 - -比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。 - -更多的API可参见[附录:API列表](#附录:API列表)。 - -除此以外,游戏中的报错等信息也是可以在Console中进行查看的。 - -![](img/console1.jpg) - -### Sources:断点调试 - -Sources页可以查看JS源代码,并进行断点调试等。 - -例如,如果相对脚本编辑中的伤害计算函数进行断点调试: -1. 在左边找到`project/functions.js`,单击打开文件 -2. 并找到对应的行(可以Ctrl+F搜索),比如搜索`getDamageInfo` -3. 在行号上点一下打断点,会出现一个蓝色标签 - -之后,当代码运行到你的断点处时,将自动停止运行。 - -![](img/sources.jpg) - -可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。 - -图中红色框内有几个按钮,从左到右分别是:**继续执行**,**执行到下一行**,**进入当前函数**,**跳出当前函数**,**单步执行**。 - -通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。 - -红圈下方是Call Stack,即当前的函数调用链(从哪些地方调用过来的)。 - -Sources还有更多有趣的功能,在此不做介绍,有兴趣的可自行网上搜索了解。 - -### Elements:网页元素查看 - -Elements页可以查看网页的源代码,调整css布局等。 - -![](img/elements.jpg) - -不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。 - -手机模式下,左边可以对屏幕分辨率进行调整和模拟。 - -这可以很有效的帮我们进行测试样板在手机端的表现。 - -## 整体项目架构 - -``` text -├── /_server/ # 为可视化地图编辑器提供一些支持的目录 -├── /libs/ # ---- 系统库目录 ---- -│ ├─ /thirdparty/ # 游戏所用到的第三方库文件 -│ ├─ actions.js # 用户交互处理 -│ ├─ core.js # 系统核心文件(游戏入口,接口&转发) -│ ├─ control.js # 游戏逻辑控制 -│ ├─ data.js # 全塔属性等 -│ ├─ enemys.js # 怪物相关处理 -│ ├─ events.js # 各个事件的执行 -│ ├─ icons.js # 图标和素材 -│ ├─ items.js # 道具效果 -│ ├─ loader.js # 各个资源加载 -│ ├─ maps.js # 地图数据和绘制 -│ ├─ ui.js # UI窗口绘制 -│ └─ utils.js # 工具类函数 -├── /project/ # ---- 项目目录 ---- -│ ├─ /animates/ # 动画目录 -│ ├─ /floors/ # 楼层文件 -│ ├─ /images/ # 图片素材 -│ ├─ /sounds/ # bgm和音效 -│ ├─ data.js # 全塔属性 -│ ├─ enemys.js # 怪物属性 -│ ├─ events.js # 公共事件 -│ ├─ functions.js # 脚本编辑 -│ ├─ icons.js # 素材和ID的对应关系定义 -│ ├─ items.js # 道具的定义和效果 -│ ├─ maps.js # 地图和数字的对应关系 -│ └─ plugins.js # 自定义插件 -├── /常用工具/ # 辅助造塔的小工具 -├── editor.html # 地图编辑器 -├── editor-mobile.html # 手机版的地图编辑器 -├── index.html # 主程序,游戏的入口 -├── main.js # JS程序的入口,将动态对所需JS进行加载 -├── style.css # 游戏所需要用到的样式表 -└── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 -``` - -`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 - -`libs`为**系统库目录**,里面存放了各个系统核心函数。 - -从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 - -`project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 - -## 函数的转发 - -在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。 - -例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的: - -```js -////// 获得某个自定义变量或flag ////// -control.prototype.getFlag = function(name, defaultValue) { - if (!core.status.hero) return defaultValue; - var value = core.status.hero.flags[name]; - return value != null ? value : defaultValue; -} -``` - -也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。 - -但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`,而不需要中间的control。 - -为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数,转发到core中执行**。 - -上述`getFlag`代码的转发实际上是增加了如下函数: - -```js -////// getFlag函数的转发 ////// -core.getFlag = function (name, defaultValue) { - return core.control.getFlag(name, defaultValue); -} -// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag() -``` - -转发是自动完成的,其满足如下两条规则: -- **在libs中其他文件定义的函数,如果不以下划线`_`开头,就会进行转发。** -- **如果core中已经存在同名函数,则会在控制台中打出一条报错信息,并不转发该函数。** - -具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。 - -!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发! - -例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。 - -## 复写函数 - -样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 - -在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: -- 不容易随着新样板来接档迁移 -- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 -- …… - -好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 - -如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` - -下面是几个例子,从简单到复杂。 - -### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 - -直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 - -```js -// 重写ui.js中的_drawBook_drawBackground函数 -core.ui._drawBook_drawBackground = function () { - // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 - core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); -} -``` - -### 重写点击楼传事件 - -重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 - -```js -// 重写events.js的useFly函数,即点击楼传按钮时的事件 -core.events.useFly = function (fromUserAction) { - if (core.isMoving()) { - core.drawTip("请先停止勇士行动"); - return; - } - if (core.status.lockControl || core.status.event.id != null) return; - - if (core.canUseItem('fly')) core.useItem('fly'); - else core.drawTip("当前无法使用"+core.material.items.fly.name); -} -``` - -其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 - -### 楼层切换时根据flag来播放不同的音效 - -整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。 - -```js -// 复制重写events.js中的_changeFloor_beforeChange,修改音效 -core.events._changeFloor_beforeChange = function (info, callback) { - // 直接替换原始函数中的 core.playSound('floor.mp3'); - if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); - if (core.getFlag("floorSound") == 1) core.playSound('floor1.mp3'); - if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); - // ... - - // 下面是原始函数中的剩余代码,保持不变 - window.setTimeout(function () { - if (info.time == 0) - core.events._changeFloor_changing(info, callback); - else - core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { - core.events._changeFloor_changing(info, callback); - }); - }, 25) -} -``` - -### 每次打开全局商店时播放一个音效 - -打开全局商店是在`events.js`中的`openShop`函数,因此需要对其进行重写。 - -然而,我们只需要在这个函数执行之前插一句音效播放,所以并不需要重写整个函数,而是直接插入一行就行。 - -```js -var openShop = core.events.openShop; // 先把原始函数用一个变量记录下来 -core.events.openShop = function (shopId, needVisited) { - core.playSound("shop.mp3"); // 播放一个音效 - return openShop(shopId, needVisited); // 直接调用原始函数 -} -``` - -### 每次绘制地图前在控制台打出一条信息 - -绘制地图在`maps.js`的`drawMap`函数,因此需要对其进行重写。 - -由于只需要额外在函数执行前增加一句控制台输出,所以直接插入一行即可。 - -但是需要注意的是,`drawMap`中使用了`this._drawMap_drawAll()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。 - -```js -var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来 -core.maps.drawMap = function (floorId, callback) { - console.log("drawMap..."); // 控制台打出一条信息 - drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps -} -``` - -详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 - -## 附录:API列表 - 这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 如有任何疑问,请联系小艾寻求帮助。 -### core.js +## core.js core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 @@ -421,7 +167,7 @@ core.doFunc(func, _this) 此函数剩余参数将作为参数被传入func。 ``` -### actions.js +## actions.js actions.js主要是处理一些和用户交互相关的内容。 @@ -513,7 +259,7 @@ core.longClick() 如果全部返回false则将停止本次长按行为,直到手指离开屏幕并重新进行长按为止。 ``` -### control.js +## control.js control.js将负责整个游戏的核心控制系统,分为如下几个部分: - requestAnimationFrame相关 @@ -947,7 +693,7 @@ core.resize() 此函数将根据当前的屏幕分辨率信息,生成一个obj,并传入各个注册好的resize函数中执行。 ``` -### enemys.js +## enemys.js enemys.js中定义了一系列和怪物相关的API函数。 @@ -1033,7 +779,7 @@ core.hasEnemyLeft(floorId) 检查某个楼层是否还有剩余的怪物。等价于 core.getCurrentEnemys(floorId).length > 0 ``` -### events.js +## events.js events.js将处理所有和事件相关的操作,主要分为五个部分: - 游戏的开始和结束 @@ -1370,7 +1116,7 @@ core.afterUseBomb() 使用炸弹或圣锤后的事件。实际被转发到了脚本编辑中。 ``` -### icons.js +## icons.js icons.js主要是负责素材相关信息,比如某个素材在对应的图片上的位置。 @@ -1384,7 +1130,7 @@ core.getTilesetOffset(id) 如果该素材不是tileset,则返回null。 ``` -### items.js +## items.js items.js主要负责一切和道具相关的内容。 @@ -1474,7 +1220,7 @@ core.quickLoadEquip() 读取当前套装。index为读取的套装编号。 ``` -### loader.js +## loader.js loader.js主要负责资源加载相关的内容。 @@ -1509,7 +1255,7 @@ core.freeBgm(name) 释放一个bgm的内存并移出缓存列表。如果该bgm正在播放则也会立刻停止。 ``` -### map.js +## map.js maps.js负责一切和地图相关的处理内容,包括如下几个方面: - 地图的初始化,保存和读取,地图数组的生成 @@ -1842,7 +1588,7 @@ core.stopAnimate(id, doCallback) 如果doCallback为真,则会执行该动画所对应的回调函数。 ``` -### ui.js +## ui.js ui.js负责一切UI界面的绘制。主要包括三个部分: - 设置某个画布的属性的相关API @@ -2104,7 +1850,7 @@ core.ui.deleteAllCanvas() 删除所有的自定义画布。 ``` -### utils.js +## utils.js utils.js是一个工具函数库,里面有各个样板中使用到的工具函数。 diff --git a/_docs/element.md b/_docs/element.md index 6f4551d3..4fed9b92 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -85,7 +85,7 @@ percentage为该装备是否按比例增加属性。 使用`core.getEquip(equipType)`来获得某个装备类型的当前装备。 -更多相关API详见[附录:API列表](api#附录:API列表)。 +更多相关API详见[附录:API列表](api)。 ### 多重装备 diff --git a/_docs/event.md b/_docs/event.md index fbd0b094..9fd851a4 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1746,11 +1746,11 @@ icon是可选的,如果设置则会在选项前绘制图标,其可以是一 `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统常见可能会被造塔所用到的的API都在[API列表](api#附录:API列表)中给出,请进行参照。 +系统常见可能会被造塔所用到的的API都在[API列表](api)中给出,请进行参照。 **警告:自定义脚本中只能执行同步代码,不可执行任何异步代码,比如直接调用core.changeFloor(...)之类都是不行的。** -[API列表](api#附录:API列表)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 +[API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 如果需要异步的代码都需要用事件(insertAction)来执行,这样事件处理过程和录像回放才不会出错。 diff --git a/_docs/img/plugin.jpg b/_docs/img/plugin.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d0d69befdce6b52593064bf9ac7cbca2206a66c8 GIT binary patch literal 32911 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 literal 0 HcmV?d00001 diff --git a/_docs/personalization.md b/_docs/personalization.md index 904b6e45..3341f2dd 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -51,7 +51,7 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); `core.deleteAllCanvas()`可以删除所有动态创建的画布,`core.relocateCanvas(name, x, y)`和`core.resizeCanvas(name, x, y)`可以对画布的位置和大小进行改变。 -更多详细API请参见[API列表](api#附录:API列表)。 +更多详细API请参见[API列表](api)。 ## 自定义素材 @@ -339,7 +339,7 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { 要修改楼传事件,需要进行如下两步: -1. 重写楼传的点击事件。在插件中对`core.control.useFly进行重写`。详细代码参见[重写点击楼传事件](api#重写点击楼传事件)。 +1. 重写楼传的点击事件。在插件中对`core.control.useFly进行重写`。详细代码参见[重写点击楼传事件](script#重写点击楼传事件)。 2. 修改楼传的使用事件。和其他永久道具一样,在地图编辑器的图块属性中修改楼传的useItemEffect和canUseItemEffect两个内容。例如: ``` js "useItemEffect": "core.insertAction([...])" // 执行某段自定义事件,或者其他脚本 @@ -429,7 +429,7 @@ this.myfunc = function(x) { 从V2.6开始,在插件中用`this.xxx`定义的函数将会被转发到core中。例如上述的`myfunc`除了`core.plugin.myfunc`外也可以直接`core.myfunc`调用。 -详见[函数的转发](api#函数的转发)。 +详见[函数的转发](script#函数的转发)。 ## 标题界面事件化 diff --git a/_docs/script.md b/_docs/script.md new file mode 100644 index 00000000..817a68a6 --- /dev/null +++ b/_docs/script.md @@ -0,0 +1,313 @@ +# 脚本 + +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * + +在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。 + +## 控制台的使用 + +在Chrome浏览器中,按(Ctrl+Shift+I)可打开控制台。 + +![](img/console.jpg) + +控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。 + +有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。 + +### Console:命令行 + +Console页为命令行。可以在这里输入一些命令进行调试。 + +比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。 + +更多的API可参见[附录:API列表](#附录:API列表)。 + +除此以外,游戏中的报错等信息也是可以在Console中进行查看的。 + +![](img/console1.jpg) + +### Sources:断点调试 + +Sources页可以查看JS源代码,并进行断点调试等。 + +例如,如果相对脚本编辑中的伤害计算函数进行断点调试: +1. 在左边找到`project/functions.js`,单击打开文件 +2. 并找到对应的行(可以Ctrl+F搜索),比如搜索`getDamageInfo` +3. 在行号上点一下打断点,会出现一个蓝色标签 + +之后,当代码运行到你的断点处时,将自动停止运行。 + +![](img/sources.jpg) + +可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。 + +图中红色框内有几个按钮,从左到右分别是:**继续执行**,**执行到下一行**,**进入当前函数**,**跳出当前函数**,**单步执行**。 + +通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。 + +红圈下方是Call Stack,即当前的函数调用链(从哪些地方调用过来的)。 + +Sources还有更多有趣的功能,在此不做介绍,有兴趣的可自行网上搜索了解。 + +### Elements:网页元素查看 + +Elements页可以查看网页的源代码,调整css布局等。 + +![](img/elements.jpg) + +不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。 + +手机模式下,左边可以对屏幕分辨率进行调整和模拟。 + +这可以很有效的帮我们进行测试样板在手机端的表现。 + +## 整体项目架构 + +``` text +├── /_server/ # 为可视化地图编辑器提供一些支持的目录 +├── /libs/ # ---- 系统库目录 ---- +│ ├─ /thirdparty/ # 游戏所用到的第三方库文件 +│ ├─ actions.js # 用户交互处理 +│ ├─ core.js # 系统核心文件(游戏入口,接口&转发) +│ ├─ control.js # 游戏逻辑控制 +│ ├─ data.js # 全塔属性等 +│ ├─ enemys.js # 怪物相关处理 +│ ├─ events.js # 各个事件的执行 +│ ├─ icons.js # 图标和素材 +│ ├─ items.js # 道具效果 +│ ├─ loader.js # 各个资源加载 +│ ├─ maps.js # 地图数据和绘制 +│ ├─ ui.js # UI窗口绘制 +│ └─ utils.js # 工具类函数 +├── /project/ # ---- 项目目录 ---- +│ ├─ /animates/ # 动画目录 +│ ├─ /floors/ # 楼层文件 +│ ├─ /images/ # 图片素材 +│ ├─ /sounds/ # bgm和音效 +│ ├─ data.js # 全塔属性 +│ ├─ enemys.js # 怪物属性 +│ ├─ events.js # 公共事件 +│ ├─ functions.js # 脚本编辑 +│ ├─ icons.js # 素材和ID的对应关系定义 +│ ├─ items.js # 道具的定义和效果 +│ ├─ maps.js # 地图和数字的对应关系 +│ └─ plugins.js # 自定义插件 +├── /常用工具/ # 辅助造塔的小工具 +├── editor.html # 地图编辑器 +├── editor-mobile.html # 手机版的地图编辑器 +├── index.html # 主程序,游戏的入口 +├── main.js # JS程序的入口,将动态对所需JS进行加载 +├── style.css # 游戏所需要用到的样式表 +└── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 +``` + +`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 + +`libs`为**系统库目录**,里面存放了各个系统核心函数。 + +从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 + +`project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 + +## 函数的转发 + +在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。 + +例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的: + +```js +////// 获得某个自定义变量或flag ////// +control.prototype.getFlag = function(name, defaultValue) { + if (!core.status.hero) return defaultValue; + var value = core.status.hero.flags[name]; + return value != null ? value : defaultValue; +} +``` + +也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。 + +但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`,而不需要中间的control。 + +为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数,转发到core中执行**。 + +上述`getFlag`代码的转发实际上是增加了如下函数: + +```js +////// getFlag函数的转发 ////// +core.getFlag = function (name, defaultValue) { + return core.control.getFlag(name, defaultValue); +} +// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag() +``` + +转发是自动完成的,其满足如下两条规则: +- **在libs中其他文件定义的函数,如果不以下划线`_`开头,就会进行转发。** +- **如果core中已经存在同名函数,则会在控制台中打出一条报错信息,并不转发该函数。** + +具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。 + +!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发! + +例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。 + +## 插件编写 + +插件编写是H5魔塔的一个重大特点,从V2.0.1引入,并逐渐发扬光大。 + +对于有一定脚本经验的人来说,可以编写插件来实现各种各样的功能,包括且不仅限于拓展功能的实现,系统代码的复写等等。 + +在V2.5.5以前,插件位置都在脚本编辑中;从V2.6开始则迁移到了新的下拉框中,并进行了切分。 + +你也可以创建自己的插件。 + +![](img/plugin.jpg) + +新的插件切分和原来的单插件使用方法完全一致,单纯进行了切分而已。可参见已有的`init`和`drawLight`的样例。 + +拆分的意义主要是将各个可能的功能独立出来,避免单个框内内容太长,过大和混杂等。 + +在V2.6中,应当每个独立的额外功能实现都新建一个自己的插件,这样也方便进行拓展,例如打包迁移到别的塔上,或发布在网页插件库中。 + +另外一点需要注意的是,所有插件的初始化都会在系统资源加载之前,此时图片等资源尚未进行加载。 + +在所有资源加载完毕时,将会执行init插件中的_afterLoadResources函数,可以在这里对资源进行一些操作,比如切分图片等。 + +```js +function () { + console.log("插件编写测试"); + + // 可以写一些直接执行的代码 + // 在这里写的代码将会在【资源加载前】被执行,此时图片等资源尚未被加载。 + // 请勿在这里对包括bgm,图片等资源进行操作。 + + + this._afterLoadResources = function () { + // 本函数将在所有资源加载完毕后,游戏开启前被执行 + // 可以在这个函数里面对资源进行一些操作,比如切分图片等。 + + // 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。 + // var arr = core.splitImage("assets.png", 32, 32); + // for (var i = 0; i < arr.length; i++) { + // core.material.images.images["asset"+i+".png"] = arr[i]; + // } + + } + + // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + // 从V2.6开始,插件中用this.XXX方式定义的函数也会被转发到core中,详见文档-脚本-函数的转发。 +} +``` + +网站上提供了一个插件库,[https://h5mota.com/plugins/](https://h5mota.com/plugins/),上面有一些大家分享的插件,可供使用。 + +可以查看附录中的[API列表](api)来查看所有的系统API内容。 + +## 复写函数 + +样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 + +在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: +- 不容易随着新样板接档进行迁移 +- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 +- …… + +好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 + +如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` + +下面是几个例子,从简单到复杂。 + +### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 + +直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 + +```js +// 重写ui.js中的_drawBook_drawBackground函数 +core.ui._drawBook_drawBackground = function () { + // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 + core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); +} +``` + +### 重写点击楼传事件 + +重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 + +```js +// 重写events.js的useFly函数,即点击楼传按钮时的事件 +core.events.useFly = function (fromUserAction) { + if (core.isMoving()) { + core.drawTip("请先停止勇士行动"); + return; + } + if (core.status.lockControl || core.status.event.id != null) return; + + if (core.canUseItem('fly')) core.useItem('fly'); + else core.drawTip("当前无法使用"+core.material.items.fly.name); +} +``` + +其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 + +### 楼层切换时根据flag来播放不同的音效 + +整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。 + +```js +// 复制重写events.js中的_changeFloor_beforeChange,修改音效 +core.events._changeFloor_beforeChange = function (info, callback) { + // 直接替换原始函数中的 core.playSound('floor.mp3'); + if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); + if (core.getFlag("floorSound") == 1) core.playSound('floor1.mp3'); + if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); + // ... + + // 下面是原始函数中的剩余代码,保持不变 + window.setTimeout(function () { + if (info.time == 0) + core.events._changeFloor_changing(info, callback); + else + core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { + core.events._changeFloor_changing(info, callback); + }); + }, 25) +} +``` + +### 每次打开全局商店时播放一个音效 + +打开全局商店是在`events.js`中的`openShop`函数,因此需要对其进行重写。 + +然而,我们只需要在这个函数执行之前插一句音效播放,所以并不需要重写整个函数,而是直接插入一行就行。 + +```js +var openShop = core.events.openShop; // 先把原始函数用一个变量记录下来 +core.events.openShop = function (shopId, needVisited) { + core.playSound("shop.mp3"); // 播放一个音效 + return openShop(shopId, needVisited); // 直接调用原始函数 +} +``` + +### 每次绘制地图前在控制台打出一条信息 + +绘制地图在`maps.js`的`drawMap`函数,因此需要对其进行重写。 + +由于只需要额外在函数执行前增加一句控制台输出,所以直接插入一行即可。 + +但是需要注意的是,`drawMap`中使用了`this._drawMap_drawAll()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。 + +```js +var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来 +core.maps.drawMap = function (floorId, callback) { + console.log("drawMap..."); // 控制台打出一条信息 + drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps +} +``` + +详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 + +========================================================================================== + +[继续阅读下一章:API列表](api) + + diff --git a/_docs/start.md b/_docs/start.md index f5b4fa12..004ea21f 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -231,7 +231,7 @@ HTML5的塔都是可以进行控制台调试的。 - `core.resetMap()` 重置当前层地图。 - …… -更多API和详细参数介绍可参见[API列表](api#附录:API列表)。 +更多API和详细参数介绍可参见[API列表](api)。 ## 编辑器的基本操作 diff --git a/libs/ui.js b/libs/ui.js index 6c4501e2..54dbccc1 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -542,7 +542,7 @@ ui.prototype._calTextBoxWidth = function (ctx, content, min_width, max_width, fo // 如果不存在手动换行,尽量调成半行形式 if (allLines.length == 1) { - var w = core.calWidth(ctx, allLines[0]) + 5; + var w = core.calWidth(ctx, allLines[0]) + 10; if (w Date: Thu, 4 Apr 2019 23:15:24 +0800 Subject: [PATCH 48/64] type:confirm --- _docs/event.md | 32 ++++++++++++++++++++++++++++++-- _docs/personalization.md | 2 +- _server/MotaAction.g4 | 26 ++++++++++++++------------ _server/editor_blockly.js | 6 +++--- libs/actions.js | 26 ++++++++++++++++++++++++++ libs/events.js | 36 ++++++++++++++++++++++++++++++++++-- libs/ui.js | 9 ++++++--- 7 files changed, 114 insertions(+), 23 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index 9fd851a4..e5a2b7b9 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1502,7 +1502,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 {"case": "a", "action": [// 若表达式的值等于a则执行该处事件 ], - {"case": "b", "action": [// 若表达式的值等于b则执行该处事件 + {"case": "b", "nobreak": true, "action": [// 若表达式的值等于b则执行该处事件,不跳出 ], {"case": "default", "action": [ // 没有条件成立则执行该处里的事件 @@ -1519,6 +1519,8 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 如果没有符合的值,则将执行`default`中的列表事件内容。 +nobreak是可选的,如果设置,则在当前条件满足并插入事件后,不跳出多重分歧,而是继续判定下一个条件。 + 例如下面这个例子,将检查当前游戏难度并赠送不同属性。 ``` js @@ -1551,7 +1553,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 - 即使某个场合不执行事件,对应的action数组也需要存在,不过简单的留空就好。 - switch语句内的内容执行完毕后将接着其后面的语句继续执行。 -由于`case`中的内容是会被计算的,因此如下写法也是合法的 +另外由于`case`中的内容是会被计算的,因此如下写法也是合法的 ```js [ @@ -1614,6 +1616,32 @@ icon是可选的,如果设置则会在选项前绘制图标,其可以是一 选项可以有任意多个,但一般不要超过6个,否则屏幕可能塞不下。 +### confirm:显示确认框 + +`{"type": "confirm"}`将提供一个确认框供用户选择,其基本写法如下: + +```js +[ + {"type": "confirm", "text": "...", // 提示文字 + "default": false, // 是否默认选中【确定】 + "yes": [ + // 点击确定时执行的事件 + ], + "no": [ + // 点击取消时执行的事件 + ] + }, +] +``` + +text为必填项,代表提示的文字,支持${}的表达式计算和\n的手动换行。 + +text暂时不支持自动换行、变色\r、图标绘制\i等效果。如有需求请使用choices事件。 + +default可选,如果为true则显示选择项时默认选中【确定】,否则默认选中【取消】。 + +yes和no均为必填项,即用户点击确认或取消后执行的事件。 + ### while:循环处理 从2.2.1样板开始,我们提供了循环处理(while事件)。 diff --git a/_docs/personalization.md b/_docs/personalization.md index 3341f2dd..c8f517e6 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -714,4 +714,4 @@ if (core.flags.enableSkill) { ========================================================================================== -[继续阅读脚本](api) +[继续阅读脚本](script) diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index aee1ed18..a43d0c9a 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -1604,14 +1604,16 @@ return code; */; switchCase - : '如果是' expression '的场合' BGNL? Newline action+ + : '如果是' expression '的场合' '不跳出' Bool BGNL? Newline action+ /* switchCase tooltip : 选项的选择 helpUrl : https://h5mota.com/games/template/docs/#/event?id=switch%EF%BC%9A%E5%A4%9A%E9%87%8D%E6%9D%A1%E4%BB%B6%E5%88%86%E6%AD%A7 +default : ["", false] colour : this.subColor -var code = '{"case": "'+expression_0+'", "action": [\n'+action_0+']},\n'; +Bool_0 = Bool_0?', "nobreak": true':''; +var code = '{"case": "'+expression_0+'"'+Bool_0+', "action": [\n'+action_0+']},\n'; return code; */; @@ -1640,13 +1642,13 @@ return code; */; choicesContext - : '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour '不跳出' Bool BGNL? Newline action+ + : '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour BGNL? Newline action+ /* choicesContext tooltip : 选项的选择 helpUrl : https://h5mota.com/games/template/docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 -default : ["提示文字:红钥匙","","",null,false] +default : ["提示文字:红钥匙","",""] colour : this.subColor if (EvalString_1) { var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/; @@ -1656,19 +1658,19 @@ if (EvalString_1) { EvalString_1 = ', "color": "'+EvalString_1+'"'; } IdString_0 = IdString_0?(', "icon": "'+IdString_0+'"'):''; -var nobreak = Bool_0?', "nobreak": true':''; -var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']'+nobreak+'},\n'; +var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']},\n'; return code; */; confirm_s - : '显示确认框' ':' EvalString BGNL? '确定' ':' BGNL? Newline action+ '取消' ':' BGNL? Newline action+ BEND Newline + : '显示确认框' ':' EvalString BGNL? '确定的场合' ':' '(默认选中' Bool ')' BGNL? Newline action+ '取消的场合' ':' BGNL? Newline action+ BEND Newline /* confirm_s tooltip : 弹出确认框 helpUrl : https://h5mota.com/games/template/docs/#/ -default : ["确认要???吗?"] -var code = ['{"type": "confirm", "text": "',EvalString_0,'",\n', +default : ["确认要xxx吗?",false] +Bool_0 = Bool_0?', "default": true':'' +var code = ['{"type": "confirm"'+Bool_0+', "text": "',EvalString_0,'",\n', '"yes": [\n',action_0,'],\n', '"no": [\n',action_1,']\n', '},\n'].join(''); @@ -2590,7 +2592,7 @@ ActionParser.prototype.parseAction = function() { break; case "confirm": // 显示确认框 this.next = MotaActionBlocks['confirm_s'].xmlText([ - this.EvalString(data.text), + this.EvalString(data.text), data["default"], this.insertActionList(data["yes"]), this.insertActionList(data["no"]), this.next]); @@ -2599,7 +2601,7 @@ ActionParser.prototype.parseAction = function() { var case_caseList = null; for(var ii=data.caseList.length-1,caseNow;caseNow=data.caseList[ii];ii--) { case_caseList=MotaActionBlocks['switchCase'].xmlText([ - this.isset(caseNow.case)?MotaActionBlocks['evalString_e'].xmlText([caseNow.case]):"值",this.insertActionList(caseNow.action),case_caseList]); + this.isset(caseNow.case)?MotaActionBlocks['evalString_e'].xmlText([caseNow.case]):"值",caseNow.nobreak,this.insertActionList(caseNow.action),case_caseList]); } this.next = MotaActionBlocks['switch_s'].xmlText([ // MotaActionBlocks['evalString_e'].xmlText([data.condition]), @@ -2610,7 +2612,7 @@ ActionParser.prototype.parseAction = function() { var text_choices = null; for(var ii=data.choices.length-1,choice;choice=data.choices[ii];ii--) { text_choices=MotaActionBlocks['choicesContext'].xmlText([ - choice.text,choice.icon,choice.color,'rgba('+choice.color+')',choice.nobreak,this.insertActionList(choice.action),text_choices]); + choice.text,choice.icon,choice.color,'rgba('+choice.color+')',this.insertActionList(choice.action),text_choices]); } this.next = MotaActionBlocks['choices_s'].xmlText([ this.isset(data.text)?this.EvalString(data.text):null,'','',text_choices,this.next]); diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 09ba5f45..1c81e65c 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -70,9 +70,9 @@ editor_blockly = function () { MotaActionBlocks['lose_s'].xmlText(), MotaActionBlocks['choices_s'].xmlText([ '选择剑或者盾','流浪者','man',MotaActionBlocks['choicesContext'].xmlText([ - '剑','','',null,null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), + '剑','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), MotaActionBlocks['choicesContext'].xmlText([ - '盾','','',null,null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]), + '盾','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]), ]) ]) ]), @@ -113,7 +113,7 @@ editor_blockly = function () { MotaActionBlocks['if_s'].xmlText(), MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [ {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, - {"action": []}, + {"action": [], "nobreak": true}, {"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]}, ]}), MotaActionBlocks['while_s'].xmlText(), diff --git a/libs/actions.js b/libs/actions.js index 2d516b52..bb0cce1f 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -892,6 +892,19 @@ actions.prototype._clickAction = function (x, y) { } } } + + if (core.status.event.data.type == 'confirm') { + if ((x == this.HSIZE-2 || x == this.HSIZE-1) && y == this.HSIZE+1) { + core.status.route.push("choices:0"); + core.insertAction(core.status.event.ui.yes); + core.doAction(); + } + if ((x == this.HSIZE+2 || x == this.HSIZE+1) && y == this.HSIZE+1) { + core.status.route.push("choices:1"); + core.insertAction(core.status.event.ui.no); + core.doAction(); + } + } } ////// 自定义事件时,按下某个键的操作 ////// @@ -899,6 +912,10 @@ actions.prototype._keyDownAction = function (keycode) { if (core.status.event.data.type == 'choices') { this._keyDownChoices(keycode); } + if (core.status.event.data.type == 'confirm' && (keycode == 37 || keycode == 39)) { + core.status.event.selection = 1 - core.status.event.selection; + core.drawConfirmBox(core.status.event.ui.text); + } } ////// 自定义事件时,放开某个键的操作 ////// @@ -924,6 +941,15 @@ actions.prototype._keyUpAction = function (keycode) { if (choices.length > 0) { this._selectChoices(choices.length, keycode, this._clickAction); } + return; + } + if (core.status.event.data.type == 'confirm'&& (keycode == 13 || keycode == 32 || keycode == 67)) { + core.status.route.push("choices:" + core.status.event.selection); + if (core.status.event.selection == 0) + core.insertAction(core.status.event.ui.yes); + else core.insertAction(core.status.event.ui.no); + core.doAction(); + return; } } diff --git a/libs/events.js b/libs/events.js index 6b3d5beb..b5b1170f 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1329,13 +1329,16 @@ events.prototype._action_if = function (data, x, y, prefix) { events.prototype._action_switch = function (data, x, y, prefix) { var key = core.calValue(data.condition, prefix) + var list = []; for (var i = 0; i < data.caseList.length; i++) { var condition = data.caseList[i]["case"]; if (condition == "default" || core.calValue(condition, prefix) == key) { - this.insertAction(data.caseList[i].action); - break; + core.push(list, data.caseList[i].action); + if (!data.caseList[i].nobreak) + break; } } + core.insertAction(list); core.doAction(); } @@ -1363,6 +1366,35 @@ events.prototype._action_choices = function (data, x, y, prefix) { core.ui.drawChoices(data.text, data.choices); } +events.prototype._action_confirm = function (data, x, y, prefix) { + core.status.event.ui = {"text": data.text, "yes": data.yes, "no": data.no}; + if (core.isReplaying()) { + var action = core.status.replay.toReplay.shift(), index; + // --- 忽略可能的turn事件 + if (action == 'turn') action = core.status.replay.toReplay.shift(); + if (action.indexOf("choices:") == 0 && ((index = parseInt(action.substring(8))) >= 0) && index < 2) { + core.status.event.selection = index; + setTimeout(function () { + core.status.route.push("choices:" + index); + if (index == 0) core.insertAction(data.yes); + else core.insertAction(data.no); + core.doAction(); + }, 750 / Math.max(1, core.status.replay.speed)) + } + else { + main.log("录像文件出错!当前需要一个 choices: 项,实际为 " + action); + core.stopReplay(); + core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); + core.doAction(); + return; + } + } + else { + core.status.event.selection = data["default"] ? 0 : 1; + } + core.ui.drawConfirmBox(data.text); +} + events.prototype._action_while = function (data, x, y, prefix) { if (core.calValue(data.condition, prefix)) { core.unshift(core.status.event.data.list, diff --git a/libs/ui.js b/libs/ui.js index 54dbccc1..db1a09e9 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1081,9 +1081,12 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) { core.lockControl(); text = core.replaceText(text || ""); - core.status.event.id = 'confirmBox'; - core.status.event.data = {'yes': yesCallback, 'no': noCallback}; - core.status.event.ui = text; + // 处理自定义事件 + if (core.status.event.id != 'action') { + core.status.event.id = 'confirmBox'; + core.status.event.data = {'yes': yesCallback, 'no': noCallback}; + } + if (core.status.event.selection != 0) core.status.event.selection = 1; this.clearUI(); From 15320257e0307f0c101f0f5ff29a1706192255a5 Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 23:32:23 +0800 Subject: [PATCH 49/64] api --- _docs/script.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_docs/script.md b/_docs/script.md index 817a68a6..ce1d6b42 100644 --- a/_docs/script.md +++ b/_docs/script.md @@ -224,8 +224,8 @@ function () { ```js // 重写ui.js中的_drawBook_drawBackground函数 core.ui._drawBook_drawBackground = function () { - // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 - core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); + // core.__PIXELS__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 + core.drawBackground(0, 0, core.__PIXELS__, core.__PIXELS__); } ``` From 6e66af683f736f6dd0ec331befecd424ced00067 Mon Sep 17 00:00:00 2001 From: oc Date: Fri, 5 Apr 2019 11:13:21 +0800 Subject: [PATCH 50/64] docs --- _docs/script.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/_docs/script.md b/_docs/script.md index ce1d6b42..96a48838 100644 --- a/_docs/script.md +++ b/_docs/script.md @@ -207,12 +207,23 @@ function () { 样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: + +- 不容易记得自己修改过什么,而且如果改错了很麻烦 + - 例如,直接修改了某函数加了新功能,结果过段时间发现不需要,想删掉,但是这时候已经很难找到自己改过了什么了。 + - 或者,如果代码改错了,不断往上面打补丁,也只会使得libs越来越乱,最后连自己做过什么都不记得。 - 不容易随着新样板接档进行迁移 -- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 +- 不方便能整理成新的插件在别的塔使用(总不能让别的塔也去修改libs吧) - …… 好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 +函数复写的好处如下: + +- 不会影响系统原有代码。 + - 即使写错了或不需要了,也只用把插件中的函数注释或删除即可,不会对原来的系统代码产生任何影响。 +- 清晰明了。很容易方便知道自己修改过什么,尤其是可以和系统原有代码进行对比。 +- 方便整理成新的插件,给其他的塔使用。 + 如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` 下面是几个例子,从简单到复杂。 @@ -300,7 +311,7 @@ core.events.openShop = function (shopId, needVisited) { var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来 core.maps.drawMap = function (floorId, callback) { console.log("drawMap..."); // 控制台打出一条信息 - drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps + return drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps } ``` From 3ee88eddea5fe32336651e7e74dbd6b3ac6dcd16 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Fri, 5 Apr 2019 20:20:35 +0800 Subject: [PATCH 51/64] fix bug --- libs/actions.js | 14 ++++++++++++-- libs/events.js | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index bb0cce1f..c4388106 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -891,6 +891,7 @@ actions.prototype._clickAction = function (x, y) { core.doAction(); } } + return; } if (core.status.event.data.type == 'confirm') { @@ -904,6 +905,7 @@ actions.prototype._clickAction = function (x, y) { core.insertAction(core.status.event.ui.no); core.doAction(); } + return; } } @@ -911,10 +913,12 @@ actions.prototype._clickAction = function (x, y) { actions.prototype._keyDownAction = function (keycode) { if (core.status.event.data.type == 'choices') { this._keyDownChoices(keycode); + return; } if (core.status.event.data.type == 'confirm' && (keycode == 37 || keycode == 39)) { core.status.event.selection = 1 - core.status.event.selection; core.drawConfirmBox(core.status.event.ui.text); + return; } } @@ -984,6 +988,7 @@ actions.prototype._clickBook = function (x, y) { var index = this.HSIZE * page + parseInt(y / 2); core.ui.drawBook(index); core.ui.drawBookDetail(index); + return; } return; } @@ -1127,17 +1132,20 @@ actions.prototype._clickViewMaps = function (x, y) { index++; if (index < core.floorIds.length) core.ui.drawMaps(index); + return; } - else if (y >= this.HSIZE + 2 && (mh == this.SIZE || (x >= per && x <= this.LAST - per))) { + if (y >= this.HSIZE + 2 && (mh == this.SIZE || (x >= per && x <= this.LAST - per))) { index--; while (index >= 0 && index != now && core.status.maps[core.floorIds[index]].cannotViewMap) index--; if (index >= 0) core.ui.drawMaps(index); + return; } - else if (x >= per && x <= this.LAST - per && y >= this.HSIZE - 1 && y <= this.HSIZE + 1) { + if (x >= per && x <= this.LAST - per && y >= this.HSIZE - 1 && y <= this.HSIZE + 1) { core.clearMap('data'); core.ui.closePanel(); + return; } } @@ -1243,7 +1251,9 @@ actions.prototype._clickQuickShop = function (x, y) { // 离开 else if (y == topIndex + keys.length) core.ui.closePanel(); + return; } + return; } ////// 快捷商店界面时,放开某个键的操作 ////// diff --git a/libs/events.js b/libs/events.js index b5b1170f..8ecea618 100644 --- a/libs/events.js +++ b/libs/events.js @@ -377,7 +377,7 @@ events.prototype.openDoor = function (x, y, needKey, callback) { events.prototype._openDoor_check = function (id, x, y, needKey) { // 是否存在门或暗墙 if (!core.terrainExists(x, y, id) || !(id.endsWith("Door") || id.endsWith("Wall")) - || !core.material.icons.animates[id]) { + || core.material.icons.animates[id] == null) { core.clearContinueAutomaticRoute(); return false; } From a60b6ff3db37decd73e5d666103369460d7998ac Mon Sep 17 00:00:00 2001 From: oc Date: Fri, 5 Apr 2019 22:16:39 +0800 Subject: [PATCH 52/64] if_1_s --- _server/MotaAction.g4 | 34 ++++++++++++++++++++++++++++------ _server/editor_blockly.js | 1 + libs/control.js | 2 +- libs/events.js | 1 + libs/ui.js | 1 + 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index a43d0c9a..a8741045 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -324,6 +324,7 @@ action | win_s | lose_s | if_s + | if_1_s | switch_s | while_s | break_s @@ -1588,6 +1589,20 @@ var code = ['{"type": "if", "condition": "',expression_0,'",\n', return code; */; +if_1_s + : '如果' ':' expression BGNL? Newline action+ BEND Newline + + +/* if_1_s +tooltip : if: 条件判断 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD +colour : this.eventColor +var code = ['{"type": "if", "condition": "',expression_0,'",\n', + '"true": [\n',action_0,'],\n', +'},\n'].join(''); +return code; +*/; + switch_s : '多重分歧 条件判定' ':' expression BGNL? Newline switchCase+ BEND Newline @@ -2583,12 +2598,19 @@ ActionParser.prototype.parseAction = function() { data.text,this.next]); break; case "if": // 条件判断 - this.next = MotaActionBlocks['if_s'].xmlText([ - // MotaActionBlocks['evalString_e'].xmlText([data.condition]), - this.tryToUseEvFlag_e('evalString_e', [data.condition]), - this.insertActionList(data["true"]), - this.insertActionList(data["false"]), - this.next]); + if (data["false"]) { + this.next = MotaActionBlocks['if_s'].xmlText([ + this.tryToUseEvFlag_e('evalString_e', [data.condition]), + this.insertActionList(data["true"]), + this.insertActionList(data["false"]), + this.next]); + } + else { + this.next = MotaActionBlocks['if_1_s'].xmlText([ + this.tryToUseEvFlag_e('evalString_e', [data.condition]), + this.insertActionList(data["true"]), + this.next]); + } break; case "confirm": // 显示确认框 this.next = MotaActionBlocks['confirm_s'].xmlText([ diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 1c81e65c..2a641312 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -111,6 +111,7 @@ editor_blockly = function () { ], '事件控制':[ MotaActionBlocks['if_s'].xmlText(), + MotaActionBlocks['if_1_s'].xmlText(), MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [ {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, {"action": [], "nobreak": true}, diff --git a/libs/control.js b/libs/control.js index f0de8ee1..5f38acfd 100644 --- a/libs/control.js +++ b/libs/control.js @@ -323,6 +323,7 @@ control.prototype._showStartAnimate_resetDom = function () { core.dom.levelChooseButtons.style.display = 'none'; core.status.played = false; core.clearStatus(); + core.clearMap('all'); core.dom.musicBtn.style.display = 'block'; core.setMusicBtn(); // 重置音量 @@ -360,7 +361,6 @@ control.prototype.clearStatus = function() { } core.status = {}; core.clearStatusBar(); - core.clearMap('all'); core.deleteAllCanvas(); core.status.played = false; } diff --git a/libs/events.js b/libs/events.js index 8ecea618..814afc2b 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1683,6 +1683,7 @@ events.prototype.load = function (fromUserAction) { if (!core.isPlaying()) { core.dom.startPanel.style.display = 'none'; core.clearStatus(); + core.clearMap('all'); core.status.event = {'id': 'load', 'data': null}; core.status.lockControl = true; core.ui.drawSLPanel(10*page+offset); diff --git a/libs/ui.js b/libs/ui.js index db1a09e9..9d63d8d0 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1084,6 +1084,7 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) { // 处理自定义事件 if (core.status.event.id != 'action') { core.status.event.id = 'confirmBox'; + core.status.event.ui = text; core.status.event.data = {'yes': yesCallback, 'no': noCallback}; } From e7fc0cd846a7832648ef915a35d0e5ccf9438c3e Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Fri, 5 Apr 2019 21:41:04 -0400 Subject: [PATCH 53/64] config table --- _server/editor_file.js | 6 +--- _server/editor_multi.js | 68 +++++++++++++++++++++++++++++++++++------ _server/editor_util.js | 20 +++++++++--- _server/refactoring.md | 2 +- editor-mobile.html | 14 ++++----- editor.html | 14 ++++----- 6 files changed, 91 insertions(+), 33 deletions(-) diff --git a/_server/editor_file.js b/_server/editor_file.js index 89b7f6c5..e074a1d5 100644 --- a/_server/editor_file.js +++ b/_server/editor_file.js @@ -941,11 +941,7 @@ editor_file = function (editor, callback) { return formatArrStr; } - var encode = function (str) { - return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { - return String.fromCharCode(parseInt(p1, 16)) - })) - } + var encode = editor.util.encode64 var alertWhenCompress = function(){ if(editor.useCompress===true){ diff --git a/_server/editor_multi.js b/_server/editor_multi.js index 9ccd00c9..e68cf56a 100644 --- a/_server/editor_multi.js +++ b/_server/editor_multi.js @@ -11,14 +11,14 @@ editor_multi = function () { tabSize: 4, indentWithTabs: true, smartIndent: true, - mode: {name: "javascript", globalVars: true, localVars: true}, + mode: { name: "javascript", globalVars: true, localVars: true }, lineWrapping: true, continueComments: "Enter", gutters: ["CodeMirror-lint-markers"], lint: true, autocomplete: true, autoCloseBrackets: true, - highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true} + highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true } }); editor_multi.codeEditor = codeEditor; @@ -26,9 +26,9 @@ editor_multi = function () { codeEditor.on("keyup", function (cm, event) { if (codeEditor.getOption("autocomplete") && !event.ctrlKey && ( (event.keyCode >= 65 && event.keyCode <= 90) || - (!event.shiftKey && event.keyCode == 190) || (event.shiftKey && event.keyCode == 189))){ + (!event.shiftKey && event.keyCode == 190) || (event.shiftKey && event.keyCode == 189))) { try { - CodeMirror.commands.autocomplete(cm, null, {completeSingle: false}); + CodeMirror.commands.autocomplete(cm, null, { completeSingle: false }); } catch (e) { } } @@ -39,7 +39,7 @@ editor_multi = function () { editor_multi.lintAutocomplete = false; editor_multi.show = function () { - if (typeof(selectBox) !== typeof(undefined)) selectBox.isSelected(false); + if (typeof (selectBox) !== typeof (undefined)) selectBox.isSelected(false); var valueNow = codeEditor.getValue(); //try{eval('function _asdygakufyg_() { return '+valueNow+'\n}');editor_multi.lintAutocomplete=true;}catch(ee){} if (valueNow.slice(0, 8) === 'function') editor_multi.lintAutocomplete = true; @@ -60,7 +60,7 @@ editor_multi = function () { } editor_multi.indent = function (field) { - if (typeof(editor) !== typeof(undefined) && editor && editor.mode && editor.mode.indent) return editor.mode.indent(field); + if (typeof (editor) !== typeof (undefined) && editor && editor.mode && editor.mode.indent) return editor.mode.indent(field); return '\t'; } @@ -102,7 +102,7 @@ editor_multi = function () { eval('var tobj=' + (input.value || 'null')); var tmap = {}; var tstr = JSON.stringify(tobj, function (k, v) { - if (typeof(v) === typeof('') && v.slice(0, 8) === 'function') { + if (typeof (v) === typeof ('') && v.slice(0, 8) === 'function') { var id_ = editor.util.guid(); tmap[id_] = v.toString(); return id_; @@ -128,13 +128,21 @@ editor_multi = function () { editor_multi.id = ''; return; } - // ----- 自动格式化 - _format(); + if (editor_multi.id === 'callFromBlockly') { + // ----- 自动格式化 + _format(); editor_multi.id = ''; editor_multi.multiLineDone(); return; } + + if (editor_multi.id === 'importFile') { + editor_multi.id = ''; + editor_multi.writeFileDone(); + return; + } + var setvalue = function (value) { var thisTr = document.getElementById(editor_multi.id); editor_multi.id = ''; @@ -159,6 +167,8 @@ editor_multi = function () { editor_multi.hide(); input.onchange(); } + // ----- 自动格式化 + _format(); setvalue(codeEditor.getValue() || ''); } @@ -179,6 +189,46 @@ editor_multi = function () { multiLineArgs[2](newvalue, multiLineArgs[0], multiLineArgs[1]) } + var _fileValues = [''] + editor_multi.importFile = function (filename) { + editor_multi.id = 'importFile' + _fileValues[0] = filename + codeEditor.setValue('loading') + editor_multi.show(); + fs.readFile(filename, 'base64', function (e, d) { + if (e) { + codeEditor.setValue('加载文件失败:\n' + e) + editor_multi.id = '' + return; + } + var str = editor.util.decode64(d) + codeEditor.setValue(str) + _fileValues[1] = str + }) + } + + editor_multi.writeFileDone = function () { + fs.writeFile(_fileValues[0], editor.util.encode64(codeEditor.getValue() || ''), 'base64', function (err, data) { + if (err) printe('文件写入失败,请手动粘贴至' + _fileValues[0] + '\n' + err); + else editor_multi.hide(); + }); + } + + editor_multi.editCommentJs = function (mod) { + var dict = { + loc: '_server/comment.js', + enemyitem: '_server/comment.js', + floor: '_server/comment.js', + tower: '_server/data.comment.js', + functions: '_server/functions.comment.js', + commonevent: '_server/events.comment.js', + plugins: '_server/plugins.comment.js', + } + editor_multi.lintAutocomplete = true + editor_multi.setLint() + editor_multi.importFile(dict[mod]) + } + return editor_multi; } //editor_multi=editor_multi(); \ No newline at end of file diff --git a/_server/editor_util.js b/_server/editor_util.js index bab29837..83b745a5 100644 --- a/_server/editor_util.js +++ b/_server/editor_util.js @@ -59,7 +59,7 @@ editor_util_wrapper = function (editor) { // SOFTWARE. //-------------------------------------------- // https://github.com/carloscabo/colz/blob/master/public/js/colz.class.js - var round=Math.round; + var round = Math.round; var rgbToHsl = function (rgba) { var arg, r, g, b, h, s, l, d, max, min; @@ -141,9 +141,21 @@ editor_util_wrapper = function (editor) { } return [round(r * 255), round(g * 255), round(b * 255)]; } - editor_util.prototype.rgbToHsl=rgbToHsl - editor_util.prototype.hue2rgb=hue2rgb - editor_util.prototype.hslToRgb=hslToRgb + editor_util.prototype.rgbToHsl = rgbToHsl + editor_util.prototype.hue2rgb = hue2rgb + editor_util.prototype.hslToRgb = hslToRgb + + editor_util.prototype.encode64 = function (str) { + return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { + return String.fromCharCode(parseInt(p1, 16)) + })) + } + + editor_util.prototype.decode64 = function (str) { + return decodeURIComponent(atob(str.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')).split('').map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + }).join('')) + } editor.constructor.prototype.util = new editor_util(); } diff --git a/_server/refactoring.md b/_server/refactoring.md index 55b02bfe..c75dc64b 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -85,7 +85,7 @@ editor: { + [ ] 画地图也自动保存 -+ [ ] 修改系统的触发器(下拉菜单增加新项) ++ [x] 修改系统的触发器(下拉菜单增加新项) 在编辑器修改`comment.js`:现场发readFile请求读文件,然后开脚本编辑器进行编辑 + [ ] ? 删除注册项/修改图块ID diff --git a/editor-mobile.html b/editor-mobile.html index ebcc334c..e77a286f 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -97,7 +97,7 @@

-

地图选点   +

地图选点    

0,0

@@ -115,7 +115,7 @@
-

图块属性       +

图块属性        

@@ -141,7 +141,7 @@
-

楼层属性       +

楼层属性        

@@ -158,7 +158,7 @@
-

全塔属性     +

全塔属性      

@@ -226,7 +226,7 @@
-

脚本编辑   +

脚本编辑    

@@ -243,7 +243,7 @@
-

公共事件       +

公共事件        

@@ -260,7 +260,7 @@
-

插件编写       +

插件编写        

diff --git a/editor.html b/editor.html index 0b5c4187..58ea0a1c 100644 --- a/editor.html +++ b/editor.html @@ -93,7 +93,7 @@
-

地图选点   +

地图选点    

0,0

@@ -111,7 +111,7 @@
-

图块属性       +

图块属性        

@@ -137,7 +137,7 @@
-

楼层属性       +

楼层属性        

@@ -154,7 +154,7 @@
-

全塔属性     +

全塔属性      

@@ -222,7 +222,7 @@
-

脚本编辑   +

脚本编辑    

@@ -239,7 +239,7 @@
-

公共事件       +

公共事件        

@@ -256,7 +256,7 @@
-

插件编写       +

插件编写        

From f43a8728951f20c9273f55b68673d190f1e6674c Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Sat, 6 Apr 2019 13:23:21 +0800 Subject: [PATCH 54/64] update comments --- _docs/api.md | 9 +- _server/comment.js | 925 +++++++++++------------ _server/data.comment.js | 1349 +++++++++++++++++----------------- _server/editor_multi.js | 6 +- _server/editor_table.js | 2 +- _server/events.comment.js | 85 +-- _server/functions.comment.js | 426 +++++------ _server/plugins.comment.js | 57 +- libs/enemys.js | 6 +- 9 files changed, 1444 insertions(+), 1421 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index 346f4bd6..ee5fbbaf 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -775,8 +775,9 @@ core.getCurrentEnemys(floorId) 另外值得注意的是,如果设置了某个怪物的displayIdInBook,则会返回对应的怪物。 -core.hasEnemyLeft(floorId) -检查某个楼层是否还有剩余的怪物。等价于 core.getCurrentEnemys(floorId).length > 0 +core.hasEnemyLeft(enemyId, floorId) +检查某个楼层是否还有剩余的(指定)怪物。floorId为楼层ID,可忽略表示当前楼层。 +enemyId如果不填或null则检查是否剩余任何怪物,否则只检查是否剩余指定的某类怪物。 ``` ## events.js @@ -1743,7 +1744,7 @@ config为绘制的配置项,目前可以包括如下几项: - align:文字对齐方式,仅在maxWidth设置时有效,默认为'left'。 - fontSize:字体大小,如果不设置则使用剧情文本设置中的正文字体大小。 - lineHeight:绘制的行距值,如果不设置则使用fontSize*1.3(即1.3被行距)。 - - time:打字机效果。如果此项不为0,则会用打字机效果逐个字进行绘制,并设置core.status.event.interval定时器。 + - time:打字机效果。若不为0,则会逐个字进行绘制,并设置core.status.event.interval定时器。 core.drawTextBox(content, showAll) @@ -1755,7 +1756,7 @@ showAll可选,如果为true则不会使用打字机效果而全部显示,主 core.drawScrollText(content, time, lineHeight, callback) -绘制一个滚动字幕。content为绘制内容,time为总滚动时间(默认为5000),lineHeight为行距比例(默认为1.4)。 +绘制一个滚动字幕。content为绘制内容,time为总时间(默认为5000),lineHeight为行距比例(默认为1.4)。 滚动字幕将绘制在UI上,支持所有的文字效果(如\n,${},\r,\\i等),但不支持\t和\b效果。 可以通过剧情文本设置中的align控制是否居中绘制,offset控制其距离左边的偏移量。 diff --git a/_server/comment.js b/_server/comment.js index d4c1ac53..ff09aa48 100644 --- a/_server/comment.js +++ b/_server/comment.js @@ -1,460 +1,467 @@ -var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = -{ - - "_type": "object", - "_data": { - "items": { - - "_type": "object", - "_data": { - "items": { - - "_type": "object", - "_data": { - "cls": { - "_leaf": true, - "_type": "select", - "_select": { - "values": [ - "keys", - "items", - "constants", - "tools", - "equips" - ] - }, - "_data": "只能取keys(钥匙) items(宝石、血瓶) constants(永久物品) tools(消耗道具) equips(装备)" - }, - "name": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "名称" - }, - "text": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "道具在道具栏中显示的描述" - }, - "equip": { - "_leaf": true, - "_type": "textarea", - "_data": "装备属性设置,仅对cls为equips有效。\n如果此项不为null,需要是一个对象,里面可含\"type\",\"atk\",\"def\",\"mdef\",\"animate\"五项,分别对应装备部位、攻防魔防和动画。\n具体详见文档(元件说明-装备)和已有的几个装备的写法。" - }, - "hideInReplay": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否回放时绘制道具栏。\n如果此项为true,则在回放录像时使用本道具将不会绘制道具栏页面,而是直接使用。\n此项建议在会频繁连续多次使用的道具开启(如开启技能,或者《镜子》那样的镜像切换等等)" - } - } - }, - "itemEffect": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_lint": true, - "_data": "即捡即用类物品的效果,仅对cls为items有效。" - }, - "itemEffectTip": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_lint": true, - "_data": "即捡即用类物品在获得时提示的文字,仅对cls为items有效。" - }, - "useItemEffect": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_lint": true, - "_data": "道具效果,仅对cls为tools或constants有效。" - }, - "canUseItemEffect": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_lint": true, - "_data": "当前能否使用该道具,仅对cls为tools或constants有效。" - }, - "canEquip":{ - "_leaf": true, - "_type": "textarea", - "_string": true, - "_lint": true, - "_data": "当前能否装备某个装备,仅对cls为equips有效。\n与canUseItemEffect不同,这里null代表可以装备。" - } - } - }, - "items_template" : {'cls': 'items', 'name': '新物品'}, - "enemys": { - - "_type": "object", - "_data": { - "name": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "名称" - }, - "displayIdInBook": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "在怪物手册中映射到的怪物ID。如果此项不为null,则在怪物手册中,将用目标ID来替换该怪物原本的ID。\n此项应被运用在同一个怪物的多朝向上。\n例如,如果想定义同一个怪物的向下和向左的行走图,则需要建立两个属性完全相同的怪物。\n但是这样会导致在怪物手册中同时存在向下和向左的两种怪物的显示。\n可以将朝向左的怪物的displayIdInBook项指定为朝向下的怪物ID,这样在怪物手册中则会归一化,只显示一个。" - }, - "hp": { - "_leaf": true, - "_type": "textarea", - "_data": "生命值" - }, - "atk": { - "_leaf": true, - "_type": "textarea", - "_data": "攻击力" - }, - "def": { - "_leaf": true, - "_type": "textarea", - "_data": "防御力" - }, - "money": { - "_leaf": true, - "_type": "textarea", - "_data": "金币" - }, - "experience": { - "_leaf": true, - "_type": "textarea", - "_data": "经验" - }, - "point": { - "_leaf": true, - "_type": "textarea", - "_data": "加点" - }, - "special": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null || thiseval instanceof Array || (thiseval==~~thiseval && thiseval>=0)", - "_data": "特殊属性\n\n0:无,1:先攻,2:魔攻,3:坚固,4:2连击,\n5:3连击,6:n连击,7:破甲,8:反击,9:净化,\n10:模仿,11:吸血,12:中毒,13:衰弱,14:诅咒,\n15:领域,16:夹击,17:仇恨,18:阻击,19:自爆,\n20:无敌,21:退化,22:固伤,23:重生,24:激光,25:光环\n\n多个属性例如用[1,4,11]表示先攻2连击吸血" - }, - "value": { - "_leaf": true, - "_type": "textarea", - "_data": "特殊属性的数值\n如:领域/阻激/激光怪的伤害值;吸血怪的吸血比例;光环怪增加生命的比例" - }, - "zoneSquare": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "领域怪是否九宫格伤害" - }, - "range": { - "_leaf": true, - "_type": "textarea", - "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", - "_data": "领域伤害的范围;不加默认为1" - }, - "notBomb": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该怪物不可被炸" - }, - "n": { - "_leaf": true, - "_type": "textarea", - "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", - "_data": "多连击的连击数" - }, - "add": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "吸血后是否加到自身;光环是否叠加" - }, - "atkValue": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==~~thiseval||thiseval==null", - "_data": "退化时勇士下降的攻击力点数;光环怪增加攻击的比例" - }, - "defValue": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==~~thiseval||thiseval==null", - "_data": "退化时勇士下降的防御力点数;光环怪增加防御的比例" - }, - "damage": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==~~thiseval||thiseval==null", - "_data": "战前扣血的点数" - } - } - }, - "enemys_template" : {'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'point': 0, 'special': 0}, - "maps": { - - "_type": "object", - "_data": { - "id": { - "_leaf": true, - "_type": "textarea", - "_range": "false", - "_data": "图块ID" - }, - "idnum": { - "_leaf": true, - "_type": "textarea", - "_range": "false", - "_data": "图块数字" - }, - "cls": { - "_leaf": true, - "_type": "textarea", - "_range": "false", - "_data": "图块类别" - }, - "trigger": { - "_leaf": true, - "_type": "select", - "_select": { - "values": [ - null, - "openDoor", - "passNet", - "changeLight", - "pushBox", - "custom" - ] - }, - "_data": "该图块的默认触发器" - }, - "noPass": { - "_leaf": true, - "_type": "select", - "_select": { - "values": [ - null, - true, - false - ] - }, - "_data": "该图块是否不可通行;true代表不可通行,false代表可通行,null代表使用系统缺省值" - }, - "canBreak": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该图块是否可被破墙或地震" - }, - "cannotOut": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null||(thiseval instanceof Array)", - "_data": "该图块的不可出方向\n可以在这里定义在该图块时不能前往哪个方向,可以达到悬崖之类的效果\n例如 [\"up\", \"left\"] 代表在该图块时不能往上和左走\n此值对背景层、事件层、前景层上的图块均有效" - }, - "cannotIn": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null||(thiseval instanceof Array)", - "_data": "该图块的不可入方向\n可以在这里定义不能朝哪个方向进入该图块,可以达到悬崖之类的效果\n例如 [\"down\"] 代表不能从该图块的上方点朝向下进入此图块\n此值对背景层、事件层、前景层上的图块均有效" - }, - "animate": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==~~thiseval||thiseval==null", - "_data": "该图块的全局动画帧数。\n如果此项为null,则对于除了npc48外,使用素材默认帧数;npc48默认是1帧(即静止)。" - }, - "faceIds": { - "_leaf": true, - "_type": "textarea", - "_data": "行走图朝向,仅对NPC有效。可以在这里定义同一个NPC的多个朝向行走图。\n比如 {\"up\":\"N333\",\"down\":\"N334\",\"left\":\"N335\",\"right\":\"N336\"} 就将该素材的上下左右朝向分别绑定到N333,N334,N335和N336四个图块。\n在勇士撞上NPC时,或NPC在移动时,会自动选择最合适的朝向图块(如果存在定义)来进行绘制。" - } - } - }, - "floors": { - - "_type": "object", - "_data": { - "floor": { - - "_type": "object", - "_data": { - "floorId": { - "_leaf": true, - "_type": "textarea", - "_range": "false", - "_data": "文件名和floorId需要保持完全一致 \n楼层唯一标识符仅能由字母、数字、下划线组成,且不能由数字开头 \n推荐用法:第20层就用MT20,第38层就用MT38,地下6层就用MT_6(用下划线代替负号),隐藏3层用MT3h(h表示隐藏),等等 \n楼层唯一标识符,需要和名字完全一致 \n这里不能更改floorId,请通过另存为来实现" - }, - "title": { - "_leaf": true, - "_type": "textarea", - "_data": "楼层中文名,将在切换楼层和浏览地图时显示" - }, - "name": { - "_leaf": true, - "_type": "textarea", - "_data": "显示在状态栏中的层数" - }, - "width": { - "_leaf": true, - "_type": "textarea", - "_range": "false", - "_data": "地图x方向大小,这里不能更改,仅能在新建地图时设置,null视为13" - }, - "height": { - "_leaf": true, - "_type": "textarea", - "_range": "false", - "_data": "地图y方向大小,这里不能更改,仅能在新建地图时设置,null视为13" - }, - "canFlyTo": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)" - }, - "canUseQuickShop": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该层是否允许使用快捷商店" - }, - "cannotViewMap": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该层是否不允许被浏览地图看到;如果勾上则浏览地图会跳过该层" - }, - "cannotMoveDirectly": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该层是否不允许瞬间移动;如果勾上则不可在此层进行瞬移" - }, - "firstArrive": { - "_leaf": true, - "_type": "event", - "_event": "firstArrive", - "_data": "第一次到该楼层触发的事件,可以双击进入事件编辑器。" - }, - "eachArrive": { - "_leaf": true, - "_type": "event", - "_event": "eachArrive", - "_data": "每次到该楼层触发的事件,可以双击进入事件编辑器;该事件会在firstArrive执行后再执行。" - }, - "parallelDo": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_lint": true, - "_data": "在该层楼时执行的并行事件处理。\n可以在这里写上任意需要自动执行的脚本,比如打怪自动开门等。\n详见文档-事件-并行事件处理。" - }, - "upFloor": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", - "_data": "该层上楼点,如[2,3]。\n如果此项不为null,则楼层转换时的stair:upFloor,以及楼传器的落点会被替换成该点而不是该层的上楼梯。" - }, - "downFloor": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", - "_data": "该层下楼点,如[2,3]。\n如果此项不为null,则楼层转换时的stair:downFloor,以及楼传器的落点会被替换成该点而不是该层的下楼梯。" - }, - "defaultGround": { - "_leaf": true, - "_type": "select", - "_select": { - "values": Object.keys(editor.core.icons.icons.terrains) - }, - "_data": "默认地面的图块ID,此项修改后需要刷新才能看到效果。" - }, - "images": { - "_leaf": true, - "_type": "textarea", - "_data": "背景/前景图;你可以选择若干张图片来作为背景/前景素材。详细用法请参见文档“自定义素材”中的说明。" - }, - "color": { - "_leaf": true, - "_type": "textarea", - "_data": "该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组如[255,0,0,0.3]" - }, - "weather": { - "_leaf": true, - "_type": "textarea", - "_data": "该层的默认天气。本项可忽略表示晴天,如果写则第一项为\"rain\",\"snow\"或\"fog\"代表雨雪雾,第二项为1-10之间的数代表强度。\n如[\"rain\", 8]代表8级雨天。" - }, - "bgm": { - "_leaf": true, - "_type": "select", - "_select": { - "values": [null].concat(Object.keys(editor.core.material.bgms)) - }, - "_data": "到达该层后默认播放的BGM。本项可忽略,或者为一个定义过的背景音乐如\"bgm.mp3\"。" - }, - "item_ratio": { - "_leaf": true, - "_type": "textarea", - "_range": "(thiseval==~~thiseval && thiseval>=0)||thiseval==null", - "_data": "每一层的宝石/血瓶效果,即获得宝石和血瓶时框内\"ratio\"的值。" - }, - "underGround": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否是地下层;如果该项为true则同层传送将传送至上楼梯" - } - } - }, - "loc": { - - "_type": "object", - "_data": { - "events": { - "_leaf": true, - "_type": "event", - "_event": "event", - "_data": "该点的可能事件列表,可以双击进入事件编辑器。" - }, - "changeFloor": { - "_leaf": true, - "_type": "event", - "_event": "changeFloor", - "_data": "该点楼层转换事件;该事件不能和上面的events同时出现,否则会被覆盖" - }, - "afterBattle": { - "_leaf": true, - "_type": "event", - "_event": "afterBattle", - "_data": "该点战斗后可能触发的事件列表,可以双击进入事件编辑器。" - }, - "afterGetItem": { - "_leaf": true, - "_type": "event", - "_event": "afterGetItem", - "_data": "该点获得道具后可能触发的事件列表,可以双击进入事件编辑器。" - }, - "afterOpenDoor": { - "_leaf": true, - "_type": "event", - "_event": "afterOpenDoor", - "_data": "该点开完门后可能触发的事件列表,可以双击进入事件编辑器。" - }, - "cannotMove": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null||(thiseval instanceof Array)", - "_data": "该点不可通行的方向 \n 可以在这里定义该点不能前往哪个方向,可以达到悬崖之类的效果\n例如 [\"up\", \"left\"] 代表该点不能往上和左走" - } - } - } - } - } - } +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:道具、怪物、图块属性、楼层属性等内容。 + */ + +var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + // --------------------------- 【道具】相关的表格配置 --------------------------- // + "items": { + "_type": "object", + "_data": { + "items": { + "_type": "object", + "_data": { + "cls": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [ + "keys", + "items", + "constants", + "tools", + "equips" + ] + }, + "_data": "只能取keys(钥匙) items(宝石、血瓶) constants(永久物品) tools(消耗道具) equips(装备)" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "名称" + }, + "text": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "道具在道具栏中显示的描述" + }, + "equip": { + "_leaf": true, + "_type": "textarea", + "_data": "装备属性设置,仅对cls为equips有效。\n如果此项不为null,需要是一个对象,里面可含\"type\",\"atk\",\"def\",\"mdef\",\"animate\"五项,分别对应装备部位、攻防魔防和动画。\n具体详见文档(元件说明-装备)和已有的几个装备的写法。" + }, + "hideInReplay": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否回放时绘制道具栏。\n如果此项为true,则在回放录像时使用本道具将不会绘制道具栏页面,而是直接使用。\n此项建议在会频繁连续多次使用的道具开启(如开启技能,或者《镜子》那样的镜像切换等等)" + } + } + }, + "itemEffect": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "即捡即用类物品的效果,仅对cls为items有效。" + }, + "itemEffectTip": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "即捡即用类物品在获得时提示的文字,仅对cls为items有效。" + }, + "useItemEffect": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "道具效果,仅对cls为tools或constants有效。" + }, + "canUseItemEffect": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "当前能否使用该道具,仅对cls为tools或constants有效。" + }, + "canEquip": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "当前能否装备某个装备,仅对cls为equips有效。\n与canUseItemEffect不同,这里null代表可以装备。" + } + } + }, + "items_template": { 'cls': 'items', 'name': '新物品' }, + + + // --------------------------- 【怪物】相关的表格配置 --------------------------- // + "enemys": { + "_type": "object", + "_data": { + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "名称" + }, + "displayIdInBook": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "在怪物手册中映射到的怪物ID。如果此项不为null,则在怪物手册中,将用目标ID来替换该怪物原本的ID。\n此项应被运用在同一个怪物的多朝向上。\n例如,如果想定义同一个怪物的向下和向左的行走图,则需要建立两个属性完全相同的怪物。\n但是这样会导致在怪物手册中同时存在向下和向左的两种怪物的显示。\n可以将朝向左的怪物的displayIdInBook项指定为朝向下的怪物ID,这样在怪物手册中则会归一化,只显示一个。" + }, + "hp": { + "_leaf": true, + "_type": "textarea", + "_data": "生命值" + }, + "atk": { + "_leaf": true, + "_type": "textarea", + "_data": "攻击力" + }, + "def": { + "_leaf": true, + "_type": "textarea", + "_data": "防御力" + }, + "money": { + "_leaf": true, + "_type": "textarea", + "_data": "金币" + }, + "experience": { + "_leaf": true, + "_type": "textarea", + "_data": "经验" + }, + "point": { + "_leaf": true, + "_type": "textarea", + "_data": "加点" + }, + "special": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null || thiseval instanceof Array || (thiseval==~~thiseval && thiseval>=0)", + "_data": "特殊属性\n\n0:无,1:先攻,2:魔攻,3:坚固,4:2连击,\n5:3连击,6:n连击,7:破甲,8:反击,9:净化,\n10:模仿,11:吸血,12:中毒,13:衰弱,14:诅咒,\n15:领域,16:夹击,17:仇恨,18:阻击,19:自爆,\n20:无敌,21:退化,22:固伤,23:重生,24:激光,25:光环\n\n多个属性例如用[1,4,11]表示先攻2连击吸血" + }, + "value": { + "_leaf": true, + "_type": "textarea", + "_data": "特殊属性的数值\n如:领域/阻激/激光怪的伤害值;吸血怪的吸血比例;光环怪增加生命的比例" + }, + "zoneSquare": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "领域怪是否九宫格伤害" + }, + "range": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", + "_data": "领域伤害的范围;不加默认为1" + }, + "notBomb": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该怪物不可被炸" + }, + "n": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", + "_data": "多连击的连击数" + }, + "add": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "吸血后是否加到自身;光环是否叠加" + }, + "atkValue": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_data": "退化时勇士下降的攻击力点数;光环怪增加攻击的比例" + }, + "defValue": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_data": "退化时勇士下降的防御力点数;光环怪增加防御的比例" + }, + "damage": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_data": "战前扣血的点数" + } + } + }, + "enemys_template": { 'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'point': 0, 'special': 0 }, + + + // --------------------------- 【图块属性】相关的表格配置 --------------------------- // + "maps": { + "_type": "object", + "_data": { + "id": { + "_leaf": true, + "_type": "textarea", + "_range": "false", + "_data": "图块ID" + }, + "idnum": { + "_leaf": true, + "_type": "textarea", + "_range": "false", + "_data": "图块数字" + }, + "cls": { + "_leaf": true, + "_type": "textarea", + "_range": "false", + "_data": "图块类别" + }, + "trigger": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [ + null, + "openDoor", + "passNet", + "changeLight", + "pushBox", + "custom" + ] + }, + "_data": "该图块的默认触发器" + }, + "noPass": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [ + null, + true, + false + ] + }, + "_data": "该图块是否不可通行;true代表不可通行,false代表可通行,null代表使用系统缺省值" + }, + "canBreak": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该图块是否可被破墙或地震" + }, + "cannotOut": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null||(thiseval instanceof Array)", + "_data": "该图块的不可出方向\n可以在这里定义在该图块时不能前往哪个方向,可以达到悬崖之类的效果\n例如 [\"up\", \"left\"] 代表在该图块时不能往上和左走\n此值对背景层、事件层、前景层上的图块均有效" + }, + "cannotIn": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null||(thiseval instanceof Array)", + "_data": "该图块的不可入方向\n可以在这里定义不能朝哪个方向进入该图块,可以达到悬崖之类的效果\n例如 [\"down\"] 代表不能从该图块的上方点朝向下进入此图块\n此值对背景层、事件层、前景层上的图块均有效" + }, + "animate": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_data": "该图块的全局动画帧数。\n如果此项为null,则对于除了npc48外,使用素材默认帧数;npc48默认是1帧(即静止)。" + }, + "faceIds": { + "_leaf": true, + "_type": "textarea", + "_data": "行走图朝向,仅对NPC有效。可以在这里定义同一个NPC的多个朝向行走图。\n比如 {\"up\":\"N333\",\"down\":\"N334\",\"left\":\"N335\",\"right\":\"N336\"} 就将该素材的上下左右朝向分别绑定到N333,N334,N335和N336四个图块。\n在勇士撞上NPC时,或NPC在移动时,会自动选择最合适的朝向图块(如果存在定义)来进行绘制。" + } + } + }, + + + // --------------------------- 【楼层属性】相关的表格配置 --------------------------- // + "floors": { + "_type": "object", + "_data": { + "floor": { + "_type": "object", + "_data": { + "floorId": { + "_leaf": true, + "_type": "textarea", + "_range": "false", + "_data": "文件名和floorId需要保持完全一致 \n楼层唯一标识符仅能由字母、数字、下划线组成,且不能由数字开头 \n推荐用法:第20层就用MT20,第38层就用MT38,地下6层就用MT_6(用下划线代替负号),隐藏3层用MT3h(h表示隐藏),等等 \n楼层唯一标识符,需要和名字完全一致 \n这里不能更改floorId,请通过另存为来实现" + }, + "title": { + "_leaf": true, + "_type": "textarea", + "_data": "楼层中文名,将在切换楼层和浏览地图时显示" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_data": "显示在状态栏中的层数" + }, + "width": { + "_leaf": true, + "_type": "textarea", + "_range": "false", + "_data": "地图x方向大小,这里不能更改,仅能在新建地图时设置,null视为13" + }, + "height": { + "_leaf": true, + "_type": "textarea", + "_range": "false", + "_data": "地图y方向大小,这里不能更改,仅能在新建地图时设置,null视为13" + }, + "canFlyTo": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)" + }, + "canUseQuickShop": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该层是否允许使用快捷商店" + }, + "cannotViewMap": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该层是否不允许被浏览地图看到;如果勾上则浏览地图会跳过该层" + }, + "cannotMoveDirectly": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该层是否不允许瞬间移动;如果勾上则不可在此层进行瞬移" + }, + "firstArrive": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_data": "第一次到该楼层触发的事件,可以双击进入事件编辑器。" + }, + "eachArrive": { + "_leaf": true, + "_type": "event", + "_event": "eachArrive", + "_data": "每次到该楼层触发的事件,可以双击进入事件编辑器;该事件会在firstArrive执行后再执行。" + }, + "parallelDo": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "在该层楼时执行的并行事件处理。\n可以在这里写上任意需要自动执行的脚本,比如打怪自动开门等。\n详见文档-事件-并行事件处理。" + }, + "upFloor": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", + "_data": "该层上楼点,如[2,3]。\n如果此项不为null,则楼层转换时的stair:upFloor,以及楼传器的落点会被替换成该点而不是该层的上楼梯。" + }, + "downFloor": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", + "_data": "该层下楼点,如[2,3]。\n如果此项不为null,则楼层转换时的stair:downFloor,以及楼传器的落点会被替换成该点而不是该层的下楼梯。" + }, + "defaultGround": { + "_leaf": true, + "_type": "select", + "_select": { + "values": Object.keys(editor.core.icons.icons.terrains) + }, + "_data": "默认地面的图块ID,此项修改后需要刷新才能看到效果。" + }, + "images": { + "_leaf": true, + "_type": "textarea", + "_data": "背景/前景图;你可以选择若干张图片来作为背景/前景素材。详细用法请参见文档“自定义素材”中的说明。" + }, + "color": { + "_leaf": true, + "_type": "textarea", + "_data": "该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组如[255,0,0,0.3]" + }, + "weather": { + "_leaf": true, + "_type": "textarea", + "_data": "该层的默认天气。本项可忽略表示晴天,如果写则第一项为\"rain\",\"snow\"或\"fog\"代表雨雪雾,第二项为1-10之间的数代表强度。\n如[\"rain\", 8]代表8级雨天。" + }, + "bgm": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [null].concat(Object.keys(editor.core.material.bgms)) + }, + "_data": "到达该层后默认播放的BGM。本项可忽略,或者为一个定义过的背景音乐如\"bgm.mp3\"。" + }, + "item_ratio": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>=0)||thiseval==null", + "_data": "每一层的宝石/血瓶效果,即获得宝石和血瓶时框内\"ratio\"的值。" + }, + "underGround": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否是地下层;如果该项为true则同层传送将传送至上楼梯" + } + } + }, + "loc": { + "_type": "object", + "_data": { + "events": { + "_leaf": true, + "_type": "event", + "_event": "event", + "_data": "该点的可能事件列表,可以双击进入事件编辑器。" + }, + "changeFloor": { + "_leaf": true, + "_type": "event", + "_event": "changeFloor", + "_data": "该点楼层转换事件;该事件不能和上面的events同时出现,否则会被覆盖" + }, + "afterBattle": { + "_leaf": true, + "_type": "event", + "_event": "afterBattle", + "_data": "该点战斗后可能触发的事件列表,可以双击进入事件编辑器。" + }, + "afterGetItem": { + "_leaf": true, + "_type": "event", + "_event": "afterGetItem", + "_data": "该点获得道具后可能触发的事件列表,可以双击进入事件编辑器。" + }, + "afterOpenDoor": { + "_leaf": true, + "_type": "event", + "_event": "afterOpenDoor", + "_data": "该点开完门后可能触发的事件列表,可以双击进入事件编辑器。" + }, + "cannotMove": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null||(thiseval instanceof Array)", + "_data": "该点不可通行的方向 \n 可以在这里定义该点不能前往哪个方向,可以达到悬崖之类的效果\n例如 [\"up\", \"left\"] 代表该点不能往上和左走" + } + } + } + } + } + } } \ No newline at end of file diff --git a/_server/data.comment.js b/_server/data.comment.js index c4b8b951..026ab732 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -1,677 +1,674 @@ -var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = -{ - - "_type": "object", - "_data": { - "main": { - - "_type": "object", - "_data": { - "floorIds": { - "_leaf": true, - "_type": "textarea", - "_range": "editor.mode.checkFloorIds(thiseval)", - "_data": "在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器、浏览地图和上/下楼器的顺序" - }, - "images": { - "_leaf": true, - "_type": "textarea", - "_range": "editor.mode.checkUnique(thiseval)", - "_data": "在此存放所有可能使用的图片(tilesets除外) \n图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 \n 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 \n 依次向后添加" - }, - "tilesets": { - "_leaf": true, - "_type": "textarea", - "_range": "editor.mode.checkUnique(thiseval)", - "_data": "在此存放额外素材的图片名, \n可以自定导入任意张素材图片,无需PS,无需注册,即可直接在游戏中使用 \n 形式如[\"1.png\", \"2.png\"] ,将需要的素材图片放在images目录下 \n 素材的宽高必须都是32的倍数,且图片上的总图块数不超过1000(即最多有1000个32*32的图块在该图片上)" - }, - "animates": { - "_leaf": true, - "_type": "textarea", - "_range": "editor.mode.checkUnique(thiseval)", - "_data": "在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" \n 根据需求自行添加" - }, - "bgms": { - "_leaf": true, - "_type": "textarea", - "_range": "editor.mode.checkUnique(thiseval)", - "_data": "在此存放所有的bgm,和文件名一致。 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" - }, - "sounds": { - "_leaf": true, - "_type": "textarea", - "_range": "editor.mode.checkUnique(thiseval)", - "_data": "在此存放所有的SE,和文件名一致 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" - }, - "startBackground": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "标题界面的背景,建议使用jpg格式以压缩背景图空间" - }, - "startLogoStyle": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "标题样式:可以改变颜色,也可以写\"display: none\"来隐藏标题" - }, - "levelChoose": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Array && thiseval.length>=1 && thiseval[0] instanceof Array && thiseval[0].length==2", - "_data": "难度选择:每个数组的第一个是其在标题界面显示的难度,第二个是在游戏内部传输的字符串,会显示在状态栏,修改此处后需要在project/functions中作相应更改。\n如果需直接开始游戏将下面的startDirectly开关打开即可。" - }, - "equipName": { - "_leaf": true, - "_type": "textarea", - "_range": "(thiseval instanceof Array && thiseval.length<=6)||thiseval==null", - "_data": "装备位名称,为不超过6个的数组,此项的顺序与equiptype数值关联;例如可写[\"武器\",\"防具\",\"首饰\"]等等。" - }, - "startBgm": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "在标题界面应该播放的bgm内容" - }, - "statusLeftBackground": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "横屏时左侧状态栏的背景样式,可以定义背景图、平铺方式等。\n具体请网上搜索\"css background\"了解写法。\n如果弄一张图片作为背景图,推荐写法:\n\"url(project/images/XXX.png) 0 0/100% 100% no-repeat\"\n图片最好进行一些压缩等操作节省流量。" - }, - "statusTopBackground": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "竖屏时上方状态栏的背景样式,可以定义背景图、平铺方式等。\n具体请网上搜索\"css background\"了解写法。\n如果弄一张图片作为背景图,推荐写法:\n\"url(project/images/XXX.png) 0 0/100% 100% no-repeat\"\n图片最好进行一些压缩等操作节省流量。" - }, - "toolsBackground": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "竖屏时下方道具栏的背景样式,可以定义背景图、平铺方式等。\n具体请网上搜索\"css background\"了解写法。\n如果弄一张图片作为背景图,推荐写法:\n\"url(project/images/XXX.png) 0 0/100% 100% no-repeat\"\n图片最好进行一些压缩等操作节省流量。" - }, - "borderColor": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "边框颜色,包括游戏边界的边框和对话框边框等。" - }, - "statusBarColor": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "状态栏的文字颜色,默认是白色" - }, - "hardLabelColor": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "难度显示的颜色,默认是红色" - }, - "floorChangingBackground": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "楼层转换界面的背景样式;可以使用纯色(默认值black),也可以使用图片(参见状态栏的图片写法)" - }, - "floorChangingTextColor": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "楼层转换界面的文字颜色,默认是白色" - }, - "font": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "游戏中使用的字体,默认是Verdana" - } - } - }, - "firstData": { - - "_type": "object", - "_data": { - "title": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "游戏名,将显示在标题页面以及切换楼层的界面中" - }, - "name": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_range": "/^[a-zA-Z0-9_]{1,30}$/.test(thiseval)", - "_data": "游戏的唯一英文标识符。由英文、数字、下划线组成,不能超过30个字符。\n此项必须修改,其将直接影响到存档的定位!" - }, - "version": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "当前游戏版本;版本不一致的存档不能通用。" - }, - "floorId": { - "_leaf": true, - "_type": "select", - "_select": { - "values": data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds - }, - "_range": "data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(thiseval)!==-1", - "_data": "初始楼层的ID" - }, - "hero": { - - "_type": "object", - "_data": { - "name": { - "_leaf": true, - "_type": "textarea", - "_string": true, - "_data": "勇士名;可以改成喜欢的" - }, - "lv": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==~~thiseval &&thiseval>0", - "_data": "初始等级,该项必须为正整数" - }, - "hpmax": { - "_leaf": true, - "_type": "textarea", - "_data": "初始生命上限,只有在enableHPMax开启时才有效" - }, - "hp": { - "_leaf": true, - "_type": "textarea", - "_data": "初始生命值" - }, - "manamax": { - "_leaf": true, - "_type": "textarea", - "_data": "魔力上限;此项非负才会生效(null或小于0都不会生效)" - }, - "mana": { - "_leaf": true, - "_type": "textarea", - "_data": "初始魔力值,只在enableMana开启时才有效" - }, - "atk": { - "_leaf": true, - "_type": "textarea", - "_data": "初始攻击" - }, - "def": { - "_leaf": true, - "_type": "textarea", - "_data": "初始防御" - }, - "mdef": { - "_leaf": true, - "_type": "textarea", - "_data": "初始魔防" - }, - "money": { - "_leaf": true, - "_type": "textarea", - "_data": "初始金币" - }, - "experience": { - "_leaf": true, - "_type": "textarea", - "_data": "初始经验" - }, - "equipment": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Array", - "_data": "初始装上的装备,此处建议请直接留空数组" - }, - "items": { - - "_type": "object", - "_data": { - "keys": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", - "_data": "初始三种钥匙个数" - }, - "constants": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", - "_data": "初始永久道具个数,例如初始送手册可以写 {\"book\": 1}" - }, - "tools": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", - "_data": "初始消耗道具个数,例如初始有两破可以写 {\"pickaxe\": 2}" - }, - "equips": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", - "_data": "初始装备个数,例如初始送铁剑可以写 {\"sword1\": 1}" - } - } - }, - "loc": { - - "_type": "object", - "_data": { - "direction": { - "_leaf": true, - "_type": "select", - "_data": "勇士初始方向", - "_select": { - "values": [ - "up", - "down", - "left", - "right" - ] - }, - }, - "x": { - "_leaf": true, - "_type": "textarea", - "_data": "勇士初始x坐标" - }, - "y": { - "_leaf": true, - "_type": "textarea", - "_data": "勇士初始y坐标" - } - } - }, - "flags": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", - "_data": "游戏过程中的变量或flags" - }, - "steps": { - "_leaf": true, - "_type": "textarea", - "_data": "行走步数统计" - } - } - }, - "startCanvas": { - "_leaf": true, - "_type": "event", - "_event": "firstArrive", - "_range": "thiseval==null || thiseval instanceof Array", - "_data": "标题界面事件化,可以使用事件流的形式来绘制开始界面等。\n需要开启startUsingCanvas这个开关。\n详见文档-个性化-标题界面事件化。" - }, - "startText": { - "_leaf": true, - "_type": "event", - "_event": "firstArrive", - "_range": "thiseval==null || thiseval instanceof Array", - "_data": "游戏开始前剧情,可以执行任意自定义事件。\n双击进入事件编辑器。\n如果无剧情直接留一个空数组即可。" - }, - "shops": { - "_leaf": true, - "_type": "event", - "_event": "shop", - "_range": "thiseval instanceof Array", - "_data": "全局商店,是一个数组,可以双击进入事件编辑器。" - }, - "levelUp": { - "_leaf": true, - "_type": "event", - "_event": "level", - "_range": "thiseval==null || thiseval instanceof Array", - "_data": "经验升级所需要的数值,是一个数组,可以双击进行编辑。 \n 第一项为初始等级,仅title生效 \n 每一个里面可以含有三个参数 need, title, action \n need为所需要的经验数值,可以是个表达式。请确保need依次递增 \n title为该等级的名称,也可以省略代表使用系统默认值;本项将显示在状态栏中 \n action为本次升级所执行的事件,可由若干项组成" - } - } - }, - "values": { - - "_type": "object", - "_data": { - "lavaDamage": { - "_leaf": true, - "_type": "textarea", - "_data": "经过血网受到的伤害" - }, - "poisonDamage": { - "_leaf": true, - "_type": "textarea", - "_data": "中毒后每步受到的伤害" - }, - "weakValue": { - "_leaf": true, - "_type": "textarea", - "_data": "衰弱状态下攻防减少的数值\n如果此项不小于1,则作为实际下降的数值(比如10就是攻防各下降10)\n如果在0到1之间则为下降的比例(比如0.3就是下降30%的攻防)" - }, - "redJewel": { - "_leaf": true, - "_type": "textarea", - "_data": "红宝石加攻击的数值" - }, - "blueJewel": { - "_leaf": true, - "_type": "textarea", - "_data": "蓝宝石加防御的数值" - }, - "greenJewel": { - "_leaf": true, - "_type": "textarea", - "_data": "绿宝石加魔防的数值" - }, - "redPotion": { - "_leaf": true, - "_type": "textarea", - "_data": "红血瓶加血数值" - }, - "bluePotion": { - "_leaf": true, - "_type": "textarea", - "_data": "蓝血瓶加血数值" - }, - "yellowPotion": { - "_leaf": true, - "_type": "textarea", - "_data": "黄血瓶加血数值" - }, - "greenPotion": { - "_leaf": true, - "_type": "textarea", - "_data": "绿血瓶加血数值" - }, - "breakArmor": { - "_leaf": true, - "_type": "textarea", - "_data": "破甲的比例(战斗前,怪物附加角色防御的x倍作为伤害)" - }, - "counterAttack": { - "_leaf": true, - "_type": "textarea", - "_data": "反击的比例(战斗时,怪物每回合附加角色攻击的x倍作为伤害,无视角色防御)" - }, - "purify": { - "_leaf": true, - "_type": "textarea", - "_data": "净化的比例(战斗前,怪物附加勇士魔防的x倍作为伤害)" - }, - "hatred": { - "_leaf": true, - "_type": "textarea", - "_data": "仇恨属性中,每杀死一个怪物获得的仇恨值" - }, - "moveSpeed": { - "_leaf": true, - "_type": "textarea", - "_data": "行走速度,即勇士每走一格的时间,一般100比较合适" - }, - "animateSpeed": { - "_leaf": true, - "_type": "textarea", - "_data": "全局动画时间,即怪物振动频率,一般300比较合适" - }, - "floorChangeTime": { - "_leaf": true, - "_type": "textarea", - "_data": "默认楼层切换时间" - } - } - }, - "flags": { - - "_type": "object", - "_data": { - "enableFloor": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏显示当前楼层" - }, - "enableName": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏显示勇士名字" - }, - "enableLv": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏显示当前等级" - }, - "enableHPMax": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否是否启用生命上限" - }, - "enableMana": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否开启魔力值" - }, - "enableMDef": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏及战斗界面显示魔防(护盾)" - }, - "enableMoney": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏、怪物手册及战斗界面显示金币" - }, - "enableExperience": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏、怪物手册及战斗界面显示经验" - }, - "enableLevelUp": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否允许等级提升(进阶);如果上面enableExperience为false,则此项恒视为false" - }, - "levelUpLeftMode": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "进阶使用扣除模式,即在状态栏显示距离下个等级所需要的经验值;只有enableExperience和enableLevelUp均开启时才有效。" - }, - "enableKeys": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏显示三色钥匙数量" - }, - "enablePZF": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏显示破炸飞数量" - }, - "enableDebuff": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在状态栏显示毒衰咒" - }, - "enableSkill": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否启用技能栏" - }, - "flyNearStair": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否需要在楼梯边使用传送器" - }, - "pickaxeFourDirections": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "使用破墙镐是否四个方向都破坏;如果false则只破坏面前的墙壁" - }, - "bombFourDirections": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "使用炸弹是否四个方向都会炸;如果false则只炸面前的怪物(即和圣锤等价)" - }, - "snowFourDirections": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "使用冰冻徽章是否四个方向都会消除熔岩;如果false则只消除面前的熔岩" - }, - "bigKeyIsBox": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "如果此项为true,则视为钥匙盒,红黄蓝钥匙+1;若为false,则视为大黄门钥匙" - }, - "equipment": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "剑和盾是否作为装备。如果此项为true,则作为装备,需要在装备栏使用,否则将直接加属性。" - }, - "equipboxButton": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "状态栏的装备按钮。若此项为true则将状态栏中的楼层转换器按钮换为装备栏按钮" - }, - "enableAddPoint": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否支持加点" - }, - "enableNegativeDamage": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否支持负伤害(回血)" - }, - "hatredDecrease": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在和仇恨怪战斗后减一半的仇恨值,此项为false则和仇恨怪不会扣减仇恨值。" - }, - "betweenAttackCeil": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "夹击方式是向上取整还是向下取整。如果此项为true则为向上取整,为false则为向下取整" - }, - "useLoop": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否循环计算临界;如果此项为true则使用循环法(而不是回合数计算法)来算临界\n从V2.5.3开始,对于大数据的循环法将改为使用二分法进行计算" - }, - "startUsingCanvas": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否开始菜单canvas化;如果此项为true,则将使用canvas来绘制开始菜单" - }, - "startDirectly": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "点击“开始游戏”后是否立刻开始游戏而不显示难度选择界面" - }, - "statusCanvas": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否状态栏canvas化,即手动自定义绘制状态栏。\n如果此项开启,则可在脚本编辑的drawStatusBar中自定义绘制菜单栏。" - }, - "statusCanvasRowsOnMobile": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval==null || (thiseval>0 && thiseval<=4)", - "_data": "竖屏模式下,顶端状态栏canvas化后的行数。\n此项将决定竖屏的状态栏高度,如果设置则不小于1且不大于4。\n仅在statusCanvas开启时才有效" - }, - "displayEnemyDamage": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否地图怪物显伤;用户可以手动在菜单栏中开关" - }, - "displayCritical": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否地图显示临界;用户可以手动在菜单栏中开关" - }, - "displayExtraDamage": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否地图高级显伤(领域、夹击等);用户可以手动在菜单栏中开关" - }, - "enableGentleClick": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否允许轻触(获得面前物品)" - }, - "potionWhileRouting": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "寻路算法是否经过血瓶;如果该项为false,则寻路算法会自动尽量绕过血瓶" - }, - "ignoreChangeFloor": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "经过楼梯、传送门时是否能“穿透”。\n穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件" - }, - "canGoDeadZone": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否允许走到将死的领域上。如果此项为true,则可以走到将死的领域上" - }, - "enableMoveDirectly": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否允许瞬间移动" - }, - "enableDisabledShop": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否允许查看未开启状态的快捷商店内容;如果此项为真,则对于未开启状态的商店允许查看其内容(但不能购买)" - }, - "disableShopOnDamage": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否在经过领域/夹击/路障等伤害后禁用快捷商店。" - }, - "checkConsole": { - "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "是否检查控制台的开启情况。" - } - } - } - } +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:全塔属性的配置项。 + */ + +var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + "main": { + "_type": "object", + "_data": { + "floorIds": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkFloorIds(thiseval)", + "_data": "在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器、浏览地图和上/下楼器的顺序" + }, + "images": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkUnique(thiseval)", + "_data": "在此存放所有可能使用的图片(tilesets除外) \n图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 \n 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 \n 依次向后添加" + }, + "tilesets": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkUnique(thiseval)", + "_data": "在此存放额外素材的图片名, \n可以自定导入任意张素材图片,无需PS,无需注册,即可直接在游戏中使用 \n 形式如[\"1.png\", \"2.png\"] ,将需要的素材图片放在images目录下 \n 素材的宽高必须都是32的倍数,且图片上的总图块数不超过1000(即最多有1000个32*32的图块在该图片上)" + }, + "animates": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkUnique(thiseval)", + "_data": "在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" \n 根据需求自行添加" + }, + "bgms": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkUnique(thiseval)", + "_data": "在此存放所有的bgm,和文件名一致。 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" + }, + "sounds": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkUnique(thiseval)", + "_data": "在此存放所有的SE,和文件名一致 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" + }, + "startBackground": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "标题界面的背景,建议使用jpg格式以压缩背景图空间" + }, + "startLogoStyle": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "标题样式:可以改变颜色,也可以写\"display: none\"来隐藏标题" + }, + "levelChoose": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Array && thiseval.length>=1 && thiseval[0] instanceof Array && thiseval[0].length==2", + "_data": "难度选择:每个数组的第一个是其在标题界面显示的难度,第二个是在游戏内部传输的字符串,会显示在状态栏,修改此处后需要在project/functions中作相应更改。\n如果需直接开始游戏将下面的startDirectly开关打开即可。" + }, + "equipName": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval instanceof Array && thiseval.length<=6)||thiseval==null", + "_data": "装备位名称,为不超过6个的数组,此项的顺序与equiptype数值关联;例如可写[\"武器\",\"防具\",\"首饰\"]等等。" + }, + "startBgm": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "在标题界面应该播放的bgm内容" + }, + "statusLeftBackground": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "横屏时左侧状态栏的背景样式,可以定义背景图、平铺方式等。\n具体请网上搜索\"css background\"了解写法。\n如果弄一张图片作为背景图,推荐写法:\n\"url(project/images/XXX.png) 0 0/100% 100% no-repeat\"\n图片最好进行一些压缩等操作节省流量。" + }, + "statusTopBackground": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "竖屏时上方状态栏的背景样式,可以定义背景图、平铺方式等。\n具体请网上搜索\"css background\"了解写法。\n如果弄一张图片作为背景图,推荐写法:\n\"url(project/images/XXX.png) 0 0/100% 100% no-repeat\"\n图片最好进行一些压缩等操作节省流量。" + }, + "toolsBackground": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "竖屏时下方道具栏的背景样式,可以定义背景图、平铺方式等。\n具体请网上搜索\"css background\"了解写法。\n如果弄一张图片作为背景图,推荐写法:\n\"url(project/images/XXX.png) 0 0/100% 100% no-repeat\"\n图片最好进行一些压缩等操作节省流量。" + }, + "borderColor": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "边框颜色,包括游戏边界的边框和对话框边框等。" + }, + "statusBarColor": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "状态栏的文字颜色,默认是白色" + }, + "hardLabelColor": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "难度显示的颜色,默认是红色" + }, + "floorChangingBackground": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "楼层转换界面的背景样式;可以使用纯色(默认值black),也可以使用图片(参见状态栏的图片写法)" + }, + "floorChangingTextColor": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "楼层转换界面的文字颜色,默认是白色" + }, + "font": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "游戏中使用的字体,默认是Verdana" + } + } + }, + "firstData": { + "_type": "object", + "_data": { + "title": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "游戏名,将显示在标题页面以及切换楼层的界面中" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_range": "/^[a-zA-Z0-9_]{1,30}$/.test(thiseval)", + "_data": "游戏的唯一英文标识符。由英文、数字、下划线组成,不能超过30个字符。\n此项必须修改,其将直接影响到存档的定位!" + }, + "version": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "当前游戏版本;版本不一致的存档不能通用。" + }, + "floorId": { + "_leaf": true, + "_type": "select", + "_select": { + "values": data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds + }, + "_range": "data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(thiseval)!==-1", + "_data": "初始楼层的ID" + }, + "hero": { + "_type": "object", + "_data": { + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "勇士名;可以改成喜欢的" + }, + "lv": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval &&thiseval>0", + "_data": "初始等级,该项必须为正整数" + }, + "hpmax": { + "_leaf": true, + "_type": "textarea", + "_data": "初始生命上限,只有在enableHPMax开启时才有效" + }, + "hp": { + "_leaf": true, + "_type": "textarea", + "_data": "初始生命值" + }, + "manamax": { + "_leaf": true, + "_type": "textarea", + "_data": "魔力上限;此项非负才会生效(null或小于0都不会生效)" + }, + "mana": { + "_leaf": true, + "_type": "textarea", + "_data": "初始魔力值,只在enableMana开启时才有效" + }, + "atk": { + "_leaf": true, + "_type": "textarea", + "_data": "初始攻击" + }, + "def": { + "_leaf": true, + "_type": "textarea", + "_data": "初始防御" + }, + "mdef": { + "_leaf": true, + "_type": "textarea", + "_data": "初始魔防" + }, + "money": { + "_leaf": true, + "_type": "textarea", + "_data": "初始金币" + }, + "experience": { + "_leaf": true, + "_type": "textarea", + "_data": "初始经验" + }, + "equipment": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Array", + "_data": "初始装上的装备,此处建议请直接留空数组" + }, + "items": { + "_type": "object", + "_data": { + "keys": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_data": "初始三种钥匙个数" + }, + "constants": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_data": "初始永久道具个数,例如初始送手册可以写 {\"book\": 1}" + }, + "tools": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_data": "初始消耗道具个数,例如初始有两破可以写 {\"pickaxe\": 2}" + }, + "equips": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_data": "初始装备个数,例如初始送铁剑可以写 {\"sword1\": 1}" + } + } + }, + "loc": { + "_type": "object", + "_data": { + "direction": { + "_leaf": true, + "_type": "select", + "_data": "勇士初始方向", + "_select": { + "values": [ + "up", + "down", + "left", + "right" + ] + }, + }, + "x": { + "_leaf": true, + "_type": "textarea", + "_data": "勇士初始x坐标" + }, + "y": { + "_leaf": true, + "_type": "textarea", + "_data": "勇士初始y坐标" + } + } + }, + "flags": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_data": "游戏过程中的变量或flags" + }, + "steps": { + "_leaf": true, + "_type": "textarea", + "_data": "行走步数统计" + } + } + }, + "startCanvas": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_range": "thiseval==null || thiseval instanceof Array", + "_data": "标题界面事件化,可以使用事件流的形式来绘制开始界面等。\n需要开启startUsingCanvas这个开关。\n详见文档-个性化-标题界面事件化。" + }, + "startText": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_range": "thiseval==null || thiseval instanceof Array", + "_data": "游戏开始前剧情,可以执行任意自定义事件。\n双击进入事件编辑器。\n如果无剧情直接留一个空数组即可。" + }, + "shops": { + "_leaf": true, + "_type": "event", + "_event": "shop", + "_range": "thiseval instanceof Array", + "_data": "全局商店,是一个数组,可以双击进入事件编辑器。" + }, + "levelUp": { + "_leaf": true, + "_type": "event", + "_event": "level", + "_range": "thiseval==null || thiseval instanceof Array", + "_data": "经验升级所需要的数值,是一个数组,可以双击进行编辑。 \n 第一项为初始等级,仅title生效 \n 每一个里面可以含有三个参数 need, title, action \n need为所需要的经验数值,可以是个表达式。请确保need依次递增 \n title为该等级的名称,也可以省略代表使用系统默认值;本项将显示在状态栏中 \n action为本次升级所执行的事件,可由若干项组成" + } + } + }, + "values": { + "_type": "object", + "_data": { + "lavaDamage": { + "_leaf": true, + "_type": "textarea", + "_data": "经过血网受到的伤害" + }, + "poisonDamage": { + "_leaf": true, + "_type": "textarea", + "_data": "中毒后每步受到的伤害" + }, + "weakValue": { + "_leaf": true, + "_type": "textarea", + "_data": "衰弱状态下攻防减少的数值\n如果此项不小于1,则作为实际下降的数值(比如10就是攻防各下降10)\n如果在0到1之间则为下降的比例(比如0.3就是下降30%的攻防)" + }, + "redJewel": { + "_leaf": true, + "_type": "textarea", + "_data": "红宝石加攻击的数值" + }, + "blueJewel": { + "_leaf": true, + "_type": "textarea", + "_data": "蓝宝石加防御的数值" + }, + "greenJewel": { + "_leaf": true, + "_type": "textarea", + "_data": "绿宝石加魔防的数值" + }, + "redPotion": { + "_leaf": true, + "_type": "textarea", + "_data": "红血瓶加血数值" + }, + "bluePotion": { + "_leaf": true, + "_type": "textarea", + "_data": "蓝血瓶加血数值" + }, + "yellowPotion": { + "_leaf": true, + "_type": "textarea", + "_data": "黄血瓶加血数值" + }, + "greenPotion": { + "_leaf": true, + "_type": "textarea", + "_data": "绿血瓶加血数值" + }, + "breakArmor": { + "_leaf": true, + "_type": "textarea", + "_data": "破甲的比例(战斗前,怪物附加角色防御的x倍作为伤害)" + }, + "counterAttack": { + "_leaf": true, + "_type": "textarea", + "_data": "反击的比例(战斗时,怪物每回合附加角色攻击的x倍作为伤害,无视角色防御)" + }, + "purify": { + "_leaf": true, + "_type": "textarea", + "_data": "净化的比例(战斗前,怪物附加勇士魔防的x倍作为伤害)" + }, + "hatred": { + "_leaf": true, + "_type": "textarea", + "_data": "仇恨属性中,每杀死一个怪物获得的仇恨值" + }, + "moveSpeed": { + "_leaf": true, + "_type": "textarea", + "_data": "行走速度,即勇士每走一格的时间,一般100比较合适" + }, + "animateSpeed": { + "_leaf": true, + "_type": "textarea", + "_data": "全局动画时间,即怪物振动频率,一般300比较合适" + }, + "floorChangeTime": { + "_leaf": true, + "_type": "textarea", + "_data": "默认楼层切换时间" + } + } + }, + "flags": { + "_type": "object", + "_data": { + "enableFloor": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示当前楼层" + }, + "enableName": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示勇士名字" + }, + "enableLv": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示当前等级" + }, + "enableHPMax": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否是否启用生命上限" + }, + "enableMana": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否开启魔力值" + }, + "enableMDef": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏及战斗界面显示魔防(护盾)" + }, + "enableMoney": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏、怪物手册及战斗界面显示金币" + }, + "enableExperience": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏、怪物手册及战斗界面显示经验" + }, + "enableLevelUp": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否允许等级提升(进阶);如果上面enableExperience为false,则此项恒视为false" + }, + "levelUpLeftMode": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "进阶使用扣除模式,即在状态栏显示距离下个等级所需要的经验值;只有enableExperience和enableLevelUp均开启时才有效。" + }, + "enableKeys": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示三色钥匙数量" + }, + "enablePZF": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示破炸飞数量" + }, + "enableDebuff": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示毒衰咒" + }, + "enableSkill": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否启用技能栏" + }, + "flyNearStair": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否需要在楼梯边使用传送器" + }, + "pickaxeFourDirections": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "使用破墙镐是否四个方向都破坏;如果false则只破坏面前的墙壁" + }, + "bombFourDirections": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "使用炸弹是否四个方向都会炸;如果false则只炸面前的怪物(即和圣锤等价)" + }, + "snowFourDirections": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "使用冰冻徽章是否四个方向都会消除熔岩;如果false则只消除面前的熔岩" + }, + "bigKeyIsBox": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "如果此项为true,则视为钥匙盒,红黄蓝钥匙+1;若为false,则视为大黄门钥匙" + }, + "equipment": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "剑和盾是否作为装备。如果此项为true,则作为装备,需要在装备栏使用,否则将直接加属性。" + }, + "equipboxButton": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "状态栏的装备按钮。若此项为true则将状态栏中的楼层转换器按钮换为装备栏按钮" + }, + "enableAddPoint": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否支持加点" + }, + "enableNegativeDamage": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否支持负伤害(回血)" + }, + "hatredDecrease": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在和仇恨怪战斗后减一半的仇恨值,此项为false则和仇恨怪不会扣减仇恨值。" + }, + "betweenAttackCeil": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "夹击方式是向上取整还是向下取整。如果此项为true则为向上取整,为false则为向下取整" + }, + "useLoop": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否循环计算临界;如果此项为true则使用循环法(而不是回合数计算法)来算临界\n从V2.5.3开始,对于大数据的循环法将改为使用二分法进行计算" + }, + "startUsingCanvas": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否开始菜单canvas化;如果此项为true,则将使用canvas来绘制开始菜单" + }, + "startDirectly": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "点击“开始游戏”后是否立刻开始游戏而不显示难度选择界面" + }, + "statusCanvas": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否状态栏canvas化,即手动自定义绘制状态栏。\n如果此项开启,则可在脚本编辑的drawStatusBar中自定义绘制菜单栏。" + }, + "statusCanvasRowsOnMobile": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==null || (thiseval>0 && thiseval<=4)", + "_data": "竖屏模式下,顶端状态栏canvas化后的行数。\n此项将决定竖屏的状态栏高度,如果设置则不小于1且不大于4。\n仅在statusCanvas开启时才有效" + }, + "displayEnemyDamage": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否地图怪物显伤;用户可以手动在菜单栏中开关" + }, + "displayCritical": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否地图显示临界;用户可以手动在菜单栏中开关" + }, + "displayExtraDamage": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否地图高级显伤(领域、夹击等);用户可以手动在菜单栏中开关" + }, + "enableGentleClick": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否允许轻触(获得面前物品)" + }, + "potionWhileRouting": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "寻路算法是否经过血瓶;如果该项为false,则寻路算法会自动尽量绕过血瓶" + }, + "ignoreChangeFloor": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "经过楼梯、传送门时是否能“穿透”。\n穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件" + }, + "canGoDeadZone": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否允许走到将死的领域上。如果此项为true,则可以走到将死的领域上" + }, + "enableMoveDirectly": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否允许瞬间移动" + }, + "enableDisabledShop": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否允许查看未开启状态的快捷商店内容;如果此项为真,则对于未开启状态的商店允许查看其内容(但不能购买)" + }, + "disableShopOnDamage": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在经过领域/夹击/路障等伤害后禁用快捷商店。" + }, + "checkConsole": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否检查控制台的开启情况。" + } + } + } + } } \ No newline at end of file diff --git a/_server/editor_multi.js b/_server/editor_multi.js index e68cf56a..df5307bb 100644 --- a/_server/editor_multi.js +++ b/_server/editor_multi.js @@ -138,6 +138,7 @@ editor_multi = function () { } if (editor_multi.id === 'importFile') { + _format(); editor_multi.id = ''; editor_multi.writeFileDone(); return; @@ -210,7 +211,10 @@ editor_multi = function () { editor_multi.writeFileDone = function () { fs.writeFile(_fileValues[0], editor.util.encode64(codeEditor.getValue() || ''), 'base64', function (err, data) { if (err) printe('文件写入失败,请手动粘贴至' + _fileValues[0] + '\n' + err); - else editor_multi.hide(); + else { + editor_multi.hide(); + printf(_fileValues[0] + " 写入成功,F5刷新后生效"); + } }); } diff --git a/_server/editor_table.js b/_server/editor_table.js index 33a1465f..a3b742cd 100644 --- a/_server/editor_table.js +++ b/_server/editor_table.js @@ -121,7 +121,7 @@ editor_table_wrapper = function (editor) { // 事实上能执行到这一步工程没崩掉打不开,就继续吧.. if (keysForTableOrder[ii] === voidMark) { if (typeof id_815975ad_ee6f_4684_aac7_397b7e392702 === "undefined") { - alert('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + // alert('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') console.error('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') id_815975ad_ee6f_4684_aac7_397b7e392702 = 1; } diff --git a/_server/events.comment.js b/_server/events.comment.js index a96301d7..4432901f 100644 --- a/_server/events.comment.js +++ b/_server/events.comment.js @@ -1,43 +1,46 @@ -var events_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = -{ - - "_type": "object", - "_data": { - "commonEvent": { +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:公共事件。 + */ - "_type": "object", - "_data": function (key) { - var obj = { - "加点事件": { - "_leaf": true, - "_type": "event", - "_range": "thiseval instanceof Array", - "_event": "commonEvent", - "_data": "打败怪物后加点" - }, - "毒衰咒处理": { - "_leaf": true, - "_type": "event", - "_range": "thiseval instanceof Array", - "_event": "commonEvent", - "_data": "毒衰咒效果处理" - }, - "滑冰事件": { - "_leaf": true, - "_type": "event", - "_range": "thiseval instanceof Array", - "_event": "commonEvent", - "_data": "滑冰事件" - }, - } - if (obj[key]) return obj[key]; - return { - "_leaf": true, - "_type": "event", - "_event": "commonEvent", - "_data": "自定义公共事件,可以双击进入事件编辑器" - } - } - } - } +var events_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + "commonEvent": { + "_type": "object", + "_data": function (key) { + var obj = { + "加点事件": { + "_leaf": true, + "_type": "event", + "_range": "thiseval instanceof Array", + "_event": "commonEvent", + "_data": "打败怪物后加点" + }, + "毒衰咒处理": { + "_leaf": true, + "_type": "event", + "_range": "thiseval instanceof Array", + "_event": "commonEvent", + "_data": "毒衰咒效果处理" + }, + "滑冰事件": { + "_leaf": true, + "_type": "event", + "_range": "thiseval instanceof Array", + "_event": "commonEvent", + "_data": "滑冰事件" + }, + } + if (obj[key]) return obj[key]; + return { + "_leaf": true, + "_type": "event", + "_event": "commonEvent", + "_data": "自定义公共事件,可以双击进入事件编辑器" + } + } + } + } } \ No newline at end of file diff --git a/_server/functions.comment.js b/_server/functions.comment.js index 1d295260..44396901 100644 --- a/_server/functions.comment.js +++ b/_server/functions.comment.js @@ -1,212 +1,216 @@ -var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = -{ - - "_type": "object", - "_data": { - "events": { - "_type": "object", - "_data": { - "resetGame": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "重置整个游戏" - }, - "setInitData": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "设置初始属性" - }, - "win": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "游戏获胜事件" - }, - "lose": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "游戏失败事件" - }, - "changingFloor": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "切换楼层中" - }, - "afterChangeFloor": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "切换楼层后" - }, - "flyTo": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "楼层飞行" - }, - "beforeBattle": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "战前事件" - }, - "afterBattle": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "战后事件" - }, - "afterOpenDoor": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "开门后事件" - }, - "afterGetItem": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "获得道具后事件" - }, - "afterChangeLight": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "改变亮灯事件" - }, - "afterPushBox": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "推箱子事件" - }, - "afterUseBomb": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "炸弹事件" - }, - "canUseQuickShop": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "能否用快捷商店" - } - } - }, - "enemys": { - "_type": "object", - "_data": { - "getSpecials": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "怪物特殊属性定义" - }, - "getEnemyInfo": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "获得怪物真实属性" - }, - "getDamageInfo": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "获得战斗伤害信息" - }, - "updateEnemys": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "更新怪物数据" - } - } - }, - "actions": { - "_type": "object", - "_data": { - "onKeyUp": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "按键处理" - } - } - }, - "control": { - "_type": "object", - "_data": { - "saveData": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "存档操作" - }, - "loadData": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "读档操作" - }, - "updateStatusBar": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "更新状态栏" - }, - "updateCheckBlock": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "阻激夹域伤害" - }, - "moveOneStep": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "每一步后的操作" - }, - "moveDirectly": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "瞬间移动处理" - }, - "parallelDo": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "并行事件处理" - } - } - }, - "ui": { - "_type": "object", - "_data": { - "drawStatusBar": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "自绘状态栏" - }, - "drawStatistics": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "地图数据统计" - }, - "drawAbout": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "绘制关于界面" - } - } - } - } +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:脚本编辑。 + */ + +var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + "events": { + "_type": "object", + "_data": { + "resetGame": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "重置整个游戏" + }, + "setInitData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "设置初始属性" + }, + "win": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "游戏获胜事件" + }, + "lose": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "游戏失败事件" + }, + "changingFloor": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "切换楼层中" + }, + "afterChangeFloor": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "切换楼层后" + }, + "flyTo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "楼层飞行" + }, + "beforeBattle": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "战前事件" + }, + "afterBattle": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "战后事件" + }, + "afterOpenDoor": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "开门后事件" + }, + "afterGetItem": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "获得道具后事件" + }, + "afterChangeLight": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "改变亮灯事件" + }, + "afterPushBox": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "推箱子事件" + }, + "afterUseBomb": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "炸弹事件" + }, + "canUseQuickShop": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "能否用快捷商店" + } + } + }, + "enemys": { + "_type": "object", + "_data": { + "getSpecials": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "怪物特殊属性定义" + }, + "getEnemyInfo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "获得怪物真实属性" + }, + "getDamageInfo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "获得战斗伤害信息" + }, + "updateEnemys": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "更新怪物数据" + } + } + }, + "actions": { + "_type": "object", + "_data": { + "onKeyUp": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "按键处理" + } + } + }, + "control": { + "_type": "object", + "_data": { + "saveData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "存档操作" + }, + "loadData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "读档操作" + }, + "updateStatusBar": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "更新状态栏" + }, + "updateCheckBlock": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "阻激夹域伤害" + }, + "moveOneStep": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "每一步后的操作" + }, + "moveDirectly": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "瞬间移动处理" + }, + "parallelDo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "并行事件处理" + } + } + }, + "ui": { + "_type": "object", + "_data": { + "drawStatusBar": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "自绘状态栏" + }, + "drawStatistics": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "地图数据统计" + }, + "drawAbout": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "绘制关于界面" + } + } + } + } } \ No newline at end of file diff --git a/_server/plugins.comment.js b/_server/plugins.comment.js index e708c576..fc4f5144 100644 --- a/_server/plugins.comment.js +++ b/_server/plugins.comment.js @@ -1,27 +1,32 @@ -var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = -{ - "_type": "object", - "_data": function (key) { - var obj = { - "init": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string'", - "_data": "自定义插件" - }, - "drawLight": { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "绘制灯光效果" - }, - } - if (obj[key]) return obj[key]; - return { - "_leaf": true, - "_type": "textarea", - "_range": "typeof(thiseval)=='string' || thiseval==null", - "_data": "自定义插件" - } - } +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:插件编写。 + */ + +var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": function (key) { + var obj = { + "init": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string'", + "_data": "自定义插件" + }, + "drawLight": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "绘制灯光效果" + }, + } + if (obj[key]) return obj[key]; + return { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "自定义插件" + } + } } \ No newline at end of file diff --git a/libs/enemys.js b/libs/enemys.js index 2b7e551d..51ef8552 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -359,6 +359,8 @@ enemys.prototype._getCurrentEnemys_sort = function (enemys) { }); } -enemys.prototype.hasEnemyLeft = function (floorId) { - return core.getCurrentEnemys(floorId).length > 0; +enemys.prototype.hasEnemyLeft = function (enemyId, floorId) { + return core.getCurrentEnemys(floorId).filter(function (enemy) { + return enemyId == null || enemy.id == enemyId; + }).length > 0; } \ No newline at end of file From 616b77a760ece42bc6d9032a5c34e6532e518984 Mon Sep 17 00:00:00 2001 From: YouWei Zhao Date: Sat, 6 Apr 2019 08:31:57 -0400 Subject: [PATCH 55/64] mv *comment.js --- _server/editor_file.js | 2 +- _server/editor_multi.js | 14 +++++++------- _server/refactoring.md | 2 +- _server/{ => table}/comment.js | 0 _server/{ => table}/data.comment.js | 0 _server/{ => table}/events.comment.js | 0 _server/{ => table}/functions.comment.js | 0 _server/{ => table}/maps.comment.js | 0 _server/{ => table}/plugins.comment.js | 0 9 files changed, 9 insertions(+), 9 deletions(-) rename _server/{ => table}/comment.js (100%) rename _server/{ => table}/data.comment.js (100%) rename _server/{ => table}/events.comment.js (100%) rename _server/{ => table}/functions.comment.js (100%) rename _server/{ => table}/maps.comment.js (100%) rename _server/{ => table}/plugins.comment.js (100%) diff --git a/_server/editor_file.js b/_server/editor_file.js index e074a1d5..467cd9dc 100644 --- a/_server/editor_file.js +++ b/_server/editor_file.js @@ -17,7 +17,7 @@ editor_file = function (editor, callback) { if (window.location.href.indexOf('_server') !== -1) script.src = key + '.js'; else - script.src = '_server/' + key + '.js'; + script.src = '_server/table/' + key + '.js'; document.body.appendChild(script); script.onload = function () { editor_file[value] = eval(key.replace('.', '_') + '_c456ea59_6018_45ef_8bcc_211a24c627dc'); diff --git a/_server/editor_multi.js b/_server/editor_multi.js index df5307bb..476536bb 100644 --- a/_server/editor_multi.js +++ b/_server/editor_multi.js @@ -220,13 +220,13 @@ editor_multi = function () { editor_multi.editCommentJs = function (mod) { var dict = { - loc: '_server/comment.js', - enemyitem: '_server/comment.js', - floor: '_server/comment.js', - tower: '_server/data.comment.js', - functions: '_server/functions.comment.js', - commonevent: '_server/events.comment.js', - plugins: '_server/plugins.comment.js', + loc: '_server/table/comment.js', + enemyitem: '_server/table/comment.js', + floor: '_server/table/comment.js', + tower: '_server/table/data.comment.js', + functions: '_server/table/functions.comment.js', + commonevent: '_server/table/events.comment.js', + plugins: '_server/table/plugins.comment.js', } editor_multi.lintAutocomplete = true editor_multi.setLint() diff --git a/_server/refactoring.md b/_server/refactoring.md index c75dc64b..d9e80082 100644 --- a/_server/refactoring.md +++ b/_server/refactoring.md @@ -16,7 +16,7 @@ + [ ] editor 执行初始化流程加组合各组件 + [ ] 原editor_mode 移除 + [ ] 原vm 移除 -+ [ ] \*comment.js 表格注释与结构, 移至comment/\*comment.js ++ [x] \*comment.js 表格注释与结构, 移至table/\*comment.js ## 对象结构 diff --git a/_server/comment.js b/_server/table/comment.js similarity index 100% rename from _server/comment.js rename to _server/table/comment.js diff --git a/_server/data.comment.js b/_server/table/data.comment.js similarity index 100% rename from _server/data.comment.js rename to _server/table/data.comment.js diff --git a/_server/events.comment.js b/_server/table/events.comment.js similarity index 100% rename from _server/events.comment.js rename to _server/table/events.comment.js diff --git a/_server/functions.comment.js b/_server/table/functions.comment.js similarity index 100% rename from _server/functions.comment.js rename to _server/table/functions.comment.js diff --git a/_server/maps.comment.js b/_server/table/maps.comment.js similarity index 100% rename from _server/maps.comment.js rename to _server/table/maps.comment.js diff --git a/_server/plugins.comment.js b/_server/table/plugins.comment.js similarity index 100% rename from _server/plugins.comment.js rename to _server/table/plugins.comment.js From 26ce1f904802a131218d8d0c8ebe613481d923e0 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Sun, 7 Apr 2019 14:50:21 +0800 Subject: [PATCH 56/64] 15x15 toolHeight --- libs/control.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/control.js b/libs/control.js index 5f38acfd..0a574875 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2637,7 +2637,7 @@ control.prototype._resize_toolBar = function (obj) { } control.prototype._resize_tools = function (obj) { - var toolsHeight = 32 * core.domStyle.scale * (core.domStyle.isVertical ? 0.95 : 1); + var toolsHeight = 32 * core.domStyle.scale * (core.domStyle.isVertical && !obj.is15x15 ? 0.95 : 1); var toolsMarginLeft; if (core.domStyle.isVertical) toolsMarginLeft = (core.__HALF_SIZE__ - 3) * 3 * core.domStyle.scale; From 3dd292e85348f2ce32ab06e52c5e22cd3aaa0ae7 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 7 Apr 2019 23:35:08 +0800 Subject: [PATCH 57/64] font bold --- _docs/api.md | 6 +++--- libs/control.js | 11 +++++++---- libs/events.js | 1 + libs/ui.js | 11 ++++++----- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index ee5fbbaf..a61be655 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -394,12 +394,12 @@ core.updateViewport() 根据大地图的偏移量来更新窗口的视野范围。 -core.nextX(n) / core.nextY(m) +core.nextX(n) / core.nextY(n) 获得勇士面对的第n个位置的横纵坐标。n可不填,默认为1。 -core.nearHero(x, y) -判定某个点是否和勇士的距离不大于1。 +core.nearHero(x, y, n) +判定某个点是否和勇士的距离不大于n。n可不填,默认为1。 core.gatherFollowers() diff --git a/libs/control.js b/libs/control.js index 0a574875..93b9bff4 100644 --- a/libs/control.js +++ b/libs/control.js @@ -856,17 +856,20 @@ control.prototype.updateViewport = function() { ////// 获得勇士面对位置的x坐标 ////// control.prototype.nextX = function(n) { - return core.getHeroLoc('x')+core.utils.scan[core.getHeroLoc('direction')].x*(n||1); + if (n == null) n = 1; + return core.getHeroLoc('x')+core.utils.scan[core.getHeroLoc('direction')].x*n; } ////// 获得勇士面对位置的y坐标 ////// control.prototype.nextY = function (n) { - return core.getHeroLoc('y')+core.utils.scan[core.getHeroLoc('direction')].y*(n||1); + if (n == null) n = 1; + return core.getHeroLoc('y')+core.utils.scan[core.getHeroLoc('direction')].y*n; } ////// 某个点是否在勇士旁边 ////// -control.prototype.nearHero = function (x, y) { - return Math.abs(x-core.getHeroLoc('x'))+Math.abs(y-core.getHeroLoc('y'))<=1; +control.prototype.nearHero = function (x, y, n) { + if (n == null) n = 1; + return Math.abs(x-core.getHeroLoc('x'))+Math.abs(y-core.getHeroLoc('y'))<=n; } ////// 聚集跟随者 ////// diff --git a/libs/events.js b/libs/events.js index 814afc2b..341bdcc8 100644 --- a/libs/events.js +++ b/libs/events.js @@ -431,6 +431,7 @@ events.prototype._sys_getItem = function (data, callback) { ////// 获得某个物品 ////// events.prototype.getItem = function (id, num, x, y, callback) { + if (num == null) num = 1; num = num || 1; var itemCls = core.material.items[id].cls; core.items.getItemEffect(id, num); diff --git a/libs/ui.js b/libs/ui.js index 9d63d8d0..2c310153 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -588,14 +588,15 @@ ui.prototype.drawTextContent = function (ctx, content, config) { ctx = core.getContextByName(ctx); if (!ctx) return; // 设置默认配置项 + var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; config = core.clone(config || {}); config.left = config.left || 0; config.right = config.left + (config.maxWidth == null ? ctx.canvas.width : config.maxWidth); config.top = config.top || 0; - config.color = config.color || core.arrayToRGBA(core.status.textAttribute.text); - config.bold = config.bold || false; - config.align = config.align || core.status.textAttribute.align || "left"; - config.fontSize = config.fontSize || core.status.textAttribute.textfont; + config.color = config.color || core.arrayToRGBA(textAttribute.text); + if (config.bold == null) config.bold = textAttribute.bold; + config.align = config.align || textAttribute.align || "left"; + config.fontSize = config.fontSize || textAttribute.textfont; config.lineHeight = config.lineHeight || (config.fontSize * 1.3); config.time = config.time || 0; @@ -783,7 +784,7 @@ ui.prototype.drawTextBox = function(content, showAll) { var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom, pInfo); var alpha = isWindowSkin ? 0.85 : textAttribute.background[3]; - // Step 4: 绘制标题、头像、 + // Step 4: 绘制标题、头像、动画 var content_top = this._drawTextBox_drawTitleAndIcon(titleInfo, hPos, vPos, alpha); // Step 5: 绘制正文 From b81c94fe625e87d527ce78988527917dfa5154f9 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 8 Apr 2019 01:12:16 +0800 Subject: [PATCH 58/64] steelDoorWithoutKey --- _server/MotaAction.g4 | 4 ++-- _server/table/data.comment.js | 6 ++++++ libs/events.js | 3 +++ project/data.js | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index a8741045..41da0c2d 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -2021,8 +2021,8 @@ Global_Value_List Global_Flag_List - : '显示当前楼层'|'显示勇士图标'|'显示当前等级'|'启用生命上限'|'显示魔力值'|'显示魔防值'|'显示金币值'|'显示经验值'|'允许等级提升'|'升级扣除模式'|'显示钥匙数量'|'显示破炸飞'|'显示毒衰咒'|'显示当前技能'|'楼梯边才能楼传'|'开启加点'|'开启负伤'|'仇恨怪战后扣减一半'|'夹击是否上整'|'循环计算临界'|'允许轻按'|'寻路算法不绕血瓶'|'允许走到将死领域'|'允许瞬间移动'|'允许查看禁用商店'|'阻激夹域后禁用快捷商店'|'检查控制台' - /*Global_Flag_List ['enableFloor','enableName','enableLv', 'enableHPMax', 'enableMana', 'enableMDef', 'enableMoney', 'enableExperience', 'enableLevelUp', 'levelUpLeftMode', 'enableKeys', 'enablePZF', 'enableDebuff', 'enableSkill', 'flyNearStair', 'enableAddPoint', 'enableNegativeDamage', 'hatredDecrease', 'betweenAttackCeil', 'useLoop', 'enableGentleClick', 'potionWhileRouting', 'canGoDeadZone', 'enableMoveDirectly', 'enableDisabledShop', 'disableShopOnDamage', 'checkConsole']*/; + : '显示当前楼层'|'显示勇士图标'|'显示当前等级'|'启用生命上限'|'显示魔力值'|'显示魔防值'|'显示金币值'|'显示经验值'|'允许等级提升'|'升级扣除模式'|'显示钥匙数量'|'显示破炸飞'|'显示毒衰咒'|'显示当前技能'|'楼梯边才能楼传'|'破墙镐四方向'|'炸弹四方向'|'冰冻徽章四方向'|'铁门不需要钥匙'|'开启加点'|'开启负伤'|'仇恨怪战后扣减一半'|'夹击是否上整'|'循环计算临界'|'允许轻按'|'寻路算法不绕血瓶'|'允许走到将死领域'|'允许瞬间移动'|'允许查看禁用商店'|'阻激夹域后禁用快捷商店'|'检查控制台' + /*Global_Flag_List ['enableFloor','enableName','enableLv', 'enableHPMax', 'enableMana', 'enableMDef', 'enableMoney', 'enableExperience', 'enableLevelUp', 'levelUpLeftMode', 'enableKeys', 'enablePZF', 'enableDebuff', 'enableSkill', 'flyNearStair', 'pickaxeFourDirections', 'bombFourDirections', 'snowFourDirections', 'steelDoorWithoutKey', 'enableAddPoint', 'enableNegativeDamage', 'hatredDecrease', 'betweenAttackCeil', 'useLoop', 'enableGentleClick', 'potionWhileRouting', 'canGoDeadZone', 'enableMoveDirectly', 'enableDisabledShop', 'disableShopOnDamage', 'checkConsole']*/; Colour : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js index 026ab732..04b3592a 100644 --- a/_server/table/data.comment.js +++ b/_server/table/data.comment.js @@ -536,6 +536,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_bool": "bool", "_data": "如果此项为true,则视为钥匙盒,红黄蓝钥匙+1;若为false,则视为大黄门钥匙" }, + "steelDoorWithoutKey": { + "_left": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "铁门是否不需要钥匙开启。如果此项为true,则无需钥匙也可以开铁门。" + }, "equipment": { "_leaf": true, "_type": "checkbox", diff --git a/libs/events.js b/libs/events.js index 341bdcc8..c1f5fd8c 100644 --- a/libs/events.js +++ b/libs/events.js @@ -382,6 +382,9 @@ events.prototype._openDoor_check = function (id, x, y, needKey) { return false; } + if (id == 'steelDoor' && core.flags.steelDoorWithoutKey) + needKey = false; + if (needKey && id.endsWith("Door")) { var key = id.replace("Door", "Key"); if (!core.hasItem(key)) { diff --git a/project/data.js b/project/data.js index ba2c9d99..72dabc4c 100644 --- a/project/data.js +++ b/project/data.js @@ -387,6 +387,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "bombFourDirections": false, "snowFourDirections": false, "bigKeyIsBox": false, + "steelDoorWithoutKey": false, "equipment": false, "equipboxButton": false, "enableAddPoint": false, From c5cc4aebe139b68e846f708ed6422c007c4b67c6 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 8 Apr 2019 01:15:56 +0800 Subject: [PATCH 59/64] 1000->10000 --- editor-mobile.html | 2 +- editor.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editor-mobile.html b/editor-mobile.html index e77a286f..0e4fde1f 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -120,7 +120,7 @@
- +
diff --git a/editor.html b/editor.html index 58ea0a1c..75b7856a 100644 --- a/editor.html +++ b/editor.html @@ -116,7 +116,7 @@
- +
From 29e9edae0e98d996d4dbcb12b1bc0740269a5b2c Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 8 Apr 2019 01:33:03 +0800 Subject: [PATCH 60/64] setBlock for id --- _docs/event.md | 3 +++ _server/MotaAction.g4 | 12 ++++++------ libs/maps.js | 9 ++++++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index e5a2b7b9..27963dfb 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -657,6 +657,7 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 {"type": "setBlock", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层的(3,3)点变成数字233 {"type": "setBlock", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 {"type": "setBlock", "number": 57}, // loc也可省略,默认为当前点 + {"type": "setBlock", "number": "yellowDoor"}, // 从V2.6开始也允许写图块ID ] ``` @@ -666,6 +667,8 @@ loc为可选的,表示要更改地图块的坐标。如果忽略此项,则 number为**要更改到的数字**,有关“数字”的定义详见参见[素材的机制](personalization#素材的机制)。 +从V2.6开始,number也允许写图块的ID,将自动转成对应的数字。 + 图块更改后: - 其启用/禁用状态不会发生任何改变。原来是启用还是启用,原来是禁用还是禁用。 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 41da0c2d..329965ab 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -728,20 +728,20 @@ return code; */; setBlock_s - : '转变图块为' Int 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline + : '转变图块为' EvalString 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline /* setBlock_s tooltip : setBlock:设置某个图块,忽略坐标楼层则为当前事件 helpUrl : https://h5mota.com/games/template/docs/#/event?id=setblock%EF%BC%9A%E8%AE%BE%E7%BD%AE%E6%9F%90%E4%B8%AA%E5%9B%BE%E5%9D%97 colour : this.dataColor -default : [0,"","",""] +default : ["yellowDoor","","",""] var floorstr = ''; if (PosString_0 && PosString_1) { floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; } IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); -var code = '{"type": "setBlock", "number":'+Int_0+floorstr+IdString_0+'},\n'; +var code = '{"type": "setBlock", "number": "'+EvalString_0+'"'+floorstr+IdString_0+'},\n'; return code; */; @@ -870,20 +870,20 @@ return code; */; setBgFgBlock_s - : '转变图层块' Bg_Fg_List '为' Int 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline + : '转变图层块' Bg_Fg_List '为' EvalString 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline /* setBgFgBlock_s tooltip : setBgFgBlock:设置某个图层块,忽略坐标楼层则为当前点 helpUrl : https://h5mota.com/games/template/docs/#/event?id=setblock%EF%BC%9A%E8%AE%BE%E7%BD%AE%E6%9F%90%E4%B8%AA%E5%9B%BE%E5%9D%97 colour : this.dataColor -default : ["bg",0,"","",""] +default : ["bg","yellowDoor","","",""] var floorstr = ''; if (PosString_0 && PosString_1) { floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; } IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); -var code = '{"type": "setBgFgBlock", "name": "' + Bg_Fg_List_0 + '", "number":'+Int_0+floorstr+IdString_0+'},\n'; +var code = '{"type": "setBgFgBlock", "name": "' + Bg_Fg_List_0 + '", "number": "'+EvalString_0+'"'+floorstr+IdString_0+'},\n'; return code; */; diff --git a/libs/maps.js b/libs/maps.js index f03bfe60..66d66a03 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1425,7 +1425,10 @@ maps.prototype.setBlock = function (number, x, y, floorId) { floorId = floorId || core.status.floorId; if (!floorId || number == null || x == null || y == null) return; if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return; - if (typeof number == 'string') number = core.getNumberById(number); + if (typeof number == 'string') { + if (/^\d+$/.test(number)) number = parseInt(number); + else number = core.getNumberById(number); + } var originBlock = core.getBlock(x, y, floorId, true); var block = this.initBlock(x, y, number, true, core.floors[floorId]); @@ -1479,6 +1482,10 @@ maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) { if (!floorId || number == null || x == null || y == null) return; if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return; if (name != 'bg' && name != 'fg') return; + if (typeof number == 'string') { + if (/^\d+$/.test(number)) number = parseInt(number); + else number = core.getNumberById(number); + } var vFlag = "__" + name + "Value__" + floorId + "_" + x + "_" + y; core.setFlag(vFlag, number); From f93c6d03f09ad2bb17672a73060d5b72c8452c36 Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 9 Apr 2019 23:13:43 +0800 Subject: [PATCH 61/64] Fix bug: follow, callBook & dowhile --- _docs/event.md | 10 ++++++++-- _server/MotaAction.g4 | 29 +++++++++++++++++++++++++---- _server/editor_blockly.js | 1 + _server/table/data.comment.js | 6 ++++++ libs/control.js | 13 +++++++++++-- libs/events.js | 11 ++++++++++- libs/ui.js | 9 ++++----- project/data.js | 1 + 8 files changed, 66 insertions(+), 14 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index 27963dfb..c68f267c 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -655,7 +655,7 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ``` js [ {"type": "setBlock", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层的(3,3)点变成数字233 - {"type": "setBlock", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 + {"type": "setBlock", "loc": [2,1],setVa "number": 121}, // 省略floorId则默认为本层 {"type": "setBlock", "number": 57}, // loc也可省略,默认为当前点 {"type": "setBlock", "number": "yellowDoor"}, // 从V2.6开始也允许写图块ID ] @@ -1645,7 +1645,7 @@ default可选,如果为true则显示选择项时默认选中【确定】,否 yes和no均为必填项,即用户点击确认或取消后执行的事件。 -### while:循环处理 +### while:前置条件循环 从2.2.1样板开始,我们提供了循环处理(while事件)。 @@ -1681,6 +1681,12 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ] ``` +### dowhile:后置条件循环 + +`type:dowhile`可以制作一个后置条件循环。 + +其写法与参数和`type:while`完全一致,不过与其不同的是,会先执行一次事件列表,再对条件进行判定,就和C/C++中的 `do {...} while (...);` 语法一样。 + ### break:跳出循环 使用 `{"type": "break"}` 可以跳出当前循环。 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 329965ab..4f38fe1e 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -327,6 +327,7 @@ action | if_1_s | switch_s | while_s + | dowhile_s | break_s | continue_s | input_s @@ -1693,11 +1694,11 @@ return code; */; while_s - : '循环处理' ':' '当' expression '时' BGNL? Newline action+ BEND Newline + : '前置条件循环' ':' '当' expression '时' BGNL? Newline action+ BEND Newline /* while_s -tooltip : while:循环处理 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=while%EF%BC%9A%E5%BE%AA%E7%8E%AF%E5%A4%84%E7%90%86 +tooltip : while:前置条件循环 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=while%ef%bc%9a%e5%89%8d%e7%bd%ae%e6%9d%a1%e4%bb%b6%e5%be%aa%e7%8e%af colour : this.eventColor var code = ['{"type": "while", "condition": "',expression_0,'",\n', '"data": [\n',action_0,'],\n', @@ -1705,6 +1706,19 @@ var code = ['{"type": "while", "condition": "',expression_0,'",\n', return code; */; +dowhile_s + : '后置条件循环' ':' BGNL? Newline action+ BEND '当' expression '时' Newline + +/* dowhile_s +tooltip : dowhile:后置条件循环 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=dowhile%ef%bc%9a%e5%90%8e%e7%bd%ae%e6%9d%a1%e4%bb%b6%e5%be%aa%e7%8e%af +colour : this.eventColor +var code = ['{"type": "dowhile", "condition": "',expression_0,'",\n', + '"data": [\n',action_0,'],\n', +'},\n'].join(''); +return code; +*/; + break_s : '跳出循环' Newline @@ -2639,13 +2653,20 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['choices_s'].xmlText([ this.isset(data.text)?this.EvalString(data.text):null,'','',text_choices,this.next]); break; - case "while": // 循环处理 + case "while": // 前置条件循环处理 this.next = MotaActionBlocks['while_s'].xmlText([ // MotaActionBlocks['evalString_e'].xmlText([data.condition]), this.tryToUseEvFlag_e('evalString_e', [data.condition]), this.insertActionList(data["data"]), this.next]); break; + case "dowhile": // 后置条件循环处理 + this.next = MotaActionBlocks['dowhile_s'].xmlText([ + this.insertActionList(data["data"]), + // MotaActionBlocks['evalString_e'].xmlText([data.condition]), + this.tryToUseEvFlag_e('evalString_e', [data.condition]), + this.next]); + break; case "break": // 跳出循环 this.next = MotaActionBlocks['break_s'].xmlText([ this.next]); diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 2a641312..4a705f37 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -118,6 +118,7 @@ editor_blockly = function () { {"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]}, ]}), MotaActionBlocks['while_s'].xmlText(), + MotaActionBlocks['dowhile_s'].xmlText(), MotaActionBlocks['break_s'].xmlText(), MotaActionBlocks['continue_s'].xmlText(), MotaActionBlocks['revisit_s'].xmlText(), diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js index 04b3592a..8389ffd6 100644 --- a/_server/table/data.comment.js +++ b/_server/table/data.comment.js @@ -554,6 +554,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_bool": "bool", "_data": "状态栏的装备按钮。若此项为true则将状态栏中的楼层转换器按钮换为装备栏按钮" }, + "iconInEquipbox": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "在装备栏中的属性变化,是否绘制图标;如果此项开启,则会绘制图标而不是文字" + }, "enableAddPoint": { "_leaf": true, "_type": "checkbox", diff --git a/libs/control.js b/libs/control.js index 93b9bff4..674b163a 100644 --- a/libs/control.js +++ b/libs/control.js @@ -798,8 +798,8 @@ control.prototype._drawHero_getDrawObjs = function (direction, x, y, status, off }); (core.status.hero.followers||[]).forEach(function (t) { drawObjs.push({ - "img": t.img, - "height": t.img.height/4, + "img": core.material.images.images[t.name], + "height": core.material.images.images[t.name].height/4, "heroIcon": heroIconArr[t.direction], "posx": 32*t.x - core.bigmap.offsetX + (t.stop?0:core.utils.scan[t.direction].x*offset), "posy": 32*t.y - core.bigmap.offsetY + (t.stop?0:core.utils.scan[t.direction].y*offset), @@ -1882,6 +1882,15 @@ control.prototype.getHeroLoc = function (name) { return core.status.hero.loc[name]; } +////// 获得某个属性的中文名 ////// +control.prototype.getStatusName = function (name) { + var map = { + name: "名称", lv: "等级", hpmax: "生命上限", hp: "生命", manamax: "魔力上限", mana: "魔力", + atk: "攻击", def: "防御", mdef: "魔防", money: "金币", exp: "经验", experience: "经验", steps: "步数" + }; + return map[name] || name; +} + ////// 获得某个等级的名称 ////// control.prototype.getLvName = function (lv) { if (!core.status.hero) return null; diff --git a/libs/events.js b/libs/events.js index c1f5fd8c..a48021b6 100644 --- a/libs/events.js +++ b/libs/events.js @@ -776,6 +776,8 @@ events.prototype.startEvents = function (list, x, y, callback) { events.prototype.doAction = function () { // 清空boxAnimate和UI层 core.clearUI(); + clearInterval(core.status.event.interval); + core.status.event.interval = null; // 判定是否执行完毕 if (this._doAction_finishEvents()) return; // 当前点坐标和前缀 @@ -1408,6 +1410,13 @@ events.prototype._action_while = function (data, x, y, prefix) { core.doAction(); } +events.prototype._action_dowhile = function (data, x, y, prefix) { + core.unshift(core.status.event.data.list, + {"todo": core.clone(data.data), "total": core.clone(data.data), "condition": data.condition} + ); + core.doAction(); +} + events.prototype._action_break = function (data, x, y, prefix) { core.status.event.data.list.shift(); core.doAction(); @@ -1718,7 +1727,7 @@ events.prototype.follow = function (name) { core.status.hero.followers = core.status.hero.followers || []; if (core.material.images.images[name] && core.material.images.images[name].width == 128) { - core.status.hero.followers.push({"name": name, "img": core.material.images.images[name]}); + core.status.hero.followers.push({"name": name}); core.gatherFollowers(); core.clearMap('hero'); core.drawHero(); diff --git a/libs/ui.js b/libs/ui.js index 2c310153..9e85f57f 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -261,8 +261,6 @@ ui.prototype.closePanel = function () { } ui.prototype.clearUI = function () { - clearInterval(core.status.event.interval); - core.status.event.interval = null; core.status.boxAnimateObjs = []; if (core.dymCanvas._selector) core.deleteCanvas("_selector"); core.clearMap('ui'); @@ -1980,13 +1978,14 @@ ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipTy core.setFont('ui', this._buildFont(14, true)); for (var name in compare) { var img = core.statusBar.icons[name]; - if (img) { // 绘制图标 + var text = core.getStatusName(name); + if (img && core.flags.iconInEquipbox) { // 绘制图标 core.drawImage('ui', img, 0, 0, 32, 32, drawOffset, y - 13, 16, 16); drawOffset += 20; } else { // 绘制文字 - core.fillText('ui', name + " ", drawOffset, y, '#CCCCCC'); - drawOffset += core.calWidth('ui', name + " "); + core.fillText('ui', text + " ", drawOffset, y, '#CCCCCC'); + drawOffset += core.calWidth('ui', text + " "); } var nowValue = core.getStatus(name) * core.getBuff(name), newValue = (nowValue + compare[name]) * core.getBuff(name); if (equip.equip.percentage) { diff --git a/project/data.js b/project/data.js index 72dabc4c..e6c64c99 100644 --- a/project/data.js +++ b/project/data.js @@ -390,6 +390,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "steelDoorWithoutKey": false, "equipment": false, "equipboxButton": false, + "iconInEquipbox": false, "enableAddPoint": false, "enableNegativeDamage": false, "hatredDecrease": true, From 3f030ff010dc2d633d5f796668d6c27a14de9b07 Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 9 Apr 2019 23:47:03 +0800 Subject: [PATCH 62/64] fix _drawAutotile bug --- libs/maps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/maps.js b/libs/maps.js index 66d66a03..f8cc1a66 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -977,10 +977,10 @@ maps.prototype._drawAutotileAnimate = function (block, animate) { if (block.name) { if (block.name == 'bg') core.drawImage('bg', core.material.groundCanvas.canvas, 32 * x, 32 * y); - this.drawAutotile(cv, core.status.autotileAnimateObjs[block.name+"map"], block, 32, 0, 0, animate); + this._drawAutotile(cv, core.status.autotileAnimateObjs[block.name+"map"], block, 32, 0, 0, animate); } else { - this.drawAutotile(cv, core.status.autotileAnimateObjs.map, block, 32, 0, 0, animate); + this._drawAutotile(cv, core.status.autotileAnimateObjs.map, block, 32, 0, 0, animate); } } From a2506d70683ae8e9273b2c35aff635dd187d3285 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Wed, 10 Apr 2019 18:46:29 +0800 Subject: [PATCH 63/64] Fix drawTextBox \b --- libs/events.js | 10 +++++++++- libs/ui.js | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/events.js b/libs/events.js index a48021b6..07ede640 100644 --- a/libs/events.js +++ b/libs/events.js @@ -70,7 +70,7 @@ events.prototype._startGame_start = function (hard, seed, route, callback) { events.prototype._startGame_afterStart = function (nowLoc, callback) { core.ui.closePanel(); - core.showStatusBar(); + this._startGame_statusBar(); core.dom.musicBtn.style.display = 'none'; core.changeFloor(core.firstData.floorId, null, nowLoc, null, function () { // 插入一个空事件避免直接回放录像出错 @@ -80,6 +80,14 @@ events.prototype._startGame_afterStart = function (nowLoc, callback) { this._startGame_upload(); } +// 开始游戏时是否显示状态栏 +events.prototype._startGame_statusBar = function () { + if (core.flags.startUsingCanvas) + core.hideStatusBar(); + else + core.showStatusBar(); +} + events.prototype._startGame_upload = function () { // Upload var formData = new FormData(); diff --git a/libs/ui.js b/libs/ui.js index 9e85f57f..fc6a9047 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -768,8 +768,8 @@ ui.prototype.drawTextBox = function(content, showAll) { var textAttribute = core.status.textAttribute; var titleInfo = this._getTitleAndIcon(content); var posInfo = this._getPosition(titleInfo.content); - if (!posInfo.position) posInfo.position = textAttribute.position; if (posInfo.position != 'up' && posInfo.position != 'down') posInfo.px = posInfo.py = null; + if (!posInfo.position) posInfo.position = textAttribute.position; content = this._drawTextBox_drawImages(posInfo.content); // Step 2: 计算对话框的矩形位置 From 6c58bf85cd6f3caba30d3623ab9fb46bc18308d0 Mon Sep 17 00:00:00 2001 From: oc Date: Wed, 10 Apr 2019 23:13:19 +0800 Subject: [PATCH 64/64] matchWildcard & drawEquipbox --- _docs/api.md | 5 +++++ libs/maps.js | 3 ++- libs/ui.js | 13 ++++++++++--- libs/utils.js | 6 ++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index a61be655..7ad94388 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1464,6 +1464,7 @@ posX, posY:素材在该素材图片上的位置;height:素材的高度;f core.searchBlock(id, floorId, showDisable) 搜索一个图块出现过的所有位置。id为图块ID,也可以传入图块的数字。 +id支持通配符搜索,比如"*Door"可以搜索所有的门,"unknownEvent*"可以所有所有的unknownEvent。 floorId为要搜索的楼层,可以是一个楼层ID,或者一个楼层数组。如果floorId不填则只搜索当前楼层。 showDisable如果为真,则对于禁用的图块也会返回。 此函数将返回一个数组,每一项为一个搜索到的结果: @@ -1997,6 +1998,10 @@ core.reverseDirection(direction) 翻转方向,即"up"转成"down", "left"转成"right"等。 +core.matchWildcard(pattern, string) +进行通配符的匹配判定,目前仅支持*(可匹配0或任意个字符)。比如"a*b*c"可以匹配"aa012bc"。 + + core.encodeBase64(str) / core.decodeBase64(str) 将字符串进行base64加密或解密。 diff --git a/libs/maps.js b/libs/maps.js index f8cc1a66..52b540b4 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1239,8 +1239,9 @@ maps.prototype.searchBlock = function (id, floorId, showDisable) { } for (var i = 0; i < core.status.maps[floorId].blocks.length; ++i) { var block = core.status.maps[floorId].blocks[i]; - if (block.event.id == id && (showDisable || !block.disable)) + if ((showDisable || !block.disable) && core.matchWildcard(id, block.event.id)) { result.push({floorId: floorId, index: i, block: block, x: block.x, y: block.y}); + } } return result; } diff --git a/libs/ui.js b/libs/ui.js index fc6a9047..13e854dd 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1956,7 +1956,7 @@ ui.prototype._drawEquipbox_description = function (info, max_height) { this._drawEquipbox_drawStatusChanged(info, curr, equip, equipType); } -ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipType) { +ui.prototype._drawEquipbox_getStatusChanged = function (info, equip, equipType) { var compare, differentMode = null; if (info.index < this.LAST) compare = core.compareEquipment(null, info.selectId); else { @@ -1973,6 +1973,12 @@ ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipTy core.fillText('ui', differentMode, 10, y, '#CCCCCC', this._buildFont(14, false)); return; } + return compare; +} + +ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipType) { + var compare = this._drawEquipbox_getStatusChanged(info, equip, equipType); + if (compare == null) return; var drawOffset = 10; // --- 变化值... core.setFont('ui', this._buildFont(14, true)); @@ -2003,13 +2009,14 @@ ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipTy } ui.prototype._drawEquipbox_drawEquiped = function (info, line) { - core.setTextAlign('ui', 'right'); + core.setTextAlign('ui', 'center'); var per_line = this.HSIZE - 3, width = Math.floor(this.PIXEL / (per_line + 0.25)); // 当前装备 for (var i = 0; i < info.equipLength ; i++) { var equipId = info.equipEquipment[i] || null; - var offset_text = width * (i % per_line) + 56; + // var offset_text = width * (i % per_line) + 56; var offset_image = width * (i % per_line) + width * 2 / 3; + var offset_text = offset_image - (width - 32) / 2; var y = line + 54 * Math.floor(i / per_line) + 19; if (equipId) { var icon = core.material.icons.items[equipId]; diff --git a/libs/utils.js b/libs/utils.js index d491197e..9da86c95 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -624,6 +624,12 @@ utils.prototype.reverseDirection = function (direction) { return {"left":"right","right":"left","down":"up","up":"down"}[direction] || direction; } +utils.prototype.matchWildcard = function (pattern, string) { + return new RegExp('^' + pattern.split(/\*+/).map(function (s) { + return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); + }).join('.*') + '$').test(string); +} + ////// Base64加密 ////// utils.prototype.encodeBase64 = function (str) { return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {