create-smore-game 2.3.0 → 2.4.0
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/package.json +1 -1
- package/templates.js +87 -6
package/package.json
CHANGED
package/templates.js
CHANGED
|
@@ -1262,6 +1262,16 @@ async function main() {
|
|
|
1262
1262
|
app.get('/', servePage(join(__dirname, 'index.html')));
|
|
1263
1263
|
app.get('/controller', servePage(join(__dirname, 'controller.html')));
|
|
1264
1264
|
|
|
1265
|
+
// Serve game.json for dev harness (minPlayers, maxPlayers, title, etc.)
|
|
1266
|
+
app.get('/game.json', (_req, res) => {
|
|
1267
|
+
try {
|
|
1268
|
+
const gameJson = JSON.parse(readFileSync(join(__dirname, '..', 'game.json'), 'utf-8'));
|
|
1269
|
+
res.json(gameJson);
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
res.json({ id: 'dev-game', title: 'Dev Game', minPlayers: 1, maxPlayers: 8 });
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1265
1275
|
// ── Socket handling ──
|
|
1266
1276
|
|
|
1267
1277
|
io.on('connection', (socket) => {
|
|
@@ -1535,6 +1545,10 @@ export function devHarness(gameId) {
|
|
|
1535
1545
|
.btn-primary:disabled:hover { background: #4f46e5; }
|
|
1536
1546
|
.btn-danger { background: #dc2626; border-color: #dc2626; color: #fff; }
|
|
1537
1547
|
.btn-danger:hover { background: #b91c1c; }
|
|
1548
|
+
.btn-start { background: #16a34a; border-color: #16a34a; color: #fff; font-size: 13px; padding: 5px 16px; }
|
|
1549
|
+
.btn-start:hover { background: #15803d; }
|
|
1550
|
+
.btn-start:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
1551
|
+
.btn-start:disabled:hover { background: #16a34a; }
|
|
1538
1552
|
|
|
1539
1553
|
/* Main layout */
|
|
1540
1554
|
.main { display: flex; height: calc(100% - 48px); }
|
|
@@ -1610,6 +1624,7 @@ export function devHarness(gameId) {
|
|
|
1610
1624
|
<div class="badge-muted" id="playerCount">0 players</div>
|
|
1611
1625
|
<div class="phase-badge phase-lobby" id="phaseBadge">Lobby</div>
|
|
1612
1626
|
<div class="topbar-spacer"></div>
|
|
1627
|
+
<button class="btn btn-start" id="startGameBtn" disabled>▶ Start</button>
|
|
1613
1628
|
<button class="btn btn-danger" id="endGameBtn" style="display:none;">End Game</button>
|
|
1614
1629
|
<button class="btn" id="resetBtn">Reset</button>
|
|
1615
1630
|
<button class="btn btn-primary" id="addPlayerBtn">+ Player</button>
|
|
@@ -1648,6 +1663,10 @@ export function devHarness(gameId) {
|
|
|
1648
1663
|
var screenIframe = null;
|
|
1649
1664
|
var screenReady = false;
|
|
1650
1665
|
var gamePhase = 'lobby';
|
|
1666
|
+
var gameStarted = false;
|
|
1667
|
+
var pendingGameReady = []; // { socket, data }
|
|
1668
|
+
var minPlayers = 1;
|
|
1669
|
+
var gameConfig = {};
|
|
1651
1670
|
|
|
1652
1671
|
// ── DOM refs ──
|
|
1653
1672
|
var roomCodeEl = document.getElementById('roomCode');
|
|
@@ -1659,6 +1678,7 @@ export function devHarness(gameId) {
|
|
|
1659
1678
|
var controllerGrid = document.getElementById('controllerGrid');
|
|
1660
1679
|
var emptyState = document.getElementById('emptyState');
|
|
1661
1680
|
var addPlayerBtn = document.getElementById('addPlayerBtn');
|
|
1681
|
+
var startGameBtn = document.getElementById('startGameBtn');
|
|
1662
1682
|
var endGameBtn = document.getElementById('endGameBtn');
|
|
1663
1683
|
var resetBtn = document.getElementById('resetBtn');
|
|
1664
1684
|
|
|
@@ -1670,12 +1690,19 @@ export function devHarness(gameId) {
|
|
|
1670
1690
|
phaseBadgeEl.className = 'phase-badge phase-' + phase;
|
|
1671
1691
|
addPlayerBtn.disabled = (phase === 'playing');
|
|
1672
1692
|
endGameBtn.style.display = (phase === 'playing') ? 'inline-block' : 'none';
|
|
1693
|
+
startGameBtn.style.display = (phase === 'lobby') ? 'inline-block' : 'none';
|
|
1694
|
+
updateStartButton();
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
function updateStartButton() {
|
|
1698
|
+
startGameBtn.disabled = gameStarted || serverPlayers.length < minPlayers;
|
|
1673
1699
|
}
|
|
1674
1700
|
|
|
1675
1701
|
// ── Helpers ──
|
|
1676
1702
|
function updatePlayerCount() {
|
|
1677
1703
|
var count = serverPlayers.length;
|
|
1678
1704
|
playerCountEl.textContent = count + ' player' + (count !== 1 ? 's' : '');
|
|
1705
|
+
updateStartButton();
|
|
1679
1706
|
}
|
|
1680
1707
|
|
|
1681
1708
|
function updateEmptyState() {
|
|
@@ -1897,9 +1924,13 @@ export function devHarness(gameId) {
|
|
|
1897
1924
|
return;
|
|
1898
1925
|
}
|
|
1899
1926
|
|
|
1900
|
-
//
|
|
1927
|
+
// Buffer game-ready until game is started (prevents auto-start)
|
|
1901
1928
|
if (event === 'smore:game-ready') {
|
|
1902
|
-
|
|
1929
|
+
if (gameStarted) {
|
|
1930
|
+
screenSocket.emit('smore:game-ready', data || {});
|
|
1931
|
+
} else {
|
|
1932
|
+
pendingGameReady.push({ socket: screenSocket, data: data });
|
|
1933
|
+
}
|
|
1903
1934
|
return;
|
|
1904
1935
|
}
|
|
1905
1936
|
|
|
@@ -1945,9 +1976,13 @@ export function devHarness(gameId) {
|
|
|
1945
1976
|
setPhase('lobby');
|
|
1946
1977
|
}
|
|
1947
1978
|
|
|
1948
|
-
//
|
|
1979
|
+
// Buffer game-ready until game is started (prevents auto-start)
|
|
1949
1980
|
if (event === 'smore:game-ready') {
|
|
1950
|
-
|
|
1981
|
+
if (gameStarted) {
|
|
1982
|
+
player.socket.emit('smore:game-ready', data || {});
|
|
1983
|
+
} else {
|
|
1984
|
+
pendingGameReady.push({ socket: player.socket, data: data });
|
|
1985
|
+
}
|
|
1951
1986
|
return;
|
|
1952
1987
|
}
|
|
1953
1988
|
|
|
@@ -1966,6 +2001,9 @@ export function devHarness(gameId) {
|
|
|
1966
2001
|
|
|
1967
2002
|
// ── End Game ──
|
|
1968
2003
|
function endGame() {
|
|
2004
|
+
gameStarted = false;
|
|
2005
|
+
pendingGameReady = [];
|
|
2006
|
+
|
|
1969
2007
|
// Broadcast phase-change to ALL controllers (including phone) via server relay
|
|
1970
2008
|
screenSocket.emit('phase-change', { phase: 'lobby' });
|
|
1971
2009
|
|
|
@@ -1986,6 +2024,9 @@ export function devHarness(gameId) {
|
|
|
1986
2024
|
|
|
1987
2025
|
// ── Reset ──
|
|
1988
2026
|
function resetAll() {
|
|
2027
|
+
gameStarted = false;
|
|
2028
|
+
pendingGameReady = [];
|
|
2029
|
+
|
|
1989
2030
|
// 1. Disconnect all local iframe player sockets
|
|
1990
2031
|
players.forEach(function(p) {
|
|
1991
2032
|
if (p.socket) p.socket.disconnect();
|
|
@@ -2034,6 +2075,24 @@ export function devHarness(gameId) {
|
|
|
2034
2075
|
if (!addPlayerBtn.disabled) addController();
|
|
2035
2076
|
});
|
|
2036
2077
|
|
|
2078
|
+
startGameBtn.addEventListener('click', function() {
|
|
2079
|
+
if (gameStarted || serverPlayers.length < minPlayers) return;
|
|
2080
|
+
gameStarted = true;
|
|
2081
|
+
|
|
2082
|
+
// Tell server to start the game (triggers smore:game-selected + smore:game-started)
|
|
2083
|
+
screenSocket.emit('smore:select-game', { gameId: gameConfig.id || 'dev-game' });
|
|
2084
|
+
|
|
2085
|
+
// Flush all buffered game-ready messages after server processes select-game
|
|
2086
|
+
setTimeout(function() {
|
|
2087
|
+
pendingGameReady.forEach(function(entry) {
|
|
2088
|
+
entry.socket.emit('smore:game-ready', entry.data || {});
|
|
2089
|
+
});
|
|
2090
|
+
pendingGameReady = [];
|
|
2091
|
+
}, 50);
|
|
2092
|
+
|
|
2093
|
+
setPhase('playing');
|
|
2094
|
+
});
|
|
2095
|
+
|
|
2037
2096
|
endGameBtn.addEventListener('click', endGame);
|
|
2038
2097
|
resetBtn.addEventListener('click', resetAll);
|
|
2039
2098
|
|
|
@@ -2053,6 +2112,13 @@ export function devHarness(gameId) {
|
|
|
2053
2112
|
});
|
|
2054
2113
|
|
|
2055
2114
|
// ── Boot ──
|
|
2115
|
+
// Fetch game.json for minPlayers config
|
|
2116
|
+
fetch('/game.json').then(function(r) { return r.json(); }).then(function(cfg) {
|
|
2117
|
+
gameConfig = cfg;
|
|
2118
|
+
minPlayers = cfg.minPlayers || 1;
|
|
2119
|
+
updateStartButton();
|
|
2120
|
+
}).catch(function() {});
|
|
2121
|
+
|
|
2056
2122
|
initScreen();
|
|
2057
2123
|
setTimeout(function() { addController(); }, 500);
|
|
2058
2124
|
})();
|
|
@@ -2127,6 +2193,8 @@ export function devControllerPage(gameId) {
|
|
|
2127
2193
|
var roomCode = '';
|
|
2128
2194
|
var players = [];
|
|
2129
2195
|
var iframeReady = false;
|
|
2196
|
+
var gameStarted = false;
|
|
2197
|
+
var pendingGameReady = null;
|
|
2130
2198
|
|
|
2131
2199
|
// Connect and join room
|
|
2132
2200
|
socket = io();
|
|
@@ -2159,6 +2227,15 @@ export function devControllerPage(gameId) {
|
|
|
2159
2227
|
}
|
|
2160
2228
|
});
|
|
2161
2229
|
|
|
2230
|
+
// Listen for game-started to flush pending game-ready
|
|
2231
|
+
socket.on('smore:game-started', function() {
|
|
2232
|
+
gameStarted = true;
|
|
2233
|
+
if (pendingGameReady !== null) {
|
|
2234
|
+
socket.emit('smore:game-ready', pendingGameReady);
|
|
2235
|
+
pendingGameReady = null;
|
|
2236
|
+
}
|
|
2237
|
+
});
|
|
2238
|
+
|
|
2162
2239
|
// Forward system events to iframe
|
|
2163
2240
|
var sysEvents = [
|
|
2164
2241
|
'smore:game-over',
|
|
@@ -2220,9 +2297,13 @@ export function devControllerPage(gameId) {
|
|
|
2220
2297
|
var data = msg.payload.data;
|
|
2221
2298
|
var ackId = msg.payload.ackId;
|
|
2222
2299
|
|
|
2223
|
-
//
|
|
2300
|
+
// Buffer game-ready until game is started (prevents auto-start)
|
|
2224
2301
|
if (event === 'smore:game-ready') {
|
|
2225
|
-
|
|
2302
|
+
if (gameStarted) {
|
|
2303
|
+
socket.emit('smore:game-ready', data || {});
|
|
2304
|
+
} else {
|
|
2305
|
+
pendingGameReady = data || {};
|
|
2306
|
+
}
|
|
2226
2307
|
return;
|
|
2227
2308
|
}
|
|
2228
2309
|
|