130 lines
4.3 KiB
JavaScript
130 lines
4.3 KiB
JavaScript
class MultiplayerSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.socket = null;
|
|
this.otherPlayers = {}; // Map socketId -> Sprite
|
|
this.isConnected = false;
|
|
|
|
// Try to connect
|
|
this.connect();
|
|
}
|
|
|
|
connect() {
|
|
if (typeof io === 'undefined') {
|
|
console.warn('⚠️ Socket.IO not found. Multiplayer disabled.');
|
|
console.warn('Please run: npm install socket.io-client OR include CDN.');
|
|
return;
|
|
}
|
|
|
|
console.log('🌐 Connecting to Multiplayer Server...');
|
|
// Connect to localhost:3000
|
|
this.socket = io('http://localhost:3000');
|
|
|
|
this.socket.on('connect', () => {
|
|
console.log('✅ Connected to Server! ID:', this.socket.id);
|
|
this.isConnected = true;
|
|
|
|
// Send initial pos
|
|
if (this.scene.player) {
|
|
const pos = this.scene.player.getPosition();
|
|
this.socket.emit('playerMovement', {
|
|
x: pos.x,
|
|
y: pos.y,
|
|
anim: 'idle',
|
|
flipX: false
|
|
});
|
|
}
|
|
});
|
|
|
|
this.socket.on('currentPlayers', (players) => {
|
|
Object.keys(players).forEach((id) => {
|
|
if (id === this.socket.id) return;
|
|
this.addOtherPlayer(players[id]);
|
|
});
|
|
});
|
|
|
|
this.socket.on('newPlayer', (playerInfo) => {
|
|
this.addOtherPlayer(playerInfo);
|
|
});
|
|
|
|
this.socket.on('playerDisconnected', (playerId) => {
|
|
this.removeOtherPlayer(playerId);
|
|
});
|
|
|
|
this.socket.on('playerMoved', (playerInfo) => {
|
|
if (this.otherPlayers[playerInfo.id]) {
|
|
const sprite = this.otherPlayers[playerInfo.id];
|
|
// Update target pos for interpolation (TODO: logic)
|
|
// For now direct teleport
|
|
|
|
// Convert grid to screen
|
|
const iso = new IsometricUtils(48, 24);
|
|
const screen = iso.toScreen(playerInfo.x, playerInfo.y);
|
|
|
|
sprite.setPosition(screen.x + this.scene.terrainOffsetX, screen.y + this.scene.terrainOffsetY);
|
|
sprite.setDepth(sprite.y);
|
|
|
|
// Anim/Flip logic could go here
|
|
if (playerInfo.flipX !== undefined) sprite.setFlipX(playerInfo.flipX);
|
|
}
|
|
});
|
|
|
|
this.socket.on('worldAction', (action) => {
|
|
// Handle world syncing
|
|
if (action.type === 'build' && this.scene.buildingSystem) {
|
|
// Hacky: place building remotely
|
|
// this.scene.terrainSystem.placeStructure(...)
|
|
}
|
|
});
|
|
}
|
|
|
|
addOtherPlayer(playerInfo) {
|
|
if (this.otherPlayers[playerInfo.id]) return;
|
|
|
|
console.log('👤 New Player Joined:', playerInfo.id);
|
|
|
|
// Use player sprite
|
|
const iso = new IsometricUtils(48, 24);
|
|
const screen = iso.toScreen(playerInfo.x, playerInfo.y);
|
|
|
|
const sprite = this.scene.add.sprite(
|
|
screen.x + this.scene.terrainOffsetX,
|
|
screen.y + this.scene.terrainOffsetY,
|
|
'player' // or player_idle
|
|
);
|
|
sprite.setOrigin(0.5, 1);
|
|
sprite.setScale(0.3); // Same as local player
|
|
|
|
// Add name tag
|
|
const text = this.scene.add.text(0, -50, 'Player', { fontSize: '12px', fill: '#ffffff' });
|
|
text.setOrigin(0.5);
|
|
// Container? For now just sprite, text handling is complex
|
|
|
|
this.otherPlayers[playerInfo.id] = sprite;
|
|
}
|
|
|
|
removeOtherPlayer(playerId) {
|
|
if (this.otherPlayers[playerId]) {
|
|
console.log('👋 Player Left:', playerId);
|
|
this.otherPlayers[playerId].destroy();
|
|
delete this.otherPlayers[playerId];
|
|
}
|
|
}
|
|
|
|
update(delta) {
|
|
if (!this.isConnected || !this.socket || !this.scene.player) return;
|
|
|
|
// Rate limit: send 10 times second? Or every frame?
|
|
// Let's send only if moved
|
|
const player = this.scene.player;
|
|
if (player.isMoving) {
|
|
this.socket.emit('playerMovement', {
|
|
x: player.gridX,
|
|
y: player.gridY,
|
|
anim: 'walk',
|
|
flipX: player.sprite.flipX // Accessing internal sprite
|
|
});
|
|
}
|
|
}
|
|
}
|