Files
Isles-Of-Medievalkor/server/static/map.js
2025-11-13 16:33:50 +00:00

210 lines
5.8 KiB
JavaScript

// server/static/map.js
export class MapView {
constructor(scene) {
this.scene = scene;
// Tile config: adjust tileSize here if you want bigger/smaller tiles.
this.tileSize = 32;
this.config = {
width: 16000, // will be overridden by world.width from server
height: 12000, // "
zoom: 1.2,
moveSpeed: 200
};
this.target = null;
this.nodes = [];
this.nodeSprites = new Map();
this.otherSprites = new Map(); // socket.id -> { img,label }
}
create(me, world, nodes) {
const W = world?.width || this.config.width;
const H = world?.height || this.config.height;
this.config.width = W;
this.config.height = H;
// Draw tiled background/grid using a Graphics object
const g = this.scene.add.graphics();
const bgColor = 0x020617; // dark background
const gridColor = 0x111827; // grid line color
const gridAlpha = 0.4;
// Fill background
g.fillStyle(bgColor, 1);
g.fillRect(0, 0, W, H);
// Grid lines
g.lineStyle(1, gridColor, gridAlpha);
for (let x = 0; x <= W; x += this.tileSize) {
g.beginPath();
g.moveTo(x, 0);
g.lineTo(x, H);
g.strokePath();
}
for (let y = 0; y <= H; y += this.tileSize) {
g.beginPath();
g.moveTo(0, y);
g.lineTo(W, y);
g.strokePath();
}
g.setDepth(-100); // stay behind everything
// Player sprite
const startPos = this.snapToTile(me.x, me.y);
this.player = this.scene.add.image(startPos.x, startPos.y, 'player').setDepth(10);
this.nameText = this.scene
.add.text(startPos.x, startPos.y - 22, me.username || me.name || 'You', {
fontSize: '12px',
color: '#e5e7eb'
})
.setOrigin(0.5);
// Camera follow & bounds
const cam = this.scene.cameras.main;
cam.setBounds(0, 0, W, H);
cam.startFollow(this.player, true, 0.15, 0.15);
cam.setZoom(this.config.zoom);
// Initialize nodes
this.replaceNodes(nodes || []);
}
// Snap any x,y world coordinate to the center of the containing tile
snapToTile(x, y) {
const size = this.tileSize;
let tx = Math.floor(x / size);
let ty = Math.floor(y / size);
// clamp to world bounds in tiles
const maxTx = Math.floor((this.config.width - 1) / size);
const maxTy = Math.floor((this.config.height - 1) / size);
tx = Math.max(0, Math.min(maxTx, tx));
ty = Math.max(0, Math.min(maxTy, ty));
return {
x: tx * size + size / 2,
y: ty * size + size / 2
};
}
setTarget(x, y) {
// When clicking, snap the target to a tile center
this.target = this.snapToTile(x, y);
}
setZoom(z) {
this.config.zoom = z;
this.scene.cameras.main.setZoom(z);
}
replaceNodes(list) {
// Clear old sprites
for (const sprite of this.nodeSprites.values()) {
sprite.destroy();
}
this.nodeSprites.clear();
this.nodes = list.slice();
// Recreate sprites, snapping them to tiles visually (without changing server state)
for (const n of this.nodes) {
const snapped = this.snapToTile(n.x, n.y);
const key = this.getTextureKeyForNode(n.type);
const spr = this.scene.add.image(snapped.x, snapped.y, key).setDepth(1);
spr.setAlpha(n.alive ? 1 : 0.2);
this.nodeSprites.set(n.id, spr);
// Also update stored coords so distance checks use snapped positions
n.x = snapped.x;
n.y = snapped.y;
}
}
updateNode(u) {
const n = this.nodes.find(n => n.id === u.id);
if (n) n.alive = u.alive;
const spr = this.nodeSprites.get(u.id);
if (spr) spr.setAlpha(u.alive ? 1 : 0.2);
}
getTextureKeyForNode(type) {
if (type === 'wood') return 'wood';
if (type === 'stone') return 'stone';
if (type === 'ore') return 'ore';
if (type === 'fiber') return 'fiber';
return 'wood';
}
getClosestNodeIdInRange(range) {
if (!this.player) return null;
let bestId = null;
let bestDist = Infinity;
for (const n of this.nodes) {
if (!n.alive) continue;
const d = Phaser.Math.Distance.Between(
this.player.x,
this.player.y,
n.x,
n.y
);
if (d <= range && d < bestDist) {
bestDist = d;
bestId = n.id;
}
}
return bestId;
}
updateOtherPlayers(players) {
const seen = new Set();
for (const p of players) {
seen.add(p.id);
const snapped = this.snapToTile(p.x, p.y);
if (!this.otherSprites.has(p.id)) {
const img = this.scene.add.image(snapped.x, snapped.y, 'other').setDepth(5);
const label = this.scene
.add.text(snapped.x, snapped.y - 22, p.name || 'Player', {
fontSize: '12px',
color: '#9ca3af'
})
.setOrigin(0.5);
this.otherSprites.set(p.id, { img, label });
} else {
const rec = this.otherSprites.get(p.id);
rec.img.setPosition(snapped.x, snapped.y);
rec.label.setPosition(snapped.x, snapped.y - 22);
}
}
// Remove any that disappeared
for (const [id, rec] of this.otherSprites) {
if (!seen.has(id)) {
rec.img.destroy();
rec.label.destroy();
this.otherSprites.delete(id);
}
}
}
update(delta) {
if (!this.target || !this.player) return;
const dt = delta / 1000;
const dx = this.target.x - this.player.x;
const dy = this.target.y - this.player.y;
const dist = Math.hypot(dx, dy);
const step = this.config.moveSpeed * dt;
if (dist <= step) {
this.player.setPosition(this.target.x, this.target.y);
this.nameText.setPosition(this.player.x, this.player.y - 22);
this.target = null;
} else {
this.player.setPosition(
this.player.x + (dx / dist) * step,
this.player.y + (dy / dist) * step
);
this.nameText.setPosition(this.player.x, this.player.y - 22);
}
}
}