Add Notes Minigame: Implement interactive note display and inventory integration

Introduce a new Notes Minigame that allows players to view and interact with notes in a notepad-style interface. The minigame supports adding notes to the inventory, displaying observations, and includes navigation features such as previous/next buttons and search functionality. Update relevant files for integration with the existing game systems, including interaction and inventory management. Add test HTML for verifying minigame features and include necessary assets for the notepad background.
This commit is contained in:
Z. Cliffe Schreuders
2025-10-10 02:39:28 +01:00
parent e8db636f45
commit b8c8c79f86
12 changed files with 1186 additions and 32 deletions

113
NOTES_MINIGAME_USAGE.md Normal file
View File

@@ -0,0 +1,113 @@
# Notes Minigame Usage
The Notes Minigame provides an interactive way to display note content with a notepad background and allows players to add notes to their inventory.
## Features
- Displays note content on a notepad background (`/assets/mini-games/notepad.png`)
- Shows observation text below the main content (if provided)
- Provides a "Add to Inventory" button with backpack icon (`/assets/mini-games/backpack.png`)
- Integrates with the existing inventory system
- Uses the minigame framework for consistent UI
## Usage in Scenarios
To use the notes minigame in your scenario files, add the following properties to note objects:
```json
{
"type": "notes",
"name": "Example Note",
"takeable": true,
"readable": true,
"text": "This is the main content of the note.\n\nIt can contain multiple lines and will be displayed on the notepad background.",
"observations": "The handwriting appears rushed and there are coffee stains on the paper."
}
```
### Required Properties
- `type`: Must be "notes"
- `text`: The main content to display on the notepad
- `readable`: Must be true to trigger the minigame
### Optional Properties
- `observations`: Additional observation text displayed below the main content
- `takeable`: Whether the note can be added to inventory (default: true)
## Programmatic Usage
You can also start the notes minigame programmatically:
```javascript
// Basic usage
window.startNotesMinigame(item, text, observations);
// Example
const testItem = {
scene: null,
scenarioData: {
type: 'notes',
name: 'Test Note',
text: 'This is a test note content.',
observations: 'The note appears to be written in haste.'
}
};
window.startNotesMinigame(testItem, testItem.scenarioData.text, testItem.scenarioData.observations);
// Show mission brief
window.showMissionBrief();
```
## Features
### Navigation
- **Previous/Next Buttons**: Navigate through collected notes
- **Search Functionality**: Search through note titles and content
- **Note Counter**: Shows current position (e.g., "2 / 5")
### Mission Brief Integration
- The mission brief is automatically displayed via the notes minigame when starting a new scenario
- Uses the same notepad interface for consistency
- Automatically added to the notes system as an important note
### Search
- Real-time search through note titles and content
- Case-insensitive matching
- Filters the note list to show only matching results
- Clear search to show all notes again
### Player Notes
- **Edit Observations**: Click the edit button (✏️) to add or modify observations
- **Handwritten Style**: Player notes use the same handwritten font as original observations
- **Persistent Storage**: Player notes are saved to the notes system and persist between sessions
- **Visual Feedback**: Dashed border and background indicate editable areas
### Visual Effects
- **Celotape Effect**: Realistic celotape strip overlapping the top of the text box
- **Binder Holes**: Small circular holes on the left side of the text box
- **Handwritten Fonts**: Uses Google Fonts 'Kalam' for authentic handwritten appearance
## Automatic Collection
- **Auto-Collection**: Notes are automatically added to the notes system when the minigame starts
- **Scene Removal**: Notes are automatically removed from the scene after being collected
- **No Manual Action**: Players don't need to click "Add to Inventory" - it happens automatically
- **Seamless Experience**: Notes are collected and removed from the world in one smooth interaction
## Integration
The notes minigame is automatically integrated into the interaction system. When a player interacts with a note object that has `text`, the minigame will be triggered instead of the default text display. The note is automatically collected and removed from the scene.
## Testing
A test file is available at `test-notes-minigame.html` to verify the implementation works correctly.
## Files Modified
- `js/minigames/notes/notes-minigame.js` - Main minigame implementation
- `js/minigames/index.js` - Registration and global export
- `js/systems/interactions.js` - Integration with interaction system
- `js/systems/inventory.js` - Made addToInventory function globally available

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -57,7 +57,7 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":11,
"name":"flat",
"name":"props",
"opacity":1,
"type":"tilelayer",
"visible":true,

View File

@@ -3,7 +3,8 @@
{
"export":
{
"format":"json"
"format":"json",
"target":"room_closet2.json"
}
},
"height":10,
@@ -64,7 +65,7 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":11,
"name":"flat",
"name":"props",
"opacity":1,
"type":"tilelayer",
"visible":true,

