mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-26 17:13:19 +00:00
Implementing a debug menu item for moments
Lets you see what moment is meant to come up next.
This commit is contained in:
parent
256c3b67c3
commit
b90df44aee
6 changed files with 270 additions and 72 deletions
|
|
@ -3,6 +3,9 @@
|
|||
* Handles the collapsible debug menu and organization of debug tools
|
||||
*/
|
||||
|
||||
import { updateNextMomentDisplay, initNextMomentTool } from "./nextMoment";
|
||||
import { initDebugProvinceHighlighting } from "./provinceHighlight";
|
||||
|
||||
export class DebugMenu {
|
||||
private toggleBtn: HTMLButtonElement;
|
||||
private panel: HTMLElement;
|
||||
|
|
@ -19,6 +22,9 @@ export class DebugMenu {
|
|||
}
|
||||
|
||||
this.initEventListeners();
|
||||
|
||||
// Start with the menu open
|
||||
this.toggle()
|
||||
}
|
||||
|
||||
private initEventListeners(): void {
|
||||
|
|
@ -39,14 +45,6 @@ export class DebugMenu {
|
|||
}
|
||||
});
|
||||
|
||||
// Close when clicking outside the menu
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target as Node;
|
||||
const debugMenu = document.getElementById('debug-menu');
|
||||
if (this.isExpanded && debugMenu && !debugMenu.contains(target)) {
|
||||
this.collapse();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,6 +54,7 @@ export class DebugMenu {
|
|||
const debugMenu = document.getElementById('debug-menu');
|
||||
if (debugMenu) {
|
||||
debugMenu.style.display = 'block';
|
||||
this.initTools()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +135,7 @@ export class DebugMenu {
|
|||
// If no specific position, insert before "Future Tools" section or at the end
|
||||
const futureToolsSection = Array.from(debugContent.querySelectorAll('.debug-section h4'))
|
||||
.find(h4 => h4.textContent === 'Future Tools')?.parentElement;
|
||||
|
||||
|
||||
if (futureToolsSection) {
|
||||
debugContent.insertBefore(section, futureToolsSection);
|
||||
} else {
|
||||
|
|
@ -170,4 +169,15 @@ export class DebugMenu {
|
|||
public get expanded(): boolean {
|
||||
return this.isExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
private initTools(): void {
|
||||
initNextMomentTool(this);
|
||||
initDebugProvinceHighlighting()
|
||||
}
|
||||
|
||||
public updateTools(): void {
|
||||
updateNextMomentDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
export let debugMenuInstance = new DebugMenu();
|
||||
|
|
|
|||
104
ai_animation/src/debug/nextMoment.ts
Normal file
104
ai_animation/src/debug/nextMoment.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* Next Moment Debug Tool
|
||||
* Shows the next moment that should occur based on phase name parsing
|
||||
*/
|
||||
|
||||
import { gameState } from '../gameState';
|
||||
import { getNextPhaseName, parsePhase } from '../types/moments';
|
||||
|
||||
/**
|
||||
* Initializes the next moment debug tool
|
||||
* @param debugMenu - Debug menu instance to add the tool to
|
||||
*/
|
||||
export function initNextMomentTool(debugMenu: any) {
|
||||
// Add next moment display tool
|
||||
debugMenu.addDebugTool(
|
||||
'Next Moment',
|
||||
`
|
||||
<div class="debug-tool">
|
||||
<div>Current Phase: <span id="debug-current-phase">--</span></div>
|
||||
<div>Next Phase: <span id="debug-next-phase">--</span></div>
|
||||
<div>Next Moment: <span id="debug-next-moment">--</span></div>
|
||||
<button id="debug-refresh-moment">Refresh</button>
|
||||
</div>
|
||||
`,
|
||||
'Future Tools'
|
||||
);
|
||||
|
||||
// Initialize the next moment display
|
||||
updateNextMomentDisplay();
|
||||
|
||||
// Add refresh button functionality
|
||||
const refreshBtn = document.getElementById('debug-refresh-moment');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener('click', updateNextMomentDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the next moment display in the debug menu
|
||||
*/
|
||||
export function updateNextMomentDisplay() {
|
||||
const currentPhaseElement = document.getElementById('debug-current-phase');
|
||||
const nextPhaseElement = document.getElementById('debug-next-phase');
|
||||
const nextMomentElement = document.getElementById('debug-next-moment');
|
||||
|
||||
if (!currentPhaseElement || !nextPhaseElement || !nextMomentElement) return;
|
||||
|
||||
if (!gameState.gameData || !gameState.gameData.phases || gameState.phaseIndex < 0) {
|
||||
currentPhaseElement.textContent = 'No game loaded';
|
||||
nextPhaseElement.textContent = '--';
|
||||
nextMomentElement.textContent = '--';
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPhase = gameState.gameData.phases[gameState.phaseIndex];
|
||||
const currentPhaseName = currentPhase.name;
|
||||
|
||||
currentPhaseElement.textContent = currentPhaseName;
|
||||
|
||||
// Get next phase name using our parser
|
||||
const nextPhaseName = getNextPhaseName(currentPhaseName);
|
||||
nextPhaseElement.textContent = nextPhaseName || 'Unable to parse';
|
||||
|
||||
// Find next moment across all phases
|
||||
if (gameState.momentsData) {
|
||||
const nextMoment = findNextMoment(currentPhaseName);
|
||||
if (nextMoment) {
|
||||
nextMomentElement.innerHTML = `<strong>${nextMoment.category}</strong><br/>Phase: ${nextMoment.phase}<br/>Score: ${nextMoment.interest_score}`;
|
||||
nextMomentElement.style.color = nextMoment.interest_score >= 9 ? '#ff6b6b' : '#4dabf7';
|
||||
} else {
|
||||
nextMomentElement.textContent = 'No future moments found';
|
||||
nextMomentElement.style.color = '#888';
|
||||
}
|
||||
} else {
|
||||
nextMomentElement.textContent = 'Moments data not loaded';
|
||||
nextMomentElement.style.color = '#888';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the next moment chronologically after the current phase
|
||||
* @param currentPhaseName - Current phase name
|
||||
* @returns Next moment or null if none found
|
||||
*/
|
||||
function findNextMoment(currentPhaseName: string) {
|
||||
if (!gameState.momentsData?.moments) return null;
|
||||
|
||||
const currentParsed = parsePhase(currentPhaseName);
|
||||
if (!currentParsed) return null;
|
||||
|
||||
// Get all moments that come after the current phase
|
||||
const futureMoments = gameState.momentsData.moments
|
||||
.map(moment => ({
|
||||
...moment,
|
||||
parsedPhase: parsePhase(moment.phase)
|
||||
}))
|
||||
.filter(moment =>
|
||||
moment.parsedPhase &&
|
||||
moment.parsedPhase.order > currentParsed.order
|
||||
)
|
||||
.sort((a, b) => a.parsedPhase!.order - b.parsedPhase!.order);
|
||||
|
||||
return futureMoments.length > 0 ? futureMoments[0] : null;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { gameState } from "../gameState";
|
||||
import { provinceInput, highlightProvinceBtn } from "../domElements";
|
||||
import { ProvinceENUM } from "../types/map";
|
||||
import { MeshBasicMaterial } from "three";
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ export function highlightProvince(provinceName: string): void {
|
|||
|
||||
// Normalize the province name to uppercase
|
||||
const normalizedName = provinceName.toUpperCase().trim();
|
||||
|
||||
|
||||
// Check if it's a valid province
|
||||
if (!Object.values(ProvinceENUM).includes(normalizedName as ProvinceENUM)) {
|
||||
console.warn(`Province "${normalizedName}" not found. Valid provinces are:`, Object.values(ProvinceENUM));
|
||||
|
|
@ -50,7 +51,7 @@ export function highlightProvince(provinceName: string): void {
|
|||
};
|
||||
|
||||
console.log(`Highlighting province: ${normalizedName}`);
|
||||
|
||||
|
||||
// Start the animation loop
|
||||
animateFlash();
|
||||
}
|
||||
|
|
@ -72,26 +73,26 @@ function animateFlash(): void {
|
|||
|
||||
// Calculate flash intensity using sine wave for smooth pulsing
|
||||
const flashIntensity = Math.sin(elapsed * 0.01) * 0.5 + 0.5; // 0 to 1
|
||||
|
||||
|
||||
// Interpolate between original color and bright yellow
|
||||
const material = currentFlashAnimation.mesh.material as MeshBasicMaterial;
|
||||
const originalColor = currentFlashAnimation.originalColor;
|
||||
|
||||
|
||||
// Extract RGB components from original color
|
||||
const originalR = (originalColor >> 16) & 255;
|
||||
const originalG = (originalColor >> 8) & 255;
|
||||
const originalB = originalColor & 255;
|
||||
|
||||
|
||||
// Flash to a bright yellow (255, 255, 0)
|
||||
const flashR = 255;
|
||||
const flashG = 255;
|
||||
const flashB = 0;
|
||||
|
||||
|
||||
// Interpolate between original and flash colors
|
||||
const r = Math.round(originalR + (flashR - originalR) * flashIntensity);
|
||||
const g = Math.round(originalG + (flashG - originalG) * flashIntensity);
|
||||
const b = Math.round(originalB + (flashB - originalB) * flashIntensity);
|
||||
|
||||
|
||||
// Set the new color
|
||||
const newColor = (r << 16) | (g << 8) | b;
|
||||
material.color.setHex(newColor);
|
||||
|
|
@ -109,7 +110,7 @@ function stopCurrentFlash(): void {
|
|||
// Restore original color
|
||||
const material = currentFlashAnimation.mesh.material as MeshBasicMaterial;
|
||||
material.color.setHex(currentFlashAnimation.originalColor);
|
||||
|
||||
|
||||
currentFlashAnimation = null;
|
||||
}
|
||||
|
||||
|
|
@ -118,4 +119,43 @@ function stopCurrentFlash(): void {
|
|||
*/
|
||||
export function getAvailableProvinces(): string[] {
|
||||
return Object.values(ProvinceENUM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize debug province highlighting functionality
|
||||
export function initDebugProvinceHighlighting() {
|
||||
|
||||
highlightProvinceBtn.addEventListener('click', () => {
|
||||
const provinceName = provinceInput.value.trim();
|
||||
if (provinceName) {
|
||||
highlightProvince(provinceName);
|
||||
} else {
|
||||
console.warn('Please enter a province name');
|
||||
}
|
||||
});
|
||||
|
||||
// Allow highlighting on Enter key press
|
||||
provinceInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
const provinceName = provinceInput.value.trim();
|
||||
if (provinceName) {
|
||||
highlightProvince(provinceName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add input validation and autocomplete suggestions
|
||||
provinceInput.addEventListener('input', () => {
|
||||
const input = provinceInput.value.toUpperCase().trim();
|
||||
const availableProvinces = getAvailableProvinces();
|
||||
|
||||
// Basic validation - turn input red if it doesn't match any province
|
||||
if (input && !availableProvinces.some(p => p.startsWith(input))) {
|
||||
provinceInput.style.borderColor = '#ff4444';
|
||||
provinceInput.style.backgroundColor = '#ffe6e6';
|
||||
} else {
|
||||
provinceInput.style.borderColor = '#4f3b16';
|
||||
provinceInput.style.backgroundColor = '#faf0d8';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue