Compare commits

...

3 Commits

Author SHA1 Message Date
95d24b114d 合并2_10_3分支
添加战斗停顿
修复手机打开黑屏
修复0层仙子剧情重复
2023-03-15 17:36:42 +08:00
15ef13c1f4 修复了显示错误bug,全塔属性中塔版本改为2.10.3 2023-03-15 17:24:38 +08:00
dbf932519e 尝试搬运样板到2.10.3 2023-03-14 23:18:08 +08:00
24 changed files with 1431 additions and 508 deletions

View File

@ -1,5 +0,0 @@
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2
[InternetShortcut]
IDList=
URL=https://www.bilibili.com/video/BV1SB4y1p7bg?spm_id_from=333.999.0.0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
_server/css/FiraCode.ttf Normal file

Binary file not shown.

View File

@ -699,3 +699,12 @@ div.checkboxSet {
.etableInputDiv textarea:hover {
margin: -5px;
}
@font-face {
font-family: code;
src: url(./FiraCode.ttf);
}
.main .CodeMirror {
font-family: code, 微软雅黑, 黑体, 新宋体, Verdana;
}

View File

@ -710,3 +710,11 @@ div.checkboxSet {
margin: -5px;
}
@font-face {
font-family: Fira;
src: url(./FiraCode.ttf);
}
.main .CodeMirror {
font-family: Fira, Menlo, Consolas, 'Courier New', Courier, monospace;
}

View File

@ -7,11 +7,11 @@ editor_multi = function () {
var extraKeys = {
"Ctrl-/": function (cm) { cm.toggleComment(); },
"Ctrl-B": function (cm) { ternServer.jumpToDef(cm); },
"Ctrl-Q": function(cm) { ternServer.rename(cm); },
"Ctrl-Q": function (cm) { ternServer.rename(cm); },
"Cmd-F": CodeMirror.commands.findPersistent,
"Ctrl-F": CodeMirror.commands.findPersistent,
"Ctrl-R": CodeMirror.commands.replaceAll,
"Ctrl-D": function(cm){ cm.foldCode(cm.getCursor()); },
"Ctrl-D": function (cm) { cm.foldCode(cm.getCursor()); },
"Ctrl-O": function () { editor_multi.openUrl('/_docs/#/api'); },
"Ctrl-P": function () { editor_multi.openUrl('https://h5mota.com/plugins/'); }
};
@ -48,14 +48,14 @@ editor_multi = function () {
'Ctrl-P': '打开在线插件列表Ctrl+P'
};
document.getElementById('codemirrorCommands').innerHTML =
"<option value='' selected>执行操作...</option>" +
document.getElementById('codemirrorCommands').innerHTML =
"<option value='' selected>执行操作...</option>" +
Object.keys(commandsName).map(function (name) {
return "<option value='" + name + "'>" + commandsName[name] + "</option>"
}).join('');
var coredef = terndefs_f6783a0a_522d_417e_8407_94c67b692e50[2];
Object.keys(core.material.enemys).forEach(function (name){
Object.keys(core.material.enemys).forEach(function (name) {
coredef.core.material.enemys[name] = {
"!type": "enemy",
"!doc": core.material.enemys[name].name || "怪物"
@ -236,7 +236,15 @@ editor_multi = function () {
document.getElementById('left7').style = 'z-index:-1;opacity: 0;';
}
editor_multi.setLint = function () {
codeEditor.setOption("lint", editor_multi.lintAutocomplete);
if (editor_multi.lintAutocomplete) {
codeEditor.setOption("lint", {
options: {
esversion: 2021
}
});
} else {
codeEditor.setOption("lint", false);
}
codeEditor.setOption("autocomplete", editor_multi.lintAutocomplete);
document.getElementById("lintCheckbox").checked = editor_multi.lintAutocomplete;
}
@ -253,7 +261,7 @@ editor_multi = function () {
var _format = function () {
if (!editor_multi.lintAutocomplete) return;
var offset = (codeEditor.getScrollInfo() || {}).top || 0;
_setValue(js_beautify(codeEditor.getValue(), {
_setValue(beautifier.js(codeEditor.getValue(), {
brace_style: "collapse-preserve-inline",
indent_with_tabs: true,
jslint_happy: true
@ -486,6 +494,22 @@ editor_multi = function () {
editor_multi.importFile(dict[mod])
}
// 字体大小
{
const CONFIG_KEY = "editor_multi.fontSize";
let fontsize = editor.config.get(CONFIG_KEY, 14);
const input = document.getElementById("editor_multi_fontsize");
const check = document.getElementById("editor_multi_fontweight")
input.value = fontsize;
editor_multi.setFontSize = function () {
const value = Number(input.value);
editor.config.set(CONFIG_KEY, value);
const ele = codeEditor.getWrapperElement()
ele.style.fontSize = `${value}px`;
ele.style.fontWeight = `${check.checked ? 'bold' : 'normal'}`
}
}
return editor_multi;
}
//editor_multi=editor_multi();

View File

@ -555,14 +555,20 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"autoScale": {
"_leaf": true,
"_type": "checkbox",
"_docs": "自动缩放最大化",
"_data": "是否自动缩放最大化,关闭后不再最大化"
"_docs": "自动缩放",
"_data": "是否自动缩放至合适值"
},
"extendToolbar": {
"_leaf": true,
"_type": "checkbox",
"_docs": "横屏隐藏状态栏",
"_data": "在横屏状态下是否隐藏左侧状态栏从而获得仿RM的沉浸式体验"
"_docs": "画面下方道具栏",
"_data": "开启后道具栏会被挪动至游戏画面的下方"
},
"hideLeftStatusBar": {
"_leaf": true,
"_type": "checkbox",
"_docs": "隐藏左侧状态栏",
"_data": "是否隐藏左侧状态栏,开启后强制开启画面下方道具栏"
},
"flyNearStair": {
"_leaf": true,

View File

@ -242,15 +242,21 @@
<button onclick="confirmColor()">确定</button>
</div>
<div id="left7" style="z-index:-1;opacity: 0;"><!-- 多行文本编辑器 -->
<button onclick="editor_multi.confirm()">确认</button>
<button onclick="editor_multi.cancel()">取消</button>
<button onclick="editor_multi.confirm(true)">应用</button>
<button onclick="editor_multi.format()">格式化</button>
<button id="editor_multi_preview" style="display: none;">预览</button>
<input type="checkbox" onclick="editor_multi.toggerLint()" id="lintCheckbox"
style="vertical-align: middle;margin-left:6px"/>
<span style="vertical-align: middle; margin-left: -3px">语法检查</span>
<select id="codemirrorCommands" onchange="editor_multi.doCommand(this)" style="vertical-align: middle; margin-left: 6px;"></select>
<div>
<button onclick="editor_multi.confirm()">确认</button>
<button onclick="editor_multi.cancel()">取消</button>
<button onclick="editor_multi.confirm(true)">应用</button>
<button onclick="editor_multi.format()">格式化</button>
<button id="editor_multi_preview" style="display: none;">预览</button>
<input type="checkbox" onclick="editor_multi.toggerLint()" id="lintCheckbox"
style="vertical-align: middle;margin-left:6px"/>
<span style="vertical-align: middle; margin-left: -3px">语法检查</span>
<select id="codemirrorCommands" onchange="editor_multi.doCommand(this)" style="vertical-align: middle; margin-left: 6px;"></select>
<span>字体大小</span>
<input style="width: 40px" type="number" onchange="editor_multi.setFontSize()" id="editor_multi_fontsize" />
<span>字体加粗</span>
<input style="width: 40px" type="checkbox" onchange="editor_multi.setFontSize()" id="editor_multi_fontweight" />
</div>
<textarea id="multiLineCode" name="multiLineCode"></textarea>
</div>
<div id="left8" class='leftTab' style="z-index:-1;opacity: 0;"><!-- functions -->

View File

@ -238,15 +238,21 @@
<button onclick="confirmColor()">确定</button>
</div>
<div id="left7" style="z-index:-1;opacity: 0;"><!-- 多行文本编辑器 -->
<button onclick="editor_multi.confirm()">确认</button>
<button onclick="editor_multi.cancel()">取消</button>
<button onclick="editor_multi.confirm(true)">应用</button>
<button onclick="editor_multi.format()">格式化</button>
<button id="editor_multi_preview" style="display: none;">预览</button>
<input type="checkbox" onclick="editor_multi.toggerLint()" id="lintCheckbox"
style="vertical-align: middle;margin-left:6px"/>
<span style="vertical-align: middle; margin-left: -3px">语法检查</span>
<select id="codemirrorCommands" onchange="editor_multi.doCommand(this)" style="vertical-align: middle; margin-left: 6px;"></select>
<div>
<button onclick="editor_multi.confirm()">确认</button>
<button onclick="editor_multi.cancel()">取消</button>
<button onclick="editor_multi.confirm(true)">应用</button>
<button onclick="editor_multi.format()">格式化</button>
<button id="editor_multi_preview" style="display: none;">预览</button>
<input type="checkbox" onclick="editor_multi.toggerLint()" id="lintCheckbox"
style="vertical-align: middle;margin-left:6px"/>
<span style="vertical-align: middle; margin-left: -3px">语法检查</span>
<select id="codemirrorCommands" onchange="editor_multi.doCommand(this)" style="vertical-align: middle; margin-left: 6px;"></select>
<span>字体大小</span>
<input style="width: 40px" type="number" onchange="editor_multi.setFontSize()" id="editor_multi_fontsize" />
<span>字体加粗</span>
<input style="width: 40px" type="checkbox" onchange="editor_multi.setFontSize()" id="editor_multi_fontweight" />
</div>
<textarea id="multiLineCode" name="multiLineCode"></textarea>
</div>
<div id="left8" class='leftTab' style="z-index:-1;opacity: 0;"><!-- functions -->

View File

@ -1,12 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='content-type' content='text/html' charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=Edge, chrome=1'>
<meta name='author' content='ckcz123'>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=yes'>
<meta name='viewport'
content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=yes'>
<title>HTML5魔塔</title>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="screen-orientation" content="portrait">
<meta name="full-screen" content="yes">
<meta name="browsermode" content="application">
@ -20,130 +22,130 @@
<div id='startImageDiv'></div>
<img id='startImageLogo' />
</div>
<script>
(function () {
var startImageBackgroundDiv = document.getElementById('startImageBackgroundDiv');
var startImageLogo = document.getElementById('startImageLogo');
var startImageDiv = document.getElementById('startImageDiv');
startImageLogo.onload = function () {
startImageBackgroundDiv.style.display = 'block';
var onAnimationEnd = function () {
startImageBackgroundDiv.style.display = 'none';
startImageLogo.classList.remove("startImageAnimation");
startImageDiv.classList.remove("startImageDivAnimation");
<script>
(function () {
var startImageBackgroundDiv = document.getElementById('startImageBackgroundDiv');
var startImageLogo = document.getElementById('startImageLogo');
var startImageDiv = document.getElementById('startImageDiv');
startImageLogo.onload = function () {
startImageBackgroundDiv.style.display = 'block';
var onAnimationEnd = function () {
startImageBackgroundDiv.style.display = 'none';
startImageLogo.classList.remove("startImageAnimation");
startImageDiv.classList.remove("startImageDivAnimation");
}
startImageDiv.addEventListener("webkitAnimationEnd", onAnimationEnd);
startImageDiv.addEventListener("animationend", onAnimationEnd);
startImageLogo.classList.add("startImageAnimation");
startImageDiv.classList.add("startImageDivAnimation");
// 注释下面这句话以禁止单击立刻跳过开场动画
startImageBackgroundDiv.onclick = onAnimationEnd;
}
startImageDiv.addEventListener("webkitAnimationEnd", onAnimationEnd);
startImageDiv.addEventListener("animationend", onAnimationEnd);
startImageLogo.classList.add("startImageAnimation");
startImageDiv.classList.add("startImageDivAnimation");
// 注释下面这句话以禁止单击立刻跳过开场动画
startImageBackgroundDiv.onclick = onAnimationEnd;
}
startImageLogo.onerror = function () {}
startImageLogo.src = "logo.png";
})();
</script>
<!-- injection -->
<div id='gameGroup'>
<canvas id="whole" style="position: absolute; left: 0; top: 0; z-index: 68;"></canvas>
<p id='mainTips'>请稍候...</p>
<img id='musicBtn'>
<div id='startPanel'>
<div id='startTop'>
<div id='startTopProgressBar'>
<div id='startTopProgress'></div>
startImageLogo.onerror = function () { }
startImageLogo.src = "logo.png";
})();
</script>
<!-- injection -->
<div id='gameGroup'>
<canvas id="whole" style="position: absolute; left: 0; top: 0; z-index: 68;"></canvas>
<p id='mainTips'>请稍候...</p>
<img id='musicBtn'>
<div id='startPanel'>
<div id='startTop'>
<div id='startTopProgressBar'>
<div id='startTopProgress'></div>
</div>
<p id='startTopLoadTips'>资源即将开始加载</p>
<p id='startTopHint'>HTML5魔塔游戏平台享受更多魔塔游戏<br />https://h5mota.com/</p>
</div>
<p id='startTopLoadTips'>资源即将开始加载</p>
<p id='startTopHint'>HTML5魔塔游戏平台享受更多魔塔游戏<br/>https://h5mota.com/</p>
</div>
<img id='startBackground'>
<p id='startLogo'></p>
<div id='startButtonGroup'>
<div id='startButtons'>
<span class='startButton' id='playGame'>开始游戏</span>
<span class='startButton' id='loadGame'>继续游戏</span>
<span class='startButton' id='replayGame'>录像回放</span>
<img id='startBackground'>
<p id='startLogo'></p>
<div id='startButtonGroup'>
<div id='startButtons'>
<span class='startButton' id='playGame'>开始游戏</span>
<span class='startButton' id='loadGame'>继续游戏</span>
<span class='startButton' id='replayGame'>录像回放</span>
</div>
<div id='levelChooseButtons'></div>
</div>
<div id='levelChooseButtons'></div>
</div>
</div>
<div id='floorMsgGroup'>
<p id='logoLabel'></p>
<p id='versionLabel'></p>
<p id='floorNameLabel'></p>
</div>
<div id='statusBar' class="clearfix">
<div class="status" id="floorCol">
<img id="img-floor">
<p class='statusLabel statusText' id='floor'></p>
</div>
<div class="status" id="nameCol">
<img id="img-name">
<p class='statusLabel statusText' id='name'></p>
</div>
<div class="status" id="lvCol">
<img id="img-lv">
<p class='statusLabel statusText' id='lv'></p>
</div>
<div class="status" id='hpmaxCol'>
<img id="img-hpmax">
<p class='statusLabel statusText' id='hpmax'></p>
</div>
<div class="status" id='hpCol'>
<img id="img-hp">
<p class='statusLabel statusText' id='hp'></p>
</div>
<div class="status" id='manaCol'>
<img id="img-mana">
<p class='statusLabel statusText' id='mana'></p>
</div>
<div class="status" id='atkCol'>
<img id="img-atk">
<p class='statusLabel statusText' id='atk'></p>
</div>
<div class="status" id='defCol'>
<img id="img-def">
<p class='statusLabel statusText' id='def'></p>
</div>
<div class="status" id="mdefCol">
<img id="img-mdef">
<p class='statusLabel statusText' id='mdef'></p>
</div>
<div class="status" id="moneyCol">
<img id="img-money">
<p class='statusLabel statusText' id='money'></p>
</div>
<div class="status" id="expCol">
<img id="img-exp">
<p class='statusLabel statusText' id='exp'></p>
</div>
<div class="status" id="upCol">
<img id="img-up">
<p class='statusLabel statusText' id='up'></p>
</div>
<div class="status" id="skillCol">
<img id="img-skill">
<p class='statusLabel statusText' id='skill' style='font-style: normal'></p>
</div>
<div class="status" id='keyCol'>
<span class='statusLabel' id='yellowKey' style="color:#FFCCAA"></span>
<span class='statusLabel' id='blueKey' style="color:#AAAADD"></span>
<span class='statusLabel' id='redKey' style="color:#FF8888"></span>
<span class='statusLabel' id='greenKey' style="color:#88FF88"></span>
</div>
<div class="status" id='pzfCol'>
<span class='statusLabel' id='pickaxe' style="color: #BC6E27"></span>
<span class='statusLabel' id='bomb' style="color: #FA14B9"></span>
<span class='statusLabel' id='fly' style="color: #8DB600"></span>
</div>
<div class="status" id="debuffCol">
<span class='statusLabel' id='poison' style="color: #AFFCA8;"></span>
<span class='statusLabel' id='weak' style="color: #FECCD0;"></span>
<span class='statusLabel' id='curse' style="color: #C2F4E7;"></span>
<div id='floorMsgGroup'>
<p id='logoLabel'></p>
<p id='versionLabel'></p>
<p id='floorNameLabel'></p>
</div>
<div id='statusBar' class="clearfix">
<div class="status" id="floorCol">
<img id="img-floor">
<p class='statusLabel statusText' id='floor'></p>
</div>
<div class="status" id="nameCol">
<img id="img-name">
<p class='statusLabel statusText' id='name'></p>
</div>
<div class="status" id="lvCol">
<img id="img-lv">
<p class='statusLabel statusText' id='lv'></p>
</div>
<div class="status" id='hpmaxCol'>
<img id="img-hpmax">
<p class='statusLabel statusText' id='hpmax'></p>
</div>
<div class="status" id='hpCol'>
<img id="img-hp">
<p class='statusLabel statusText' id='hp'></p>
</div>
<div class="status" id='manaCol'>
<img id="img-mana">
<p class='statusLabel statusText' id='mana'></p>
</div>
<div class="status" id='atkCol'>
<img id="img-atk">
<p class='statusLabel statusText' id='atk'></p>
</div>
<div class="status" id='defCol'>
<img id="img-def">
<p class='statusLabel statusText' id='def'></p>
</div>
<div class="status" id="mdefCol">
<img id="img-mdef">
<p class='statusLabel statusText' id='mdef'></p>
</div>
<div class="status" id="moneyCol">
<img id="img-money">
<p class='statusLabel statusText' id='money'></p>
</div>
<div class="status" id="expCol">
<img id="img-exp">
<p class='statusLabel statusText' id='exp'></p>
</div>
<div class="status" id="upCol">
<img id="img-up">
<p class='statusLabel statusText' id='up'></p>
</div>
<div class="status" id="skillCol">
<img id="img-skill">
<p class='statusLabel statusText' id='skill' style='font-style: normal'></p>
</div>
<div class="status" id='keyCol'>
<span class='statusLabel' id='yellowKey' style="color:#FFCCAA"></span>
<span class='statusLabel' id='blueKey' style="color:#AAAADD"></span>
<span class='statusLabel' id='redKey' style="color:#FF8888"></span>
<span class='statusLabel' id='greenKey' style="color:#88FF88"></span>
</div>
<div class="status" id='pzfCol'>
<span class='statusLabel' id='pickaxe' style="color: #BC6E27"></span>
<span class='statusLabel' id='bomb' style="color: #FA14B9"></span>
<span class='statusLabel' id='fly' style="color: #8DB600"></span>
</div>
<div class="status" id="debuffCol">
<span class='statusLabel' id='poison' style="color: #AFFCA8;"></span>
<span class='statusLabel' id='weak' style="color: #FECCD0;"></span>
<span class='statusLabel' id='curse' style="color: #C2F4E7;"></span>
</div>
<!-- 状态栏canvas化 -->
<canvas id="statusCanvas" style="position: absolute; left: 0; top: 0; z-index: 50;"></canvas>
<canvas id="statusCanvas" style="position: absolute; left: 0; top: 0;"></canvas>
<canvas id="caidan1" style="position: absolute; left: 0; top: 0;"></canvas>
</div>
<div id="toolBar" class="clearfix">
@ -168,11 +170,11 @@
<div id="gameDraw">
<div id="gif"></div>
<div id="gif2"></div>
<canvas class='gameCanvas' id='bg'></canvas>
<canvas class='gameCanvas' id='event'></canvas>
<canvas class='gameCanvas' id='hero'></canvas>
<canvas class='gameCanvas' id='event2'></canvas>
<canvas class='gameCanvas' id='fg'></canvas>
<canvas class='gameCanvas anti-aliasing' id='bg'></canvas>
<canvas class='gameCanvas anti-aliasing' id='event'></canvas>
<canvas class='gameCanvas anti-aliasing' id='hero'></canvas>
<canvas class='gameCanvas anti-aliasing' id='event2'></canvas>
<canvas class='gameCanvas anti-aliasing' id='fg'></canvas>
<canvas class='gameCanvas' id='caidan'></canvas>
<canvas class='gameCanvas' id='damage'></canvas>
<canvas class='gameCanvas' id='animate'></canvas>
@ -200,4 +202,5 @@
<script>main.init('play');main.listen();</script>
</body>
</html>

View File

@ -1225,7 +1225,7 @@ actions.prototype._clickBook = function (x, y) {
var data = core.status.event.data;
if (data != null && y < core._HEIGHT_-1) {
var per_page = pageinfo.per_page, page = parseInt(data / per_page);
var u = this.LAST / per_page;
var u = (core._HEIGHT_ - 1) / per_page;
for (var i = 0; i < per_page; ++i) {
if (y >= u * i && y < u * (i + 1)) {
var index = per_page * page + i;

View File

@ -18,6 +18,7 @@ control.prototype._init = function () {
this.weathers = {};
this.resizes = [];
this.noAutoEvents = true;
this.updateNextFrame = false;
// --- 注册系统的animationFrame
this.registerAnimationFrame("totalTime", false, this._animationFrame_totalTime);
this.registerAnimationFrame("autoSave", true, this._animationFrame_autoSave);
@ -3046,22 +3047,27 @@ control.prototype.clearStatusBar = function () {
////// 更新状态栏 //////
control.prototype.updateStatusBar = function (doNotCheckAutoEvents, immediate) {
if (!core.isPlaying()) return;
if (immediate) {
return this.updateStatusBar_update();
}
if (!doNotCheckAutoEvents) this.noAutoEvents = false;
if (core.isReplaying()) return this.updateStatusBar_update();
requestAnimationFrame(this.updateStatusBar_update)
}
if (!core.control.updateNextFrame) {
core.control.updateNextFrame = true;
requestAnimationFrame(this.updateStatusBar_update);
}
};
control.prototype.updateStatusBar_update = function () {
core.control.updateNextFrame = false;
if (!core.isPlaying() || core.hasFlag('__statistics__')) return;
core.control.controldata.updateStatusBar();
if (!core.control.noAutoEvents) core.checkAutoEvents();
core.control._updateStatusBar_setToolboxIcon();
core.clearRouteFolding();
core.control.noAutoEvents = true;
}
};
control.prototype._updateStatusBar_setToolboxIcon = function () {
if (core.isReplaying()) {
@ -3265,9 +3271,10 @@ control.prototype.resize = function () {
var clientWidth = main.dom.body.clientWidth, clientHeight = main.dom.body.clientHeight;
var BORDER = 3;
var extendToolbar = core.flags.extendToolbar;
var BAR_WIDTH = extendToolbar ? 0 : Math.round(core._PY_ * 0.3);
let hideLeftStatusBar = core.flags.hideLeftStatusBar;
var BAR_WIDTH = hideLeftStatusBar ? 0 : Math.round(core._PY_ * 0.31);
var horizontalMaxRatio = (clientHeight - 2 * BORDER - (extendToolbar ? BORDER : 0)) / (core._PY_ + (extendToolbar ? 38 : 0));
var horizontalMaxRatio = (clientHeight - 2 * BORDER - (hideLeftStatusBar ? BORDER : 0)) / (core._PY_ + (hideLeftStatusBar ? 38 : 0));
if (clientWidth - 3 * BORDER >= core._PX_ + BAR_WIDTH || (clientWidth > clientHeight && horizontalMaxRatio < 1)) {
// 横屏
@ -3289,6 +3296,7 @@ control.prototype.resize = function () {
core.domStyle.scale = Math.min((clientWidth - 2 * BORDER) / core._PX_);
core.domStyle.availableScale = [];
extendToolbar = false;
hideLeftStatusBar = false;
BAR_WIDTH = Math.round(core._PX_ * 0.3);
}
@ -3317,7 +3325,8 @@ control.prototype.resize = function () {
statusBarHeightInVertical: core.domStyle.isVertical ? (32 * col + 6) * core.domStyle.scale + 2 * BORDER : 0,
toolbarHeightInVertical: core.domStyle.isVertical ? 38 * core.domStyle.scale + 2 * BORDER : 0,
extendToolbar: extendToolbar,
is15x15: false
is15x15: false,
hideLeftStatusBar
};
this._doResize(obj);
@ -3339,7 +3348,7 @@ control.prototype._resize_gameGroup = function (obj) {
totalHeight = obj.outerHeight + obj.statusBarHeightInVertical + obj.toolbarHeightInVertical
}
else {
totalWidth = obj.outerWidth + obj.BAR_WIDTH * core.domStyle.scale + (obj.extendToolbar ? 0 : obj.BORDER);
totalWidth = obj.outerWidth + obj.BAR_WIDTH * core.domStyle.scale + (obj.hideLeftStatusBar ? 0 : obj.BORDER);
totalHeight = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0);
}
gameGroup.style.width = totalWidth + "px";
@ -3409,9 +3418,7 @@ control.prototype._resize_canvas = function (obj) {
} else {
for (var name in core.dymCanvas) {
var ctx = core.dymCanvas[name], canvas = ctx.canvas;
var ratio = canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1;
canvas.style.width = canvas.width / ratio * core.domStyle.scale + "px";
canvas.style.height = canvas.height / ratio * core.domStyle.scale + "px";
core.resizeCanvas(ctx, parseFloat(canvas.getAttribute("_width")), parseFloat(canvas.getAttribute("_height")))
canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
}
@ -3435,13 +3442,13 @@ control.prototype._resize_statusBar = function (obj) {
statusBar.style.height = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0) + "px";
statusBar.style.background = obj.globalAttribute.statusLeftBackground;
// --- 计算文字大小
if (obj.extendToolbar) {
if (obj.hideLeftStatusBar) {
statusBar.style.fontSize = 16 * core.domStyle.scale + "px";
} else {
statusBar.style.fontSize = 16 * Math.min(1, (core._HEIGHT_ - 4) / obj.count) * core.domStyle.scale + "px";
}
}
statusBar.style.display = obj.extendToolbar ? 'none' : 'block';
statusBar.style.display = obj.hideLeftStatusBar ? 'none' : '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;
@ -3456,7 +3463,7 @@ control.prototype._resize_statusBar = function (obj) {
core.dom.statusCanvas.style.height = obj.outerHeight - 2 * obj.BORDER + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0) + "px";
core.maps._setHDCanvasSize(core.dom.statusCanvasCtx, obj.BAR_WIDTH, core._PY_ + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT + obj.BORDER : 0));
}
core.dom.statusCanvas.style.display = core.flags.statusCanvas && !obj.extendToolbar ? "block" : "none";
core.dom.statusCanvas.style.display = core.flags.statusCanvas && !obj.hideLeftStatusBar ? "block" : "none";
}
control.prototype._resize_status = function (obj) {
@ -3464,7 +3471,7 @@ control.prototype._resize_status = function (obj) {
if (core.domStyle.isVertical) {
statusHeight = 32 * core.domStyle.scale * 0.8;
} else {
statusHeight = (obj.extendToolbar ? core._HEIGHT_ : core._HEIGHT_ - 4) / obj.count * 32 * core.domStyle.scale * 0.8;
statusHeight = (obj.hideLeftStatusBar ? core._HEIGHT_ : core._HEIGHT_ - 4) / obj.count * 32 * core.domStyle.scale * 0.8;
}
// status
for (var i = 0; i < core.dom.status.length; ++i) {
@ -3507,7 +3514,7 @@ control.prototype._resize_toolBar = function (obj) {
toolBar.style.background = obj.globalAttribute.toolsBackground;
}
else {
if (obj.extendToolbar) {
if (obj.extendToolbar || obj.hideLeftStatusBar) {
toolBar.style.left = "";
toolBar.style.right = 0;
toolBar.style.width = obj.outerWidth + "px";

View File

@ -34,11 +34,10 @@ maps.prototype._resetFloorImages = function () {
}
}
maps.prototype._setHDCanvasSize = function (ctx, width, height, isTempCanvas) {
maps.prototype._setHDCanvasSize = function (ctx, width, height) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
var ratio = core.domStyle.ratio;
if (ctx === core.bigmap.tempCanvas) ratio = core.domStyle.scale;
if (isTempCanvas) ratio = core.domStyle.ratio;
var ratio = core.domStyle.scale;
ratio *= devicePixelRatio;
if (width != null) ctx.canvas.width = width * ratio;
if (height != null) ctx.canvas.height = height * ratio;
ctx.scale(ratio, ratio);
@ -1728,18 +1727,20 @@ maps.prototype._drawThumbnail_drawTempCanvas = function (floorId, blocks, option
// 如果是大地图模式?
if (options.all) {
// 计算比例
var scale = Math.max(core._WIDTH_ / width, core._HEIGHT_ / height);
if (options.noHD) {
tempCanvas.canvas.width = width * 32 * scale;
tempCanvas.canvas.height = height * 32 * scale;
} else core.resizeCanvas(tempCanvas, width * 32 * scale, height * 32 * scale, false, true);
tempCanvas.scale(scale, scale);
tempCanvas.canvas.width = width * 32;
tempCanvas.canvas.height = height * 32;
tempCanvas.canvas.removeAttribute('isHD');
} else {
core.maps._setHDCanvasSize(tempCanvas, width * 32, height * 32);
}
} else if (width * height > core.bigmap.threshold) {
options.v2 = true;
if (options.noHD) {
tempCanvas.canvas.width = core._PX_;
tempCanvas.canvas.height = core._PY_;
} else core.resizeCanvas(tempCanvas, core._PX_, core._PY_);
tempCanvas.canvas.removeAttribute('isHD');
} else core.maps._setHDCanvasSize(tempCanvas, width * 32, height * 32);
var centerX = options.centerX, centerY = options.centerY;
if (centerX == null) centerX = Math.floor(width / 2);
if (centerY == null) centerY = Math.floor(height / 2);
@ -1751,7 +1752,8 @@ maps.prototype._drawThumbnail_drawTempCanvas = function (floorId, blocks, option
if (options.noHD) {
tempCanvas.canvas.width = width * 32;
tempCanvas.canvas.height = height * 32;
} else core.resizeCanvas(tempCanvas, width * 32, height * 32, false, true);
tempCanvas.canvas.removeAttribute('isHD');
} else core.maps._setHDCanvasSize(tempCanvas, width * 32, height * 32);
}
options.ctx = tempCanvas;
@ -1813,6 +1815,7 @@ maps.prototype._drawThumbnail_drawToTarget = function (floorId, options) {
if (centerY == null) centerY = Math.floor(height / 2);
var tempCanvas = core.bigmap.tempCanvas;
const scale = core.domStyle.scale * devicePixelRatio;
if (options.all) {
var tempWidth = tempCanvas.canvas.width, tempHeight = tempCanvas.canvas.height;
// 绘制全景图
@ -1833,20 +1836,28 @@ maps.prototype._drawThumbnail_drawToTarget = function (floorId, options) {
}
else {
// 只绘制可见窗口
var pw = core._PX_, ph = core._PY_, hw = core._HALF_WIDTH_, hh = core._HALF_HEIGHT_, W = core._WIDTH_, H = core._HEIGHT_;
var ratio = core.domStyle.isVertical ? core.domStyle.ratio : core.domStyle.scale;
if (main.mode == 'editor') { pw = ph = core.__PIXELS__; hw = hh = core.__HALF_SIZE__; W = H = core.__SIZE__; }
var pw = core._PX_,
ph = core._PY_,
hw = core._HALF_WIDTH_,
hh = core._HALF_HEIGHT_,
W = core._WIDTH_,
H = core._HEIGHT_;
if (main.mode == 'editor') {
pw = ph = core.__PIXELS__;
hw = hh = core.__HALF_SIZE__;
W = H = core.__SIZE__;
}
if (options.v2) {
core.drawImage(ctx, tempCanvas.canvas, 0, 0, pw * ratio, ph * ratio, x, y, w, h);
if (options.noHD) core.drawImage(ctx, tempCanvas.canvas, 0, 0, pw, ph, x, y, w, h);
else core.drawImage(ctx, tempCanvas.canvas, 0, 0, pw * scale, ph * scale, x, y, w, h);
} else {
var offsetX = core.clamp(centerX - hw, 0, width - W),
offsetY = core.clamp(centerY - hh, 0, height - H),
c = options.noHD ? 1 : core.domStyle.scale;
offsetY = core.clamp(centerY - hh, 0, height - H);
if (options.noHD) {
core.drawImage(ctx, tempCanvas.canvas, offsetX * 32, offsetY * 32, pw, ph, x, y, w, h);
return;
} else {
core.drawImage(ctx, tempCanvas.canvas, offsetX * 32 * scale, offsetY * 32 * scale, pw * scale, ph * scale, x, y, w, h);
}
core.drawImage(ctx, tempCanvas.canvas, offsetX * 32 * ratio, offsetY * 32 * ratio, pw * ratio, ph * ratio, x, y, w, h);
}
}
}

View File

@ -57,7 +57,13 @@ ui.prototype.clearMap = function (name, x, y, width, height) {
if (x != null && y != null && width != null && height != null) {
ctx.clearRect(x, y, width, height);
} else {
ctx.clearRect(-32, -32, ctx.canvas.width + 32, ctx.canvas.height + 32);
if (ctx.canvas.getAttribute('isHD')) {
const width = ctx.canvas.width / core.domStyle.scale / devicePixelRatio;
const height = ctx.canvas.height / core.domStyle.scale / devicePixelRatio;
ctx.clearRect(0, 0, width, height);
} else {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}
}
}
}
@ -1108,12 +1114,16 @@ ui.prototype.drawTextContent = function (ctx, content, config) {
config.offsetY = 0;
config.line = 0;
config.blocks = [];
config.isHD = ctx != null && ctx.canvas.hasAttribute('isHD');
config.isHD = ctx == null || ctx.canvas.hasAttribute('isHD');
// 创建一个新的临时画布
var tempCtx = document.createElement('canvas').getContext('2d');
if (config.isHD) {
core.maps._setHDCanvasSize(tempCtx, ctx.canvas.width, ctx.canvas.height);
if (config.isHD && ctx) {
core.maps._setHDCanvasSize(
tempCtx,
ctx.canvas.width,
ctx.canvas.height
);
} else {
tempCtx.canvas.width = ctx == null ? 1 : ctx.canvas.width;
tempCtx.canvas.height = ctx == null ? 1 : ctx.canvas.height;
@ -1148,10 +1158,20 @@ ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) {
if (config.index >= config.blocks.length) return false;
var block = config.blocks[config.index++];
if (block != null) {
var ratio = config.isHD ? core.domStyle.ratio : 1;
core.drawImage(ctx, tempCtx.canvas, block.left * ratio, block.top * ratio, block.width * ratio, block.height * ratio,
config.left + block.left + block.marginLeft, config.top + block.top + block.marginTop,
block.width, block.height);
// It works, why?
const scale = config.isHD ? devicePixelRatio * core.domStyle.scale : 1;
core.drawImage(
ctx,
tempCtx.canvas,
block.left * scale,
block.top * scale,
block.width * scale,
block.height * scale,
config.left + block.left + block.marginLeft,
config.top + block.top + block.marginTop,
block.width,
block.height
);
}
return true;
}
@ -2512,23 +2532,24 @@ ui.prototype.drawFly = function (page) {
core.setTextAlign('ui', 'center');
var middle = core._PY_ / 2 + 39;
const lastWidth = 0.25 * core._PX_ - 16;
// 换行
var lines = core.splitLines('ui', title, 120, this._buildFont(19, true));
var lines = core.splitLines('ui', title, lastWidth, this._buildFont(19, true));
var start_y = middle - (lines.length - 1) * 11;
for (var i in lines) {
core.fillText('ui', lines[i], core._PX_ - 60, start_y, '#FFFFFF');
core.fillText('ui', lines[i], core._PX_ - lastWidth * 0.5, start_y, '#FFFFFF');
start_y += 22;
}
if (core.actions._getNextFlyFloor(1) != page) {
core.fillText('ui', '▲', core._PX_ - 60, middle - 64, null, this._buildFont(17, false));
core.fillText('ui', '▲', core._PX_ - 60, middle - 96);
core.fillText('ui', '▲', core._PX_ - 60, middle - 96 - 7);
core.fillText('ui', '▲', core._PX_ - lastWidth * 0.5, middle - 64, null, this._buildFont(17, false));
core.fillText('ui', '▲', core._PX_ - lastWidth * 0.5, middle - 96);
core.fillText('ui', '▲', core._PX_ - lastWidth * 0.5, middle - 96 - 7);
}
if (core.actions._getNextFlyFloor(-1) != page) {
core.fillText('ui', '▼', core._PX_ - 60, middle + 64, null, this._buildFont(17, false));
core.fillText('ui', '▼', core._PX_ - 60, middle + 96);
core.fillText('ui', '▼', core._PX_ - 60, middle + 96 + 7);
core.fillText('ui', '▼', core._PX_ - lastWidth * 0.5, middle + 64, null, this._buildFont(17, false));
core.fillText('ui', '▼', core._PX_ - lastWidth * 0.5, middle + 96);
core.fillText('ui', '▼', core._PX_ - lastWidth * 0.5, middle + 96 + 7);
}
var size = 0.75;
core.strokeRect('ui', 16, 64, size * core._PX_, size * core._PY_, '#FFFFFF', 2);
@ -3381,6 +3402,8 @@ ui.prototype.createCanvas = function (name, x, y, width, height, z) {
newCanvas.style.display = 'block';
newCanvas.setAttribute("_left", x);
newCanvas.setAttribute("_top", y);
newCanvas.setAttribute("_width", width);
newCanvas.setAttribute("_height", height);
newCanvas.style.width = width * core.domStyle.scale + 'px';
newCanvas.style.height = height * core.domStyle.scale + 'px';
newCanvas.style.left = x * core.domStyle.scale + 'px';
@ -3439,16 +3462,21 @@ ui.prototype.rotateCanvas = function (name, angle, centerX, centerY) {
}
////// canvas重置 //////
ui.prototype.resizeCanvas = function (name, width, height, styleOnly, isTempCanvas) {
ui.prototype.resizeCanvas = function (name, width, height, styleOnly) {
var ctx = core.getContextByName(name);
const canvas = ctx.canvas;
if (!ctx) return null;
if (width != null) {
if (!styleOnly) core.maps._setHDCanvasSize(ctx, width, null, isTempCanvas);
if (!styleOnly && ctx.canvas.hasAttribute('isHD'))
core.maps._setHDCanvasSize(ctx, width, null);
ctx.canvas.style.width = width * core.domStyle.scale + 'px';
canvas.setAttribute('_width', width);
}
if (height != null) {
if (!styleOnly) core.maps._setHDCanvasSize(ctx, null, height, isTempCanvas);
if (!styleOnly && ctx.canvas.hasAttribute('isHD'))
core.maps._setHDCanvasSize(ctx, null, height);
ctx.canvas.style.height = height * core.domStyle.scale + 'px';
canvas.setAttribute('_height', height);
}
return ctx;
}

715
main.js

File diff suppressed because it is too large Load Diff

View File

@ -718,7 +718,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"firstData": {
"title": "天塔",
"name": "tianta",
"version": "Ver 2.10.0",
"version": "Ver 2.10.3",
"floorId": "nandu",
"hero": {
"image": "hero.png",
@ -1595,6 +1595,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"enableMoveDirectly": true,
"enableRouteFolding": true,
"disableShopOnDamage": false,
"blurFg": false
"blurFg": false,
"hideLeftStatusBar": false
}
}

View File

@ -907,6 +907,11 @@ main.floors.MT0=
"steps": [
"right:1"
]
},
{
"type": "hide",
"remove": true,
"time": 0
}
]
},

View File

@ -326,12 +326,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
core.status.hero.exp += exp;
core.status.hero.statistics.exp += exp;
var hint = "打败 " + core.getEnemyValue(enemy, "name", x, y);
if (core.flags.statusBarItems.indexOf('enableMoney') >= 0)
hint += ',' + core.getStatusLabel('money') + '+' + money; // hint += ",金币+" + money;
if (core.flags.statusBarItems.indexOf('enableExp') >= 0)
hint += ',' + core.getStatusLabel('exp') + '+' + exp; // hint += ",经验+" + exp;
if (!core.getFlag("isNoTip")) core.drawTip(hint, enemy.id);
var hint = "打败 " + core.getEnemyValue(enemy, "name", x, y);
if (core.flags.statusBarItems.indexOf('enableMoney') >= 0)
hint += ',' + core.getStatusLabel('money') + '+' + money; // hint += ",金币+" + money;
if (core.flags.statusBarItems.indexOf('enableExp') >= 0)
hint += ',' + core.getStatusLabel('exp') + '+' + exp; // hint += ",经验+" + exp;
if (!core.getFlag("isNoTip")) core.drawTip(hint, enemy.id);
// 中毒
if (core.enemys.hasSpecial(special, 12)) {
@ -388,6 +388,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]);
}
//等待动画执行完毕
core.push(todo, [{ "type": "waitAsync" }]);
// 战后事件
if (core.status.floorId != null) {
core.push(todo, core.floors[core.status.floorId].afterBattle[x + "," + y]);

View File

@ -42,7 +42,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
if (clientWidth - 3 * BORDER >= core._PX_ + BAR_WIDTH || (clientWidth > clientHeight && horizontalMaxRatio < 1)) {
core.domStyle.isVertical = false;
core.clearMap('Vertical');
} else {
} else if (core.status.played) {
// 竖屏
core.domStyle.isVertical = true;
core.createCanvas('Vertical', 0, 0, 480, 480, 200);
@ -1856,7 +1856,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
if (!name) return;
var canvas = document.createElement('canvas');
canvas.id = name;
canvas.className = 'gameCanvas';
canvas.className = 'gameCanvas anti-aliasing';
// 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高
if (main.mode != "editor") canvas.style.zIndex = zIndex || 0;
// 将图层插入进游戏内容

651
server.js Normal file
View File

@ -0,0 +1,651 @@
const http = require('http');
const fs = require('fs/promises');
const fss = require('fs');
const path = require('path');
const name = (() => {
const data = fss.readFileSync('./project/data.js', 'utf-8');
const json = JSON.parse(
data
.split(/(\n|\r\n)/)
.slice(1)
.join('\n')
);
return json.firstData.name;
})();
/** 核心服务器 */
const server = http.createServer();
/** 是否需要重新加载浏览器 */
let needReload = true;
/** 热重载信息 */
let hotReloadData = '';
/** 是否已经启动了热重载模块 */
let watched = false;
/** 是否已经启动了录像调试模块 */
let replayed = false;
/** 监听端口 */
let port = 3000;
const next = () => {
server.listen(port, '127.0.0.1');
server.on('error', () => {
console.log(`${port}端口已被占用`);
port++;
next();
});
};
next();
let repStart;
const listenedFloors = [];
// ----- GET file
/**
* 请求文件
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
* @param {string} path
*/
async function getFile(req, res, path) {
try {
const data = await fs.readFile(path);
if (path.endsWith('.js'))
res.writeHead(200, { 'Content-type': 'text/javascript' });
if (path.endsWith('.css'))
res.writeHead(200, { 'Content-type': 'text/css' });
if (path.endsWith('.html'))
res.writeHead(200, { 'Content-type': 'text/html' });
return res.end(data), true;
} catch {
return false;
}
}
/**
* 高层塔优化及动画加载
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
* @param {string[]} ids
* @param {string} suffix 后缀名
* @param {string} dir 文件夹路径
* @param {string} join 分隔符
*/
async function getAll(req, res, ids, suffix, dir, join) {
let data = {};
const tasks = ids.map(v => {
return new Promise(res => {
const d = path.resolve(__dirname, `${dir}${v}${suffix}`);
try {
fs.readFile(d).then(vv => {
data[v] = vv;
res(`${v} pack success.`);
});
} catch {
throw new ReferenceError(`The file ${d} does not exists.`);
}
});
});
await Promise.all(tasks);
const result = ids.map(v => data[v]);
return res.end(result.join(join)), true;
}
// ----- 样板的fs功能
/**
* 获取POST的数据
* @param {http.IncomingMessage} req
*/
async function getPostData(req) {
let data = '';
await new Promise(res => {
req.on('data', chunk => {
data += chunk.toString();
});
req.on('end', res);
});
return data;
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function readDir(req, res) {
const data = await getPostData(req);
const dir = path.resolve(__dirname, data.toString().slice(5));
try {
const info = await fs.readdir(dir);
res.end(JSON.stringify(info));
} catch (e) {
console.error(e);
res.end(`error: Read dir ${dir} fail. Does the dir exists?`);
}
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function mkdir(req, res) {
const data = await getPostData(req);
const dir = path.resolve(__dirname, data.toString().slice(5));
try {
await fs.mkdir(dir);
} catch (e) {}
res.end();
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function readFile(req, res) {
const data = (await getPostData(req)).toString();
const dir = path.resolve(__dirname, data.split('&name=')[1]);
try {
const type = /^type=(utf8|base64)/.exec(data)[0];
const info = await fs.readFile(dir, { encoding: type.slice(5) });
res.end(info);
} catch (e) {
res.end();
}
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function writeFile(req, res) {
const data = (await getPostData(req)).toString();
const name = data.split('&name=')[1].split('&value=')[0];
const dir = path.resolve(__dirname, name);
try {
const type = /^type=(utf8|base64)/.exec(data)[0].slice(5);
const value = /&value=[^]+/.exec(data)[0].slice(7);
await fs.writeFile(dir, value, { encoding: type });
testWatchFloor(name);
} catch (e) {
console.error(e);
res.end(
`error: Write file ${dir} fail. Does the parent folder exists?`
);
}
res.end();
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function rm(req, res) {
const data = (await getPostData(req)).toString();
const dir = path.resolve(__dirname, data.slice(5));
try {
await fs.rm(dir);
} catch (e) {
console.error(e);
res.end(`error: Remove file ${dir} fail. Does this file exists?`);
}
res.end();
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function moveFile(req, res) {
const data = (await getPostData(req)).toString();
const info = data.split('&dest=');
const src = path.resolve(__dirname, info[0].slice(4));
const dest = info[1];
try {
const data = await fs.readFile(src);
await fs.writeFile(path.resolve(__dirname, dest), data);
await fs.rm(src);
} catch (e) {
console.error(e);
res.end(`error: Move file ${dir} fail.`);
}
res.end();
}
/**
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function writeMultiFiles(req, res) {
const data = (await getPostData(req)).toString();
const names = /name=[^]+&value=/.exec(data)[0].slice(5, -7).split(';');
const value = /&value=[^]+/.exec(data)[0].slice(7).split(';');
const tasks = names.map((v, i) => {
try {
return new Promise(res => {
fs.writeFile(
path.resolve(__dirname, v),
value[i],
'base64' // 多文件是base64写入的
).then(v => {
testWatchFloor(v);
res(`write ${v} success.`);
});
});
} catch {
console.error(e);
res.end(`error: Write multi files fail.`);
}
});
await Promise.all(tasks);
res.end();
}
// ----- extract path & utils
/**
* 解析路径
* @param {...string} dirs
* @returns {Promise<string[]>}
*/
async function extract(...dirs) {
const res = [];
const tasks = dirs.map(v => {
return new Promise(resolve => {
if (v.endsWith('/')) {
// 匹配路径
const dir = path.resolve(__dirname, v.slice(0, -1));
fs.readdir(dir).then(files => {
const all = files
.filter(v => v !== 'thirdparty') // 排除第三方库
.map(vv => v + vv);
res.push(...all);
resolve('success');
});
} else if (/\/\*.\w+$/.test(v)) {
// 匹配文件夹中的后缀名
const suffix = /\.\w+$/.exec(v)[0];
const d = v.split(`/*${suffix}`)[0];
const dir = path.resolve(__dirname, d);
fs.readdir(dir).then(files => {
const all = files
.filter(v => v.endsWith(suffix))
.map(v => `${d === '' ? '' : d + '/'}${v}`);
res.push(...all);
resolve('success');
});
} else {
res.push(v);
resolve('success');
}
});
});
await Promise.all(tasks);
return res;
}
// ----- hot reload
/**
* 监听文件变化
*/
async function watch() {
// 需要重新加载的文件
const refresh = await extract('main.js', 'index.html', 'libs/');
const option = {
interval: 1000
};
refresh.forEach(v => {
const dir = path.resolve(__dirname, v);
fss.watchFile(dir, option, () => {
needReload = true;
console.log(`change: ${v}`);
});
});
// css 热重载
const css = await extract('/*.css');
css.forEach(v => {
const dir = path.resolve(__dirname, v);
fss.watchFile(dir, option, () => {
hotReloadData += `@@css:${v}`;
console.log(`css hot reload: ${v}`);
});
});
// 楼层 热重载
// 注意这里要逐个监听,并通过创建文件来监听文件改变
const floors = await extract('project/floors/*.js');
floors.forEach(v => {
watchOneFloor(v.slice(15));
});
// 脚本编辑 及 插件 热重载
const scripts = await extract('project/functions.js', 'project/plugins.js');
scripts.forEach(v => {
const dir = path.resolve(__dirname, v);
const type = v.split('/').at(-1).slice(0, -3);
fss.watchFile(dir, option, () => {
hotReloadData += `@@script:${type}`;
console.log(`script hot reload: ${type}.js`);
});
});
// 数据热重载
const datas = (await extract('project/*.js')).filter(
v => !v.endsWith('functions.js') && !v.endsWith('plugins.js')
);
datas.forEach(v => {
const dir = path.resolve(__dirname, v);
const type = v.split('/').at(-1).slice(0, -3);
fss.watchFile(dir, option, () => {
hotReloadData += `@@data:${type}`;
console.log(`data hot reload: ${type}`);
});
});
}
/**
* 检测是否是楼层文件并进行监听
* @param {string} url 要测试的路径
*/
function testWatchFloor(url) {
if (/project(\/|\\)floors(\/|\\).*\.js/.test(url)) {
const f = url.slice(15);
if (!listenedFloors.includes(f.slice(0, -3))) {
watchOneFloor(f);
}
}
}
/**
* 监听一个楼层文件
* @param {string} file 要监听的文件
*/
function watchOneFloor(file) {
if (!/.*\.js/.test(file)) return;
const f = file.slice(0, -3);
listenedFloors.push(file.slice(0, -3));
fss.watchFile(`project/floors/${file}`, { interval: 1000 }, () => {
const floorId = f;
if (hotReloadData.includes(`@@floor:${floorId}`)) return;
hotReloadData += `@@floor:${floorId}`;
console.log(`floor hot reload: ${floorId}`);
});
}
/**
* 修改部分文件后重新加载及热重载
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
function reload(req, res, hot = false) {
req.on('data', chunk => {
if (chunk.toString() === 'test' && !watched) {
watch();
watched = true;
console.log(`服务器热重载模块已开始服务`);
}
});
req.on('end', () => {
if (!hot) {
res.end(`${needReload}`);
needReload = false;
} else {
res.end(hotReloadData);
hotReloadData = '';
}
});
}
// ----- replay debugger
/**
* 录像调试
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
function replay(req, res) {
req.on('data', async chunk => {
if (chunk.toString() === 'test' && !replayed) {
replayed = true;
try {
await fs.mkdir(path.resolve(__dirname, '_replay'));
await fs.mkdir(path.resolve(__dirname, '_replay/status'));
await fs.mkdir(path.resolve(__dirname, '_replay/save'));
} catch {}
try {
await fs.readFile(
path.resolve(__dirname, '_replay/.info'),
'utf-8'
);
} catch {
await fs.writeFile(
path.resolve(__dirname, '_replay/.info'),
`{
"cnt": 0
}`,
'utf-8'
);
}
const data = fss.readFileSync(
path.resolve(__dirname, '_replay/.info'),
'utf-8'
);
repStart = Number(JSON.parse(data).cnt);
console.log(`服务器录像调试模块已开始服务`);
}
});
req.on('end', () => {
res.end();
});
}
/**
* 获取未占用的状态栏位
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function replayCnt() {
const data = `{
"cnt": ${++repStart}
}`;
fss.writeFileSync(path.resolve(__dirname, '_replay/.info'), data, 'utf-8');
return repStart;
}
/**
* 写入
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function replayWrite(req, res) {
const data = await getPostData(req);
const n = await replayCnt();
if (isNaN(n)) res.end('@error');
await Promise.all([
fs.writeFile(
path.resolve(__dirname, '_replay/.info'),
`{
"cnt": ${n + 1}
}`,
'utf-8'
),
fs.writeFile(
path.resolve(__dirname, `_replay/status/${n}.rep`),
data,
'utf-8'
)
]);
res.end(n.toString());
}
/**
* 比对录像与本地数据
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function replayCheck(req, res) {
const ans = await getPostData(req);
const [n, data] = ans.split('@-|-@');
const local = (
await fs.readFile(
path.resolve(__dirname, `_replay/status/${n}.rep`),
'utf-8'
)
)
.split('@---@')
.map(v => JSON.parse(v));
const rep = data.split('@---@').map(v => JSON.parse(v));
if (local.length !== rep.length) return res.end('false');
const check = (a, b) => {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (typeof a === 'object' && a !== null) {
for (const j in a) {
if (j === 'statistics' || j === 'timeout') continue; // 忽略统计信息
const aa = a[j];
const bb = b[j];
if (!check(aa, bb)) {
return false;
}
}
return true;
}
if (
typeof a === 'boolean' ||
typeof a === 'number' ||
typeof a === 'string' ||
typeof a === 'symbol' ||
typeof a === 'undefined' ||
typeof a === 'bigint' ||
a === null
) {
return a === b;
}
return true;
};
for (let i = 0; i < local.length; i++) {
const a = local[i];
const b = rep[i];
if (!check(a, b)) return res.end('false');
}
res.end('true');
}
/**
* 获取本地属性
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function replayGet(req, res, dir) {
const ans = Number(await getPostData(req));
const data = await fs.readFile(
path.resolve(__dirname, `_replay/${dir}/${ans}.rep`)
);
res.end(data);
}
/**
* 录像回放存档
* @param {http.IncomingMessage} req
* @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res
*/
async function replaySave(req, res) {
const data = await getPostData(req);
const [cnt, save] = data.split('@-|-@');
if (isNaN(Number(cnt))) {
console.log('Invalid input of save cnt');
res.end('@error: 不合法的录像存档信息');
}
await fs.writeFile(
path.resolve(__dirname, `_replay/save/${cnt}.rep`),
save,
'utf-8'
);
res.end('success');
}
// ----- server
server.on('listening', () => {
console.log(`服务已启动编辑器地址http://127.0.0.1:${port}/editor.html`);
console.log(`游戏地址http://127.0.0.1:${port}`);
});
// 处理请求
server.on('request', async (req, res) => {
/** @type {string} */
const p = req.url.replace(`/games/${name}`, '').replace('/all/', '/');
if (req.method === 'GET') {
const dir = path
.resolve(__dirname, p === '/' ? 'index.html' : p.slice(1))
.split('?v=')[0];
if (await getFile(req, res, dir)) return;
if (p.startsWith('/__all_floors__.js')) {
const all = p.split('&id=')[1].split(',');
res.writeHead(200, { 'Content-type': 'text/javascript' });
return await getAll(req, res, all, '.js', 'project/floors/', '\n');
}
if (p.startsWith('/__all_animates__')) {
const all = p.split('&id=')[1].split(',');
return await getAll(
req,
res,
all,
'.animate',
'project/animates/',
'@@@~~~###~~~@@@'
);
}
}
if (req.method === 'POST') {
if (p === '/listFile') return await readDir(req, res);
if (p === '/makeDir') return await mkdir(req, res);
if (p === '/readFile') return await readFile(req, res);
if (p === '/writeFile') return await writeFile(req, res);
if (p === '/deleteFile') return await rm(req, res);
if (p === '/moveFile') return await moveFile(req, res);
if (p === '/writeMultiFiles') return await writeMultiFiles(req, res);
if (p === '/reload') return reload(req, res);
if (p === '/hotReload') return reload(req, res, true);
if (p === '/replay') return replay(req, res);
if (p === '/replayWrite') return await replayWrite(req, res);
if (p === '/replayCheck') return await replayCheck(req, res);
if (p === '/replayGet') return await replayGet(req, res, 'status');
if (p === '/replaySave') return await replaySave(req, res);
if (p === '/replayGetSave') return await replayGet(req, res, 'save');
}
res.statusCode = 404;
res.end();
});

View File

@ -527,4 +527,9 @@ p#name {
@font-face {
font-family: Fira Code;
src: url(../src/fonts/FiraCode-Regular.ttf);
}
/* 注释下面这三行以开启抗锯齿 */
.anti-aliasing {
image-rendering: pixelated;
}

View File

@ -1,9 +0,0 @@
{
"compilerOptions": {
"allowJs": true,
"noEmit": true,
},
"include": [
"main.js", "project/**/*.js", "libs/**/*.js", "runtime.d.ts"
]
}

Binary file not shown.