Implement Notebook Functionality: Add a notebook button to the Password and Container minigames for saving post-it notes. Enhance inventory interactions with pulse animations instead of notifications for item collection. Update CSS for new animations and layout adjustments in various minigames to improve user experience.

This commit is contained in:
Z. Cliffe Schreuders
2025-10-21 12:17:28 +01:00
parent f787bd7683
commit 8fe71efa89
10 changed files with 231 additions and 27 deletions

View File

@@ -38,6 +38,26 @@
background: rgb(149 157 216 / 80%);
}
/* Pulse animation for newly added items */
@keyframes pulse-slot {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
}
50% {
transform: scale(1.5);
box-shadow: 0 0 0 10px rgba(255, 255, 255, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
}
}
.inventory-slot.pulse {
animation: pulse-slot 0.6s ease-out;
}
.inventory-item {
max-width: 48px;
max-height: 48px;

View File

@@ -77,6 +77,7 @@
margin: 0;
overflow: auto; /* Allow scrolling if content is too long */
box-sizing: border-box;
height: 90%; /* so that it scolls before the bottom of the image */
}
/* Text box container */

View File

@@ -180,11 +180,7 @@
background: rgba(0, 255, 0, 0.1);
}
.icon-small {
width: 20px;
height: 20px;
object-fit: contain;
}
.icon-keyboard {
width: 40px;
@@ -226,6 +222,13 @@
transform: scale(0.95);
}
.hint-controls {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 10px;
}
.password-hint-container {
display: flex;
flex-direction: column;
@@ -236,7 +239,7 @@
background: #f39c12;
color: white;
border: none;
padding: 8px 16px;
padding: 12px 24px;
/* border-radius: 5px; */
cursor: pointer;
font-size: 18px;

View File

@@ -43,6 +43,15 @@ export class ContainerMinigame extends MinigameScene {
`;
}
// Add notebook button to minigame controls if postit note exists
if (this.controlsElement && this.containerItem.scenarioData.postitNote && this.containerItem.scenarioData.showPostit) {
const notebookBtn = document.createElement('button');
notebookBtn.className = 'minigame-button';
notebookBtn.id = 'minigame-notebook-postit';
notebookBtn.innerHTML = '<img src="assets/icons/notes-sm.png" alt="Notebook" class="icon-small"> Add to Notebook';
this.controlsElement.appendChild(notebookBtn);
}
// Create the container minigame UI
this.createContainerUI();
}
@@ -235,6 +244,12 @@ export class ContainerMinigame extends MinigameScene {
if (closeBtn) {
this.addEventListener(closeBtn, 'click', () => this.complete(false));
}
// Add to Notebook button
const addToNotebookBtn = document.getElementById('minigame-notebook-postit');
if (addToNotebookBtn) {
this.addEventListener(addToNotebookBtn, 'click', () => this.addPostitToNotebook());
}
}
isInteractiveItem(item) {
@@ -396,6 +411,80 @@ export class ContainerMinigame extends MinigameScene {
window.gameAlert('Crypto workstation not available', 'error', 'Error', 3000);
}
}
addPostitToNotebook() {
console.log('Adding postit note to notebook:', this.containerItem.scenarioData.postitNote);
const postitNote = this.containerItem.scenarioData.postitNote;
if (!postitNote || postitNote.trim() === '') {
this.showMessage('No postit note to add.', 'error');
return;
}
// Create comprehensive notebook content
const notebookTitle = `Postit Note - ${this.containerItem.scenarioData.name}`;
let notebookContent = `Postit Note:\n${'-'.repeat(50)}\n\n${postitNote}`;
// Add container contents list
notebookContent += `\n\n${'='.repeat(20)}\n`;
notebookContent += `CONTAINER CONTENTS: ${this.containerItem.scenarioData.name}\n`;
notebookContent += `${'='.repeat(20)}\n`;
if (this.contents && this.contents.length > 0) {
this.contents.forEach((item, index) => {
notebookContent += `${index + 1}. ${item.name || item.type}`;
if (item.description) {
notebookContent += ` - ${item.description}`;
}
notebookContent += '\n';
});
} else {
notebookContent += 'No items found\n';
}
notebookContent += `${'='.repeat(20)}\n`;
notebookContent += `Date: ${new Date().toLocaleString()}`;
const notebookObservations = `Postit note found in ${this.containerItem.scenarioData.name}.`;
// Check if notes minigame is available
if (window.startNotesMinigame) {
// Store the container state globally so we can return to it
const containerState = {
containerItem: this.containerItem,
contents: this.contents,
isTakeable: this.isTakeable
};
window.pendingContainerReturn = containerState;
// Create a postit item for the notes minigame
const postitItem = {
scenarioData: {
type: 'postit_note',
name: notebookTitle,
text: notebookContent,
observations: notebookObservations,
important: true
}
};
// Start notes minigame
window.startNotesMinigame(
postitItem,
notebookContent,
notebookObservations,
null,
false,
false
);
this.showMessage("Added postit note to notebook", 'success');
} else {
console.error('Notes minigame not available');
this.showMessage('Notebook not available', 'error');
}
}
takeItem(item, itemElement) {
console.log('Taking item from container:', item);

View File

@@ -799,10 +799,6 @@ window.addNote = function(title, text, important = false) {
window.gameState.notes.push(note);
// Show notification for new note
if (window.showNotification) {
window.showNotification(`New note added: ${title}`, 'info', 'Note Added', 3000);
}
return note;
};

View File

@@ -36,6 +36,15 @@ export class PasswordMinigame extends MinigameScene {
// Set up the password interface
this.setupPasswordInterface();
// Add notebook button to minigame controls if postit note exists (before cancel button)
if (this.controlsElement && this.gameData.showPostit && this.gameData.postitNote) {
const notebookBtn = document.createElement('button');
notebookBtn.className = 'minigame-button';
notebookBtn.id = 'minigame-notebook-postit';
notebookBtn.innerHTML = '<img src="assets/icons/notes-sm.png" alt="Notebook" class="icon-small"> Add to Notebook';
this.controlsElement.appendChild(notebookBtn);
}
// Set up event listeners
this.setupEventListeners();
}
@@ -61,8 +70,11 @@ export class PasswordMinigame extends MinigameScene {
</div>
${this.gameData.showHint ? `
<div class="password-hint-container">
<div class="hint-controls">
<button type="button" class="hint-btn" id="show-hint">Show Hint</button>
<button type="button" class="submit-btn" id="submit-password">Submit</button>
</div>
<div class="password-hint-container">
<div class="password-hint" id="password-hint" style="display: none;">
<strong>Hint:</strong> ${this.gameData.passwordHint}
</div>
@@ -78,12 +90,9 @@ export class PasswordMinigame extends MinigameScene {
` : ''}
<div class="password-controls">
${this.gameData.showHint ? `
<button type="button" class="submit-btn" id="submit-password">Submit</button>
` : ''}
${this.gameData.showKeyboard ? `
<button type="button" class="keyboard-toggle-btn" id="keyboard-toggle">
<img class="icon-keyboard" src="assets/objects/keyboard3.png" alt="Toggle keyboard">
<img class="icon-keyboard" src="assets/objects/keyboard1.png" alt="Toggle keyboard">
</button>
` : ''}
</div>
@@ -211,6 +220,14 @@ export class PasswordMinigame extends MinigameScene {
this.handleKeyboardClick(event);
});
}
// Notebook button for postit (in minigame controls)
const notebookBtn = document.getElementById('minigame-notebook-postit');
if (notebookBtn) {
this.addEventListener(notebookBtn, 'click', () => {
this.addPostitToNotebook();
});
}
}
start() {
@@ -385,6 +402,78 @@ export class PasswordMinigame extends MinigameScene {
};
}
addPostitToNotebook() {
if (!this.gameState.isActive) return;
const postitNote = this.gameData.postitNote;
if (!postitNote || postitNote.trim() === '') {
this.showFailure("No postit note to add.", false, 2000);
return;
}
// Get the device name from available sources
const deviceName = this.params.deviceName ||
this.params.scenarioData?.name ||
this.params.title ||
'Unknown Device';
// Create comprehensive notebook content
const notebookTitle = `Postit Note - ${deviceName}`;
let notebookContent = `Postit Note:\n${'-'.repeat(20)}\n\n${postitNote}`;
notebookContent += `\n\n${'='.repeat(20)}\n`;
notebookContent += `PASSWORD PROTECTED: ${deviceName}\n`;
notebookContent += `${'='.repeat(20)}\n`;
notebookContent += `Date: ${new Date().toLocaleString()}`;
const notebookObservations = 'Postit note found during password entry.';
// Check if notes minigame is available
if (window.startNotesMinigame) {
// Store the password state globally so we can return to it
const passwordState = {
password: this.gameData.password,
passwordHint: this.gameData.passwordHint,
showHint: this.gameData.showHint,
showKeyboard: this.gameData.showKeyboard,
maxAttempts: this.gameData.maxAttempts,
attempts: this.gameData.attempts,
showPassword: this.gameData.showPassword,
postitNote: this.gameData.postitNote,
showPostit: this.gameData.showPostit,
capsLock: this.gameData.capsLock,
keyboardVisible: this.gameData.keyboardVisible,
params: this.params
};
window.pendingPasswordReturn = passwordState;
// Create a postit item for the notes minigame
const postitItem = {
scenarioData: {
type: 'postit_note',
name: notebookTitle,
text: notebookContent,
observations: notebookObservations,
important: true
}
};
// Start notes minigame
window.startNotesMinigame(
postitItem,
notebookContent,
notebookObservations,
null,
false,
false
);
this.showSuccess("Added postit note to notebook", false, 2000);
} else {
this.showFailure("Notebook not available", false, 2000);
}
}
cleanup() {
// Call parent cleanup (handles event listeners)
super.cleanup();

View File

@@ -776,7 +776,7 @@ export class PhoneMessagesMinigame extends MinigameScene {
content += `Source: ${this.params?.title || 'Phone'}\n`;
content += `Total Messages: ${this.phoneData.messages.length}\n`;
content += `Date: ${new Date().toLocaleString()}\n\n`;
content += `${'='.repeat(50)}\n\n`;
content += `${'='.repeat(20)}\n\n`;
this.phoneData.messages.forEach((message, index) => {
content += `Message ${index + 1}:\n`;
@@ -796,7 +796,7 @@ export class PhoneMessagesMinigame extends MinigameScene {
}
});
content += `${'='.repeat(50)}\n`;
content += `${'='.repeat(20)}\n`;
content += `End of Phone Messages Log`;
return content;

View File

@@ -340,11 +340,11 @@ export class TextFileMinigame extends MinigameScene {
content += `Source: ${this.textFileData.source}\n`;
content += `Type: ${this.textFileData.fileType.toUpperCase()}\n`;
content += `Date: ${new Date().toLocaleString()}\n\n`;
content += `${'='.repeat(50)}\n\n`;
content += `${'='.repeat(20)}\n\n`;
content += `FILE CONTENTS:\n`;
content += `${'-'.repeat(20)}\n\n`;
content += this.textFileData.fileContent;
content += `\n\n${'='.repeat(50)}\n`;
content += `\n\n${'='.repeat(20)}\n`;
content += `End of File: ${this.textFileData.fileName}`;
return content;

View File

@@ -375,7 +375,7 @@ export function handleObjectInteraction(sprite) {
}
// Show notification
window.gameAlert(message, 'info', data.name, 0);
window.gameAlert(message, 'info', data.name, 5000);
}
// Handle container item interactions

View File

@@ -170,10 +170,12 @@ export function addToInventory(sprite) {
// Add to inventory array
window.inventory.items.push(itemImg);
// Show notification
if (window.gameAlert) {
window.gameAlert(`Added ${sprite.scenarioData.name} to inventory`, 'success', 'Item Collected', 3000);
}
// Apply pulse animation to the slot instead of showing notification
slot.classList.add('pulse');
// Remove the pulse class after the animation completes
setTimeout(() => {
slot.classList.remove('pulse');
}, 600);
// If this is the Bluetooth scanner, automatically open the minigame after adding to inventory
if (sprite.scenarioData.type === "bluetooth_scanner" && window.startBluetoothScannerMinigame) {
@@ -234,9 +236,13 @@ function addKeyToInventory(sprite) {
// Update or create the key ring display
updateKeyRingDisplay();
// Show notification
if (window.gameAlert) {
window.gameAlert(`Added ${sprite.scenarioData.name} to key ring`, 'success', 'Key Collected', 3000);
// Apply pulse animation to the key ring slot instead of showing notification
const keyRingSlot = window.inventory.keyRing.slot;
if (keyRingSlot) {
keyRingSlot.classList.add('pulse');
setTimeout(() => {
keyRingSlot.classList.remove('pulse');
}, 600);
}
return true;