diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 04d024e3..6f28f112 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -948,13 +948,13 @@ return code; */; setText_s - : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' IntString? '对齐' TextAlign_List? BGNL? '标题颜色' ColorString? Colour '正文颜色' ColorString? Colour '背景色' EvalString? Colour BGNL? '粗体' B_1_List '标题字体大小' IntString? '正文字体大小' IntString? '行距' IntString? '打字间隔' IntString? '字符间距' IntString? Newline + : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' IntString? '对齐' TextAlign_List? '粗体' B_1_List? BGNL? '标题颜色' ColorString? Colour '正文颜色' ColorString? Colour '背景色' EvalString? Colour BGNL? '标题大小' IntString? '正文大小' IntString? '行距' IntString? '打字间隔' IntString? '字符间距' IntString? '淡入淡出时间' IntString? Newline /* setText_s tooltip : setText:设置剧情文本的属性,颜色为RGB三元组或RGBA四元组,打字间隔为剧情文字添加的时间间隔,为整数或不填,字符间距为字符之间的距离,为整数或不填。 helpUrl : /_docs/#/instruction -default : [null,"",null,"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',null,"","","","",""] +default : [null,"",null,null,"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"","","","","",""] SetTextPosition_List_0 =SetTextPosition_List_0==='null'?'': ', "position": "'+SetTextPosition_List_0+'"'; TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; var colorRe = MotaActionFunctions.pattern.colorRe; @@ -976,9 +976,10 @@ IntString_1 = IntString_1 ? (', "titlefont": '+IntString_1) : ''; IntString_2 = IntString_2 ? (', "textfont": '+IntString_2) : ''; IntString_3 = IntString_3 ? (', "lineHeight": '+IntString_3) : ''; IntString_4 = IntString_4 ? (', "time": '+IntString_4) : ''; -IntString_5 = IntString_5 ? (', "interval": '+IntString_5) : ''; +IntString_5 = IntString_5 ? (', "letterSpacing": '+IntString_5) : ''; +IntString_6 = IntString_6 ? (', "animateTime": ' + IntString_6) : ''; B_1_List_0 = B_1_List_0==='null'?'':', "bold": '+B_1_List_0; -var code = '{"type": "setText"'+SetTextPosition_List_0+IntString_0+TextAlign_List_0+ColorString_0+ColorString_1+B_1_List_0+EvalString_0+IntString_1+IntString_2+IntString_3+IntString_4+IntString_5+'},\n'; +var code = '{"type": "setText"'+SetTextPosition_List_0+IntString_0+TextAlign_List_0+B_1_List_0+ColorString_0+ColorString_1+EvalString_0+IntString_1+IntString_2+IntString_3+IntString_4+IntString_5+IntString_6+'},\n'; return code; */; diff --git a/_server/MotaActionParser.js b/_server/MotaActionParser.js index ede248a6..4338e92a 100644 --- a/_server/MotaActionParser.js +++ b/_server/MotaActionParser.js @@ -277,9 +277,9 @@ ActionParser.prototype.parseAction = function() { if (!/^\w+\.png$/.test(data.background)) data.background=this.Colour(data.background); this.next = MotaActionBlocks['setText_s'].xmlText([ - data.position,data.offset,data.align,data.title,'rgba('+data.title+')', + data.position,data.offset,data.align,data.title,data.bold,'rgba('+data.title+')', data.text,'rgba('+data.text+')',data.background,'rgba('+data.background+')', - data.bold,data.titlefont,data.textfont,data.lineHeight,data.time,data.interval,this.next]); + data.titlefont,data.textfont,data.lineHeight,data.time,data.letterSpacing,data.animateTime,this.next]); break; case "tip": this.next = MotaActionBlocks['tip_s'].xmlText([ diff --git a/libs/actions.js b/libs/actions.js index ce6c6a4e..e0707d5e 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1013,14 +1013,18 @@ actions.prototype._onMoveConfirmBox = function (x, y) { ////// 自定义事件时的点击操作 ////// actions.prototype._clickAction = function (x, y) { if (core.status.event.data.type == 'text') { + // 正在淡入淡出的话不执行 + if (core.status.event.animateUI) return; // 打字机效果显示全部文字 if (core.status.event.interval != null) { core.insertAction({"type": "text", "text": core.status.event.ui, "showAll": true}); + core.doAction(); + return; } // 文字 - core.doAction(); + core.ui._animateUI('hide', core.doAction); return; } @@ -1088,11 +1092,16 @@ actions.prototype._keyDownAction = function (keycode) { ////// 自定义事件时,放开某个键的操作 ////// actions.prototype._keyUpAction = function (keycode) { if (core.status.event.data.type == 'text' && (keycode == 13 || keycode == 32 || keycode == 67)) { + // 正在淡入淡出的话不执行 + if (core.status.event.animateUI) return; + // 打字机效果显示全部文字 if (core.status.event.interval != null) { core.insertAction({"type": "text", "text": core.status.event.ui, "showAll": true}); + core.doAction(); + return; } - core.doAction(); + core.ui._animateUI('hide', core.doAction); return; } if (core.status.event.data.type == 'wait') { diff --git a/libs/core.js b/libs/core.js index fb790c9e..1940f080 100644 --- a/libs/core.js +++ b/libs/core.js @@ -209,6 +209,8 @@ function core() { "textfont": 16, "bold": false, "time": 0, + "letterSpacing": 0, + "animateTime": 0, }, "globalAttribute": { 'equipName': main.equipName || [], diff --git a/libs/events.js b/libs/events.js index 224a184a..4ca79588 100644 --- a/libs/events.js +++ b/libs/events.js @@ -927,10 +927,12 @@ events.prototype.startEvents = function (list, x, y, callback) { ////// 执行当前自定义事件列表中的下一个事件 ////// events.prototype.doAction = function () { // 清空boxAnimate和UI层 - core.clearUI(); clearInterval(core.status.event.interval); clearTimeout(core.status.event.interval); + clearInterval(core.status.event.animateUI); core.status.event.interval = null; + delete core.status.event.aniamteUI; + core.clearUI(); // 判定是否执行完毕 if (this._doAction_finishEvents()) return; var floorId = core.status.event.data.floorId || core.status.floorId; @@ -1289,6 +1291,7 @@ events.prototype._action_text = function (data, x, y, prefix) { if (this.__action_checkReplaying()) return; data.text = core.replaceText(data.text, prefix); core.ui.drawTextBox(data.text, data.showAll); + if (!data.showAll) core.ui._animateUI('show'); } events.prototype._action_autoText = function (data, x, y, prefix) { @@ -1313,7 +1316,7 @@ events.prototype._action__label = function (data, x, y, prefix) { } events.prototype._action_setText = function (data, x, y, prefix) { - ["position", "offset", "align", "bold", "titlefont", "textfont", "lineHeight", "time", "interval"].forEach(function (t) { + ["position", "offset", "align", "bold", "titlefont", "textfont", "lineHeight", "time", "letterSpacing", "animateTime"].forEach(function (t) { if (data[t] != null) core.status.textAttribute[t] = data[t]; }); ["background", "title", "text"].forEach(function (t) { diff --git a/libs/ui.js b/libs/ui.js index 33445e29..29eeca32 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -653,8 +653,10 @@ ui.prototype.clearUI = function () { core.status.boxAnimateObjs = []; if (core.dymCanvas._selector) core.deleteCanvas("_selector"); main.dom.next.style.display = 'none'; + main.dom.next.style.opacity = 1; core.clearMap('ui'); core.setAlpha('ui', 1); + core.setOpacity('ui', 1); } ////// 左上角绘制一段提示 ////// @@ -1043,7 +1045,7 @@ ui.prototype._buildFont = function (fontSize, bold, italic, font) { // content:要绘制的内容;转义字符目前只允许留 \n, \r[...], \i[...], \c[...], \d, \e // config:绘制配置项,目前暂时包含如下内容(均为可选) // left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右 -// fontSize:字体大小;lineHeight:行高;time:打字机间隔;font:字体类型 +// fontSize:字体大小;lineHeight:行高;time:打字机间隔;font:字体类型;letterSpacing:字符间距 ui.prototype.drawTextContent = function (ctx, content, config) { ctx = core.getContextByName(ctx); // 设置默认配置项 @@ -1061,7 +1063,7 @@ ui.prototype.drawTextContent = function (ctx, content, config) { config.lineHeight = config.lineHeight || (config.fontSize * 1.3); config.defaultFont = config.font = config.font || globalAttribute.font; config.time = config.time || 0; - config.interval = config.interval == null ? (textAttribute.interval || 0) : config.interval; + config.letterSpacing = config.letterSpacing == null ? (textAttribute.letterSpacing || 0) : config.letterSpacing; config.index = 0; config.currcolor = config.color; @@ -1183,7 +1185,7 @@ ui.prototype._drawTextContent_drawChar = function (tempCtx, content, config, ch) if (c == 'z') return this._drawTextContent_emptyChar(tempCtx, content, config); } // 检查是不是自动换行 - var charwidth = core.calWidth(tempCtx, ch) + config.interval; + var charwidth = core.calWidth(tempCtx, ch) + config.letterSpacing; if (config.maxWidth != null) { if (config.offsetX + charwidth > config.maxWidth) { // --- 当前应当换行,然而还是检查一下是否是forbidStart @@ -1200,7 +1202,7 @@ ui.prototype._drawTextContent_drawChar = function (tempCtx, content, config, ch) // 确认不是手动换行 if (nextch != '\n' && !(nextch == '\\' && content.charAt(config.index+1) == 'n')) { // 检查是否会换行 - var nextchwidth = core.calWidth(tempCtx, nextch) + config.interval; + var nextchwidth = core.calWidth(tempCtx, nextch) + config.letterSpacing; if (config.offsetX + charwidth + nextchwidth > config.maxWidth) { // 下一项会换行,因此在此处换行 this._drawTextContent_newLine(tempCtx, config); @@ -1345,6 +1347,34 @@ ui.prototype._getRealContent = function (content) { return content.replace(/(\r|\\(r|c|d|e|g|z))(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "占1"); } +ui.prototype._animateUI = function (type, callback) { + var time = core.status.textAttribute.animateTime || 0; + if (!core.status.event || !time || core.isReplaying() || (type != 'show' && type != 'hide')) { + if (callback) callback(); + return; + } + clearInterval(core.status.event.animateUI); + var opacity = 0; + if (type == 'show') { + opacity = 0; + } else if (type == 'hide') { + opacity = 1; + } + core.setOpacity('ui', opacity); + core.dom.next.style.opacity = opacity; + core.status.event.animateUI = setInterval(function () { + if (type == 'show') opacity += 0.05; + else opacity -= 0.05; + core.setOpacity('ui', opacity); + core.dom.next.style.opacity = opacity; + if (opacity >= 1 || opacity <= 0) { + clearInterval(core.status.event.animateUI); + delete core.status.event.animateUI; + if (callback) callback(); + } + }, time / 20); +} + ////// 绘制一个对话框 ////// ui.prototype.drawTextBox = function(content, showAll) { if (core.status.event && core.status.event.id == 'action') diff --git a/runtime.d.ts b/runtime.d.ts index 9ad6248b..d37fcb85 100644 --- a/runtime.d.ts +++ b/runtime.d.ts @@ -215,6 +215,8 @@ type gameStatus = { textfont: number bold: boolean time: number + letterSpacing: number + animateTime: number }, globalAttribute: { equipName: string[]