Migrate project from typing_practiceweb

This commit is contained in:
2026-05-14 16:04:14 +08:00
parent dbc5425706
commit 0e87d5546d
225 changed files with 92811 additions and 0 deletions

View 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>