// Debug alert for mobile debugging
if (typeof debugAlert === 'function') {
debugAlert('files.js loaded');
}
// Global variables for file management
let selectedImage = null;
let selectedImageName = null;
let selectedImageDiv = null;
let selectedTileSize = null;
// DOM elements
const fileList = document.getElementById('fileList');
const breadcrumb = document.querySelector('.breadcrumb');
const preview = document.getElementById('preview');
/**
* Load files from the server for a given subdirectory
* @param {string} sub - The subdirectory path
*/
function loadFiles(sub) {
fetch('media.php?sub=' + encodeURIComponent(sub))
.then(r => r.json())
.then(data => {
renderBreadcrumb(data.breadcrumb);
renderFiles(data);
})
.catch(err => console.error('Error loading files:', err));
}
/**
* Render the breadcrumb navigation
* @param {Array} crumbs - Array of breadcrumb objects
*/
function renderBreadcrumb(crumbs) {
breadcrumb.innerHTML = '';
crumbs.forEach(c => {
const span = document.createElement('span');
span.textContent = c.label;
span.onclick = () => loadFiles(c.sub);
breadcrumb.appendChild(span);
});
}
/**
* Render the file list (folders and images)
* @param {Object} data - Data object containing folders and images arrays
*/
function renderFiles(data) {
fileList.innerHTML = '';
preview.innerHTML = '';
// Render folders
data.folders.forEach(f => {
const div = document.createElement('div');
div.className = 'folder';
div.textContent = f.name;
div.onclick = () => {
// If folder name is numeric, treat it as tile size
if (/^\d+$/.test(f.name)) {
selectedTileSize = parseInt(f.name, 10);
loadFiles(f.sub);
} else {
loadFiles(f.sub);
}
};
fileList.appendChild(div);
});
// Render images
data.images.forEach(img => {
const div = document.createElement('div');
div.className = 'image';
const imgElement = document.createElement('img');
imgElement.src = img.url;
imgElement.alt = img.name;
div.appendChild(imgElement);
div.onclick = () => {
// Remove previous selection
if (selectedImageDiv) {
selectedImageDiv.classList.remove('selected');
}
// Set new selection
div.classList.add('selected');
selectedImageDiv = div;
selectedImage = img.url;
selectedImageName = img.name;
showPreview(img);
};
fileList.appendChild(div);
});
}
/**
* Show preview of selected image
* @param {Object} img - Image object with name and url
*/
function showPreview(img) {
preview.innerHTML = `
<h3>Selected: ${img.name}</h3>
<img id="previewImg" src="${img.url}" alt="${img.name}">
`;
// Click big preview image to open tile picker
document.getElementById('previewImg').onclick = () => openOverlay('tiles');
}
/**
* Open overlay with specified content type
* @param {string} type - Type of overlay content ('tiles', 'map', 'game')
*/
function openOverlay(type) {
const overlay = document.getElementById('overlay');
const overlayContent = document.getElementById('overlayContent');
overlayContent.innerHTML = '';
overlay.style.display = 'block';
if (type === 'tiles') {
openTilePickerOverlay();
} else if (type === 'map') {
openMapOverlay();
} else if (type === 'game') {
openGameOverlay();
}
}
/**
* Open the tile picker overlay
*/
function openTilePickerOverlay() {
const overlayContent = document.getElementById('overlayContent');
if (selectedImage && selectedTileSize) {
overlayContent.innerHTML = `
<h2>Tile Picker 🧩</h2>
<p>Tile size: ${selectedTileSize}</p>
<div id="groupTabs"></div>
<div id="pickedImages"></div>
<div id="tileViewport">
<div id="tileContainer">
<img id="tileImage" src="${selectedImage}" alt="${selectedImageName}">
</div>
</div>
`;
// Initialize tile picker functionality
initializeTilePicker();
} else {
overlayContent.innerHTML = `
<h2>Tile Picker 🧩</h2>
<p>Select an image and a numeric folder first.</p>
`;
}
}
/**
* Open the map overlay
*/
function openMapOverlay() {
openTilemapOverlay();
}
/**
* Open the game overlay
*/
function openGameOverlay() {
const overlayContent = document.getElementById('overlayContent');
overlayContent.innerHTML = `
<div style="height: 100%; overflow: auto; padding: 10px;">
<div id="gameController"></div>
</div>
`;
renderCompleteGameController();
}
/**
* Render complete game controller interface
*/
function renderCompleteGameController() {
const container = document.getElementById('gameController');
if (!container) return;
container.innerHTML = '';
// Game Object Section
const gameSection = document.createElement('div');
gameSection.style.cssText = 'padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;';
gameSection.innerHTML = `
<h3 style="color: #6cf; margin: 0 0 15px 0;">🎮 Game Object</h3>
<div style="margin-bottom: 10px;">
<strong style="color: #f44;">Mandatory:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>ID: game_001</div>
<div>Name: My Tile Game</div>
<div>Version: 1.0.0</div>
</div>
</div>
<div style="margin-bottom: 10px;">
<strong style="color: #4a4;">Changeable:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>Rules: {win: "reach_exit", lose: "no_health", score: "collect"}</div>
<div>Physics: {gravity: 300, friction: 0.8, bounce: 0.3}</div>
<div>Theme: retro-pixel</div>
</div>
</div>
<div>
<strong style="color: #888;">Optional:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>UI Layout: minimal-hud</div>
<div>Progression Mode: linear</div>
<div>Settings: {difficulty: "normal", mode: "single"}</div>
</div>
</div>
`;
container.appendChild(gameSection);
// Tile Groups Section with images and IDs
const groupsSection = document.createElement('div');
groupsSection.style.cssText = 'padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;';
const groupsTitle = document.createElement('h3');
groupsTitle.textContent = '🧱 Tile Groups';
groupsTitle.style.cssText = 'color: #6cf; margin: 0 0 15px 0;';
groupsSection.appendChild(groupsTitle);
if (typeof groups !== 'undefined' && groups && groups.length > 0) {
groups.forEach((group, index) => {
const groupDiv = document.createElement('div');
groupDiv.style.cssText = 'margin-bottom: 20px; padding: 15px; background: #2a2a2a; border-radius: 6px; border-left: 4px solid #6cf;';
groupDiv.innerHTML = `
<h4 style="margin: 0 0 10px 0; color: #fff;">Group ${index + 1}</h4>
<div style="margin-bottom: 10px;">
<strong style="color: #f44;">Mandatory:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>ID: group_${index + 1}</div>
<div>Name: ${group.name || `Tile Group ${index + 1}`}</div>
<div>Atlas Key: ${group.url ? group.url.split('/').pop() : 'none'}</div>
<div>Tile Size: ${group.tiles && group.tiles[0] ? group.tiles[0].size : 32}px</div>
</div>
</div>
<div style="margin-bottom: 10px;">
<strong style="color: #4a4;">Changeable:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>Members: ${group.tiles ? group.tiles.length : 0} tiles</div>
</div>
</div>
<div style="margin-bottom: 10px;">
<strong style="color: #888;">Optional:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>Collidable: true</div>
<div>Interactions: none</div>
<div>AI Profile: idle</div>
</div>
</div>
`;
// Add tile previews with ID badges
if (group.tiles && group.tiles.length > 0) {
const previewDiv = document.createElement('div');
previewDiv.style.cssText = 'display: flex; gap: 5px; flex-wrap: wrap; margin-top: 10px;';
group.tiles.forEach(tile => {
const tileWrapper = document.createElement('div');
tileWrapper.style.cssText = 'position: relative; display: inline-block;';
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
canvas.style.cssText = 'border: 2px solid #666; border-radius: 4px; background: #000;';
const ctx = canvas.getContext('2d');
const tempCanvas = document.createElement('canvas');
tempCanvas.width = tile.size;
tempCanvas.height = tile.size;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.putImageData(tile.data, 0, 0);
ctx.drawImage(tempCanvas, 0, 0, tile.size, tile.size, 0, 0, 32, 32);
const idBadge = document.createElement('span');
idBadge.textContent = tile.uniqueId;
idBadge.style.cssText = `
position: absolute; bottom: -2px; right: -2px;
background: rgba(0,0,0,0.8); color: #fff;
font-size: 8px; padding: 1px 3px; border-radius: 2px;
border: 1px solid #6cf;
`;
tileWrapper.appendChild(canvas);
tileWrapper.appendChild(idBadge);
previewDiv.appendChild(tileWrapper);
});
groupDiv.appendChild(previewDiv);
}
groupsSection.appendChild(groupDiv);
});
} else {
groupsSection.innerHTML += '<div style="color: #888; font-style: italic; padding: 10px;">No tile groups created yet. Use the Tile Picker to create groups.</div>';
}
container.appendChild(groupsSection);
// Tilemaps Section with arrays
const mapsSection = document.createElement('div');
mapsSection.style.cssText = 'padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;';
const mapsTitle = document.createElement('h3');
mapsTitle.textContent = '🗺️ Tile Maps';
mapsTitle.style.cssText = 'color: #6cf; margin: 0 0 15px 0;';
mapsSection.appendChild(mapsTitle);
if (typeof tilemaps !== 'undefined' && tilemaps && tilemaps.length > 0) {
tilemaps.forEach((tilemap, index) => {
const tilemapDiv = document.createElement('div');
tilemapDiv.style.cssText = 'margin-bottom: 20px; padding: 15px; background: #2a2a2a; border-radius: 6px; border-left: 4px solid #6cf;';
const isActive = typeof currentTilemapIndex !== 'undefined' && currentTilemapIndex === index;
const totalTiles = tilemap.data ? tilemap.data.filter(t => t !== 0).length : 0;
const fillPercent = ((totalTiles / (tilemap.width * tilemap.height)) * 100).toFixed(1);
tilemapDiv.innerHTML = `
<h4 style="margin: 0 0 10px 0; color: ${isActive ? '#6cf' : '#fff'};">${tilemap.name}${isActive ? ' (Active)' : ''}</h4>
<div style="margin-bottom: 10px;">
<strong style="color: #f44;">Mandatory:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>ID: map_${tilemap.id}</div>
<div>Name: ${tilemap.name}</div>
<div>Rows: ${tilemap.height}</div>
<div>Cols: ${tilemap.width}</div>
</div>
</div>
<div style="margin-bottom: 10px;">
<strong style="color: #4a4;">Changeable:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>Start Points: {player: {x: 0, y: 0}}</div>
<div>Layer Index: 1</div>
<div>Background: #2a2a2a</div>
</div>
</div>
<div style="margin-bottom: 10px;">
<strong style="color: #888;">Optional:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;">
<div>Fill: ${fillPercent}% (${totalTiles} tiles)</div>
<div>Events: []</div>
<div>Progression: {nextMap: "level2"}</div>
</div>
</div>
`;
// Add Phaser 2D array if there's data
if (tilemap.data && tilemap.data.length > 0) {
const arrayDiv = document.createElement('div');
arrayDiv.innerHTML = '<strong style="color: #6cf;">Phaser 2D Array:</strong>';
const arrayContainer = document.createElement('div');
arrayContainer.style.cssText = `
background: #1a1a1a; padding: 10px; border-radius: 4px; margin-top: 5px;
font-family: 'Courier New', monospace; font-size: 10px;
color: #ccc; overflow-x: auto; max-height: 150px; overflow-y: auto;
`;
let arrayText = '[\n';
for (let y = 0; y < tilemap.height; y++) {
let row = ' [';
for (let x = 0; x < tilemap.width; x++) {
const index = y * tilemap.width + x;
const tileId = tilemap.data[index] || 0;
row += tileId.toString().padStart(3, ' ');
if (x < tilemap.width - 1) row += ',';
}
row += ']';
if (y < tilemap.height - 1) row += ',';
arrayText += row + '\n';
}
arrayText += ']';
const formattedArray = arrayText.split('\n').map(line => {
return `<div style="line-height: 1.2;">${line}</div>`;
}).join('');
arrayContainer.innerHTML = formattedArray;
arrayDiv.appendChild(arrayContainer);
const copyBtn = document.createElement('button');
copyBtn.textContent = 'Copy Array';
copyBtn.style.cssText = `
margin-top: 5px; background: #444; color: white; border: none;
padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 10px;
`;
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(arrayText).then(() => {
copyBtn.textContent = 'Copied!';
setTimeout(() => copyBtn.textContent = 'Copy Array', 1000);
});
});
arrayDiv.appendChild(copyBtn);
tilemapDiv.appendChild(arrayDiv);
}
mapsSection.appendChild(tilemapDiv);
});
} else {
mapsSection.innerHTML += '<div style="color: #888; font-style: italic; padding: 10px;">No tilemaps created yet. Use the Tilemap Editor to create maps.</div>';
}
container.appendChild(mapsSection);
// Export section
const exportSection = document.createElement('div');
exportSection.style.cssText = 'padding: 15px; background: #1a1a1a; border-radius: 8px;';
exportSection.innerHTML = `
<h3 style="margin: 0 0 10px 0; color: #6cf; font-size: 18px;">Export Project</h3>
<button onclick="alert('Export functionality coming soon!')" style="background: #4a4; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px;">
Export Complete Project
</button>
`;
container.appendChild(exportSection);
}