View File

@@ -484,6 +484,11 @@ export function create() {
// Show introduction
introduceScenario();
// Initialize physics debug display (visual debug off by default)
if (window.initializePhysicsDebugDisplay) {
window.initializePhysicsDebugDisplay();
}
// Store game reference globally
window.game = this;
}

View File

@@ -5,6 +5,7 @@ export { MinigameScene } from './framework/base-minigame.js';
// Export minigame implementations
export { LockpickingMinigamePhaser } from './lockpicking/lockpicking-game-phaser.js';
export { DustingMinigame } from './dusting/dusting-game.js';
export { NotesMinigame, startNotesMinigame, showMissionBrief } from './notes/notes-minigame.js';
// Initialize the global minigame framework for backward compatibility
import { MinigameFramework } from './framework/minigame-manager.js';
@@ -16,7 +17,15 @@ window.MinigameFramework = MinigameFramework;
// Import the dusting minigame
import { DustingMinigame } from './dusting/dusting-game.js';
// Import the notes minigame
import { NotesMinigame, startNotesMinigame, showMissionBrief } from './notes/notes-minigame.js';
// Register minigames
MinigameFramework.registerScene('lockpicking', LockpickingMinigamePhaser); // Use Phaser version as default
MinigameFramework.registerScene('lockpicking-phaser', LockpickingMinigamePhaser); // Keep explicit phaser name
MinigameFramework.registerScene('dusting', DustingMinigame);
MinigameFramework.registerScene('dusting', DustingMinigame);
MinigameFramework.registerScene('notes', NotesMinigame);
// Make notes minigame functions available globally
window.startNotesMinigame = startNotesMinigame;
window.showMissionBrief = showMissionBrief;

View File

