refactor: improve captain data management and UI layout - Move captain configs to individual JSON files - Add dynamic captain indexing - Update UI to show missions above captain selector - Remove player labels for cleaner interface - Add captain selection filtering to prevent duplicate selections
This commit is contained in:
parent
c54e010801
commit
f0e597a3d8
12 changed files with 258 additions and 158 deletions
|
|
@ -4,9 +4,10 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "node scripts/generate-captains-index.js && astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro",
|
||||||
|
"generate-captains": "node scripts/generate-captains-index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-dialog": "^1.1.6",
|
"@radix-ui/react-dialog": "^1.1.6",
|
||||||
|
|
|
||||||
41
scripts/generate-captains-index.js
Normal file
41
scripts/generate-captains-index.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
const CAPTAINS_DIR = path.join(__dirname, '../src/assets/captains');
|
||||||
|
const INDEX_FILE = path.join(CAPTAINS_DIR, 'index.json');
|
||||||
|
|
||||||
|
function generateCaptainsIndex() {
|
||||||
|
try {
|
||||||
|
// Read all JSON files in the captains directory
|
||||||
|
const files = fs.readdirSync(CAPTAINS_DIR)
|
||||||
|
.filter(file => file.endsWith('.json') && file !== 'index.json');
|
||||||
|
|
||||||
|
// Parse each captain file to get their id and name
|
||||||
|
const captains = files.map(file => {
|
||||||
|
const content = fs.readFileSync(path.join(CAPTAINS_DIR, file), 'utf-8');
|
||||||
|
const captain = JSON.parse(content);
|
||||||
|
return {
|
||||||
|
id: captain.id,
|
||||||
|
name: captain.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create the index object
|
||||||
|
const index = {
|
||||||
|
captains: captains
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the index file
|
||||||
|
fs.writeFileSync(INDEX_FILE, JSON.stringify(index, null, 4));
|
||||||
|
console.log('Successfully generated captains index');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating captains index:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCaptainsIndex();
|
||||||
|
|
@ -44,17 +44,5 @@
|
||||||
"name": "Time Crystal Recovery",
|
"name": "Time Crystal Recovery",
|
||||||
"description": "Retrieve a valuable time crystal from a dangerous location.",
|
"description": "Retrieve a valuable time crystal from a dangerous location.",
|
||||||
"points": 3
|
"points": 3
|
||||||
},
|
}
|
||||||
"advancedMissions": [
|
|
||||||
{
|
|
||||||
"name": "Red Angel Investigation",
|
|
||||||
"description": "Investigate the mysterious Red Angel phenomenon.",
|
|
||||||
"points": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Control Protocol Override",
|
|
||||||
"description": "Override the Control AI's protocols to save the galaxy.",
|
|
||||||
"points": 8
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
28
src/assets/captains/index.json
Normal file
28
src/assets/captains/index.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"captains": [
|
||||||
|
{
|
||||||
|
"id": "burnham",
|
||||||
|
"name": "Burnham"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "koloth",
|
||||||
|
"name": "Koloth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "picard",
|
||||||
|
"name": "Picard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sela",
|
||||||
|
"name": "Sela"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "shran",
|
||||||
|
"name": "Shran"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sisko",
|
||||||
|
"name": "Sisko"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -44,17 +44,5 @@
|
||||||
"name": "Klingon Honor",
|
"name": "Klingon Honor",
|
||||||
"description": "Uphold Klingon honor in a challenging diplomatic situation.",
|
"description": "Uphold Klingon honor in a challenging diplomatic situation.",
|
||||||
"points": 3
|
"points": 3
|
||||||
},
|
}
|
||||||
"advancedMissions": [
|
|
||||||
{
|
|
||||||
"name": "House Koloth",
|
|
||||||
"description": "Strengthen the position of House Koloth in the Klingon Empire.",
|
|
||||||
"points": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Klingon Empire Conquest",
|
|
||||||
"description": "Lead a successful military campaign to expand Klingon territory.",
|
|
||||||
"points": 8
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
@ -44,17 +44,5 @@
|
||||||
"name": "Diplomatic Mission",
|
"name": "Diplomatic Mission",
|
||||||
"description": "Lead a diplomatic mission to establish peaceful relations with a new species.",
|
"description": "Lead a diplomatic mission to establish peaceful relations with a new species.",
|
||||||
"points": 2
|
"points": 2
|
||||||
},
|
}
|
||||||
"advancedMissions": [
|
|
||||||
{
|
|
||||||
"name": "Scientific Discovery",
|
|
||||||
"description": "Make a groundbreaking scientific discovery that advances Federation knowledge.",
|
|
||||||
"points": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "First Contact Protocol",
|
|
||||||
"description": "Successfully execute first contact protocols with a technologically advanced species.",
|
|
||||||
"points": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
@ -44,17 +44,5 @@
|
||||||
"name": "Romulan Infiltration",
|
"name": "Romulan Infiltration",
|
||||||
"description": "Lead a covert operation to infiltrate a strategic Federation outpost.",
|
"description": "Lead a covert operation to infiltrate a strategic Federation outpost.",
|
||||||
"points": 3
|
"points": 3
|
||||||
},
|
}
|
||||||
"advancedMissions": [
|
|
||||||
{
|
|
||||||
"name": "Tal Shiar Operation",
|
|
||||||
"description": "Execute a complex Tal Shiar intelligence gathering mission.",
|
|
||||||
"points": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Romulan Empire Expansion",
|
|
||||||
"description": "Expand Romulan influence into a new sector of space.",
|
|
||||||
"points": 8
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
---
|
---
|
||||||
import Layout from '../layouts/Layout.astro';
|
import Layout from '../layouts/Layout.astro';
|
||||||
import { UIService } from '../utils/ui';
|
import { UIService } from '../utils/ui';
|
||||||
|
import { captainDataManager } from '../utils/captainData';
|
||||||
|
|
||||||
// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build
|
// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build
|
||||||
// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.
|
// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.
|
||||||
|
|
@ -18,6 +19,9 @@ const captains: Captain[] = [
|
||||||
{ id: 'shran', name: 'Shran' },
|
{ id: 'shran', name: 'Shran' },
|
||||||
{ id: 'koloth', name: 'Koloth' }
|
{ id: 'koloth', name: 'Koloth' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Initialize UI service
|
||||||
|
const uiService = UIService.getInstance();
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="STCC - Token Counter">
|
<Layout title="STCC - Token Counter">
|
||||||
|
|
@ -25,21 +29,26 @@ const captains: Captain[] = [
|
||||||
<h1 class="text-4xl font-bold text-center mb-8 text-blue-400">Star Trek Captain's Chair</h1>
|
<h1 class="text-4xl font-bold text-center mb-8 text-blue-400">Star Trek Captain's Chair</h1>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
<!-- Player 1 -->
|
<!-- Player 1 Section -->
|
||||||
<div class="bg-gray-800 rounded-lg p-6 shadow-lg border border-blue-500">
|
<div class="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||||
<div class="mb-4">
|
<div class="space-y-4">
|
||||||
|
<!-- Missions Section -->
|
||||||
|
<div id="player1-missions" class="hidden">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-300 mb-2 text-center">Available Missions</h3>
|
||||||
|
<div id="player1-missions-list" class="space-y-2">
|
||||||
|
<!-- Missions will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="player1-captain" class="block text-sm font-medium text-gray-300 mb-1">Select Captain</label>
|
||||||
|
<select id="player1-captain" class="w-full bg-gray-700 text-white rounded px-3 py-2">
|
||||||
|
<option value="">Choose a captain...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<label class="block text-sm font-medium text-gray-300 mb-2">Select Captain</label>
|
<label class="block text-sm font-medium text-gray-300 mb-2">Mode:</label>
|
||||||
<select
|
|
||||||
id="player1-captain"
|
|
||||||
class="w-full bg-gray-700 text-white rounded px-4 py-2 border border-blue-400 focus:outline-none focus:border-blue-300"
|
|
||||||
>
|
|
||||||
<option value="">Choose a captain...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<label class="text-sm font-medium text-gray-300">Mode:</label>
|
|
||||||
<button
|
<button
|
||||||
id="player1-mode-toggle"
|
id="player1-mode-toggle"
|
||||||
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||||
|
|
@ -135,32 +144,29 @@ const captains: Captain[] = [
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Missions Section -->
|
|
||||||
<div id="player1-missions" class="hidden">
|
|
||||||
<h3 class="text-lg font-semibold text-gray-300 mb-2 text-center">Available Missions</h3>
|
|
||||||
<div id="player1-missions-list" class="space-y-2">
|
|
||||||
<!-- Missions will be populated here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Player 2 -->
|
<!-- Player 2 Section -->
|
||||||
<div class="bg-gray-800 rounded-lg p-6 shadow-lg border border-red-500">
|
<div class="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||||
<div class="mb-4">
|
<div class="space-y-4">
|
||||||
|
<!-- Missions Section -->
|
||||||
|
<div id="player2-missions" class="hidden">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-300 mb-2 text-center">Available Missions</h3>
|
||||||
|
<div id="player2-missions-list" class="space-y-2">
|
||||||
|
<!-- Missions will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="player2-captain" class="block text-sm font-medium text-gray-300 mb-1">Select Captain</label>
|
||||||
|
<select id="player2-captain" class="w-full bg-gray-700 text-white rounded px-3 py-2">
|
||||||
|
<option value="">Choose a captain...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<label class="block text-sm font-medium text-gray-300 mb-2">Select Captain</label>
|
<label class="block text-sm font-medium text-gray-300 mb-2">Mode:</label>
|
||||||
<select
|
|
||||||
id="player2-captain"
|
|
||||||
class="w-full bg-gray-700 text-white rounded px-4 py-2 border border-red-400 focus:outline-none focus:border-red-300"
|
|
||||||
>
|
|
||||||
<option value="">Choose a captain...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<label class="text-sm font-medium text-gray-300">Mode:</label>
|
|
||||||
<button
|
<button
|
||||||
id="player2-mode-toggle"
|
id="player2-mode-toggle"
|
||||||
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
|
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
|
||||||
|
|
@ -256,14 +262,6 @@ const captains: Captain[] = [
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Missions Section -->
|
|
||||||
<div id="player2-missions" class="hidden">
|
|
||||||
<h3 class="text-lg font-semibold text-gray-300 mb-2 text-center">Available Missions</h3>
|
|
||||||
<div id="player2-missions-list" class="space-y-2">
|
|
||||||
<!-- Missions will be populated here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -319,7 +317,7 @@ const captains: Captain[] = [
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { UIService } from '../utils/ui';
|
import { UIService } from '../utils/ui';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
UIService.getInstance().initialize();
|
UIService.getInstance().initialize();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { Captain } from './types';
|
||||||
|
|
||||||
interface ThresholdTable {
|
interface ThresholdTable {
|
||||||
basic: {
|
basic: {
|
||||||
threshold: number;
|
threshold: number;
|
||||||
|
|
@ -38,12 +40,13 @@ interface CaptainConfig {
|
||||||
[key: string]: ThresholdTable;
|
[key: string]: ThresholdTable;
|
||||||
};
|
};
|
||||||
basicMission: Mission;
|
basicMission: Mission;
|
||||||
advancedMissions: Mission[];
|
advancedMissions?: Mission[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class CaptainDataManager {
|
class CaptainDataManager {
|
||||||
private static instance: CaptainDataManager;
|
private static instance: CaptainDataManager;
|
||||||
private captainConfigs: Map<string, CaptainConfig> = new Map();
|
private captainConfigs: Map<string, CaptainConfig> = new Map();
|
||||||
|
private availableCaptains: Captain[] = [];
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
|
|
@ -54,13 +57,35 @@ class CaptainDataManager {
|
||||||
return CaptainDataManager.instance;
|
return CaptainDataManager.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadAvailableCaptains(): Promise<Captain[]> {
|
||||||
|
if (this.availableCaptains.length > 0) {
|
||||||
|
console.log('Returning cached captains:', this.availableCaptains);
|
||||||
|
return this.availableCaptains;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Loading captains from index.json...');
|
||||||
|
const response = await fetch('/src/assets/captains/index.json');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to load captains index');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
this.availableCaptains = data.captains;
|
||||||
|
console.log('Set available captains:', this.availableCaptains);
|
||||||
|
return this.availableCaptains;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading captains:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async loadCaptainConfig(captainId: string): Promise<CaptainConfig> {
|
async loadCaptainConfig(captainId: string): Promise<CaptainConfig> {
|
||||||
if (this.captainConfigs.has(captainId)) {
|
if (this.captainConfigs.has(captainId)) {
|
||||||
return this.captainConfigs.get(captainId)!;
|
return this.captainConfigs.get(captainId)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/src/assets/captains/${captainId}/config.json`);
|
const response = await fetch(`/src/assets/captains/${captainId}.json`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to load captain config for ${captainId}`);
|
throw new Error(`Failed to load captain config for ${captainId}`);
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +148,7 @@ class CaptainDataManager {
|
||||||
|
|
||||||
// In advanced mode, include additional missions
|
// In advanced mode, include additional missions
|
||||||
if (mode === 'advanced') {
|
if (mode === 'advanced') {
|
||||||
missions.push(...config.advancedMissions);
|
missions.push(...config.advancedMissions || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
return missions;
|
return missions;
|
||||||
|
|
|
||||||
171
src/utils/ui.ts
171
src/utils/ui.ts
|
|
@ -46,36 +46,61 @@ export class UIService {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCaptainOptions(playerNum: PlayerNumber): void {
|
private async updateCaptainOptions(playerNum: PlayerNumber): Promise<void> {
|
||||||
const playerSelect = this.getElement<HTMLSelectElement>(`player${playerNum}-captain`);
|
const select = this.getElement<HTMLSelectElement>(`player${playerNum}-captain`);
|
||||||
if (!playerSelect) return;
|
if (!select) return;
|
||||||
|
|
||||||
// Clear existing options except the first one
|
try {
|
||||||
while (playerSelect.options.length > 1) {
|
console.log('Loading available captains...');
|
||||||
playerSelect.remove(1);
|
const captains = await captainDataManager.loadAvailableCaptains();
|
||||||
}
|
console.log('Loaded captains:', captains);
|
||||||
|
const currentValue = select.value;
|
||||||
|
|
||||||
// Add available captains
|
// Clear all existing options
|
||||||
const captains = [
|
select.innerHTML = '';
|
||||||
{ id: 'picard', name: 'Picard' },
|
|
||||||
{ id: 'sisko', name: 'Sisko' },
|
|
||||||
{ id: 'sela', name: 'Sela' },
|
|
||||||
{ id: 'burnham', name: 'Burnham' },
|
|
||||||
{ id: 'shran', name: 'Shran' },
|
|
||||||
{ id: 'koloth', name: 'Koloth' }
|
|
||||||
];
|
|
||||||
|
|
||||||
captains.forEach(captain => {
|
// Get the other player's selected captain
|
||||||
const otherPlayer = playerNum === 1 ? 'player2' : 'player1';
|
const otherPlayerNum = playerNum === 1 ? 2 : 1;
|
||||||
if (captain.id !== this.gameState[otherPlayer].captain) {
|
const otherPlayerKey = `player${otherPlayerNum}` as keyof GameState;
|
||||||
const option = new Option(captain.name, captain.id);
|
const otherPlayerCaptain = this.gameState[otherPlayerKey].captain;
|
||||||
playerSelect.add(option);
|
|
||||||
|
// Filter out the other player's selected captain
|
||||||
|
const availableCaptains = captains.filter(captain => captain.id !== otherPlayerCaptain);
|
||||||
|
|
||||||
|
// Add captain options
|
||||||
|
availableCaptains.forEach(captain => {
|
||||||
|
console.log('Adding captain option:', captain);
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = captain.id;
|
||||||
|
option.textContent = captain.name;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If no captain is currently selected, select the first one
|
||||||
|
if (!currentValue && availableCaptains.length > 0) {
|
||||||
|
select.value = availableCaptains[0].id;
|
||||||
|
this.gameState[`player${playerNum}` as keyof GameState].captain = availableCaptains[0].id;
|
||||||
|
try {
|
||||||
|
await captainDataManager.loadCaptainConfig(availableCaptains[0].id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading captain config:', error);
|
||||||
|
}
|
||||||
|
} else if (currentValue && availableCaptains.some(c => c.id === currentValue)) {
|
||||||
|
// Restore the previously selected value if it exists and is still available
|
||||||
|
select.value = currentValue;
|
||||||
|
} else if (availableCaptains.length > 0) {
|
||||||
|
// If the current selection is no longer available, select the first available captain
|
||||||
|
select.value = availableCaptains[0].id;
|
||||||
|
this.gameState[`player${playerNum}` as keyof GameState].captain = availableCaptains[0].id;
|
||||||
|
try {
|
||||||
|
await captainDataManager.loadCaptainConfig(availableCaptains[0].id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading captain config:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
console.error('Error updating captain options:', error);
|
||||||
// Set the current value
|
}
|
||||||
const playerKey = `player${playerNum}` as keyof GameState;
|
|
||||||
playerSelect.value = this.gameState[playerKey].captain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateMultiplier(playerNum: PlayerNumber, specialty: 'research' | 'influence' | 'military'): string {
|
private calculateMultiplier(playerNum: PlayerNumber, specialty: 'research' | 'influence' | 'military'): string {
|
||||||
|
|
@ -123,11 +148,48 @@ export class UIService {
|
||||||
const toggle = this.getElement<HTMLButtonElement>(`player${playerNum}-mode-toggle`);
|
const toggle = this.getElement<HTMLButtonElement>(`player${playerNum}-mode-toggle`);
|
||||||
const label = this.getElement<HTMLSpanElement>(`player${playerNum}-mode-label`);
|
const label = this.getElement<HTMLSpanElement>(`player${playerNum}-mode-label`);
|
||||||
|
|
||||||
if (toggle) toggle.setAttribute('aria-checked', (player.mode === 'advanced').toString());
|
// Disable/enable mode toggle based on captain selection
|
||||||
|
const isCaptainSelected = player.captain !== '' && player.captain !== 'Choose a captain...';
|
||||||
|
if (toggle) {
|
||||||
|
toggle.disabled = !isCaptainSelected;
|
||||||
|
toggle.setAttribute('aria-checked', (player.mode === 'advanced').toString());
|
||||||
|
if (!isCaptainSelected) {
|
||||||
|
toggle.classList.add('opacity-50', 'cursor-not-allowed');
|
||||||
|
} else {
|
||||||
|
toggle.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||||
|
}
|
||||||
|
}
|
||||||
if (label) label.textContent = player.mode === 'advanced' ? 'Advanced' : 'Basic';
|
if (label) label.textContent = player.mode === 'advanced' ? 'Advanced' : 'Basic';
|
||||||
|
|
||||||
|
// Update missions display
|
||||||
|
const missionsContainer = this.getElement<HTMLDivElement>(`player${playerNum}-missions`);
|
||||||
|
const missionsList = this.getElement<HTMLDivElement>(`player${playerNum}-missions-list`);
|
||||||
|
|
||||||
|
if (missionsContainer && missionsList) {
|
||||||
|
missionsList.innerHTML = '';
|
||||||
|
if (isCaptainSelected) {
|
||||||
|
const missions = captainDataManager.getAvailableMissions(
|
||||||
|
player.captain,
|
||||||
|
{
|
||||||
|
research: player.research,
|
||||||
|
influence: player.influence,
|
||||||
|
military: player.military
|
||||||
|
},
|
||||||
|
player.mode
|
||||||
|
);
|
||||||
|
|
||||||
|
missions.forEach(mission => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = `${mission.name} (${mission.points} points)`;
|
||||||
|
missionsList.appendChild(li);
|
||||||
|
});
|
||||||
|
missionsContainer.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
missionsContainer.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Disable/enable counter buttons based on captain selection
|
// Disable/enable counter buttons based on captain selection
|
||||||
const isCaptainSelected = !!player.captain;
|
|
||||||
const playerButtons = document.querySelectorAll(`.token-btn[data-player="${playerNum}"]`);
|
const playerButtons = document.querySelectorAll(`.token-btn[data-player="${playerNum}"]`);
|
||||||
playerButtons.forEach(button => {
|
playerButtons.forEach(button => {
|
||||||
(button as HTMLButtonElement).disabled = !isCaptainSelected;
|
(button as HTMLButtonElement).disabled = !isCaptainSelected;
|
||||||
|
|
@ -137,6 +199,16 @@ export class UIService {
|
||||||
button.classList.remove('opacity-50', 'cursor-not-allowed');
|
button.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset counters if no captain is selected
|
||||||
|
if (!isCaptainSelected) {
|
||||||
|
player.latinum = 0;
|
||||||
|
player.dilithium = 0;
|
||||||
|
player.glory = 0;
|
||||||
|
player.research = 0;
|
||||||
|
player.influence = 0;
|
||||||
|
player.military = 0;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update resource counters
|
// Update resource counters
|
||||||
|
|
@ -160,16 +232,9 @@ export class UIService {
|
||||||
const multiplier = this.getElement<HTMLSpanElement>(`player${playerNum}-${specialty}-multiplier`);
|
const multiplier = this.getElement<HTMLSpanElement>(`player${playerNum}-${specialty}-multiplier`);
|
||||||
|
|
||||||
if (counter) counter.textContent = player[specialty as keyof typeof player].toString();
|
if (counter) counter.textContent = player[specialty as keyof typeof player].toString();
|
||||||
if (multiplier) {
|
if (multiplier) multiplier.textContent = this.calculateMultiplier(playerNum as PlayerNumber, specialty as 'research' | 'influence' | 'military');
|
||||||
const multiplierValue = this.calculateMultiplier(playerNum as PlayerNumber, specialty as 'research' | 'influence' | 'military');
|
|
||||||
console.log(`Updating multiplier for player ${playerNum} ${specialty}:`, multiplierValue);
|
|
||||||
multiplier.textContent = multiplierValue;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update missions
|
|
||||||
this.updateMissions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateMissions(): void {
|
private updateMissions(): void {
|
||||||
|
|
@ -254,18 +319,13 @@ export class UIService {
|
||||||
if (player1CaptainSelect) {
|
if (player1CaptainSelect) {
|
||||||
player1CaptainSelect.addEventListener('change', async (e: Event) => {
|
player1CaptainSelect.addEventListener('change', async (e: Event) => {
|
||||||
const target = e.target as HTMLSelectElement;
|
const target = e.target as HTMLSelectElement;
|
||||||
if (target.value === player2CaptainSelect?.value && target.value !== '') {
|
|
||||||
alert('Cannot select the same captain for both players');
|
|
||||||
target.value = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.gameState.player1.captain = target.value;
|
this.gameState.player1.captain = target.value;
|
||||||
if (target.value) {
|
try {
|
||||||
try {
|
await captainDataManager.loadCaptainConfig(target.value);
|
||||||
await captainDataManager.loadCaptainConfig(target.value);
|
// Update player 2's options to remove the selected captain
|
||||||
} catch (error) {
|
await this.updateCaptainOptions(2);
|
||||||
console.error('Error loading captain config:', error);
|
} catch (error) {
|
||||||
}
|
console.error('Error loading captain config:', error);
|
||||||
}
|
}
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
StorageService.saveGameState(this.gameState);
|
StorageService.saveGameState(this.gameState);
|
||||||
|
|
@ -275,18 +335,13 @@ export class UIService {
|
||||||
if (player2CaptainSelect) {
|
if (player2CaptainSelect) {
|
||||||
player2CaptainSelect.addEventListener('change', async (e: Event) => {
|
player2CaptainSelect.addEventListener('change', async (e: Event) => {
|
||||||
const target = e.target as HTMLSelectElement;
|
const target = e.target as HTMLSelectElement;
|
||||||
if (target.value === player1CaptainSelect?.value && target.value !== '') {
|
|
||||||
alert('Cannot select the same captain for both players');
|
|
||||||
target.value = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.gameState.player2.captain = target.value;
|
this.gameState.player2.captain = target.value;
|
||||||
if (target.value) {
|
try {
|
||||||
try {
|
await captainDataManager.loadCaptainConfig(target.value);
|
||||||
await captainDataManager.loadCaptainConfig(target.value);
|
// Update player 1's options to remove the selected captain
|
||||||
} catch (error) {
|
await this.updateCaptainOptions(1);
|
||||||
console.error('Error loading captain config:', error);
|
} catch (error) {
|
||||||
}
|
console.error('Error loading captain config:', error);
|
||||||
}
|
}
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
StorageService.saveGameState(this.gameState);
|
StorageService.saveGameState(this.gameState);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue