Fix tile rendering dimensions and alignment, update tile definitions to use height

This commit is contained in:
2026-01-02 23:06:40 +01:00
parent 9234a2e3a0
commit 970ff224c3
17 changed files with 2322 additions and 869 deletions

View File

@@ -1,69 +1,50 @@
import { GameEngine } from './engine/game/GameEngine.js';
import { GameRenderer } from './view/GameRenderer.js';
import { CameraManager } from './view/CameraManager.js';
import { UIManager } from './view/UIManager.js';
import { DoorModal } from './view/DoorModal.js';
import { MissionConfig, MISSION_TYPES } from './engine/dungeon/MissionConfig.js';
console.log("Initializing Warhammer Quest Engine... SYSTEM: GAME_LOOP_ARC_V1");
window.TEXTURE_DEBUG = true;
console.log("🏗️ Warhammer Quest - Manual Dungeon Construction");
// 1. Setup Mission
const mission = new MissionConfig({
id: 'mission_1',
name: 'The First Dive',
name: 'Manual Construction',
type: MISSION_TYPES.ESCAPE,
minTiles: 6
minTiles: 13
});
// 2. Initialize Core Systems
const renderer = new GameRenderer('app'); // Visuals
const cameraManager = new CameraManager(renderer); // Camera
const game = new GameEngine(); // Logic Brain
// 3. Initialize UI
// UIManager currently reads directly from DungeonGenerator for minimap
const renderer = new GameRenderer('app');
const cameraManager = new CameraManager(renderer);
const game = new GameEngine();
const ui = new UIManager(cameraManager, game);
const doorModal = new DoorModal();
// Global Access for Debugging in Browser Console
// Global Access
window.GAME = game;
window.RENDERER = renderer;
// 4. Bridge Logic & View (Event Hook)
// When logic places a tile, we tell the renderer to spawn 3D meshes.
// Ideally, this should be an Event in GameEngine, but we keep this patch for now to verify.
// 3. Connect Dungeon Generator to Renderer
const generator = game.dungeon;
const originalPlaceTile = generator.grid.placeTile.bind(generator.grid);
generator.grid.placeTile = (instance, def) => {
// 1. Execute Logic
originalPlaceTile(instance, def);
generator.grid.placeTile = (instance, variant, card) => {
originalPlaceTile(instance, variant);
// 2. Execute Visuals
const cells = generator.grid.getGlobalCells(def, instance.x, instance.y, instance.rotation);
renderer.addTile(cells, def.type, def, instance);
const cells = generator.grid.calculateCells(variant, instance.x, instance.y);
renderer.addTile(cells, card.type, card, instance);
// 3. Update Exits Visuals
setTimeout(() => {
renderer.renderExits(generator.pendingExits);
}, 50); // Small delay to ensure logic updated pendingExits
renderer.renderExits(generator.availableExits);
}, 50);
};
// 5. Connect UI Buttons to Game Actions (Temporary)
// We will add a temporary button in pure JS here or modify UIManager later.
// For now, let's expose a global function for the UI to call if needed,
// or simply rely on UIManager updates.
// 6. Start the Game
// 5a. Bridge Game Interactions
// 5a. Bridge Game Interactions
// 4. Connect Player to Renderer
game.onEntityUpdate = (entity) => {
renderer.addEntity(entity);
renderer.updateEntityPosition(entity);
// Initial Center on Player Spawn
// Center camera on player spawn
if (entity.id === 'p1' && !entity._centered) {
cameraManager.centerOn(entity.x, entity.y);
entity._centered = true;
@@ -79,85 +60,84 @@ game.onEntitySelect = (entityId, isSelected) => {
};
renderer.onHeroFinishedMove = (x, y) => {
// x, y are World Coordinates (x, -z grid)
// Actually, renderer returns Mesh Position.
// Mesh X = Grid X. Mesh Z = -Grid Y.
// Camera centerOn takes (Grid X, Grid Y).
// So we need to convert back?
// centerOn implementation: this.target.set(x, 0, -y);
// If onHeroFinishedMove passes (mesh.x, -mesh.z), that is (Grid X, Grid Y).
// Let's verify what we passed in renderer:
// this.onHeroFinishedMove(mesh.position.x, -mesh.position.z);
// So if mesh is at (5, 1.5, -5), we pass (5, 5).
// centerOn(5, 5) -> target(5, 0, -5). Correct.
cameraManager.centerOn(x, y);
};
game.onPathChange = (path) => {
renderer.highlightCells(path);
// 5. Connect Generator State to UI
generator.onStateChange = (state) => {
console.log(`[Main] State: ${state}`);
if (state === 'PLACING_TILE') {
ui.showPlacementControls(true);
} else {
ui.showPlacementControls(false);
}
};
// Custom click handler that checks for doors first
const handleCellClick = async (x, y, doorMesh) => {
// If doorMesh is provided, user clicked directly on a door texture
generator.onPlacementUpdate = (preview) => {
if (preview) {
renderer.showPlacementPreview(preview);
ui.updatePlacementStatus(preview.isValid);
} else {
renderer.hidePlacementPreview();
}
};
// 6. Handle Clicks
const handleClick = (x, y, doorMesh) => {
// PRIORITY 1: Tile Placement Mode - ignore all clicks
if (generator.state === 'PLACING_TILE') {
console.log('[Main] Use placement controls to place tile');
return;
}
// PRIORITY 2: Door Click (must be adjacent to player)
if (doorMesh && doorMesh.userData.isDoor && !doorMesh.userData.isOpen) {
// Get player position
const player = game.player;
if (!player) {
console.log('[Main] Player not found');
return;
}
const doorExit = doorMesh.userData.cells[0];
// Check if player is adjacent to the door
if (renderer.isPlayerAdjacentToDoor(player.x, player.y, doorMesh)) {
// Show modal
const confirmed = await doorModal.show('¿Quieres abrir la puerta?');
if (game.isPlayerAdjacentToDoor(doorExit)) {
console.log('[Main] 🚪 Opening door and drawing tile...');
if (confirmed) {
// Open the door
renderer.openDoor(doorMesh);
// Open door visually
renderer.openDoor(doorMesh);
// Trigger exploration of the next tile
const exitCell = doorMesh.userData.cells[0];
console.log('[Main] Opening door at exit:', exitCell);
// Call game logic to explore through this exit
game.exploreExit(exitCell);
// Get proper exit data with direction
const exitData = doorMesh.userData.exitData;
if (exitData) {
generator.selectDoor(exitData);
} else {
console.error('[Main] Door missing exitData');
}
} else {
console.log('[Main] Player is not adjacent to the door. Move closer first.');
console.log('[Main] ⚠️ Move adjacent to the door first');
}
} else if (x !== null && y !== null) {
// Normal cell click (no door involved)
return;
}
// PRIORITY 3: Normal cell click (player selection/movement)
if (x !== null && y !== null) {
game.onCellClick(x, y);
}
};
renderer.setupInteraction(
() => cameraManager.getCamera(),
handleCellClick,
(x, y) => game.onCellRightClick(x, y)
handleClick,
() => { } // No right-click
);
console.log("--- Starting Game Session ---");
// 7. Start
console.log("--- Starting Game ---");
game.startMission(mission);
// 7. Render Loop
// 8. Render Loop
const animate = (time) => {
requestAnimationFrame(animate);
// Update Game Logic (State Machine, Timers, etc)
game.update(time);
// Update Camera Animations
cameraManager.update(time);
// Update Visual Animations
renderer.updateAnimations(time);
// Render Frame
renderer.render(cameraManager.getCamera());
};
animate(0);
console.log("✅ Ready - Move barbarian next to a door and click it");