上传文件至 /

Signed-off-by: whr12386 <2654095812@qq.com>
This commit is contained in:
whr12386 2025-01-14 12:25:24 +08:00
commit 3f45b47256
12 changed files with 6714 additions and 0 deletions

501
editor-mobile.html Normal file
View File

@ -0,0 +1,501 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1,initial-scale=1,user-scalable=no" />
<link href="_server/css/editor_mobile.css" rel="stylesheet">
<link href="_server/CodeMirror/codemirror.css" rel="stylesheet">
<link href="_server/thirdparty/awesomplete.css" rel="stylesheet">
<link id="color_css" rel="stylesheet">
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>
<script>
if(innerWidth>innerHeight){ //pic:1242*2208 | chrome info:1340*2380
confirm('宽大于高的设备请使用正常版本的editor, 点击确定跳转')?(window.location='./editor.html'):'';
}
if (location.protocol.indexOf("http")!=0) {
alert("请在启动服务中打开本编辑器!不然包括编辑在内的绝大多数功能都无法使用。");
}
</script>
<div class="main">
<div id="left" style="z-index:-1;opacity: 0;"><!-- map -->
<div id="arrEditor">
<table class="col" id='arrColMark'></table>
<table class="row" id='arrRowMark'></table>
<div id="mapEditArea">
<textarea cols="10" rows="10" id="pout"></textarea>
</div>
<div id="editTip">
<input id='newFileName' placeholder="新楼层id" style="width: 100px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapWidth' style="width: 20px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapHeight' style="width: 20px"/>
<input type="checkbox" id='newMapStatus' checked='checked' style='vertical-align: bottom'/>
<span style='vertical-align: bottom;'>保留楼层属性</span>
<br/>
<input type="button" value="新建空白地图" id='newMap'/>
</div>
<div id='editBtns'>
<input type="button" value="导出并复制地图" id="exportMap"/>
<input type="button" value="导入地图" id="importMap"/>
<input type="button" value="清除地图" id='clearMapButton'/>
<input type="button" value="删除地图" id="deleteMap"/>
</div>
<input type="button" value="批量创建空白地图 ↓" id='newMaps'/>
<div id='newFloors' style='display:none'>
<span style="vertical-align: bottom">楼层ID格式: </span>
<input id='newFloorIds' style="width: 70px" value='MT${i}'/>
<br/>
<span style="vertical-align: bottom">地图中文名格式: </span>
<input id='newFloorTitles' style="width: 100px" value='主塔 ${i} 层'/>
<br/>
<span style="vertical-align: bottom">状态栏名称: </span>
<input id='newFloorNames' style="width: 70px" value='${i}'/>
<br/>
<span style="vertical-align: bottom"></span>
<input id='newMapsWidth' style="width: 20px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapsHeight' style="width: 20px"/>
<input type="checkbox" id='newMapsStatus' checked='checked' style='vertical-align: bottom'/>
<span style='vertical-align: bottom; margin-left: -4px'>保留楼层属性</span>
<br/>
<span style="vertical-align: bottom">从 i=</span>
<input id='newMapsFrom' value="1" style="width: 20px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapsTo' value="5" style="width: 20px"/>
<input type="button" value="确认创建" id='createNewMaps'>
</div>
</div>
</div>
<div id="left1" class='leftTab' style="z-index:-1;opacity: 0;"><!-- appendpic -->
<h3 class="leftTabHeader">追加素材</h3>
<div class="leftTabContent">
<p>
<input id="selectFileBtn" type="button" value="导入文件到画板"/>
<select id="selectAppend"></select>
<!-- ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48"] -->
<input id="appendConfirm" type="button" value="追加"/>
<input id="quickAppendConfirm" type="button" value="快速追加"/>
<span style="font-size: 13px">自动注册</span><input id="appendRegister" type="checkbox" checked/>
</p>
<p>
色相:<input id='changeColorInput' type="range" min="0" max="12" step="1" value="0" list="huelists" style="width: 60%;margin-left: 3%;vertical-align: middle">
<datalist id="huelists" style="display: none">
<option value="0"/><option value="1"/><option value="2"/>
<option value="3"/><option value="4"/><option value="5"/>
<option value="6"/><option value="7"/><option value="8"/>
<option value="9"/><option value="10"/><option value="11"/><option value="12"/>
</datalist>
</p>
<div id="appendPicCanvas" style="position:relative;overflow: auto;height:470px;">
<canvas style="position:absolute"></canvas><!-- 用于画出灰白相间背景 -->
<canvas style="position:absolute"></canvas><!-- 用于画出选中文件 -->
<canvas style="position:absolute;z-index:100"></canvas><!-- 用于响应鼠标点击 -->
<canvas style="position:absolute;display:none;"></canvas><!-- 画出追加后的sprite用于储存 -->
<div id="appendPicSelection">
<div class="appendSelection"><span style="top: 0; left: 2px;">1</span></div>
<div class="appendSelection"><span style="top: 0; left: 14px;">2</span></div>
<div class="appendSelection"><span style="top: 12px; left: 2px;">3</span></div>
<div class="appendSelection"><span style="top: 12px; left: 14px;">4</span></div>
</div>
</div>
</div>
</div>
<div id="left2" class='leftTab' style="z-index:-1;opacity: 0;"><!-- loc -->
<h3 class="leftTabHeader">地图选点&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.uifunctions.addAutoEvent()">添加自动事件页</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('loc')">配置表格</button>
</h3>
<div class="leftTabContent">
<p id='pos_a6771a78_a099_417c_828f_0a24851ebfce' style="margin-left: 15px">0,0</p>
<div class='etable'>
<table>
<tbody id='table_3d846fc4_7644_44d1_aa04_433d266a73df'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left3" class='leftTab' style="z-index:-1;opacity: 0;"><!-- enemyitem -->
<h3 class="leftTabHeader">图块属性&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('add')">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('enemyitem')">配置表格</button>
</h3>
<div class="leftTabContent">
<div id="enemyItemTable"><!-- enemy and item -->
<div class='etable'>
<table>
<tbody id='table_a3f03d4c_55b8_4ef6_b362_b345783acd72'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top: -10px; margin-bottom: 10px">
<button id="copyEnemyItem">复制属性</button>
<button id="pasteEnemyItem">粘贴属性</button>
<button id="clearEnemyItem">清空属性</button>
<button id="clearAllEnemyItem">批量清空属性</button>
</div>
</div>
<div id='newIdIdnum'><!-- id and idnum -->
<input placeholder="新id唯一标识符"/>
<input placeholder="新idnum10000以内数字"/>
<button>确定</button>
<br/>
<button style="margin-top: 10px">自动注册</button>
<button style="margin-top: 10px; margin-left: 5px">删除此素材</button>
<button style="margin-top: 10px; margin-left: 5px">以此素材为模板追加</button>
</div>
<div id='changeId'><!-- id and idnum -->
<input placeholder="修改图块id为" style="width: 100px"/>
<button>确定</button>
<button style="margin-left: 5px">删除此素材</button>
<button style="margin-left: 5px">以此素材为模板追加</button>
</div>
</div>
</div>
<div id="left4" class='leftTab' style="z-index:-1;opacity: 0;"><!-- floor -->
<h3 class="leftTabHeader">楼层属性&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('add')">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('floor')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_4a3b1b09_b2fb_4bdf_b9ab_9f4cdac14c74'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div id='changeFloorId'><!-- id and idnum -->
<input placeholder="修改floorId为"/>
<button>确定</button>
</div>
<div id='changeFloorSize' style="font-size: 13px;">
修改地图大小:宽<input style="width: 25px;" value="13" />,高<input style="width: 25px;" value="13" />
偏移x<input style="width: 25px;" value="0" /> y<input style="width: 25px;" value="0" />
<button>确定</button>
</div>
</div>
</div>
<div id="left5" class='leftTab' style="z-index:-1;opacity: 0;"><!-- tower -->
<h3 class="leftTabHeader">全塔属性&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('add')">添加</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('tower')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_b6a03e4c_5968_4633_ac40_0dfdd2c9cde5'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left6" class='leftTab' style="z-index:-1;opacity: 0;">
<div style="position: relative; height: 95%"><!-- eventsEditor -->
<h3>事件编辑器 &nbsp;&nbsp;
<!--
<button onclick="editor_blockly.showXML()">Show XML</button>
<button onclick="editor_blockly.runCode()">console.log(obj=code)</button>
-->
<button onclick="editor_blockly.confirm()">确认</button>
<button onclick="editor_blockly.confirm(true)">应用</button>
<button id='blocklyParse' onclick="editor_blockly.parse()">解析</button>
<button onclick="editor_blockly.cancel()">取消</button>
<!-- 手机端放不下,因此不显示搜索框 -->
<div style="display: none">
<div class="searchLogo"></div>
<input type="text" id="searchBlock" placeholder="搜索事件块..."/>
</div>
<button class="cpPanel" onclick="editor_blockly.selectPointFromButton()">地图选点</button>
<button class="cpPanel" onclick="editor.uievent.searchUsedFlags()" style="margin-left:5px">变量出现位置搜索</button>
<input type="checkbox" class="cpPanel" id="blocklyReplace" onchange="editor_blockly.triggerReplace()" style="margin-left: 10px" />
<span class="cpPanel" style="margin-left: -4px; font-size: 13px">开启中文名替换</span>
<input type="checkbox" class="cpPanel" id="blocklyExpandCompare" onchange="editor_blockly.triggerExpandCompare()" style="margin-left: 10px" />
<span class="cpPanel" style="margin-left: -4px; font-size: 13px">展开值块逻辑运算</span>
<xml id="toolbox" style="display:none">
</xml>
</h3>
<div style="position: relative;height: 100%">
<div id="blocklyArea">
<div id="blocklyDiv"></div>
</div>
<textarea id="codeArea" spellcheck="false"></textarea>
</div>
</div>
</div>
<div id="colorPanel" class="cpPanel" style="display: none">
<input class="color" id="colorPicker" value="255,215,0,1"/>
<button onclick="confirmColor()">确定</button>
</div>
<div id="left7" style="z-index:-1;opacity: 0;"><!-- 多行文本编辑器 -->
<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 -->
<h3 class="leftTabHeader">脚本编辑&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('functions')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_e260a2be_5690_476a_b04e_dacddede78b3'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left9" class='leftTab' style="z-index:-1;opacity: 0;"><!-- commonevent -->
<h3 class="leftTabHeader">公共事件&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.table.addfunc()">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('commonevent')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_b7bf0124_99fd_4af8_ae2f_0017f04a7c7d'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left10" class='leftTab' style="z-index:-1;opacity: 0;"><!-- plugins -->
<h3 class="leftTabHeader">插件编写&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.table.addfunc()">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('plugins')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_e2c034ec_47c6_48ae_8db8_4f8f32fea2d6'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="mid">
<div class="col" id='mapColMark'></div>
<div class="row" id='mapRowMark'></div>
<div class="map" id="mapEdit">
<canvas class='gameCanvas' id='ebm'></canvas>
<canvas class='gameCanvas' id='efg'></canvas>
<canvas class='gameCanvas' id='eui' style='z-index:100'></canvas>
</div>
</div>
<div id="mid2" style="display: none">
<p style="margin: 10px"><span id='lastUsedTitle'></span> <button id='clearLastUsedBtn'>清除</button></p>
<div class="map" style="height: 160px; margin-top: 25px" id="lastUsedDiv">
<canvas class='gameCanvas' id='lastUsed'></canvas>
</div>
</div>
<div id="right" style="z-index:-1;opacity: 0;">
<div id="iconLib">
<div id="iconImages"></div>
<div id="selectBox">
<div id='dataSelection' style="display:none"></div>
</div>
</div>
<button id="iconExpandBtn"></button>
</div>
<div id="down">
<div style="margin:0.5rem">
<div class="tools">
<div id="tip"></div>
<span id='mobileview'>
<input type="button" value="数据区"/>
<input type="button" value="地图区"/>
<br />
<input type="button" value="素材库"/>
<input type="button" value="前往游戏" onclick="window.location='./index.html'"/>
</span>
<div id="menuDiv">
<div id="midMenu" style="display:none">
<div id='extraEvent' class='menuitem' style="display:none"><div class="menuitem-content"></div></div>
<div id='chooseThis' class="menuitem"><div class="menuitem-content">选中此点</div></div>
<div id='chooseInRight' class="menuitem"><div class="menuitem-content">在素材区选中此图块</div></div>
<div id='copyLoc' class="menuitem"><div class="menuitem-content">复制此事件</div></div>
<div id='pasteLoc' class="menuitem"><div class="menuitem-content">粘贴到此事件</div></div>
<div id='clearEvent' class="menuitem"><div class="menuitem-content">仅清空此点事件</div></div>
<div id='clearLoc' class="menuitem"><div class="menuitem-content">清空此点及事件</div></div>
</div>
</div>
<select id="editModeSelect" style="font-size: 12px">
<option value="map">地图编辑</option>
<option value="loc">地图选点</option>
<option value="enemyitem">图块属性</option>
<option value="floor">楼层属性</option>
<option value="tower">全塔属性</option>
<option value="functions">脚本编辑</option>
<option value="appendpic">追加素材</option>
<option value="commonevent">公共事件</option>
<option value="plugins">插件编写</option>
</select>
<span style="font-size: 12px"><input type="checkbox" id="showMovable"/>通行度</span>
<select id="editorTheme" style="font-size: 11px;">
<option value="editor_color">默认白</option>
<option value="editor_color_dark">夜间黑</option>
</select>
<select id="brushMod" style="clear:right">
<option value="line">画线</option>
<option value="rectangle">画矩形</option>
<option value="tileset">tile平铺</option>
<option value="fill">填充模式</option>
</select>
<select id="layerMod" style="float:left;margin-right:3px">
<option value="bgmap">背景层</option>
<option value="map" selected>事件层</option>
<option value="fgmap">前景层</option>
</select>
<div id="viewportButtons" style="float:left">
<input type="button" style="padding:1px 1px" value="←"/>
<input type="button" style="padding:1px 6px" value="↑"/>
<input type="button" style="padding:1px 6px" value="↓"/>
<input type="button" style="padding:1px 1px" value="→"/>
<input type="button" id="bigmapBtn" value="大地图" style="margin-left: 5px"/>
</div>
<select id="selectFloor" style="clear:left"></select>
<input type="button" value="选层" id='selectFloorBtn'/>
<input type="button" value="保存地图" id='saveFloor'/>
<input type="button" value="后退" id="undoFloor" style="display: none;"/>
<input type="button" value="帮助文档" id="openDoc" />
<span id='mobileeditdata' style="display:none">
<input type="button" value="编辑"/>
<input type="button" value="显示完整名称" style="display: none;"/>
<input type="button" value="显示完整注释"/>
</span>
</div>
</div>
</div>
</div>
<!-- <script>/* -->
<div id="gameInject" style='display: none'></div>
<!-- UI预览 & 地图选点 -->
<div id='uieventDiv' style='display: none'>
<div id='uieventDialog'>
<div id="uieventHead">
<span id="uieventTitle"></span>
<select id="uieventSelect" style="margin-left: 20px"></select>
<button id="uieventNo">关闭</button>
<button id="uieventYes">确定</button>
</div>
<hr style="clear: both; margin-top: 0"/>
<div id='uieventBody'>
<canvas class='gameCanvas' id='uievent'></canvas>
<div id="selectPointBox"></div>
<div id="uieventExtraBody" style="display: none"></div>
</div>
<div id="selectPoint">
<select id="selectPointFloor"></select>
<div id="selectPointButtons">
<input type="button" value="←"/>
<input type="button" value="↑"/>
<input type="button" value="↓"/>
<input type="button" value="→"/>
<input type="button" value="大地图" style="margin-left: 5px"/>
<input type="button" value="复制楼层ID">
</div>
</div>
</div>
</div>
<!-- */</script> -->
<!-- =========================================================== -->
<!-- <script src='_server/vendor/vue.min.js'></script> -->
<!-- <script src="https://cdn.bootcss.com/vue/2.5.13/vue.js"></script> -->
<!-- <script src='_server/vendor/polyfill.min.js'></script> -->
<script src='_server/fs.js'></script>
<script src='_server/editor_config.js'></script>
<script src='_server/editor_util.js'></script>
<script src='_server/editor_game.js'></script>
<script src='_server/editor_file.js'></script>
<script src='_server/editor_table.js'></script>
<script src='_server/editor_mode.js'></script>
<script src='_server/editor_ui.js'></script>
<script src='_server/editor_uievent.js'></script>
<script src='_server/editor_mappanel.js'></script>
<script src='_server/editor_datapanel.js'></script>
<script src='_server/editor_materialpanel.js'></script>
<script src='_server/editor_listen.js'></script>
<script src='libs/thirdparty/lz-string.min.js'></script>
<script src='libs/thirdparty/localforage.min.js'></script>
<script src='libs/thirdparty/zip.min.js'></script>
<script src='_server/editor.js'></script>
<script>
editor.isMobile=true;
editor.init(function () {
editor.listen();
editor.mode_listen();
editor.mobile_listen();
});
//main.listen();
</script>
<!-- hightlight textarea -->
<script src='_server/editor_multi.js'></script>
<!-- blockly -->
<script src="_server/blockly/Converter.bundle.min.js"></script>
<script src="_server/blockly/blockly_compressed.js"></script>
<script src="_server/blockly/blocks_compressed.js"></script>
<script src="_server/blockly/javascript_compressed.js"></script>
<script src="_server/blockly/zh-hans.js"></script>
<script src='_server/MotaActionParser.js'></script>
<script src='_server/editor_blocklyconfig.js'></script>
<script src='_server/editor_blockly.js'></script>
<!-- codemirror -->
<script src="_server/CodeMirror/codeMirror.bundle.min.js"></script>
<script src="_server/CodeMirror/beautify.min.js"></script>
<script src="_server/CodeMirror/jshint.min.js"></script>
<script src="_server/CodeMirror/codeMirror.plugin.min.js"></script>
<script src="_server/CodeMirror/acorn.min.js"></script>
<script src="_server/CodeMirror/defs.js"></script>
<script src="_server/CodeMirror/tern.min.js"></script>
<!-- thirdparty -->
<script src="_server/thirdparty/color.all.min.js"></script>
<script src="_server/thirdparty/awesomplete.min.js"></script>
<script src="_server/thirdparty/caret-position.js"></script>
<script src="_server/thirdparty/jsColor.js"></script>
</body>
</html>

485
editor.html Normal file
View File

@ -0,0 +1,485 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link href="_server/css/editor.css" rel="stylesheet">
<link href="_server/CodeMirror/codemirror.css" rel="stylesheet">
<link href="_server/thirdparty/awesomplete.css" rel="stylesheet">
<link id="color_css" rel="stylesheet">
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>
<script>
if(innerWidth<innerHeight){ //pic:1242*2208 | chrome info:1340*2380
confirm('高大于宽的设备请使用移动版本的editor, 点击确定跳转')?(window.location='./editor-mobile.html'):'';
}
if (location.protocol.indexOf("http")!=0) {
alert("请在启动服务中打开本编辑器!不然包括编辑在内的绝大多数功能都无法使用。");
}
</script>
<div class="main">
<div id="left" style="z-index:-1;opacity: 0;"><!-- map -->
<div id="arrEditor">
<table class="col" id='arrColMark'></table>
<table class="row" id='arrRowMark'></table>
<div id="mapEditArea">
<textarea cols="10" rows="10" id="pout"></textarea>
</div>
<div id="editTip">
<input type="button" value="新建空白地图" id='newMap'/>
<input id='newFileName' placeholder="新楼层id" style="width: 70px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapWidth' style="width: 20px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapHeight' style="width: 20px"/>
<input type="checkbox" id='newMapStatus' checked='checked' style='vertical-align: bottom'/>
<span style='vertical-align: bottom; margin-left: -4px'>保留楼层属性</span>
</div>
<div id="editBtns">
<input type="button" value="导出并复制地图" id="exportMap"/>
<input type="button" value="从框中导入地图" id="importMap"/>
<input type="button" value="清除地图" id='clearMapButton'/>
<input type="button" value="删除地图" id="deleteMap"/>
</div>
<input type="button" value="批量创建空白地图 ↓" id='newMaps'/>
<div id='newFloors' style='display:none'>
<span style="vertical-align: bottom">楼层ID格式: </span>
<input id='newFloorIds' style="width: 70px" value='MT${i}'/>
<span style="vertical-align: bottom">地图中文名格式: </span>
<input id='newFloorTitles' style="width: 100px" value='主塔 ${i} 层'/>
<br/>
<span style="vertical-align: bottom">状态栏名称: </span>
<input id='newFloorNames' style="width: 70px" value='${i}'/>
<span style="vertical-align: bottom"></span>
<input id='newMapsWidth' style="width: 20px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapsHeight' style="width: 20px"/>
<input type="checkbox" id='newMapsStatus' checked='checked' style='vertical-align: bottom'/>
<span style='vertical-align: bottom; margin-left: -4px'>保留楼层属性</span>
<br/>
<span style="vertical-align: bottom">从 i=</span>
<input id='newMapsFrom' value="1" style="width: 20px"/>
<span style="vertical-align: bottom"></span>
<input id='newMapsTo' value="5" style="width: 20px"/>
<input type="button" value="确认创建" id='createNewMaps'>
</div>
</div>
</div>
<div id="left1" class='leftTab' style="z-index:-1;opacity: 0;"><!-- appendpic -->
<h3 class="leftTabHeader">追加素材</h3>
<div class="leftTabContent">
<p>
<input id="selectFileBtn" type="button" value="导入文件到画板"/>
<select id="selectAppend"></select>
<!-- ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48"] -->
<input id="appendConfirm" type="button" value="追加"/>
<input id="quickAppendConfirm" type="button" value="快速追加"/>
<span style="font-size: 13px">&nbsp;&nbsp;自动注册</span><input id="appendRegister" type="checkbox" checked/>
</p>
<p><small>从V2.7.1开始你可以直接将素材图片拖到对应的素材区将自动追加并注册。同时4x4的道具素材已支持快速追加一次16个。</small></p>
<p>
色相:<input id='changeColorInput' type="range" min="0" max="12" step="1" value="0" list="huelists" style="width: 60%;margin-left: 3%;vertical-align: middle">
<datalist id="huelists" style="display: none">
<option value="0"/><option value="1"/><option value="2"/>
<option value="3"/><option value="4"/><option value="5"/>
<option value="6"/><option value="7"/><option value="8"/>
<option value="9"/><option value="10"/><option value="11"/><option value="12"/>
</datalist>
</p>
<div id="appendPicCanvas" style="position:relative;overflow: auto;height:470px;">
<canvas style="position:absolute"></canvas><!-- 用于画出灰白相间背景 -->
<canvas style="position:absolute"></canvas><!-- 用于画出选中文件 -->
<canvas style="position:absolute;z-index:100"></canvas><!-- 用于响应鼠标点击 -->
<canvas style="position:absolute;display:none;"></canvas><!-- 画出追加后的sprite用于储存 -->
<div id="appendPicSelection">
<div class="appendSelection"><span style="top: 0; left: 2px;">1</span></div>
<div class="appendSelection"><span style="top: 0; left: 14px;">2</span></div>
<div class="appendSelection"><span style="top: 12px; left: 2px;">3</span></div>
<div class="appendSelection"><span style="top: 12px; left: 14px;">4</span></div>
</div>
</div>
</div>
</div>
<div id="left2" class='leftTab' style="z-index:-1;opacity: 0;"><!-- loc -->
<h3 class="leftTabHeader">地图选点&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.uifunctions.addAutoEvent()">添加自动事件页</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('loc')">配置表格</button>
</h3>
<div class="leftTabContent">
<p id='pos_a6771a78_a099_417c_828f_0a24851ebfce' style="margin-left: 15px">0,0</p>
<div class='etable'>
<table>
<tbody id='table_3d846fc4_7644_44d1_aa04_433d266a73df'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left3" class='leftTab' style="z-index:-1;opacity: 0;"><!-- enemyitem -->
<h3 class="leftTabHeader">图块属性&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('add')">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('enemyitem')">配置表格</button>
</h3>
<div class="leftTabContent">
<div id="enemyItemTable"><!-- enemy and item -->
<div class='etable'>
<table>
<tbody id='table_a3f03d4c_55b8_4ef6_b362_b345783acd72'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top: -10px; margin-bottom: 10px">
<button id="copyEnemyItem">复制属性</button>
<button id="pasteEnemyItem">粘贴属性</button>
<button id="clearEnemyItem">清空属性</button>
<button id="clearAllEnemyItem">批量清空属性</button>
</div>
</div>
<div id='newIdIdnum'><!-- id and idnum -->
<input placeholder="新id唯一标识符"/>
<input placeholder="新idnum10000以内数字"/>
<button>确定</button>
<br/>
<button style="margin-top: 10px">自动注册</button>
<button style="margin-top: 10px; margin-left: 5px">删除此素材</button>
<button style="margin-top: 10px; margin-left: 5px">以此素材为模板追加</button>
</div>
<div id='changeId'><!-- id and idnum -->
<input placeholder="修改图块id为" style="width: 100px"/>
<button>确定</button>
<button style="margin-left: 5px">删除此素材</button>
<button style="margin-left: 5px">以此素材为模板追加</button>
</div>
</div>
</div>
<div id="left4" class='leftTab' style="z-index:-1;opacity: 0;"><!-- floor -->
<h3 class="leftTabHeader">楼层属性&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('add')">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('floor')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_4a3b1b09_b2fb_4bdf_b9ab_9f4cdac14c74'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div id='changeFloorId'><!-- id and idnum -->
<input placeholder="修改floorId为"/>
<button>确定</button>
</div>
<div id='changeFloorSize' style="font-size: 13px;">
修改地图大小:宽<input style="width: 25px;" value="13" />,高<input style="width: 25px;" value="13" />
偏移x<input style="width: 25px;" value="0" /> y<input style="width: 25px;" value="0" />
<button>确定</button>
</div>
</div>
</div>
<div id="left5" class='leftTab' style="z-index:-1;opacity: 0;"><!-- tower -->
<h3 class="leftTabHeader">全塔属性&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('add')">添加</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('tower')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_b6a03e4c_5968_4633_ac40_0dfdd2c9cde5'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left6" class='leftTab' style="z-index:-1;opacity: 0;">
<div style="position: relative; height: 95%"><!-- eventsEditor -->
<h3>事件编辑器 &nbsp;&nbsp;
<!--
<button onclick="editor_blockly.showXML()">Show XML</button>
<button onclick="editor_blockly.runCode()">console.log(obj=code)</button>
-->
<button onclick="editor_blockly.confirm()">确认</button>
<button onclick="editor_blockly.confirm(true)">应用</button>
<button id='blocklyParse' onclick="editor_blockly.parse()">解析</button>
<button onclick="editor_blockly.cancel()">取消</button>
<div style="position: relative; display: inline-block; margin-left: 10px">
<div class="searchLogo"></div>
<input type="text" id="searchBlock" placeholder="搜索事件块..."/>
</div>
<button class="cpPanel" onclick="editor_blockly.selectPointFromButton()" style="margin-left:5px">地图选点</button>
<button class="cpPanel" onclick="editor.uievent.searchUsedFlags()" style="margin-left:5px">变量出现位置搜索</button>
<input type="checkbox" class="cpPanel" id="blocklyReplace" onchange="editor_blockly.triggerReplace()" style="margin-left: 10px" />
<span class="cpPanel" style="margin-left: -4px; font-size: 13px">开启中文名替换</span>
<input type="checkbox" class="cpPanel" id="blocklyExpandCompare" onchange="editor_blockly.triggerExpandCompare()" style="margin-left: 10px" />
<span class="cpPanel" style="margin-left: -4px; font-size: 13px">展开值块逻辑运算</span>
<xml id="toolbox" style="display:none">
</xml>
</h3>
<div style="position: relative;height: 100%">
<div id="blocklyArea">
<div id="blocklyDiv"></div>
</div>
<textarea id="codeArea" spellcheck="false"></textarea>
</div>
</div>
</div>
<div id="colorPanel" class="cpPanel" style="display: none">
<input class="color" id="colorPicker" value="255,215,0,1"/>
<button onclick="confirmColor()">确定</button>
</div>
<div id="left7" style="z-index:-1;opacity: 0;"><!-- 多行文本编辑器 -->
<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 -->
<h3 class="leftTabHeader">脚本编辑&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('functions')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_e260a2be_5690_476a_b04e_dacddede78b3'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left9" class='leftTab' style="z-index:-1;opacity: 0;"><!-- commonevent -->
<h3 class="leftTabHeader">公共事件&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.table.addfunc()">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('commonevent')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_b7bf0124_99fd_4af8_ae2f_0017f04a7c7d'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="left10" class='leftTab' style="z-index:-1;opacity: 0;"><!-- plugins -->
<h3 class="leftTabHeader">插件编写&nbsp;&nbsp;<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;<button onclick="editor.table.addfunc()">添加</button>&nbsp;&nbsp;<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;<button onclick="editor_multi.editCommentJs('plugins')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_e2c034ec_47c6_48ae_8db8_4f8f32fea2d6'>
<tr>
<td>条目</td>
<td>注释</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="mid">
<table class="col" id='mapColMark'></table>
<table class="row" id='mapRowMark'></table>
<div class="map" id="mapEdit">
<canvas class='gameCanvas' id='ebm'></canvas>
<canvas class='gameCanvas' id='efg'></canvas>
<canvas class='gameCanvas' id='eui' style='z-index:100'></canvas>
</div>
<div class="tools">
<div id="tip"></div>
<select id="editModeSelect" style="font-size: 12px">
<option value="map">地图编辑(Z)</option>
<option value="loc">地图选点(X)</option>
<option value="enemyitem">图块属性(C)</option>
<option value="floor">楼层属性(V)</option>
<option value="tower">全塔属性(B)</option>
<option value="functions">脚本编辑(N)</option>
<option value="appendpic">追加素材(M)</option>
<option value="commonevent">公共事件(,)</option>
<option value="plugins">插件编写(.)</option>
</select>
<span style="font-size: 12px"><input type="checkbox" id="showMovable" style="margin-left:0;margin-right: 2px"/>通行度</span>
<select id="editorTheme" style="margin-left: 0; font-size: 11px;">
<option value="editor_color">默认白</option>
<option value="editor_color_dark">夜间黑</option>
</select>
<br/>
<span style="font-size: 12px;">
<input type="radio" id="brushMod" name="brushMod" value="line" checked="checked" />线
<input type="radio" id="brushMod2" name="brushMod" value="rectangle" />矩形
<input type="radio" id="brushMod3" name="brushMod" value="tileset" />tile平铺
<input type="radio" id="brushMod4" name="brushMod" value="fill" />填充
</span>
<br/>
<span style="font-size: 12px">
<input type="radio" id="layerMod2" name="layerMod" value="bgmap" />背景层
<input type="radio" id="layerMod" name="layerMod" value="map" checked="checked" style="margin-left: 5px" />事件层
<input type="radio" id="layerMod3" name="layerMod" value="fgmap" style="margin-left: 5px" />前景层
</span>
<br>
<div id="viewportButtons" style="margin-bottom: 7px">
<input type="button" value="←"/>
<input type="button" value="↑"/>
<input type="button" value="↓"/>
<input type="button" value="→"/>
<input type="button" id='bigmapBtn' value="大地图" style="margin-left: 5px"/>
</div>
<select id="selectFloor" style="margin-bottom: 5px;"></select>
<input type="button" value="选层" id='selectFloorBtn'/>
<input type="button" value="保存地图" id='saveFloor'/>
<input type="button" value="后退" id="undoFloor" style="display: none;" />
<input type="button" value="帮助文档" id="openDoc" />
<input type="button" value="前往游戏" onclick="window.open('./index.html', '_blank')"/>
</div>
</div>
<div id="mid2">
<p style="margin: 10px"><span id='lastUsedTitle'></span><small>Ctrl+滚轮放缩,右键置顶)</small> <button id='clearLastUsedBtn'>清除</button></p>
<div class="map" id="lastUsedDiv">
<canvas id='lastUsed' class="gameCanvas" style="overflow: hidden"></canvas>
</div>
</div>
<div id="right">
<div id="iconLib">
<div id="iconImages"></div>
<div id="selectBox">
<div id='dataSelection' style="display:none"></div>
</div>
</div>
<button id="iconExpandBtn"></button>
</div>
<div id="menuDiv">
<div id="midMenu" style="display:none">
<div id='extraEvent' class='menuitem' style="display:none"><div class="menuitem-content"></div></div>
<div id='chooseThis' class="menuitem"><div class="menuitem-content">选中此点</div></div>
<div id='chooseInRight' class="menuitem"><div class="menuitem-content">在素材区选中此图块</div></div>
<div id='copyLoc' class="menuitem"><div class="menuitem-content">复制此事件</div></div>
<div id='pasteLoc' class="menuitem"><div class="menuitem-content">粘贴到此事件</div></div>
<div id='clearEvent' class="menuitem"><div class="menuitem-content">仅清空此点事件</div></div>
<div id='clearLoc' class="menuitem"><div class="menuitem-content">清空此点及事件</div></div>
</div>
</div>
</div>
<!-- <script>/* -->
<div id="gameInject" style='display: none'></div>
<!-- UI预览 & 地图选点 -->
<div id='uieventDiv' style='display: none'>
<div id='uieventDialog'>
<div id="uieventHead">
<span id="uieventTitle"></span>
<select id="uieventSelect" style="margin-left: 20px"></select>
<button id="uieventNo">关闭</button>
<button id="uieventYes">确定</button>
</div>
<hr style="clear: both; margin-top: 0"/>
<div id='uieventBody'>
<canvas class='gameCanvas' id='uievent'></canvas>
<div id="selectPointBox"></div>
<div id="uieventExtraBody" style="display: none; margin-top: -10px"></div>
</div>
<div id="selectPoint">
<select id="selectPointFloor"></select>
<div id="selectPointButtons">
<input type="button" value="←"/>
<input type="button" value="↑"/>
<input type="button" value="↓"/>
<input type="button" value="→"/>
<input type="button" value="切换大地图" style="margin-left: 10px;">
<input type="button" value="复制楼层ID">
</div>
</div>
</div>
</div>
<!-- */</script> -->
<!-- =========================================================== -->
<!-- <script src='_server/vendor/vue.min.js'></script> -->
<!-- <script src="https://cdn.bootcss.com/vue/2.5.13/vue.js"></script> -->
<!-- <script src='_server/vendor/polyfill.min.js'></script> -->
<script src='_server/fs.js'></script>
<script src='_server/editor_config.js'></script>
<script src='_server/editor_util.js'></script>
<script src='_server/editor_game.js'></script>
<script src='_server/editor_file.js'></script>
<script src='_server/editor_table.js'></script>
<script src='_server/editor_mode.js'></script>
<script src='_server/editor_ui.js'></script>
<script src='_server/editor_uievent.js'></script>
<script src='_server/editor_mappanel.js'></script>
<script src='_server/editor_datapanel.js'></script>
<script src='_server/editor_materialpanel.js'></script>
<script src='_server/editor_listen.js'></script>
<script src='libs/thirdparty/lz-string.min.js'></script>
<script src='libs/thirdparty/localforage.min.js'></script>
<script src='libs/thirdparty/zip.min.js'></script>
<script src='_server/editor.js'></script>
<script>
editor.init(function () {
editor.listen();
editor.mode_listen();
editor.mobile_listen();
});
//main.listen();
</script>
<!-- hightlight textarea -->
<script src='_server/editor_multi.js'></script>
<!-- blockly -->
<script src="_server/blockly/Converter.bundle.min.js"></script>
<script src="_server/blockly/blockly_compressed.js"></script>
<script src="_server/blockly/blocks_compressed.js"></script>
<script src="_server/blockly/javascript_compressed.js"></script>
<script src="_server/blockly/zh-hans.js"></script>
<script src='_server/MotaActionParser.js'></script>
<script src='_server/editor_blocklyconfig.js'></script>
<script src='_server/editor_blockly.js'></script>
<!-- codemirror -->
<script src="_server/CodeMirror/codeMirror.bundle.min.js"></script>
<script src="_server/CodeMirror/beautify.min.js"></script>
<script src="_server/CodeMirror/jshint.min.js"></script>
<script src="_server/CodeMirror/codeMirror.plugin.min.js"></script>
<script src="_server/CodeMirror/acorn.min.js"></script>
<script src="_server/CodeMirror/defs.js"></script>
<script src="_server/CodeMirror/tern.min.js"></script>
<!-- thirdparty -->
<script src="_server/thirdparty/color.all.min.js"></script>
<script src="_server/thirdparty/awesomplete.min.js"></script>
<script src="_server/thirdparty/caret-position.js"></script>
<script src="_server/thirdparty/jsColor.js"></script>
</body>
</html>

208
index.html Normal file
View File

@ -0,0 +1,208 @@
<!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'>
<title>HTML5魔塔</title>
<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">
<meta name="x5-orientation" content="portrait">
<meta name="x5-fullscreen" content="true">
<meta name="x5-page-mode" content="app">
<link type='text/css' href='styles.css' rel='stylesheet'>
</head>
<body>
<!-- <div id="stars"></div>
<div id="stars2"></div>
<div id="stars3"></div> -->
<div id='startImageBackgroundDiv'>
<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");
}
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'>
<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>
<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>
<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;"></canvas>
</div>
<div id="toolBar" class="clearfix">
<img class="tools" id='img-book'>
<img class="tools" id='img-fly'>
<img class="tools" id='img-toolbox'>
<img class="tools" id='img-keyboard'>
<img class="tools" id='img-shop'>
<img class="tools" id='img-save'>
<img class="tools" id='img-load'>
<img class="tools" id='img-settings'>
<img class="tools" id='img-btn1' style='display:none'>
<img class="tools" id='img-btn2' style='display:none'>
<img class="tools" id='img-btn3' style='display:none'>
<img class="tools" id='img-btn4' style='display:none'>
<img class="tools" id='img-btn5' style='display:none'>
<img class="tools" id='img-btn6' style='display:none'>
<img class="tools" id='img-btn7' style='display:none'>
<img class="tools" id='img-btn8' style='display:none'>
<p class="statusLabel tools" id="hard"></p>
</div>
<div id="gameDraw">
<div id="gif"></div>
<div id="gif2"></div>
<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='damage'></canvas>
<canvas class='gameCanvas' id='animate'></canvas>
<canvas class='gameCanvas' id='curtain'></canvas>
<canvas class='gameCanvas' id='ui'></canvas>
<canvas class='gameCanvas' id='data'>此浏览器不支持HTML5</canvas>
<div id="next"></div>
</div>
</div>
<div id='inputDiv'>
<div id='inputDialog'>
<p id="inputMessage">请输入文字...</p>
<input id='inputBox' type="text" autocomplete="off" />
<button id='inputYes'>确定</button>
<button id='inputNo'>取消</button>
</div>
</div>
<div id="ui-editor"></div>
<!-- injection -->
<script src='libs/thirdparty/lz-string.min.js'></script>
<script src='libs/thirdparty/priority-queue.min.js'></script>
<script src='libs/thirdparty/localforage.min.js'></script>
<script src='libs/thirdparty/zip.min.js'></script>
<script id='mainScript' src='main.js'></script>
<script>main.init('play'); main.listen();</script>
<script>
</script>
</body>
</html>

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

