Add Password Minigame: Introduce a new minigame for password entry, featuring customizable hints, keyboard input, and attempt tracking. Implement CSS styles for the minigame interface and integrate it into the existing framework. Update index.html to include the new CSS file and register the minigame in the minigame manager. Add test page for functionality and ensure compatibility with existing game mechanics.

This commit is contained in:
Z. Cliffe Schreuders
2025-10-14 01:52:15 +01:00
parent 6c06aeafe7
commit e3ed198059
16 changed files with 2689 additions and 31 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -8,6 +8,141 @@
gap: 20px;
}
/* Desktop Mode Styles */
.container-minigame.desktop-mode {
padding: 0;
gap: 0;
background: #000;
}
.desktop-background {
flex: 1;
position: relative;
background: #000;
overflow: hidden;
}
.desktop-wallpaper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url('../assets/mini-games/desktop-wallpaper.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
opacity: 0.8;
}
.desktop-wallpaper::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.2);
}
.desktop-icons {
position: relative;
z-index: 2;
width: 100%;
height: 100%;
padding: 20px;
}
.desktop-icon {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
width: 80px;
cursor: pointer;
transition: all 0.2s ease;
}
.desktop-icon:hover {
transform: scale(1.1);
}
.desktop-icon-image {
width: 48px;
height: 48px;
object-fit: contain;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 4px;
border: 2px solid transparent;
}
.desktop-icon:hover .desktop-icon-image {
border-color: #00ff00;
background: rgba(0, 255, 0, 0.1);
}
.desktop-icon-label {
font-family: 'Press Start 2P', monospace;
font-size: 8px;
color: white;
text-align: center;
margin-top: 4px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
word-wrap: break-word;
max-width: 80px;
}
.desktop-taskbar {
background: rgba(0, 0, 0, 0.8);
border-top: 2px solid #333;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
min-height: 50px;
}
.desktop-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.desktop-title {
font-family: 'Press Start 2P', monospace;
font-size: 10px;
color: #00ff00;
}
.desktop-subtitle {
font-family: 'VT323', monospace;
font-size: 14px;
color: #ccc;
}
.desktop-actions {
display: flex;
gap: 10px;
}
.empty-desktop {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: 'Press Start 2P', monospace;
font-size: 12px;
color: #666;
text-align: center;
}
.container-image-section {
display: flex;
align-items: center;

View File

@@ -190,3 +190,385 @@
transition: width 0.3s ease;
border-radius: 5px;
}
/* Password Minigame Specific Styles */
.password-minigame-area {
display: flex;
flex-direction: column;
height: 100%;
padding: 20px;
gap: 15px;
background: #1a1a1a;
position: relative;
}
.password-input-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.monitor-bezel {
background: #2a2a2a;
border: 8px solid #1a1a1a;
border-radius: 15px;
padding: 20px;
box-shadow:
inset 0 0 20px rgba(0, 0, 0, 0.5),
0 0 30px rgba(0, 0, 0, 0.8);
position: relative;
background-image: url('../assets/mini-games/desktop-wallpaper.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
}
.monitor-bezel::before {
content: '';
position: absolute;
top: -4px;
left: -4px;
right: -4px;
bottom: -4px;
background: linear-gradient(45deg, #444, #666, #444);
border-radius: 19px;
z-index: -1;
}
.monitor-bezel::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
border-radius: 7px;
z-index: 1;
}
.monitor-screen {
border: 2px solid #333;
border-radius: 8px;
padding: 15px;
min-height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
z-index: 2;
}
.monitor-screen::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(0, 255, 0, 0.1), rgba(0, 255, 255, 0.1));
border-radius: 6px;
z-index: 1;
}
.monitor-screen > * {
position: relative;
z-index: 2;
}
.password-input-container label {
font-size: 12px;
color: #00ff00;
margin-bottom: 5px;
}
.password-field-wrapper {
position: relative;
display: flex;
align-items: center;
}
.password-field {
width: 100%;
padding: 12px 45px 12px 12px;
background: #1a1a1a;
border: 2px solid #00ff00;
border-radius: 5px;
color: white;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
outline: none;
transition: border-color 0.3s ease;
}
.password-field:focus {
border-color: #00ffff;
box-shadow: 0 0 10px rgba(0, 255, 255, 0.3);
}
.password-field::placeholder {
color: #666;
}
.toggle-password-btn {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #00ff00;
cursor: pointer;
font-size: 16px;
padding: 5px;
border-radius: 3px;
transition: background-color 0.3s ease;
}
.toggle-password-btn:hover {
background: rgba(0, 255, 0, 0.1);
}
.password-hint-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.hint-btn {
background: #f39c12;
color: white;
border: none;
padding: 8px 16px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 8px;
transition: background 0.3s ease;
align-self: flex-start;
}
.hint-btn:hover {
background: #e67e22;
}
.password-hint {
background: rgba(243, 156, 18, 0.1);
border: 1px solid #f39c12;
border-radius: 5px;
padding: 10px;
font-size: 10px;
color: #f39c12;
}
.postit-note {
background: #ffff88;
border: 1px solid #ddd;
border-radius: 3px;
padding: 15px;
margin: 10px 0;
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
position: relative;
transform: rotate(-2deg);
font-family: 'Press Start 2P', monospace;
font-size: 8px;
color: #333;
max-width: 200px;
word-wrap: break-word;
}
.postit-note::before {
content: '';
position: absolute;
top: -1px;
right: -1px;
width: 0;
height: 0;
border-left: 15px solid transparent;
border-top: 15px solid #f0f0f0;
}
.postit-note::after {
content: '';
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
background: #ff6b6b;
border-radius: 50%;
box-shadow: 0 0 0 1px #fff, 0 0 0 2px #ff6b6b;
}
.onscreen-keyboard {
display: flex;
flex-direction: column;
gap: 5px;
background: #2a2a2a;
border: 2px solid #444;
border-radius: 8px;
padding: 10px;
margin: 10px 0;
}
.keyboard-row {
display: flex;
justify-content: center;
gap: 3px;
flex-wrap: wrap;
}
.key {
background: #444;
color: white;
border: 1px solid #666;
border-radius: 4px;
padding: 8px 12px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 8px;
min-width: 35px;
text-align: center;
transition: all 0.2s ease;
user-select: none;
}
.key:hover {
background: #555;
border-color: #00ff00;
}
.key:active {
background: #00ff00;
color: black;
transform: scale(0.95);
}
.key-backspace {
background: #e74c3c;
min-width: 60px;
}
.key-backspace:hover {
background: #c0392b;
}
.key-space {
background: #3498db;
min-width: 100px;
}
.key-space:hover {
background: #2980b9;
}
.key-special {
background: #9b59b6;
min-width: 80px;
}
.key-special:hover {
background: #8e44ad;
}
.password-actions {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 10px;
}
.submit-btn {
background: #2ecc71;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
transition: background 0.3s ease;
}
.submit-btn:hover {
background: #27ae60;
}
.submit-btn:active {
background: #229954;
}
.cancel-btn {
background: #e74c3c;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
transition: background 0.3s ease;
}
.cancel-btn:hover {
background: #c0392b;
}
.cancel-btn:active {
background: #a93226;
}
.attempts-counter {
text-align: center;
font-size: 10px;
color: #f39c12;
background: rgba(243, 156, 18, 0.1);
border: 1px solid #f39c12;
border-radius: 5px;
padding: 8px;
margin-top: 10px;
}
.attempts-counter span {
color: #e74c3c;
font-weight: bold;
}
/* Responsive design for smaller screens */
@media (max-width: 768px) {
.onscreen-keyboard {
padding: 5px;
}
.key {
padding: 6px 8px;
font-size: 7px;
min-width: 30px;
}
.key-backspace {
min-width: 50px;
}
.key-space {
min-width: 80px;
}
.key-special {
min-width: 60px;
}
.password-field {
font-size: 9px;
padding: 10px 40px 10px 10px;
}
.submit-btn, .cancel-btn {
padding: 10px 20px;
font-size: 9px;
}
}

View File

@@ -41,6 +41,7 @@
<link rel="stylesheet" href="css/container-minigame.css">
<link rel="stylesheet" href="css/phone.css">
<link rel="stylesheet" href="css/pin.css">
<link rel="stylesheet" href="css/minigames.css">
<!-- External JavaScript libraries -->
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>

View File

