mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
669 lines
25 KiB
HTML
669 lines
25 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Locksmith Forge - Lockpicking Challenges</title>
|
|
|
|
<!-- Google Fonts - Press Start 2P, VT323 -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
|
|
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
|
|
|
|
<!-- Web Font Loader script to ensure fonts load properly -->
|
|
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
|
|
<script>
|
|
WebFont.load({
|
|
google: {
|
|
families: ['Press Start 2P', 'VT323']
|
|
},
|
|
active: function() {
|
|
console.log('Fonts loaded successfully');
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
body {
|
|
font-family: 'VT323', monospace;
|
|
background: #333;
|
|
color: #ffffff;
|
|
margin: 0;
|
|
padding: 20px;
|
|
text-align: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.minigame-close-button, #minigame-cancel, .minigame-header {
|
|
display: none;
|
|
}
|
|
|
|
.header {
|
|
background: rgba(0, 0, 0, 0.95);
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
margin-bottom: 20px;
|
|
border: 2px solid #444;
|
|
box-shadow: 0 0 30px rgba(0, 0, 0, 0.8);
|
|
max-width: 800px;
|
|
width: 100%;
|
|
color: #00ff00;
|
|
}
|
|
|
|
.level-display {
|
|
font-family: 'Press Start 2P', monospace;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
margin-bottom: 15px;
|
|
text-shadow: 0 0 10px #00ff00;
|
|
}
|
|
|
|
.stats {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.stat {
|
|
background: #333;
|
|
padding: 8px 15px;
|
|
border-radius: 5px;
|
|
border: 1px solid #00ff00;
|
|
}
|
|
|
|
.game-container {
|
|
background: rgba(0, 0, 0, 0.95);
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
border: 2px solid #444;
|
|
box-shadow: 0 0 30px rgba(0, 0, 0, 0.8);
|
|
margin-bottom: 20px;
|
|
min-height: 400px;
|
|
position: relative;
|
|
max-width: 800px;
|
|
width: 100%;
|
|
}
|
|
|
|
#gameContainer {
|
|
width: 100%;
|
|
background: #1a1a1a;
|
|
/* border: 1px solid #444; */
|
|
border-radius: 5px;
|
|
position: relative;
|
|
}
|
|
|
|
.phaser-game-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
|
|
canvas {
|
|
display: block;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
input:disabled, select:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.control-group label {
|
|
color: #00ff00;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.control-group input:disabled + .range-value {
|
|
color: #00ff00;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.controls {
|
|
background: #333;
|
|
padding: 15px;
|
|
border-radius: 10px;
|
|
margin-bottom: 20px;
|
|
border: 1px solid #00ff00;
|
|
}
|
|
|
|
.control-group {
|
|
margin: 10px 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
label {
|
|
font-weight: bold;
|
|
min-width: 120px;
|
|
text-align: right;
|
|
}
|
|
|
|
input[type="range"] {
|
|
width: 200px;
|
|
}
|
|
|
|
.range-value {
|
|
min-width: 40px;
|
|
text-align: left;
|
|
}
|
|
|
|
button {
|
|
background: #00ff00;
|
|
color: #000;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-family: 'Courier New', monospace;
|
|
font-weight: bold;
|
|
margin: 5px;
|
|
}
|
|
|
|
button:hover {
|
|
background: #00cc00;
|
|
}
|
|
|
|
button:disabled {
|
|
background: #666;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.status {
|
|
background: rgba(0, 0, 0, 0.8);
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
margin: 10px 0;
|
|
border: 1px solid #444;
|
|
min-height: 20px;
|
|
font-family: 'VT323', monospace;
|
|
font-size: 18px;
|
|
max-width: 800px;
|
|
width: 100%;
|
|
}
|
|
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 20px;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
border-radius: 10px;
|
|
border: 1px solid #444;
|
|
overflow: hidden;
|
|
margin: 10px 0;
|
|
max-width: 800px;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #00ff00, #00cc00);
|
|
width: 0%;
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.achievement {
|
|
font-family: 'Press Start 2P', monospace;
|
|
background: #ffaa00;
|
|
color: #000;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
margin: 10px 0;
|
|
font-weight: bold;
|
|
animation: glow 2s ease-in-out infinite alternate;
|
|
}
|
|
|
|
@keyframes glow {
|
|
from { box-shadow: 0 0 5px #ffaa00; }
|
|
to { box-shadow: 0 0 20px #ffaa00, 0 0 30px #ffaa00; }
|
|
}
|
|
|
|
.progress-achievement {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
color: white;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 90%;
|
|
z-index: 10;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<div class="level-display">LEVEL <span id="currentLevel">1</span></div>
|
|
<div class="stats">
|
|
<div class="stat">Pins: <span id="pinCount">3</span></div>
|
|
<div class="stat">Difficulty: <span id="difficulty">Easy</span></div>
|
|
<div class="stat">Sensitivity: <span id="sensitivity">5</span></div>
|
|
<div class="stat">Lift Speed: <span id="liftSpeed">1.0</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="game-container">
|
|
<div id="gameContainer"></div>
|
|
</div>
|
|
|
|
<div class="controls" style="display: none;">
|
|
<!-- Controls hidden - parameters are automatically set by level -->
|
|
<div class="control-group">
|
|
<label>Threshold Sensitivity:</label>
|
|
<input type="range" id="thresholdSensitivity" min="1" max="10" value="5" step="1" disabled>
|
|
<span class="range-value" id="thresholdSensitivityValue">5</span>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>Highlight Binding Order:</label>
|
|
<select id="highlightBindingOrder" disabled>
|
|
<option value="enabled">Enabled</option>
|
|
<option value="disabled">Disabled</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>Pin Alignment Highlighting:</label>
|
|
<select id="pinAlignmentHighlighting" disabled>
|
|
<option value="enabled">Enabled</option>
|
|
<option value="disabled">Disabled</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>Lift Speed:</label>
|
|
<input type="range" id="liftSpeedRange" min="0.5" max="3.0" value="1.0" step="0.1" disabled>
|
|
<span class="range-value" id="liftSpeedValue">1.0</span>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<button id="startButton">Restart Level</button>
|
|
<button id="resetButton">Reset Progress</button>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" id="progressFill"></div>
|
|
<div class="progress-achievement" id="progressAchievement"></div>
|
|
</div>
|
|
|
|
<div id="achievements"></div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>
|
|
<script type="module">
|
|
import { LockpickingMinigamePhaser } from './js/minigames/lockpicking/lockpicking-game-phaser.js';
|
|
|
|
class ProgressiveLockpicking {
|
|
constructor() {
|
|
this.maxLevel = 50;
|
|
this.levelConfig = this.generateLevelConfig();
|
|
|
|
// Load saved progress from localStorage
|
|
this.loadProgress();
|
|
|
|
this.currentGame = null;
|
|
|
|
this.initializeUI();
|
|
this.bindEvents();
|
|
this.updateDisplay();
|
|
}
|
|
|
|
loadProgress() {
|
|
try {
|
|
const savedProgress = localStorage.getItem('lockpickingProgress');
|
|
if (savedProgress) {
|
|
const progress = JSON.parse(savedProgress);
|
|
this.currentLevel = Math.max(1, Math.min(this.maxLevel, progress.currentLevel || 1));
|
|
this.successCount = progress.successCount || 0;
|
|
this.failureCount = progress.failureCount || 0;
|
|
} else {
|
|
this.currentLevel = 1;
|
|
this.successCount = 0;
|
|
this.failureCount = 0;
|
|
}
|
|
} catch (error) {
|
|
console.warn('Failed to load progress from localStorage:', error);
|
|
this.currentLevel = 1;
|
|
this.successCount = 0;
|
|
this.failureCount = 0;
|
|
}
|
|
}
|
|
|
|
saveProgress() {
|
|
try {
|
|
const progress = {
|
|
currentLevel: this.currentLevel,
|
|
successCount: this.successCount,
|
|
failureCount: this.failureCount,
|
|
lastSaved: new Date().toISOString()
|
|
};
|
|
localStorage.setItem('lockpickingProgress', JSON.stringify(progress));
|
|
} catch (error) {
|
|
console.warn('Failed to save progress to localStorage:', error);
|
|
}
|
|
}
|
|
|
|
generateLevelConfig() {
|
|
const config = {};
|
|
|
|
for (let level = 1; level <= this.maxLevel; level++) {
|
|
// Base progression
|
|
let pinCount = Math.min(3 + Math.floor((level - 1) / 5), 8); // 3-8 pins
|
|
let difficulty = this.getDifficulty(level);
|
|
let sensitivity = Math.max(1, Math.min(8, 1 + Math.floor((level - 1) / 5.57))); // 1-8, reaches 8 at level 40
|
|
let liftSpeed = Math.max(0.5, Math.min(3.0, 0.6 + (level - 1) * 0.03)); // 0.5-3.0 (starts much slower, progresses very gradually)
|
|
|
|
// Add some randomness and complexity
|
|
if (level > 10) {
|
|
// Randomly disable hints for higher levels
|
|
const disableHints = Math.random() > 0.7;
|
|
if (disableHints) {
|
|
sensitivity = Math.max(1, sensitivity - 2);
|
|
}
|
|
}
|
|
|
|
if (level > 20) {
|
|
// Increase difficulty more aggressively
|
|
liftSpeed = Math.min(3.0, liftSpeed + 0.1);
|
|
}
|
|
|
|
if (level > 30) {
|
|
// Very challenging levels
|
|
pinCount = Math.min(8, pinCount + 1);
|
|
sensitivity = Math.max(1, sensitivity - 1);
|
|
}
|
|
|
|
config[level] = {
|
|
pinCount,
|
|
difficulty,
|
|
sensitivity,
|
|
liftSpeed: Math.round(liftSpeed * 10) / 10,
|
|
highlightBindingOrder: (level % 10 === 0) ? 'disabled' : (level <= 15 ? 'enabled' : (Math.random() > 0.5 ? 'enabled' : 'disabled')),
|
|
pinAlignmentHighlighting: (level % 10 === 0) ? 'disabled' : (level <= 10 ? 'enabled' : (Math.random() > 0.6 ? 'enabled' : 'disabled'))
|
|
};
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
getDifficulty(level) {
|
|
if (level <= 5) return 'easy';
|
|
if (level <= 15) return 'medium';
|
|
if (level <= 30) return 'difficult';
|
|
if (level <= 40) return 'hard';
|
|
return 'expert';
|
|
}
|
|
|
|
initializeUI() {
|
|
// Initialize range inputs
|
|
this.updateRangeValue('thresholdSensitivity');
|
|
this.updateRangeValue('liftSpeedRange');
|
|
|
|
// Set initial values from level config
|
|
this.updateControlsFromLevel();
|
|
}
|
|
|
|
bindEvents() {
|
|
// Range input events
|
|
document.getElementById('thresholdSensitivity').addEventListener('input', (e) => {
|
|
this.updateRangeValue('thresholdSensitivity');
|
|
});
|
|
|
|
document.getElementById('liftSpeedRange').addEventListener('input', (e) => {
|
|
this.updateRangeValue('liftSpeedRange');
|
|
});
|
|
|
|
// Button events
|
|
document.getElementById('startButton').addEventListener('click', () => {
|
|
this.startChallenge();
|
|
});
|
|
|
|
document.getElementById('resetButton').addEventListener('click', () => {
|
|
this.resetLevel();
|
|
});
|
|
|
|
|
|
}
|
|
|
|
updateRangeValue(id) {
|
|
const input = document.getElementById(id);
|
|
const value = input.value;
|
|
const valueDisplay = document.getElementById(id + 'Value');
|
|
if (valueDisplay) {
|
|
valueDisplay.textContent = value;
|
|
}
|
|
}
|
|
|
|
updateControlsFromLevel() {
|
|
const config = this.levelConfig[this.currentLevel];
|
|
if (!config) return;
|
|
|
|
document.getElementById('thresholdSensitivity').value = config.sensitivity;
|
|
document.getElementById('liftSpeedRange').value = config.liftSpeed;
|
|
document.getElementById('highlightBindingOrder').value = config.highlightBindingOrder;
|
|
document.getElementById('pinAlignmentHighlighting').value = config.pinAlignmentHighlighting;
|
|
|
|
this.updateRangeValue('thresholdSensitivity');
|
|
this.updateRangeValue('liftSpeedRange');
|
|
}
|
|
|
|
startChallenge() {
|
|
if (this.currentGame) {
|
|
this.currentGame.cleanup();
|
|
}
|
|
|
|
const config = this.levelConfig[this.currentLevel];
|
|
const params = {
|
|
pinCount: config.pinCount,
|
|
difficulty: config.difficulty,
|
|
thresholdSensitivity: config.sensitivity,
|
|
highlightBindingOrder: config.highlightBindingOrder === 'enabled',
|
|
pinAlignmentHighlighting: config.pinAlignmentHighlighting === 'enabled',
|
|
liftSpeed: config.liftSpeed,
|
|
lockable: { id: 'progressive-challenge' },
|
|
closeButtonText: 'Reset',
|
|
closeButtonAction: 'reset'
|
|
};
|
|
|
|
this.updateStatus(`Starting Level ${this.currentLevel}...`);
|
|
|
|
console.log('Creating minigame with params:', params);
|
|
console.log('Game container:', document.getElementById('gameContainer'));
|
|
|
|
this.currentGame = new LockpickingMinigamePhaser(
|
|
document.getElementById('gameContainer'),
|
|
params
|
|
);
|
|
|
|
console.log('Minigame created:', this.currentGame);
|
|
|
|
// Initialize the minigame
|
|
this.currentGame.init();
|
|
|
|
console.log('Minigame initialized');
|
|
|
|
// Start the minigame
|
|
this.currentGame.start();
|
|
|
|
console.log('Minigame started');
|
|
|
|
// Listen for completion by overriding the complete method
|
|
const originalComplete = this.currentGame.complete.bind(this.currentGame);
|
|
this.currentGame.complete = (success) => {
|
|
console.log('Minigame completed with success:', success);
|
|
this.handleChallengeComplete(success);
|
|
originalComplete(success);
|
|
};
|
|
}
|
|
|
|
handleChallengeComplete(success) {
|
|
if (success) {
|
|
this.successCount++;
|
|
this.saveProgress();
|
|
|
|
// Check if this was the final level
|
|
if (this.currentLevel === this.maxLevel) {
|
|
this.updateStatus(`🎊 INCREDIBLE! You've completed ALL ${this.maxLevel} levels! You are a TRUE MASTER LOCKPICKER! 🎊`);
|
|
this.showAchievement(`👑 LEGENDARY LOCKPICKER - ALL LEVELS COMPLETE! 👑`);
|
|
} else {
|
|
// Check for milestone levels
|
|
const milestoneMessages = {
|
|
1: {
|
|
status: `🎯 Great start! Level 1 complete! You're on your way to becoming a lockpicking master! 🎯`,
|
|
achievement: `🌟 First Steps - Level 1 Complete! 🌟`
|
|
},
|
|
10: {
|
|
status: `🔥 Excellent progress! Level 10 conquered! You're getting the hang of this! 🔥`,
|
|
achievement: `⚡ Rising Star - Level 10 Complete! ⚡`
|
|
},
|
|
20: {
|
|
status: `💪 Impressive! Level 20 mastered! Your skills are really developing! 💪`,
|
|
achievement: `🏅 Skillful Picker - Level 20 Complete! 🏅`
|
|
},
|
|
30: {
|
|
status: `🚀 Outstanding! Level 30 achieved! You're becoming a true expert! 🚀`,
|
|
achievement: `🎖️ Expert Level - Level 30 Complete! 🎖️`
|
|
},
|
|
40: {
|
|
status: `⚔️ Phenomenal! Level 40 conquered! You're almost at the ultimate challenge! ⚔️`,
|
|
achievement: `🏆 Elite Picker - Level 40 Complete! 🏆`
|
|
}
|
|
};
|
|
|
|
const milestone = milestoneMessages[this.currentLevel];
|
|
if (milestone) {
|
|
this.updateStatus(milestone.status);
|
|
this.showAchievement(milestone.achievement);
|
|
} else {
|
|
this.updateStatus(`Level ${this.currentLevel} completed successfully!`);
|
|
this.showAchievement(`Level ${this.currentLevel} Complete!`);
|
|
}
|
|
}
|
|
|
|
// Progress to next level after a delay
|
|
setTimeout(() => {
|
|
const nextLevel = Math.min(this.currentLevel + 1, this.maxLevel);
|
|
this.currentLevel = nextLevel;
|
|
this.saveProgress();
|
|
this.updateDisplay();
|
|
this.updateControlsFromLevel();
|
|
|
|
// Check if player has reached the final level
|
|
if (this.currentLevel === this.maxLevel) {
|
|
this.updateStatus(`🎉 CONGRATULATIONS! You've reached the ultimate challenge - Level ${this.currentLevel}! 🎉`);
|
|
this.showAchievement(`🏆 MASTER LOCKPICKER - Level ${this.currentLevel} Unlocked! 🏆`);
|
|
} else {
|
|
this.updateStatus(`Starting Level ${this.currentLevel}...`);
|
|
}
|
|
|
|
// Auto-start the next level
|
|
setTimeout(() => {
|
|
this.startChallenge();
|
|
}, 1000);
|
|
}, 2000);
|
|
} else {
|
|
this.failureCount++;
|
|
this.saveProgress();
|
|
this.updateStatus(`Level ${this.currentLevel} failed. Retrying...`);
|
|
|
|
// Auto-retry after a delay
|
|
setTimeout(() => {
|
|
this.startChallenge();
|
|
}, 2000);
|
|
}
|
|
|
|
this.updateProgress();
|
|
}
|
|
|
|
resetLevel() {
|
|
this.currentLevel = 1;
|
|
this.successCount = 0;
|
|
this.failureCount = 0;
|
|
this.saveProgress();
|
|
this.updateDisplay();
|
|
this.updateControlsFromLevel();
|
|
this.updateStatus(`Progress reset. Ready for Level ${this.currentLevel}`);
|
|
this.updateProgress();
|
|
|
|
// Auto-start the first level
|
|
setTimeout(() => {
|
|
this.startChallenge();
|
|
}, 500);
|
|
}
|
|
|
|
|
|
|
|
updateDisplay() {
|
|
document.getElementById('currentLevel').textContent = this.currentLevel;
|
|
|
|
const config = this.levelConfig[this.currentLevel];
|
|
if (config) {
|
|
document.getElementById('pinCount').textContent = config.pinCount;
|
|
document.getElementById('difficulty').textContent = config.difficulty;
|
|
document.getElementById('sensitivity').textContent = config.sensitivity;
|
|
document.getElementById('liftSpeed').textContent = config.liftSpeed;
|
|
}
|
|
|
|
// Update progress bar to reflect current level
|
|
this.updateProgress();
|
|
}
|
|
|
|
updateStatus(message) {
|
|
const feedbackElement = document.querySelector('.lockpick-feedback');
|
|
if (feedbackElement) {
|
|
feedbackElement.textContent = message;
|
|
}
|
|
}
|
|
|
|
updateProgress() {
|
|
const progress = (this.currentLevel - 1) / this.maxLevel * 100;
|
|
document.getElementById('progressFill').style.width = progress + '%';
|
|
}
|
|
|
|
showAchievement(message) {
|
|
const achievements = document.getElementById('achievements');
|
|
const achievement = document.createElement('div');
|
|
achievement.className = 'achievement';
|
|
achievement.textContent = message;
|
|
|
|
achievements.appendChild(achievement);
|
|
|
|
// Display achievement message in progress bar
|
|
const progressAchievement = document.getElementById('progressAchievement');
|
|
if (progressAchievement) {
|
|
progressAchievement.textContent = message;
|
|
|
|
// Clear the progress bar message after 5 seconds
|
|
setTimeout(() => {
|
|
progressAchievement.textContent = '';
|
|
}, 5000);
|
|
}
|
|
|
|
// Remove achievement popup after 5 seconds
|
|
setTimeout(() => {
|
|
if (achievement.parentNode) {
|
|
achievement.parentNode.removeChild(achievement);
|
|
}
|
|
}, 5000);
|
|
}
|
|
}
|
|
|
|
// Initialize the progressive lockpicking system
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const progressiveSystem = new ProgressiveLockpicking();
|
|
// Auto-start the current level (could be loaded from saved progress)
|
|
setTimeout(() => {
|
|
progressiveSystem.startChallenge();
|
|
}, 500);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |