index.html 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Your Electron App</title>
  5. <style>
  6. #nav {
  7. /* 导航栏样式 */
  8. }
  9. #content {
  10. /* 内容区样式 */
  11. }
  12. #level-list {
  13. width: 200px;
  14. float: left;
  15. }
  16. #level-details {
  17. margin-left: 210px;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <div id="hdr" style="background-color: antiquewhite;">
  23. <input type="text" id="group-suffix" placeholder="分组文件的后缀">
  24. <button id="load_grp">加载分组信息</button>
  25. </div>
  26. <div id="panels-area" style="display: flex; height: 95vh;">
  27. <div id="nav" style="overflow-y: scroll; height: 90%; width: 5%; background-color: coral;">
  28. <ul>
  29. <li>
  30. <button id="levels">关卡</button>
  31. </li>
  32. <li>
  33. <button id="examples">实例</button>
  34. </li>
  35. <li>
  36. <button id="templates">模板</button>
  37. </li>
  38. </ul>
  39. </div>
  40. <div id="content" style="overflow-y: scroll; height: 90%; width: 85%; background-color: beige;">
  41. <!-- 动态内容 -->
  42. </div>
  43. </div>
  44. <script>
  45. var levels = null;
  46. var instances = null;
  47. var templateFNs = null;
  48. var templateFNsByDir = null;
  49. function getInstanceByName(name) {
  50. for (let i = 0; i < instances.length; i++) {
  51. if (instances[i].lvinst == name) {
  52. return instances[i];
  53. }
  54. }
  55. return null;
  56. }
  57. function getTemplateByName(name) {
  58. for (let i = 0; i < templateFNs.length; i++) {
  59. if (templateFNs[i] == name) {
  60. return templateFNs[i];
  61. }
  62. }
  63. return null;
  64. }
  65. function getAllLvIdsByInstName(name) {
  66. // 返回所有用到了该实例的关卡的id
  67. const ids = [];
  68. for (let i = 0; i < levels.length; i++) {
  69. if (levels[i].lvinst == name) {
  70. ids.push(levels[i].lvid);
  71. }
  72. }
  73. return ids;
  74. }
  75. function saveInst(div, inst) {
  76. // 从 div中获取实例信息
  77. const instName = div.querySelector('#instid-in-inst-detail').textContent.split(':')[1].trim();
  78. var inst = null;
  79. for (let i = 0; i < instances.length; i++) {
  80. if (instances[i].lvinst == instName) {
  81. inst = instances[i];
  82. break;
  83. }
  84. }
  85. if (inst == null) {
  86. alert('实例不存在');
  87. return;
  88. }
  89. inst['template'] = div.querySelector('#template-select').value;
  90. for (let i = 1; i <= 10; i++) {
  91. const tileCnt = div.querySelector('#try' + i + '_tileCnt').value;
  92. const seed = div.querySelector('#try' + i + '_seed').value;
  93. const param_template = div.querySelector('#try' + i + '_param_template').value;
  94. const trynParams = tileCnt + '|' + seed + '|' + param_template;
  95. inst['try' + i + '(tileCnt|seed|param_template)'] = trynParams;
  96. }
  97. window.electron.send('save2csv', '../../conf/levelInstInfo.csv', instances);
  98. }
  99. function showInstanceInfoAt(div, inst) {
  100. // 将其显示在页面上,其中template应该是一个下拉框
  101. // try1-10应该是一个表格, 表格的头部应该是:尝试次数,瓦片数,种子,参数模板,表格里面的每个值是可以编辑的
  102. div.innerHTML = `
  103. <h2 id='instid-in-inst-detail'>实例名: ${inst.lvinst}</h2>
  104. `;
  105. // 显示所有用到了该实例的关卡
  106. const lvIds = getAllLvIdsByInstName(inst.lvinst);
  107. div.innerHTML += `
  108. <p style="display: inline;">用到了该实例的关卡: ${lvIds}</p>
  109. `;
  110. // 加入 template 下拉框
  111. {
  112. var div1 = document.createElement('div');
  113. const p = document.createElement('p');
  114. p.style.display = 'inline';
  115. p.appendChild(document.createTextNode('模板名称: '));
  116. div1.appendChild(p);
  117. const templateSelect = document.createElement('select');
  118. templateSelect.id = 'template-select';
  119. templateFNs.forEach(fn => {
  120. const option = document.createElement('option');
  121. option.value = fn;
  122. option.textContent = fn;
  123. if (fn == inst.template) {
  124. option.selected = true;
  125. }
  126. templateSelect.appendChild(option);
  127. });
  128. div1.appendChild(templateSelect);
  129. // 增加一个编辑模板的按钮
  130. const editTemplateButton = document.createElement('button');
  131. editTemplateButton.id = 'edit-template-button';
  132. editTemplateButton.textContent = '编辑模板';
  133. editTemplateButton.style.marginLeft = '10px';
  134. editTemplateButton.addEventListener('click', () => {
  135. // 判断模板是在哪个目录下
  136. for (let dir in templateFNsByDir) {
  137. if (templateFNsByDir[dir].has(inst.template)) {
  138. window.electron.send('open-app', '/Applications/Tiled.app/Contents/MacOS/Tiled ' + dir + '/' + inst.template + '.json');
  139. return;
  140. }
  141. }
  142. });
  143. div1.appendChild(editTemplateButton);
  144. // 增加一个复制模板的按钮
  145. const copyTemplateButton = document.createElement('button');
  146. copyTemplateButton.id = 'copy-template-button';
  147. copyTemplateButton.textContent = '复制模板';
  148. copyTemplateButton.style.marginLeft = '10px';
  149. copyTemplateButton.addEventListener('click', () => {
  150. // 复制模板
  151. var path = "";
  152. for (let dir in templateFNsByDir) {
  153. if (templateFNsByDir[dir].has(inst.template)) {
  154. path = dir + '/' + inst.template + '.json';
  155. break;
  156. }
  157. }
  158. window.electron.send('copy-template', path);
  159. window.electron.receive('copy-template-replay', (data) => {
  160. if (data.length == "0") {
  161. // 失败了
  162. } else {
  163. // 得到文件名和路径前缀
  164. const arr = data.split('/');
  165. const filename = arr[arr.length - 1];
  166. const prefix = arr.slice(0, arr.length - 1).join('/');
  167. // 放入到templateFNsByDir中
  168. templateName = filename.split(".")[0];
  169. if (templateFNsByDir[prefix] == null) {
  170. templateFNsByDir[prefix] = new Set();
  171. }
  172. templateFNsByDir[prefix].add(templateName);
  173. // 放入到templateFNs中
  174. templateFNs.push(templateName);
  175. // 设置为新的模板
  176. inst.template = templateName;
  177. // 刷新页面,增加templateSelect的选项,并选中新的,同时取消旧的选中状态
  178. const option = document.createElement('option');
  179. option.value = templateName;
  180. option.textContent = templateName;
  181. option.selected = true;
  182. templateSelect.appendChild(option);
  183. }
  184. });
  185. });
  186. div1.appendChild(copyTemplateButton);
  187. div.appendChild(div1);
  188. }
  189. // 加入表格
  190. const table = document.createElement('table');
  191. const thead = document.createElement('thead');
  192. const tbody = document.createElement('tbody');
  193. const tr = document.createElement('tr');
  194. const th1 = document.createElement('th');
  195. const th2 = document.createElement('th');
  196. const th3 = document.createElement('th');
  197. const th4 = document.createElement('th');
  198. th1.textContent = '尝试次数';
  199. th2.textContent = '瓦片数';
  200. th3.textContent = '种子';
  201. th4.textContent = '参数模板';
  202. tr.appendChild(th1);
  203. tr.appendChild(th2);
  204. tr.appendChild(th3);
  205. tr.appendChild(th4);
  206. thead.appendChild(tr);
  207. table.appendChild(thead);
  208. table.appendChild(tbody);
  209. div.appendChild(table);
  210. // 加入表格内容
  211. for (let i = 1; i <= 10; i++) {
  212. var trynParams = inst['try' + i + '(tileCnt|seed|param_template)'].split('|');
  213. const tr = document.createElement('tr');
  214. const td1 = document.createElement('td');
  215. const td2 = document.createElement('td');
  216. const td3 = document.createElement('td');
  217. const td4 = document.createElement('td');
  218. const input1 = document.createElement('input');
  219. const input2 = document.createElement('input');
  220. const input3 = document.createElement('input');
  221. const input4 = document.createElement('input');
  222. input1.type = 'text';
  223. input1.value = trynParams[0];
  224. input1.id = 'try' + i + '_tileCnt';
  225. input2.type = 'text';
  226. input2.value = trynParams[1];
  227. input2.id = 'try' + i + '_seed';
  228. input3.type = 'text';
  229. input3.value = trynParams[2];
  230. input3.id = 'try' + i + '_param_template';
  231. td1.textContent = i;
  232. td2.appendChild(input1);
  233. td3.appendChild(input2);
  234. td4.appendChild(input3);
  235. tr.appendChild(td1);
  236. tr.appendChild(td2);
  237. tr.appendChild(td3);
  238. tr.appendChild(td4);
  239. tbody.appendChild(tr);
  240. }
  241. // 增加一个按钮,点击后可以保存
  242. const saveButton = document.createElement('button');
  243. saveButton.id = 'save-instance-button';
  244. saveButton.textContent = '保存';
  245. div.appendChild(saveButton);
  246. saveButton.addEventListener('click', () => {
  247. // 保存实例信息
  248. saveInst(div, inst);
  249. });
  250. }
  251. function showTemplateInfoAt(div, templateInfo) {
  252. }
  253. // 过滤实例列表
  254. function filterInstance() {
  255. // 获取输入框的值
  256. var filter = document.getElementById('instance-filter').value;
  257. // 获取实例列表
  258. var list = document.getElementById('instance-list').getElementsByTagName('li');
  259. // 遍历实例列表
  260. for (var i = 0; i < list.length; i++) {
  261. // 获取实例的名字
  262. var name = list[i].innerHTML;
  263. // 如果实例的名字包含输入框的值,显示实例
  264. if (name.indexOf(filter) > -1) {
  265. list[i].style.display = '';
  266. } else {
  267. // 否则隐藏实例
  268. list[i].style.display = 'none';
  269. }
  270. }
  271. }
  272. function initData(suffix) {
  273. // 读取关卡信息并填充到页面
  274. if (levels == null) {
  275. window.electron.send('parse-csv', '../../conf/levelInfo'+suffix+'.csv')
  276. window.electron.receive('csv-data', (data) => {
  277. if (levels == null) {
  278. levels = data;
  279. window.electron.send('parse-csv', '../../conf/levelInstInfo'+suffix+'.csv')
  280. } else {
  281. if (instances == null) {
  282. instances = data
  283. }
  284. }
  285. });
  286. }
  287. if (templateFNs == null) {
  288. templateFNs = [];
  289. templateFNsByDir = {};
  290. window.electron.send('get-file-list', '../../tf_templates;../../miniGame')
  291. window.electron.receive('file-list-error', (data) => {
  292. console.error('Error reading directory: ', err);
  293. });
  294. window.electron.receive('file-list-data', (data, path) => {
  295. templateFNsByDir[path] = new Set();
  296. // 过滤掉非 .json 后缀的文件
  297. for (let i = 0; i < data.length; i++) {
  298. if (data[i].endsWith('.json')) {
  299. // 去掉后缀
  300. const name = data[i].substring(0, data[i].length - 5);
  301. templateFNs.push(name);
  302. templateFNsByDir[path].add(name);
  303. }
  304. }
  305. });
  306. }
  307. //
  308. }
  309. // 创建一个新实例,并将其关联到指定的关卡中
  310. function addNewInst(level) {
  311. const instDetails = document.getElementById('instance-details');
  312. // 获取实例名称
  313. window.electron.send('open-newinst-prompt', 'some data');
  314. }
  315. function saveLvInfo() {
  316. const lvDetails = document.getElementById('level-details');
  317. // 保存关卡的详细信息
  318. const lvid = lvDetails.querySelector('#lvid-in-lvdetails').textContent.split(':')[1].trim();
  319. const instid = lvDetails.querySelector('#inst-select').value;
  320. const type = lvDetails.querySelector('#type-select-in-lvdetails').value;
  321. const ddaType = lvDetails.querySelector('#ddatype-in-lvdetails').textContent.split(':')[1].trim();
  322. const ddaPara = lvDetails.querySelector('#ddapara-in-lvdetails').textContent.split(':')[1].trim();
  323. var lv = null;
  324. for (let i = 0; i < levels.length; i++) {
  325. if (levels[i].lvid == lvid) {
  326. lv = levels[i];
  327. break;
  328. }
  329. }
  330. lv['lvinst'] = instid;
  331. lv['type(normal=0,hard=1,superHard=2)'] = type;
  332. lv['dda_type'] = ddaType;
  333. lv['dda_para'] = ddaPara;
  334. // 保存到文件
  335. window.electron.send('save2csv', '../../conf/levelInfo.csv', levels);
  336. }
  337. function fillLvInfo() {
  338. // 读取关卡信息并填充到页面
  339. if (levels != null) {
  340. const levelList = document.getElementById('level-list');
  341. const levelDetails = document.getElementById('level-details');
  342. const instDetails = document.getElementById('instance-details');
  343. const templateDetails = document.getElementById('tempalte-details');
  344. // 先清空
  345. levelList.innerHTML = '';
  346. // 将levels里面的信息填充到页面
  347. levels.forEach(level => {
  348. const listItem = document.createElement('li');
  349. listItem.textContent = level.lvid;
  350. listItem.addEventListener('click', () => {
  351. // 将选中的关卡id条目的背景颜色设置为蓝色
  352. Array.from(levelList.children).forEach(node => {
  353. if (node.textContent == level.lvid) {
  354. node.style.backgroundColor = 'lightblue';
  355. } else {
  356. node.style.backgroundColor = 'white';
  357. }
  358. });
  359. levelDetails.innerHTML = `
  360. <h2 id='lvid-in-lvdetails'>关卡ID: ${level.lvid}</h2>
  361. <p id='instid-in-lvdetails'>关卡实例名称: ${level.lvinst}</p>`
  362. //
  363. if (level.lvinst == '') {
  364. // 实例不存在,
  365. // 显示一个下来列表,选择实例
  366. const instSelect = document.createElement('select');
  367. instSelect.id = 'inst-select';
  368. instances.forEach(inst => {
  369. const option = document.createElement('option');
  370. option.value = inst.lvinst;
  371. option.textContent = inst.lvinst;
  372. instSelect.appendChild(option);
  373. });
  374. levelDetails.appendChild(instSelect);
  375. }
  376. // 加入一个按钮,点击后可以创建实例
  377. {
  378. const div = document.createElement('div');
  379. const createInstButton = document.createElement('button');
  380. createInstButton.id = 'create-instance-button';
  381. createInstButton.textContent = '创建新的实例';
  382. div.appendChild(createInstButton);
  383. levelDetails.appendChild(div);
  384. // createInstButton.addEventListener('click', () => {
  385. // addNewInst(level);
  386. // });
  387. console.log(createInstButton.onclick);
  388. }
  389. {
  390. const div = document.createElement('div');
  391. // 增加一个下拉框,选择类型:normal,hard,super-hard
  392. const type = level['type(normal=0,hard=1,superHard=2)'];
  393. const typeSelect = document.createElement('select');
  394. typeSelect.id = 'type-select-in-lvdetails';
  395. const normalOption = document.createElement('option');
  396. normalOption.value = '0';
  397. normalOption.textContent = 'normal';
  398. normalOption.selected = type == '0';
  399. const hardOption = document.createElement('option');
  400. hardOption.value = '1';
  401. hardOption.textContent = 'hard';
  402. hardOption.selected = type == '1';
  403. const superHardOption = document.createElement('option');
  404. superHardOption.value = '2';
  405. superHardOption.textContent = 'super-hard';
  406. superHardOption.selected = type == '2';
  407. typeSelect.appendChild(normalOption);
  408. typeSelect.appendChild(hardOption);
  409. typeSelect.appendChild(superHardOption);
  410. const p = document.createElement('p');
  411. p.style.display = 'inline';
  412. p.appendChild(document.createTextNode('关卡难度级别:'));
  413. div.appendChild(p);
  414. div.appendChild(typeSelect);
  415. levelDetails.appendChild(div);
  416. }
  417. // 显示dda类型和参数
  418. const ddatype = document.createElement('p');
  419. ddatype.id = 'ddatype-in-lvdetails';
  420. ddatype.textContent = 'DDA类型: ' + level.dda_type;
  421. levelDetails.appendChild(ddatype);
  422. const ddapara = document.createElement('p');
  423. ddapara.id = 'ddapara-in-lvdetails';
  424. ddapara.textContent = 'DDA参数: ' + level.dda_para;
  425. levelDetails.appendChild(ddapara);
  426. // 增加一个按钮,点击后可以保存
  427. const saveButton = document.createElement('button');
  428. saveButton.id = 'save-lv-button';
  429. saveButton.textContent = '保存';
  430. levelDetails.appendChild(saveButton);
  431. levelDetails.addEventListener('click', (event) => {
  432. if (event.target.id === 'create-instance-button') {
  433. addNewInst(level);
  434. } else if (event.target.id === 'save-lv-button') {
  435. saveLvInfo();
  436. }
  437. });
  438. // 显示实例信息
  439. const inst = getInstanceByName(level.lvinst);
  440. if (inst != null) {
  441. showInstanceInfoAt(instDetails, inst);
  442. // 显示模板信息
  443. const template = getTemplateByName(inst.template);
  444. if (template != null) {
  445. showTemplateInfoAt(templateDetails, getTemplateByName(inst.template));
  446. }
  447. } else {
  448. instDetails.innerHTML = `
  449. <h2>实例名: Error:未定义</h2>
  450. `
  451. }
  452. });
  453. levelList.appendChild(listItem);
  454. });
  455. }
  456. }
  457. function fillInstanceInfo() {
  458. // 读取关卡信息并填充到页面
  459. if (instances != null) {
  460. const lst = document.getElementById('instance-list');
  461. const details = document.getElementById('instance-details');
  462. // 将levels里面的信息填充到页面
  463. instances.forEach(inst => {
  464. const listItem = document.createElement('li');
  465. listItem.textContent = inst.lvinst;
  466. listItem.addEventListener('click', () => {
  467. showInstanceInfoAt(details, inst)
  468. });
  469. lst.appendChild(listItem);
  470. });
  471. }
  472. }
  473. function fillTemplateInfo() {
  474. if (templateFNs != null) {
  475. const lst = document.getElementById('template-list');
  476. const details = document.getElementById('template-details');
  477. // 将levels里面的信息填充到页面
  478. templateFNs.forEach(fn => {
  479. const listItem = document.createElement('li');
  480. listItem.textContent = fn;
  481. listItem.addEventListener('click', () => {
  482. // 放入截图
  483. });
  484. lst.appendChild(listItem);
  485. });
  486. }
  487. }
  488. // 处理新增关卡的回复
  489. window.electron.receive('prompt-newlv-reply', (event, arg) => {
  490. const lvid = event;
  491. // 检查是否已经存在
  492. for (let i = 0; i < levels.length; i++) {
  493. if (levels[i].lvid == lvid) {
  494. alert('关卡id已经存在');
  495. return;
  496. }
  497. }
  498. // 不存在则加入
  499. const level = {
  500. lvid: lvid,
  501. lvinst: '',
  502. type: 0,
  503. dda_type: '0',
  504. dda_para: '0|10000'
  505. };
  506. levels.push(level);
  507. // 刷新页面
  508. fillLvInfo();
  509. // 选中该关卡
  510. const levelList = document.getElementById('level-list');
  511. levelList.childNodes.forEach(node => {
  512. if (node.textContent == lvid) {
  513. node.click();
  514. }
  515. });
  516. })
  517. // 处理新增实例的回复
  518. window.electron.receive('prompt-newinst-reply', (event, arg) => {
  519. const instName = event;
  520. // 检查是否已经存在
  521. for (let i = 0; i < instances.length; i++) {
  522. if (instances[i].lvinst == instName) {
  523. alert('实例已经存在');
  524. return;
  525. }
  526. }
  527. // 不存在则加入
  528. const inst = {
  529. lvinst: instName,
  530. template: '',
  531. 'try1(tileCnt|seed|param_template)': '0|0|normal',
  532. 'try2(tileCnt|seed|param_template)': '0|0|normal',
  533. 'try3(tileCnt|seed|param_template)': '0|0|normal',
  534. 'try4(tileCnt|seed|param_template)': '0|0|normal',
  535. 'try5(tileCnt|seed|param_template)': '0|0|normal',
  536. 'try6(tileCnt|seed|param_template)': '0|0|normal',
  537. 'try7(tileCnt|seed|param_template)': '0|0|normal',
  538. 'try8(tileCnt|seed|param_template)': '0|0|normal',
  539. 'try9(tileCnt|seed|param_template)': '0|0|normal',
  540. 'try10(tileCnt|seed|param_template)': '0|0|normal'
  541. };
  542. instances.push(inst);
  543. const instDetails = document.getElementById('instance-details');
  544. showInstanceInfoAt(instDetails, inst);
  545. // 将当前关卡关联到该实例
  546. const lvid = document.getElementById('lvid-in-lvdetails').textContent.split(':')[1].trim();
  547. for (let i = 0; i < levels.length; i++) {
  548. if (levels[i].lvid == lvid) {
  549. levels[i].lvinst = instName;
  550. break;
  551. }
  552. }
  553. // 刷新页面
  554. const instId = document.getElementById('instid-in-lvdetails');
  555. instId.textContent = '关卡实例名称: ' + instName;
  556. });
  557. document.getElementById('load_grp').addEventListener('click', function() {
  558. const suffix = document.getElementById('group-suffix').value;
  559. // 初始化数据
  560. if (suffix == '') {
  561. initData("");
  562. } else {
  563. initData("-"+suffix);
  564. }
  565. });
  566. document.getElementById('levels').addEventListener('click', function() {
  567. fetch('level.html')
  568. .then(response => response.text())
  569. .then(data => {
  570. document.getElementById('content').innerHTML = data;
  571. fillLvInfo();
  572. // 绑定新增关卡按钮
  573. document.getElementById('add-level-button').addEventListener('click', function() {
  574. window.electron.send('open-newlv-prompt', 'some data')
  575. });
  576. })
  577. .catch(error => {
  578. console.error('Error loading the levels content:', error);
  579. });
  580. });
  581. document.getElementById('examples').addEventListener('click', function() {
  582. fetch('instance.html')
  583. .then(response => response.text())
  584. .then(data => {
  585. document.getElementById('content').innerHTML = data;
  586. fillInstanceInfo();
  587. // 给过滤按钮添加事件,点击时调用filterInstance函数
  588. document.getElementById('intance-btn-filter').addEventListener('click', filterInstance);
  589. })
  590. .catch(error => {
  591. console.error('Error loading the intance content:', error);
  592. });
  593. });
  594. document.getElementById('templates').addEventListener('click', function() {
  595. fetch('template.html')
  596. .then(response => response.text())
  597. .then(data => {
  598. document.getElementById('content').innerHTML = data;
  599. fillTemplateInfo();
  600. })
  601. .catch(error => {
  602. console.error('Error loading the intance content:', error);
  603. });
  604. });
  605. </script>
  606. </body>
  607. </html>