230 lines
7.1 KiB
JavaScript
230 lines
7.1 KiB
JavaScript
// state.js
|
|
import { LOCAL_STORAGE_KEY, DEFAULT_PLAYERS, GAME_STATES, DEFAULT_PLAYER_TIME_SECONDS } from '../config.js';
|
|
|
|
let players = [];
|
|
let currentPlayerIndex = 0;
|
|
let gameState = GAME_STATES.SETUP;
|
|
|
|
// --- State Accessors ---
|
|
|
|
export function getPlayers() {
|
|
return [...players]; // Return a copy to prevent direct mutation
|
|
}
|
|
|
|
export function getCurrentPlayer() {
|
|
if (players.length === 0) return null;
|
|
return players[currentPlayerIndex];
|
|
}
|
|
|
|
export function getPlayerById(id) {
|
|
return players.find(p => p.id === id);
|
|
}
|
|
|
|
export function getCurrentPlayerIndex() {
|
|
return currentPlayerIndex;
|
|
}
|
|
|
|
export function getGameState() {
|
|
return gameState;
|
|
}
|
|
|
|
// --- State Mutators ---
|
|
|
|
export function setPlayers(newPlayers) {
|
|
players = newPlayers;
|
|
saveData();
|
|
}
|
|
|
|
export function setCurrentPlayerIndex(index) {
|
|
if (index >= 0 && index < players.length) {
|
|
currentPlayerIndex = index;
|
|
saveData();
|
|
} else {
|
|
console.error(`Invalid player index: ${index}`);
|
|
}
|
|
}
|
|
|
|
export function setGameState(newState) {
|
|
if (Object.values(GAME_STATES).includes(newState)) {
|
|
gameState = newState;
|
|
saveData();
|
|
} else {
|
|
console.error(`Invalid game state: ${newState}`);
|
|
}
|
|
}
|
|
|
|
export function updatePlayerTime(index, remainingTime) {
|
|
if (index >= 0 && index < players.length) {
|
|
players[index].remainingTime = Math.max(0, remainingTime); // Ensure time doesn't go below 0
|
|
saveData(); // Save data whenever time updates
|
|
}
|
|
}
|
|
|
|
export function addPlayer(name, timeInMinutes, image = null) {
|
|
const timeInSeconds = timeInMinutes * 60;
|
|
const newId = Date.now();
|
|
players.push({
|
|
id: newId,
|
|
name: name,
|
|
timeInSeconds: timeInSeconds,
|
|
remainingTime: timeInSeconds,
|
|
image: image
|
|
});
|
|
currentPlayerIndex = players.length - 1; // Focus new player
|
|
saveData();
|
|
return players[players.length - 1]; // Return the newly added player
|
|
}
|
|
|
|
export function updatePlayer(index, name, timeInMinutes, remainingTimeSeconds, image) {
|
|
if (index >= 0 && index < players.length) {
|
|
const player = players[index];
|
|
const timeInSeconds = timeInMinutes * 60;
|
|
|
|
player.name = name;
|
|
player.timeInSeconds = timeInSeconds;
|
|
|
|
// Update remaining time carefully based on game state
|
|
if (gameState === GAME_STATES.SETUP) {
|
|
player.remainingTime = timeInSeconds;
|
|
} else if (gameState === GAME_STATES.PAUSED || gameState === GAME_STATES.OVER) {
|
|
// Allow direct setting of remaining time only when paused or over
|
|
player.remainingTime = remainingTimeSeconds;
|
|
}
|
|
// If running, remaining time is managed by the timer, don't override here unless intended
|
|
|
|
if (image !== undefined) { // Allow updating image (null means remove image)
|
|
player.image = image;
|
|
}
|
|
saveData();
|
|
return player;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function deletePlayer(index) {
|
|
if (players.length <= 2) {
|
|
console.warn('Cannot delete player, minimum 2 players required.');
|
|
return false; // Indicate deletion failed
|
|
}
|
|
if (index >= 0 && index < players.length) {
|
|
players.splice(index, 1);
|
|
if (currentPlayerIndex >= players.length) {
|
|
currentPlayerIndex = players.length - 1;
|
|
} else if (currentPlayerIndex > index) {
|
|
// Adjust index if deleting someone before the current player
|
|
// No adjustment needed if deleting current or after current
|
|
}
|
|
saveData();
|
|
return true; // Indicate success
|
|
}
|
|
return false; // Indicate deletion failed
|
|
}
|
|
|
|
export function resetPlayersTime() {
|
|
players.forEach(player => {
|
|
player.remainingTime = player.timeInSeconds;
|
|
});
|
|
saveData();
|
|
}
|
|
|
|
export function resetToDefaults() {
|
|
// Deep copy default players to avoid modifying the constant
|
|
players = JSON.parse(JSON.stringify(DEFAULT_PLAYERS));
|
|
gameState = GAME_STATES.SETUP;
|
|
currentPlayerIndex = 0;
|
|
saveData();
|
|
}
|
|
|
|
export function areAllTimersFinished() {
|
|
return players.every(player => player.remainingTime <= 0);
|
|
}
|
|
|
|
// Returns the index of the next player with time > 0, or -1 if none
|
|
export function findNextPlayerWithTime() {
|
|
if (players.length === 0) return -1;
|
|
const startIndex = (currentPlayerIndex + 1) % players.length;
|
|
let index = startIndex;
|
|
|
|
do {
|
|
if (players[index].remainingTime > 0) {
|
|
return index;
|
|
}
|
|
index = (index + 1) % players.length;
|
|
} while (index !== startIndex);
|
|
|
|
// Check current player last if no others found
|
|
if(players[currentPlayerIndex].remainingTime > 0) {
|
|
return currentPlayerIndex;
|
|
}
|
|
|
|
return -1; // No player has time left
|
|
}
|
|
|
|
// Find next player with time in specified direction (1 for next, -1 for prev)
|
|
export function findNextPlayerWithTimeCircular(direction) {
|
|
if (players.length === 0) return -1;
|
|
let index = currentPlayerIndex;
|
|
|
|
for (let i = 0; i < players.length; i++) {
|
|
index = (index + direction + players.length) % players.length;
|
|
if (players[index]?.remainingTime > 0) { // Check if player exists and has time
|
|
return index;
|
|
}
|
|
}
|
|
|
|
// If no other player found, check if current player has time (only relevant if direction search fails)
|
|
if (players[currentPlayerIndex]?.remainingTime > 0) {
|
|
return currentPlayerIndex;
|
|
}
|
|
|
|
return -1; // No player has time left
|
|
}
|
|
|
|
|
|
// --- Persistence ---
|
|
|
|
export function saveData() {
|
|
const dataToSave = {
|
|
players,
|
|
gameState,
|
|
currentPlayerIndex
|
|
};
|
|
try {
|
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(dataToSave));
|
|
} catch (error) {
|
|
console.error("Error saving data to localStorage:", error);
|
|
// Maybe notify the user that settings won't be saved
|
|
}
|
|
}
|
|
|
|
export function loadData() {
|
|
const savedData = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
if (savedData) {
|
|
try {
|
|
const parsedData = JSON.parse(savedData);
|
|
players = parsedData.players || JSON.parse(JSON.stringify(DEFAULT_PLAYERS));
|
|
gameState = parsedData.gameState || GAME_STATES.SETUP;
|
|
currentPlayerIndex = parsedData.currentPlayerIndex || 0;
|
|
|
|
// Basic validation/migration if needed
|
|
if (currentPlayerIndex >= players.length) {
|
|
currentPlayerIndex = 0;
|
|
}
|
|
// Ensure all players have necessary properties
|
|
players = players.map(p => ({
|
|
id: p.id || Date.now() + Math.random(), // Ensure ID exists
|
|
name: p.name || 'Player',
|
|
timeInSeconds: p.timeInSeconds || DEFAULT_PLAYER_TIME_SECONDS,
|
|
remainingTime: p.remainingTime !== undefined ? p.remainingTime : (p.timeInSeconds || DEFAULT_PLAYER_TIME_SECONDS),
|
|
image: p.image || null
|
|
}));
|
|
|
|
} catch (error) {
|
|
console.error("Error parsing data from localStorage:", error);
|
|
resetToDefaults(); // Reset to defaults if stored data is corrupt
|
|
}
|
|
} else {
|
|
resetToDefaults(); // Use defaults if no saved data
|
|
}
|
|
// No saveData() here, loadData just loads the state
|
|
} |