@littlepartytime/dev-kit 1.12.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/webapp/pages/Play.tsx +30 -18
- package/dist/webapp/pages/Preview.tsx +21 -9
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
|
2
2
|
import { io, Socket } from 'socket.io-client';
|
|
3
3
|
import PhoneFrame from '../components/PhoneFrame';
|
|
4
4
|
|
|
@@ -45,23 +45,35 @@ export default function Play() {
|
|
|
45
45
|
const isHost = me?.isHost;
|
|
46
46
|
const isReady = me?.ready;
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
// Use refs to avoid recreating platform on every room/me change
|
|
49
|
+
const roomRef = useRef(room);
|
|
50
|
+
roomRef.current = room;
|
|
51
|
+
const meRef = useRef(me);
|
|
52
|
+
meRef.current = me;
|
|
53
|
+
|
|
54
|
+
const platform = useMemo(() => {
|
|
55
|
+
if (!socket) return null;
|
|
56
|
+
return {
|
|
57
|
+
getPlayers: () => roomRef.current.players.map((p: any) => ({ id: p.id, nickname: p.nickname, avatarUrl: null, isHost: p.isHost })),
|
|
58
|
+
getLocalPlayer: () => {
|
|
59
|
+
const m = meRef.current;
|
|
60
|
+
return m ? { id: m.id, nickname: m.nickname, avatarUrl: null, isHost: m.isHost } : { id: '', nickname: '', avatarUrl: null, isHost: false };
|
|
61
|
+
},
|
|
62
|
+
send: (action: any) => socket.emit('game:action', action),
|
|
63
|
+
on: (event: string, handler: Function) => {
|
|
64
|
+
if (event === 'stateUpdate') socket.on('game:state', handler as any);
|
|
65
|
+
},
|
|
66
|
+
off: (event: string, handler: Function) => {
|
|
67
|
+
if (event === 'stateUpdate') socket.off('game:state', handler as any);
|
|
68
|
+
},
|
|
69
|
+
reportResult: () => {},
|
|
70
|
+
getAssetUrl: (assetPath: string) => `/assets/${assetPath}`,
|
|
71
|
+
getDeviceCapabilities: () => ({ haptics: false, motion: false }),
|
|
72
|
+
haptic: () => {},
|
|
73
|
+
onShake: () => () => {},
|
|
74
|
+
onTilt: () => () => {},
|
|
75
|
+
};
|
|
76
|
+
}, [socket]);
|
|
65
77
|
|
|
66
78
|
if (!joined) {
|
|
67
79
|
return (
|
|
@@ -11,11 +11,12 @@ const btnAmber: React.CSSProperties = { background: '#d97706', color: '#fff', bo
|
|
|
11
11
|
const btnZinc: React.CSSProperties = { background: '#3f3f46', color: '#fff', border: 'none', padding: '4px 12px', borderRadius: 4, fontSize: 13, cursor: 'pointer', width: '100%' };
|
|
12
12
|
|
|
13
13
|
export default function Preview() {
|
|
14
|
-
const [playerCount, setPlayerCount] = useState(
|
|
14
|
+
const [playerCount, setPlayerCount] = useState<number | null>(null);
|
|
15
15
|
const [playerIndex, setPlayerIndex] = useState(0);
|
|
16
16
|
const [actions, setActions] = useState<any[]>([]);
|
|
17
17
|
const [GameRenderer, setGameRenderer] = useState<React.ComponentType<any> | null>(null);
|
|
18
18
|
const [engine, setEngine] = useState<any>(null);
|
|
19
|
+
const [config, setConfig] = useState<{ minPlayers?: number; maxPlayers?: number } | null>(null);
|
|
19
20
|
const [fullState, setFullState] = useState<any>(null);
|
|
20
21
|
const [viewState, setViewState] = useState<any>(null);
|
|
21
22
|
const [gameOver, setGameOver] = useState(false);
|
|
@@ -31,6 +32,7 @@ export default function Preview() {
|
|
|
31
32
|
|
|
32
33
|
// Generate mock players
|
|
33
34
|
const mockPlayers = useMemo(() => {
|
|
35
|
+
if (playerCount === null) return [];
|
|
34
36
|
return Array.from({ length: playerCount }, (_, i) => ({
|
|
35
37
|
id: `player-${i + 1}`,
|
|
36
38
|
nickname: PLAYER_NAMES[i] || `Player ${i + 1}`,
|
|
@@ -42,15 +44,25 @@ export default function Preview() {
|
|
|
42
44
|
const mockPlayersRef = useRef(mockPlayers);
|
|
43
45
|
mockPlayersRef.current = mockPlayers;
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
const minPlayers = config?.minPlayers ?? 2;
|
|
48
|
+
const maxPlayers = config?.maxPlayers ?? 32;
|
|
49
|
+
|
|
50
|
+
// Load renderer, engine, and config dynamically
|
|
46
51
|
useEffect(() => {
|
|
47
52
|
import('/src/index.ts').then((mod) => {
|
|
48
53
|
setGameRenderer(() => mod.Renderer || mod.default);
|
|
49
54
|
if (mod.engine) {
|
|
50
55
|
setEngine(mod.engine);
|
|
51
56
|
}
|
|
57
|
+
if (mod.config) {
|
|
58
|
+
setConfig(mod.config);
|
|
59
|
+
setPlayerCount(mod.config.minPlayers ?? 3);
|
|
60
|
+
} else {
|
|
61
|
+
setPlayerCount(3);
|
|
62
|
+
}
|
|
52
63
|
}).catch((err) => {
|
|
53
64
|
console.error('Failed to load game module:', err);
|
|
65
|
+
setPlayerCount(3);
|
|
54
66
|
// Fallback: try loading renderer directly
|
|
55
67
|
import('/src/renderer.tsx').then((mod) => {
|
|
56
68
|
setGameRenderer(() => mod.default || mod.Renderer);
|
|
@@ -62,7 +74,7 @@ export default function Preview() {
|
|
|
62
74
|
|
|
63
75
|
// Initialize game when engine loads or player count changes
|
|
64
76
|
useEffect(() => {
|
|
65
|
-
if (!engine) return;
|
|
77
|
+
if (!engine || mockPlayers.length === 0) return;
|
|
66
78
|
const initialState = engine.init(mockPlayers);
|
|
67
79
|
setFullState(initialState);
|
|
68
80
|
setGameOver(false);
|
|
@@ -166,7 +178,7 @@ export default function Preview() {
|
|
|
166
178
|
|
|
167
179
|
// Clamp playerIndex when playerCount decreases
|
|
168
180
|
useEffect(() => {
|
|
169
|
-
if (playerIndex >= playerCount) {
|
|
181
|
+
if (playerCount !== null && playerIndex >= playerCount) {
|
|
170
182
|
setPlayerIndex(0);
|
|
171
183
|
}
|
|
172
184
|
}, [playerCount, playerIndex]);
|
|
@@ -195,7 +207,7 @@ export default function Preview() {
|
|
|
195
207
|
<div style={{ width: '50%', height: '100%' }}>
|
|
196
208
|
<PhoneFrame>
|
|
197
209
|
{GameRenderer && platform && viewState ? (
|
|
198
|
-
<GameRenderer platform={platform} state={viewState} />
|
|
210
|
+
<GameRenderer key={mockPlayers[playerIndex].id} platform={platform} state={viewState} />
|
|
199
211
|
) : (
|
|
200
212
|
<div style={{ padding: 16, color: '#71717a' }}>
|
|
201
213
|
{!engine ? 'Loading engine...' : 'Initializing game...'}
|
|
@@ -213,10 +225,10 @@ export default function Preview() {
|
|
|
213
225
|
<h3 style={label}>Player Count</h3>
|
|
214
226
|
<input
|
|
215
227
|
type="number"
|
|
216
|
-
min={
|
|
217
|
-
max={
|
|
218
|
-
value={playerCount}
|
|
219
|
-
onChange={(e) => setPlayerCount(Math.max(
|
|
228
|
+
min={minPlayers}
|
|
229
|
+
max={maxPlayers}
|
|
230
|
+
value={playerCount ?? ''}
|
|
231
|
+
onChange={(e) => setPlayerCount(Math.max(minPlayers, Math.min(maxPlayers, Number(e.target.value))))}
|
|
220
232
|
className="dk-input"
|
|
221
233
|
style={inputBase}
|
|
222
234
|
/>
|