996
main.js Normal file
View File

@ -0,0 +1,996 @@
/// <reference path="./runtime.d.ts" />
function main() {
//------------------------ 用户修改内容 ------------------------//
this.version = '2.10.3'; // 游戏版本号如果更改了游戏内容建议修改此version以免造成缓存问题。
this.useCompress = false; // 是否使用压缩文件
// 当你即将发布你的塔时请使用“JS代码压缩工具”将所有js代码进行压缩然后将这里的useCompress改为true。
// 请注意只有useCompress是false时才会读取floors目录下的文件为true时会直接读取libs目录下的floors.min.js文件。
// 如果要进行剧本的修改请务必将其改成false。
this.bgmRemote = false; // 是否采用远程BGM
this.bgmRemoteRoot = 'https://h5mota.com/music/'; // 远程BGM的根目录
this.isCompetition = false; // 是否是比赛模式
this.savePages = 1000; // 存档页数每页可存5个默认为1000页5000个存档
this.criticalUseLoop = 1; // 循环临界的分界
//------------------------ 用户修改内容 END ------------------------//
this.dom = {
body: document.body,
gameGroup: document.getElementById('gameGroup'),
mainTips: document.getElementById('mainTips'),
musicBtn: document.getElementById('musicBtn'),
enlargeBtn: document.createElement('img'),
startPanel: document.getElementById('startPanel'),
startTop: document.getElementById('startTop'),
startTopProgressBar: document.getElementById('startTopProgressBar'),
startTopProgress: document.getElementById('startTopProgress'),
startTopLoadTips: document.getElementById('startTopLoadTips'),
startBackground: document.getElementById('startBackground'),
startLogo: document.getElementById('startLogo'),
startButtonGroup: document.getElementById('startButtonGroup'),
floorMsgGroup: document.getElementById('floorMsgGroup'),
logoLabel: document.getElementById('logoLabel'),
versionLabel: document.getElementById('versionLabel'),
floorNameLabel: document.getElementById('floorNameLabel'),
statusBar: document.getElementById('statusBar'),
status: document.getElementsByClassName('status'),
toolBar: document.getElementById('toolBar'),
tools: document.getElementsByClassName('tools'),
gameCanvas: document.getElementsByClassName('gameCanvas'),
gif: document.getElementById('gif'),
gif2: document.getElementById('gif2'),
gameDraw: document.getElementById('gameDraw'),
startButtons: document.getElementById('startButtons'),
playGame: document.getElementById('playGame'),
loadGame: document.getElementById('loadGame'),
replayGame: document.getElementById('replayGame'),
levelChooseButtons: document.getElementById('levelChooseButtons'),
data: document.getElementById('data'),
statusLabels: document.getElementsByClassName('statusLabel'),
statusTexts: document.getElementsByClassName('statusText'),
floorCol: document.getElementById('floorCol'),
nameCol: document.getElementById('nameCol'),
lvCol: document.getElementById('lvCol'),
hpmaxCol: document.getElementById('hpmaxCol'),
hpCol: document.getElementById('hpCol'),
manaCol: document.getElementById('manaCol'),
atkCol: document.getElementById('atkCol'),
defCol: document.getElementById('defCol'),
mdefCol: document.getElementById('mdefCol'),
moneyCol: document.getElementById('moneyCol'),
expCol: document.getElementById('expCol'),
upCol: document.getElementById('upCol'),
keyCol: document.getElementById('keyCol'),
pzfCol: document.getElementById('pzfCol'),
debuffCol: document.getElementById('debuffCol'),
skillCol: document.getElementById('skillCol'),
hard: document.getElementById('hard'),
statusCanvas: document.getElementById('statusCanvas'),
statusCanvasCtx: document
.getElementById('statusCanvas')
.getContext('2d'),
inputDiv: document.getElementById('inputDiv'),
inputMessage: document.getElementById('inputMessage'),
inputBox: document.getElementById('inputBox'),
inputYes: document.getElementById('inputYes'),
inputNo: document.getElementById('inputNo'),
next: document.getElementById('next')
};
this.mode = 'play';
this.loadList = [
'loader',
'control',
'utils',
'items',
'icons',
'maps',
'enemys',
'events',
'actions',
'data',
'ui',
'extensions',
'core'
];
this.pureData = [
'data',
'enemys',
'icons',
'maps',
'items',
'functions',
'events',
'plugins'
];
this.materials = [
'animates',
'enemys',
'items',
'npcs',
'terrains',
'enemy48',
'npc48',
'icons'
];
this.statusBar = {
image: {
floor: document.getElementById('img-floor'),
name: document.getElementById('img-name'),
lv: document.getElementById('img-lv'),
hpmax: document.getElementById('img-hpmax'),
hp: document.getElementById('img-hp'),
mana: document.getElementById('img-mana'),
atk: document.getElementById('img-atk'),
def: document.getElementById('img-def'),
mdef: document.getElementById('img-mdef'),
money: document.getElementById('img-money'),
exp: document.getElementById('img-exp'),
up: document.getElementById('img-up'),
skill: document.getElementById('img-skill'),
book: document.getElementById('img-book'),
fly: document.getElementById('img-fly'),
toolbox: document.getElementById('img-toolbox'),
keyboard: document.getElementById('img-keyboard'),
shop: document.getElementById('img-shop'),
save: document.getElementById('img-save'),
load: document.getElementById('img-load'),
settings: document.getElementById('img-settings'),
btn1: document.getElementById('img-btn1'),
btn2: document.getElementById('img-btn2'),
btn3: document.getElementById('img-btn3'),
btn4: document.getElementById('img-btn4'),
btn5: document.getElementById('img-btn5'),
btn6: document.getElementById('img-btn6'),
btn7: document.getElementById('img-btn7'),
btn8: document.getElementById('img-btn8')
},
icons: {
floor: 0,
name: null,
lv: 1,
hpmax: 2,
hp: 3,
atk: 4,
def: 5,
mdef: 6,
money: 7,
exp: 8,
up: 9,
book: 10,
fly: 11,
toolbox: 12,
keyboard: 13,
shop: 14,
save: 15,
load: 16,
settings: 17,
play: 18,
pause: 19,
stop: 20,
speedDown: 21,
speedUp: 22,
rewind: 23,
equipbox: 24,
mana: 25,
skill: 26,
btn1: 27,
btn2: 28,
btn3: 29,
btn4: 30,
btn5: 31,
btn6: 32,
btn7: 33,
btn8: 34
},
floor: document.getElementById('floor'),
name: document.getElementById('name'),
lv: document.getElementById('lv'),
hpmax: document.getElementById('hpmax'),
hp: document.getElementById('hp'),
mana: document.getElementById('mana'),
atk: document.getElementById('atk'),
def: document.getElementById('def'),
mdef: document.getElementById('mdef'),
money: document.getElementById('money'),
exp: document.getElementById('exp'),
up: document.getElementById('up'),
skill: document.getElementById('skill'),
yellowKey: document.getElementById('yellowKey'),
blueKey: document.getElementById('blueKey'),
redKey: document.getElementById('redKey'),
greenKey: document.getElementById('greenKey'),
poison: document.getElementById('poison'),
weak: document.getElementById('weak'),
curse: document.getElementById('curse'),
pickaxe: document.getElementById('pickaxe'),
bomb: document.getElementById('bomb'),
fly: document.getElementById('fly'),
hard: document.getElementById('hard')
};
this.floors = {};
this.canvas = {};
this.__VERSION__ = '2.10.3';
this.__VERSION_CODE__ = 512;
}
main.prototype.init = function (mode, callback) {
for (var i = 0; i < main.dom.gameCanvas.length; i++) {
main.canvas[main.dom.gameCanvas[i].id] =
main.dom.gameCanvas[i].getContext('2d');
}
main.mode = mode;
main.loadJs('project', main.pureData, function () {
var mainData = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main;
for (var ii in mainData) main[ii] = mainData[ii];
main.dom.startLogo.style = main.styles.startLogoStyle;
main.dom.startButtonGroup.style = main.styles.startButtonsStyle;
main.levelChoose = main.levelChoose || [];
main.levelChoose.forEach(function (value) {
var span = document.createElement('span');
span.setAttribute('class', 'startButton');
span.innerText = value.title || '';
(function (span, str_) {
span.onclick = function () {
core.events.startGame(str_);
};
})(span, value.name || '');
main.dom.levelChooseButtons.appendChild(span);
});
main.createOnChoiceAnimation();
main.importFonts(main.fonts);
main.loadJs('libs', main.loadList, function () {
main.core = core;
for (i = 0; i < main.loadList.length; i++) {
var name = main.loadList[i];
if (name === 'core') continue;
main.core[name] = new window[name]();
}
main.loadFloors(function () {
var coreData = {};
[
'dom',
'statusBar',
'canvas',
'images',
'tilesets',
'materials',
'animates',
'bgms',
'sounds',
'floorIds',
'floors',
'floorPartitions'
].forEach(function (t) {
coreData[t] = main[t];
});
main.core.init(coreData, callback);
main.core.resize();
// 自动放缩最大化
if (!main.replayChecking) {
return;
if (core.getLocalStorage('autoScale') == null) {
core.setLocalStorage('autoScale', true);
}
if (
core.getLocalStorage('autoScale') &&
!core.domStyle.isVertical
) {
try {
if (main.core) {
var index =
main.core.domStyle.availableScale.indexOf(
core.domStyle.scale
);
main.core.control.setDisplayScale(
main.core.domStyle.availableScale.length -
1 -
index
);
if (
!main.core.isPlaying() &&
main.core.flags.enableHDCanvas
) {
main.core.domStyle.ratio = Math.max(
window.devicePixelRatio || 1,
main.core.domStyle.scale
);
main.core.resize();
}
requestAnimationFrame(function () {
var style = getComputedStyle(
main.dom.gameGroup
);
var height = parseFloat(style.height);
if (height > window.innerHeight * 0.95) {
main.core.control.setDisplayScale(-1);
if (
!main.core.isPlaying() &&
main.core.flags.enableHDCanvas
) {
main.core.domStyle.ratio = Math.max(
window.devicePixelRatio || 1,
main.core.domStyle.scale
);
main.core.resize();
}
}
});
}
} catch (e) {
console.error(e);
}
}
}
});
});
});
};
////// 动态加载所有核心JS文件 //////
main.prototype.loadJs = function (dir, loadList, callback) {
// 加载js
main.setMainTipsText('正在加载核心js文件...');
if (this.useCompress) {
main.loadMod(dir, dir, function () {
callback();
});
} else {
var instanceNum = 0;
for (var i = 0; i < loadList.length; i++) {
main.loadMod(dir, loadList[i], function (modName) {
main.setMainTipsText(modName + '.js 加载完毕');
instanceNum++;
if (instanceNum === loadList.length) {
callback();
}
});
}
}
};
////// 加载某一个JS文件 //////
main.prototype.loadMod = function (dir, modName, callback, onerror) {
var script = document.createElement('script');
var name = modName;
script.src =
dir +
'/' +
modName +
(this.useCompress ? '.min' : '') +
'.js?v=' +
this.version;
script.onload = function () {
callback(name);
};
main.dom.body.appendChild(script);
};
////// 动态加载所有楼层(剧本) //////
main.prototype.loadFloors = function (callback) {
// 加载js
main.setMainTipsText('正在加载楼层文件...');
if (this.useCompress) {
// 读取压缩文件
var script = document.createElement('script');
script.src = 'project/floors.min.js?v=' + this.version;
main.dom.body.appendChild(script);
script.onload = function () {
main.dom.mainTips.style.display = 'none';
callback();
};
return;
}
// 高层塔优化
var script = document.createElement('script');
script.src =
'__all_floors__.js?v=' +
this.version +
'&id=' +
main.floorIds.join(',');
script.onload = function () {
main.dom.mainTips.style.display = 'none';
main.supportBunch = true;
callback();
};
script.onerror =
script.onabort =
script.ontimeout =
function (e) {
// console.clear();
for (var i = 0; i < main.floorIds.length; i++) {
main.loadFloor(main.floorIds[i], function (modName) {
main.setMainTipsText(
'楼层 ' + modName + '.js 加载完毕'
);
if (
Object.keys(main.floors).length ===
main.floorIds.length
) {
main.dom.mainTips.style.display = 'none';
callback();
}
});
}
};
main.dom.body.appendChild(script);
};
////// 加载某一个楼层 //////
main.prototype.loadFloor = function (floorId, callback) {
var script = document.createElement('script');
script.src = 'project/floors/' + floorId + '.js?v=' + this.version;
main.dom.body.appendChild(script);
script.onload = function () {
callback(floorId);
};
};
////// 加载过程提示 //////
main.prototype.setMainTipsText = function (text) {
main.dom.mainTips.innerHTML = text;
};
main.prototype.log = function (e, error) {
if (e) {
if (error) return console.error(e);
if (main.core && main.core.platform && !main.core.platform.isPC) {
console.log(e.stack || e.toString());
} else {
console.log(e);
}
}
};
main.prototype.createOnChoiceAnimation = function () {
var borderColor =
main.dom.startButtonGroup.style.caretColor || 'rgb(255, 215, 0)';
// get rgb value
var rgb =
/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*\d+\s*)?\)$/.exec(
borderColor
);
if (rgb != null) {
var value = rgb[1] + ', ' + rgb[2] + ', ' + rgb[3];
var style = document.createElement('style');
style.type = 'text/css';
var keyFrames =
'onChoice { ' +
'0% { border-color: rgba(' +
value +
', 0.9); } ' +
'50% { border-color: rgba(' +
value +
', 0.3); } ' +
'100% { border-color: rgba(' +
value +
', 0.9); } ' +
'}';
style.innerHTML =
'@-webkit-keyframes ' + keyFrames + ' @keyframes ' + keyFrames;
document.body.appendChild(style);
}
};
////// 选项 //////
main.prototype.selectButton = function (index) {
var select = function (children) {
index = (index + children.length) % children.length;
for (var i = 0; i < children.length; ++i) {
children[i].classList.remove('onChoiceAnimate');
}
children[index].classList.add('onChoiceAnimate');
if (main.selectedButton == index) {
children[index].click();
} else {
main.selectedButton = index;
}
};
if (core.dom.startPanel.style.display != 'block') return;
if (main.dom.startButtons.style.display == 'block') {
select(main.dom.startButtons.children);
} else if (main.dom.levelChooseButtons.style.display == 'block') {
select(main.dom.levelChooseButtons.children);
}
};
////// 创建字体 //////
main.prototype.importFonts = function (fonts) {
if (!(fonts instanceof Array) || fonts.length == 0) return;
var style = document.createElement('style');
style.type = 'text/css';
var html = '';
fonts.forEach(function (font) {
html +=
'@font-face { font-family: "' +
font +
'"; src: url("project/fonts/' +
font +
'.ttf") format("truetype"); }';
});
style.innerHTML = html;
document.body.appendChild(style);
};
main.prototype.listen = function () {
////// 窗口大小变化时 //////
window.onresize = function () {
try {
main.core.resize();
} catch (ee) {
console.error(ee);
}
};
////// 在界面上按下某按键时 //////
main.dom.body.onkeydown = function (e) {
if (main.editorOpened) return;
try {
if (main.dom.inputDiv.style.display == 'block') return;
if (
main.core &&
(main.core.isPlaying() || main.core.status.lockControl)
)
main.core.onkeyDown(e);
} catch (ee) {
console.error(ee);
}
};
////// 在界面上放开某按键时 //////
main.dom.body.onkeyup = function (e) {
if (main.editorOpened) return;
try {
if (
main.dom.startPanel.style.display == 'block' &&
(main.dom.startButtons.style.display == 'block' ||
main.dom.levelChooseButtons.style.display == 'block')
) {
if (e.keyCode == 38 || e.keyCode == 33)
// up/pgup
main.selectButton((main.selectedButton || 0) - 1);
else if (e.keyCode == 40 || e.keyCode == 34)
// down/pgdn
main.selectButton((main.selectedButton || 0) + 1);
else if (e.keyCode == 67 || e.keyCode == 13 || e.keyCode == 32)
// C/Enter/Space
main.selectButton(main.selectedButton);
else if (
e.keyCode == 27 &&
main.dom.levelChooseButtons.style.display == 'block'
) {
// ESC
main.core.showStartAnimate(true);
}
e.stopPropagation();
return;
}
if (main.dom.inputDiv.style.display == 'block') {
if (e.keyCode == 13) {
setTimeout(function () {
main.dom.inputYes.click();
}, 50);
} else if (e.keyCode == 27) {
setTimeout(function () {
main.dom.inputNo.click();
}, 50);
}
return;
}
if (
main.core &&
main.core.isPlaying &&
main.core.status &&
(main.core.isPlaying() || main.core.status.lockControl)
)
main.core.onkeyUp(e);
} catch (ee) {
console.error(ee);
}
};
[main.dom.startButtons, main.dom.levelChooseButtons].forEach(function (
dom
) {
dom.onmousemove = function (e) {
for (var i = 0; i < dom.children.length; ++i) {
if (
dom.children[i] == e.target &&
i != (main.selectedButton || 0)
) {
main.selectButton(i);
}
}
};
});
////// 开始选择时 //////
main.dom.body.onselectstart = function () {
return false;
};
////// 鼠标按下时 //////
main.dom.data.onmousedown = function (e) {
try {
e.stopPropagation();
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.ondown(loc);
} catch (ee) {
console.error(ee);
}
};
////// 鼠标移动时 //////
main.dom.data.onmousemove = function (e) {
try {
e.stopPropagation();
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.onmove(loc);
} catch (ee) {
console.error(ee);
}
};
////// 鼠标放开时 //////
main.dom.data.onmouseup = function (e) {
try {
e.stopPropagation();
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.onup(loc);
} catch (ee) {
console.error(ee);
}
};
////// 鼠标滑轮滚动时 //////
main.dom.data.onmousewheel = function (e) {
try {
if (e.wheelDelta) main.core.onmousewheel(Math.sign(e.wheelDelta));
else if (e.detail) main.core.onmousewheel(Math.sign(e.detail));
} catch (ee) {
console.error(ee);
}
};
////// 手指在触摸屏开始触摸时 //////
main.dom.data.ontouchstart = function (e) {
try {
e.preventDefault();
var loc = main.core.actions._getClickLoc(
e.targetTouches[0].clientX,
e.targetTouches[0].clientY
);
if (loc == null) return;
main.lastTouchLoc = loc;
main.core.ondown(loc);
} catch (ee) {
console.error(ee);
}
};
////// 手指在触摸屏上移动时 //////
main.dom.data.ontouchmove = function (e) {
try {
e.preventDefault();
var loc = main.core.actions._getClickLoc(
e.targetTouches[0].clientX,
e.targetTouches[0].clientY
);
if (loc == null) return;
main.lastTouchLoc = loc;
main.core.onmove(loc);
} catch (ee) {
console.error(ee);
}
};
////// 手指离开触摸屏时 //////
main.dom.data.ontouchend = function (e) {
try {
e.preventDefault();
if (main.lastTouchLoc == null) return;
var loc = main.lastTouchLoc;
delete main.lastTouchLoc;
main.core.onup(loc);
} catch (e) {
console.error(e);
}
};
main.dom.statusCanvas.onclick = function (e) {
try {
e.preventDefault();
main.core.onStatusBarClick(e);
} catch (e) {
console.error(e);
}
};
////// 点击状态栏中的怪物手册时 //////
main.statusBar.image.book.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.triggerReplay();
return;
}
if (main.core.isPlaying()) main.core.openBook(true);
};
////// 点击状态栏中的楼层传送器/装备栏时 //////
main.statusBar.image.fly.onclick = function (e) {
e.stopPropagation();
// 播放录像时
if (core.isReplaying()) {
core.stopReplay();
return;
}
if (main.core.isPlaying()) {
if (!main.core.flags.equipboxButton) {
main.core.useFly(true);
} else {
main.core.openEquipbox(true);
}
}
};
////// 点击状态栏中的工具箱时 //////
main.statusBar.image.toolbox.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.rewindReplay();
return;
}
if (main.core.isPlaying()) {
main.core.openToolbox(core.status.event.id != 'equipbox');
}
};
////// 双击状态栏中的工具箱时 //////
main.statusBar.image.toolbox.ondblclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
return;
}
if (main.core.isPlaying()) main.core.openEquipbox(true);
};
////// 点击状态栏中的虚拟键盘时 //////
main.statusBar.image.keyboard.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.control._replay_book();
return;
}
if (main.core.isPlaying()) main.core.openKeyBoard(true);
};
////// 点击状态栏中的快捷商店时 //////
main.statusBar.image.shop.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.control._replay_viewMap();
return;
}
if (main.core.isPlaying()) main.core.openQuickShop(true);
};
////// 点击金币时也可以开启快捷商店 //////
main.statusBar.image.money.onclick = function (e) {
e.stopPropagation();
if (main.core.isPlaying()) main.core.openQuickShop(true);
};
////// 点击楼梯图标也可以浏览地图 //////
main.statusBar.image.floor.onclick = function (e) {
e.stopPropagation();
if (
main.core &&
main.core.isPlaying() &&
!core.isMoving() &&
!core.status.lockControl
) {
core.ui._drawViewMaps();
}
};
////// 点击状态栏中的存档按钮时 //////
main.statusBar.image.save.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.speedDownReplay();
return;
}
if (main.core.isPlaying()) main.core.save(true);
};
////// 点击状态栏中的读档按钮时 //////
main.statusBar.image.load.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.speedUpReplay();
return;
}
if (main.core.isPlaying()) main.core.load(true);
};
////// 点击状态栏中的系统菜单时 //////
main.statusBar.image.settings.onclick = function (e) {
e.stopPropagation();
if (core.isReplaying()) {
core.control._replay_SL();
return;
}
if (main.core.isPlaying()) main.core.openSettings(true);
};
////// 点击工具栏时 //////
main.dom.hard.onclick = function () {
main.core.control.setToolbarButton(!core.domStyle.toolbarBtn);
};
////// 手机端的按钮1-7 //////
main.statusBar.image.btn1.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 49,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn2.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 50,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn3.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 51,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn4.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 52,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn5.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 53,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn6.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 54,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn7.onclick = function (e) {
e.stopPropagation();
main.core.onkeyUp({
keyCode: 55,
altKey: core.getLocalStorage('altKey')
});
};
main.statusBar.image.btn8.onclick = function (e) {
e.stopPropagation();
if (core.getLocalStorage('altKey')) {
core.removeLocalStorage('altKey');
core.drawTip('Alt模式已关闭。');
main.statusBar.image.btn8.style.filter = '';
} else {
core.setLocalStorage('altKey', true);
core.drawTip('Alt模式已开启此模式下1~7按钮视为Alt+1~7。');
main.statusBar.image.btn8.style.filter = 'sepia(1) contrast(1.5)';
}
};
////// 点击“开始游戏”时 //////
main.dom.playGame.onclick = function () {
main.dom.startButtons.style.display = 'none';
main.core.control.checkBgm();
if (main.levelChoose.length == 0) {
core.events.startGame('');
} else {
main.dom.levelChooseButtons.style.display = 'block';
main.selectedButton = null;
main.selectButton(0);
}
};
////// 点击“载入游戏”时 //////
main.dom.loadGame.onclick = function () {
main.core.control.checkBgm();
main.core.load();
};
////// 点击“录像回放”时 //////
main.dom.replayGame.onclick = function () {
main.core.control.checkBgm();
main.core.chooseReplayFile();
};
main.dom.musicBtn.onclick = function () {
try {
if (main.core) main.core.triggerBgm();
} catch (ee) {
console.error(ee);
}
};
window.onblur = function () {
if (main.core && main.core.control) {
try {
main.core.control.checkAutosave();
} catch (e) {}
}
};
main.dom.inputYes.onclick = function () {
main.dom.inputDiv.style.display = 'none';
var func = core.platform.successCallback;
core.platform.successCallback = core.platform.errorCallback = null;
if (func) func(main.dom.inputBox.value);
};
main.dom.inputNo.onclick = function () {
main.dom.inputDiv.style.display = 'none';
var func = core.platform.errorCallback;
core.platform.successCallback = core.platform.errorCallback = null;
if (func) func(null);
};
}; //listen end
var main = new main();

