mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-02-28 17:37:07 +08:00
feat: 新的渲染系统
This commit is contained in:
parent
af38d2e47e
commit
2f8fbd375c
@ -802,11 +802,19 @@ control.prototype.setHeroMoveInterval = function (callback) {
|
||||
if (core.status.replay.speed > 6) toAdd = 4;
|
||||
if (core.status.replay.speed > 12) toAdd = 8;
|
||||
|
||||
Mota.r(() => {
|
||||
const render = Mota.require('module', 'Render').heroRender;
|
||||
render.move(true);
|
||||
});
|
||||
|
||||
core.interval.heroMoveInterval = window.setInterval(function () {
|
||||
render.offset += toAdd * 4;
|
||||
core.status.heroMoving += toAdd;
|
||||
if (core.status.heroMoving >= 8) {
|
||||
clearInterval(core.interval.heroMoveInterval);
|
||||
core.status.heroMoving = 0;
|
||||
render.offset = 0;
|
||||
render.move(false);
|
||||
if (callback) callback();
|
||||
}
|
||||
}, ((core.values.moveSpeed / 8) * toAdd) / core.status.replay.speed);
|
||||
@ -1003,22 +1011,32 @@ control.prototype.tryMoveDirectly = function (destX, destY) {
|
||||
};
|
||||
|
||||
////// 绘制勇士 //////
|
||||
control.prototype.drawHero = function (status, offset, frame) {
|
||||
control.prototype.drawHero = function (status, offset = 0, frame) {
|
||||
if (!core.isPlaying() || !core.status.floorId || core.status.gameOver)
|
||||
return;
|
||||
if (main.mode === 'play') {
|
||||
Mota.require('module', 'Render').heroRender.draw();
|
||||
if (!core.hasFlag('__lockViewport__')) {
|
||||
const { x, y, direction } = core.status.hero.loc;
|
||||
var way = core.utils.scan2[direction];
|
||||
var dx = way.x,
|
||||
dy = way.y;
|
||||
var offsetX =
|
||||
typeof offset == 'number' ? dx * offset : offset.x || 0;
|
||||
var offsetY =
|
||||
typeof offset == 'number' ? dy * offset : offset.y || 0;
|
||||
offset = { x: offsetX, y: offsetY, offset: offset };
|
||||
this._drawHero_updateViewport(x, y, offset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var x = core.getHeroLoc('x'),
|
||||
y = core.getHeroLoc('y'),
|
||||
direction = core.getHeroLoc('direction');
|
||||
status = status || 'stop';
|
||||
if (!offset) offset = 0;
|
||||
|
||||
var way = core.utils.scan2[direction];
|
||||
var dx = way.x,
|
||||
dy = way.y;
|
||||
var offsetX = typeof offset == 'number' ? dx * offset : offset.x || 0;
|
||||
var offsetY = typeof offset == 'number' ? dy * offset : offset.y || 0;
|
||||
offset = { x: offsetX, y: offsetY, offset: offset };
|
||||
|
||||
core.clearAutomaticRouteNode(x + dx, y + dy);
|
||||
core.clearMap('hero');
|
||||
core.status.heroCenter.px = 32 * x + offsetX + 16;
|
||||
@ -1030,9 +1048,6 @@ control.prototype.drawHero = function (status, offset, frame) {
|
||||
delete core.canvas.hero._px;
|
||||
delete core.canvas.hero._py;
|
||||
core.status.preview.enabled = false;
|
||||
if (!core.hasFlag('__lockViewport__')) {
|
||||
this._drawHero_updateViewport(x, y, offset);
|
||||
}
|
||||
|
||||
this._drawHero_draw(direction, x, y, status, offset, frame);
|
||||
};
|
||||
@ -3499,75 +3514,6 @@ control.prototype.hideStatusBar = function (showToolbox) {
|
||||
////// 改变工具栏为按钮1-8 //////
|
||||
control.prototype.setToolbarButton = function (useButton) {
|
||||
// Deprecated. Use CustomToolbar instead.
|
||||
return;
|
||||
if (!core.domStyle.showStatusBar) {
|
||||
// 隐藏状态栏时检查竖屏
|
||||
if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
|
||||
for (var i = 0; i < core.dom.tools.length; ++i)
|
||||
core.dom.tools[i].style.display = 'none';
|
||||
return;
|
||||
}
|
||||
if (!core.hasFlag('showToolbox')) return;
|
||||
else core.dom.tools.hard.style.display = 'block';
|
||||
}
|
||||
|
||||
if (useButton == null) useButton = core.domStyle.toolbarBtn;
|
||||
if (!core.domStyle.isVertical && !core.flags.extendToolbar)
|
||||
useButton = false;
|
||||
core.domStyle.toolbarBtn = useButton;
|
||||
|
||||
if (useButton) {
|
||||
[
|
||||
'book',
|
||||
'fly',
|
||||
'toolbox',
|
||||
'keyboard',
|
||||
'shop',
|
||||
'save',
|
||||
'load',
|
||||
'settings'
|
||||
].forEach(function (t) {
|
||||
core.statusBar.image[t].style.display = 'none';
|
||||
});
|
||||
[
|
||||
'btn1',
|
||||
'btn2',
|
||||
'btn3',
|
||||
'btn4',
|
||||
'btn5',
|
||||
'btn6',
|
||||
'btn7',
|
||||
'btn8'
|
||||
].forEach(function (t) {
|
||||
core.statusBar.image[t].style.display = 'block';
|
||||
});
|
||||
main.statusBar.image.btn8.style.filter = core.getLocalStorage('altKey')
|
||||
? 'sepia(1) contrast(1.5)'
|
||||
: '';
|
||||
} else {
|
||||
[
|
||||
'btn1',
|
||||
'btn2',
|
||||
'btn3',
|
||||
'btn4',
|
||||
'btn5',
|
||||
'btn6',
|
||||
'btn7',
|
||||
'btn8'
|
||||
].forEach(function (t) {
|
||||
core.statusBar.image[t].style.display = 'none';
|
||||
});
|
||||
['book', 'fly', 'toolbox', 'save', 'load', 'settings'].forEach(
|
||||
function (t) {
|
||||
core.statusBar.image[t].style.display = 'block';
|
||||
}
|
||||
);
|
||||
core.statusBar.image.keyboard.style.display =
|
||||
core.statusBar.image.shop.style.display =
|
||||
core.domStyle.isVertical || core.flags.extendToolbar
|
||||
? 'block'
|
||||
: 'none';
|
||||
}
|
||||
};
|
||||
|
||||
////// ------ resize处理 ------ //
|
||||
@ -3789,16 +3735,4 @@ control.prototype._resize_toolBar = function (obj) {
|
||||
|
||||
control.prototype._resize_tools = function (obj) {
|
||||
// Deprecated. Use CustomToolbar instead.
|
||||
// var toolsHeight = 32 * core.domStyle.scale;
|
||||
// var toolsMarginLeft;
|
||||
// toolsMarginLeft = (core._HALF_WIDTH_ - 3) * 3 * core.domStyle.scale;
|
||||
// for (var i = 0; i < core.dom.tools.length; ++i) {
|
||||
// var style = core.dom.tools[i].style;
|
||||
// style.height = toolsHeight + 'px';
|
||||
// style.marginLeft = toolsMarginLeft + 'px';
|
||||
// style.marginTop = 3 * core.domStyle.scale + 'px';
|
||||
// }
|
||||
// core.dom.hard.style.lineHeight = toolsHeight + 'px';
|
||||
// core.dom.hard.style.width =
|
||||
// obj.outerWidth - 9 * toolsMarginLeft - 8.5 * toolsHeight - 12 + 'px';
|
||||
};
|
||||
|
@ -843,6 +843,7 @@ events.prototype._changeFloor_afterChange = function (info, callback) {
|
||||
|
||||
events.prototype.changingFloor = function (floorId, heroLoc) {
|
||||
this.eventdata.changingFloor(floorId, heroLoc);
|
||||
Mota.require('var', 'hook').emit('changingFloor', floorId, heroLoc);
|
||||
};
|
||||
|
||||
////// 转换楼层结束的事件 //////
|
||||
@ -4252,6 +4253,7 @@ events.prototype._vibrate_update = function (shakeInfo) {
|
||||
/////// 使用事件让勇士移动。这个函数将不会触发任何事件 //////
|
||||
events.prototype.eventMoveHero = function (steps, time, callback) {
|
||||
time = time || core.values.moveSpeed;
|
||||
// const render = Mota.require('module', 'Render').heroRender;
|
||||
var step = 0,
|
||||
moveSteps = (steps || [])
|
||||
.map(function (t) {
|
||||
@ -4275,9 +4277,11 @@ events.prototype.eventMoveHero = function (steps, time, callback) {
|
||||
);
|
||||
});
|
||||
core.status.heroMoving = -1;
|
||||
// render.move(false);
|
||||
var _run = function () {
|
||||
var cb = function () {
|
||||
core.status.heroMoving = 0;
|
||||
// render.move(false);
|
||||
core.drawHero();
|
||||
if (callback) callback();
|
||||
};
|
||||
@ -4339,15 +4343,18 @@ events.prototype.jumpHero = function (ex, ey, time, callback) {
|
||||
};
|
||||
|
||||
events.prototype._jumpHero_doJump = function (jumpInfo, callback) {
|
||||
// const render = Mota.require('module', 'Render').heroRender;
|
||||
var cb = function () {
|
||||
core.setHeroLoc('x', jumpInfo.ex);
|
||||
core.setHeroLoc('y', jumpInfo.ey);
|
||||
render.move(false);
|
||||
core.status.heroMoving = 0;
|
||||
core.drawHero();
|
||||
if (callback) callback();
|
||||
};
|
||||
|
||||
core.status.heroMoving = -1;
|
||||
// render.move(false);
|
||||
var animate = window.setInterval(function () {
|
||||
if (jumpInfo.jump_count > 0) core.events._jumpHero_jumping(jumpInfo);
|
||||
else {
|
||||
@ -4386,6 +4393,7 @@ events.prototype.setHeroIcon = function (name, noDraw) {
|
||||
core.material.images.hero = img;
|
||||
core.material.icons.hero.width = img.width / 4;
|
||||
core.material.icons.hero.height = img.height / 4;
|
||||
// Mota.require('module', 'Render').heroRender.setHero();
|
||||
if (!noDraw) core.drawHero();
|
||||
};
|
||||
|
||||
|
@ -738,28 +738,29 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
||||
},
|
||||
{
|
||||
"need": "100000",
|
||||
"title": "中级智人",
|
||||
"title": "高级智人",
|
||||
"clear": true,
|
||||
"action": [
|
||||
{
|
||||
"type": "setValue",
|
||||
"name": "status:mdef",
|
||||
"operator": "+=",
|
||||
"value": "2000"
|
||||
"value": "10000"
|
||||
},
|
||||
{
|
||||
"type": "setValue",
|
||||
"name": "status:atk",
|
||||
"operator": "+=",
|
||||
"value": "25"
|
||||
"value": "250"
|
||||
},
|
||||
{
|
||||
"type": "setValue",
|
||||
"name": "status:def",
|
||||
"operator": "+=",
|
||||
"value": "25"
|
||||
"value": "250"
|
||||
},
|
||||
"恭喜升级!攻防+25,智慧+2000!"
|
||||
"恭喜升级!攻防+250,智慧+10000!",
|
||||
"这是这个游戏的最后一级,第三章的玩法会改变,不再设置等级"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -16,7 +16,11 @@ main.floors.MT59=
|
||||
"firstArrive": [],
|
||||
"eachArrive": [],
|
||||
"parallelDo": "",
|
||||
"events": {},
|
||||
"events": {
|
||||
"12,5": [
|
||||
"注意这个boss现在不是必打的,只要能打过去苍蓝殿上方区域的那个黄卫兵就能去上面的区域了,之后回头再来清这个boss也可以"
|
||||
]
|
||||
},
|
||||
"changeFloor": {
|
||||
"14,7": {
|
||||
"floorId": "MT58",
|
||||
@ -113,7 +117,7 @@ main.floors.MT59=
|
||||
[648,491, 28,491,648, 0,648, 0,648,648,648,648, 0, 29,648],
|
||||
[648, 27,468, 27,648,487,648, 21,648, 27, 0,648, 27, 0,648],
|
||||
[648, 0, 28, 0, 85, 0,648,487,648, 0, 29,657, 0, 28,648],
|
||||
[648,648,648,648,648,390,648,390,648,484,648,648,648,648,648],
|
||||
[648,648,648,648,648,390,648,390,648,484,648,648,129,648,648],
|
||||
[648,482, 0,482,648, 0,249, 0,648,539,648,482, 0,482,648],
|
||||
[648, 0,666, 0,492,403,648,648,648, 0,492, 0,381, 0, 94],
|
||||
[648,482, 0,482,648, 0,249, 0,648,539,648,482, 0,482,648],
|
||||
|
@ -21,10 +21,10 @@ main.floors.MT69=
|
||||
"提示一个本地图的较优解法:从此处直接向右走,然后反过来把红骑士杀了,就基本上没光环了"
|
||||
],
|
||||
"2,7": [
|
||||
"中间的那个木牌会提示一种较优解法"
|
||||
"不要被这一层吓到了(,实际上光环只有四个黄光环,其他的都是黄光环产生的。如果实在想不到解法,中间的那个木牌会提示一种较优解法"
|
||||
],
|
||||
"6,13": [
|
||||
"中间的那个木牌会提示一种较优解法"
|
||||
"不要被这一层吓到了(,实际上光环只有四个黄光环,其他的都是黄光环产生的。如果实在想不到解法,中间的那个木牌会提示一种较优解法"
|
||||
]
|
||||
},
|
||||
"changeFloor": {
|
||||
|
@ -29,6 +29,13 @@ main.floors.MT73=
|
||||
7,
|
||||
0
|
||||
]
|
||||
},
|
||||
"7,0": {
|
||||
"floorId": "MT74",
|
||||
"loc": [
|
||||
7,
|
||||
14
|
||||
]
|
||||
}
|
||||
},
|
||||
"beforeBattle": {},
|
||||
|
@ -17,7 +17,22 @@ main.floors.MT74=
|
||||
"eachArrive": [],
|
||||
"parallelDo": "",
|
||||
"events": {},
|
||||
"changeFloor": {},
|
||||
"changeFloor": {
|
||||
"7,0": {
|
||||
"floorId": "MT75",
|
||||
"loc": [
|
||||
7,
|
||||
14
|
||||
]
|
||||
},
|
||||
"7,14": {
|
||||
"floorId": "MT73",
|
||||
"loc": [
|
||||
7,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"beforeBattle": {},
|
||||
"afterBattle": {},
|
||||
"afterGetItem": {},
|
||||
@ -26,21 +41,21 @@ main.floors.MT74=
|
||||
"cannotMove": {},
|
||||
"cannotMoveIn": {},
|
||||
"map": [
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0,103, 0, 0, 0,103, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0,103, 0, 0, 0,103, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
[648,648,648,648,648,648,648, 91,648,648,648,648,648,648,648],
|
||||
[648,430,472,420,648, 0, 0, 0, 0, 0,648,420,472,430,648],
|
||||
[648,648, 85,648,648, 0, 0, 0, 0, 0,648,648, 85,648,648],
|
||||
[648, 0, 0, 0,648, 0, 0, 0, 0, 0,648, 0, 0, 0,648],
|
||||
[648, 0,487, 0,648, 0, 0, 0, 0, 0,648, 0,487, 0,648],
|
||||
[648, 0, 0, 0,648,103, 0, 0, 0,103,648, 0, 0, 0,648],
|
||||
[648,648,492,648,648, 0, 0, 0, 0, 0,648,648,492,648,648],
|
||||
[ 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94],
|
||||
[648,648,492,648,648, 0, 0, 0, 0, 0,648,648,492,648,648],
|
||||
[648, 0, 0, 0,648,103, 0, 0, 0,103,648, 0, 0, 0,648],
|
||||
[648, 0,487, 0,648, 0, 0, 0, 0, 0,648, 0,487, 0,648],
|
||||
[648, 0, 0, 0,648, 0, 0, 0, 0, 0,648, 0, 0, 0,648],
|
||||
[648,648, 85,648,648, 0, 0, 0, 0, 0,648,648, 85,648,648],
|
||||
[648,430,472,420,648, 0, 0, 0, 0, 0,648,420,472,430,648],
|
||||
[648,648,648,648,648,648,648, 93,648,648,648,648,648,648,648]
|
||||
],
|
||||
"bgmap": [
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
|
@ -17,7 +17,15 @@ main.floors.MT75=
|
||||
"eachArrive": [],
|
||||
"parallelDo": "",
|
||||
"events": {},
|
||||
"changeFloor": {},
|
||||
"changeFloor": {
|
||||
"7,14": {
|
||||
"floorId": "MT74",
|
||||
"loc": [
|
||||
7,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"beforeBattle": {},
|
||||
"afterBattle": {},
|
||||
"afterGetItem": {},
|
||||
@ -26,21 +34,21 @@ main.floors.MT75=
|
||||
"cannotMove": {},
|
||||
"cannotMoveIn": {},
|
||||
"map": [
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
[648,648,648,648,648,648,648,648,648,648,648,648,648,648,648],
|
||||
[648,491,491,494,491,494, 0, 0, 0,494,491,494,491,491,648],
|
||||
[648,648,648,648,648,648, 0, 0, 0,648,648,648,648,648,648],
|
||||
[648,484,484,492,484,492, 0, 0, 0,492,484,492,484,484,648],
|
||||
[648,648,648,648,648,648, 0, 0, 0,648,648,648,648,648,648],
|
||||
[648, 0, 0, 0, 0,103, 0, 0, 0,103, 0, 0, 0, 0,648],
|
||||
[648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,648],
|
||||
[ 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94],
|
||||
[648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,648],
|
||||
[648, 0, 0, 0, 0,103, 0, 0, 0,103, 0, 0, 0, 0,648],
|
||||
[648,648,648,648,648,648, 0, 0, 0,648,648,648,648,648,648],
|
||||
[648,484,484,492,484,492, 0, 0, 0,492,484,492,484,484,648],
|
||||
[648,648,648,648,648,648, 0, 0, 0,648,648,648,648,648,648],
|
||||
[648,491,491,494,491,494, 0, 0, 0,494,491,494,491,491,648],
|
||||
[648,648,648,648,648,648,648, 93,648,648,648,648,648,648,648]
|
||||
],
|
||||
"bgmap": [
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
|
@ -226,27 +226,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
if (core.flags.flyRecordPosition) {
|
||||
loc = core.getFlag('__leaveLoc__', {})[toId] || null;
|
||||
}
|
||||
if (
|
||||
core.status.maps[toId].flyPoint != null &&
|
||||
core.status.maps[toId].flyPoint.length == 2
|
||||
) {
|
||||
loc = {
|
||||
x: core.status.maps[toId].flyPoint[0],
|
||||
y: core.status.maps[toId].flyPoint[1]
|
||||
};
|
||||
}
|
||||
if (loc == null) {
|
||||
// 获得两个楼层的索引,以决定是上楼梯还是下楼梯
|
||||
var fromIndex = core.floorIds.indexOf(fromId),
|
||||
toIndex = core.floorIds.indexOf(toId);
|
||||
var stair = fromIndex <= toIndex ? 'downFloor' : 'upFloor';
|
||||
// 地下层:同层传送至上楼梯
|
||||
if (
|
||||
fromIndex == toIndex &&
|
||||
core.status.maps[fromId].underGround
|
||||
)
|
||||
stair = 'upFloor';
|
||||
}
|
||||
|
||||
// 记录录像
|
||||
core.status.route.push('fly:' + toId);
|
||||
|
@ -1048,7 +1048,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
|
||||
},
|
||||
"I472": {
|
||||
"cls": "items",
|
||||
"name": "新物品",
|
||||
"name": "传奇绿宝石",
|
||||
"text": ",防御+${core.values.blueGem}",
|
||||
"itemEffect": "core.status.hero.mdef += Math.round(640 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.Plugin.require('skillTree_g').getSkillLevel(12) / 20 + 1))",
|
||||
"itemEffectTip": ",智慧+${Math.round(640 * core.status.thisMap.ratio / core.getFlag(\"hard\") * (Mota.Plugin.require('skillTree_g').getSkillLevel(12) / 20 + 1))}",
|
||||
|
@ -392,6 +392,7 @@ p#name {
|
||||
}
|
||||
|
||||
#hero {
|
||||
display: none;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
/** 是否是高清画布 */
|
||||
highResolution: boolean = true;
|
||||
|
||||
scale: number = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -41,6 +43,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
if (this.autoScale && this.highResolution) {
|
||||
ratio *= core.domStyle.scale;
|
||||
}
|
||||
this.scale = ratio;
|
||||
this.canvas.width = width * ratio;
|
||||
this.canvas.height = height * ratio;
|
||||
this.width = width;
|
||||
@ -82,7 +85,13 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
newCanvas.setHD(canvas.highResolution);
|
||||
newCanvas.withGameScale(canvas.autoScale);
|
||||
newCanvas.size(canvas.width, canvas.height);
|
||||
newCanvas.ctx.drawImage(canvas.canvas, 0, 0);
|
||||
newCanvas.ctx.drawImage(
|
||||
canvas.canvas,
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height
|
||||
);
|
||||
return newCanvas;
|
||||
}
|
||||
}
|
||||
@ -144,6 +153,7 @@ export class MotaCanvas2D extends MotaOffscreenCanvas2D {
|
||||
this.canvas.style.width = `${width}px`;
|
||||
this.canvas.style.height = `${height}px`;
|
||||
}
|
||||
this.scale = ratio;
|
||||
this.canvas.width = width * ratio;
|
||||
this.canvas.height = height * ratio;
|
||||
this.width = width;
|
||||
|
@ -114,18 +114,18 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
Shadow.update(true);
|
||||
}
|
||||
});
|
||||
Mota.rewrite(core.events, 'changingFloor', 'add', (_, floorId) => {
|
||||
if (!main.replayChecking) {
|
||||
Shadow.clearBuffer();
|
||||
Shadow.update();
|
||||
setCanvasFilterByFloorId(floorId);
|
||||
}
|
||||
});
|
||||
Mota.rewrite(core.control, 'loadData', 'add', () => {
|
||||
if (!main.replayChecking) {
|
||||
Shadow.update(true);
|
||||
}
|
||||
});
|
||||
Mota.require('var', 'hook').on('changingFloor', (floorId) => {
|
||||
if (!main.replayChecking) {
|
||||
Shadow.clearBuffer();
|
||||
Shadow.update();
|
||||
setCanvasFilterByFloorId(floorId);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// 深度测试着色器
|
||||
|
@ -60,6 +60,15 @@ import { ResourceController } from './loader/controller';
|
||||
import { logger } from './common/logger';
|
||||
import { Danmaku } from './main/custom/danmaku';
|
||||
import * as Shadow from './fx/shadow';
|
||||
import { MotaCanvas2D } from './fx/canvas2d';
|
||||
import * as portal from './fx/portal';
|
||||
import { heroRender } from './render/hero';
|
||||
import { MotaRenderer } from './render/render';
|
||||
import { Container } from './render/container';
|
||||
import { Sprite } from './render/sprite';
|
||||
import { Camera } from './render/camera';
|
||||
import { Image, Text } from './render/preset/misc';
|
||||
import { RenderItem } from './render/item';
|
||||
|
||||
// ----- 类注册
|
||||
Mota.register('class', 'AudioPlayer', AudioPlayer);
|
||||
@ -78,6 +87,7 @@ Mota.register('class', 'UiController', UiController);
|
||||
Mota.register('class', 'MComponent', MComponent);
|
||||
Mota.register('class', 'ResourceController', ResourceController);
|
||||
Mota.register('class', 'Danmaku', Danmaku);
|
||||
Mota.register('class', 'MotaCanvas2D', MotaCanvas2D);
|
||||
// ----- 函数注册
|
||||
Mota.register('fn', 'm', m);
|
||||
Mota.register('fn', 'unwrapBinary', unwarpBinary);
|
||||
@ -133,6 +143,19 @@ Mota.register('module', 'UIComponents', {
|
||||
});
|
||||
Mota.register('module', 'MCGenerator', MCGenerator);
|
||||
Mota.register('module', 'Shadow', Shadow);
|
||||
Mota.register('module', 'Effect', {
|
||||
Portal: portal
|
||||
});
|
||||
Mota.register('module', 'Render', {
|
||||
heroRender,
|
||||
MotaRenderer,
|
||||
Container,
|
||||
Sprite,
|
||||
Camera,
|
||||
Text,
|
||||
Image,
|
||||
RenderItem
|
||||
});
|
||||
|
||||
main.renderLoaded = true;
|
||||
Mota.require('var', 'hook').emit('renderLoaded');
|
||||
|
@ -17,3 +17,5 @@ export interface ResponseBase {
|
||||
code: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type CSSObj = Partial<Record<CanParseCss, string>>;
|
||||
|
@ -27,6 +27,8 @@ import {
|
||||
FolderOpenOutlined,
|
||||
LayoutOutlined,
|
||||
MessageOutlined,
|
||||
RetweetOutlined,
|
||||
RollbackOutlined,
|
||||
SwapOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { generateKeyboardEvent } from '../custom/keyboard';
|
||||
@ -888,7 +890,7 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
() => {
|
||||
core.doSL('autoSave', 'load');
|
||||
},
|
||||
h(BackwardOutlined)
|
||||
h(RollbackOutlined)
|
||||
);
|
||||
CustomToolbar.misc.register(
|
||||
'redo',
|
||||
@ -896,7 +898,7 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
() => {
|
||||
core.doSL('autoSave', 'reload');
|
||||
},
|
||||
h(SwapOutlined)
|
||||
h(RetweetOutlined)
|
||||
);
|
||||
CustomToolbar.misc.register(
|
||||
'setting',
|
||||
|
@ -474,6 +474,7 @@ mainSetting
|
||||
new MotaSetting()
|
||||
.register('paraLight', '野外阴影', true, COM.Boolean)
|
||||
.register('frag', '打怪特效', true, COM.Boolean)
|
||||
.register('portalParticle', '传送门特效', true, COM.Boolean)
|
||||
)
|
||||
.register(
|
||||
'ui',
|
||||
@ -512,6 +513,7 @@ loading.once('coreInit', () => {
|
||||
'utils.autoScale': !!storage.getValue('utils.autoScale', true),
|
||||
'fx.paraLight': !!storage.getValue('fx.paraLight', true),
|
||||
'fx.frag': !!storage.getValue('fx.frag', true),
|
||||
'fx.portalParticle': !!storage.getValue('fx.portalParticle', true),
|
||||
'ui.mapScale': storage.getValue(
|
||||
'ui.mapScale',
|
||||
isMobile ? 300 : Math.floor(window.innerWidth / 600) * 50
|
||||
@ -565,7 +567,8 @@ mainSetting
|
||||
.setDescription('ui.danmaku', '是否显示弹幕')
|
||||
.setDescription('ui.danmakuSpeed', '弹幕速度,刷新或开关弹幕显示后起效')
|
||||
.setDescription('screen.fontSizeStatus', `修改状态栏的字体大小`)
|
||||
.setDescription('screen.blur', '打开任意ui界面时是否有背景虚化效果,移动端打开后可能会有掉帧或者发热现象。关闭ui后生效');
|
||||
.setDescription('screen.blur', '打开任意ui界面时是否有背景虚化效果,移动端打开后可能会有掉帧或者发热现象。关闭ui后生效')
|
||||
.setDescription('fx.portalParticle', '是否启用苍蓝之殿的传送门粒子特效,启用后可能对性能及设备发热有所影响');
|
||||
|
||||
function setFontSize() {
|
||||
const absoluteSize = storage.getValue(
|
||||
|
184
src/core/render/camera.ts
Normal file
184
src/core/render/camera.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import { ReadonlyMat3, ReadonlyVec3, mat3, vec2, vec3 } from 'gl-matrix';
|
||||
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
|
||||
|
||||
interface CameraEvent extends EmitableEvent {}
|
||||
|
||||
export class Camera extends EventEmitter<CameraEvent> {
|
||||
mat: mat3 = mat3.create();
|
||||
|
||||
x: number = 0;
|
||||
y: number = 0;
|
||||
scaleX: number = 1;
|
||||
scaleY: number = 1;
|
||||
rad: number = 0;
|
||||
|
||||
/**
|
||||
* 重设摄像机的所有参数
|
||||
*/
|
||||
reset() {
|
||||
mat3.identity(this.mat);
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.scaleX = 1;
|
||||
this.scaleY = 1;
|
||||
this.rad = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改摄像机的缩放,叠加关系
|
||||
*/
|
||||
scale(x: number, y: number = x) {
|
||||
mat3.scale(this.mat, this.mat, [x, y]);
|
||||
this.scaleX *= x;
|
||||
this.scaleY *= y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动摄像机,叠加关系
|
||||
*/
|
||||
move(x: number, y: number) {
|
||||
mat3.translate(this.mat, this.mat, [x, y]);
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转摄像机,叠加关系
|
||||
*/
|
||||
rotate(rad: number) {
|
||||
mat3.rotate(this.mat, this.mat, rad);
|
||||
this.rad += rad;
|
||||
if (this.rad >= Math.PI * 2) {
|
||||
const n = Math.floor(this.rad / Math.PI / 2);
|
||||
this.rad -= n * Math.PI * 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的缩放,非叠加关系
|
||||
*/
|
||||
setScale(x: number, y: number = x) {
|
||||
mat3.scale(this.mat, this.mat, [x / this.scaleX, y / this.scaleY]);
|
||||
this.scaleX = x;
|
||||
this.scaleY = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的位置,非叠加关系
|
||||
*/
|
||||
setTranslate(x: number, y: number) {
|
||||
mat3.translate(this.mat, this.mat, [x - this.x, y - this.y]);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的旋转,非叠加关系
|
||||
*/
|
||||
setRotate(rad: number) {
|
||||
mat3.rotate(this.mat, this.mat, rad - this.rad);
|
||||
this.rad = rad;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的变换矩阵,叠加模式
|
||||
* @param a 水平缩放
|
||||
* @param b 垂直倾斜
|
||||
* @param c 水平倾斜
|
||||
* @param d 垂直缩放
|
||||
* @param e 水平移动
|
||||
* @param f 垂直移动
|
||||
*/
|
||||
transform(
|
||||
a: number,
|
||||
b: number,
|
||||
c: number,
|
||||
d: number,
|
||||
e: number,
|
||||
f: number
|
||||
) {
|
||||
mat3.multiply(
|
||||
this.mat,
|
||||
this.mat,
|
||||
mat3.fromValues(a, b, 0, c, d, 0, e, f, 1)
|
||||
);
|
||||
this.calAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的变换矩阵,非叠加模式
|
||||
* @param a 水平缩放
|
||||
* @param b 垂直倾斜
|
||||
* @param c 水平倾斜
|
||||
* @param d 垂直缩放
|
||||
* @param e 水平移动
|
||||
* @param f 垂直移动
|
||||
*/
|
||||
setTransform(
|
||||
a: number,
|
||||
b: number,
|
||||
c: number,
|
||||
d: number,
|
||||
e: number,
|
||||
f: number
|
||||
) {
|
||||
mat3.set(this.mat, a, b, 0, c, d, 0, e, f, 1);
|
||||
this.calAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算 translation scaling rotation
|
||||
*/
|
||||
calAttributes() {
|
||||
const [x, y] = getTranslation(this.mat);
|
||||
const [scaleX, scaleY] = getScaling(this.mat);
|
||||
const rad = getRotation(this.mat);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
this.rad = rad;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据摄像机的信息,将一个点转换为计算后的位置
|
||||
* @param camera 摄像机
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
static transformed(camera: Camera, x: number, y: number) {
|
||||
return multiplyVec3(camera.mat, [x, y, 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据摄像机的信息,将一个计算后的位置逆转换为原位置
|
||||
* @param camera 摄像机
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
static untransformed(camera: Camera, x: number, y: number) {
|
||||
const invert = mat3.create();
|
||||
mat3.invert(invert, camera.mat);
|
||||
return multiplyVec3(invert, [x, y, 1]);
|
||||
}
|
||||
}
|
||||
|
||||
function multiplyVec3(mat: ReadonlyMat3, vec: ReadonlyVec3) {
|
||||
return vec3.fromValues(
|
||||
mat[0] * vec[0] + mat[3] * vec[1] + mat[6] * vec[2],
|
||||
mat[1] * vec[0] + mat[4] * vec[1] + mat[7] * vec[2],
|
||||
mat[2] * vec[0] + mat[5] * vec[1] + mat[8] * vec[2]
|
||||
);
|
||||
}
|
||||
|
||||
function getTranslation(mat: ReadonlyMat3): vec2 {
|
||||
return [mat[6], mat[7]];
|
||||
}
|
||||
|
||||
function getScaling(mat: ReadonlyMat3): vec2 {
|
||||
return [Math.hypot(mat[0], mat[3]), Math.hypot(mat[1], mat[4])];
|
||||
}
|
||||
|
||||
function getRotation(mat: ReadonlyMat3): number {
|
||||
return Math.atan2(mat[3], mat[0]);
|
||||
}
|
61
src/core/render/container.ts
Normal file
61
src/core/render/container.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Camera } from './camera';
|
||||
import {
|
||||
ICanvasCachedRenderItem,
|
||||
RenderItem,
|
||||
RenderItemPosition,
|
||||
withCacheRender
|
||||
} from './item';
|
||||
|
||||
export class Container extends RenderItem implements ICanvasCachedRenderItem {
|
||||
children: RenderItem[] = [];
|
||||
sortedChildren: RenderItem[] = [];
|
||||
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
|
||||
constructor(type: RenderItemPosition = 'static') {
|
||||
super();
|
||||
this.canvas = new MotaOffscreenCanvas2D();
|
||||
this.type = type;
|
||||
this.canvas.withGameScale(true);
|
||||
}
|
||||
|
||||
render(
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
camera: Camera
|
||||
): void {
|
||||
this.emit('beforeUpdate', this);
|
||||
withCacheRender(this, canvas, ctx, camera, c => {
|
||||
this.sortedChildren.forEach(v => {
|
||||
v.render(c.canvas, c.ctx, camera);
|
||||
});
|
||||
});
|
||||
this.emit('afterUpdate', this);
|
||||
}
|
||||
|
||||
size(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.canvas.size(width, height);
|
||||
this.writing = this.using;
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
pos(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子元素到这个容器上,然后在下一个tick执行更新
|
||||
* @param children 要添加的子元素
|
||||
*/
|
||||
appendChild(children: RenderItem[]) {
|
||||
children.forEach(v => (v.parent = this));
|
||||
this.children.push(...children);
|
||||
this.sortedChildren = this.children
|
||||
.slice()
|
||||
.sort((a, b) => a.zIndex - b.zIndex);
|
||||
}
|
||||
}
|
174
src/core/render/hero.ts
Normal file
174
src/core/render/hero.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import { Ticker } from 'mutate-animate';
|
||||
import { MotaCanvas2D } from '../fx/canvas2d';
|
||||
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
// 重写样板的勇士绘制
|
||||
|
||||
const canvas = MotaCanvas2D.for('@hero', false);
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
canvas.withGameScale(true);
|
||||
canvas.setHD(false);
|
||||
canvas.size(480, 480);
|
||||
canvas.pos(0, 0);
|
||||
canvas.css(`z-index: 40`);
|
||||
canvas.canvas.classList.add('no-anti-aliasing');
|
||||
canvas.setTarget(core.dom.gameDraw);
|
||||
canvas.mount();
|
||||
});
|
||||
|
||||
const DIR_INDEX: Record<Dir, number> = {
|
||||
down: 0,
|
||||
left: 1,
|
||||
right: 2,
|
||||
up: 3
|
||||
};
|
||||
|
||||
interface HeroDrawItem {
|
||||
id: AllIds | 'hero';
|
||||
image: HTMLCanvasElement;
|
||||
}
|
||||
|
||||
interface HeroDrawing extends HeroDrawItem {
|
||||
x: number;
|
||||
y: number;
|
||||
dir: Dir;
|
||||
}
|
||||
|
||||
interface HeroRendererEvent extends EmitableEvent {
|
||||
beforeDraw: () => void;
|
||||
afterDraw: () => void;
|
||||
}
|
||||
|
||||
const resetMoving = debounce((render: HeroRenderer) => {
|
||||
render.moving = 0;
|
||||
}, 500);
|
||||
|
||||
export class HeroRenderer extends EventEmitter<HeroRendererEvent> {
|
||||
followers: HeroDrawItem[] = [];
|
||||
hero!: HeroDrawItem;
|
||||
|
||||
/** 移动状态 */
|
||||
moving: number = 0;
|
||||
/** 移动过程中的偏移 */
|
||||
offset: number = 0;
|
||||
/** 勇士绘制时的alpha通道 */
|
||||
alpha: number = 1;
|
||||
|
||||
ticker: Ticker = new Ticker();
|
||||
|
||||
/** 是否是后退状态 */
|
||||
private back: boolean = false;
|
||||
/** 是否正在移动 */
|
||||
private isMoving: boolean = false;
|
||||
/** 上一次换腿(?的时间 */
|
||||
private lastMoving: number = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
this.ticker.add(time => {
|
||||
if (core.status.heroMoving < 0 || !this.isMoving) return;
|
||||
if (time - this.lastMoving > core.values.moveSpeed) {
|
||||
this.lastMoving = time;
|
||||
this.moving++;
|
||||
this.moving %= 4;
|
||||
}
|
||||
this.draw();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置勇士绘制信息
|
||||
*/
|
||||
setHero() {
|
||||
const image = core.material.images.hero;
|
||||
const canvas = new MotaCanvas2D();
|
||||
canvas.setHD(false);
|
||||
canvas.size(image.width, image.height);
|
||||
canvas.ctx.drawImage(image, 0, 0);
|
||||
this.hero = {
|
||||
id: 'hero',
|
||||
image: canvas.canvas
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行绘制
|
||||
*/
|
||||
draw() {
|
||||
if (!core.isPlaying()) return;
|
||||
const { ctx, canvas: can } = canvas;
|
||||
const { x, y, direction: dir } = core.status.hero.loc;
|
||||
ctx.clearRect(0, 0, can.width, can.height);
|
||||
ctx.globalAlpha = this.alpha;
|
||||
|
||||
this.emit('beforeDraw');
|
||||
|
||||
const data: HeroDrawing[] = [];
|
||||
data.push({ ...this.hero, x, y, dir });
|
||||
core.status.hero.followers.forEach((v, i) => {
|
||||
const { id, image } = this.followers[i];
|
||||
data.push({ x: v.x, y: v.y, dir: v.direction, id, image });
|
||||
});
|
||||
|
||||
const { offsetX: ox, offsetY: oy } = core.bigmap;
|
||||
const sgn = this.back ? -1 : 1;
|
||||
const offset = this.offset * sgn;
|
||||
|
||||
const frame = this.isMoving ? this.moving % 4 : 0;
|
||||
|
||||
data.forEach(v => {
|
||||
// hero offset x
|
||||
const hox = offset * core.utils.scan[v.dir].x;
|
||||
const hoy = offset * core.utils.scan[v.dir].y;
|
||||
const cx = v.x * 32 + 16 - ox + hox;
|
||||
const cy = v.y * 32 + 16 - oy + hoy;
|
||||
const { width, height } = v.image;
|
||||
const pw = width / 4;
|
||||
const ph = height / 4;
|
||||
const line = DIR_INDEX[v.dir];
|
||||
|
||||
const px = cx - pw / 2;
|
||||
const py = cy - ph + 16;
|
||||
|
||||
const sx = frame * pw;
|
||||
const sy = line * ph;
|
||||
|
||||
ctx.drawImage(v.image, sx, sy, pw, ph, px, py, pw, ph);
|
||||
});
|
||||
|
||||
this.emit('afterDraw');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置绘制状态为正在移动还是静止
|
||||
*/
|
||||
move(moving: boolean) {
|
||||
this.isMoving = moving;
|
||||
if (!moving) {
|
||||
resetMoving(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前是否为后退状态
|
||||
*/
|
||||
backward(back: boolean) {
|
||||
this.back = back;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置绘制时的alpha
|
||||
*/
|
||||
setAlpha(alpha: number) {
|
||||
this.alpha = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
const render = new HeroRenderer();
|
||||
|
||||
export { render as heroRender };
|
200
src/core/render/item.ts
Normal file
200
src/core/render/item.ts
Normal file
@ -0,0 +1,200 @@
|
||||
import { isNil } from 'lodash-es';
|
||||
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Camera } from './camera';
|
||||
|
||||
export type RenderFunction = (
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
camera: Camera
|
||||
) => void;
|
||||
|
||||
export type RenderItemPosition = 'absolute' | 'static';
|
||||
|
||||
interface IRenderCache {
|
||||
/** 缓存列表 */
|
||||
cacheList: Map<string, MotaOffscreenCanvas2D>;
|
||||
/** 当前正在使用的缓存 */
|
||||
using?: string;
|
||||
/** 下次绘制需要写入的缓存 */
|
||||
writing?: string;
|
||||
|
||||
/**
|
||||
* 在之后的绘制中使用缓存,如果缓存不存在,那么就不使用缓存,并在下一次绘制结束后写入
|
||||
* @param index 缓存的索引,不填时表示不使用缓存
|
||||
*/
|
||||
useCache(index?: string): void;
|
||||
|
||||
/**
|
||||
* 将下次绘制写入缓存并使用
|
||||
* @param index 缓存的索引,不填时表示不进行缓存
|
||||
*/
|
||||
cache(index?: string): void;
|
||||
|
||||
/**
|
||||
* 清除指定或所有缓存
|
||||
* @param index 要清除的缓存,不填代表清除所有
|
||||
*/
|
||||
clearCache(index?: string): void;
|
||||
}
|
||||
|
||||
export interface IRenderUpdater {
|
||||
/**
|
||||
* 更新这个渲染元素
|
||||
* @param item 触发更新事件的元素,可以是自身触发。如果不填表示手动触发,而非渲染内容发生变化而引起的触发
|
||||
*/
|
||||
update(item?: RenderItem): void;
|
||||
}
|
||||
|
||||
export interface ICanvasCachedRenderItem {
|
||||
/** 离屏画布,首先渲染到它上面,然后由Renderer渲染到最终画布上 */
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
}
|
||||
|
||||
interface IRenderAnchor {
|
||||
anchorX: number;
|
||||
anchorY: number;
|
||||
|
||||
/**
|
||||
* 设置渲染元素的位置锚点
|
||||
* @param x 锚点的横坐标,小数,0表示最左边,1表示最右边
|
||||
* @param y 锚点的纵坐标,小数,0表示最上边,1表示最下边
|
||||
*/
|
||||
setAnchor(x: number, y: number): void;
|
||||
}
|
||||
|
||||
interface RenderItemEvent extends EmitableEvent {
|
||||
beforeUpdate: (item?: RenderItem) => void;
|
||||
afterUpdate: (item?: RenderItem) => void;
|
||||
}
|
||||
|
||||
export abstract class RenderItem
|
||||
extends EventEmitter<RenderItemEvent>
|
||||
implements IRenderCache, IRenderUpdater, IRenderAnchor
|
||||
{
|
||||
zIndex: number = 0;
|
||||
|
||||
x: number = 0;
|
||||
y: number = 0;
|
||||
width: number = 200;
|
||||
height: number = 200;
|
||||
|
||||
cacheList: Map<string, MotaOffscreenCanvas2D> = new Map();
|
||||
|
||||
using?: string;
|
||||
writing?: string;
|
||||
|
||||
anchorX: number = 0;
|
||||
anchorY: number = 0;
|
||||
|
||||
/** 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动,只对顶层元素有效 */
|
||||
type: 'absolute' | 'static' = 'static';
|
||||
|
||||
parent?: RenderItem;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.using = '@default';
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染这个对象
|
||||
* @param canvas 渲染至的画布
|
||||
* @param ctx 渲染至的画布的渲染上下文
|
||||
* @param camera 渲染时使用的摄像机
|
||||
*/
|
||||
abstract render(
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
camera: Camera
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 修改这个对象的大小
|
||||
*/
|
||||
abstract size(width: number, height: number): void;
|
||||
|
||||
/**
|
||||
* 修改这个对象的位置
|
||||
*/
|
||||
abstract pos(x: number, y: number): void;
|
||||
|
||||
useCache(index?: string): void {
|
||||
if (isNil(index)) {
|
||||
this.using = void 0;
|
||||
return;
|
||||
}
|
||||
if (!this.cacheList.has(index)) {
|
||||
this.writing = index;
|
||||
}
|
||||
this.using = index;
|
||||
}
|
||||
|
||||
cache(index?: string): void {
|
||||
this.writing = index;
|
||||
this.using = index;
|
||||
}
|
||||
|
||||
clearCache(index?: string): void {
|
||||
if (isNil(index)) {
|
||||
this.writing = void 0;
|
||||
this.using = void 0;
|
||||
this.cacheList.clear();
|
||||
} else {
|
||||
this.cacheList.delete(index);
|
||||
}
|
||||
}
|
||||
|
||||
setAnchor(x: number, y: number): void {
|
||||
this.anchorX = x;
|
||||
this.anchorY = y;
|
||||
}
|
||||
|
||||
update(item?: RenderItem): void {
|
||||
this.writing = this.using;
|
||||
this.using = void 0;
|
||||
this.parent?.update(item);
|
||||
}
|
||||
}
|
||||
|
||||
export function withCacheRender(
|
||||
item: RenderItem & ICanvasCachedRenderItem,
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
camera: Camera,
|
||||
fn: RenderFunction
|
||||
) {
|
||||
const { width, height } = item;
|
||||
const ax = width * item.anchorX;
|
||||
const ay = height * item.anchorY;
|
||||
let write = item.writing;
|
||||
if (!isNil(item.using)) {
|
||||
const cache = item.cacheList.get(item.using);
|
||||
if (cache) {
|
||||
ctx.drawImage(
|
||||
cache.canvas,
|
||||
item.x - ax,
|
||||
item.y - ay,
|
||||
item.width,
|
||||
item.height
|
||||
);
|
||||
return;
|
||||
}
|
||||
write = item.using;
|
||||
}
|
||||
const { canvas: c, ctx: ct } = item.canvas;
|
||||
ct.clearRect(0, 0, c.width, c.height);
|
||||
fn(item.canvas, camera);
|
||||
if (!isNil(write)) {
|
||||
const cache = item.cacheList.get(write);
|
||||
if (cache) {
|
||||
const { canvas, ctx } = cache;
|
||||
ctx.drawImage(c, 0, 0);
|
||||
} else {
|
||||
item.cacheList.set(write, MotaOffscreenCanvas2D.clone(item.canvas));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.drawImage(c, item.x - ax, item.y - ay, item.width, item.height);
|
||||
item.using = write;
|
||||
}
|
123
src/core/render/preset/misc.ts
Normal file
123
src/core/render/preset/misc.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { Sprite } from '../sprite';
|
||||
|
||||
type CanvasStyle = string | CanvasGradient | CanvasPattern;
|
||||
|
||||
export class Text extends Sprite {
|
||||
text: string;
|
||||
|
||||
fillStyle?: CanvasStyle = '#fff';
|
||||
strokeStyle?: CanvasStyle;
|
||||
font?: string = '';
|
||||
|
||||
private length: number = 0;
|
||||
private descent: number = 0;
|
||||
|
||||
constructor(text: string = '') {
|
||||
super();
|
||||
|
||||
this.text = text;
|
||||
if (text.length > 0) this.calBox();
|
||||
|
||||
this.renderFn = ({ canvas, ctx }) => {
|
||||
ctx.save();
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
||||
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
||||
ctx.font = this.font ?? '';
|
||||
|
||||
if (this.fillStyle) {
|
||||
ctx.fillText(this.text, 0, this.descent);
|
||||
}
|
||||
if (this.strokeStyle) {
|
||||
ctx.strokeText(this.text, 0, this.descent);
|
||||
}
|
||||
ctx.restore();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文字的长度
|
||||
*/
|
||||
measure() {
|
||||
this.canvas.ctx.save();
|
||||
this.canvas.ctx.textBaseline = 'bottom';
|
||||
this.canvas.ctx.font = this.font ?? '';
|
||||
const res = this.canvas.ctx.measureText(this.text);
|
||||
this.canvas.ctx.restore();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置显示文字
|
||||
* @param text 显示的文字
|
||||
*/
|
||||
setText(text: string) {
|
||||
this.text = text;
|
||||
this.writing = this.using;
|
||||
this.using = void 0;
|
||||
this.calBox();
|
||||
if (this.parent) this.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置使用的字体
|
||||
* @param font 字体
|
||||
*/
|
||||
setFont(font: string) {
|
||||
this.font = font;
|
||||
this.calBox();
|
||||
if (this.parent) this.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字体样式
|
||||
* @param fill 填充样式
|
||||
* @param stroke 描边样式
|
||||
*/
|
||||
setStyle(fill?: CanvasStyle, stroke?: CanvasStyle) {
|
||||
this.fillStyle = fill;
|
||||
this.strokeStyle = stroke;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算字体所占空间,从而确定这个元素的大小
|
||||
*/
|
||||
calBox() {
|
||||
const { width, fontBoundingBoxAscent } = this.measure();
|
||||
this.length = width;
|
||||
this.descent = fontBoundingBoxAscent;
|
||||
this.size(width, fontBoundingBoxAscent);
|
||||
}
|
||||
}
|
||||
|
||||
type SizedCanvasImageSource = Exclude<
|
||||
CanvasImageSource,
|
||||
VideoFrame | SVGElement
|
||||
>;
|
||||
|
||||
export class Image extends Sprite {
|
||||
image: SizedCanvasImageSource;
|
||||
|
||||
constructor(image: SizedCanvasImageSource) {
|
||||
super();
|
||||
this.image = image;
|
||||
this.canvas.withGameScale(false);
|
||||
this.size(image.width, image.height);
|
||||
|
||||
this.renderFn = ({ canvas, ctx }) => {
|
||||
ctx.drawImage(this.image, 0, 0);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片资源
|
||||
* @param image 图片资源
|
||||
*/
|
||||
setImage(image: SizedCanvasImageSource) {
|
||||
this.image = image;
|
||||
this.size(image.width, image.height);
|
||||
this.writing = this.using;
|
||||
this.using = void 0;
|
||||
this.update(this);
|
||||
}
|
||||
}
|
142
src/core/render/render.ts
Normal file
142
src/core/render/render.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import { isNil } from 'lodash-es';
|
||||
import { MotaCanvas2D, MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Camera } from './camera';
|
||||
import { Container } from './container';
|
||||
import { RenderItem, withCacheRender } from './item';
|
||||
import { Image, Text } from './preset/misc';
|
||||
|
||||
export class MotaRenderer extends Container {
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
camera: Camera;
|
||||
|
||||
/** 摄像机缓存,如果是需要快速切换摄像机的场景,使用缓存可以大幅提升性能表现 */
|
||||
cameraCache: Map<Camera, MotaOffscreenCanvas2D> = new Map();
|
||||
target: MotaCanvas2D;
|
||||
|
||||
private needUpdate: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.canvas = new MotaOffscreenCanvas2D();
|
||||
this.camera = new Camera();
|
||||
this.target = new MotaCanvas2D(`render-main`);
|
||||
this.width = 480;
|
||||
this.height = 480;
|
||||
this.target.withGameScale(true);
|
||||
this.target.size(480, 480);
|
||||
this.canvas.withGameScale(true);
|
||||
this.canvas.size(480, 480);
|
||||
this.target.css(`z-index: 100`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用某个摄像机
|
||||
* @param camera 要使用的摄像机
|
||||
* @param noCache 是否不使用缓存,当切换至的目标摄像机相比切走时发生了例如位置的变化时,一般需要设置为true,
|
||||
* 否则会使用上一次被切换走时的缓存
|
||||
*/
|
||||
useCamera(camera: Camera, noCache: boolean = false) {
|
||||
const cache = MotaOffscreenCanvas2D.clone(this.canvas);
|
||||
this.cameraCache.set(this.camera, cache);
|
||||
this.camera = camera;
|
||||
const nowCache = this.cameraCache.get(camera);
|
||||
if (!nowCache || !noCache) this.render();
|
||||
else this.renderCache(nowCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除某个摄像机的画面缓存
|
||||
* @param camera 要删除的缓存对应的摄像机
|
||||
*/
|
||||
freeCameraCache(camera: Camera) {
|
||||
this.cameraCache.delete(camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染游戏画面
|
||||
*/
|
||||
render() {
|
||||
const { canvas, ctx } = this.target;
|
||||
const camera = this.camera;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
withCacheRender(this, canvas, ctx, camera, canvas => {
|
||||
const { canvas: ca, ctx: ct, scale } = canvas;
|
||||
const mat = camera.mat;
|
||||
const a = mat[0] * scale;
|
||||
const b = mat[1];
|
||||
const c = mat[3];
|
||||
const d = mat[4] * scale;
|
||||
const e = mat[6];
|
||||
const f = mat[7];
|
||||
this.sortedChildren.forEach(v => {
|
||||
if (v.type === 'absolute') {
|
||||
ct.setTransform(scale, 0, 0, scale, 0, 0);
|
||||
} else {
|
||||
ct.setTransform(a, b, c, d, e, f);
|
||||
}
|
||||
v.render(ca, ct, camera);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新渲染,在下一个tick更新
|
||||
*/
|
||||
update(item?: RenderItem) {
|
||||
if (this.needUpdate) return;
|
||||
this.needUpdate = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.writing = this.using;
|
||||
this.using = void 0;
|
||||
this.needUpdate = false;
|
||||
this.emit('beforeUpdate', item);
|
||||
this.render();
|
||||
this.emit('afterUpdate', item);
|
||||
this.using = this.writing;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存内容渲染至画面
|
||||
* @param cache 渲染缓存,是一个离屏Canvas2D对象
|
||||
*/
|
||||
renderCache(cache: MotaOffscreenCanvas2D) {
|
||||
const { canvas, ctx } = this.canvas;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.width);
|
||||
ctx.drawImage(cache.canvas, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加至游戏画面
|
||||
*/
|
||||
mount() {
|
||||
this.target.mount();
|
||||
}
|
||||
}
|
||||
|
||||
Mota.require('var', 'hook').once('reset', () => {
|
||||
const render = new MotaRenderer();
|
||||
const con = new Container('static');
|
||||
const camera = render.camera;
|
||||
render.mount();
|
||||
|
||||
const testText = new Text();
|
||||
testText.setText('测试测试');
|
||||
testText.pos(100, 100);
|
||||
testText.setFont('32px normal');
|
||||
testText.setStyle('#fff');
|
||||
con.size(480, 480);
|
||||
const testImage = new Image(core.material.images.images['arrow.png']);
|
||||
testImage.pos(200, 200);
|
||||
|
||||
con.appendChild([testText, testImage]);
|
||||
|
||||
render.appendChild([con]);
|
||||
|
||||
render.update(render);
|
||||
|
||||
setTimeout(() => {
|
||||
testText.setFont('18px normal');
|
||||
}, 2000);
|
||||
});
|
41
src/core/render/sprite.ts
Normal file
41
src/core/render/sprite.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Camera } from './camera';
|
||||
import { RenderFunction, RenderItem, withCacheRender } from './item';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
|
||||
export class Sprite extends RenderItem {
|
||||
renderFn: RenderFunction;
|
||||
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.renderFn = () => {};
|
||||
this.canvas = new MotaOffscreenCanvas2D();
|
||||
this.canvas.withGameScale(true);
|
||||
}
|
||||
|
||||
render(
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
camera: Camera
|
||||
): void {
|
||||
this.emit('beforeUpdate', this);
|
||||
withCacheRender(this, canvas, ctx, camera, canvas => {
|
||||
this.renderFn(canvas, camera);
|
||||
});
|
||||
this.emit('afterUpdate', this);
|
||||
}
|
||||
|
||||
size(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.canvas.size(width, height);
|
||||
this.writing = this.using;
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
pos(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { EmitableEvent, EventEmitter } from '../core/common/eventEmitter';
|
||||
import { DamageEnemy } from './enemy/damage';
|
||||
import type { DamageEnemy } from './enemy/damage';
|
||||
|
||||
// ----- 加载事件
|
||||
interface GameLoadEvent extends EmitableEvent {
|
||||
@ -75,25 +75,36 @@ class GameLoading extends EventEmitter<GameLoadEvent> {
|
||||
export const loading = new GameLoading();
|
||||
|
||||
export interface GameEvent extends EmitableEvent {
|
||||
/** Emitted in events.prototype.resetGame. */
|
||||
/** Emitted in libs/events.js resetGame. */
|
||||
reset: () => void;
|
||||
/** Emitted in src/App.vue setup. */
|
||||
mounted: () => void;
|
||||
/** Emitted in plugin/ui.js */
|
||||
/** Emitted in plugin/game/ui.ts updateStatusBar_update */
|
||||
statusBarUpdate: () => void;
|
||||
/** Emitted in core/index.ts */
|
||||
renderLoaded: () => void;
|
||||
// /** Emitted in libs/events.js */
|
||||
/** Emitted in libs/events.js getItem */
|
||||
afterGetItem: (
|
||||
itemId: AllIdsOf<'items'>,
|
||||
x: number,
|
||||
y: number,
|
||||
isGentleClick: boolean
|
||||
) => void;
|
||||
/** Emitted in libs/events.js _openDoor_animate */
|
||||
afterOpenDoor: (doorId: AllIdsOf<'animates'>, x: number, y: number) => void;
|
||||
/** Emitted in project/functions.js afterChangeFloor */
|
||||
afterChangeFloor: (floorId: FloorIds) => void;
|
||||
/** Emitted in project/functions.js moveOneStep */
|
||||
moveOneStep: (x: number, y: number, floorId: FloorIds) => void;
|
||||
/** Emitted in src/game/enemy/battle.ts afterBattle */
|
||||
afterBattle: (enemy: DamageEnemy, x?: number, y?: number) => void;
|
||||
/** Emitted in libs/events.js changingFloor */
|
||||
changingFloor: (floorId: FloorIds, heroLoc: Loc) => void;
|
||||
drawHero: (
|
||||
status?: Exclude<keyof MaterialIcon['hero']['down'], 'loc'>,
|
||||
offset?: number,
|
||||
frame?: number
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const hook = new EventEmitter<GameEvent>();
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { has } from '@/plugin/game/utils';
|
||||
import { loading } from '../game';
|
||||
|
||||
export namespace BluePalace {
|
||||
type DoorConvertInfo = [id: AllIds, x: number, y: number];
|
||||
|
||||
// ---------- 黄蓝门转换
|
||||
|
||||
function drawDoors(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
convert: DoorConvertInfo[],
|
||||
@ -75,4 +78,30 @@ export namespace BluePalace {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- 传送门部分
|
||||
|
||||
interface Portal {
|
||||
fx: number;
|
||||
fy: number;
|
||||
dir: Dir;
|
||||
tx: number;
|
||||
ty: number;
|
||||
toDir: Dir;
|
||||
}
|
||||
|
||||
export const portals: Partial<Record<FloorIds, Portal[]>> = {
|
||||
MT75: [
|
||||
{ fx: 7, fy: 7, dir: 'left', tx: 9, ty: 9, toDir: 'down' },
|
||||
{ fx: 5, fy: 11, dir: 'right', tx: 7, ty: 9, toDir: 'up' },
|
||||
{ fx: 4, fy: 6, dir: 'right', tx: 9, ty: 4, toDir: 'up' },
|
||||
{ fx: 5, fy: 9, dir: 'right', tx: 3, ty: 7, toDir: 'up' },
|
||||
{ fx: 7, fy: 5, dir: 'right', tx: 4, ty: 9, toDir: 'up' }
|
||||
]
|
||||
};
|
||||
loading.once('coreInit', initPortals);
|
||||
|
||||
function initPortals() {
|
||||
// 主要是复写勇士绘制以及传送判定,还有自动寻路
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,16 @@ import type * as damage from './enemy/damage';
|
||||
import type { Logger } from '@/core/common/logger';
|
||||
import type { Danmaku } from '@/core/main/custom/danmaku';
|
||||
import type * as misc from './mechanism/misc';
|
||||
import type { MotaCanvas2D } from '@/core/fx/canvas2d';
|
||||
import type * as portal from '@/core/fx/portal';
|
||||
import type { HeroRenderer } from '@/core/render/hero';
|
||||
|
||||
interface ClassInterface {
|
||||
// 渲染进程与游戏进程通用
|
||||
EventEmitter: typeof EventEmitter;
|
||||
IndexedEventEmitter: typeof IndexedEventEmitter;
|
||||
Disposable: typeof Disposable;
|
||||
// 定义于渲染进程,录像中会进行polyfill,但是不执行任何内容
|
||||
// 定义于渲染进程
|
||||
GameStorage: typeof GameStorage;
|
||||
MotaSetting: typeof MotaSetting;
|
||||
SettingDisplayer: typeof SettingDisplayer;
|
||||
@ -45,12 +48,13 @@ interface ClassInterface {
|
||||
SoundEffect: typeof SoundEffect;
|
||||
SoundController: typeof SoundController;
|
||||
BgmController: typeof BgmController;
|
||||
MotaCanvas2D: typeof MotaCanvas2D;
|
||||
Danmaku: typeof Danmaku;
|
||||
// todo: 放到插件 ShaderEffect: typeof ShaderEffect;
|
||||
// 定义于游戏进程,渲染进程依然可用
|
||||
Range: typeof Range;
|
||||
EnemyCollection: typeof EnemyCollection;
|
||||
DamageEnemy: typeof DamageEnemy;
|
||||
Danmaku: typeof Danmaku;
|
||||
}
|
||||
|
||||
type _IBattle = typeof battle;
|
||||
@ -90,6 +94,12 @@ interface ModuleInterface {
|
||||
Mechanism: {
|
||||
BluePalace: typeof misc.BluePalace;
|
||||
};
|
||||
Effect: {
|
||||
Portal: typeof portal;
|
||||
};
|
||||
Render: {
|
||||
heroRender: HeroRenderer;
|
||||
};
|
||||
}
|
||||
|
||||
interface SystemInterfaceMap {
|
||||
|
@ -6,7 +6,7 @@ let pop: any[] = [];
|
||||
let time = 0;
|
||||
|
||||
export function init() {
|
||||
core.registerAnimationFrame('pop', true, popValue);
|
||||
// core.registerAnimationFrame('pop', true, popValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { sleep } from 'mutate-animate';
|
||||
import { tip } from './utils';
|
||||
|
||||
export default function init() {
|
||||
@ -29,6 +30,12 @@ window.addEventListener('resize', () => {
|
||||
});
|
||||
checkMobile();
|
||||
|
||||
sleep(2000).then(() => {
|
||||
if (!isMobile) {
|
||||
tip('info', `注意,不推荐使用浏览器的缩放功能,使用游戏内的缩放即可`);
|
||||
}
|
||||
});
|
||||
|
||||
function checkMobile() {
|
||||
if (isMobile && !alerted) {
|
||||
tip(
|
||||
|
@ -242,9 +242,6 @@ export function tip(
|
||||
class: 'antdv-message'
|
||||
});
|
||||
}
|
||||
sleep(2000).then(() => {
|
||||
tip('info', `注意,不推荐使用浏览器的缩放功能,使用游戏内的缩放即可`);
|
||||
});
|
||||
|
||||
/**
|
||||
* 设置文字分段换行等
|
||||
|
10
src/types/control.d.ts
vendored
10
src/types/control.d.ts
vendored
@ -368,7 +368,7 @@ interface Control {
|
||||
*/
|
||||
drawHero(
|
||||
status?: Exclude<keyof MaterialIcon['hero']['down'], 'loc'>,
|
||||
offset?: number,
|
||||
offset?: number | (Loc & { offset: number }),
|
||||
frame?: number
|
||||
): void;
|
||||
|
||||
@ -1071,6 +1071,14 @@ interface Control {
|
||||
|
||||
_drawHero_updateViewport(x: number, y: number, offset: Loc): void;
|
||||
_moveAction_moving(callback: () => void): void;
|
||||
_drawHero_draw(
|
||||
direction: Dir,
|
||||
x: number,
|
||||
y: number,
|
||||
status: Exclude<keyof MaterialIcon['hero']['down'], 'loc'>,
|
||||
offset: Loc & { offset: number },
|
||||
frame?: number
|
||||
): void;
|
||||
}
|
||||
|
||||
declare const control: new () => Control;
|
||||
|
2
src/types/core.d.ts
vendored
2
src/types/core.d.ts
vendored
@ -240,6 +240,7 @@ interface AnimateFrame {
|
||||
readonly animateTime: number;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* 勇士移动的时候上一次的换腿时间
|
||||
*/
|
||||
moveTime: number;
|
||||
@ -251,6 +252,7 @@ interface AnimateFrame {
|
||||
lastLegTime: number;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* 当前是否在左腿上,使用了四帧插件时无效
|
||||
*/
|
||||
leftLeg: boolean;
|
||||
|
6
src/types/status.d.ts
vendored
6
src/types/status.d.ts
vendored
@ -628,6 +628,7 @@ interface InitGameStatus {
|
||||
lockControl: boolean;
|
||||
|
||||
/**
|
||||
* @deprecated 迟早给你删喽\
|
||||
* 勇士移动状态,每个数字干啥的自己去libs翻,这东西太复杂了,不过应该不会有人用这个东西吧(
|
||||
*/
|
||||
heroMoving: number;
|
||||
@ -792,6 +793,11 @@ interface Follower {
|
||||
* 跟随者的图片id
|
||||
*/
|
||||
name: ImageIds;
|
||||
|
||||
direction: Dir;
|
||||
x: number;
|
||||
y: number;
|
||||
stop: boolean;
|
||||
}
|
||||
|
||||
interface HeroStatistics {
|
||||
|
@ -290,10 +290,10 @@ gameKey
|
||||
}
|
||||
})
|
||||
.realize('@shop_add', () => {
|
||||
count.value--;
|
||||
count.value++;
|
||||
})
|
||||
.realize('@shop_min', () => {
|
||||
count.value++;
|
||||
count.value--;
|
||||
})
|
||||
.realize('exit', () => {
|
||||
exit();
|
||||
@ -311,10 +311,12 @@ function exit() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
core.lockControl();
|
||||
core.status.route.push(`openShop:${id}`);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
core.unlockControl();
|
||||
gameKey.dispose(props.ui.symbol);
|
||||
});
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user