// 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 configuration from attributes.json
const gameAttributes = {
"changeable": {
"physicsDefaults": {
"engine": { "value": "arcade", "options": ["arcade", "matter"] },
"gravityY": { "value": 300, "options": [0, 300, 600] },
"gravityX": { "value": 0, "options": [0, 50, 100] },
"debug": { "value": false, "options": [false, true] }
},
"rendering": {
"pixelArt": { "value": true, "options": [true, false] },
"antialias": { "value": false, "options": [false, true] },
"roundPixels": { "value": true, "options": [true, false] }
},
"camera": {
"zoom": { "value": 1, "options": [0.5, 1, 2, 3] },
"scrollFactor":{ "value": 1, "options": [0, 0.5, 1, 2] }
}
},
"optional": {
"audio": { "value": null, "options": ["music_on", "sfx_on", "mute_all"] },
"ui": { "value": null, "options": ["hud", "inventory", "dialog", "minimap"] },
"saveSystem": { "value": null, "options": ["none", "autosave", "manual", "checkpoint"] },
"state": { "value": null, "options": [null, "fish", "mammal", "bird"] },
"metadata": { "value": null, "options": ["author", "notes", "tags"] }
}
};
const tileGroupAttributes = {
"changeable": {
"size": {
"tileWidth": { "value": 16, "options": [8, 16, 32, 64] },
"tileHeight": { "value": 16, "options": [8, 16, 32, 64] },
"scale": { "value": 1, "options": [0.5, 1, 2] }
},
"physics": {
"bodyType": { "value": "static", "options": ["static", "dynamic", "kinematic"] },
"immovable": { "value": false, "options": [false, true] },
"collidable": { "value": false, "options": [false, true] }
},
"render": {
"depth": { "value": 0, "options": [-10, 0, 10, 100] },
"alpha": { "value": 1, "options": [0.5, 0.75, 1] },
"visible": { "value": true, "options": [true, false] }
}
},
"optional": {
"atlasKey": { "value": null, "options": ["defaultAtlas", "terrainSet", "characters", "props"] },
"aiProfile": { "value": null, "options": ["idle", "patrol", "chaser", "wander"] },
"properties": { "value": null, "options": ["hazard", "one_way", "breakable", "slippery"] },
"soundOnUse": { "value": null, "options": ["step_grass", "step_stone", "coin", "hit"] },
"stateTag": { "value": null, "options": [null, "fish", "mammal", "bird"] }
}
};
const tileMapAttributes = {
"changeable": {
"grid": {
"orientation": { "value": "orthogonal", "options": ["orthogonal", "isometric"] },
"cellWidth": { "value": 16, "options": [8, 16, 32, 64] },
"cellHeight": { "value": 16, "options": [8, 16, 32, 64] }
},
"camera": {
"zoom": { "value": 1, "options": [0.5, 1, 2, 3] },
"follow": { "value": "player", "options": ["player", "none", "target_key"] }
},
"physics": {
"collisionMode": { "value": "perTile", "options": ["none", "layer", "perTile"] }
},
"timeLimitSec": { "value": 0, "options": [0, 60, 120, 300] },
"music": { "value": "none", "options": ["none", "level_theme_1", "level_theme_2"] }
},
"optional": {
"events": { "value": null, "options": ["spawnEnemy", "dialog", "collectItem", "teleport"] },
"ambient": { "value": null, "options": ["day", "night", "fog", "rain", "snow"] },
"progression": { "value": null, "options": ["nextMap", "branchIf", "unlockFlag", "checkpoint"] },
"stateTag": { "value": null, "options": [null, "fish", "mammal", "bird"] },
"metadata": { "value": null, "options": ["author", "difficulty", "estimated_time"] }
}
};
function openGameController() {
const overlayContent = document.getElementById('overlayContent');
overlayContent.innerHTML = `
<div style="height: 100%; overflow: auto; padding: 10px;">
<div id="gameController"></div>
</div>
`;
renderGameController();
}
function renderGameController() {
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="margin-bottom: 15px;">
<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: ${gameObject.id}</div>
<div>Name: ${gameObject.name}</div>
<div>Version: ${gameObject.version}</div>
</div>
</div>
<div id="gameChangeable" style="margin-bottom: 15px;">
<strong style="color: #4a4;">Changeable:</strong>
<div style="margin: 8px 0; padding: 10px; background: #333; border-radius: 4px; font-size: 11px;"></div>
</div>
<div id="gameOptional">
<strong style="color: #888;">Optional:</strong>
<div 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 addGameAttributes() {
// Add changeable attributes
const changeableContainer = document.querySelector('#gameChangeable > div');
addAttributesToContainer(changeableContainer, gameAttributes.changeable, 'game', 'changeable');
// Add optional attributes
const optionalContainer = document.querySelector('#gameOptional > div');
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 title = document.createElement('h3');
title.textContent = 'Tile Groups';
title.style.cssText = 'color: #6cf; margin: 0 0 15px 0;';
section.appendChild(title);
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;';
section.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 groupTitle = document.createElement('h4');
groupTitle.textContent = 'Group ' + (index + 1) + ': ' + (group.name || 'Unnamed');
groupTitle.style.cssText = 'margin: 0 0 10px 0; color: #fff;';
groupDiv.appendChild(groupTitle);
// Mandatory info
const mandatoryDiv = document.createElement('div');
mandatoryDiv.style.cssText = 'margin-bottom: 10px;';
const mandatoryLabel = document.createElement('strong');
mandatoryLabel.textContent = 'Mandatory:';
mandatoryLabel.style.cssText = 'color: #f44;';
mandatoryDiv.appendChild(mandatoryLabel);
const mandatoryInfo = document.createElement('div');
mandatoryInfo.style.cssText = 'margin: 8px 0; padding: 8px; background: #333; border-radius: 4px; font-size: 11px; color: #ccc;';
mandatoryInfo.innerHTML = 'ID: group_' + (index + 1) + '<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);
groupDiv.appendChild(mandatoryDiv);
// Changeable attributes
const changeableDiv = document.createElement('div');
changeableDiv.style.cssText = 'margin-bottom: 10px;';
const changeableLabel = document.createElement('strong');
changeableLabel.textContent = 'Changeable:';
changeableLabel.style.cssText = 'color: #4a4;';
changeableDiv.appendChild(changeableLabel);
const changeableContainer = document.createElement('div');
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);
groupDiv.appendChild(changeableDiv);
// Optional attributes
const optionalDiv = document.createElement('div');
optionalDiv.style.cssText = 'margin-bottom: 10px;';
const optionalLabel = document.createElement('strong');
optionalLabel.textContent = 'Optional:';
optionalLabel.style.cssText = 'color: #888;';
optionalDiv.appendChild(optionalLabel);
const optionalContainer = document.createElement('div');
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);
groupDiv.appendChild(optionalDiv);
// Add tile previews if tiles exist
if (group.tiles && group.tiles.length > 0) {
addTilePreviewsToGroup(groupDiv, group);
}
section.appendChild(groupDiv);
});
}
container.appendChild(section);
}
function addTilePreviewsToGroup(groupDiv, group) {
const previewContainer = document.createElement('div');
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);
});
groupDiv.appendChild(previewContainer);
}
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 title = document.createElement('h3');
title.textContent = 'Tile Maps';
title.style.cssText = 'color: #6cf; margin: 0 0 15px 0;';
section.appendChild(title);
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;';
section.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 mapTitle = document.createElement('h4');
mapTitle.textContent = tilemap.name + (isActive ? ' (Active)' : '');
mapTitle.style.cssText = 'margin: 0 0 10px 0; color: ' + (isActive ? '#6cf' : '#fff') + ';';
mapDiv.appendChild(mapTitle);
// Mandatory info
const mandatoryDiv = document.createElement('div');
mandatoryDiv.style.cssText = 'margin-bottom: 10px;';
const mandatoryLabel = document.createElement('strong');
mandatoryLabel.textContent = 'Mandatory:';
mandatoryLabel.style.cssText = 'color: #f44;';
mandatoryDiv.appendChild(mandatoryLabel);
const mandatoryInfo = document.createElement('div');
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);
mapDiv.appendChild(mandatoryDiv);
// Changeable attributes
const changeableDiv = document.createElement('div');
changeableDiv.style.cssText = 'margin-bottom: 10px;';
const changeableLabel = document.createElement('strong');
changeableLabel.textContent = 'Changeable:';
changeableLabel.style.cssText = 'color: #4a4;';
changeableDiv.appendChild(changeableLabel);
const changeableContainer = document.createElement('div');
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);
mapDiv.appendChild(changeableDiv);
// Optional attributes
const optionalDiv = document.createElement('div');
optionalDiv.style.cssText = 'margin-bottom: 10px;';
const optionalLabel = document.createElement('strong');
optionalLabel.textContent = 'Optional:';
optionalLabel.style.cssText = 'color: #888;';
optionalDiv.appendChild(optionalLabel);
const optionalContainer = document.createElement('div');
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);
mapDiv.appendChild(optionalDiv);
// Add Phaser array if data exists
if (tilemap.data && tilemap.data.length > 0) {
addPhaserArrayToMap(mapDiv, tilemap, index);
}
section.appendChild(mapDiv);
});
}
container.appendChild(section);
}
function addPhaserArrayToMap(mapDiv, tilemap, mapIndex) {
const arrayContainer = document.createElement('div');
const arrayLabel = document.createElement('strong');
arrayLabel.textContent = 'Phaser 2D Array:';
arrayLabel.style.cssText = 'color: #6cf;';
arrayContainer.appendChild(arrayLabel);
const arrayDisplay = document.createElement('div');
arrayDisplay.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;';
// 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);
mapDiv.appendChild(arrayContainer);
}
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);
}
}
}
function updateSetting(key, value) {
if (typeof debugAlert === 'function') {
debugAlert('Updated ' + key + ' = ' + value);
}
}
if (typeof debugAlert === 'function') {
debugAlert('object.js loaded successfully');
}