feat: render actual dungeon tile textures in radar
Extract 614 UB tile BMPs into dungeon_tiles.json (287KB base64 bundle). Frontend loads tiles once, then draws them rotated per-cell using environment IDs. Falls back to colored rectangles if tiles not loaded. Current floor at 70% opacity, other floors at 15%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b941a29f04
commit
e2982e34b5
2 changed files with 47 additions and 7 deletions
1
static/dungeon_tiles.json
Normal file
1
static/dungeon_tiles.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -3586,6 +3586,24 @@ function openPlayerDashboard() {
|
|||
|
||||
const radarWindows = {}; // character_name -> window element
|
||||
const dungeonMapCache = {}; // landblock hex string -> dungeon map data
|
||||
let dungeonTileImages = null; // env_id -> Image, loaded once
|
||||
|
||||
// Load dungeon tile textures (614 tiles, ~287KB JSON)
|
||||
function loadDungeonTiles() {
|
||||
if (dungeonTileImages) return; // already loading/loaded
|
||||
dungeonTileImages = {};
|
||||
fetch('dungeon_tiles.json')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
Object.entries(data).forEach(([envId, dataUrl]) => {
|
||||
const img = new Image();
|
||||
img.src = dataUrl;
|
||||
dungeonTileImages[envId] = img;
|
||||
});
|
||||
console.log(`Loaded ${Object.keys(dungeonTileImages).length} dungeon tile textures`);
|
||||
})
|
||||
.catch(err => console.warn('Failed to load dungeon tiles:', err));
|
||||
}
|
||||
|
||||
const RADAR_COLORS = {
|
||||
Monster: '#ff4444',
|
||||
|
|
@ -3705,6 +3723,9 @@ function showRadarWindow(name) {
|
|||
win._radarSelectedId = closest ? closest.id : null;
|
||||
});
|
||||
|
||||
// Load dungeon tiles on first radar open
|
||||
loadDungeonTiles();
|
||||
|
||||
// Send start_radar command
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({ player_name: name, command: 'start_radar' }));
|
||||
|
|
@ -3764,25 +3785,43 @@ function updateRadarWindow(msg) {
|
|||
ctx.clip();
|
||||
|
||||
if (isDungeon && landblock && dungeonMapCache[landblock]) {
|
||||
// ─── Dungeon cell background ───
|
||||
// ─── Dungeon cell background with tile textures ───
|
||||
const dmap = dungeonMapCache[landblock];
|
||||
const playerRoundedZ = Math.floor((playerRawZ + 3) / 6) * 6;
|
||||
ctx.translate(cx, cy);
|
||||
ctx.rotate(-playerHeading * Math.PI / 180); // heading-up rotation
|
||||
const cellSize = 10 * scale; // each cell is 10 game units
|
||||
const hasTiles = dungeonTileImages && Object.keys(dungeonTileImages).length > 0;
|
||||
|
||||
// Normalize rotation value to radians (same as UB's DungeonCell.cs)
|
||||
function cellRotation(rot) {
|
||||
if (rot === 1) return Math.PI;
|
||||
if (rot < -0.70 && rot > -0.8) return Math.PI / 2;
|
||||
if (rot > 0.70 && rot < 0.8) return -Math.PI / 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
(dmap.z_levels || []).forEach(level => {
|
||||
const isCurrentFloor = (level.z === playerRoundedZ);
|
||||
ctx.globalAlpha = isCurrentFloor ? 0.5 : 0.15;
|
||||
ctx.fillStyle = isCurrentFloor ? '#3a5a3a' : '#1a2a1a';
|
||||
ctx.strokeStyle = isCurrentFloor ? '#4a6a4a' : '#2a3a2a';
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.globalAlpha = isCurrentFloor ? 0.7 : 0.15;
|
||||
|
||||
(level.cells || []).forEach(cell => {
|
||||
const dx = (cell.x - playerX) * scale;
|
||||
const dy = -(cell.y - playerY) * scale; // Y flipped
|
||||
const tileImg = hasTiles ? dungeonTileImages[String(cell.env_id)] : null;
|
||||
|
||||
if (tileImg && tileImg.complete && tileImg.naturalWidth) {
|
||||
// Draw textured tile with rotation
|
||||
ctx.save();
|
||||
ctx.translate(dx, dy);
|
||||
ctx.rotate(cellRotation(cell.rotation));
|
||||
ctx.drawImage(tileImg, -cellSize / 2, -cellSize / 2, cellSize, cellSize);
|
||||
ctx.restore();
|
||||
} else {
|
||||
// Fallback: colored rectangle
|
||||
ctx.fillStyle = isCurrentFloor ? '#3a5a3a' : '#1a2a1a';
|
||||
ctx.fillRect(dx - cellSize / 2, dy - cellSize / 2, cellSize, cellSize);
|
||||
ctx.strokeRect(dx - cellSize / 2, dy - cellSize / 2, cellSize, cellSize);
|
||||
}
|
||||
});
|
||||
});
|
||||
ctx.globalAlpha = 1.0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue