Files
2025-11-13 17:44:54 +00:00

166 lines
4.7 KiB
JavaScript

import { setupAuthPanel } from './ui_auth.js';
import { setupHUD } from './ui_hud.js';
import { setupChat } from './ui_chat.js';
import { setupInventory } from './ui_inventory.js';
import { setupCrafting } from './ui_crafting.js';
import { MapView } from './map.js';
const socket = io();
// shared state
const state = {
me: null,
world: null,
inventory: [],
players: [],
nodes: []
};
window.GameState = state;
window.GameSocket = socket;
async function fetchMe() {
const res = await fetch('/api/me', { credentials: 'include' });
if (res.status !== 200) return null;
return res.json();
}
// Phaser scene
class GameScene extends Phaser.Scene {
constructor() {
super('Game');
}
preload() {
this.textures.generate('player', { data: ['333', '383', '333'], pixelWidth: 8 });
this.textures.generate('other', { data: ['939', '999', '939'], pixelWidth: 8 });
this.textures.generate('wood', { data: ['060', '660', '060'], pixelWidth: 8 });
this.textures.generate('stone', { data: ['888', 'aaa', '888'], pixelWidth: 8 });
this.textures.generate('ore', { data: ['a73', 'c95', 'a73'], pixelWidth: 8 });
this.textures.generate('fiber', { data: ['6a6', '8c8', '6a6'], pixelWidth: 8 });
}
create() {
this.mapView = new MapView(this);
this.mapView.create(state.me, state.world, state.nodes);
// click to move
this.input.on('pointerdown', p => {
this.mapView.setTarget(p.worldX, p.worldY);
socket.emit('move:click', { x: p.worldX, y: p.worldY });
});
// gather on E
this.input.keyboard.on('keydown-E', () => {
const nodeId = this.mapView.getClosestNodeIdInRange(40);
if (nodeId == null) return;
socket.emit('gather', { nodeId }, res => {
if (res?.ok) {
window.updateXP?.(res.level);
window.addChatLine?.(`You gather 1 ${res.item} (+${res.xp} xp)`);
} else if (res?.error) {
window.addChatLine?.(`Gather failed: ${res.error}`);
}
});
});
// resource nodes from server
socket.on('nodes:init', list => {
state.nodes = list;
this.mapView.replaceNodes(list);
});
socket.on('node:update', u => {
this.mapView.updateNode(u);
});
// player positions
socket.on('state', snap => {
state.players = snap.players;
this.mapView.updateOtherPlayers(snap.players);
});
// HUD zoom hook
window.setGameZoom = z => this.mapView.setZoom(z);
}
update(time, delta) {
// Let MapView handle movement
if (this.mapView) {
this.mapView.update(delta);
} else {
return;
}
// Get the actual player sprite from MapView
const player = this.mapView.player;
if (!player) return;
// --- sync true current position to server ---
if (!this._lastPosSyncTime) this._lastPosSyncTime = 0;
if (!this._lastPos) this._lastPos = { x: player.x, y: player.y };
const dist = Phaser.Math.Distance.Between(
player.x,
player.y,
this._lastPos.x,
this._lastPos.y
);
// Only send if we moved enough or some time has passed
if (dist > 8 || time - this._lastPosSyncTime > 250) {
socket.emit('pos:update', { x: player.x, y: player.y });
this._lastPos = { x: player.x, y: player.y };
this._lastPosSyncTime = time;
}
}
}
async function boot() {
// Mount UI modules
setupAuthPanel(document.getElementById('ui-auth'), socket, bootAfterLogin);
setupHUD(document.getElementById('ui-hud'));
setupChat(document.getElementById('ui-chat'), socket);
setupInventory(document.getElementById('ui-inventory'));
setupCrafting(document.getElementById('ui-crafting'));
// auto-login attempt
const meData = await fetchMe();
if (!meData) {
const authPanel = document.getElementById('ui-auth').firstElementChild;
if (authPanel) authPanel.classList.remove('hidden');
return;
}
await bootAfterLogin();
}
async function bootAfterLogin() {
const data = await fetchMe();
if (!data) return;
state.me = data.character;
state.inventory = data.inventory;
state.world = data.world;
// join world socket
await new Promise(resolve => {
socket.emit('auth:join', ack => resolve(ack));
});
// show HUD + chat; hide auth
document.getElementById('ui-auth').innerHTML = '';
const hudPanel = document.getElementById('ui-hud').firstElementChild;
if (hudPanel) hudPanel.classList.remove('hidden');
const chatPanel = document.getElementById('ui-chat').firstElementChild;
if (chatPanel) chatPanel.classList.remove('hidden');
// start Phaser
const config = {
type: Phaser.AUTO,
parent: 'game',
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: '#020617',
scene: [GameScene]
};
new Phaser.Game(config);
}
boot();