2026-05-04 03:42:47 +02:00

231 lines
7.9 KiB
JavaScript

// main.js — point d'entree client, init Pixi et handlers reseau
import { Application, Container } from 'pixi.js';
import { connect, send, on } from './network.js';
import { showLeaderboard, hideLeaderboard } from './leaderboard.js';
import { updateIsoLayout, drawStaticArena, updatePlayers, updateProjectiles, updateEnemies, updateAoeZones, updateCamera, screenToWorld } from './renderer.js';
import { loadArenaMap } from './renderArena.js';
import { startInputTracking, isInputBlocked } from './input.js';
import { updatePlayerHud, updateSoulgateBar, updateWaveInfo, updateBossBar, updateUpgradePanel, checkGameEnd } from './hud.js';
import { bindEvents } from './bindings.js';
import { loadKaelAssets, loadSerisAssets, loadAldricAssets } from './renderPlayers.js';
import { loadFractureAssets, loadRampantAssets, loadColosseAssets, loadEclatAssets, loadVexarisAssets } from './renderEnemies.js';
let localUsername = null;
let localId = null;
let isHost = false;
let lobbyCode = '';
let camX = 0, camY = 0;
let gameStartTime = null;
async function main() {
const app = new Application();
await app.init({
canvas: document.getElementById('game-canvas'),
resizeTo: window,
backgroundAlpha: 0,
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true,
});
let worldContainer = null;
let layerArena = null;
let layerAoe = null;
let layerEnemies = null;
let layerEntities = null;
let layerProjectiles = null;
const playerPool = {};
const projPool = {};
const enemyPool = {};
const aoePool = {};
async function initGameWorld() {
await loadArenaMap();
await loadKaelAssets();
await loadSerisAssets();
await loadAldricAssets();
await loadFractureAssets();
await loadRampantAssets();
await loadColosseAssets();
await loadEclatAssets();
await loadVexarisAssets();
worldContainer = new Container();
layerArena = new Container();
layerAoe = new Container();
layerEnemies = new Container();
layerEntities = new Container();
layerProjectiles = new Container();
// ordre d'affichage : arene tout en bas, projectiles tout en haut
worldContainer.addChild(layerArena, layerAoe, layerEnemies, layerEntities, layerProjectiles);
app.stage.addChild(worldContainer);
updateIsoLayout(app.screen.width, app.screen.height);
layerArena.removeChildren();
drawStaticArena(layerArena);
updateCamera(worldContainer, app.screen.width, app.screen.height, camX, camY);
app.renderer.on('resize', (w, h) => {
updateIsoLayout(w, h);
layerArena.removeChildren();
drawStaticArena(layerArena);
updateCamera(worldContainer, w, h, camX, camY);
});
}
connect();
on('_open', () => setStatus('ok', 'Connected'));
on('_close', () => setStatus('err', 'Disconnected'));
on('username_set', (msg) => {
if (msg.my_id) localId = msg.my_id;
document.getElementById('home-buttons').classList.remove('hidden');
document.getElementById('home-username').classList.add('hidden');
});
on('lobby_created', (msg) => {
isHost = true;
lobbyCode = msg.code;
document.getElementById('code-display').textContent = msg.code;
document.getElementById('start-btn').classList.remove('hidden');
switchToLobby();
notify('Lobby created — code: ' + msg.code);
});
on('lobby_joined', (msg) => {
lobbyCode = msg.code;
switchToLobby();
document.getElementById('code-display').textContent = msg.code;
notify('Joined lobby ' + msg.code);
});
on('player_joined', (msg) => notify(msg.player.username + ' joined'));
on('player_ready', () => {
const btn = document.getElementById('ready-btn');
btn.textContent = 'READY!';
btn.classList.add('mc-btn-gold');
btn.disabled = true;
});
on('game_starting', async () => {
gameStartTime = Date.now();
document.getElementById('home-screen').classList.add('hidden');
document.getElementById('lobby-screen').classList.add('hidden');
document.getElementById('loading-screen').classList.remove('hidden');
await initGameWorld();
document.getElementById('loading-screen').classList.add('hidden');
['player-hud', 'soulgate-hud', 'wave-hud', 'fullscreen-btn'].forEach(id =>
document.getElementById(id).classList.remove('hidden')
);
const getTarget = () => screenToWorld(mouseX, mouseY, worldContainer.x, worldContainer.y);
startInputTracking(getTarget);
app.canvas.addEventListener('click', (e) => {
if (isInputBlocked()) return;
const { wx, wy } = screenToWorld(e.clientX, e.clientY, worldContainer.x, worldContainer.y);
send('attack', { tx: wx, ty: wy });
});
app.canvas.addEventListener('contextmenu', e => e.preventDefault());
});
on('game_state', (msg) => {
if (!worldContainer) return;
updatePlayers(layerEntities, playerPool, msg.players);
updateEnemies(layerEnemies, enemyPool, msg.enemies);
updateProjectiles(layerProjectiles, projPool, msg.projectiles);
updateAoeZones(layerAoe, aoePool, msg.aoe_zones ?? []);
// retrouver le joueur local : par id si dispo (fiable meme avec pseudos en doublon), sinon par username
const local = localId
? msg.players.find(p => p.id === localId)
: localUsername
? msg.players.find(p => p.username === localUsername)
: null;
if (local) {
camX = local.x;
camY = local.y;
updateCamera(worldContainer, app.screen.width, app.screen.height, camX, camY);
updatePlayerHud(local);
}
updateSoulgateBar(msg.soulgate);
updateWaveInfo(msg.wave);
updateBossBar(msg.wave);
updateUpgradePanel(msg.wave, local);
checkGameEnd(msg, gameStartTime, isHost, lobbyCode, localId);
});
on('error', (msg) => notify(msg.message, true));
bindEvents({
setUsername: (v) => { localUsername = v; },
switchToHome,
notify,
showLeaderboard,
hideLeaderboard,
});
}
let mouseX = 0, mouseY = 0;
document.addEventListener('mousemove', e => {
mouseX = e.clientX;
mouseY = e.clientY;
});
function switchToLobby() {
document.getElementById('home-screen').classList.add('hidden');
document.getElementById('lobby-screen').classList.remove('hidden');
}
function switchToHome() {
isHost = false;
lobbyCode = '';
document.getElementById('lobby-screen').classList.add('hidden');
document.getElementById('home-screen').classList.remove('hidden');
document.getElementById('home-buttons').classList.remove('hidden');
document.getElementById('home-username').classList.add('hidden');
document.getElementById('join-row').classList.add('hidden');
document.getElementById('code-input').value = '';
document.getElementById('code-display').textContent = '------';
const readyBtn = document.getElementById('ready-btn');
readyBtn.disabled = true;
readyBtn.textContent = 'Ready';
readyBtn.classList.remove('mc-btn-gold');
document.getElementById('start-btn').classList.add('hidden');
document.querySelectorAll('.class-btn').forEach(b => b.classList.remove('active'));
}
function setStatus(cls, text) {
const el = document.getElementById('home-status');
el.className = 'home-status ' + cls;
el.textContent = text;
}
function notify(text, isError = false) {
const el = document.getElementById('overlay-msg');
if (!el) return;
el.textContent = text;
el.className = 'msg' + (isError ? ' err' : '');
}
main().catch(console.error);