@@ -0,0 +1,800 @@
import { MinigameScene } from '../framework/base-minigame.js';
// Load handwritten font
const fontLink = document.createElement('link');
fontLink.href = 'https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap';
fontLink.rel = 'stylesheet';
if (!document.querySelector('link[href*="Kalam"]')) {
document.head.appendChild(fontLink);
}
// Notes Minigame Scene implementation
export class NotesMinigame extends MinigameScene {
constructor(container, params) {
// Ensure params is defined before calling parent constructor
params = params || {};
// Set default title if not provided
if (!params.title) {
params.title = 'Reading Notes';
}
super(container, params);
this.item = params.item;
this.noteContent = params.noteContent || this.item?.scenarioData?.noteContent || this.item?.scenarioData?.text || 'No content available';
this.observationText = params.observationText || this.item?.scenarioData?.observationText || this.item?.scenarioData?.observations || '';
// Initialize note navigation
this.currentNoteIndex = 0;
this.collectedNotes = this.getCollectedNotes();
this.autoAddToNotes = true;
}
init() {
// Call parent init to set up common components
super.init();
console.log("Notes minigame initializing");
// Set container dimensions to take up most of the screen
this.container.style.width = '90%';
this.container.style.height = '85%';
this.container.style.padding = '20px';
// Set up header content
this.headerElement.innerHTML = `
<h3>Reading Notes</h3>
<p>Note automatically added to your collection</p>
`;
// Configure game container with notepad background - scaled to fill most of the screen
this.gameContainer.style.cssText = `
width: 100%;
min-height: 100%;
max-width: 800px;
max-height: none;
background-image: url('assets/mini-games/notepad.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
position: relative;
margin: 20px auto;
padding: 10px 140px 50px 150px;
box-sizing: border-box;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
`;
// Create content area
const contentArea = document.createElement('div');
contentArea.style.cssText = `
width: 100%;
min-height: 100%;
font-family: 'Courier New', monospace;
font-size: 18px;
line-height: 1.5;
color: #333;
background: transparent;
padding: 0;
margin: 0;
`;
// Create text box container to look like it's stuck in a binder
const textBox = document.createElement('div');
textBox.style.cssText = `
margin: 20px 50px 60px 80px;
padding: 40px;
background: #fefefe;
border: 2px solid #ddd;
border-radius: 3px;
box-shadow:
0 2px 4px rgba(0,0,0,0.1),
inset 0 1px 0 rgba(255,255,255,0.8);
position: relative;
min-height: fit-content;
`;
// Add celotape effect
const celotape = document.createElement('div');
celotape.className = 'notes-minigame-celotape';
celotape.style.cssText = `
position: absolute;
top: -8px;
left: 80px;
right: 80px;
height: 16px;
background: linear-gradient(90deg,
rgba(255,255,255,0.9) 0%,
rgba(255,255,255,0.7) 20%,
rgba(255,255,255,0.9) 40%,
rgba(255,255,255,0.7) 60%,
rgba(255,255,255,0.9) 80%,
rgba(255,255,255,0.7) 100%);
border: 1px solid rgba(200,200,200,0.8);
border-radius: 2px;
box-shadow:
0 1px 2px rgba(0,0,0,0.1),
inset 0 1px 0 rgba(255,255,255,0.9);
z-index: 1;
`;
textBox.appendChild(celotape);
// Add binder holes effect
const binderHoles = document.createElement('div');
binderHoles.style.cssText = `
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 80px;
display: flex;
flex-direction: column;
justify-content: space-between;
`;
// Add note title/name above the text box
const noteTitle = document.createElement('div');
noteTitle.className = 'notes-minigame-title';
noteTitle.style.cssText = `
margin: 0 50px 20px 80px;
font-family: 'Kalam', 'Comic Sans MS', cursive;
font-size: 20px;
font-weight: bold;
color: #2c3e50;
text-decoration: underline;
text-decoration-color: #3498db;
text-underline-offset: 3px;
text-align: center;
`;
noteTitle.textContent = this.item?.scenarioData?.name || 'Note';
contentArea.appendChild(noteTitle);
// Add note content
const noteText = document.createElement('div');
noteText.className = 'notes-minigame-text';
noteText.style.cssText = `
margin-left: 30px;
white-space: pre-wrap;
word-wrap: break-word;
color: #333;
`;
noteText.textContent = this.noteContent;
textBox.appendChild(noteText);
contentArea.appendChild(textBox);
// Add observation text if available - handwritten directly on the page
if (this.observationText) {
const observationContainer = document.createElement('div');
observationContainer.className = 'notes-minigame-observation-container';
observationContainer.style.cssText = `
margin: 20px 50px 60px 80px;
position: relative;
`;
const observationDiv = document.createElement('div');
observationDiv.className = 'notes-minigame-observation';
observationDiv.style.cssText = `
font-family: 'Kalam', 'Comic Sans MS', cursive;
font-style: italic;
color: #666;
font-size: 18px;
line-height: 1.4;
text-align: left;
min-height: 30px;
padding: 10px;
border: 1px dashed #ccc;
border-radius: 3px;
background: rgba(255, 255, 255, 0.3);
`;
observationDiv.innerHTML = this.observationText;
// Add edit button
const editBtn = document.createElement('button');
editBtn.className = 'notes-minigame-edit-btn';
editBtn.style.cssText = `
position: absolute;
top: -8px;
right: -8px;
background: #3498db;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
transition: background-color 0.3s ease;
`;
editBtn.innerHTML = '✏️';
editBtn.title = 'Edit observations';
editBtn.addEventListener('click', () => this.editObservations(observationDiv));
observationContainer.appendChild(observationDiv);
observationContainer.appendChild(editBtn);
contentArea.appendChild(observationContainer);
} else {
// Add empty observation area with edit button
const observationContainer = document.createElement('div');
observationContainer.className = 'notes-minigame-observation-container';
observationContainer.style.cssText = `
margin: 20px 50px 60px 80px;
position: relative;
`;
const observationDiv = document.createElement('div');
observationDiv.className = 'notes-minigame-observation';
observationDiv.style.cssText = `
font-family: 'Kalam', 'Comic Sans MS', cursive;
font-style: italic;
color: #999;
font-size: 18px;
line-height: 1.4;
text-align: left;
min-height: 30px;
padding: 10px;
border: 1px dashed #ccc;
border-radius: 3px;
background: rgba(255, 255, 255, 0.3);
`;
observationDiv.innerHTML = '<em>Click edit to add your observations...</em>';
// Add edit button
const editBtn = document.createElement('button');
editBtn.className = 'notes-minigame-edit-btn';
editBtn.style.cssText = `
position: absolute;
top: -8px;
right: -8px;
background: #3498db;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
transition: background-color 0.3s ease;
`;
editBtn.innerHTML = '✏️';
editBtn.title = 'Add observations';
editBtn.addEventListener('click', () => this.editObservations(observationDiv));
observationContainer.appendChild(observationDiv);
observationContainer.appendChild(editBtn);
contentArea.appendChild(observationContainer);
}
this.gameContainer.appendChild(contentArea);
// Create navigation buttons container
const navContainer = document.createElement('div');
navContainer.style.cssText = `
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
z-index: 10;
`;
// Add search input if there are multiple notes
if (this.collectedNotes.length > 1) {
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = 'Search notes...';
searchInput.className = 'notes-minigame-search';
searchInput.style.cssText = `
padding: 8px 12px;
border: 1px solid #555;
border-radius: 5px;
background: rgba(0,0,0,0.7);
color: white;
font-size: 14px;
width: 200px;
margin-right: 10px;
`;
searchInput.addEventListener('input', (e) => this.searchNotes(e.target.value));
navContainer.appendChild(searchInput);
const prevBtn = document.createElement('button');
prevBtn.className = 'minigame-button notes-nav-button';
prevBtn.style.cssText = `
background: #95a5a6;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: background-color 0.3s ease;
`;
prevBtn.textContent = '← Previous';
prevBtn.addEventListener('click', () => this.navigateToNote(-1));
navContainer.appendChild(prevBtn);
const nextBtn = document.createElement('button');
nextBtn.className = 'minigame-button notes-nav-button';
nextBtn.style.cssText = `
background: #95a5a6;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: background-color 0.3s ease;
`;
nextBtn.textContent = 'Next →';
nextBtn.addEventListener('click', () => this.navigateToNote(1));
navContainer.appendChild(nextBtn);
// Add note counter
const noteCounter = document.createElement('div');
noteCounter.className = 'notes-minigame-counter';
noteCounter.style.cssText = `
color: white;
font-size: 14px;
display: flex;
align-items: center;
padding: 8px 15px;
background: rgba(0,0,0,0.5);
border-radius: 5px;
`;
noteCounter.textContent = `${this.currentNoteIndex + 1} / ${this.collectedNotes.length}`;
navContainer.appendChild(noteCounter);
this.container.appendChild(navContainer);
}
// Create action buttons container
const buttonsContainer = document.createElement('div');
buttonsContainer.style.cssText = `
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
z-index: 10;
`;
// Close button
const closeBtn = document.createElement('button');
closeBtn.className = 'minigame-button notes-close-button';
closeBtn.style.cssText = `
background: #95a5a6;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: background-color 0.3s ease;
`;
closeBtn.textContent = 'Close';
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.backgroundColor = '#7f8c8d';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.backgroundColor = '#95a5a6';
});
closeBtn.addEventListener('click', () => {
this.complete(false);
});
buttonsContainer.appendChild(closeBtn);
this.container.appendChild(buttonsContainer);
}
removeNoteFromScene() {
// Remove the note from the scene if it has a sprite
if (this.item && this.item.sprite) {
console.log('Removing note from scene:', this.item.sprite);
// Hide the sprite
if (this.item.sprite.setVisible) {
this.item.sprite.setVisible(false);
}
// Remove from scene if it has a destroy method
if (this.item.sprite.destroy) {
this.item.sprite.destroy();
}
// Also try to remove from the scene's object list if available
if (this.item.scene && this.item.scene.objects) {
const objectIndex = this.item.scene.objects.findIndex(obj => obj.sprite === this.item.sprite);
if (objectIndex !== -1) {
this.item.scene.objects.splice(objectIndex, 1);
console.log('Removed note from scene objects list');
}
}
// Update the scene's interactive objects if available
if (this.item.scene && this.item.scene.interactiveObjects) {
const interactiveIndex = this.item.scene.interactiveObjects.findIndex(obj => obj.sprite === this.item.sprite);
if (interactiveIndex !== -1) {
this.item.scene.interactiveObjects.splice(interactiveIndex, 1);
console.log('Removed note from scene interactive objects list');
}
}
}
}
getCollectedNotes() {
// Get all notes from the notes system that are marked as important or have been collected
if (!window.gameState || !window.gameState.notes) {
return [];
}
// Filter for important notes or notes that look like they were collected from objects
return window.gameState.notes.filter(note =>
note.important ||
note.title.includes('Log') ||
note.title.includes('Note') ||
note.title.includes('Security') ||
note.title.includes('Report')
);
}
navigateToNote(direction) {
if (this.collectedNotes.length <= 1) return;
this.currentNoteIndex += direction;
// Wrap around
if (this.currentNoteIndex < 0) {
this.currentNoteIndex = this.collectedNotes.length - 1;
} else if (this.currentNoteIndex >= this.collectedNotes.length) {
this.currentNoteIndex = 0;
}
// Update the displayed note
this.updateDisplayedNote();
// Update the counter
const noteCounter = this.container.querySelector('.notes-minigame-counter');
if (noteCounter) {
noteCounter.textContent = `${this.currentNoteIndex + 1} / ${this.collectedNotes.length}`;
}
}
updateDisplayedNote() {
const currentNote = this.collectedNotes[this.currentNoteIndex];
if (!currentNote) return;
// Parse the note text to extract observations
const noteParts = this.parseNoteText(currentNote.text);
this.noteContent = noteParts.mainText;
this.observationText = noteParts.observationText;
// Update the displayed content
const noteTitle = this.container.querySelector('.notes-minigame-title');
const noteText = this.container.querySelector('.notes-minigame-text');
const observationDiv = this.container.querySelector('.notes-minigame-observation');
if (noteTitle) {
noteTitle.textContent = currentNote.title;
}
if (noteText) {
noteText.textContent = this.noteContent;
}
// Update observation container
const observationContainer = this.container.querySelector('.notes-minigame-observation-container');
if (observationContainer) {
const observationDiv = observationContainer.querySelector('.notes-minigame-observation');
const editBtn = observationContainer.querySelector('.notes-minigame-edit-btn');
if (this.observationText) {
observationDiv.innerHTML = this.observationText;
observationDiv.style.color = '#666';
editBtn.title = 'Edit observations';
} else {
observationDiv.innerHTML = '<em>Click edit to add your observations...</em>';
observationDiv.style.color = '#999';
editBtn.title = 'Add observations';
}
}
}
parseNoteText(text) {
// Parse note text to separate main content from observations
const observationMatch = text.match(/\n\nObservation:\s*(.+)$/s);
if (observationMatch) {
return {
mainText: text.replace(/\n\nObservation:\s*.+$/s, '').trim(),
observationText: observationMatch[1].trim()
};
}
return {
mainText: text,
observationText: ''
};
}
searchNotes(searchTerm) {
if (!searchTerm || searchTerm.trim() === '') {
// Reset to show all notes
this.collectedNotes = this.getCollectedNotes();
this.currentNoteIndex = 0;
this.updateDisplayedNote();
this.updateCounter();
return;
}
const searchLower = searchTerm.toLowerCase();
const matchingNotes = this.collectedNotes.filter(note =>
note.title.toLowerCase().includes(searchLower) ||
note.text.toLowerCase().includes(searchLower)
);
if (matchingNotes.length > 0) {
this.collectedNotes = matchingNotes;
this.currentNoteIndex = 0;
this.updateDisplayedNote();
this.updateCounter();
}
}
updateCounter() {
const noteCounter = this.container.querySelector('.notes-minigame-counter');
if (noteCounter) {
noteCounter.textContent = `${this.currentNoteIndex + 1} / ${this.collectedNotes.length}`;
}
}
editObservations(observationDiv) {
const currentText = observationDiv.textContent.trim();
const isPlaceholder = currentText === 'Click edit to add your observations...';
const originalText = isPlaceholder ? '' : currentText;
// Create textarea for editing
const textarea = document.createElement('textarea');
textarea.value = originalText;
textarea.style.cssText = `
width: 100%;
min-height: 60px;
font-family: 'Kalam', 'Comic Sans MS', cursive;
font-size: 18px;
line-height: 1.4;
color: #666;
border: 2px solid #3498db;
border-radius: 3px;
padding: 10px;
background: rgba(255, 255, 255, 0.9);
resize: vertical;
outline: none;
`;
textarea.placeholder = 'Add your observations here...';
// Create button container
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = `
margin-top: 10px;
display: flex;
gap: 10px;
justify-content: flex-end;
`;
// Save button
const saveBtn = document.createElement('button');
saveBtn.textContent = 'Save';
saveBtn.style.cssText = `
background: #2ecc71;
color: white;
border: none;
padding: 8px 16px;
border-radius: 3px;
cursor: pointer;
font-size: 14px;
`;
saveBtn.addEventListener('click', () => {
const newText = textarea.value.trim();
observationDiv.innerHTML = newText || '<em>Click edit to add your observations...</em>';
observationDiv.style.color = newText ? '#666' : '#999';
// Update the stored observation text
this.observationText = newText;
// Save to the current note in the notes system
this.saveObservationToNote(newText);
// Remove editing elements
textarea.remove();
buttonContainer.remove();
});
// Cancel button
const cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Cancel';
cancelBtn.style.cssText = `
background: #95a5a6;
color: white;
border: none;
padding: 8px 16px;
border-radius: 3px;
cursor: pointer;
font-size: 14px;
`;
cancelBtn.addEventListener('click', () => {
// Restore original text
if (originalText) {
observationDiv.innerHTML = originalText;
observationDiv.style.color = '#666';
} else {
observationDiv.innerHTML = '<em>Click edit to add your observations...</em>';
observationDiv.style.color = '#999';
}
// Remove editing elements
textarea.remove();
buttonContainer.remove();
});
buttonContainer.appendChild(saveBtn);
buttonContainer.appendChild(cancelBtn);
// Replace content with editing interface
observationDiv.innerHTML = '';
observationDiv.appendChild(textarea);
observationDiv.appendChild(buttonContainer);
// Focus the textarea
textarea.focus();
textarea.select();
}
saveObservationToNote(newObservationText) {
// Update the current note in the notes system
const currentNote = this.collectedNotes[this.currentNoteIndex];
if (currentNote) {
// Parse the existing text to separate main content from observations
const noteParts = this.parseNoteText(currentNote.text);
// Update the observation text
noteParts.observationText = newObservationText;
// Reconstruct the full text
let fullText = noteParts.mainText;
if (newObservationText) {
fullText += `\n\nObservation: ${newObservationText}`;
}
// Update the note in the notes system
currentNote.text = fullText;
// Also update in the global notes system if it exists
if (window.gameState && window.gameState.notes) {
const globalNote = window.gameState.notes.find(note =>
note.title === currentNote.title && note.timestamp === currentNote.timestamp
);
if (globalNote) {
globalNote.text = fullText;
}
}
console.log('Observation saved to note:', currentNote.title);
}
}
start() {
super.start();
console.log("Notes minigame started");
// Automatically add current note to notes system when starting
if (this.autoAddToNotes && window.addNote) {
const noteTitle = this.item?.scenarioData?.name || 'Note';
const noteText = this.noteContent + (this.observationText ? `\n\nObservation: ${this.observationText}` : '');
const isImportant = this.item?.scenarioData?.important || false;
const addedNote = window.addNote(noteTitle, noteText, isImportant);
if (addedNote) {
console.log('Note automatically added to notes system on start:', addedNote);
// Refresh collected notes
this.collectedNotes = this.getCollectedNotes();
// Automatically remove the note from the scene
this.removeNoteFromScene();
}
}
}
complete(success) {
// Call parent complete with result
super.complete(success, this.gameResult);
}
cleanup() {
super.cleanup();
}
}
// Export the minigame for the framework to register
// The registration is handled in the main minigames/index.js file
// Function to show mission brief via notes minigame
export function showMissionBrief() {
if (!window.gameScenario || !window.gameScenario.scenario_brief) {
console.warn('No mission brief available');
return;
}
const missionBriefItem = {
scene: null,
scenarioData: {
type: 'notes',
name: 'Mission Brief',
text: window.gameScenario.scenario_brief,
important: true
}
};
startNotesMinigame(missionBriefItem, window.gameScenario.scenario_brief, '');
}
// Function to start the notes minigame
export function startNotesMinigame(item, noteContent, observationText) {
console.log('Starting notes minigame with:', { item, noteContent, observationText });
// Make sure the minigame is registered
if (window.MinigameFramework && !window.MinigameFramework.registeredScenes['notes']) {
window.MinigameFramework.registerScene('notes', NotesMinigame);
console.log('Notes minigame registered on demand');
}
// Initialize the framework if not already done
if (!window.MinigameFramework.mainGameScene && item && item.scene) {
window.MinigameFramework.init(item.scene);
}
// Start the notes minigame with proper parameters
const params = {
title: item?.scenarioData?.name || 'Reading Notes',
item: item,
noteContent: noteContent,
observationText: observationText,
onComplete: (success, result) => {
if (success && result && result.addedToInventory) {
console.log('NOTES SUCCESS - Added to inventory', result);
// Show notification
if (window.showNotification) {
window.showNotification('Note added to inventory', 'success');
} else if (window.gameAlert) {
window.gameAlert('Note added to inventory', 'success', 'Item Collected', 3000);
}
} else {
console.log('NOTES COMPLETED - Not added to inventory');
}
}
};
console.log('Starting minigame with params:', params);
window.MinigameFramework.startMinigame('notes', null, params);
}

View File

@@ -4,7 +4,7 @@
// Debug system variables
let debugMode = false;
let debugLevel = 1; // 1 = basic, 2 = detailed, 3 = verbose
let visualDebugMode = false;
let visualDebugMode = false; // Visual debug (collision boxes, movement vectors) - off by default
// Initialize the debug system
export function initializeDebugSystem() {
@@ -13,18 +13,10 @@ export function initializeDebugSystem() {
// Toggle debug mode with backtick
if (event.key === '`') {
if (event.shiftKey) {
// Toggle visual debug mode with Shift+backtick
visualDebugMode = !visualDebugMode;
console.log(`%c[DEBUG] === VISUAL DEBUG MODE ${visualDebugMode ? 'ENABLED' : 'DISABLED'} ===`,
`color: ${visualDebugMode ? '#00AA00' : '#DD0000'}; font-weight: bold;`);
// Update physics debug display if game exists
if (window.game && window.game.scene && window.game.scene.scenes && window.game.scene.scenes[0]) {
const scene = window.game.scene.scenes[0];
if (scene.physics && scene.physics.world) {
scene.physics.world.drawDebug = debugMode && visualDebugMode;
}
}
// Toggle console debug mode with Shift+backtick
debugMode = !debugMode;
console.log(`%c[DEBUG] === CONSOLE DEBUG MODE ${debugMode ? 'ENABLED' : 'DISABLED'} ===`,
`color: ${debugMode ? '#00AA00' : '#DD0000'}; font-weight: bold;`);
} else if (event.ctrlKey) {
// Cycle through debug levels with Ctrl+backtick
if (debugMode) {
@@ -33,18 +25,13 @@ export function initializeDebugSystem() {
`color: #0077FF; font-weight: bold;`);
}
} else {
// Regular debug mode toggle
debugMode = !debugMode;
console.log(`%c[DEBUG] === DEBUG MODE ${debugMode ? 'ENABLED' : 'DISABLED'} ===`,
`color: ${debugMode ? '#00AA00' : '#DD0000'}; font-weight: bold;`);
// Regular backtick toggles visual debug mode (collision boxes, movement vectors)
visualDebugMode = !visualDebugMode;
console.log(`%c[DEBUG] === VISUAL DEBUG MODE ${visualDebugMode ? 'ENABLED' : 'DISABLED'} ===`,
`color: ${visualDebugMode ? '#00AA00' : '#DD0000'}; font-weight: bold;`);
// Update physics debug display if game exists
if (window.game && window.game.scene && window.game.scene.scenes && window.game.scene.scenes[0]) {
const scene = window.game.scene.scenes[0];
if (scene.physics && scene.physics.world) {
scene.physics.world.drawDebug = debugMode && visualDebugMode;
}
}
updatePhysicsDebugDisplay();
}
}
});
@@ -52,6 +39,17 @@ export function initializeDebugSystem() {
console.log('Debug system initialized');
}
// Function to update physics debug display
function updatePhysicsDebugDisplay() {
if (window.game && window.game.scene && window.game.scene.scenes && window.game.scene.scenes[0]) {
const scene = window.game.scene.scenes[0];
if (scene.physics && scene.physics.world) {
// Visual debug (collision boxes, movement vectors) is controlled by visualDebugMode only
scene.physics.world.drawDebug = visualDebugMode;
}
}
}
// Debug logging function that only logs when debug mode is active
export function debugLog(message, data = null, level = 1) {
if (!debugMode || debugLevel < level) return;
@@ -101,5 +99,11 @@ export function debugLog(message, data = null, level = 1) {
}
}
// Function to initialize physics debug display (called when game starts)
export function initializePhysicsDebugDisplay() {
updatePhysicsDebugDisplay();
}
// Export for global access
window.debugLog = debugLog;
window.debugLog = debugLog;
window.initializePhysicsDebugDisplay = initializePhysicsDebugDisplay;

View File

@@ -232,7 +232,16 @@ export function handleObjectInteraction(sprite) {
if (data.readable && data.text) {
message += `Text: ${data.text}\n`;
// Add readable text as a note
// For notes type objects, use the notes minigame
if (data.type === 'notes' && data.text) {
// Start the notes minigame
if (window.startNotesMinigame) {
window.startNotesMinigame(sprite, data.text, data.observations);
return; // Exit early since minigame handles the interaction
}
}
// Add readable text as a note (fallback for other readable objects)
if (data.text.trim().length > 0) {
const addedNote = window.addNote(data.name, data.text, data.important || false);

View File

@@ -193,4 +193,5 @@ function addToInventory(sprite) {
// Export for global access
window.initializeInventory = initializeInventory;
window.processInitialInventoryItems = processInitialInventoryItems;
window.processInitialInventoryItems = processInitialInventoryItems;
window.addToInventory = addToInventory;

View File

@@ -12,8 +12,16 @@ export function introduceScenario() {
// Add scenario brief as an important note
addNote("Mission Brief", gameScenario.scenario_brief, true);
// Show notification
gameAlert(gameScenario.scenario_brief, 'info', 'Mission Brief', 0);
// Show mission brief via notes minigame if available, otherwise fallback to alert
if (window.showMissionBrief) {
// Delay slightly to ensure the game is fully loaded
setTimeout(() => {
window.showMissionBrief();
}, 500);
} else {
// Fallback to old alert system
gameAlert(gameScenario.scenario_brief, 'info', 'Mission Brief', 0);
}
}
// Import crypto workstation functions

204
test-notes-features.html Normal file
View File

@@ -0,0 +1,204 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notes Minigame Features Test</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background: #1a1a1a;
color: white;
}
.test-section {
margin: 20px 0;
padding: 15px;
border: 1px solid #444;
border-radius: 5px;
}
button {
background: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #2980b9;
}
.test-notes {
background: #2c3e50;
padding: 10px;
margin: 10px 0;
border-radius: 3px;
}
</style>
</head>
<body>
<h1>Notes Minigame Features Test</h1>
<div class="test-section">
<h2>Test Mission Brief</h2>
<p>Test the mission brief functionality:</p>
<button onclick="testMissionBrief()">Show Mission Brief</button>
</div>
<div class="test-section">
<h2>Test Notes with Observations</h2>
<p>Test notes with observations, search functionality, and editing:</p>
<button onclick="addTestNotes()">Add Test Notes</button>
<button onclick="testNotesMinigame()">Open Notes Minigame</button>
<p><small>Features to test: Edit observations, celotape effect, search, navigation</small></p>
</div>
<div class="test-section">
<h2>Current Notes</h2>
<div id="notes-display"></div>
</div>
<!-- Load the minigame framework -->
<script type="module">
// Import the minigame framework
import { MinigameFramework, showMissionBrief, startNotesMinigame } from './js/minigames/index.js';
// Make functions available globally for testing
window.showMissionBrief = showMissionBrief;
window.startNotesMinigame = startNotesMinigame;
// Initialize game state
window.gameState = {
notes: []
};
// Mock game scenario
window.gameScenario = {
scenario_brief: `MISSION BRIEF: CYBER SECURITY BREACH INVESTIGATION
You are a cybersecurity expert called in to investigate a potential data breach at TechCorp Industries.
OBJECTIVES:
- Investigate suspicious network activity
- Identify the source of the breach
- Recover any compromised data
- Document findings for legal proceedings
INTEL:
- Unusual network traffic detected at 2:47 AM
- Multiple failed login attempts from unknown IPs
- Employee reports of system slowdowns
- CEO's laptop may contain critical evidence
APPROACH:
- Start in the reception area
- Gather information from available systems
- Look for physical evidence
- Document everything you find
Remember: Time is critical. The longer the breach goes unaddressed, the more data could be compromised.`
};
// Mock addNote function
window.addNote = function(title, text, important = false) {
const note = {
title: title,
text: text,
important: important,
timestamp: new Date().toISOString()
};
window.gameState.notes.push(note);
updateNotesDisplay();
return note;
};
// Mock addToInventory function
window.addToInventory = function(item) {
console.log('Added to inventory:', item);
return true;
};
// Mock notification functions
window.showNotification = function(message, type) {
console.log(`Notification [${type}]: ${message}`);
};
window.gameAlert = function(message, type, title, duration) {
console.log(`Alert [${type}] ${title}: ${message}`);
};
function updateNotesDisplay() {
const display = document.getElementById('notes-display');
display.innerHTML = '';
window.gameState.notes.forEach((note, index) => {
const noteDiv = document.createElement('div');
noteDiv.className = 'test-notes';
noteDiv.innerHTML = `
<strong>${note.title}</strong> ${note.important ? '(Important)' : ''}
<br><small>${note.text.substring(0, 100)}...</small>
`;
display.appendChild(noteDiv);
});
}
// Test functions
window.testMissionBrief = function() {
console.log('Testing mission brief...');
window.showMissionBrief();
};
window.addTestNotes = function() {
// Add some test notes with observations
window.addNote("Security Log Entry #1",
"System accessed at 2:47 AM from IP 192.168.1.100\nMultiple failed login attempts detected\nUser: admin\nStatus: BLOCKED\n\nObservation: The handwriting appears rushed and there are coffee stains on the paper.",
true);
window.addNote("Employee Report",
"Sarah Johnson reports system slowdowns starting around 2:30 AM\nScreens freezing intermittently\nNetwork connection unstable\n\nObservation: The note is written in neat handwriting with a blue pen.",
false);
window.addNote("Network Analysis",
"Traffic analysis shows unusual patterns\nHigh bandwidth usage during off-hours\nMultiple connection attempts to external servers\n\nObservation: This appears to be printed from a system log.",
true);
window.addNote("CEO Laptop Access",
"Laptop found in CEO's office\nPassword protected\nContains sensitive financial data\n\nObservation: The laptop shows signs of recent use - warm to the touch.",
true);
// Add a note without observations to test the edit functionality
window.addNote("Suspicious Activity",
"Found unusual files in the temp directory\nFiles appear to be encrypted\nCreated around 2:45 AM\nNo legitimate user activity at that time",
true);
updateNotesDisplay();
};
window.testNotesMinigame = function() {
if (window.gameState.notes.length === 0) {
alert('Please add test notes first!');
return;
}
// Create a test item for the first note
const firstNote = window.gameState.notes[0];
const testItem = {
scene: null,
scenarioData: {
type: 'notes',
name: firstNote.title,
text: firstNote.text,
important: firstNote.important
}
};
window.startNotesMinigame(testItem, firstNote.text, '');
};
// Initialize
updateNotesDisplay();
console.log('Notes minigame test page loaded');
</script>
</body>
</html>