Merge pull request #266 from ckcz123/v2.0

V2.0
This commit is contained in:
Zhang Chen 2018-11-30 23:55:09 +08:00 committed by GitHub
commit eb4406f408
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 195 additions and 102 deletions

View File

@ -57,13 +57,16 @@ HTML5 canvas制作的魔塔样板支持全平台游戏
* [x] 怪物和NPC的行走图和朝向问题
* [x] 可以引入WindowSkin作为对话框的背景素材
* [x] \r可以动态调整剧情文本的颜色
* [x] 允许使用\t[标题,1.png]来绘制大头像图
* [x] 对话框的宽度可以根据文本长度自动调整
* [x] \r[red]可以动态调整剧情文本的颜色
* [x] 升级事件改用事件编辑器完成
* [x] 每层楼都增添该层的并行事件处理
* [x] 新增快捷键N返回标题P查看评论O打开工程
* [x] 道具可以设置是否在回放时绘制道具栏或直接使用
* [x] 追加素材一次可以追加多个
* [x] 可以同时异步移动/跳跃勇士和多个NPC
* [x] 可以同时异步移动两张或以上的图片了
* [x] 追加素材一次可以追加多个
* [x] 菜单栏中新增虚拟键盘的弹出
* [x] 修复所有已知Bug部分细节优化

View File

