diff --git a/NOTES_MINIGAME_USAGE.md b/NOTES_MINIGAME_USAGE.md new file mode 100644 index 0000000..21cc0c2 --- /dev/null +++ b/NOTES_MINIGAME_USAGE.md @@ -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 diff --git a/assets/mini-games/notepad.png b/assets/mini-games/notepad.png new file mode 100644 index 0000000..52b44f5 Binary files /dev/null and b/assets/mini-games/notepad.png differ diff --git a/assets/rooms/room_closet2.json b/assets/rooms/room_closet2.json index 67cc070..c42af31 100644 --- a/assets/rooms/room_closet2.json +++ b/assets/rooms/room_closet2.json @@ -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, diff --git a/assets/rooms/room_closet2.tmj b/assets/rooms/room_closet2.tmj index 61ca1b9..7c970c8 100644 --- a/assets/rooms/room_closet2.tmj +++ b/assets/rooms/room_closet2.tmj @@ -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, diff --git a/js/core/game.js b/js/core/game.js index 44b1d28..f8ccc8d 100644 --- a/js/core/game.js +++ b/js/core/game.js @@ -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; } diff --git a/js/minigames/index.js b/js/minigames/index.js index c795e85..ae3f617 100644 --- a/js/minigames/index.js +++ b/js/minigames/index.js @@ -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); \ No newline at end of file +MinigameFramework.registerScene('dusting', DustingMinigame); +MinigameFramework.registerScene('notes', NotesMinigame); + +// Make notes minigame functions available globally +window.startNotesMinigame = startNotesMinigame; +window.showMissionBrief = showMissionBrief; \ No newline at end of file diff --git a/js/minigames/notes/notes-minigame.js b/js/minigames/notes/notes-minigame.js new file mode 100644 index 0000000..bae3c1b --- /dev/null +++ b/js/minigames/notes/notes-minigame.js @@ -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 = ` +

Reading Notes

+

Note automatically added to your collection

+ `; + + // 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 = 'Click edit to add your observations...'; + + // 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 = 'Click edit to add your observations...'; + 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 || 'Click edit to add your observations...'; + 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 = 'Click edit to add your observations...'; + 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); +} diff --git a/js/systems/debug.js b/js/systems/debug.js index 58f20c1..cf0b8bf 100644 --- a/js/systems/debug.js +++ b/js/systems/debug.js @@ -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; \ No newline at end of file +window.debugLog = debugLog; +window.initializePhysicsDebugDisplay = initializePhysicsDebugDisplay; \ No newline at end of file diff --git a/js/systems/interactions.js b/js/systems/interactions.js index 8f7e438..95f9e4e 100644 --- a/js/systems/interactions.js +++ b/js/systems/interactions.js @@ -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); diff --git a/js/systems/inventory.js b/js/systems/inventory.js index aa74942..618b263 100644 --- a/js/systems/inventory.js +++ b/js/systems/inventory.js @@ -193,4 +193,5 @@ function addToInventory(sprite) { // Export for global access window.initializeInventory = initializeInventory; -window.processInitialInventoryItems = processInitialInventoryItems; \ No newline at end of file +window.processInitialInventoryItems = processInitialInventoryItems; +window.addToInventory = addToInventory; \ No newline at end of file diff --git a/js/utils/helpers.js b/js/utils/helpers.js index 3feaafa..be2fab6 100644 --- a/js/utils/helpers.js +++ b/js/utils/helpers.js @@ -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 diff --git a/test-notes-features.html b/test-notes-features.html new file mode 100644 index 0000000..8d38e14 --- /dev/null +++ b/test-notes-features.html @@ -0,0 +1,204 @@ + + + + + + Notes Minigame Features Test + + + +

Notes Minigame Features Test

+ +
+

Test Mission Brief

+

Test the mission brief functionality:

+ +
+ +
+

Test Notes with Observations

+

Test notes with observations, search functionality, and editing:

+ + +

Features to test: Edit observations, celotape effect, search, navigation

+
+ +
+

Current Notes

+
+
+ + + + +