|
108 | 108 | ctx.restore(); |
109 | 109 | } |
110 | 110 |
|
111 | | -// Monster definitions |
| 111 | +// Monster definitions (all on the path) |
112 | 112 | const monsters = [ |
113 | | - { name: 'YAML Yeti', color: '#007acc', x: 2, y: 2, icon: 'yeti' }, |
114 | | - { name: 'Dependency Dragon', color: '#25d6a2', x: 25, y: 2, icon: 'dragon' }, |
115 | | - { name: 'Build Bug', color: '#e74c3c', x: 2, y: 21, icon: 'bug' }, |
116 | | - { name: 'Merge Monster', color: '#9b59b6', x: 25, y: 21, icon: 'merge' }, |
117 | | - { name: 'Registry Ghost', color: '#fff', x: 14, y: 2, icon: 'ghost' }, |
118 | | - { name: 'Port Goblin', color: '#f39c12', x: 2, y: 12, icon: 'goblin' }, |
119 | | - { name: 'Token Troll', color: '#f1c40f', x: 25, y: 12, icon: 'troll' }, |
120 | | - { name: 'Cache Kraken', color: '#17a2b8', x: 14, y: 21, icon: 'kraken' } |
| 113 | + { name: 'YAML Yeti', color: '#007acc', x: 6, y: 1, icon: 'yeti' }, // path |
| 114 | + { name: 'Dependency Dragon', color: '#25d6a2', x: 21, y: 1, icon: 'dragon' }, // path |
| 115 | + { name: 'Build Bug', color: '#e74c3c', x: 6, y: 23, icon: 'bug' }, // path |
| 116 | + { name: 'Merge Monster', color: '#9b59b6', x: 21, y: 23, icon: 'merge' }, // path |
| 117 | + { name: 'Registry Ghost', color: '#fff', x: 14, y: 5, icon: 'ghost' }, // path |
| 118 | + { name: 'Port Goblin', color: '#f39c12', x: 7, y: 12, icon: 'goblin' }, // path |
| 119 | + { name: 'Token Troll', color: '#f1c40f', x: 21, y: 12, icon: 'troll' }, // path |
| 120 | + { name: 'Cache Kraken', color: '#17a2b8', x: 14, y: 18, icon: 'kraken' } // path |
121 | 121 | ]; |
122 | 122 |
|
| 123 | +// Monster state for movement |
| 124 | +globalThis.monsterStates = monsters.map(m => ({...m, dx: 0, dy: 0})); |
| 125 | + |
| 126 | +function moveMonsters() { |
| 127 | + for (const m of globalThis.monsterStates) { |
| 128 | + // Simple random movement: pick a random direction that is not a wall |
| 129 | + const dirs = [ |
| 130 | + {dx: 1, dy: 0}, {dx: -1, dy: 0}, {dx: 0, dy: 1}, {dx: 0, dy: -1} |
| 131 | + ]; |
| 132 | + const validDirs = dirs.filter(d => canMove(m.x + d.dx, m.y + d.dy)); |
| 133 | + if (validDirs.length > 0) { |
| 134 | + const choice = validDirs[Math.floor(Math.random() * validDirs.length)]; |
| 135 | + m.x += choice.dx; |
| 136 | + m.y += choice.dy; |
| 137 | + } |
| 138 | + } |
| 139 | +} |
| 140 | + |
123 | 141 | function drawMonsters() { |
124 | | - for (const m of monsters) { |
| 142 | + for (const m of globalThis.monsterStates) { |
125 | 143 | drawMonsterIcon(ctx, m.x * tileSize + tileSize/2, m.y * tileSize + tileSize/2, 28, m.color, m.icon); |
126 | 144 | } |
127 | 145 | } |
|
266 | 284 | } |
267 | 285 | }); |
268 | 286 |
|
| 287 | +let lastMonsterMove = 0; |
| 288 | +const MONSTER_MOVE_INTERVAL = 300; // ms |
| 289 | + |
269 | 290 | function gameLoop(timestamp) { |
270 | 291 | ctx.clearRect(0, 0, canvas.width, canvas.height); |
271 | 292 | drawMap(); |
| 293 | + if (!lastMonsterMove) lastMonsterMove = timestamp; |
| 294 | + if (timestamp - lastMonsterMove > MONSTER_MOVE_INTERVAL) { |
| 295 | + moveMonsters(); |
| 296 | + lastMonsterMove = timestamp; |
| 297 | + } |
272 | 298 | drawMonsters(); |
273 | 299 | drawPacman(); |
274 | 300 | drawScore(); |
|
0 commit comments