@ -1084,45 +1084,48 @@ return code;
*/;
move_s
: '移动事件' 'x' PosString? ',' 'y' PosString? '动画时间' Int? '不消失' Bool BGNL? StepString Newline
: '移动事件' 'x' PosString? ',' 'y' PosString? '动画时间' Int? '不消失' Bool '不等待执行完毕' Bool BGNL? StepString Newline
/* move_s
tooltip : move: 让某个NPC/怪物移动,位置可不填代表当前事件
helpUrl : https://h5mota.com/games/template/docs/#/event?id=move%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AAnpc%E6%80%AA%E7%89%A9%E7%A7%BB%E5%8A%A8
default : ["","",500,false,"上右3下2左上左2"]
default : ["","",500,false,false,"上右3下2左上左2"]
colour : this.eventColor
var floorstr = '';
if (PosString_0 && PosString_1) {
floorstr = ', "loc": ['+PosString_0+','+PosString_1+']';
}
Int_0 = Int_0 ?(', "time": '+Int_0):'';
var code = '{"type": "move"'+floorstr+''+Int_0+', "steps": '+JSON.stringify(StepString_0)+', "keep": '+Bool_0+'},\n';
Bool_0 = Bool_0?', "keep": true':'';
Bool_1 = Bool_1?', "async": true':'';
var code = '{"type": "move"'+floorstr+Int_0+Bool_0+Bool_1+', "steps": '+JSON.stringify(StepString_0)+'},\n';
return code;
*/;
moveHero_s
: '移动勇士' '动画时间' Int? BGNL? StepString Newline
: '移动勇士' '动画时间' Int? '不等待执行完毕' Bool BGNL? StepString Newline
/* moveHero_s
tooltip : moveHero移动勇士,用这种方式移动勇士的过程中将无视一切地形, 无视一切事件, 中毒状态也不会扣血
helpUrl : https://h5mota.com/games/template/docs/#/event?id=movehero%EF%BC%9A%E7%A7%BB%E5%8A%A8%E5%8B%87%E5%A3%AB
default : [500,"上右3下2左上左2"]
default : [500,false,"上右3下2左上左2"]
colour : this.dataColor
Int_0 = Int_0 ?(', "time": '+Int_0):'';
var code = '{"type": "moveHero"'+Int_0+', "steps": '+JSON.stringify(StepString_0)+'},\n';
Bool_0 = Bool_0?', "async": true':'';
var code = '{"type": "moveHero"'+Int_0+Bool_0+', "steps": '+JSON.stringify(StepString_0)+'},\n';
return code;
*/;
jump_s
: '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? '动画时间' Int? '不消失' Bool Newline
: '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? '动画时间' Int? '不消失' Bool '不等待执行完毕' Bool Newline
/* jump_s
tooltip : jump: 让某个NPC/怪物跳跃
helpUrl : https://h5mota.com/games/template/docs/#/event?id=jump%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AANPC%2F%E6%80%AA%E7%89%A9%E8%B7%B3%E8%B7%83
default : ["","","","",500,true]
default : ["","","","",500,true,false]
colour : this.eventColor
var floorstr = '';
if (PosString_0 && PosString_1) {
@ -1132,25 +1135,28 @@ if (PosString_2 && PosString_3) {
floorstr += ', "to": ['+PosString_2+','+PosString_3+']';
}
Int_0 = Int_0 ?(', "time": '+Int_0):'';
var code = '{"type": "jump"'+floorstr+''+Int_0+', "keep": '+Bool_0+'},\n';
Bool_0 = Bool_0?', "keep": true':'';
Bool_1 = Bool_1?', "async": true':'';
var code = '{"type": "jump"'+floorstr+''+Int_0+Bool_0+Bool_1+'},\n';
return code;
*/;
jumpHero_s
: '跳跃勇士' 'x' PosString? ',' 'y' PosString? '动画时间' Int? Newline
: '跳跃勇士' 'x' PosString? ',' 'y' PosString? '动画时间' Int? '不等待执行完毕' Bool Newline
/* jumpHero_s
tooltip : jumpHero: 跳跃勇士
helpUrl : https://h5mota.com/games/template/docs/#/event?id=jumpHero%EF%BC%9A%E8%B7%B3%E8%B7%83%E5%8B%87%E5%A3%AB
default : ["","",500]
default : ["","",500,false]
colour : this.dataColor
var floorstr = '';
if (PosString_0 && PosString_1) {
floorstr = ', "loc": ['+PosString_0+','+PosString_1+']';
}
Int_0 = Int_0 ?(', "time": '+Int_0):'';
var code = '{"type": "jumpHero"'+floorstr+Int_0+'},\n';
Bool_0 = Bool_0?', "async": true':'';
var code = '{"type": "jumpHero"'+floorstr+Int_0+Bool_0+'},\n';
return code;
*/;
@ -1910,22 +1916,22 @@ ActionParser.prototype.parseAction = function() {
case "move": // 移动事件
data.loc=data.loc||['',''];
this.next = MotaActionBlocks['move_s'].xmlText([
data.loc[0],data.loc[1],data.time||0,data.keep,this.StepString(data.steps),this.next]);
data.loc[0],data.loc[1],data.time||0,data.keep||false,data.async||false,this.StepString(data.steps),this.next]);
break;
case "moveHero":
this.next = MotaActionBlocks['moveHero_s'].xmlText([
data.time||0,this.StepString(data.steps),this.next]);
data.time||0,data.async||false,this.StepString(data.steps),this.next]);
break;
case "jump": // 跳跃事件
data.from=data.from||['',''];
data.to=data.to||['',''];
this.next = MotaActionBlocks['jump_s'].xmlText([
data.from[0],data.from[1],data.to[0],data.to[1],data.time||0,data.keep,this.next]);
data.from[0],data.from[1],data.to[0],data.to[1],data.time||0,data.keep||false,data.async||false,this.next]);
break;
case "jumpHero": // 跳跃勇士
data.loc=data.loc||['','']
this.next = MotaActionBlocks['jumpHero_s'].xmlText([
data.loc[0],data.loc[1],data.time||0,this.next]);
data.loc[0],data.loc[1],data.time||0,data.async||false,this.next]);
break;
case "changeFloor": // 楼层转换
data.loc=data.loc||['','']

View File

