// 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);