Refactored code formatting to use consistent indentation and object destructuring across client and server files.
All checks were successful
Build and publish / Build Container (push) Successful in 1m44s

This commit is contained in:
2026-03-02 12:28:18 -05:00
parent 09a59f170c
commit 7b2621c264
5 changed files with 627 additions and 519 deletions

View File

@@ -13,8 +13,10 @@ function shadeColor(hex, amount) {
}
function hex2Int(hex) {
let r = 0, g = 0, b = 0;
if (hex.length === 4) {
let r = 0,
g = 0,
b = 0;
if(hex.length === 4) {
r = parseInt(hex[1] + hex[1], 16);
g = parseInt(hex[2] + hex[2], 16);
b = parseInt(hex[3] + hex[3], 16);
@@ -23,15 +25,19 @@ function shadeColor(hex, amount) {
g = parseInt(hex.slice(3, 5), 16);
b = parseInt(hex.slice(5, 7), 16);
}
return { r, g, b };
return {
r,
g,
b
};
}
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1 / 6) return p + (q - p) * 6 * t;
if(t < 1 / 2) return q;
if(t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
@@ -39,21 +45,38 @@ function shadeColor(hex, amount) {
return '#' + dec2Hex(r) + dec2Hex(g) + dec2Hex(b);
}
let { r, g, b } = hex2Int(hex);
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
let {
r,
g,
b
} = hex2Int(hex);
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b),
min = Math.min(r, g, b);
let h,
s,
l = (max + min) / 2;
if (max === min) {
if(max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
default: h = 0; break;
switch(max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
default:
h = 0;
break;
}
h /= 6;
}
@@ -67,7 +90,7 @@ function shadeColor(hex, amount) {
class BtnComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>

View File

@@ -9,216 +9,216 @@ const TILE_DEPTH = 16;
// THEME HANDLER
// ============================================
function applyTheme(theme) {
const body = document.body;
const body = document.body;
if (theme.background.image) {
body.style.backgroundImage = `url(${theme.background.image})`;
}
if(theme.background.image) {
body.style.backgroundImage = `url(${theme.background.image})`;
}
body.style.backgroundSize = theme.background.style || 'cover';
body.style.backgroundPosition = 'center';
body.style.backgroundRepeat = 'no-repeat';
body.style.backgroundAttachment = 'fixed';
body.style.backgroundSize = theme.background.style || 'cover';
body.style.backgroundPosition = 'center';
body.style.backgroundRepeat = 'no-repeat';
body.style.backgroundAttachment = 'fixed';
const root = document.documentElement;
Object.entries(theme.colors).forEach(([key, value]) => {
const cssVar = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
root.style.setProperty(cssVar, value);
});
const root = document.documentElement;
Object.entries(theme.colors).forEach(([key, value]) => {
const cssVar = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
root.style.setProperty(cssVar, value);
});
console.log('🎨 Theme applied:', theme.name);
console.log('🎨 Theme applied:', theme.name);
}
// ============================================
// TILE RENDERER
// ============================================
function isoToScreen(gridX, gridY) {
return {
x: (gridX - gridY) * (TILE_WIDTH / 2) + window.innerWidth / 2,
y: (gridX + gridY) * (TILE_HEIGHT / 2) + 100
};
return {
x: (gridX - gridY) * (TILE_WIDTH / 2) + window.innerWidth / 2,
y: (gridX + gridY) * (TILE_HEIGHT / 2) + 100
};
}
function createTile(tileData, theme) {
const graphics = new PIXI.Graphics();
const pos = isoToScreen(tileData.x, tileData.y);
const graphics = new PIXI.Graphics();
const pos = isoToScreen(tileData.x, tileData.y);
const colors = {
top: parseInt(theme.colors.tileTop.replace('#', '0x')),
side: parseInt(theme.colors.tileSide.replace('#', '0x')),
grid: parseInt(theme.colors.gridColor.replace('#', '0x')),
highlight: parseInt(theme.colors.tileHighlight.replace('#', '0x')),
gridHighlight: parseInt(theme.colors.gridHighlight.replace('#', '0x'))
};
const colors = {
top: parseInt(theme.colors.tileTop.replace('#', '0x')),
side: parseInt(theme.colors.tileSide.replace('#', '0x')),
grid: parseInt(theme.colors.gridColor.replace('#', '0x')),
highlight: parseInt(theme.colors.tileHighlight.replace('#', '0x')),
gridHighlight: parseInt(theme.colors.gridHighlight.replace('#', '0x'))
};
function drawNormalTile() {
graphics.clear();
graphics.beginFill(colors.top);
graphics.lineStyle(1, colors.grid);
graphics.moveTo(pos.x, pos.y);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y);
graphics.endFill();
function drawNormalTile() {
graphics.clear();
graphics.beginFill(colors.top);
graphics.lineStyle(1, colors.grid);
graphics.moveTo(pos.x, pos.y);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y);
graphics.endFill();
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.grid);
graphics.moveTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.grid);
graphics.moveTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.grid);
graphics.moveTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
}
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.grid);
graphics.moveTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
}
function drawHighlightTile() {
graphics.clear();
graphics.beginFill(colors.highlight);
graphics.lineStyle(2, colors.gridHighlight);
graphics.moveTo(pos.x, pos.y);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y);
graphics.endFill();
function drawHighlightTile() {
graphics.clear();
graphics.beginFill(colors.highlight);
graphics.lineStyle(2, colors.gridHighlight);
graphics.moveTo(pos.x, pos.y);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y);
graphics.endFill();
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.gridHighlight);
graphics.moveTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.gridHighlight);
graphics.moveTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x - TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.gridHighlight);
graphics.moveTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
}
graphics.beginFill(colors.side);
graphics.lineStyle(1, colors.gridHighlight);
graphics.moveTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT);
graphics.lineTo(pos.x, pos.y + TILE_HEIGHT + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2 + TILE_DEPTH);
graphics.lineTo(pos.x + TILE_WIDTH / 2, pos.y + TILE_HEIGHT / 2);
graphics.endFill();
}
drawNormalTile();
drawNormalTile();
graphics.interactive = true;
graphics.buttonMode = true;
graphics.gridX = tileData.x;
graphics.gridY = tileData.y;
graphics.interactive = true;
graphics.buttonMode = true;
graphics.gridX = tileData.x;
graphics.gridY = tileData.y;
graphics.on('pointerover', () => {
drawHighlightTile();
});
graphics.on('pointerover', () => {
drawHighlightTile();
});
graphics.on('pointerout', () => {
drawNormalTile();
});
graphics.on('pointerout', () => {
drawNormalTile();
});
return graphics;
return graphics;
}
function createPet(gridX, gridY, name = 'PET') {
const container = new PIXI.Container();
const pos = isoToScreen(gridX, gridY);
const container = new PIXI.Container();
const pos = isoToScreen(gridX, gridY);
const body = new PIXI.Graphics();
body.beginFill(0xff6b9d);
body.drawCircle(0, -30, 15);
body.endFill();
const body = new PIXI.Graphics();
body.beginFill(0xff6b9d);
body.drawCircle(0, -30, 15);
body.endFill();
body.beginFill(0xffffff);
body.drawCircle(-5, -32, 4);
body.drawCircle(5, -32, 4);
body.endFill();
body.beginFill(0xffffff);
body.drawCircle(-5, -32, 4);
body.drawCircle(5, -32, 4);
body.endFill();
body.beginFill(0x000000);
body.drawCircle(-5, -32, 2);
body.drawCircle(5, -32, 2);
body.endFill();
body.beginFill(0x000000);
body.drawCircle(-5, -32, 2);
body.drawCircle(5, -32, 2);
body.endFill();
const nameText = new PIXI.Text(name, {
fontFamily: 'Courier New',
fontSize: 12,
fill: '#ffffff',
stroke: '#000000',
strokeThickness: 2
});
nameText.anchor.set(0.5);
nameText.y = -50;
const nameText = new PIXI.Text(name, {
fontFamily: 'Courier New',
fontSize: 12,
fill: '#ffffff',
stroke: '#000000',
strokeThickness: 2
});
nameText.anchor.set(0.5);
nameText.y = -50;
container.addChild(body);
container.addChild(nameText);
container.x = pos.x;
container.y = pos.y;
container.gridX = gridX;
container.gridY = gridY;
container.addChild(body);
container.addChild(nameText);
container.x = pos.x;
container.y = pos.y;
container.gridX = gridX;
container.gridY = gridY;
return container;
return container;
}
// ============================================
// GAME CLASS
// ============================================
class Game {
constructor() {
this.worldId = '';
this.world = null;
this.app = null;
this.pet = null;
this.otherPlayers = new Map();
this.isMoving = false;
this.dialogue = null;
this.keys = {};
constructor() {
this.worldId = '';
this.world = null;
this.app = null;
this.pet = null;
this.otherPlayers = new Map();
this.isMoving = false;
this.dialogue = null;
this.keys = {};
// Use global singleton 🌍
this.navi = window.navi;
this.worldActions = null;
// Use global singleton 🌍
this.navi = window.navi;
this.worldActions = null;
this.playerInfo = {
name: 'Guest',
apiUrl: this.navi.navi
};
}
this.playerInfo = {
name: 'Guest',
apiUrl: this.navi.navi
};
}
async init() {
try {
// Join world with callbacks 🌍
async init() {
try {
// Join world with callbacks 🌍
await this.navi.init();
this.worldActions = this.navi.connect(this.worldId);
this.worldActions = this.navi.connect(this.worldId);
this.worldActions.onData = (data) => {
this.world = data;
applyTheme(this.world.theme);
this.initializeRenderer();
}
};
this.worldActions.onPlayers = (players) => {
players.forEach(player => {
if (player.name !== this.navi.info.name) {
if(player.name !== this.navi.info.name) {
this.addOtherPlayer(player);
}
});
}
};
this.worldActions.onJoined = (player) => {
this.addOtherPlayer(player);
}
};
this.worldActions.onMoved = (data) => {
const sprite = this.otherPlayers.get(data.socketId);
if (sprite) {
if(sprite) {
this.moveOtherPlayer(sprite, data.x, data.y);
}
};
@@ -226,149 +226,147 @@ class Game {
this.worldActions.onLeft = (data) => {
const sprite = this.otherPlayers.get(data.socketId);
if(sprite) this.otherPlayers.delete(data.socketId);
}
};
this.worldActions.onError = (error) => console.error('❌ World error:', error);
console.log('✨ Game initializing...');
} catch (error) {
console.error('❌ Failed to initialize game:', error);
}
}
console.log('✨ Game initializing...');
} catch(error) {
console.error('❌ Failed to initialize game:', error);
}
}
addOtherPlayer(player) {
const sprite = createPet(player.x, player.y, player.name);
sprite.alpha = 0.7;
this.otherPlayers.set(player.socketId, sprite);
this.app.stage.addChild(sprite);
}
addOtherPlayer(player) {
const sprite = createPet(player.x, player.y, player.name);
sprite.alpha = 0.7;
this.otherPlayers.set(player.socketId, sprite);
this.app.stage.addChild(sprite);
}
moveOtherPlayer(sprite, targetX, targetY) {
const targetPos = isoToScreen(targetX, targetY);
moveOtherPlayer(sprite, targetX, targetY) {
const targetPos = isoToScreen(targetX, targetY);
const startX = sprite.x;
const startY = sprite.y;
let progress = 0;
const startX = sprite.x;
const startY = sprite.y;
let progress = 0;
const animate = () => {
progress += 0.08;
if (progress >= 1) {
sprite.x = targetPos.x;
sprite.y = targetPos.y;
sprite.gridX = targetX;
sprite.gridY = targetY;
return;
}
const animate = () => {
progress += 0.08;
if(progress >= 1) {
sprite.x = targetPos.x;
sprite.y = targetPos.y;
sprite.gridX = targetX;
sprite.gridY = targetY;
return;
}
sprite.x = startX + (targetPos.x - startX) * progress;
sprite.y = startY + (targetPos.y - startY) * progress;
sprite.x = startX + (targetPos.x - startX) * progress;
sprite.y = startY + (targetPos.y - startY) * progress;
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
};
animate();
}
animate();
}
initializeRenderer() {
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundAlpha: 0,
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true
});
document.getElementById('game').appendChild(this.app.view);
initializeRenderer() {
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundAlpha: 0,
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true
});
document.getElementById('game').appendChild(this.app.view);
const tiles = new PIXI.Container();
this.app.stage.addChild(tiles);
const tiles = new PIXI.Container();
this.app.stage.addChild(tiles);
this.world.tiles.forEach(tileData => {
const tile = createTile(tileData, this.world.theme);
tile.on('pointerdown', () => this.movePetTo(tile.gridX, tile.gridY));
tiles.addChild(tile);
});
this.world.tiles.forEach(tileData => {
const tile = createTile(tileData, this.world.theme);
tile.on('pointerdown', () => this.movePetTo(tile.gridX, tile.gridY));
tiles.addChild(tile);
});
const spawn = this.world.tiles.find(t => t.type === 'spawn');
this.pet = createPet(spawn.x, spawn.y, this.playerInfo.name);
this.app.stage.addChild(this.pet);
this.pet = createPet(spawn.x, spawn.y, this.playerInfo.name);
this.app.stage.addChild(this.pet);
this.dialogue = document.getElementById('llm');
this.dialogue = document.getElementById('llm');
this.setupInput();
this.app.ticker.add(() => this.gameLoop());
}
this.setupInput();
this.app.ticker.add(() => this.gameLoop());
}
movePetTo(targetX, targetY) {
if (this.isMoving ||
targetX < 0 || targetX >= this.world.gridSize ||
targetY < 0 || targetY >= this.world.gridSize) return;
movePetTo(targetX, targetY) {
if(this.isMoving || targetX < 0 || targetX >= this.world.gridSize || targetY < 0 || targetY >= this.world.gridSize) return;
this.isMoving = true;
const targetPos = isoToScreen(targetX, targetY);
this.isMoving = true;
const targetPos = isoToScreen(targetX, targetY);
const startX = this.pet.x;
const startY = this.pet.y;
let progress = 0;
const startX = this.pet.x;
const startY = this.pet.y;
let progress = 0;
const animate = () => {
progress += 0.08;
if (progress >= 1) {
this.pet.x = targetPos.x;
this.pet.y = targetPos.y;
this.pet.gridX = targetX;
this.pet.gridY = targetY;
this.isMoving = false;
const animate = () => {
progress += 0.08;
if(progress >= 1) {
this.pet.x = targetPos.x;
this.pet.y = targetPos.y;
this.pet.gridX = targetX;
this.pet.gridY = targetY;
this.isMoving = false;
// Use API action to send move 📤
if (this.worldActions) {
this.worldActions.move(targetX, targetY);
}
return;
}
// Use API action to send move 📤
if(this.worldActions) {
this.worldActions.move(targetX, targetY);
}
return;
}
this.pet.x = startX + (targetPos.x - startX) * progress;
this.pet.y = startY + (targetPos.y - startY) * progress;
this.pet.x = startX + (targetPos.x - startX) * progress;
this.pet.y = startY + (targetPos.y - startY) * progress;
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
};
animate();
}
animate();
}
setupInput() {
window.addEventListener('keydown', (e) => {
if (this.dialogue.isOpen) return;
setupInput() {
window.addEventListener('keydown', (e) => {
if(this.dialogue.isOpen) return;
this.keys[e.key.toLowerCase()] = true;
this.keys[e.key.toLowerCase()] = true;
if (!this.isMoving) {
let newX = this.pet.gridX;
let newY = this.pet.gridY;
if(!this.isMoving) {
let newX = this.pet.gridX;
let newY = this.pet.gridY;
if (this.keys['w'] || this.keys['arrowup']) {
newY--;
} else if (this.keys['s'] || this.keys['arrowdown']) {
newY++;
} else if (this.keys['a'] || this.keys['arrowleft']) {
newX--;
} else if (this.keys['d'] || this.keys['arrowright']) {
newX++;
}
if(this.keys['w'] || this.keys['arrowup']) {
newY--;
} else if(this.keys['s'] || this.keys['arrowdown']) {
newY++;
} else if(this.keys['a'] || this.keys['arrowleft']) {
newX--;
} else if(this.keys['d'] || this.keys['arrowright']) {
newX++;
}
this.movePetTo(newX, newY);
}
});
this.movePetTo(newX, newY);
}
});
window.addEventListener('keyup', (e) => {
this.keys[e.key.toLowerCase()] = false;
});
}
window.addEventListener('keyup', (e) => {
this.keys[e.key.toLowerCase()] = false;
});
}
gameLoop() {
if (!this.isMoving && this.pet) {
this.pet.children[0].y = -30 + Math.sin(Date.now() / 300) * 2;
}
}
gameLoop() {
if(!this.isMoving && this.pet) {
this.pet.children[0].y = -30 + Math.sin(Date.now() / 300) * 2;
}
}
}
// ============================================