Migrate project from typing_practiceweb
This commit is contained in:
347
public/tower-defense/td.html
Normal file
347
public/tower-defense/td.html
Normal file
@@ -0,0 +1,347 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=1.0, user-scalable=yes">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-family: Verdana, "Times New Roman", serif;
|
||||
background: #1A74BA;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 48px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: Verdana, "Times New Roman", serif;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
margin: 0 auto;
|
||||
max-width: 980px;
|
||||
}
|
||||
|
||||
#td-wrapper {
|
||||
padding: 8px 24px 24px 24px;
|
||||
background: #E0F4FC;
|
||||
}
|
||||
|
||||
#td-board {
|
||||
display: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#td-board canvas#td-canvas {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border: solid 1px #cdf;
|
||||
}
|
||||
|
||||
#td-loading {
|
||||
font-size: 18px;
|
||||
line-height: 48px;
|
||||
padding: 60px 0 120px 0;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body id="tower-defense">
|
||||
<div id="wrapper">
|
||||
<div id="td-wrapper">
|
||||
|
||||
<div id="td-loading">加载中...</div>
|
||||
<div id="td-board">
|
||||
<canvas id="td-canvas">抱歉,您的浏览器不支持 HTML 5 Canvas 标签,请使用 Chrome / Edge 等现代浏览器打开。</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="td-pkg-zh.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
console.log('[init] window.onload called');
|
||||
// 原站点的初始化方式:_TD.init('td-board', true)
|
||||
_TD.init('td-board', true);
|
||||
document.getElementById('td-loading').style.display = 'none';
|
||||
document.getElementById('td-board').style.display = 'block';
|
||||
};
|
||||
|
||||
// --- Monster save/load enhancement ---
|
||||
(function() {
|
||||
setTimeout(function() {
|
||||
if (typeof TD === 'undefined') {
|
||||
console.error('[MonsterSaveLoad] TD is still undefined after 500ms');
|
||||
return;
|
||||
}
|
||||
console.log('[MonsterSaveLoad] Applying monster save/load enhancement...');
|
||||
|
||||
window.__TD_getState = function () {
|
||||
try {
|
||||
console.log('[__TD_getState] ===== START SAVING STATE =====');
|
||||
|
||||
var scene = TD.stage && TD.stage.current_act && TD.stage.current_act.current_scene;
|
||||
var map = scene && scene.map;
|
||||
|
||||
var buildings = [];
|
||||
if (map && map.buildings && map.buildings.length) {
|
||||
for (var i = 0; i < map.buildings.length; i++) {
|
||||
var b = map.buildings[i];
|
||||
if (!b || !b.grid) continue;
|
||||
buildings.push({ type: b.type, mx: b.grid.mx, my: b.grid.my, level: b.level, money: b.money });
|
||||
}
|
||||
}
|
||||
|
||||
var monsters_data = [];
|
||||
if (map && map.monsters && map.monsters.length) {
|
||||
console.log('[__TD_getState] Found ' + map.monsters.length + ' monsters');
|
||||
for (var i = 0; i < map.monsters.length; i++) {
|
||||
var m = map.monsters[i];
|
||||
if (!m || !m.is_valid || !m.grid) continue;
|
||||
|
||||
var monster_data = {
|
||||
idx: m.idx,
|
||||
difficulty: m.difficulty,
|
||||
life: m.life,
|
||||
life0: m.life0,
|
||||
shield: m.shield,
|
||||
speed: m.speed,
|
||||
damage: m.damage,
|
||||
money: m.money,
|
||||
mx: m.grid.mx,
|
||||
my: m.grid.my,
|
||||
cx: m.cx,
|
||||
cy: m.cy,
|
||||
r: m.r,
|
||||
color: m.color,
|
||||
toward: m.toward,
|
||||
way: m.way || [],
|
||||
next_grid_mx: m.next_grid ? m.next_grid.mx : null,
|
||||
next_grid_my: m.next_grid ? m.next_grid.my : null,
|
||||
_dx: m._dx || 0,
|
||||
_dy: m._dy || 0,
|
||||
step_level: m.step_level,
|
||||
render_level: m.render_level,
|
||||
is_paused: m.is_paused,
|
||||
is_blocked: m.is_blocked
|
||||
};
|
||||
monsters_data.push(monster_data);
|
||||
}
|
||||
console.log('[__TD_getState] Gathered ' + monsters_data.length + ' valid monsters data:', monsters_data);
|
||||
}
|
||||
|
||||
var state = {
|
||||
money: TD.money,
|
||||
life: TD.life,
|
||||
score: TD.score,
|
||||
difficulty: TD.difficulty,
|
||||
wave_damage: TD.wave_damage,
|
||||
wave: (scene && scene.wave) || 0,
|
||||
map_step_level: (map && map.step_level) || 1,
|
||||
map_render_level: (map && map.render_level) || 2,
|
||||
buildings: buildings,
|
||||
monsters: monsters_data
|
||||
};
|
||||
console.log('[__TD_getState] Returning state:', state);
|
||||
return state;
|
||||
} catch (e) {
|
||||
console.error('[__TD_getState] error', e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
window.__TD_loadState = function (s) {
|
||||
try {
|
||||
console.log('[__TD_loadState] ===== START LOADING STATE =====', s);
|
||||
if (!s) return;
|
||||
|
||||
var scene = TD.stage && TD.stage.current_act && TD.stage.current_act.current_scene;
|
||||
var map = scene && scene.map;
|
||||
if (!map) return;
|
||||
|
||||
// clear existing
|
||||
for (var i = 0; i < map.grids.length; i++) {
|
||||
var g = map.grids[i];
|
||||
if (g && g.building) g.removeBuilding();
|
||||
}
|
||||
if (map.monsters && map.monsters.length) {
|
||||
for (var mi = 0; mi < map.monsters.length; mi++) {
|
||||
if (map.monsters[mi]) {
|
||||
map.monsters[mi].pause();
|
||||
map.monsters[mi].del();
|
||||
}
|
||||
}
|
||||
map.monsters = [];
|
||||
}
|
||||
|
||||
// restore buildings
|
||||
if (s.buildings) {
|
||||
for (var j = 0; j < s.buildings.length; j++) {
|
||||
var bi = s.buildings[j];
|
||||
var grid = map.getGrid(bi.mx, bi.my);
|
||||
if (grid) {
|
||||
grid.addBuilding(bi.type);
|
||||
var b = grid.building;
|
||||
if (b) {
|
||||
if (typeof bi.level !== 'undefined') b.level = bi.level;
|
||||
if (typeof bi.money !== 'undefined') b.money = bi.money;
|
||||
b.updateBtnDesc && b.updateBtnDesc();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore monsters
|
||||
if (s.monsters) {
|
||||
for (var mj = 0; mj < s.monsters.length; mj++) {
|
||||
var md = s.monsters[mj];
|
||||
if (md.life <= 0) continue;
|
||||
var m_grid = map.getGrid(md.mx, md.my);
|
||||
if (!m_grid || m_grid === map.exit) continue;
|
||||
|
||||
var monster = new TD.Monster(null, {
|
||||
idx: md.idx,
|
||||
difficulty: md.difficulty,
|
||||
step_level: md.step_level,
|
||||
render_level: md.render_level
|
||||
});
|
||||
monster.life = md.life;
|
||||
monster.life0 = md.life0;
|
||||
monster.shield = md.shield;
|
||||
monster.speed = md.speed;
|
||||
monster.damage = md.damage;
|
||||
monster.money = md.money;
|
||||
monster.r = md.r;
|
||||
monster.color = md.color;
|
||||
monster.toward = md.toward;
|
||||
monster._dx = md._dx;
|
||||
monster._dy = md._dy;
|
||||
monster.beAddToGrid(m_grid);
|
||||
monster.cx = md.cx;
|
||||
monster.cy = md.cy;
|
||||
monster.caculatePos();
|
||||
if (md.next_grid_mx !== null && md.next_grid_my !== null) {
|
||||
monster.next_grid = map.getGrid(md.next_grid_mx, md.next_grid_my);
|
||||
}
|
||||
monster.way = md.way || [];
|
||||
m_grid.scene.addElement(monster, monster.step_level, monster.render_level);
|
||||
map.monsters.push(monster);
|
||||
if (!monster.is_paused) monster.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof s.money !== 'undefined') TD.money = s.money;
|
||||
if (typeof s.life !== 'undefined') TD.life = s.life;
|
||||
if (typeof s.score !== 'undefined') TD.score = s.score;
|
||||
if (typeof s.difficulty !== 'undefined') TD.difficulty = s.difficulty;
|
||||
if (typeof s.wave_damage !== 'undefined') TD.wave_damage = s.wave_damage;
|
||||
if (typeof s.wave !== 'undefined' && scene) {
|
||||
scene.wave = s.wave;
|
||||
// 补偿逻辑:如果难度系数过低,根据波数自动修正
|
||||
var min_difficulty = Math.pow(1.05, scene.wave - 1);
|
||||
if (TD.difficulty < min_difficulty) {
|
||||
TD.difficulty = min_difficulty;
|
||||
}
|
||||
}
|
||||
if (typeof s.map_step_level !== 'undefined' && map) map.step_level = s.map_step_level;
|
||||
if (typeof s.map_render_level !== 'undefined' && map) map.render_level = s.map_render_level;
|
||||
|
||||
console.log('[__TD_loadState] Load completed');
|
||||
} catch (e) {
|
||||
console.error('[__TD_loadState] error', e);
|
||||
}
|
||||
};
|
||||
|
||||
console.log('[MonsterSaveLoad] Enhancement applied successfully!');
|
||||
}, 500);
|
||||
})();
|
||||
|
||||
// Bridge: respond to parent requests for game state (debug-friendly)
|
||||
window.addEventListener('message', function (e) {
|
||||
try {
|
||||
console.debug('td.html received postMessage', e.origin, e.data);
|
||||
if (!e.data || !e.data.type) return;
|
||||
// only accept same-origin or allow if parent is our host during dev
|
||||
if (e.origin !== window.location.origin && !e.origin.startsWith('http://localhost')) {
|
||||
console.debug('Ignoring postMessage from', e.origin);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.data.type === 'tower-defense:request-state') {
|
||||
// Try several possible getters
|
||||
var state = null;
|
||||
try {
|
||||
if (typeof window.__TD_getState === 'function') {
|
||||
console.log('[request-state] Using window.__TD_getState()');
|
||||
state = window.__TD_getState();
|
||||
console.log('[request-state] State from __TD_getState:', state);
|
||||
} else {
|
||||
console.log('[request-state] No TD getter available');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('state getter threw', err);
|
||||
}
|
||||
|
||||
e.source.postMessage({ type: 'tower-defense:state', state: state }, e.origin);
|
||||
console.debug('Replied with tower-defense:state', state);
|
||||
} else if (e.data.type === 'tower-defense:load') {
|
||||
// Forward load to any exposed loader
|
||||
try {
|
||||
if (typeof window.__TD_loadState === 'function') {
|
||||
window.__TD_loadState(e.data.state);
|
||||
console.debug('Invoked __TD_loadState');
|
||||
} else {
|
||||
console.debug('No load API available');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('load handling error', err);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('postMessage bridge error', err);
|
||||
}
|
||||
}, false);
|
||||
|
||||
// --- Shortcut keys enhancement ---
|
||||
window.addEventListener('keydown', function (e) {
|
||||
if (typeof TD === 'undefined' || !TD.stage || !TD.stage.current_act || !TD.stage.current_act.current_scene) return;
|
||||
var map = TD.stage.current_act.current_scene.map;
|
||||
if (!map) return;
|
||||
|
||||
var key = e.key.toLowerCase();
|
||||
if (key === 's') {
|
||||
// S: Sell selected building
|
||||
if (map.selected_building) {
|
||||
console.log('[Shortcut] Selling building:', map.selected_building.id);
|
||||
map.selected_building.tryToSell();
|
||||
}
|
||||
} else if (key === 'w') {
|
||||
// W: Select Wall (路障)
|
||||
if (!map.selected_building) {
|
||||
console.log('[Shortcut] Pre-building Wall');
|
||||
map.preBuild('wall');
|
||||
}
|
||||
} else if (key === 'e') {
|
||||
// E: Select Laser Gun (激光枪)
|
||||
if (!map.selected_building) {
|
||||
console.log('[Shortcut] Pre-building Laser Gun');
|
||||
map.preBuild('laser_gun');
|
||||
}
|
||||
} else if (key === 'r') {
|
||||
// R: Select Heavy Machine Gun (重机枪)
|
||||
if (!map.selected_building) {
|
||||
console.log('[Shortcut] Pre-building HMG');
|
||||
map.preBuild('HMG');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user