@@ -8,6 +8,27 @@ export class ContainerMinigame extends MinigameScene {
this.containerItem = params.containerItem;
this.contents = params.contents || [];
this.isTakeable = params.isTakeable || false;
// Auto-detect desktop mode for PC/tablet containers
this.desktopMode = params.desktopMode || this.shouldUseDesktopMode();
}
shouldUseDesktopMode() {
// Check if the container is a PC, tablet, or computer-related device
const containerName = this.containerItem?.scenarioData?.name?.toLowerCase() || '';
const containerType = this.containerItem?.scenarioData?.type?.toLowerCase() || '';
const containerImage = this.containerItem?.name?.toLowerCase() || '';
// Keywords that indicate desktop/computer devices
const desktopKeywords = [
'computer', 'pc', 'laptop', 'desktop', 'terminal', 'workstation',
'tablet', 'ipad', 'surface', 'monitor', 'screen', 'display',
'server', 'mainframe', 'console', 'kiosk', 'smartboard'
];
// Check if any keyword matches
const allText = `${containerName} ${containerType} ${containerImage}`.toLowerCase();
return desktopKeywords.some(keyword => allText.includes(keyword));
}
init() {
@@ -27,6 +48,20 @@ export class ContainerMinigame extends MinigameScene {
}
createContainerUI() {
if (this.desktopMode) {
this.createDesktopUI();
} else {
this.createStandardUI();
}
// Populate contents
this.populateContents();
// Set up event listeners
this.setupEventListeners();
}
createStandardUI() {
this.gameContainer.innerHTML = `
<div class="container-minigame">
<div class="container-image-section">
@@ -52,15 +87,41 @@ export class ContainerMinigame extends MinigameScene {
</div>
</div>
`;
// Populate contents
this.populateContents();
// Set up event listeners
this.setupEventListeners();
}
createDesktopUI() {
this.gameContainer.innerHTML = `
<div class="container-minigame desktop-mode">
<div class="desktop-background">
<div class="desktop-wallpaper"></div>
<div class="desktop-icons" id="desktop-icons">
<!-- Desktop icons will be populated here -->
</div>
</div>
<div class="desktop-taskbar">
<div class="desktop-info">
<span class="desktop-title">${this.containerItem.scenarioData.name}</span>
<span class="desktop-subtitle">${this.containerItem.scenarioData.observations || ''}</span>
</div>
<div class="desktop-actions">
${this.isTakeable ? '<button class="minigame-button" id="take-container-btn">Take Container</button>' : ''}
<button class="minigame-button" id="close-container-btn">Close</button>
</div>
</div>
</div>
`;
}
populateContents() {
if (this.desktopMode) {
this.populateDesktopIcons();
} else {
this.populateStandardContents();
}
}
populateStandardContents() {
const contentsGrid = document.getElementById('container-contents-grid');
if (!contentsGrid) return;
@@ -107,6 +168,57 @@ export class ContainerMinigame extends MinigameScene {
});
}
populateDesktopIcons() {
const desktopIcons = document.getElementById('desktop-icons');
if (!desktopIcons) return;
if (this.contents.length === 0) {
desktopIcons.innerHTML = '<div class="empty-desktop">Desktop is empty</div>';
return;
}
this.contents.forEach((item, index) => {
const icon = document.createElement('div');
icon.className = 'desktop-icon';
const iconImg = document.createElement('img');
iconImg.className = 'desktop-icon-image';
iconImg.src = `assets/objects/${item.type}.png`;
iconImg.alt = item.name;
const iconLabel = document.createElement('div');
iconLabel.className = 'desktop-icon-label';
iconLabel.textContent = item.name;
// Add item data
iconImg.scenarioData = item;
iconImg.name = item.type;
iconImg.objectId = `desktop_${index}`;
// Add click handler for taking items
if (item.takeable) {
icon.style.cursor = 'pointer';
// Special handling for notes - trigger notes minigame instead of taking
if (item.type === 'notes' && item.readable && item.text) {
icon.addEventListener('click', () => this.handleNotesItem(item, iconImg));
} else {
icon.addEventListener('click', () => this.takeItem(item, iconImg));
}
}
// Position icon randomly on desktop
const x = Math.random() * 70 + 10; // 10% to 80% of width
const y = Math.random() * 60 + 10; // 10% to 70% of height
icon.style.left = `${x}%`;
icon.style.top = `${y}%`;
icon.appendChild(iconImg);
icon.appendChild(iconLabel);
desktopIcons.appendChild(icon);
});
}
setupEventListeners() {
// Take container button
const takeContainerBtn = document.getElementById('take-container-btn');
@@ -254,8 +366,13 @@ export class ContainerMinigame extends MinigameScene {
}
// Function to start the container minigame
export function startContainerMinigame(containerItem, contents, isTakeable = false) {
console.log('Starting container minigame', { containerItem, contents, isTakeable });
export function startContainerMinigame(containerItem, contents, isTakeable = false, desktopMode = null) {
// Auto-detect desktop mode if not explicitly set
if (desktopMode === null) {
desktopMode = shouldUseDesktopModeForContainer(containerItem);
}
console.log('Starting container minigame', { containerItem, contents, isTakeable, desktopMode });
// Initialize the minigame framework if not already done
if (!window.MinigameFramework) {
@@ -273,6 +390,7 @@ export function startContainerMinigame(containerItem, contents, isTakeable = fal
containerItem: containerItem,
contents: contents,
isTakeable: isTakeable,
desktopMode: desktopMode,
cancelText: 'Close',
showCancel: true,
onComplete: (success, result) => {
@@ -281,6 +399,25 @@ export function startContainerMinigame(containerItem, contents, isTakeable = fal
});
}
// Helper function to determine if a container should use desktop mode
function shouldUseDesktopModeForContainer(containerItem) {
// Check if the container is a PC, tablet, or computer-related device
const containerName = containerItem?.scenarioData?.name?.toLowerCase() || '';
const containerType = containerItem?.scenarioData?.type?.toLowerCase() || '';
const containerImage = containerItem?.name?.toLowerCase() || '';
// Keywords that indicate desktop/computer devices
const desktopKeywords = [
'computer', 'pc', 'laptop', 'desktop', 'terminal', 'workstation',
'tablet', 'ipad', 'surface', 'monitor', 'screen', 'display',
'server', 'mainframe', 'console', 'kiosk', 'smartboard'
];
// Check if any keyword matches
const allText = `${containerName} ${containerType} ${containerImage}`.toLowerCase();
return desktopKeywords.some(keyword => allText.includes(keyword));
}
// Function to return to container after notes minigame
export function returnToContainerAfterNotes() {
console.log('Returning to container after notes minigame');

View File

@@ -12,6 +12,7 @@ export { LockpickSetMinigame, startLockpickSetMinigame } from './lockpick/lockpi
export { ContainerMinigame, startContainerMinigame, returnToContainerAfterNotes } from './container/container-minigame.js';
export { PhoneMessagesMinigame, returnToPhoneAfterNotes } from './phone/phone-messages-minigame.js';
export { PinMinigame, startPinMinigame } from './pin/pin-minigame.js';
export { PasswordMinigame } from './password/password-minigame.js';
// Initialize the global minigame framework for backward compatibility
import { MinigameFramework } from './framework/minigame-manager.js';
@@ -61,6 +62,9 @@ import { PhoneMessagesMinigame, returnToPhoneAfterNotes } from './phone/phone-me
// Import the PIN minigame
import { PinMinigame, startPinMinigame } from './pin/pin-minigame.js';
// Import the password minigame
import { PasswordMinigame } from './password/password-minigame.js';
// Register minigames
MinigameFramework.registerScene('lockpicking', LockpickingMinigamePhaser); // Use Phaser version as default
MinigameFramework.registerScene('lockpicking-phaser', LockpickingMinigamePhaser); // Keep explicit phaser name
@@ -72,6 +76,7 @@ MinigameFramework.registerScene('lockpick-set', LockpickSetMinigame);
MinigameFramework.registerScene('container', ContainerMinigame);
MinigameFramework.registerScene('phone-messages', PhoneMessagesMinigame);
MinigameFramework.registerScene('pin', PinMinigame);
MinigameFramework.registerScene('password', PasswordMinigame);
// Make minigame functions available globally
window.startNotesMinigame = startNotesMinigame;

View File

@@ -0,0 +1,357 @@
import { MinigameScene } from '../framework/base-minigame.js';
export class PasswordMinigame extends MinigameScene {
constructor(container, params) {
super(container, params);
// Initialize password-specific state
this.gameData = {
password: params.password || '',
passwordHint: params.passwordHint || '',
showHint: params.showHint || false,
showKeyboard: params.showKeyboard || false,
maxAttempts: params.maxAttempts || 3,
attempts: 0,
showPassword: false,
postitNote: params.postitNote || '',
showPostit: params.showPostit || false
};
// Store the correct password for validation
this.correctPassword = params.password || '';
}
init() {
// Call parent init to set up basic UI structure
super.init();
// Customize the header
this.headerElement.innerHTML = `
<h3>${this.params.title || 'Password Entry'}</h3>
<p>Enter the correct password to proceed</p>
`;
// Set up the password interface
this.setupPasswordInterface();
// Set up event listeners
this.setupEventListeners();
}
setupPasswordInterface() {
// Create the password entry interface
this.gameContainer.innerHTML = `
<div class="password-minigame-area">
${this.gameData.showPostit && this.gameData.postitNote ? `
<div class="postit-note">
${this.gameData.postitNote}
</div>
` : ''}
<div class="monitor-bezel">
<div class="monitor-screen">
<div class="password-input-container">
<label for="password-field">Password:</label>
<div class="password-field-wrapper">
<input type="${this.gameData.showPassword ? 'text' : 'password'}"
id="password-field"
class="password-field"
placeholder="Enter password..."
maxlength="50">
<button type="button" class="toggle-password-btn" id="toggle-password">
${this.gameData.showPassword ? '👁️' : '👁️‍🗨️'}
</button>
</div>
</div>
${this.gameData.showHint ? `
<div class="password-hint-container">
<button type="button" class="hint-btn" id="show-hint">Show Hint</button>
<div class="password-hint" id="password-hint" style="display: none;">
<strong>Hint:</strong> ${this.gameData.passwordHint}
</div>
</div>
` : ''}
</div>
</div>
${this.gameData.showKeyboard ? `
<div class="onscreen-keyboard" id="onscreen-keyboard">
<div class="keyboard-row">
<button class="key" data-key="1">1</button>
<button class="key" data-key="2">2</button>
<button class="key" data-key="3">3</button>
<button class="key" data-key="4">4</button>
<button class="key" data-key="5">5</button>
<button class="key" data-key="6">6</button>
<button class="key" data-key="7">7</button>
<button class="key" data-key="8">8</button>
<button class="key" data-key="9">9</button>
<button class="key" data-key="0">0</button>
<button class="key key-backspace" data-key="Backspace">⌫</button>
</div>
<div class="keyboard-row">
<button class="key" data-key="q">Q</button>
<button class="key" data-key="w">W</button>
<button class="key" data-key="e">E</button>
<button class="key" data-key="r">R</button>
<button class="key" data-key="t">T</button>
<button class="key" data-key="y">Y</button>
<button class="key" data-key="u">U</button>
<button class="key" data-key="i">I</button>
<button class="key" data-key="o">O</button>
<button class="key" data-key="p">P</button>
</div>
<div class="keyboard-row">
<button class="key" data-key="a">A</button>
<button class="key" data-key="s">S</button>
<button class="key" data-key="d">D</button>
<button class="key" data-key="f">F</button>
<button class="key" data-key="g">G</button>
<button class="key" data-key="h">H</button>
<button class="key" data-key="j">J</button>
<button class="key" data-key="k">K</button>
<button class="key" data-key="l">L</button>
</div>
<div class="keyboard-row">
<button class="key" data-key="z">Z</button>
<button class="key" data-key="x">X</button>
<button class="key" data-key="c">C</button>
<button class="key" data-key="v">V</button>
<button class="key" data-key="b">B</button>
<button class="key" data-key="n">N</button>
<button class="key" data-key="m">M</button>
<button class="key key-space" data-key=" ">Space</button>
</div>
<div class="keyboard-row">
<button class="key key-special" data-key="Enter">Enter</button>
<button class="key key-special" data-key="Escape">Cancel</button>
</div>
</div>
` : ''}
<div class="password-actions">
<button type="button" class="submit-btn" id="submit-password">Submit</button>
<button type="button" class="cancel-btn" id="cancel-password">Cancel</button>
</div>
<div class="attempts-counter">
Attempts: <span id="attempts-display">${this.gameData.attempts}</span>/${this.gameData.maxAttempts}
</div>
</div>
`;
// Get references to important elements
this.passwordField = document.getElementById('password-field');
this.togglePasswordBtn = document.getElementById('toggle-password');
this.submitBtn = document.getElementById('submit-password');
this.cancelBtn = document.getElementById('cancel-password');
this.attemptsDisplay = document.getElementById('attempts-display');
// Focus the password field
if (this.passwordField) {
this.passwordField.focus();
}
}
setupEventListeners() {
// Password field events
if (this.passwordField) {
this.addEventListener(this.passwordField, 'keydown', (event) => {
this.handleKeyPress(event);
});
this.addEventListener(this.passwordField, 'input', (event) => {
this.handlePasswordInput(event);
});
}
// Toggle password visibility
if (this.togglePasswordBtn) {
this.addEventListener(this.togglePasswordBtn, 'click', () => {
this.togglePasswordVisibility();
});
}
// Submit button
if (this.submitBtn) {
this.addEventListener(this.submitBtn, 'click', () => {
this.submitPassword();
});
}
// Cancel button
if (this.cancelBtn) {
this.addEventListener(this.cancelBtn, 'click', () => {
this.cancelPassword();
});
}
// Hint button
const hintBtn = document.getElementById('show-hint');
if (hintBtn) {
this.addEventListener(hintBtn, 'click', () => {
this.toggleHint();
});
}
// Onscreen keyboard
const keyboard = document.getElementById('onscreen-keyboard');
if (keyboard) {
this.addEventListener(keyboard, 'click', (event) => {
this.handleKeyboardClick(event);
});
}
}
start() {
// Call parent start
super.start();
console.log("Password minigame started");
}
handleKeyPress(event) {
if (!this.gameState.isActive) return;
switch(event.key) {
case 'Enter':
event.preventDefault();
this.submitPassword();
break;
case 'Escape':
event.preventDefault();
this.cancelPassword();
break;
}
}
handlePasswordInput(event) {
// Update the internal password state
this.gameData.password = event.target.value;
}
togglePasswordVisibility() {
this.gameData.showPassword = !this.gameData.showPassword;
// Update input type
this.passwordField.type = this.gameData.showPassword ? 'text' : 'password';
// Update button icon
this.togglePasswordBtn.textContent = this.gameData.showPassword ? '👁️' : '👁️‍🗨️';
}
toggleHint() {
const hintElement = document.getElementById('password-hint');
const hintBtn = document.getElementById('show-hint');
if (hintElement && hintBtn) {
if (hintElement.style.display === 'none') {
hintElement.style.display = 'block';
hintBtn.textContent = 'Hide Hint';
} else {
hintElement.style.display = 'none';
hintBtn.textContent = 'Show Hint';
}
}
}
handleKeyboardClick(event) {
if (!this.gameState.isActive) return;
const key = event.target;
if (!key.classList.contains('key')) return;
const keyValue = key.dataset.key;
if (keyValue === 'Enter') {
this.submitPassword();
} else if (keyValue === 'Escape') {
this.cancelPassword();
} else if (keyValue === 'Backspace') {
this.passwordField.value = this.passwordField.value.slice(0, -1);
this.gameData.password = this.passwordField.value;
} else if (keyValue === ' ') {
this.passwordField.value += ' ';
this.gameData.password = this.passwordField.value;
} else if (keyValue && keyValue.length === 1) {
this.passwordField.value += keyValue;
this.gameData.password = this.passwordField.value;
}
// Keep focus on password field
this.passwordField.focus();
}
submitPassword() {
if (!this.gameState.isActive) return;
const enteredPassword = this.passwordField.value.trim();
if (!enteredPassword) {
this.showFailure("Please enter a password", false, 2000);
return;
}
this.gameData.attempts++;
this.attemptsDisplay.textContent = this.gameData.attempts;
if (enteredPassword === this.correctPassword) {
this.passwordCorrect();
} else {
this.passwordIncorrect();
}
}
passwordCorrect() {
this.cleanup();
this.showSuccess("Password accepted! Access granted.", true, 3000);
// Set game result for the callback
this.gameResult = {
success: true,
password: this.gameData.password,
attempts: this.gameData.attempts
};
}
passwordIncorrect() {
if (this.gameData.attempts >= this.gameData.maxAttempts) {
this.passwordFailed();
} else {
this.showFailure(`Incorrect password. ${this.gameData.maxAttempts - this.gameData.attempts} attempts remaining.`, false, 3000);
// Clear the password field
this.passwordField.value = '';
this.gameData.password = '';
this.passwordField.focus();
}
}
passwordFailed() {
this.cleanup();
this.showFailure("Maximum attempts exceeded. Access denied.", true, 3000);
this.gameResult = {
success: false,
reason: 'max_attempts_exceeded',
attempts: this.gameData.attempts
};
}
cancelPassword() {
this.cleanup();
this.showFailure("Password entry cancelled.", true, 2000);
this.gameResult = {
success: false,
reason: 'cancelled',
attempts: this.gameData.attempts
};
}
cleanup() {
// Call parent cleanup (handles event listeners)
super.cleanup();
}
}

View File

@@ -264,8 +264,58 @@ export function startPinMinigame(lockable, type, correctPin, callback) {
});
}
export function startPasswordMinigame(lockable, type, correctPassword, callback, options = {}) {
console.log('Starting password minigame for', type, 'with password:', correctPassword);
// Initialize the minigame framework if not already done
if (!window.MinigameFramework) {
console.error('MinigameFramework not available');
// Fallback to simple prompt
const passwordInput = prompt(`Enter password:`);
if (passwordInput === correctPassword) {
console.log('PASSWORD SUCCESS (fallback)');
window.gameAlert(`Correct password! The ${type} is now unlocked.`, 'success', 'Password Accepted', 4000);
callback(true);
} else if (passwordInput !== null) {
console.log('PASSWORD FAIL (fallback)');
window.gameAlert("Incorrect password.", 'error', 'Password Rejected', 3000);
callback(false);
}
return;
}
// Use the advanced minigame framework
if (!window.MinigameFramework.mainGameScene) {
window.MinigameFramework.init(window.game);
}
// Start the password minigame
window.MinigameFramework.startMinigame('password', null, {
title: `Enter password for ${type}`,
password: correctPassword,
passwordHint: options.passwordHint || '',
showHint: options.showHint || false,
showKeyboard: options.showKeyboard || false,
maxAttempts: options.maxAttempts || 3,
postitNote: options.postitNote || '',
showPostit: options.showPostit || false,
onComplete: (success, result) => {
if (success) {
console.log('PASSWORD MINIGAME SUCCESS');
window.gameAlert(`Correct password! The ${type} is now unlocked.`, 'success', 'Password Accepted', 4000);
callback(true);
} else {
console.log('PASSWORD MINIGAME FAILED');
window.gameAlert("Failed to enter correct password.", 'error', 'Password Rejected', 3000);
callback(false);
}
}
});
}
// Export for global access
window.startLockpickingMinigame = startLockpickingMinigame;
window.startKeySelectionMinigame = startKeySelectionMinigame;
window.startPinMinigame = startPinMinigame;
window.startPasswordMinigame = startPasswordMinigame;

View File

@@ -10,7 +10,7 @@
import { DOOR_ALIGN_OVERLAP } from '../utils/constants.js';
import { rooms } from '../core/rooms.js';
import { unlockDoor } from './doors.js';
import { startLockpickingMinigame, startKeySelectionMinigame, startPinMinigame } from './minigame-starters.js';
import { startLockpickingMinigame, startKeySelectionMinigame, startPinMinigame, startPasswordMinigame } from './minigame-starters.js';
// Helper function to check if two rectangles overlap
function boundsOverlap(rect1, rect2) {
@@ -106,29 +106,22 @@ export function handleUnlock(lockable, type) {
case 'password':
console.log('PASSWORD REQUESTED');
if (window.showPasswordModal) {
window.showPasswordModal(function(passwordInput) {
if (passwordInput === lockRequirements.requires) {
unlockTarget(lockable, type, lockable.layer);
console.log('PASSWORD SUCCESS');
window.gameAlert(`Correct password! The ${type} is now unlocked.`, 'success', 'Password Accepted', 4000);
} else if (passwordInput !== null) {
console.log('PASSWORD FAIL');
window.gameAlert("Incorrect password.", 'error', 'Password Rejected', 3000);
}
});
} else {
// Fallback to prompt
const passwordInput = prompt(`Enter password:`);
if (passwordInput === lockRequirements.requires) {
// Get password options from the lockable object
const passwordOptions = {
passwordHint: lockable.passwordHint || lockable.scenarioData?.passwordHint || '',
showHint: lockable.showHint || lockable.scenarioData?.showHint || false,
showKeyboard: lockable.showKeyboard || lockable.scenarioData?.showKeyboard || false,
maxAttempts: lockable.maxAttempts || lockable.scenarioData?.maxAttempts || 3,
postitNote: lockable.postitNote || lockable.scenarioData?.postitNote || '',
showPostit: lockable.showPostit || lockable.scenarioData?.showPostit || false
};
startPasswordMinigame(lockable, type, lockRequirements.requires, (success) => {
if (success) {
unlockTarget(lockable, type, lockable.layer);
console.log('PASSWORD SUCCESS');
window.gameAlert(`Correct password! The ${type} is now unlocked.`, 'success', 'Password Accepted', 4000);
} else if (passwordInput !== null) {
console.log('PASSWORD FAIL');
window.gameAlert("Incorrect password.", 'error', 'Password Rejected', 3000);
}
}
}, passwordOptions);
break;
case 'biometric':

View File

@@ -0,0 +1,157 @@
{
"name": "Password Minigame Example",
"description": "Example scenario showing how to use the password minigame",
"rooms": {
"office": {
"id": "office",
"name": "Office",
"image": "room_office.png",
"map": "room_office.json",
"position": { "x": 0, "y": 0 },
"connections": []
}
},
"objects": [
{
"id": "secure_door",
"name": "Secure Door",
"type": "door",
"room": "office",
"x": 400,
"y": 300,
"image": "door.png",
"lockType": "password",
"requires": "admin123",
"passwordHint": "The default administrator password",
"showHint": true,
"showKeyboard": false,
"maxAttempts": 3,
"locked": true,
"observations": "A secure door with a password keypad."
},
{
"id": "computer_terminal",
"name": "Computer Terminal",
"type": "computer",
"room": "office",
"x": 200,
"y": 200,
"image": "computer.png",
"lockType": "password",
"requires": "cyber2024",
"passwordHint": "Current year with cyber prefix",
"showHint": true,
"showKeyboard": true,
"maxAttempts": 5,
"postitNote": "Password: cyber2024",
"showPostit": true,
"locked": true,
"observations": "A computer terminal requiring password access."
},
{
"id": "safe_box",
"name": "Safe Box",
"type": "container",
"room": "office",
"x": 600,
"y": 400,
"image": "safe.png",
"lockType": "password",
"requires": "treasure",
"showHint": false,
"showKeyboard": true,
"maxAttempts": 2,
"locked": true,
"contents": [
{
"id": "secret_document",
"name": "Secret Document",
"type": "document",
"image": "document.png",
"takeable": true,
"observations": "A classified document containing sensitive information."
}
],
"observations": "A heavy safe box with a digital keypad."
},
{
"id": "office_computer",
"name": "Office Computer",
"type": "container",
"room": "office",
"x": 300,
"y": 300,
"image": "computer.png",
"lockType": "password",
"requires": "desktop123",
"showHint": false,
"showKeyboard": false,
"maxAttempts": 3,
"locked": true,
"contents": [
{
"id": "project_files",
"name": "Project Files",
"type": "document",
"takeable": true,
"observations": "Important project documentation."
},
{
"id": "meeting_notes",
"name": "Meeting Notes",
"type": "notes",
"takeable": true,
"readable": true,
"text": "Meeting notes about the upcoming project deadline..."
},
{
"id": "encryption_key",
"name": "Encryption Key",
"type": "key",
"takeable": true,
"observations": "A digital encryption key file."
}
],
"observations": "An office computer with desktop access. Desktop mode will be automatically enabled."
},
{
"id": "tablet_device",
"name": "Executive Tablet",
"type": "container",
"room": "office",
"x": 500,
"y": 200,
"image": "tablet.png",
"lockType": "password",
"requires": "tablet2024",
"showHint": false,
"showKeyboard": true,
"maxAttempts": 3,
"locked": true,
"contents": [
{
"id": "executive_notes",
"name": "Executive Notes",
"type": "notes",
"takeable": true,
"readable": true,
"text": "Confidential executive meeting notes..."
},
{
"id": "financial_data",
"name": "Financial Data",
"type": "document",
"takeable": true,
"observations": "Sensitive financial information."
}
],
"observations": "An executive tablet device. Desktop mode will be automatically enabled."
}
],
"victoryConditions": [
{
"type": "collect_item",
"itemId": "secret_document"
}
]
}

View File

@@ -30,8 +30,31 @@
"type": "pc",
"name": "Reception Computer",
"takeable": false,
"lockType": "password",
"passwordHint": "Optional hint text",
"showHint": true,
"showKeyboard": true,
"maxAttempts": 3,
"locked": true,
"requires": "password",
"observations": "The reception's computer, currently locked"
"observations": "The reception's computer, currently locked",
"contents": [
{
"type": "notes",
"name": "Private Note",
"takeable": true,
"readable": true,
"text": "Closet keypad code: 7391 - Must move evidence to safe before audit",
"observations": "A hastily written note on expensive paper"
},
{
"type": "key",
"name": "Safe Key",
"takeable": true,
"key_id": "safe_key:52,29,44,37",
"observations": "A heavy-duty safe key hidden behind server equipment"
}
]
},
{
"type": "tablet",

383
test-auto-desktop.html Normal file
View File

@@ -0,0 +1,383 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Auto Desktop Mode Test</title>
<!-- Include the game's CSS -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/minigames.css">
<link rel="stylesheet" href="css/container-minigame.css">
<link rel="stylesheet" href="css/notifications.css">
<style>
body {
background: #1a1a1a;
color: white;
font-family: 'Press Start 2P', monospace;
margin: 0;
padding: 20px;
}
.test-container {
max-width: 1000px;
margin: 0 auto;
}
.test-section {
margin: 20px 0;
padding: 20px;
border: 2px solid #444;
border-radius: 10px;
background: #2a2a2a;
}
.test-button {
background: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
margin: 5px;
transition: background 0.3s ease;
}
.test-button:hover {
background: #2980b9;
}
.test-button.success {
background: #2ecc71;
}
.test-button.success:hover {
background: #27ae60;
}
.test-description {
font-size: 8px;
color: #ccc;
margin-bottom: 10px;
line-height: 1.4;
}
.test-results {
margin-top: 20px;
padding: 15px;
background: #1a1a1a;
border-radius: 5px;
border: 1px solid #444;
}
.test-results h3 {
color: #00ff00;
margin-top: 0;
}
.test-results pre {
background: #0a0a0a;
padding: 10px;
border-radius: 3px;
overflow-x: auto;
font-size: 8px;
}
.detection-info {
background: rgba(0, 255, 0, 0.1);
border: 1px solid #00ff00;
border-radius: 5px;
padding: 10px;
margin: 10px 0;
font-size: 8px;
}
</style>
</head>
<body>
<div class="test-container">
<h1>Auto Desktop Mode Detection Test</h1>
<p>Test automatic desktop mode detection for PC, tablet, and computer containers.</p>
<div class="detection-info">
<strong>Auto-Detection Keywords:</strong> computer, pc, laptop, desktop, terminal, workstation, tablet, ipad, surface, monitor, screen, display, server, mainframe, console, kiosk, smartboard
</div>
<!-- Computer Container Test -->
<div class="test-section">
<h2>Computer Container (Auto Desktop)</h2>
<div class="test-description">
Test container with "computer" in the name - should automatically enable desktop mode.
</div>
<button class="test-button success" onclick="testComputerContainer()">Test Computer Container</button>
</div>
<!-- Tablet Container Test -->
<div class="test-section">
<h2>Tablet Container (Auto Desktop)</h2>
<div class="test-description">
Test container with "tablet" in the name - should automatically enable desktop mode.
</div>
<button class="test-button success" onclick="testTabletContainer()">Test Tablet Container</button>
</div>
<!-- PC Container Test -->
<div class="test-section">
<h2>PC Container (Auto Desktop)</h2>
<div class="test-description">
Test container with "pc" in the name - should automatically enable desktop mode.
</div>
<button class="test-button success" onclick="testPCContainer()">Test PC Container</button>
</div>
<!-- Regular Container Test -->
<div class="test-section">
<h2>Regular Container (No Auto Desktop)</h2>
<div class="test-description">
Test container with "safe" in the name - should NOT automatically enable desktop mode.
</div>
<button class="test-button" onclick="testRegularContainer()">Test Regular Container</button>
</div>
<!-- Manual Override Test -->
<div class="test-section">
<h2>Manual Override Test</h2>
<div class="test-description">
Test container with "safe" but manually force desktop mode - should enable desktop mode.
</div>
<button class="test-button" onclick="testManualOverride()">Test Manual Override</button>
</div>
<!-- Test Results -->
<div class="test-results">
<h3>Test Results</h3>
<pre id="test-results">Click a test button to see results here...</pre>
</div>
</div>
<!-- Include Phaser first -->
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>
<!-- Include the minigame framework -->
<script type="module">
// Import the minigame framework and container minigame
import { MinigameFramework } from './js/minigames/framework/minigame-manager.js';
import { ContainerMinigame } from './js/minigames/container/container-minigame.js';
// Initialize the framework
window.MinigameFramework = MinigameFramework;
MinigameFramework.registerScene('container', ContainerMinigame);
// Mock game object
window.game = {
sound: {
play: function(soundName) {
console.log('Playing sound:', soundName);
}
}
};
// Mock game alert function
window.gameAlert = function(message, type, title, duration) {
console.log(`[${type.toUpperCase()}] ${title}: ${message}`);
updateTestResults(`[${type.toUpperCase()}] ${title}: ${message}`);
};
// Mock inventory functions
window.addToInventory = function(item) {
console.log('Added to inventory:', item);
updateTestResults(`Added to inventory: ${item.name}`);
};
window.removeFromInventory = function(item) {
console.log('Removed from inventory:', item);
updateTestResults(`Removed from inventory: ${item.name}`);
};
// Test functions
function testComputerContainer() {
updateTestResults("Testing computer container (should auto-enable desktop mode)...");
const mockContainer = {
scenarioData: {
name: "Office Computer",
type: "container",
observations: "A computer that should auto-enable desktop mode"
},
name: "computer"
};
const contents = [
{
id: "file1",
name: "Document",
type: "document",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Computer Container Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: null, // Let it auto-detect
onComplete: (success, result) => {
updateTestResults(`Computer container test completed. Success: ${success}, Desktop mode should be enabled automatically.`);
}
});
}
function testTabletContainer() {
updateTestResults("Testing tablet container (should auto-enable desktop mode)...");
const mockContainer = {
scenarioData: {
name: "Executive Tablet",
type: "container",
observations: "A tablet that should auto-enable desktop mode"
},
name: "tablet"
};
const contents = [
{
id: "note1",
name: "Notes",
type: "notes",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Tablet Container Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: null, // Let it auto-detect
onComplete: (success, result) => {
updateTestResults(`Tablet container test completed. Success: ${success}, Desktop mode should be enabled automatically.`);
}
});
}
function testPCContainer() {
updateTestResults("Testing PC container (should auto-enable desktop mode)...");
const mockContainer = {
scenarioData: {
name: "Gaming PC",
type: "container",
observations: "A PC that should auto-enable desktop mode"
},
name: "pc"
};
const contents = [
{
id: "game1",
name: "Game Files",
type: "document",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'PC Container Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: null, // Let it auto-detect
onComplete: (success, result) => {
updateTestResults(`PC container test completed. Success: ${success}, Desktop mode should be enabled automatically.`);
}
});
}
function testRegularContainer() {
updateTestResults("Testing regular container (should NOT auto-enable desktop mode)...");
const mockContainer = {
scenarioData: {
name: "Safe Box",
type: "container",
observations: "A safe that should NOT auto-enable desktop mode"
},
name: "safe"
};
const contents = [
{
id: "item1",
name: "Valuable Item",
type: "key",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Regular Container Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: null, // Let it auto-detect
onComplete: (success, result) => {
updateTestResults(`Regular container test completed. Success: ${success}, Desktop mode should NOT be enabled automatically.`);
}
});
}
function testManualOverride() {
updateTestResults("Testing manual override (force desktop mode on safe)...");
const mockContainer = {
scenarioData: {
name: "Safe Box",
type: "container",
observations: "A safe with manually forced desktop mode"
},
name: "safe"
};
const contents = [
{
id: "item1",
name: "Valuable Item",
type: "key",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Manual Override Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: true, // Manually force desktop mode
onComplete: (success, result) => {
updateTestResults(`Manual override test completed. Success: ${success}, Desktop mode should be enabled manually.`);
}
});
}
function updateTestResults(message) {
const resultsElement = document.getElementById('test-results');
const timestamp = new Date().toLocaleTimeString();
resultsElement.textContent += `[${timestamp}] ${message}\n`;
resultsElement.scrollTop = resultsElement.scrollHeight;
}
// Make functions globally available
window.testComputerContainer = testComputerContainer;
window.testTabletContainer = testTabletContainer;
window.testPCContainer = testPCContainer;
window.testRegularContainer = testRegularContainer;
window.testManualOverride = testManualOverride;
// Initialize test results
updateTestResults("Auto desktop mode detection test suite loaded and ready.");
</script>
</body>
</html>

348
test-container-desktop.html Normal file
View File

@@ -0,0 +1,348 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Container Desktop Mode Test</title>
<!-- Include the game's CSS -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/minigames.css">
<link rel="stylesheet" href="css/container-minigame.css">
<link rel="stylesheet" href="css/notifications.css">
<style>
body {
background: #1a1a1a;
color: white;
font-family: 'Press Start 2P', monospace;
margin: 0;
padding: 20px;
}
.test-container {
max-width: 800px;
margin: 0 auto;
}
.test-section {
margin: 20px 0;
padding: 20px;
border: 2px solid #444;
border-radius: 10px;
background: #2a2a2a;
}
.test-button {
background: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
margin: 5px;
transition: background 0.3s ease;
}
.test-button:hover {
background: #2980b9;
}
.test-button.success {
background: #2ecc71;
}
.test-button.success:hover {
background: #27ae60;
}
.test-description {
font-size: 8px;
color: #ccc;
margin-bottom: 10px;
line-height: 1.4;
}
.test-results {
margin-top: 20px;
padding: 15px;
background: #1a1a1a;
border-radius: 5px;
border: 1px solid #444;
}
.test-results h3 {
color: #00ff00;
margin-top: 0;
}
.test-results pre {
background: #0a0a0a;
padding: 10px;
border-radius: 3px;
overflow-x: auto;
font-size: 8px;
}
</style>
</head>
<body>
<div class="test-container">
<h1>Container Desktop Mode Test Suite</h1>
<p>Test the container minigame in desktop mode with desktop wallpaper and icons.</p>
<!-- Standard Container Test -->
<div class="test-section">
<h2>Standard Container Test</h2>
<div class="test-description">
Test the standard container minigame (non-desktop mode) with various items.
</div>
<button class="test-button" onclick="testStandardContainer()">Test Standard Container</button>
</div>
<!-- Desktop Container Test -->
<div class="test-section">
<h2>Desktop Container Test</h2>
<div class="test-description">
Test the container minigame in desktop mode with desktop wallpaper and scattered icons.
</div>
<button class="test-button success" onclick="testDesktopContainer()">Test Desktop Container</button>
</div>
<!-- Desktop with Notes Test -->
<div class="test-section">
<h2>Desktop with Notes Test</h2>
<div class="test-description">
Test desktop mode with notes that should trigger the notes minigame when clicked.
</div>
<button class="test-button" onclick="testDesktopWithNotes()">Test Desktop with Notes</button>
</div>
<!-- Empty Desktop Test -->
<div class="test-section">
<h2>Empty Desktop Test</h2>
<div class="test-description">
Test desktop mode with no items to see the empty desktop message.
</div>
<button class="test-button" onclick="testEmptyDesktop()">Test Empty Desktop</button>
</div>
<!-- Test Results -->
<div class="test-results">
<h3>Test Results</h3>
<pre id="test-results">Click a test button to see results here...</pre>
</div>
</div>
<!-- Include Phaser first -->
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>
<!-- Include the minigame framework -->
<script type="module">
// Import the minigame framework and container minigame
import { MinigameFramework } from './js/minigames/framework/minigame-manager.js';
import { ContainerMinigame } from './js/minigames/container/container-minigame.js';
// Initialize the framework
window.MinigameFramework = MinigameFramework;
MinigameFramework.registerScene('container', ContainerMinigame);
// Mock game object
window.game = {
sound: {
play: function(soundName) {
console.log('Playing sound:', soundName);
}
}
};
// Mock game alert function
window.gameAlert = function(message, type, title, duration) {
console.log(`[${type.toUpperCase()}] ${title}: ${message}`);
updateTestResults(`[${type.toUpperCase()}] ${title}: ${message}`);
};
// Mock inventory functions
window.addToInventory = function(item) {
console.log('Added to inventory:', item);
updateTestResults(`Added to inventory: ${item.name}`);
};
window.removeFromInventory = function(item) {
console.log('Removed from inventory:', item);
updateTestResults(`Removed from inventory: ${item.name}`);
};
// Test functions - define them globally
function testStandardContainer() {
updateTestResults("Starting standard container test...");
const mockContainer = {
scenarioData: {
name: "Test Container",
observations: "A standard container for testing"
},
name: "container"
};
const contents = [
{
id: "item1",
name: "Test Item 1",
type: "key",
takeable: true
},
{
id: "item2",
name: "Test Item 2",
type: "document",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Standard Container Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: false,
onComplete: (success, result) => {
updateTestResults(`Standard container test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
function testDesktopContainer() {
updateTestResults("Starting desktop container test...");
const mockContainer = {
scenarioData: {
name: "Computer Desktop",
observations: "A computer desktop with scattered files and applications"
},
name: "computer"
};
const contents = [
{
id: "file1",
name: "Secret Document",
type: "document",
takeable: true
},
{
id: "file2",
name: "Encryption Key",
type: "key",
takeable: true
},
{
id: "file3",
name: "Password List",
type: "notes",
takeable: true
},
{
id: "file4",
name: "Backup Files",
type: "folder",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Desktop Container Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: true,
onComplete: (success, result) => {
updateTestResults(`Desktop container test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
function testDesktopWithNotes() {
updateTestResults("Starting desktop with notes test...");
const mockContainer = {
scenarioData: {
name: "Office Computer",
observations: "An office computer with important documents"
},
name: "computer"
};
const contents = [
{
id: "notes1",
name: "Meeting Notes",
type: "notes",
takeable: true,
readable: true,
text: "Important meeting notes about the project..."
},
{
id: "file1",
name: "Project Files",
type: "document",
takeable: true
}
];
MinigameFramework.startMinigame('container', null, {
title: 'Desktop with Notes Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: true,
onComplete: (success, result) => {
updateTestResults(`Desktop with notes test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
function testEmptyDesktop() {
updateTestResults("Starting empty desktop test...");
const mockContainer = {
scenarioData: {
name: "Empty Desktop",
observations: "A clean desktop with no files"
},
name: "computer"
};
const contents = [];
MinigameFramework.startMinigame('container', null, {
title: 'Empty Desktop Test',
containerItem: mockContainer,
contents: contents,
isTakeable: false,
desktopMode: true,
onComplete: (success, result) => {
updateTestResults(`Empty desktop test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
function updateTestResults(message) {
const resultsElement = document.getElementById('test-results');
const timestamp = new Date().toLocaleTimeString();
resultsElement.textContent += `[${timestamp}] ${message}\n`;
resultsElement.scrollTop = resultsElement.scrollHeight;
}
// Make functions globally available
window.testStandardContainer = testStandardContainer;
window.testDesktopContainer = testDesktopContainer;
window.testDesktopWithNotes = testDesktopWithNotes;
window.testEmptyDesktop = testEmptyDesktop;
// Initialize test results
updateTestResults("Container desktop mode test suite loaded and ready.");
</script>
</body>
</html>

189
test-container-simple.html Normal file
View File

@@ -0,0 +1,189 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Container Desktop Test</title>
<!-- Include the game's CSS -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/minigames.css">
<link rel="stylesheet" href="css/container-minigame.css">
<link rel="stylesheet" href="css/notifications.css">
<style>
body {
background: #1a1a1a;
color: white;
font-family: 'Press Start 2P', monospace;
margin: 0;
padding: 20px;
}
.test-container {
max-width: 800px;
margin: 0 auto;
}
.test-section {
margin: 20px 0;
padding: 20px;
border: 2px solid #444;
border-radius: 10px;
background: #2a2a2a;
}
.test-button {
background: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
margin: 5px;
transition: background 0.3s ease;
}
.test-button:hover {
background: #2980b9;
}
.test-button.success {
background: #2ecc71;
}
.test-button.success:hover {
background: #27ae60;
}
.test-description {
font-size: 8px;
color: #ccc;
margin-bottom: 10px;
line-height: 1.4;
}
.demo-area {
margin: 20px 0;
padding: 20px;
border: 2px solid #00ff00;
border-radius: 10px;
background: #1a1a1a;
min-height: 400px;
}
</style>
</head>
<body>
<div class="test-container">
<h1>Simple Container Desktop Mode Test</h1>
<p>Test the container desktop mode CSS and layout without the full framework.</p>
<!-- Standard Container Demo -->
<div class="test-section">
<h2>Standard Container Layout</h2>
<div class="test-description">
Standard container minigame layout (non-desktop mode).
</div>
<div class="demo-area">
<div class="container-minigame">
<div class="container-image-section">
<img src="assets/objects/container.png" alt="Container" class="container-image">
<div class="container-info">
<h4>Test Container</h4>
<p>A standard container for testing</p>
</div>
</div>
<div class="container-contents-section">
<h4>Contents</h4>
<div class="container-contents-grid">
<div class="container-content-slot">
<img src="assets/objects/key.png" alt="Key" class="container-content-item">
<div class="container-content-tooltip">Test Key</div>
</div>
<div class="container-content-slot">
<img src="assets/objects/document.png" alt="Document" class="container-content-item">
<div class="container-content-tooltip">Test Document</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Desktop Container Demo -->
<div class="test-section">
<h2>Desktop Container Layout</h2>
<div class="test-description">
Container minigame in desktop mode with desktop wallpaper and scattered icons.
</div>
<div class="demo-area">
<div class="container-minigame desktop-mode">
<div class="desktop-background">
<div class="desktop-wallpaper"></div>
<div class="desktop-icons">
<div class="desktop-icon" style="left: 20%; top: 15%;">
<img src="assets/objects/document.png" alt="Document" class="desktop-icon-image">
<div class="desktop-icon-label">Secret Files</div>
</div>
<div class="desktop-icon" style="left: 60%; top: 25%;">
<img src="assets/objects/key.png" alt="Key" class="desktop-icon-image">
<div class="desktop-icon-label">Encryption Key</div>
</div>
<div class="desktop-icon" style="left: 40%; top: 45%;">
<img src="assets/objects/notes.png" alt="Notes" class="desktop-icon-image">
<div class="desktop-icon-label">Meeting Notes</div>
</div>
<div class="desktop-icon" style="left: 70%; top: 50%;">
<img src="assets/objects/folder.png" alt="Folder" class="desktop-icon-image">
<div class="desktop-icon-label">Backup Files</div>
</div>
</div>
</div>
<div class="desktop-taskbar">
<div class="desktop-info">
<span class="desktop-title">Office Computer</span>
<span class="desktop-subtitle">Desktop with scattered files</span>
</div>
<div class="desktop-actions">
<button class="minigame-button">Take Container</button>
<button class="minigame-button">Close</button>
</div>
</div>
</div>
</div>
</div>
<!-- Empty Desktop Demo -->
<div class="test-section">
<h2>Empty Desktop Layout</h2>
<div class="test-description">
Desktop mode with no items to show the empty desktop message.
</div>
<div class="demo-area">
<div class="container-minigame desktop-mode">
<div class="desktop-background">
<div class="desktop-wallpaper"></div>
<div class="desktop-icons">
<div class="empty-desktop">Desktop is empty</div>
</div>
</div>
<div class="desktop-taskbar">
<div class="desktop-info">
<span class="desktop-title">Empty Desktop</span>
<span class="desktop-subtitle">No files found</span>
</div>
<div class="desktop-actions">
<button class="minigame-button">Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

290
test-password-minigame.html Normal file
View File

@@ -0,0 +1,290 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Password Minigame Test</title>
<!-- Include the game's CSS -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/minigames.css">
<link rel="stylesheet" href="css/notifications.css">
<style>
body {
background: #1a1a1a;
color: white;
font-family: 'Press Start 2P', monospace;
margin: 0;
padding: 20px;
}
.test-container {
max-width: 800px;
margin: 0 auto;
}
.test-section {
margin: 20px 0;
padding: 20px;
border: 2px solid #444;
border-radius: 10px;
background: #2a2a2a;
}
.test-button {
background: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
margin: 5px;
transition: background 0.3s ease;
}
.test-button:hover {
background: #2980b9;
}
.test-button.success {
background: #2ecc71;
}
.test-button.success:hover {
background: #27ae60;
}
.test-button.warning {
background: #f39c12;
}
.test-button.warning:hover {
background: #e67e22;
}
.test-button.danger {
background: #e74c3c;
}
.test-button.danger:hover {
background: #c0392b;
}
.test-description {
font-size: 8px;
color: #ccc;
margin-bottom: 10px;
line-height: 1.4;
}
.test-results {
margin-top: 20px;
padding: 15px;
background: #1a1a1a;
border-radius: 5px;
border: 1px solid #444;
}
.test-results h3 {
color: #00ff00;
margin-top: 0;
}
.test-results pre {
background: #0a0a0a;
padding: 10px;
border-radius: 3px;
overflow-x: auto;
font-size: 8px;
}
</style>
</head>
<body>
<div class="test-container">
<h1>Password Minigame Test Suite</h1>
<p>Test the new password minigame with various configurations and options.</p>
<!-- Basic Password Test -->
<div class="test-section">
<h2>Basic Password Test</h2>
<div class="test-description">
Test basic password entry with show/hide functionality. Password: "secret123"
</div>
<button class="test-button success" onclick="testBasicPassword()">Test Basic Password</button>
</div>
<!-- Password with Hint -->
<div class="test-section">
<h2>Password with Hint (Desktop Background)</h2>
<div class="test-description">
Test password entry with hint button inside monitor bezel with desktop wallpaper. Password: "admin"<br>
Hint: "The default administrator password"
</div>
<button class="test-button warning" onclick="testPasswordWithHint()">Test Password with Hint</button>
</div>
<!-- Password with Onscreen Keyboard -->
<div class="test-section">
<h2>Password with Onscreen Keyboard</h2>
<div class="test-description">
Test password entry with onscreen QWERTY keyboard. Password: "keyboard"
</div>
<button class="test-button" onclick="testPasswordWithKeyboard()">Test Password with Keyboard</button>
</div>
<!-- Full Featured Password -->
<div class="test-section">
<h2>Full Featured Password</h2>
<div class="test-description">
Test password with all features: hint, keyboard, and custom attempts. Password: "fulltest"<br>
Hint: "A complete test of all features"
</div>
<button class="test-button" onclick="testFullFeaturedPassword()">Test Full Featured Password</button>
</div>
<!-- Password with Post-it Note -->
<div class="test-section">
<h2>Password with Post-it Note</h2>
<div class="test-description">
Test password with post-it note on desktop. Password: "postit"<br>
Post-it: "Password is written on the sticky note!"
</div>
<button class="test-button" onclick="testPasswordWithPostit()">Test Password with Post-it</button>
</div>
<!-- Wrong Password Test -->
<div class="test-section">
<h2>Wrong Password Test</h2>
<div class="test-description">
Test entering wrong passwords to trigger failure scenarios. Try: "wrong", "incorrect", "fail"
</div>
<button class="test-button danger" onclick="testWrongPassword()">Test Wrong Password</button>
</div>
<!-- Test Results -->
<div class="test-results">
<h3>Test Results</h3>
<pre id="test-results">Click a test button to see results here...</pre>
</div>
</div>
<!-- Include the minigame framework -->
<script type="module">
// Import the minigame framework and password minigame
import { MinigameFramework } from './js/minigames/framework/minigame-manager.js';
import { PasswordMinigame } from './js/minigames/password/password-minigame.js';
// Initialize the framework
window.MinigameFramework = MinigameFramework;
MinigameFramework.registerScene('password', PasswordMinigame);
// Mock game alert function
window.gameAlert = function(message, type, title, duration) {
console.log(`[${type.toUpperCase()}] ${title}: ${message}`);
updateTestResults(`[${type.toUpperCase()}] ${title}: ${message}`);
};
// Test functions
window.testBasicPassword = function() {
updateTestResults("Starting basic password test...");
MinigameFramework.startMinigame('password', null, {
title: 'Basic Password Test',
password: 'secret123',
showHint: false,
showKeyboard: false,
maxAttempts: 3,
onComplete: (success, result) => {
updateTestResults(`Basic password test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
window.testPasswordWithHint = function() {
updateTestResults("Starting password with hint test...");
MinigameFramework.startMinigame('password', null, {
title: 'Password with Hint Test',
password: 'admin',
passwordHint: 'The default administrator password',
showHint: true,
showKeyboard: false,
maxAttempts: 3,
onComplete: (success, result) => {
updateTestResults(`Password with hint test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
window.testPasswordWithKeyboard = function() {
updateTestResults("Starting password with keyboard test...");
MinigameFramework.startMinigame('password', null, {
title: 'Password with Keyboard Test',
password: 'keyboard',
showHint: false,
showKeyboard: true,
maxAttempts: 3,
onComplete: (success, result) => {
updateTestResults(`Password with keyboard test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
window.testFullFeaturedPassword = function() {
updateTestResults("Starting full featured password test...");
MinigameFramework.startMinigame('password', null, {
title: 'Full Featured Password Test',
password: 'fulltest',
passwordHint: 'A complete test of all features',
showHint: true,
showKeyboard: true,
maxAttempts: 5,
onComplete: (success, result) => {
updateTestResults(`Full featured password test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
window.testWrongPassword = function() {
updateTestResults("Starting wrong password test...");
MinigameFramework.startMinigame('password', null, {
title: 'Wrong Password Test',
password: 'correct',
showHint: false,
showKeyboard: false,
maxAttempts: 3,
onComplete: (success, result) => {
updateTestResults(`Wrong password test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
window.testPasswordWithPostit = function() {
updateTestResults("Starting password with post-it test...");
MinigameFramework.startMinigame('password', null, {
title: 'Password with Post-it Test',
password: 'postit',
showHint: false,
showKeyboard: false,
maxAttempts: 3,
postitNote: 'Password is written on the sticky note!',
showPostit: true,
onComplete: (success, result) => {
updateTestResults(`Password with post-it test completed. Success: ${success}, Result: ${JSON.stringify(result)}`);
}
});
};
function updateTestResults(message) {
const resultsElement = document.getElementById('test-results');
const timestamp = new Date().toLocaleTimeString();
resultsElement.textContent += `[${timestamp}] ${message}\n`;
resultsElement.scrollTop = resultsElement.scrollHeight;
}
// Initialize test results
updateTestResults("Password minigame test suite loaded and ready.");
</script>
</body>
</html>

208
verify-css.html Normal file
View File

@@ -0,0 +1,208 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Verification</title>
<!-- Include the game's CSS -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/minigames.css">
<link rel="stylesheet" href="css/notifications.css">
<style>
body {
background: #1a1a1a;
color: white;
font-family: 'Press Start 2P', monospace;
margin: 0;
padding: 20px;
}
.verification-container {
max-width: 600px;
margin: 0 auto;
}
.test-element {
margin: 20px 0;
padding: 20px;
border: 2px solid #444;
border-radius: 10px;
background: #2a2a2a;
}
.status {
display: inline-block;
padding: 5px 10px;
border-radius: 3px;
font-size: 8px;
margin-left: 10px;
}
.status.success {
background: #2ecc71;
color: white;
}
.status.error {
background: #e74c3c;
color: white;
}
</style>
</head>
<body>
<div class="verification-container">
<h1>CSS Verification Test</h1>
<p>This page verifies that the password minigame CSS is properly loaded and applied.</p>
<div class="test-element">
<h2>Password Field with Monitor Bezel Test</h2>
<div class="password-minigame-area">
<div class="monitor-bezel">
<div class="monitor-screen">
<div class="password-input-container">
<label for="test-password">Password:</label>
<div class="password-field-wrapper">
<input type="password" id="test-password" class="password-field" placeholder="Enter password...">
<button type="button" class="toggle-password-btn">👁️‍🗨️</button>
</div>
</div>
<div class="password-hint-container">
<button type="button" class="hint-btn">Show Hint</button>
</div>
</div>
</div>
</div>
<div id="password-status" class="status error">CSS NOT LOADED</div>
</div>
<div class="test-element">
<h2>Hint System Test</h2>
<div class="password-hint-container">
<button type="button" class="hint-btn">Show Hint</button>
<div class="password-hint" style="display: none;">
<strong>Hint:</strong> This is a test hint
</div>
</div>
<div id="hint-status" class="status error">CSS NOT LOADED</div>
</div>
<div class="test-element">
<h2>Onscreen Keyboard Test</h2>
<div class="onscreen-keyboard">
<div class="keyboard-row">
<button class="key" data-key="q">Q</button>
<button class="key" data-key="w">W</button>
<button class="key" data-key="e">E</button>
<button class="key key-backspace" data-key="Backspace"></button>
</div>
</div>
<div id="keyboard-status" class="status error">CSS NOT LOADED</div>
</div>
<div class="test-element">
<h2>Action Buttons Test</h2>
<div class="password-actions">
<button type="button" class="submit-btn">Submit</button>
<button type="button" class="cancel-btn">Cancel</button>
</div>
<div id="buttons-status" class="status error">CSS NOT LOADED</div>
</div>
<div class="test-element">
<h2>Attempts Counter Test</h2>
<div class="attempts-counter">
Attempts: <span>0</span>/3
</div>
<div id="counter-status" class="status error">CSS NOT LOADED</div>
</div>
<div class="test-element">
<h2>Overall Status</h2>
<div id="overall-status" class="status error">VERIFICATION IN PROGRESS</div>
</div>
</div>
<script>
// Check if CSS classes are properly styled
function verifyCSS() {
let allPassed = true;
// Check monitor bezel background
const monitorBezel = document.querySelector('.monitor-bezel');
if (monitorBezel) {
const styles = window.getComputedStyle(monitorBezel);
if (styles.backgroundImage && styles.backgroundImage !== 'none') {
document.getElementById('password-status').textContent = 'CSS LOADED';
document.getElementById('password-status').className = 'status success';
} else {
allPassed = false;
}
}
// Check hint button
const hintBtn = document.querySelector('.hint-btn');
if (hintBtn) {
const styles = window.getComputedStyle(hintBtn);
if (styles.backgroundColor === 'rgb(243, 156, 18)' || styles.backgroundColor === '#f39c12') {
document.getElementById('hint-status').textContent = 'CSS LOADED';
document.getElementById('hint-status').className = 'status success';
} else {
allPassed = false;
}
}
// Check keyboard keys
const key = document.querySelector('.key');
if (key) {
const styles = window.getComputedStyle(key);
if (styles.backgroundColor === 'rgb(68, 68, 68)' || styles.backgroundColor === '#444') {
document.getElementById('keyboard-status').textContent = 'CSS LOADED';
document.getElementById('keyboard-status').className = 'status success';
} else {
allPassed = false;
}
}
// Check submit button
const submitBtn = document.querySelector('.submit-btn');
if (submitBtn) {
const styles = window.getComputedStyle(submitBtn);
if (styles.backgroundColor === 'rgb(46, 204, 113)' || styles.backgroundColor === '#2ecc71') {
document.getElementById('buttons-status').textContent = 'CSS LOADED';
document.getElementById('buttons-status').className = 'status success';
} else {
allPassed = false;
}
}
// Check attempts counter
const counter = document.querySelector('.attempts-counter');
if (counter) {
const styles = window.getComputedStyle(counter);
if (styles.color === 'rgb(243, 156, 18)' || styles.color === '#f39c12') {
document.getElementById('counter-status').textContent = 'CSS LOADED';
document.getElementById('counter-status').className = 'status success';
} else {
allPassed = false;
}
}
// Overall status
if (allPassed) {
document.getElementById('overall-status').textContent = 'ALL CSS LOADED SUCCESSFULLY';
document.getElementById('overall-status').className = 'status success';
} else {
document.getElementById('overall-status').textContent = 'SOME CSS NOT LOADED';
document.getElementById('overall-status').className = 'status error';
}
}
// Run verification after page loads
window.addEventListener('load', () => {
setTimeout(verifyCSS, 100); // Small delay to ensure CSS is loaded
});
</script>
</body>
</html>