@ -305,7 +305,7 @@ floorId指定的是目标楼层的唯一标识符ID
**从2.1.1开始,楼层属性中提供了`upFloor`和`downFloor`两项。如果设置此项(比如`"upFloor": [2,3]`则写stair:upFloor或者楼传器的落点将用此点来替换楼梯位置即类似于RM中的上箭头。**
## 剧情文本控制与界面皮肤
## 剧情文本控制与对话框效果
在写剧情文本时,可以:
@ -314,6 +314,8 @@ floorId指定的是目标楼层的唯一标识符ID
- 使用`\r[...]`来动态修改局部文本的颜色,如`\r[red]`。
- 使用`${}`来计算一个表达式的值,如`${status:atk+status:def}`。
从V2.5.2开始,也允许绘制一张头像图在对话框中,只要通过`\t[1.png]`或`\t[标题,1.png]`的写法。
详细信息请参见[剧情文本控制](event#text显示一段文字剧情)中的说明。
从V2.5.2开始可以用一张WindowSkin图片作为对话框的背景皮肤。
@ -324,6 +326,14 @@ floorId指定的是目标楼层的唯一标识符ID
!> 关于对话框效果请注意现在是采用WindowSkin的右下角两个32x32的图片作为对话框尖角进行绘制。因此请尽量使用群文件或网盘的常用素材中给出的WindowSkin素材均已进行对话框适配。如需使用来自第三方的WindowSkin素材请自行注意对话框的尖角问题或弃用`\b`效果。
另外一点是V2.5.2以后,对话框`\b`可以根据文字长度来自动控制文本框宽度,其基本控制原理如下:
- 如果用户存在手动换行`\n`,则选取**最长的一段话**作为文本框宽度。
- 如果用户不存在手动换行,则会将文本框宽度调整为**尽量刚好达到三行**的最佳宽度。
- 文本框宽度存在上下界,最终宽度一定会控制在该范围内。
该自动调整仅对`\b`的对话框效果有效。非对话框仍然会绘制整个界面的宽度。
## 大地图
从V2.4开始H5魔塔开始支持大地图。

View File

@ -207,6 +207,8 @@
对于hero和怪物也可以不写名字代表使用默认值。
从V2.5.2以后,新增了绘制大头像的功能。绘制大头像图的基本写法是`\t[1.png]`或者`\t[标题,1.png]`。
``` js
"x,y": [ // 实际执行的事件列表
"一段普通文字",
@ -216,9 +218,13 @@
"\t[blackMagician]如果使用怪物的默认名称也可以简写怪物id",
"\t[小妖精,fairy]这是一段小妖精说的话,使用仙子(fairy)的图标",
"\t[你赢了]直接显示标题为【你赢了】",
"\t[1.png]绘制1.png这个头像图",
"\t[标题,1.png]同时绘制标题和1.png这个头像图"
]
```
!> 大头像的头像图需要在全塔属性中注册且必须是png格式不可以用jpg或者其他格式请自行转换。
除此以外,我们还能实现“对话框效果”,只要有`\b[...]`就可以。
- `\b[up]` 直接显示在当前点上方。同样把这里的up换成down则为下方。
@ -990,7 +996,7 @@ level为天气的强度等级在1-10之间。1级为最弱10级为最强
{"type": "move", "time": 750, "loc": [x,y], "steps": [// 动画效果time为移动速度(比如这里每750ms一步)loc为位置可选steps为移动数组
{"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步在向下移动一步并消失
"down" // 如果该方向上只移动一步则可以这样简写效果等价于上面value为1
], "keep": true }, // keep可选如果为true则不消失否则渐变消失
], "keep": true, "async":true }, // keep可选如果为true则不消失否则渐变消失async可选如果为true则异步执行。
]
```
@ -1026,7 +1032,9 @@ keep为一个可选项代表该事件移动完毕后是否消失。如果该
}
```
在移动的到达点指定一个初始禁用的相同NPC然后move事件中指定immediateHide使立刻消失并show该到达点坐标使其立刻显示看起来就像没有消失然后就可以触发目标点的事件了。
在移动的到达点指定一个事件然后move事件中指定"keep":true然后就可以触发目标点的事件了。
async可选如果为true则会异步执行即不等待当前事件执行完毕立刻执行下一个事件
### moveHero移动勇士
@ -1036,7 +1044,7 @@ keep为一个可选项代表该事件移动完毕后是否消失。如果该
``` js
"x,y": [ // 实际执行的事件列表
{"type": "moveHero", "time": 750, "steps": [// 动画效果time为移动速度(比如这里每750ms一步)steps为移动数组
{"type": "moveHero", "time": 750, "async": true, "steps": [// 动画效果time为移动速度(比如这里每750ms一步)steps为移动数组
{"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步在向下移动一步并消失
"down" // 如果该方向上只移动一步则可以这样简写效果等价于上面value为1
]},
@ -1047,6 +1055,8 @@ keep为一个可选项代表该事件移动完毕后是否消失。如果该
不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。
async可选如果为true则会异步执行即不等待当前事件执行完毕立刻执行下一个事件
### jump让某个NPC/怪物跳跃
如果我们需要移动某个NPC或怪物可以使用`{"type": "jump"}`。
@ -1055,7 +1065,7 @@ keep为一个可选项代表该事件移动完毕后是否消失。如果该
``` js
"x,y": [ // 实际执行的事件列表
{"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "keep": true},
{"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "keep": true, "async": true},
]
```
@ -1069,6 +1079,8 @@ keep为一个可选项同上代表该跳跃完毕后是否不消失。如果
如果指定了`"keep": true`,则相当于会在目标地点触发一个`setBlock`事件;如需能继续对话交互请在目标地点再写事件。
async可选如果为true则会异步执行即不等待当前事件执行完毕立刻执行下一个事件
### jumpHero跳跃勇士
如果我们需要跳跃勇士,可以使用`{"type": "jumpHero"}`。
@ -1077,7 +1089,7 @@ keep为一个可选项同上代表该跳跃完毕后是否不消失。如果
``` js
"x,y": [ // 实际执行的事件列表
{"type": "jump", "loc": [3,6], "time": 750},
{"type": "jump", "loc": [3,6], "time": 750, "async": true},
]
```
@ -1085,6 +1097,8 @@ loc为目标坐标可以忽略表示原地跳跃请注意是原地跳跃
time选项为该跳跃所需要用到的时间。
async可选如果为true则会异步执行即不等待当前事件执行完毕立刻执行下一个事件
### playBgm播放背景音乐
使用playBgm可以播放一个背景音乐。

View File

@ -832,12 +832,8 @@ control.prototype.moveHero = function (direction, callback) {
/////// 使用事件让勇士移动。这个函数将不会触发任何事件 //////
control.prototype.eventMoveHero = function(steps, time, callback) {
time = time || 100;
core.clearMap('ui');
core.setAlpha('ui', 1.0);
// 要运行的轨迹将steps展开
var moveSteps=[];
steps.forEach(function (e) {
@ -864,14 +860,11 @@ control.prototype.eventMoveHero = function(steps, time, callback) {
'right': {'x': 1, 'y': 0}
};
// core.status.replay.animate=true;
var animate=window.setInterval(function() {
var x=core.getHeroLoc('x'), y=core.getHeroLoc('y');
if (moveSteps.length==0) {
clearInterval(animate);
core.drawHero(null, x, y);
// core.status.replay.animate=false;
if (core.isset(callback)) callback();
}
else {
@ -902,9 +895,6 @@ control.prototype.jumpHero = function (ex, ey, time, callback) {
if (!core.isset(ey)) ey=sy;
time = time || 500;
core.clearMap('ui');
core.setAlpha('ui', 1.0);
// core.status.replay.animate=true;
core.playSound('jump.mp3');
@ -954,7 +944,6 @@ control.prototype.jumpHero = function (ex, ey, time, callback) {
core.setHeroLoc('x', ex);
core.setHeroLoc('y', ey);
core.drawHero();
// core.status.replay.animate=false;
if (core.isset(callback)) callback();
}

View File

@ -598,14 +598,26 @@ events.prototype.doAction = function() {
x=core.calValue(data.loc[0]);
y=core.calValue(data.loc[1]);
}
core.moveBlock(x,y,data.steps,data.time,data.keep,function() {
core.events.doAction();
})
if (data.async) {
core.moveBlock(x,y,data.steps,data.time,data.keep);
this.doAction();
}
else {
core.moveBlock(x,y,data.steps,data.time,data.keep,function() {
core.events.doAction();
})
}
break;
case "moveHero":
core.eventMoveHero(data.steps,data.time,function() {
core.events.doAction();
});
if (data.async) {
core.eventMoveHero(data.steps, data.time);
this.doAction();
}
else {
core.eventMoveHero(data.steps,data.time,function() {
core.events.doAction();
});
}
break;
case "jump": // 跳跃事件
{
@ -618,9 +630,15 @@ events.prototype.doAction = function() {
ex=core.calValue(data.to[0]);
ey=core.calValue(data.to[1]);
}
core.jumpBlock(sx,sy,ex,ey,data.time,data.keep,function() {
core.events.doAction();
});
if (data.async) {
core.jumpBlock(sx,sy,ex,ey,data.time,data.keep);
this.doAction();
}
else {
core.jumpBlock(sx,sy,ex,ey,data.time,data.keep,function() {
core.events.doAction();
});
}
break;
}
case "jumpHero":
@ -630,9 +648,15 @@ events.prototype.doAction = function() {
ex=core.calValue(data.loc[0]);
ey=core.calValue(data.loc[1]);
}
core.jumpHero(ex,ey,data.time,function() {
core.events.doAction();
});
if (data.async) {
core.jumpHero(ex,ey,data.time);
this.doAction();
}
else {
core.jumpHero(ex,ey,data.time,function() {
core.events.doAction();
});
}
break;
}
case "changeFloor": // 楼层转换

View File

@ -738,8 +738,6 @@ maps.prototype.getBlockCls = function (x, y, floorId, showDisable) {
maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
time = time || 500;
core.clearMap('route');
var block = core.getBlock(x,y);
if (block==null) {// 不存在
if (core.isset(callback)) callback();
@ -747,14 +745,9 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
}
var id = block.block.id;
// core.status.replay.animate=true;
// 需要删除该块
core.removeBlock(x,y);
core.clearMap('ui');
core.setAlpha('ui', 1.0);
block=block.block;
var image, bx, by, height = block.event.height || 32;
@ -786,8 +779,8 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
faceIds = block.event.faceIds||{};
}
var opacityVal = 1;
core.setOpacity('route', opacityVal);
var alpha = 1;
core.setAlpha('route', alpha);
core.canvas.route.drawImage(image, bx * 32, by * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height);
// 要运行的轨迹将steps展开
@ -834,15 +827,11 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
// 已经移动完毕,消失
if (moveSteps.length==0) {
if (keep) opacityVal=0;
else opacityVal -= 0.06;
core.setOpacity('route', opacityVal);
if (keep) alpha=0;
else alpha -= 0.06;
core.clearMap('route', nowX, nowY-height+32, 32, height);
core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, nowX, nowY-height+32, 32, height);
if (opacityVal<=0) {
if (alpha<=0) {
clearInterval(animate);
core.clearMap('route');
core.setOpacity('route', 1);
// 不消失
if (keep) {
core.setBlock(id, nowX/32, nowY/32);
@ -851,6 +840,11 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
// core.status.replay.animate=false;
if (core.isset(callback)) callback();
}
else {
core.setAlpha('route', alpha);
core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, nowX, nowY-height+32, 32, height);
core.setAlpha('route', 1);
}
}
else {
// 移动中
@ -865,10 +859,10 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
}
}
core.clearMap('route', nowX, nowY-height+32, 32, height);
step++;
nowX+=scan[direction].x*2;
nowY+=scan[direction].y*2;
core.clearMap('route', nowX-32, nowY-32, 96, 96);
// 绘制
core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, nowX, nowY-height+32, 32, height);
if (step==16) {
@ -883,7 +877,6 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) {
////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 //////
maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) {
time = time || 500;
core.clearMap('route');
var block = core.getBlock(sx,sy);
if (block==null) {
if (core.isset(callback)) callback();
@ -891,12 +884,8 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) {
}
var id = block.block.id;
// core.status.replay.animate=true;
// 需要删除该块
core.removeBlock(sx,sy);
core.clearMap('ui');
core.setAlpha('ui', 1.0);
block=block.block;
var image, bx, by, height = block.event.height || 32;
@ -926,8 +915,8 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) {
by = core.material.icons[block.event.cls][block.event.id];
}
var opacityVal = 1;
core.setOpacity('route', opacityVal);
var alpha = 1;
core.setAlpha('route', alpha);
core.canvas.route.drawImage(image, bx*32, by * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height);
core.playSound('jump.mp3');
@ -977,12 +966,10 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) {
core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, drawX(), drawY()-height+32, 32, height);
}
else {
if (keep) opacityVal=0;
else opacityVal -= 0.06;
core.setOpacity('route', opacityVal);
if (keep) alpha=0;
else alpha -= 0.06;
core.clearMap('route', drawX(), drawY()-height+32, 32, height);
core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, drawX(), drawY()-height+32, 32, height);
if (opacityVal<=0) {
if (alpha<=0) {
clearInterval(animate);
core.clearMap('route');
core.setOpacity('route', 1);
@ -990,9 +977,13 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) {
core.setBlock(id, ex, ey);
core.showBlock(ex, ey);
}
// core.status.replay.animate=false;
if (core.isset(callback)) callback();
}
else {
core.setAlpha('route', alpha);
core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, drawX(), drawY()-height+32, 32, height);
core.setAlpha('route', 1);
}
}
}, time / 16 / core.status.replay.speed);

View File

@ -314,21 +314,29 @@ ui.prototype.getTitleAndIcon = function (content) {
content=content.substring(index+1);
var ss=str.split(",");
if (ss.length==1) {
id=ss[0];
if (id=='hero') name = core.status.hero.name;
else if (core.isset(core.material.enemys[id])) {
name = core.material.enemys[id].name;
getInfo(id);
if (/^[-\w.]+\.png$/.test(ss[0])) {
image = core.material.images.images[ss[0]];
}
else {
name=id;
id='npc';
id=ss[0];
if (id=='hero') name = core.status.hero.name;
else if (core.isset(core.material.enemys[id])) {
name = core.material.enemys[id].name;
getInfo(id);
}
else {
name=id;
id='npc';
}
}
}
else {
name=ss[0];
id = 'npc';
if (ss[1]=='hero') id = 'hero';
else if (/^[-\w.]+\.png$/.test(ss[1])) {
image = core.material.images.images[ss[1]];
}
else getInfo(ss[1]);
}
}
@ -390,9 +398,32 @@ ui.prototype.drawWindowSkin = function(background,canvas,x,y,w,h,direction,px,py
// 仿RM窗口皮肤 ↑
}
// 绘制纯色的背景框
ui.prototype.drawPureBackground = function (background,canvas,borderColor,x,y,w,h,direction,px,py) {
// 计算有效文本框的宽度
ui.prototype.calTextBoxWidth = function (canvas, content, min_width, max_width) {
// 无限长度自动换行
var allLines = core.splitLines(canvas, content);
// 如果不存在手动换行,则二分自动换行
if (allLines.length == 1) {
var prefer_lines = 3;
var start = Math.floor(min_width), end = Math.floor(max_width);
while (start < end) {
var mid = Math.floor((start+end)/2);
if (core.splitLines(canvas, content, mid).length < prefer_lines)
end = mid;
else
start = mid + 1;
}
return mid;
}
// 存在手动换行:以最长的为准
else {
var w = 0;
allLines.forEach(function (t) {
w = Math.max(w, core.canvas[canvas].measureText(t).width);
});
return core.clamp(w, min_width, max_width);
}
}
////// 绘制一个对话框 //////
@ -469,17 +500,36 @@ ui.prototype.drawTextBox = function(content, showAll) {
core.status.boxAnimateObjs = [];
core.clearMap('ui');
var left=7, right=416-left, width = right-left;
var content_left = left + 25;
if (id=='hero' || core.isset(icon)) content_left=left+63;
var validWidth = right-content_left - 10;
var font = textfont + 'px Verdana';
if (textAttribute.bold) font = "bold "+font;
var realContent = content.replace(/(\r|\\r)(\[.*?])?/g, "");
var height = 20 + (textfont+5)*(core.splitLines("ui", realContent, validWidth, font).length+1)
+ (id=='hero'?core.material.icons.hero.height-10:core.isset(name)?iconHeight-10:0);
var leftSpace = 25, rightSpace = 12;
if (core.isset(px) && core.isset(py)) leftSpace = 20;
if (id=='hero' || core.isset(icon)) leftSpace = 62; // 行走图15+32+15
else if (core.isset(image)) leftSpace = 90; // 大头像10+70+10
var left = 7, right = 416 - left, width = right - left, validWidth = width - leftSpace - rightSpace;
// 对话框效果:改为动态计算
if (core.isset(px) && core.isset(py)) {
var min_width = 220 - leftSpace, max_width = validWidth;
core.setFont('ui', font);
validWidth = this.calTextBoxWidth('ui', realContent, min_width, max_width);
width = validWidth + leftSpace + rightSpace;
// left必须在7~416-7-width区间内以保证left>=7right<=416-7
left = core.clamp(32*px+16-width/2, 7, 416-7-width);
right = left + width;
}
var content_left = left + leftSpace;
var height = 30 + (textfont+5)*core.splitLines("ui", realContent, validWidth, font).length;
if (core.isset(name)) height += titlefont + 5;
if (id == 'hero')
height = Math.max(height, core.material.icons.hero.height+50);
else if (core.isset(icon))
height = Math.max(height, iconHeight+50);
else if (core.isset(image))
height = Math.max(height, 90);
var xoffset = 11, yoffset = 16;
@ -539,10 +589,10 @@ ui.prototype.drawTextBox = function(content, showAll) {
// 名称
core.canvas.ui.textAlign = "left";
var content_top = top + 35;
var content_top = top + 15 + textfont;
if (core.isset(id)) {
content_top = top+57;
content_top += (titlefont + 5);
core.setFillStyle('ui', titleColor);
core.setStrokeStyle('ui', titleColor);
@ -551,17 +601,17 @@ ui.prototype.drawTextBox = function(content, showAll) {
core.setAlpha('ui', alpha);
core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, heroHeight+2, null, 2);
core.setAlpha('ui', 1);
core.fillText('ui', name, content_left, top + 30, null, 'bold '+titlefont+'px Verdana');
core.fillText('ui', name, content_left, top + 8 + titlefont, null, 'bold '+titlefont+'px Verdana');
core.clearMap('ui', left + 15, top + 40, 32, heroHeight);
core.fillRect('ui', left + 15, top + 40, 32, heroHeight, core.material.groundPattern);
var heroIcon = core.material.icons.hero['down'];
core.canvas.ui.drawImage(core.material.images.hero, heroIcon.stop * 32, heroIcon.loc * heroHeight, 32, heroHeight, left+15, top+40, 32, heroHeight);
}
else {
core.fillText('ui', name, content_left, top + 30, null, 'bold '+titlefont+'px Verdana');
core.fillText('ui', name, content_left, top + 8 + titlefont, null, 'bold '+titlefont+'px Verdana');
if (core.isset(icon)) {
core.setAlpha('ui', alpha);
core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, iconHeight + 2, null, 2);
core.strokeRect('ui', left + 15 - 1, top + 40-1, 34, iconHeight + 2, null, 2);
core.setAlpha('ui', 1);
core.status.boxAnimateObjs = [];
core.status.boxAnimateObjs.push({
@ -570,11 +620,13 @@ ui.prototype.drawTextBox = function(content, showAll) {
'image': image,
'pos': icon*iconHeight
});
core.drawBoxAnimate();
}
}
}
if (core.isset(image) && !core.isset(icon)) {
core.canvas.ui.drawImage(image, 0, 0, image.width, image.height, left+10, top+10, 70, 70);
}
var offsetx = content_left, offsety = content_top;
core.setFont('ui', font);

View File

@ -79,7 +79,7 @@ utils.prototype.splitLines = function(canvas, text, maxLength, font) {
else {
var toAdd = text.substring(last, i+1);
var width = core.canvas[canvas].measureText(toAdd).width;
if (width>maxLength) {
if (core.isset(maxLength) && width>maxLength) {
contents.push(text.substring(last, i));
last=i;
}

View File

@ -25,6 +25,7 @@ main.floors.sample0=
[ 88, 89, 90, 91, 92, 93, 94, 2, 81, 82, 83, 84, 86]
],
"firstArrive": [
{"type": "setText", "background": "winskin.png"},
"\t[样板提示]首次到达某层可以触发 firstArrive 事件该事件可类似于RMXP中的“自动执行脚本”。\n\n本事件支持一切的事件类型常常用来触发对话例如",
"\t[hero]\b[up,hero]我是谁?我从哪来?我又要到哪去?",
"\t[仙子,fairy]你问我...?我也不知道啊...",

View File

@ -2,13 +2,16 @@
怪物和NPC的行走图和朝向问题详见文档
可以引入WindowSkin作为对话框的背景素材
\r可以动态调整剧情文本的颜色
允许使用\t[标题,1.png]来绘制大头像图
对话框的宽度可以根据文本长度自动调整
\r[red]可以动态调整剧情文本的颜色
升级事件改用事件编辑器完成
每层楼都增添该层的并行事件处理
新增快捷键N返回标题P查看评论O打开工程
道具可以设置是否在回放时绘制道具栏或直接使用
追加素材一次可以追加多个
可以同时异步移动/跳跃勇士和多个NPC
可以同时异步移动两张或以上的图片了
追加素材一次可以追加多个
菜单栏中新增虚拟键盘的弹出
修复所有已知Bug部分细节优化