📜
object.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
// Debug alert for mobile debugging if (typeof debugAlert === 'function') { debugAlert('object.js loaded'); } // Simple game object let gameObject = { id: 'game_001', name: 'My Tile Game', version: '1.0.0' }; // Attributes will be loaded from attributes.json let gameAttributes = null; let tileGroupAttributes = null; let tileMapAttributes = null; function openGameController() { const overlayContent = document.getElementById('overlayContent'); overlayContent.innerHTML = ` <div style="height: 100%; overflow: auto; padding: 10px;"> <div id="gameController"></div> </div> `; // Load attributes first, then render loadAttributesAndRender(); } function loadAttributesAndRender() { fetch('attributes.json') .then(response => { if (!response.ok) { throw new Error('Failed to load attributes.json'); } return response.json(); }) .then(data => { gameAttributes = data.game; tileGroupAttributes = data.tileGroup; tileMapAttributes = data.tileMap; if (typeof debugAlert === 'function') { debugAlert('Attributes loaded successfully'); } renderGameController(); }) .catch(error => { if (typeof debugAlert === 'function') { debugAlert('Failed to load attributes.json: ' + error.message); } // Fallback to basic view if attributes.json fails to load renderBasicGameController(); }); } function renderBasicGameController() { const container = document.getElementById('gameController'); container.innerHTML = ` <div style="padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;"> <h3 style="color: #6cf; margin: 0 0 15px 0;">Game Object</h3> <div style="color: #f44; padding: 10px;"> Could not load attributes.json. Using basic view. </div> <div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;"> <div>ID: ${gameObject.id}</div> <div>Name: ${gameObject.name}</div> <div>Version: ${gameObject.version}</div> <div>Groups: ${typeof groups !== 'undefined' ? groups.length : 0}</div> <div>Tilemaps: ${typeof tilemaps !== 'undefined' ? tilemaps.length : 0}</div> </div> </div> `; } function renderGameController() { const container = document.getElementById('gameController'); if (!gameAttributes || !tileGroupAttributes || !tileMapAttributes) { container.innerHTML = ` <div style="padding: 15px; background: #1a1a1a; border-radius: 8px;"> <h3 style="color: #6cf;">Loading attributes...</h3> <div style="color: #888;">Please wait while attributes.json loads.</div> </div> `; return; } container.innerHTML = ` <div style="padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"> <h3 style="color: #6cf; margin: 0;">Game Object</h3> <button onclick="copyGameObjectJSON()" style="background: #4a4; color: white; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 10px;">Copy JSON</button> </div> <div style="margin-bottom: 15px;"> <div onclick="toggleSection('gameMandatory')" style="cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;"> <span id="gameMandatoryToggle" style="margin-right: 8px; color: #f44;">▼</span> <strong style="color: #f44;">Mandatory:</strong> </div> <div id="gameMandatory" style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; color: #ccc; font-size: 12px;"> <div>ID: ${gameObject.id}</div> <div>Name: ${gameObject.name}</div> <div>Version: ${gameObject.version}</div> </div> </div> <div id="gameChangeable" style="margin-bottom: 15px;"> <div onclick="toggleSection('gameChangeableContent')" style="cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;"> <span id="gameChangeableContentToggle" style="margin-right: 8px; color: #4a4;">▼</span> <strong style="color: #4a4;">Changeable:</strong> </div> <div id="gameChangeableContent" style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; font-size: 11px;"></div> </div> <div id="gameOptional"> <div onclick="toggleSection('gameOptionalContent')" style="cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;"> <span id="gameOptionalContentToggle" style="margin-right: 8px; color: #888;">▼</span> <strong style="color: #888;">Optional:</strong> </div> <div id="gameOptionalContent" style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; font-size: 11px;"></div> </div> </div> <div id="tileGroupsContainer"></div> <div id="tilemapsContainer"></div> `; // Add attributes using DOM manipulation addGameAttributes(); addTileGroupsSection(); addTilemapsSection(); } function toggleSection(sectionId) { const section = document.getElementById(sectionId); const toggle = document.getElementById(sectionId + 'Toggle'); if (section && toggle) { if (section.style.display === 'none') { section.style.display = 'block'; toggle.textContent = '▼'; } else { section.style.display = 'none'; toggle.textContent = '►'; } } } function copyGameObjectJSON() { try { // Collect current attribute values from dropdowns const gameChangeable = {}; const gameOptional = {}; // Get all select elements in the game sections const changeableSelects = document.querySelectorAll('#gameChangeableContent select'); const optionalSelects = document.querySelectorAll('#gameOptionalContent select'); // Process changeable attributes changeableSelects.forEach((select, index) => { const parentDiv = select.parentElement; const label = parentDiv ? parentDiv.querySelector('span') : null; if (label) { const key = label.textContent.replace(':', '').trim(); let value = select.value; // Convert types if (value === 'null') value = null; else if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); gameChangeable[key] = value; } }); // Process optional attributes optionalSelects.forEach((select, index) => { const parentDiv = select.parentElement; const label = parentDiv ? parentDiv.querySelector('span') : null; if (label) { const key = label.textContent.replace(':', '').trim(); let value = select.value; // Convert types if (value === 'null') value = null; else if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); gameOptional[key] = value; } }); // Collect tile group attributes - FIXED VERSION const collectGroupAttributes = (groupIndex) => { const groupChangeable = {}; const groupOptional = {}; // Find changeable selects in this group const changeableContainer = document.getElementById(`tileGroup${groupIndex}Changeable`); if (changeableContainer) { const selects = changeableContainer.querySelectorAll('select'); selects.forEach(select => { const label = select.parentElement.querySelector('span'); if (label) { const key = label.textContent.replace(':', '').trim(); let value = select.value; // Convert types if (value === 'null') value = null; else if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); groupChangeable[key] = value; } }); } // Find optional selects in this group const optionalContainer = document.getElementById(`tileGroup${groupIndex}Optional`); if (optionalContainer) { const selects = optionalContainer.querySelectorAll('select'); selects.forEach(select => { const label = select.parentElement.querySelector('span'); if (label) { const key = label.textContent.replace(':', '').trim(); let value = select.value; // Convert types if (value === 'null') value = null; else if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); groupOptional[key] = value; } }); } return { changeable: groupChangeable, optional: groupOptional }; }; // Collect tilemap attributes - FIXED VERSION const collectMapAttributes = (mapIndex) => { const mapChangeable = {}; const mapOptional = {}; // Find changeable selects in this map const changeableContainer = document.getElementById(`tileMap${mapIndex}Changeable`); if (changeableContainer) { const selects = changeableContainer.querySelectorAll('select'); selects.forEach(select => { const label = select.parentElement.querySelector('span'); if (label) { const key = label.textContent.replace(':', '').trim(); let value = select.value; // Convert types if (value === 'null') value = null; else if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); mapChangeable[key] = value; } }); } // Find optional selects in this map const optionalContainer = document.getElementById(`tileMap${mapIndex}Optional`); if (optionalContainer) { const selects = optionalContainer.querySelectorAll('select'); selects.forEach(select => { const label = select.parentElement.querySelector('span'); if (label) { const key = label.textContent.replace(':', '').trim(); let value = select.value; // Convert types if (value === 'null') value = null; else if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); mapOptional[key] = value; } }); } return { changeable: mapChangeable, optional: mapOptional }; }; // Helper function to remove null values from objects function removeNulls(obj) { if (Array.isArray(obj)) { return obj.map(removeNulls); } else if (obj !== null && typeof obj === 'object') { const cleaned = {}; for (const [key, value] of Object.entries(obj)) { if (value !== null) { cleaned[key] = removeNulls(value); } } return cleaned; } return obj; } const completeGameObject = { metadata: { id: gameObject.id, name: gameObject.name, version: gameObject.version, exportDate: new Date().toISOString(), editor: "Tile Game Editor" }, // Actual game configuration gameObject: removeNulls({ changeable: gameChangeable, optional: gameOptional }), // Complete tile groups data with attributes tileGroups: typeof groups !== 'undefined' ? groups.map((group, index) => removeNulls({ id: group.id, // Use the group's actual ID name: group.name, category: group.category, // Include category url: group.url, tileCount: group.tiles ? group.tiles.length : 0, tileSize: group.tiles && group.tiles[0] ? group.tiles[0].size : null, attributes: collectGroupAttributes(index), tiles: group.tiles ? group.tiles.map(tile => ({ uniqueId: tile.uniqueId, sourceX: tile.sourceX, sourceY: tile.sourceY, size: tile.size, linearIndex: Math.floor(tile.sourceY / tile.size) * 8 + Math.floor(tile.sourceX / tile.size) })) : [] })) : [], // Complete tilemaps data with attributes tilemaps: typeof tilemaps !== 'undefined' ? tilemaps.map((tilemap, index) => removeNulls({ id: tilemap.id, name: tilemap.name, width: tilemap.width, height: tilemap.height, tileSize: tilemap.tileSize, attributes: collectMapAttributes(index), data: tilemap.data, fillPercentage: tilemap.width && tilemap.height ? ((tilemap.data ? tilemap.data.filter(t => t !== 0).length : 0) / (tilemap.width * tilemap.height) * 100).toFixed(1) : 0 })) : [], // Project statistics statistics: { totalTileGroups: typeof groups !== 'undefined' ? groups.length : 0, totalTiles: typeof groups !== 'undefined' ? groups.reduce((sum, group) => sum + (group.tiles ? group.tiles.length : 0), 0) : 0, totalTilemaps: typeof tilemaps !== 'undefined' ? tilemaps.length : 0, totalPlacedTiles: typeof tilemaps !== 'undefined' ? tilemaps.reduce((sum, map) => sum + (map.data ? map.data.filter(t => t !== 0).length : 0), 0) : 0, uniqueSourceImages: typeof groups !== 'undefined' ? new Set(groups.map(g => g.url).filter(url => url)).size : 0 } }; // Custom JSON stringify with compact arrays const jsonString = JSON.stringify(completeGameObject, null, 2).replace( /\[\s*(\d+(?:,\s*\d+)*)\s*\]/g, (match, numbers) => '[' + numbers.replace(/\s+/g, ' ') + ']' ); // Try modern clipboard API first, then fallback if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(jsonString).then(() => { if (typeof debugAlert === 'function') { debugAlert('Complete game data copied! ' + Math.round(jsonString.length / 1024) + 'KB'); } }).catch(() => { fallbackCopy(jsonString); }); } else { fallbackCopy(jsonString); } function fallbackCopy(text) { // Create temporary textarea for fallback copy const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { const successful = document.execCommand('copy'); if (successful && typeof debugAlert === 'function') { debugAlert('Game data copied (fallback)! ' + Math.round(text.length / 1024) + 'KB'); } else { throw new Error('Copy command failed'); } } catch (err) { // If all copy methods fail, show the data if (typeof debugAlert === 'function') { debugAlert('Copy failed. Data: ' + text.substring(0, 200) + '...'); } console.log('Full game data:', text); } finally { document.body.removeChild(textarea); } } } catch (error) { if (typeof debugAlert === 'function') { debugAlert('Copy failed: ' + error.message); } } } function addGameAttributes() { // Add changeable attributes const changeableContainer = document.querySelector('#gameChangeableContent'); addAttributesToContainer(changeableContainer, gameAttributes.changeable, 'game', 'changeable'); // Add optional attributes const optionalContainer = document.querySelector('#gameOptionalContent'); addAttributesToContainer(optionalContainer, gameAttributes.optional, 'game', 'optional'); } function addAttributesToContainer(container, attributes, section, type) { for (const categoryKey in attributes) { const category = attributes[categoryKey]; if (category.value !== undefined) { // Single attribute addSingleAttribute(container, categoryKey, category, section, type); } else { // Category with sub-attributes const categoryLabel = document.createElement('div'); categoryLabel.textContent = categoryKey + ':'; categoryLabel.style.cssText = 'font-weight: bold; margin: 8px 0 4px 0; color: #fff;'; container.appendChild(categoryLabel); for (const attrKey in category) { const attr = category[attrKey]; addSingleAttribute(container, categoryKey + '.' + attrKey, attr, section, type); } } } } function addSingleAttribute(container, key, attr, section, type) { const attrDiv = document.createElement('div'); attrDiv.style.cssText = 'margin: 4px 0; display: flex; align-items: center; gap: 10px;'; const label = document.createElement('span'); label.textContent = key + ':'; label.style.cssText = 'min-width: 100px; color: #ccc;'; attrDiv.appendChild(label); const select = document.createElement('select'); select.style.cssText = 'background: #222; color: #fff; border: 1px solid #555; padding: 2px 4px; border-radius: 2px; font-size: 11px;'; attr.options.forEach(option => { const optionElement = document.createElement('option'); optionElement.value = option; optionElement.textContent = option === null ? 'null' : option; optionElement.selected = option === attr.value; select.appendChild(optionElement); }); select.onchange = function() { updateAttribute(section, type, key, this.value); }; attrDiv.appendChild(select); container.appendChild(attrDiv); } function updateAttribute(section, type, key, value) { let convertedValue = value; if (value === 'null') convertedValue = null; else if (value === 'true') convertedValue = true; else if (value === 'false') convertedValue = false; else if (!isNaN(value) && value !== '') convertedValue = Number(value); if (typeof debugAlert === 'function') { debugAlert('Updated ' + section + '.' + key + ' = ' + convertedValue); } } function addTileGroupsSection() { const container = document.getElementById('tileGroupsContainer'); if (!container) return; const section = document.createElement('div'); section.style.cssText = 'padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;'; const titleContainer = document.createElement('div'); titleContainer.onclick = () => toggleSection('tileGroupsContent'); titleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 15px;'; const toggle = document.createElement('span'); toggle.id = 'tileGroupsContentToggle'; toggle.textContent = '▼'; toggle.style.cssText = 'margin-right: 8px; color: #6cf;'; const title = document.createElement('h3'); title.textContent = 'Tile Groups'; title.style.cssText = 'color: #6cf; margin: 0;'; titleContainer.appendChild(toggle); titleContainer.appendChild(title); section.appendChild(titleContainer); const content = document.createElement('div'); content.id = 'tileGroupsContent'; if (typeof groups === 'undefined' || !groups || groups.length === 0) { const noGroups = document.createElement('div'); noGroups.textContent = 'No tile groups created yet. Use the Tile Picker to create groups.'; noGroups.style.cssText = 'color: #888; font-style: italic; padding: 10px;'; content.appendChild(noGroups); } else { 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;'; const groupTitleContainer = document.createElement('div'); groupTitleContainer.onclick = () => toggleSection(`tileGroup${index}Content`); groupTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 10px;'; const groupToggle = document.createElement('span'); groupToggle.id = `tileGroup${index}ContentToggle`; groupToggle.textContent = '▼'; groupToggle.style.cssText = 'margin-right: 8px; color: #6cf;'; const groupTitle = document.createElement('h4'); groupTitle.textContent = 'Group ' + (index + 1) + ': ' + (group.name || 'Unnamed'); groupTitle.style.cssText = 'margin: 0; color: #fff;'; groupTitleContainer.appendChild(groupToggle); groupTitleContainer.appendChild(groupTitle); groupDiv.appendChild(groupTitleContainer); const groupContent = document.createElement('div'); groupContent.id = `tileGroup${index}Content`; // Mandatory info - UPDATED to show ID and Category const mandatoryDiv = document.createElement('div'); mandatoryDiv.style.cssText = 'margin-bottom: 10px;'; const mandatoryTitleContainer = document.createElement('div'); mandatoryTitleContainer.onclick = () => toggleSection(`tileGroup${index}Mandatory`); mandatoryTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const mandatoryToggle = document.createElement('span'); mandatoryToggle.id = `tileGroup${index}MandatoryToggle`; mandatoryToggle.textContent = '▼'; mandatoryToggle.style.cssText = 'margin-right: 8px; color: #f44;'; const mandatoryLabel = document.createElement('strong'); mandatoryLabel.textContent = 'Mandatory:'; mandatoryLabel.style.cssText = 'color: #f44;'; mandatoryTitleContainer.appendChild(mandatoryToggle); mandatoryTitleContainer.appendChild(mandatoryLabel); mandatoryDiv.appendChild(mandatoryTitleContainer); const mandatoryInfo = document.createElement('div'); mandatoryInfo.id = `tileGroup${index}Mandatory`; mandatoryInfo.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px; color: #ccc;'; mandatoryInfo.innerHTML = 'ID: ' + (group.id || 'group_' + (index + 1)) + '<br>' + 'Category: ' + (group.category || 'None') + '<br>' + 'Atlas Key: ' + (group.url ? group.url.split('/').pop() : 'none') + '<br>' + 'Tile Size: ' + (group.tiles && group.tiles[0] ? group.tiles[0].size : 32) + 'px<br>' + 'Members: ' + (group.tiles ? group.tiles.length : 0) + ' tiles'; mandatoryDiv.appendChild(mandatoryInfo); groupContent.appendChild(mandatoryDiv); // Changeable attributes const changeableDiv = document.createElement('div'); changeableDiv.style.cssText = 'margin-bottom: 10px;'; const changeableTitleContainer = document.createElement('div'); changeableTitleContainer.onclick = () => toggleSection(`tileGroup${index}Changeable`); changeableTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const changeableToggle = document.createElement('span'); changeableToggle.id = `tileGroup${index}ChangeableToggle`; changeableToggle.textContent = '▼'; changeableToggle.style.cssText = 'margin-right: 8px; color: #4a4;'; const changeableLabel = document.createElement('strong'); changeableLabel.textContent = 'Changeable:'; changeableLabel.style.cssText = 'color: #4a4;'; changeableTitleContainer.appendChild(changeableToggle); changeableTitleContainer.appendChild(changeableLabel); changeableDiv.appendChild(changeableTitleContainer); const changeableContainer = document.createElement('div'); changeableContainer.id = `tileGroup${index}Changeable`; changeableContainer.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px;'; addAttributesToContainer(changeableContainer, tileGroupAttributes.changeable, 'tileGroup', 'changeable'); changeableDiv.appendChild(changeableContainer); groupContent.appendChild(changeableDiv); // Optional attributes const optionalDiv = document.createElement('div'); optionalDiv.style.cssText = 'margin-bottom: 10px;'; const optionalTitleContainer = document.createElement('div'); optionalTitleContainer.onclick = () => toggleSection(`tileGroup${index}Optional`); optionalTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const optionalToggle = document.createElement('span'); optionalToggle.id = `tileGroup${index}OptionalToggle`; optionalToggle.textContent = '▼'; optionalToggle.style.cssText = 'margin-right: 8px; color: #888;'; const optionalLabel = document.createElement('strong'); optionalLabel.textContent = 'Optional:'; optionalLabel.style.cssText = 'color: #888;'; optionalTitleContainer.appendChild(optionalToggle); optionalTitleContainer.appendChild(optionalLabel); optionalDiv.appendChild(optionalTitleContainer); const optionalContainer = document.createElement('div'); optionalContainer.id = `tileGroup${index}Optional`; optionalContainer.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px;'; addAttributesToContainer(optionalContainer, tileGroupAttributes.optional, 'tileGroup', 'optional'); optionalDiv.appendChild(optionalContainer); groupContent.appendChild(optionalDiv); // Add tile previews if tiles exist if (group.tiles && group.tiles.length > 0) { const previewTitleContainer = document.createElement('div'); previewTitleContainer.onclick = () => toggleSection(`tileGroup${index}Previews`); previewTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const previewToggle = document.createElement('span'); previewToggle.id = `tileGroup${index}PreviewsToggle`; previewToggle.textContent = '▼'; previewToggle.style.cssText = 'margin-right: 8px; color: #6cf;'; const previewLabel = document.createElement('strong'); previewLabel.textContent = 'Tile Previews:'; previewLabel.style.cssText = 'color: #6cf;'; previewTitleContainer.appendChild(previewToggle); previewTitleContainer.appendChild(previewLabel); groupContent.appendChild(previewTitleContainer); const previewContainer = document.createElement('div'); previewContainer.id = `tileGroup${index}Previews`; addTilePreviewsToGroup(previewContainer, group); groupContent.appendChild(previewContainer); } groupDiv.appendChild(groupContent); content.appendChild(groupDiv); }); } section.appendChild(content); container.appendChild(section); } function addTilePreviewsToGroup(previewContainer, group) { previewContainer.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;'; // Create canvas for tile display const canvas = document.createElement('canvas'); canvas.width = 32; canvas.height = 32; canvas.style.cssText = 'border: 2px solid #666; border-radius: 4px; background: #000;'; // Draw the tile 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); // Scale to 32x32 for preview ctx.drawImage(tempCanvas, 0, 0, tile.size, tile.size, 0, 0, 32, 32); // Create ID badge (bottom-right) 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;'; // Create linear index badge (top-left) const tileX = Math.floor(tile.sourceX / tile.size); const tileY = Math.floor(tile.sourceY / tile.size); const tilesPerRow = 8; // Default assumption const linearIndex = tileY * tilesPerRow + tileX; const indexBadge = document.createElement('span'); indexBadge.textContent = linearIndex.toString(); indexBadge.style.cssText = 'position: absolute; top: -2px; left: -2px; background: rgba(0,0,0,0.8); color: #fff; font-size: 7px; padding: 1px 2px; border-radius: 2px; border: 1px solid #4a4;'; // Assemble the tile preview tileWrapper.appendChild(canvas); tileWrapper.appendChild(idBadge); tileWrapper.appendChild(indexBadge); previewContainer.appendChild(tileWrapper); }); } function addTilemapsSection() { const container = document.getElementById('tilemapsContainer'); if (!container) return; const section = document.createElement('div'); section.style.cssText = 'padding: 15px; background: #1a1a1a; border-radius: 8px; margin-bottom: 20px;'; const titleContainer = document.createElement('div'); titleContainer.onclick = () => toggleSection('tilemapsContent'); titleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 15px;'; const toggle = document.createElement('span'); toggle.id = 'tilemapsContentToggle'; toggle.textContent = '▼'; toggle.style.cssText = 'margin-right: 8px; color: #6cf;'; const title = document.createElement('h3'); title.textContent = 'Tile Maps'; title.style.cssText = 'color: #6cf; margin: 0;'; titleContainer.appendChild(toggle); titleContainer.appendChild(title); section.appendChild(titleContainer); const content = document.createElement('div'); content.id = 'tilemapsContent'; if (typeof tilemaps === 'undefined' || !tilemaps || tilemaps.length === 0) { const noMaps = document.createElement('div'); noMaps.textContent = 'No tilemaps created yet. Use the Tilemap Editor to create maps.'; noMaps.style.cssText = 'color: #888; font-style: italic; padding: 10px;'; content.appendChild(noMaps); } else { tilemaps.forEach((tilemap, index) => { const isActive = typeof currentTilemapIndex !== 'undefined' && currentTilemapIndex === index; const totalTiles = tilemap.data ? tilemap.data.filter(t => t !== 0).length : 0; const fillPercent = tilemap.width && tilemap.height ? ((totalTiles / (tilemap.width * tilemap.height)) * 100).toFixed(1) : '0.0'; const mapDiv = document.createElement('div'); mapDiv.style.cssText = 'margin-bottom: 20px; padding: 15px; background: #2a2a2a; border-radius: 6px; border-left: 4px solid #6cf;'; const mapTitleContainer = document.createElement('div'); mapTitleContainer.onclick = () => toggleSection(`tileMap${index}Content`); mapTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 10px;'; const mapToggle = document.createElement('span'); mapToggle.id = `tileMap${index}ContentToggle`; mapToggle.textContent = '▼'; mapToggle.style.cssText = 'margin-right: 8px; color: #6cf;'; const mapTitle = document.createElement('h4'); mapTitle.textContent = tilemap.name + (isActive ? ' (Active)' : ''); mapTitle.style.cssText = 'margin: 0; color: ' + (isActive ? '#6cf' : '#fff') + ';'; mapTitleContainer.appendChild(mapToggle); mapTitleContainer.appendChild(mapTitle); mapDiv.appendChild(mapTitleContainer); const mapContent = document.createElement('div'); mapContent.id = `tileMap${index}Content`; // Mandatory info const mandatoryDiv = document.createElement('div'); mandatoryDiv.style.cssText = 'margin-bottom: 10px;'; const mandatoryTitleContainer = document.createElement('div'); mandatoryTitleContainer.onclick = () => toggleSection(`tileMap${index}Mandatory`); mandatoryTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const mandatoryToggle = document.createElement('span'); mandatoryToggle.id = `tileMap${index}MandatoryToggle`; mandatoryToggle.textContent = '▼'; mandatoryToggle.style.cssText = 'margin-right: 8px; color: #f44;'; const mandatoryLabel = document.createElement('strong'); mandatoryLabel.textContent = 'Mandatory:'; mandatoryLabel.style.cssText = 'color: #f44;'; mandatoryTitleContainer.appendChild(mandatoryToggle); mandatoryTitleContainer.appendChild(mandatoryLabel); mandatoryDiv.appendChild(mandatoryTitleContainer); const mandatoryInfo = document.createElement('div'); mandatoryInfo.id = `tileMap${index}Mandatory`; mandatoryInfo.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px; color: #ccc;'; mandatoryInfo.innerHTML = 'ID: map_' + tilemap.id + '<br>' + 'Dimensions: ' + tilemap.width + ' x ' + tilemap.height + '<br>' + 'Fill: ' + fillPercent + '% (' + totalTiles + ' tiles)'; mandatoryDiv.appendChild(mandatoryInfo); mapContent.appendChild(mandatoryDiv); // Changeable attributes const changeableDiv = document.createElement('div'); changeableDiv.style.cssText = 'margin-bottom: 10px;'; const changeableTitleContainer = document.createElement('div'); changeableTitleContainer.onclick = () => toggleSection(`tileMap${index}Changeable`); changeableTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const changeableToggle = document.createElement('span'); changeableToggle.id = `tileMap${index}ChangeableToggle`; changeableToggle.textContent = '▼'; changeableToggle.style.cssText = 'margin-right: 8px; color: #4a4;'; const changeableLabel = document.createElement('strong'); changeableLabel.textContent = 'Changeable:'; changeableLabel.style.cssText = 'color: #4a4;'; changeableTitleContainer.appendChild(changeableToggle); changeableTitleContainer.appendChild(changeableLabel); changeableDiv.appendChild(changeableTitleContainer); const changeableContainer = document.createElement('div'); changeableContainer.id = `tileMap${index}Changeable`; changeableContainer.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px;'; addAttributesToContainer(changeableContainer, tileMapAttributes.changeable, 'tileMap', 'changeable'); changeableDiv.appendChild(changeableContainer); mapContent.appendChild(changeableDiv); // Optional attributes const optionalDiv = document.createElement('div'); optionalDiv.style.cssText = 'margin-bottom: 10px;'; const optionalTitleContainer = document.createElement('div'); optionalTitleContainer.onclick = () => toggleSection(`tileMap${index}Optional`); optionalTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const optionalToggle = document.createElement('span'); optionalToggle.id = `tileMap${index}OptionalToggle`; optionalToggle.textContent = '▼'; optionalToggle.style.cssText = 'margin-right: 8px; color: #888;'; const optionalLabel = document.createElement('strong'); optionalLabel.textContent = 'Optional:'; optionalLabel.style.cssText = 'color: #888;'; optionalTitleContainer.appendChild(optionalToggle); optionalTitleContainer.appendChild(optionalLabel); optionalDiv.appendChild(optionalTitleContainer); const optionalContainer = document.createElement('div'); optionalContainer.id = `tileMap${index}Optional`; optionalContainer.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px;'; addAttributesToContainer(optionalContainer, tileMapAttributes.optional, 'tileMap', 'optional'); optionalDiv.appendChild(optionalContainer); mapContent.appendChild(optionalDiv); // Add Phaser array if data exists if (tilemap.data && tilemap.data.length > 0) { const arrayTitleContainer = document.createElement('div'); arrayTitleContainer.onclick = () => toggleSection(`tileMap${index}Array`); arrayTitleContainer.style.cssText = 'cursor: pointer; display: flex; align-items: center; margin-bottom: 8px;'; const arrayToggle = document.createElement('span'); arrayToggle.id = `tileMap${index}ArrayToggle`; arrayToggle.textContent = '▼'; arrayToggle.style.cssText = 'margin-right: 8px; color: #6cf;'; const arrayLabel = document.createElement('strong'); arrayLabel.textContent = 'Phaser 2D Array:'; arrayLabel.style.cssText = 'color: #6cf;'; arrayTitleContainer.appendChild(arrayToggle); arrayTitleContainer.appendChild(arrayLabel); mapContent.appendChild(arrayTitleContainer); const arrayContainer = document.createElement('div'); arrayContainer.id = `tileMap${index}Array`; addPhaserArrayToMap(arrayContainer, tilemap, index); mapContent.appendChild(arrayContainer); } mapDiv.appendChild(mapContent); content.appendChild(mapDiv); }); } section.appendChild(content); container.appendChild(section); } function addPhaserArrayToMap(arrayContainer, tilemap, mapIndex) { arrayContainer.style.cssText = 'background: #1a1a1a; padding: 10px; border-radius: 4px; margin-top: 5px;'; const arrayDisplay = document.createElement('div'); arrayDisplay.style.cssText = 'font-family: "Courier New", monospace; font-size: 10px; color: #ccc; overflow-x: auto; max-height: 150px; overflow-y: auto;'; // Build the array text let arrayText = '[\n'; for (let y = 0; y < tilemap.height; y++) { let row = ' ['; for (let x = 0; x < tilemap.width; x++) { const dataIndex = y * tilemap.width + x; const tileId = tilemap.data[dataIndex] || 0; row += tileId.toString().padStart(3, ' '); if (x < tilemap.width - 1) row += ','; } row += ']'; if (y < tilemap.height - 1) row += ','; arrayText += row + '\n'; } arrayText += ']'; // Display the formatted array const lines = arrayText.split('\n'); lines.forEach(line => { const lineDiv = document.createElement('div'); lineDiv.textContent = line; lineDiv.style.cssText = 'line-height: 1.2;'; arrayDisplay.appendChild(lineDiv); }); arrayContainer.appendChild(arrayDisplay); // Add copy button 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.onclick = function() { copyTilemapArray(arrayText, mapIndex); }; arrayContainer.appendChild(copyBtn); } function copyTilemapArray(arrayText, mapIndex) { try { navigator.clipboard.writeText(arrayText).then(function() { if (typeof debugAlert === 'function') { debugAlert('Array copied to clipboard!'); } }).catch(function() { if (typeof debugAlert === 'function') { debugAlert('Array preview: ' + arrayText.substring(0, 50) + '...'); } }); } catch (error) { if (typeof debugAlert === 'function') { debugAlert('Copy failed: ' + error.message); } } } if (typeof debugAlert === 'function') { debugAlert('object.js loaded successfully'); }