diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 2a84bb7..3120da1 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -5,6 +5,16 @@ RUN npm install -g pnpm@9 COPY . . RUN pnpm install --frozen-lockfile RUN pnpm --filter @rps-royale/shared build + +ARG NEXT_PUBLIC_API_URL +ARG NEXT_PUBLIC_CONTRACT_ADDRESS +ARG NEXT_PUBLIC_CHAIN_ID +ARG NEXT_PUBLIC_HARDHAT_RPC_URL +ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} +ENV NEXT_PUBLIC_CONTRACT_ADDRESS=${NEXT_PUBLIC_CONTRACT_ADDRESS} +ENV NEXT_PUBLIC_CHAIN_ID=${NEXT_PUBLIC_CHAIN_ID} +ENV NEXT_PUBLIC_HARDHAT_RPC_URL=${NEXT_PUBLIC_HARDHAT_RPC_URL} + RUN pnpm --filter @rps-royale/web build ENV NODE_ENV=production EXPOSE 3000 diff --git a/apps/web/src/app/play/PlayClient.tsx b/apps/web/src/app/play/PlayClient.tsx deleted file mode 100644 index 8593b42..0000000 --- a/apps/web/src/app/play/PlayClient.tsx +++ /dev/null @@ -1,22 +0,0 @@ -'use client'; - -import { useEffect, useRef } from 'react'; -import { initGame } from '@/phaser/Game'; - -export default function PlayClient() { - const containerRef = useRef(null); - - useEffect(() => { - if (!containerRef.current) return; - const game = initGame('phaser-container'); - return () => { - game.destroy(true); - }; - }, []); - - return ( -
-
-
- ); -} diff --git a/apps/web/src/app/play/page.tsx b/apps/web/src/app/play/page.tsx index 60631a1..89a2a01 100644 --- a/apps/web/src/app/play/page.tsx +++ b/apps/web/src/app/play/page.tsx @@ -1,9 +1,56 @@ 'use client'; -import dynamic from 'next/dynamic'; - -const PlayClient = dynamic(() => import('./PlayClient'), { ssr: false }); +import { useEffect, useRef, useState } from 'react'; export default function PlayPage() { - return ; + const containerRef = useRef(null); + const [error, setError] = useState(null); + const gameRef = useRef(null); + + useEffect(() => { + let mounted = true; + const init = async () => { + try { + const { initGame } = await import('@/phaser/Game'); + if (!mounted || !containerRef.current) return; + // Delay slightly to ensure container has layout + await new Promise((r) => setTimeout(r, 100)); + if (!mounted || !containerRef.current) return; + const game = initGame('phaser-container'); + gameRef.current = game; + } catch (err: any) { + console.error('Failed to init Phaser game:', err); + setError(err?.message || 'Erreur de chargement du jeu'); + } + }; + init(); + return () => { + mounted = false; + if (gameRef.current) { + gameRef.current.destroy(true); + gameRef.current = null; + } + }; + }, []); + + if (error) { + return ( +
+

+ Erreur de chargement +

+

{error}

+
+ ); + } + + return ( +
+
+
+ ); } diff --git a/apps/web/src/phaser/scenes/ArenaScene.ts b/apps/web/src/phaser/scenes/ArenaScene.ts index c62d423..d5875f7 100644 --- a/apps/web/src/phaser/scenes/ArenaScene.ts +++ b/apps/web/src/phaser/scenes/ArenaScene.ts @@ -208,8 +208,10 @@ export class ArenaScene extends Phaser.Scene { this.cameras.main.shake(3000, 0.005); // Intense particles - this.particles.setFrequency(30); - this.particles.setLifespan(2000); + if (this.particles) { + this.particles.frequency = 30; + this.particles.lifespan = 2000; + } // Flash effect const flash = this.add.rectangle(width / 2, height / 2, width, height, 0xffffff).setAlpha(0); diff --git a/apps/web/src/phaser/scenes/ResultScene.ts b/apps/web/src/phaser/scenes/ResultScene.ts index 8f55682..772ba21 100644 --- a/apps/web/src/phaser/scenes/ResultScene.ts +++ b/apps/web/src/phaser/scenes/ResultScene.ts @@ -71,13 +71,13 @@ export class ResultScene extends Phaser.Scene { } // Impact particles - const graphics = this.make.graphics({ x: 0, y: 0, }); + const graphics = this.make.graphics({ x: 0, y: 0 }); graphics.fillStyle(0xffffff, 1); graphics.fillCircle(4, 4, 4); graphics.generateTexture('resultSpark', 8, 8); graphics.destroy(); - this.add.particles(width / 2, height / 2 - 60, 'resultSpark', { + const emitter = this.add.particles(width / 2, height / 2 - 60, 'resultSpark', { speed: { min: 100, max: 300 }, angle: { min: 0, max: 360 }, quantity: 40, @@ -86,6 +86,7 @@ export class ResultScene extends Phaser.Scene { scale: { start: 0.8, end: 0 }, tint: isDraw ? 0x94a3b8 : isWinner ? 0x22c55e : 0xef4444, }); + emitter?.explode(40); new UIButton(this, width / 2, height / 2 + 140, 'Retour au Lobby', () => { this.cameras.main.fadeOut(400, 0, 0, 0); diff --git a/docker-compose.yml b/docker-compose.yml index b772cd3..0cdc27a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -74,6 +74,11 @@ services: build: context: . dockerfile: apps/web/Dockerfile + args: + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-https://api.jeu.cosmolan.fr} + NEXT_PUBLIC_CONTRACT_ADDRESS: ${NEXT_PUBLIC_CONTRACT_ADDRESS} + NEXT_PUBLIC_CHAIN_ID: ${NEXT_PUBLIC_CHAIN_ID:-31337} + NEXT_PUBLIC_HARDHAT_RPC_URL: ${NEXT_PUBLIC_HARDHAT_RPC_URL} container_name: rps-web restart: unless-stopped environment: