generated from ztimson/template
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
All checks were successful
Build and publish / Build Container (push) Successful in 1m44s
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>NetNavi v1.0.0</title>
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link rel="icon" href="/favicon.png"/>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
|
||||
<script src="https://cdn.socket.io/4.6.0/socket.io.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/7.3.2/pixi.min.js"></script>
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
<script type="module">
|
||||
import Navi from './navi.mjs';
|
||||
|
||||
window.navi = new Navi();
|
||||
</script>
|
||||
<script type="module" src="/components/jukebox.mjs"></script>
|
||||
|
||||
258
public/navi.mjs
258
public/navi.mjs
@@ -1,4 +1,4 @@
|
||||
export default class Navi {
|
||||
class Navi {
|
||||
api;
|
||||
connected = false;
|
||||
icon;
|
||||
@@ -12,11 +12,11 @@ export default class Navi {
|
||||
#secret;
|
||||
#world;
|
||||
|
||||
constructor(api = window.location.origin, secret = '') {
|
||||
this.api = api.replace(/\/$/, '');
|
||||
constructor(api = window.location.origin, secret = '') {
|
||||
this.api = api.replace(/\/$/, '');
|
||||
this.icon = `${this.api}/favicon.png`;
|
||||
this.#secret = secret;
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
if(this.#init) return this.#init;
|
||||
@@ -56,7 +56,7 @@ export default class Navi {
|
||||
this.connected = true;
|
||||
this.emit('init');
|
||||
res(this);
|
||||
} catch (err) {
|
||||
} catch(err) {
|
||||
rej(err);
|
||||
}
|
||||
});
|
||||
@@ -86,41 +86,49 @@ export default class Navi {
|
||||
callbacks.forEach(cb => cb(data));
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// REST API
|
||||
// ============================================
|
||||
// ============================================
|
||||
// REST API
|
||||
// ============================================
|
||||
|
||||
async sendUserMessage(userId, message) {
|
||||
const response = await fetch(`${this.api}/api/message`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
if (!response.ok) throw new Error('Message failed to send bestie');
|
||||
async sendUserMessage(userId, message) {
|
||||
const response = await fetch(`${this.api}/api/message`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({message})
|
||||
});
|
||||
if(!response.ok) throw new Error('Message failed to send bestie');
|
||||
|
||||
const result = await response.json();
|
||||
this.emit('message:sent', { userId, message, result });
|
||||
return result;
|
||||
}
|
||||
const result = await response.json();
|
||||
this.emit('message:sent', {
|
||||
userId,
|
||||
message,
|
||||
result
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
async linkPet(petId, targetApiUrl) {
|
||||
const response = await fetch(`${this.api}/api/link`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ targetApiUrl })
|
||||
});
|
||||
if (!response.ok) throw new Error('Link connection is bussin (negatively)');
|
||||
async linkPet(petId, targetApiUrl) {
|
||||
const response = await fetch(`${this.api}/api/link`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({targetApiUrl})
|
||||
});
|
||||
if(!response.ok) throw new Error('Link connection is bussin (negatively)');
|
||||
|
||||
const result = await response.json();
|
||||
this.emit('pet:linked', { petId, targetApiUrl, result });
|
||||
return result;
|
||||
}
|
||||
const result = await response.json();
|
||||
this.emit('pet:linked', {
|
||||
petId,
|
||||
targetApiUrl,
|
||||
result
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// WORLD SOCKET
|
||||
// ============================================
|
||||
// ============================================
|
||||
// WORLD SOCKET
|
||||
// ============================================
|
||||
|
||||
connect(apiOrWorld, world) {
|
||||
connect(apiOrWorld, world) {
|
||||
let api;
|
||||
if(world) {
|
||||
api = apiOrWorld;
|
||||
@@ -129,12 +137,12 @@ export default class Navi {
|
||||
world = apiOrWorld;
|
||||
}
|
||||
|
||||
if(!this.world) this.world = {};
|
||||
if(this.#world && this.world.api !== api) {
|
||||
if(!this.world) this.world = {};
|
||||
if(this.#world && this.world.api !== api) {
|
||||
this.#world.disconnect();
|
||||
this.#world = null;
|
||||
}
|
||||
if(!this.#world) this.#world = io(`${api}/world`, {auth: this.#secret ? {token: this.#secret} : null});
|
||||
}
|
||||
if(!this.#world) this.#world = io(`${api}/world`, {auth: this.#secret ? {token: this.#secret} : null});
|
||||
this.world.api = api;
|
||||
this.world.data = null;
|
||||
this.world.name = world;
|
||||
@@ -142,113 +150,127 @@ export default class Navi {
|
||||
|
||||
const callbacks = {
|
||||
move: (x, y) => {
|
||||
this.#world.emit('move', {x, y});
|
||||
this.#world.emit('move', {
|
||||
x,
|
||||
y
|
||||
});
|
||||
},
|
||||
leave: () => {
|
||||
this.#world.disconnect();
|
||||
this.world = null;
|
||||
},
|
||||
onData: (data) => { },
|
||||
onPlayers: (players) => { },
|
||||
onJoined: (player) => { },
|
||||
onMoved: (player) => { },
|
||||
onLeft: (player) => { },
|
||||
onError: (error) => { }
|
||||
}
|
||||
onData: (data) => {
|
||||
},
|
||||
onPlayers: (players) => {
|
||||
},
|
||||
onJoined: (player) => {
|
||||
},
|
||||
onMoved: (player) => {
|
||||
},
|
||||
onLeft: (player) => {
|
||||
},
|
||||
onError: (error) => {
|
||||
}
|
||||
};
|
||||
|
||||
this.#world.on('data', (data) => {
|
||||
this.#world.on('data', (data) => {
|
||||
this.world.data = data;
|
||||
callbacks.onData(data);
|
||||
callbacks.onData(data);
|
||||
this.emit('world:data', data);
|
||||
});
|
||||
});
|
||||
|
||||
this.#world.on('players', (players) => {
|
||||
this.world.players.clear();
|
||||
players.forEach(p => this.world.players.set(p.socketId, p));
|
||||
callbacks.onPlayers(players);
|
||||
this.emit('world:players', players)
|
||||
});
|
||||
this.#world.on('players', (players) => {
|
||||
this.world.players.clear();
|
||||
players.forEach(p => this.world.players.set(p.socketId, p));
|
||||
callbacks.onPlayers(players);
|
||||
this.emit('world:players', players);
|
||||
});
|
||||
|
||||
this.#world.on('joined', (player) => {
|
||||
this.world.players.set(player.socketId, player);
|
||||
callbacks.onJoined(player);
|
||||
this.#world.on('joined', (player) => {
|
||||
this.world.players.set(player.socketId, player);
|
||||
callbacks.onJoined(player);
|
||||
this.emit('world:joined', player);
|
||||
});
|
||||
});
|
||||
|
||||
this.#world.on('moved', (data) => {
|
||||
const player = this.world.players.get(data.socketId);
|
||||
if(player) {
|
||||
player.x = data.x;
|
||||
player.y = data.y;
|
||||
}
|
||||
callbacks.onMoved(player);
|
||||
this.emit('world:moved', player);
|
||||
});
|
||||
|
||||
this.#world.on('left', (data) => {
|
||||
this.#world.on('moved', (data) => {
|
||||
const player = this.world.players.get(data.socketId);
|
||||
this.world.players.delete(data.socketId);
|
||||
callbacks.onLeft(player);
|
||||
if(player) {
|
||||
player.x = data.x;
|
||||
player.y = data.y;
|
||||
}
|
||||
callbacks.onMoved(player);
|
||||
this.emit('world:moved', player);
|
||||
});
|
||||
|
||||
this.#world.on('left', (data) => {
|
||||
const player = this.world.players.get(data.socketId);
|
||||
this.world.players.delete(data.socketId);
|
||||
callbacks.onLeft(player);
|
||||
this.emit('world:left', player);
|
||||
});
|
||||
});
|
||||
|
||||
this.#world.on('error', (error) => {
|
||||
console.error('World error:', error);
|
||||
callbacks.onError(error);
|
||||
this.#world.on('error', (error) => {
|
||||
console.error('World error:', error);
|
||||
callbacks.onError(error);
|
||||
this.emit('world:error', error);
|
||||
});
|
||||
});
|
||||
|
||||
this.#world.emit('join', {world, api});
|
||||
this.#world.emit('join', {
|
||||
world,
|
||||
api
|
||||
});
|
||||
|
||||
this.emit('world:join', world);
|
||||
return callbacks;
|
||||
}
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// LLM
|
||||
// ============================================
|
||||
// ============================================
|
||||
// LLM
|
||||
// ============================================
|
||||
|
||||
ask(message, stream) {
|
||||
this.llmCallback = stream;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.llmResolve = resolve;
|
||||
this.llmReject = reject;
|
||||
this.#socket.emit('llm-ask', {message});
|
||||
this.emit('llm:ask')
|
||||
});
|
||||
ask(message, stream) {
|
||||
this.llmCallback = stream;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.llmResolve = resolve;
|
||||
this.llmReject = reject;
|
||||
this.#socket.emit('llm-ask', {message});
|
||||
this.emit('llm:ask');
|
||||
});
|
||||
|
||||
promise.abort = () => {
|
||||
this.#socket.emit('llm-abort');
|
||||
if(this.llmReject) this.llmReject(new Error('Aborted by user'));
|
||||
this.llmCallback = null;
|
||||
this.llmResolve = null;
|
||||
this.llmReject = null;
|
||||
this.emit('llm:abort')
|
||||
};
|
||||
promise.abort = () => {
|
||||
this.#socket.emit('llm-abort');
|
||||
if(this.llmReject) this.llmReject(new Error('Aborted by user'));
|
||||
this.llmCallback = null;
|
||||
this.llmResolve = null;
|
||||
this.llmReject = null;
|
||||
this.emit('llm:abort');
|
||||
};
|
||||
|
||||
return promise;
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
clearChat() {
|
||||
if(this.#socket) this.#socket.emit('llm-clear');
|
||||
clearChat() {
|
||||
if(this.#socket) this.#socket.emit('llm-clear');
|
||||
this.emit('llm:clear');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// UTILITY
|
||||
// ============================================
|
||||
// ============================================
|
||||
// UTILITY
|
||||
// ============================================
|
||||
|
||||
disconnect() {
|
||||
this.connected = false;
|
||||
disconnect() {
|
||||
this.connected = false;
|
||||
this.icon = this.info = this.theme = this.world = this.#init = this.#secret = null;
|
||||
if(this.#world) {
|
||||
this.#world.disconnect();
|
||||
this.#world = null;
|
||||
}
|
||||
if(this.#socket) {
|
||||
this.#socket.disconnect();
|
||||
this.#socket = null;
|
||||
}
|
||||
if(this.#world) {
|
||||
this.#world.disconnect();
|
||||
this.#world = null;
|
||||
}
|
||||
if(this.#socket) {
|
||||
this.#socket.disconnect();
|
||||
this.#socket = null;
|
||||
}
|
||||
this.emit('disconnected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Navi;
|
||||
|
||||
Reference in New Issue
Block a user