3052
runtime.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

1
runtime.min.d.ts vendored Normal file

File diff suppressed because one or more lines are too long

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();
});

243
server.py Normal file
View File

@ -0,0 +1,243 @@
# -*- coding: utf-8 -*-
# HTML5魔塔样板启动服务Python版
# 需要安装Python环境并 pip install flask 安装Flask库
# 运行方式python server.py 或 python3 server.py
import sys
import json
import os
import shutil
import base64
isPy3 = sys.version_info > (3, 0)
def p(s): # s is unicode in py2 and str in py3
if isPy3: print(s)
else: print(s.decode('utf-8'))
p("")
try:
from flask import Flask, request, Response, abort
import mimetypes
import socket
except:
p("需要flask才可使用本服务。\n安装方式:%s install flask" % ("pip3" if isPy3 else "pip"))
exit(1)
app = Flask(__name__, static_folder='__static__')
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
@app.after_request
def add_header(r):
r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
r.headers["Pragma"] = "no-cache"
r.headers["Expires"] = "0"
r.headers['Cache-Control'] = 'public, max-age=0'
return r
def is_sub(filename):
try:
return (os.path.realpath(filename) + os.sep).startswith(os.path.realpath(".") + os.sep)
except:
return True
def get_mimetype(path):
return mimetypes.guess_type(path)[0] or 'application/octet-stream'
def get_file(path):
if not os.path.isfile(path):
if path.startswith('_saves/'):
return ''
abort(404)
return None
if not is_sub(path):
abort(403)
with open(path, 'rb') as f:
content = f.read() # str in py2 and bytes in py3
return content
@app.route('/', methods=['GET'])
def root():
return static_file('index.html')
@app.route('/__all_floors__.js', methods=['GET'])
def all_floors():
ids = request.args.get('id', '').split(',')
if len(ids) == 0:
abort(404)
return None
content = []
for id in ids:
v = get_file('project/floors/%s.js' % id)
if isPy3: v = str(v, encoding = 'utf-8')
content.append(v)
return Response('\n'.join(content), mimetype = 'text/javascript')
@app.route('/__all_animates__', methods=['GET'])
def all_animates():
ids = request.args.get('id', '').split(',')
if len(ids) == 0:
abort(404)
return None
content = []
for id in ids:
animate = 'project/animates/%s.animate' % id
if os.path.exists(animate):
v = get_file(animate)
if isPy3: v = str(v, encoding = 'utf-8')
content.append(v)
else: content.append('')
return '@@@~~~###~~~@@@'.join(content)
@app.route('/favicon.ico', methods=['GET'])
def favicon():
return ''
@app.route('/<path:path>', methods=['GET'])
def static_file(path):
if os.path.isdir(path):
if not path.endswith('/'): path += '/'
path += 'index.html'
if not os.path.isfile(path):
abort(404)
return None
mimetype = get_mimetype(path)
response = Response(get_file(path), mimetype = mimetype)
if mimetype.startswith('audio/'): response.headers["Accept-Ranges"] = "bytes"
return response
def process_request():
data = request.get_data() # str in py2 and bytes in py3
if isPy3: data = str(data, encoding = 'utf-8')
params = data.split("&")
d = {}
for one in params:
index = one.find("=")
if index >= 0:
d[one[:index]] = one[index+1:]
return d # str in py2 & py3
@app.route('/readFile', methods=['POST'])
def readFile():
data = process_request()
tp = data.get('type', 'base64')
filename = data.get('name', None)
content = get_file(filename)
return content if tp == 'utf8' or content is None else base64.b64encode(content)
@app.route('/writeFile', methods=['POST'])
def writeFile():
data = process_request()
tp = data.get('type', 'base64')
filename = data.get('name', None)
if not is_sub(filename):
abort(403)
return
value = data.get('value', '')
if isPy3: value = value.encode('utf-8')
if tp == 'base64': value = base64.b64decode(value)
with open(filename, 'wb') as f:
f.write(value) # str in py2 and bytes in py3
return str(len(value))
@app.route('/writeMultiFiles', methods=['POST'])
def writeMultiFiles():
data = process_request()
filenames = data.get('name', '').split(';')
values = data.get('value', '').split(';')
l = 0
for i in range(len(filenames)):
if i >= len(values):
break
filename = filenames[i]
value = values[i].encode('utf-8') if isPy3 else values[i]
value = base64.b64decode(value)
if not is_sub(filename):
abort(403)
return
with open(filename, 'wb') as f:
f.write(value)
l += len(value)
return str(l)
@app.route('/listFile', methods=['POST'])
def listFile():
data = process_request()
filename = data.get('name', None)
if filename is None or not os.path.isdir(filename):
abort(404)
return
if not is_sub(filename):
abort(403)
return
files = [f
for f in os.listdir(filename)
if os.path.isfile(os.path.join(filename, f))]
return "[" + ", ".join(['"'+f+'"' for f in files]) + "]"
@app.route('/makeDir', methods=['POST'])
def makeDir():
data = process_request()
filename = data.get('name', None)
if filename is None or not is_sub(filename):
abort(403)
return
if not os.path.exists(filename):
os.makedirs(filename)
return 'Success'
@app.route('/moveFile', methods=['POST'])
def moveFile():
data = process_request()
src = data.get('src', None)
dest = data.get('dest', None)
if src is None or dest is None or not is_sub(src) or not is_sub(dest):
abort(403)
return
if not os.path.exists(src):
abort(404)
return
if src == dest:
return 'Success'
if os.path.exists(dest):
os.remove(dest)
os.rename(src, dest)
return 'Success'
@app.route('/deleteFile', methods=['POST'])
def deleteFile():
data = process_request()
name = data.get('name', None)
if name is None or not is_sub(name):
abort(403)
return
if os.path.isfile(name):
os.remove(name)
elif os.path.isdir(name):
shutil.rmtree(name)
return 'Success'
@app.route('/games/upload.php', methods=['POST'])
def upload():
return ''
def port_used(port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = True
try:
sock.bind(("0.0.0.0", port))
result = False
except:
pass
sock.close()
return result
if __name__ == '__main__':
port = 1055
while port_used(port):
port += 1
if port > 1055:
p("默认的1055端口已被占用自动选择%d端口。请注意,不同端口下的存档等信息都是不共用的。\n" % port)
p("服务已启动...\n游戏地址http://127.0.0.1:%d/\n编辑器地址http://127.0.0.1:%d/editor.html\n" % (port, port))
app.run(host = '0.0.0.0', port = port, debug = False)

70
stars.css Normal file

File diff suppressed because one or more lines are too long

507
styles.css Normal file
View File

@ -0,0 +1,507 @@
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #000;
overflow: hidden;
}
#gameGroup {
position: absolute;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
background-color: #000;
}
#mainTips {
color: #fff;
font-size: 0.8em;
position: fixed;
top: 10px;
left: 10px;
z-index: 370;
}
#musicBtn {
position: fixed;
bottom: 3px;
right: 3px;
cursor: pointer;
z-index: 400;
display: none;
}
#startPanel {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: #fff;
overflow: hidden;
z-index: 300;
}
#startTop {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: #000000;
z-index: 350;
}
#startTopProgressBar {
width: 90%;
height: 5%;
margin: 0 5%;
position: absolute;
top: 5%;
background-color: #fff;
z-index: 15;
}
#startTopProgress {
width: 0%;
height: 100%;
background-color: #666;
}
#startTopLoadTips {
color: #fff;
font-size: 0.6em;
position: absolute;
top: 10%;
left: 5%;
z-index: 15;
}
#startTopHint {
color: #66CCFF;
position: absolute;
bottom: 0;
left: 5%;
z-index: 15;
font-size: 1.1em;
}
#startBackground {
position:absolute;
top:50%;
left:50%;
height: 100%;
width: auto;
transform:translate(-50%,-50%);
z-index: 260;
}
#startLogo {
user-select: none;
position: absolute;
z-index: 290;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
margin-top: 8%;
max-width: 100%;
text-align: center;
font: bold 4em STXingkai;
}
#startTitle {
user-select: none;
position: absolute;
z-index: 280;
}
#startButtonGroup {
width: auto;
position: absolute;
text-align: center;
font-size: 1.4em;
display: none;
z-index: 310;
bottom: 0;
margin-bottom: 5%;
left: 50%;
transform: translateX(-50%);
padding: 15px 25px;
min-width: 20%;
/* default value */
background-color: #32369F;
opacity: 0.85;
color: #FFFFFF;
border: #FFFFFF 2px solid;
caret-color: #FFD700;
border-radius: 10px;
}
#startButtons {
display: block;
}
#levelChooseButtons {
display: none;
}
.startButton {
width: 100%;
margin: 0;
font-weight: bold;
display: block;
cursor: pointer;
padding: 4px 0;
border-color: transparent;
border-width: 2px;
border-style: solid;
border-radius: 6px;
}
.onChoiceAnimate {
animation: onChoice 2s ease-in-out 0s infinite normal none running;
}
#floorMsgGroup {
top: 3px;
right: 3px;
position: absolute;
text-align: center;
display: none;
color: #fff;
background-color: #000;
z-index: 230;
}
#logoLabel {
margin-top: 8%;
font: bold 3em STXingkai;
margin-left: auto;
margin-right: auto;
}
#versionLabel {
margin-top: -3%;
font-size: 1.2em;
font-weight: bold;
}
#floorNameLabel {
margin-top: 30px;
font-size: 1.6em;
font-weight: bold;
}
#statusBar {
position: absolute;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
background: url(project/materials/ground.png) repeat;
z-index: 185;
display: none;
top: 0;
left: 0;
padding: 3px;
}
#statusBar .status{
user-select: none;
position: relative;
display: block;
float: left;
width: 100%;
}
.status img{
vertical-align: middle;
width: auto;
height: 100%;
max-height: 1.6em;
}
#statusBar span{
user-select: none;
font: bold italic 1.1em Verdana;
display: inline;
}
#statusBar p {
user-select: none;
display: inline-block;
vertical-align: middle;
width: 60%;
margin: 0;
color: white;
font: bold italic 1.1em Verdana;
white-space: nowrap;
}
#toolBar {
position: absolute;
background: url(project/materials/ground.png) repeat;
z-index: 210;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
display: none;
padding: 3px;
}
#toolBar .tools{
position: relative;
display: block;
float: left;
}
p#hard {
user-select: none;
width: 6em;
vertical-align: middle;
display: inline-block;
color: red;
font: bold normal 1.1em "Arial Black";
text-align: center;
margin: 0 6px 6px 0;
word-break: keep-all;
}
span#poison, span#weak, span#curse, span#pickaxe, span#bomb, span#fly {
user-select: none;
font-style: normal;
font-size: 1em;
}
p#name {
font-style: normal;
}
.gameCanvas {
position: absolute;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
#gif {
z-index: 20;
position: absolute;
overflow: hidden;
}
#gif2 {
z-index: 90;
position: absolute;
overflow: hidden;
}
#gameDraw {
position: absolute;
background: #000000;
overflow: hidden;
z-index: 185;
}
#bg {
z-index: 10;
}
#event {
z-index: 30;
}
#hero {
z-index: 40;
}
#event2 {
z-index: 50;
}
#fg {
z-index: 60;
}
#damage {
z-index: 65;
}
#animate {
z-index: 70;
}
#curtain {
z-index: 125;
}
#ui {
z-index: 140;
}
#data {
z-index: 170;
}
#inputDiv {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background: rgba(127,127,127,0.6);
z-index: 2000
}
#inputDialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -55%);
background: white;
width: 250px;
min-height: 50px;
}
#inputMessage {
word-wrap: break-word;
text-align: left;
margin-left: 8%;
margin-right: 5%;
}
#inputBox {
margin-left: 8%;
width: 80%;
margin-bottom: 10px;
padding: 5px 3px;
border: 1px solid;
background: #F0F0F0;
}
#inputYes {
margin-bottom: 15px;
margin-left: 8%;
}
#inputNo {
float:right;
margin-right: 10%;
}
#_selector, ._uievent_selector {
animation: selector 2s ease-in-out 0s infinite normal none running;
}
@-webkit-keyframes selector {
0% { opacity: 0.95; }
50% { opacity: 0.55; }
100% { opacity: 0.95; }
}
@keyframes selector {
0% { opacity: 0.95; }
50% { opacity: 0.55; }
100% { opacity: 0.95; }
}
#next {
width: 5px;
height: 5px;
display: none;
position: absolute;
transform: rotate(45deg);
border-bottom-width: 4px;
border-bottom-style: solid;
border-right-width: 4px;
border-right-style: solid;
-webkit-animation: next .5s ease-in-out alternate infinite;
animation: next .5s ease-in-out alternate infinite;
left: 0;
top: 0;
opacity: 0.7;
z-index: 169;
}
@-webkit-keyframes next {
100% {
transform: rotate(45deg) translate(-3px, -3px);
}
}
@keyframes next {
100% {
transform: rotate(45deg) translate(-3px, -3px);
}
}
#startImageBackgroundDiv {
display: none;
width: 100%;
height: 100%;
position: fixed;
z-index: 10000;
}
#startImageDiv {
width: 100%;
height: 100%;
position: fixed;
background: black;
opacity: 1;
}
#startImageLogo {
opacity: 0;
max-width: 60%;
max-height: 60%;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.startImageAnimation {
-webkit-animation: startImage 4s ease-in-out 1s alternate 1;
animation: startImage 4s ease-in-out 1s alternate 1;
}
@-webkit-keyframes startImage {
0% { opacity: 0; }
60% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes startImage {
0% { opacity: 0; }
60% { opacity: 1; }
100% { opacity: 0; }
}
.startImageDivAnimation {
-webkit-animation: startImageDivDisappear 2s ease-in-out 5s alternate 1;
animation: startImageDivDisappear 2s ease-in-out 5s alternate 1;
}
@-webkit-keyframes startImageDivDisappear {
0% { opacity: 1 }
100% { opacity: 0 }
}
@keyframes startImageDivDisappear {
0% { opacity: 1 }
100% { opacity: 0 }
}
#ui-editor {
z-index: 9999;
position: absolute;
overflow: visible;
height: 100%;
}
@font-face {
font-family: Fira Code;
src: url(../src/fonts/FiraCode-Regular.ttf);
}
/* 注释下面这三行以开启抗锯齿 */
.anti-aliasing {
image-rendering: pixelated;
}

BIN
启动服务.exe Normal file

Binary file not shown.