Files
aether-chronicles/frontend/js/input.js
T
arch_agent 5cf42a5e5d v0.1.0 — M1: Project Setup + Tilemap + Combat + Backend
Frontend:
- HTML5 Canvas isometric tilemap renderer
- Eichenhafen starting city (procedural)
- Click-to-move A* pathfinding
- Player stats, HP/SP/EXP bars, skill bar, minimap
- 3 NPCs (merchant, class master, innkeeper)
- 6 monster spawns with aggro AI
- Combat: auto-attack, damage numbers, EXP/gold
- Player death + respawn

Backend:
- FastAPI server with SQLite
- Ollama LLM bridge for events + dialogue
- Event validation (no soft-lock)
- NPC dialogue generation

Design:
- Full DESIGN.md with architecture, formulas, milestones
- 6 base classes + 6 advanced classes
- 3 fixed cities, dynamic events
2026-07-17 20:14:16 +02:00

85 lines
2.7 KiB
JavaScript

// input.js — Mouse and keyboard input handling
class InputHandler {
constructor(canvas, game) {
this.canvas = canvas;
this.game = game;
this.mouseX = 0;
this.mouseY = 0;
canvas.addEventListener('click', (e) => this.handleClick(e));
canvas.addEventListener('mousemove', (e) => this.handleMouseMove(e));
canvas.addEventListener('contextmenu', (e) => e.preventDefault());
document.addEventListener('keydown', (e) => this.handleKey(e));
}
handleClick(e) {
const rect = this.canvas.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
// Convert screen to world coords
const worldX = mx + this.game.camera.x - 480;
const worldY = my + this.game.camera.y - 320;
// Convert to tile coords
const tile = this.game.map.screenToTile(worldX, worldY);
// Check if clicking a monster
for (const monster of this.game.monsters) {
if (monster.tileX === tile.x && monster.tileY === tile.y) {
this.game.player.attackTarget = monster;
this.game.addEvent(`Targeting ${monster.name}`, 'combat');
return;
}
}
// Check if clicking an NPC
for (const npc of this.game.npcs) {
if (npc.tileX === tile.x && npc.tileY === tile.y) {
this.game.interactNPC(npc);
return;
}
}
// Otherwise, move to clicked tile
if (!this.game.map.isSolid(tile.x, tile.y)) {
const playerTile = { x: Math.floor(this.game.player.x), y: Math.floor(this.game.player.y) };
const path = this.game.findPath(playerTile, tile);
if (path) {
this.game.player.setPath(path);
this.game.player.attackTarget = null;
}
}
}
handleMouseMove(e) {
const rect = this.canvas.getBoundingClientRect();
this.mouseX = e.clientX - rect.left;
this.mouseY = e.clientY - rect.top;
}
handleKey(e) {
// Skill bar (1-0)
if (e.key >= '0' && e.key <= '9') {
const slot = e.key === '0' ? 10 : parseInt(e.key);
this.game.useSkill(slot);
}
// Other keys
switch(e.key) {
case 'i':
case 'I':
this.game.toggleInventory();
break;
case 'q':
case 'Q':
this.game.toggleQuestLog();
break;
case 'Escape':
this.game.closeMenus();
break;
}
}
}