diff --git a/assets/objects/text_file.png b/assets/objects/text_file.png new file mode 100644 index 0000000..90ed349 Binary files /dev/null and b/assets/objects/text_file.png differ diff --git a/css/container-minigame.css b/css/container-minigame.css index af8a541..395452e 100644 --- a/css/container-minigame.css +++ b/css/container-minigame.css @@ -15,11 +15,105 @@ background: #000; } +/* Monitor bezel for desktop containers */ +.container-monitor-bezel { + background: #666; + border: 8px solid #444; + 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; + margin: 20px; + flex: 1; + display: flex; + flex-direction: column; +} + +.container-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; +} + +.container-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; +} + +/* Post-it notes for container monitor bezel */ +.container-monitor-bezel .postit-note { + position: absolute; + bottom: -15px; + left: 20px; + z-index: 15; + margin: 0; + transform: rotate(-3deg); + background: #ffff88; + border: 1px solid #ddd; + border-radius: 3px; + padding: 15px; + box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3); + font-family: 'Press Start 2P', monospace; + font-size: 8px; + color: #333; + max-width: 200px; + word-wrap: break-word; +} + +.container-monitor-bezel .postit-note::before { + content: ''; + position: absolute; + top: -1px; + right: -1px; + width: 0; + height: 0; + border-left: 15px solid transparent; + border-top: 15px solid #f0f0f0; +} + +.container-monitor-bezel .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; +} + +.container-monitor-bezel .postit-note:nth-child(2) { + left: 120px; + transform: rotate(2deg); +} + +.container-monitor-bezel .postit-note:nth-child(3) { + left: 220px; + transform: rotate(-1deg); +} + .desktop-background { flex: 1; position: relative; background: #000; overflow: hidden; + min-height: 400px; } .desktop-wallpaper { @@ -28,6 +122,8 @@ left: 0; right: 0; bottom: 0; + width: 100%; + height: 100%; background-image: url('../assets/mini-games/desktop-wallpaper.png'); background-size: cover; background-position: center; @@ -35,18 +131,9 @@ image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges; - opacity: 0.8; + opacity: 1.0; } -.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; diff --git a/css/minigames.css b/css/minigames.css index da49885..99eb195 100644 --- a/css/minigames.css +++ b/css/minigames.css @@ -38,7 +38,6 @@ border-radius: 5px; box-shadow: 0 0 15px rgba(0, 0, 0, 0.5) inset; position: relative; - overflow: hidden; } .minigame-message-container { @@ -191,384 +190,4 @@ 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; - } -} diff --git a/css/password-minigame.css b/css/password-minigame.css new file mode 100644 index 0000000..160c2eb --- /dev/null +++ b/css/password-minigame.css @@ -0,0 +1,448 @@ +/* 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: #666; + border: 8px solid #444; + 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); +} + +.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: 180px; + 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; + 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; + top: -40px; + z-index: 15; +} + +/* Post-it notes stuck to monitor bezel */ +.monitor-bezel .postit-note { + bottom: -15px; + left: 20px; + z-index: 15; + margin: 0; + transform: rotate(-3deg); +} + +/* Post-it notes between monitor-bezel and keyboard */ +.password-minigame-area .postit-note { + position: relative; + margin: 15px 20px; + z-index: 15; + transform: rotate(-3deg); + align-self: flex-start; +} + +.password-minigame-area .postit-note:nth-child(2) { + margin-left: 140px; + transform: rotate(2deg); +} + +.password-minigame-area .postit-note:nth-child(3) { + margin-left: 260px; + transform: rotate(-1deg); +} + +.monitor-bezel .postit-note:nth-child(2) { + left: 120px; + transform: rotate(2deg); +} + +.monitor-bezel .postit-note:nth-child(3) { + left: 220px; + transform: rotate(-1deg); +} + +.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; + position: relative; + z-index: 10; +} + +.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; + position: relative; + z-index: 11; +} + +.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; +} + +.key-shift { + background: #e67e22; + min-width: 60px; +} + +.key-shift:hover { + background: #d35400; +} + +.key-shift.active { + background: #f39c12; + color: #000; +} + +.password-actions { + display: flex; + justify-content: center; + gap: 15px; + margin-top: 10px; + position: relative; + z-index: 10; +} + +.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; + position: relative; + z-index: 11; +} + +.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; + position: relative; + z-index: 11; +} + +.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; + position: relative; + z-index: 10; +} + +.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; + } +} diff --git a/css/text-file-minigame.css b/css/text-file-minigame.css new file mode 100644 index 0000000..9bfa450 --- /dev/null +++ b/css/text-file-minigame.css @@ -0,0 +1,433 @@ +/* Text File Minigame Styles */ + +/* Import VT font */ +@import url('https://fonts.googleapis.com/css2?family=VT323:wght@400&display=swap'); + +/* Text File Minigame Container */ +.text-file-container { + display: flex; + flex-direction: column; + height: 100%; + background: #ffffff; + border-radius: 12px; + border: 1px solid #d1d5db; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); + overflow: hidden; + font-family: 'VT323', monospace; +} + +/* Mac-style Window Title Bar */ +.text-file-window-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 12px; + background: linear-gradient(to bottom, #f6f6f6 0%, #e8e8e8 100%); + border-bottom: 1px solid #d1d5db; + border-radius: 12px 12px 0 0; + min-height: 28px; +} + +.window-controls { + display: flex; + gap: 6px; + align-items: center; +} + +.window-control { + width: 12px; + height: 12px; + border-radius: 50%; + border: none; + cursor: pointer; + transition: all 0.2s ease; +} + +.window-control.close { + background: #ff5f57; +} + +.window-control.minimize { + background: #ffbd2e; +} + +.window-control.maximize { + background: #28ca42; +} + +.window-control:hover { + transform: scale(1.1); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.window-title { + font-size: 13px; + font-weight: 500; + color: #333333; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + flex: 1; + text-align: center; + margin: 0 20px; +} + +/* File Header Section */ +.file-header { + display: flex; + align-items: center; + padding: 16px 20px; + background: #f8f9fa; + border-bottom: 1px solid #e9ecef; +} + +.file-icon { + font-size: 24px; + margin-right: 12px; + color: #495057; +} + +.file-info { + flex: 1; +} + +.file-name { + font-size: 18px; + font-weight: bold; + color: #212529; + margin-bottom: 4px; + font-family: 'VT323', monospace; +} + +.file-meta { + display: flex; + gap: 12px; + font-size: 14px; + color: #6c757d; + font-family: 'VT323', monospace; +} + +.file-type { + background: #e9ecef; + padding: 2px 8px; + border-radius: 4px; + border: 1px solid #dee2e6; + color: #495057; +} + +.file-size { + color: #6c757d; +} + +/* File Content Area */ +.file-content-area { + flex: 1; + display: flex; + flex-direction: column; + background: #ffffff; + overflow: hidden; +} + +.content-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; + background: #f8f9fa; + border-bottom: 1px solid #e9ecef; + min-height: 36px; +} + +.content-label { + font-size: 14px; + color: #495057; + font-weight: 500; + font-family: 'VT323', monospace; +} + +.content-actions { + display: flex; + gap: 8px; +} + +.action-btn { + background: #ffffff; + border: 1px solid #d1d5db; + color: #374151; + padding: 4px 12px; + border-radius: 6px; + font-size: 12px; + cursor: pointer; + font-family: 'VT323', monospace; + transition: all 0.2s ease; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.action-btn:hover { + background: #f3f4f6; + border-color: #9ca3af; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.action-btn:active { + background: #e5e7eb; + transform: translateY(1px); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +/* File Content Display */ +.file-content { + flex: 1; + padding: 16px 20px; + overflow: auto; + background: #ffffff; + border: none; + margin: 0; +} + +.file-text { + color: #000000; + font-family: 'VT323', monospace; + font-size: 16px; + line-height: 1.5; + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; + user-select: text; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; +} + +/* File Observations Section */ +.file-observations { + margin: 0; + padding: 16px 20px; + background: #fff3cd; + border-top: 1px solid #ffeaa7; +} + +.file-observations h4 { + color: #856404; + font-size: 14px; + margin: 0 0 8px 0; + font-family: 'VT323', monospace; + font-weight: bold; +} + +.file-observations p { + color: #6c5700; + font-size: 14px; + line-height: 1.4; + margin: 0; + font-family: 'VT323', monospace; +} + +/* Text Selection Styling */ +.file-content ::selection { + background: #3b82f6; + color: #ffffff; +} + +.file-content ::-moz-selection { + background: #3b82f6; + color: #ffffff; +} + +/* Custom Scrollbar Styling */ +.file-content::-webkit-scrollbar { + width: 12px; +} + +.file-content::-webkit-scrollbar-track { + background: #f1f5f9; + border-radius: 6px; +} + +.file-content::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 6px; + border: 2px solid #f1f5f9; +} + +.file-content::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +.file-content::-webkit-scrollbar-corner { + background: #f1f5f9; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .text-file-container { + border-radius: 8px; + } + + .text-file-window-header { + border-radius: 8px 8px 0 0; + padding: 6px 10px; + min-height: 24px; + } + + .window-control { + width: 10px; + height: 10px; + } + + .window-title { + font-size: 12px; + margin: 0 15px; + } + + .file-header { + padding: 12px 16px; + } + + .file-name { + font-size: 16px; + } + + .file-meta { + font-size: 12px; + gap: 8px; + } + + .content-header { + padding: 6px 12px; + min-height: 32px; + } + + .content-label { + font-size: 12px; + } + + .action-btn { + padding: 3px 8px; + font-size: 11px; + } + + .file-content { + padding: 12px 16px; + } + + .file-text { + font-size: 14px; + } + + .file-observations { + padding: 12px 16px; + } + + .file-observations h4 { + font-size: 12px; + } + + .file-observations p { + font-size: 12px; + } +} + +/* Dark Mode Support (Optional) */ +@media (prefers-color-scheme: dark) { + .text-file-container { + background: #1f2937; + border-color: #374151; + } + + .text-file-window-header { + background: linear-gradient(to bottom, #374151 0%, #1f2937 100%); + border-bottom-color: #374151; + } + + .window-title { + color: #f9fafb; + } + + .file-header { + background: #374151; + border-bottom-color: #4b5563; + } + + .file-icon { + color: #d1d5db; + } + + .file-name { + color: #f9fafb; + } + + .file-meta { + color: #9ca3af; + } + + .file-type { + background: #4b5563; + border-color: #6b7280; + color: #d1d5db; + } + + .file-size { + color: #9ca3af; + } + + .content-header { + background: #374151; + border-bottom-color: #4b5563; + } + + .content-label { + color: #d1d5db; + } + + .action-btn { + background: #4b5563; + border-color: #6b7280; + color: #f9fafb; + } + + .action-btn:hover { + background: #6b7280; + border-color: #9ca3af; + } + + .action-btn:active { + background: #374151; + } + + .file-content { + background: #1f2937; + } + + .file-text { + color: #f9fafb; + } + + .file-observations { + background: #451a03; + border-top-color: #92400e; + } + + .file-observations h4 { + color: #fbbf24; + } + + .file-observations p { + color: #fcd34d; + } + + .file-content::-webkit-scrollbar-track { + background: #374151; + } + + .file-content::-webkit-scrollbar-thumb { + background: #6b7280; + border-color: #374151; + } + + .file-content::-webkit-scrollbar-thumb:hover { + background: #9ca3af; + } + + .file-content::-webkit-scrollbar-corner { + background: #374151; + } +} diff --git a/index.html b/index.html index 0795eb2..35bf7c5 100644 --- a/index.html +++ b/index.html @@ -42,6 +42,8 @@ + + diff --git a/js/minigames/container/container-minigame.js b/js/minigames/container/container-minigame.js index a0bcf0e..9fcd877 100644 --- a/js/minigames/container/container-minigame.js +++ b/js/minigames/container/container-minigame.js @@ -92,12 +92,18 @@ export class ContainerMinigame extends MinigameScene { createDesktopUI() { this.gameContainer.innerHTML = `
-
-
-
- +
+
+
+
+ +
+
+ ${this.containerItem.scenarioData.postitNote && this.containerItem.scenarioData.showPostit ? ` +
${this.containerItem.scenarioData.postitNote}
+ ` : ''}
@@ -145,16 +151,15 @@ export class ContainerMinigame extends MinigameScene { itemImg.name = item.type; itemImg.objectId = `container_${index}`; - // Add click handler for taking items - if (item.takeable) { - itemImg.style.cursor = 'pointer'; - - // Special handling for notes - trigger notes minigame instead of taking - if (item.type === 'notes' && item.readable && item.text) { - itemImg.addEventListener('click', () => this.handleNotesItem(item, itemImg)); - } else { - itemImg.addEventListener('click', () => this.takeItem(item, itemImg)); - } + // Add click handler for all items (both takeable and interactive) + itemImg.style.cursor = 'pointer'; + + // Check if this is an interactive item that should trigger a minigame + if (this.isInteractiveItem(item)) { + itemImg.addEventListener('click', () => this.handleInteractiveItem(item, itemImg)); + } else if (item.takeable) { + // Regular takeable items + itemImg.addEventListener('click', () => this.takeItem(item, itemImg)); } // Create tooltip @@ -195,16 +200,15 @@ export class ContainerMinigame extends MinigameScene { 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)); - } + // Add click handler for all items (both takeable and interactive) + icon.style.cursor = 'pointer'; + + // Check if this is an interactive item that should trigger a minigame + if (this.isInteractiveItem(item)) { + icon.addEventListener('click', () => this.handleInteractiveItem(item, iconImg)); + } else if (item.takeable) { + // Regular takeable items + icon.addEventListener('click', () => this.takeItem(item, iconImg)); } // Position icon randomly on desktop @@ -233,30 +237,37 @@ export class ContainerMinigame extends MinigameScene { } } - handleNotesItem(item, itemElement) { - console.log('Handling notes item from container:', item); + isInteractiveItem(item) { + // Check if this item should trigger a minigame instead of being taken - // Remove the note from container display - itemElement.parentElement.remove(); - - // Remove from contents array - const itemIndex = this.contents.findIndex(content => content === item); - if (itemIndex !== -1) { - this.contents.splice(itemIndex, 1); + // Notes with readable text + if (item.type === 'notes' && item.readable && item.text) { + return true; } - // Show success message - this.showMessage(`Read ${item.name}`, 'success'); - - // If container is now empty, update display - if (this.contents.length === 0) { - const contentsGrid = document.getElementById('container-contents-grid'); - if (contentsGrid) { - contentsGrid.innerHTML = '

This container is empty.

'; - } + // Text files + if (item.type === 'text_file' && item.text) { + return true; } - // Store container state for return after notes minigame + // Phone with messages + if (item.type === 'phone' && (item.text || item.voice)) { + return true; + } + + // Workstation (crypto workstation) + if (item.type === 'workstation') { + return true; + } + + // Add more interactive item types as needed + return false; + } + + handleInteractiveItem(item, itemElement) { + console.log('Handling interactive item from container:', item); + + // Store container state for return after minigame const containerState = { containerItem: this.containerItem, contents: this.contents, @@ -269,6 +280,24 @@ export class ContainerMinigame extends MinigameScene { // Close the container minigame first this.complete(false); + // Route to appropriate minigame based on item type + if (item.type === 'notes' && item.readable && item.text) { + this.handleNotesItem(item); + } else if (item.type === 'text_file' && item.text) { + this.handleTextFileItem(item); + } else if (item.type === 'phone' && (item.text || item.voice)) { + this.handlePhoneItem(item); + } else if (item.type === 'workstation') { + this.handleWorkstationItem(item); + } else { + console.warn('Unknown interactive item type:', item.type); + this.showMessage(`Unknown item type: ${item.type}`, 'error'); + } + } + + handleNotesItem(item) { + console.log('Handling notes item from container:', item); + // Start the notes minigame if (window.startNotesMinigame) { // Create a temporary sprite-like object for the notes minigame @@ -286,6 +315,88 @@ export class ContainerMinigame extends MinigameScene { } } + handleTextFileItem(item) { + console.log('Handling text file item from container:', item); + + // Start the text file minigame + if (window.MinigameFramework) { + const minigameParams = { + title: `Text File - ${item.name || 'Unknown File'}`, + fileName: item.name || 'Unknown File', + fileContent: item.text, + fileType: item.fileType || 'text', + observations: item.observations, + source: item.source || 'Container', + onComplete: (success, result) => { + console.log('Text file minigame completed:', success, result); + } + }; + + window.MinigameFramework.startMinigame('text-file', null, minigameParams); + } else { + console.error('MinigameFramework not available'); + window.gameAlert('Text file minigame not available', 'error', 'Error', 3000); + } + } + + handlePhoneItem(item) { + console.log('Handling phone item from container:', item); + + // Start the phone messages minigame + if (window.MinigameFramework) { + const messages = []; + + // Add text message if available + if (item.text) { + messages.push({ + type: 'text', + sender: item.sender || 'Unknown', + text: item.text, + timestamp: item.timestamp || 'Unknown time', + read: false + }); + } + + // Add voice message if available + if (item.voice) { + messages.push({ + type: 'voice', + sender: item.sender || 'Unknown', + text: item.text || null, + voice: item.voice, + timestamp: item.timestamp || 'Unknown time', + read: false + }); + } + + const minigameParams = { + title: item.name || 'Phone Messages', + messages: messages, + observations: item.observations, + onComplete: (success, result) => { + console.log('Phone messages minigame completed:', success, result); + } + }; + + window.MinigameFramework.startMinigame('phone-messages', null, minigameParams); + } else { + console.error('MinigameFramework not available'); + window.gameAlert('Phone minigame not available', 'error', 'Error', 3000); + } + } + + handleWorkstationItem(item) { + console.log('Handling workstation item from container:', item); + + // Open the crypto workstation + if (window.openCryptoWorkstation) { + window.openCryptoWorkstation(); + } else { + console.error('Crypto workstation not available'); + window.gameAlert('Crypto workstation not available', 'error', 'Error', 3000); + } + } + takeItem(item, itemElement) { console.log('Taking item from container:', item); diff --git a/js/minigames/index.js b/js/minigames/index.js index 50f7020..10e82a9 100644 --- a/js/minigames/index.js +++ b/js/minigames/index.js @@ -13,6 +13,7 @@ export { ContainerMinigame, startContainerMinigame, returnToContainerAfterNotes export { PhoneMessagesMinigame, returnToPhoneAfterNotes } from './phone/phone-messages-minigame.js'; export { PinMinigame, startPinMinigame } from './pin/pin-minigame.js'; export { PasswordMinigame } from './password/password-minigame.js'; +export { TextFileMinigame, returnToTextFileAfterNotes } from './text-file/text-file-minigame.js'; // Initialize the global minigame framework for backward compatibility import { MinigameFramework } from './framework/minigame-manager.js'; @@ -65,6 +66,9 @@ import { PinMinigame, startPinMinigame } from './pin/pin-minigame.js'; // Import the password minigame import { PasswordMinigame } from './password/password-minigame.js'; +// Import the text file minigame +import { TextFileMinigame, returnToTextFileAfterNotes } from './text-file/text-file-minigame.js'; + // Register minigames MinigameFramework.registerScene('lockpicking', LockpickingMinigamePhaser); // Use Phaser version as default MinigameFramework.registerScene('lockpicking-phaser', LockpickingMinigamePhaser); // Keep explicit phaser name @@ -77,6 +81,7 @@ MinigameFramework.registerScene('container', ContainerMinigame); MinigameFramework.registerScene('phone-messages', PhoneMessagesMinigame); MinigameFramework.registerScene('pin', PinMinigame); MinigameFramework.registerScene('password', PasswordMinigame); +MinigameFramework.registerScene('text-file', TextFileMinigame); // Make minigame functions available globally window.startNotesMinigame = startNotesMinigame; @@ -87,4 +92,5 @@ window.startLockpickSetMinigame = startLockpickSetMinigame; window.startContainerMinigame = startContainerMinigame; window.returnToContainerAfterNotes = returnToContainerAfterNotes; window.returnToPhoneAfterNotes = returnToPhoneAfterNotes; +window.returnToTextFileAfterNotes = returnToTextFileAfterNotes; window.startPinMinigame = startPinMinigame; \ No newline at end of file diff --git a/js/minigames/notes/notes-minigame.js b/js/minigames/notes/notes-minigame.js index 9b084e7..58911ee 100644 --- a/js/minigames/notes/notes-minigame.js +++ b/js/minigames/notes/notes-minigame.js @@ -748,6 +748,15 @@ export function startNotesMinigame(item, noteContent, observationText, navigateT window.returnToPhoneAfterNotes(); }, 100); } + + // Check if we need to return to text file after notes minigame + if (window.pendingTextFileReturn && window.returnToTextFileAfterNotes) { + console.log('Returning to text file after notes minigame'); + // Small delay to ensure notes minigame cleanup completes + setTimeout(() => { + window.returnToTextFileAfterNotes(); + }, 100); + } } }; diff --git a/js/minigames/password/password-minigame.js b/js/minigames/password/password-minigame.js index efd8fbb..c72c5bc 100644 --- a/js/minigames/password/password-minigame.js +++ b/js/minigames/password/password-minigame.js @@ -14,7 +14,8 @@ export class PasswordMinigame extends MinigameScene { attempts: 0, showPassword: false, postitNote: params.postitNote || '', - showPostit: params.showPostit || false + showPostit: params.showPostit || false, + capsLock: false }; // Store the correct password for validation @@ -42,12 +43,6 @@ export class PasswordMinigame extends MinigameScene { // Create the password entry interface this.gameContainer.innerHTML = `
- ${this.gameData.showPostit && this.gameData.postitNote ? ` -
- ${this.gameData.postitNote} -
- ` : ''} -
@@ -75,6 +70,12 @@ export class PasswordMinigame extends MinigameScene {
+ ${this.gameData.showPostit && this.gameData.postitNote ? ` +
+ ${this.gameData.postitNote} +
+ ` : ''} + ${this.gameData.showKeyboard ? `
@@ -114,6 +115,7 @@ export class PasswordMinigame extends MinigameScene {
+ @@ -268,6 +270,8 @@ export class PasswordMinigame extends MinigameScene { this.submitPassword(); } else if (keyValue === 'Escape') { this.cancelPassword(); + } else if (keyValue === 'Shift') { + this.toggleCapsLock(); } else if (keyValue === 'Backspace') { this.passwordField.value = this.passwordField.value.slice(0, -1); this.gameData.password = this.passwordField.value; @@ -275,7 +279,8 @@ export class PasswordMinigame extends MinigameScene { this.passwordField.value += ' '; this.gameData.password = this.passwordField.value; } else if (keyValue && keyValue.length === 1) { - this.passwordField.value += keyValue; + const char = this.gameData.capsLock ? keyValue.toUpperCase() : keyValue.toLowerCase(); + this.passwordField.value += char; this.gameData.password = this.passwordField.value; } @@ -283,6 +288,18 @@ export class PasswordMinigame extends MinigameScene { this.passwordField.focus(); } + toggleCapsLock() { + this.gameData.capsLock = !this.gameData.capsLock; + const shiftKey = document.getElementById('shift-key'); + if (shiftKey) { + if (this.gameData.capsLock) { + shiftKey.classList.add('active'); + } else { + shiftKey.classList.remove('active'); + } + } + } + submitPassword() { if (!this.gameState.isActive) return; diff --git a/js/minigames/text-file/text-file-minigame.js b/js/minigames/text-file/text-file-minigame.js new file mode 100644 index 0000000..6287090 --- /dev/null +++ b/js/minigames/text-file/text-file-minigame.js @@ -0,0 +1,377 @@ +import { MinigameScene } from '../framework/base-minigame.js'; + +export class TextFileMinigame extends MinigameScene { + constructor(container, params) { + super(container, params); + + // Ensure params is an object with default values + const safeParams = params || {}; + + // Initialize text file specific state + this.textFileData = { + fileName: safeParams.fileName || 'Unknown File', + fileContent: safeParams.fileContent || '', + fileType: safeParams.fileType || 'text', + observations: safeParams.observations || '', + source: safeParams.source || 'Unknown Source' + }; + } + + init() { + // Call parent init to set up basic UI structure + super.init(); + + // Customize the header + this.headerElement.innerHTML = ` +

📄 ${this.textFileData.fileName}

+

Viewing text file contents

+ `; + + // Add notebook button to minigame controls (before cancel button) + if (this.controlsElement) { + const notebookBtn = document.createElement('button'); + notebookBtn.className = 'minigame-button'; + notebookBtn.id = 'minigame-notebook'; + notebookBtn.innerHTML = '📝 Add to Notebook'; + this.controlsElement.appendChild(notebookBtn); + + // Change cancel button text to "Close" + const cancelBtn = document.getElementById('minigame-cancel'); + if (cancelBtn) { + cancelBtn.innerHTML = 'Close'; + } + } + + // Set up the text file interface + this.setupTextFileInterface(); + + // Set up event listeners + this.setupEventListeners(); + } + + setupTextFileInterface() { + // Create the text file interface with Mac-style window + this.gameContainer.innerHTML = ` +
+
+
+ + + +
+
${this.textFileData.fileName}
+
+
+ +
+
📄
+
+
${this.textFileData.fileName}
+
+ ${this.textFileData.fileType.toUpperCase()} + ${this.getFileSize()} +
+
+
+ +
+
+ +
+ + +
+
+
+ ${this.formatFileContent()} +
+
+ + ${this.textFileData.observations ? ` +
+

📋 Observations:

+

${this.textFileData.observations}

+
+ ` : ''} +
+ `; + + // Get references to important elements + this.fileContent = document.getElementById('file-content'); + this.copyBtn = document.getElementById('copy-btn'); + this.selectAllBtn = document.getElementById('select-all-btn'); + + // Get window control references + this.closeBtn = this.gameContainer.querySelector('.window-control.close'); + this.minimizeBtn = this.gameContainer.querySelector('.window-control.minimize'); + this.maximizeBtn = this.gameContainer.querySelector('.window-control.maximize'); + } + + formatFileContent() { + // Format the file content for display + let content = this.textFileData.fileContent; + + // Escape HTML characters + content = content.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + + // Convert line breaks to
tags + content = content.replace(/\n/g, '
'); + + // Wrap in a pre element to preserve formatting + return `
${content}
`; + } + + getFileSize() { + // Calculate approximate file size + const bytes = new Blob([this.textFileData.fileContent]).size; + if (bytes < 1024) { + return `${bytes} B`; + } else if (bytes < 1024 * 1024) { + return `${(bytes / 1024).toFixed(1)} KB`; + } else { + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + } + } + + setupEventListeners() { + // Window controls + this.addEventListener(this.closeBtn, 'click', () => { + this.complete(false); + }); + + this.addEventListener(this.minimizeBtn, 'click', () => { + // For now, just show a message (could implement minimize functionality later) + this.showSuccess("Minimize functionality not implemented", false, 2000); + }); + + this.addEventListener(this.maximizeBtn, 'click', () => { + // For now, just show a message (could implement maximize functionality later) + this.showSuccess("Maximize functionality not implemented", false, 2000); + }); + + // Copy button + this.addEventListener(this.copyBtn, 'click', () => { + this.copyToClipboard(); + }); + + // Select all button + this.addEventListener(this.selectAllBtn, 'click', () => { + this.selectAllText(); + }); + + // Notebook button (in minigame controls) + const notebookBtn = document.getElementById('minigame-notebook'); + if (notebookBtn) { + this.addEventListener(notebookBtn, 'click', () => { + this.addToNotebook(); + }); + } + + // Keyboard controls + this.addEventListener(document, 'keydown', (event) => { + this.handleKeyPress(event); + }); + + // Double-click to select all + this.addEventListener(this.fileContent, 'dblclick', () => { + this.selectAllText(); + }); + } + + handleKeyPress(event) { + if (!this.gameState.isActive) return; + + // Handle Ctrl+A for select all + if (event.ctrlKey && event.key === 'a') { + event.preventDefault(); + this.selectAllText(); + } + + // Handle Ctrl+C for copy (when text is selected) + if (event.ctrlKey && event.key === 'c') { + // Let the default behavior handle copying selected text + return; + } + + // Handle Escape to close + if (event.key === 'Escape') { + event.preventDefault(); + this.complete(false); + } + } + + copyToClipboard() { + try { + // Use the modern clipboard API if available + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(this.textFileData.fileContent).then(() => { + this.showSuccess("File content copied to clipboard!", false, 2000); + }).catch(err => { + console.error('Failed to copy to clipboard:', err); + this.fallbackCopyToClipboard(); + }); + } else { + // Fallback for older browsers or non-secure contexts + this.fallbackCopyToClipboard(); + } + } catch (error) { + console.error('Copy failed:', error); + this.fallbackCopyToClipboard(); + } + } + + fallbackCopyToClipboard() { + // Create a temporary textarea element + const textArea = document.createElement('textarea'); + textArea.value = this.textFileData.fileContent; + textArea.style.position = 'fixed'; + textArea.style.left = '-999999px'; + textArea.style.top = '-999999px'; + document.body.appendChild(textArea); + + try { + textArea.focus(); + textArea.select(); + const successful = document.execCommand('copy'); + + if (successful) { + this.showSuccess("File content copied to clipboard!", false, 2000); + } else { + this.showFailure("Failed to copy to clipboard", false, 2000); + } + } catch (err) { + console.error('Fallback copy failed:', err); + this.showFailure("Copy not supported on this browser", false, 2000); + } finally { + document.body.removeChild(textArea); + } + } + + selectAllText() { + // Select all text in the file content + const range = document.createRange(); + range.selectNodeContents(this.fileContent); + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + + this.showSuccess("All text selected", false, 1000); + } + + addToNotebook() { + // Check if there's content to add + if (!this.textFileData.fileContent || this.textFileData.fileContent.trim() === '') { + this.showFailure("No content to add to notebook", false, 2000); + return; + } + + // Create comprehensive notebook content + const notebookContent = this.formatContentForNotebook(); + const notebookTitle = `Text File - ${this.textFileData.fileName}`; + const notebookObservations = this.textFileData.observations || + `Text file "${this.textFileData.fileName}" from ${this.textFileData.source}`; + + // Check if notes minigame is available + if (window.startNotesMinigame) { + // Store the text file state globally so we can return to it + const textFileState = { + fileName: this.textFileData.fileName, + fileContent: this.textFileData.fileContent, + fileType: this.textFileData.fileType, + observations: this.textFileData.observations, + source: this.textFileData.source, + params: this.params + }; + + window.pendingTextFileReturn = textFileState; + + // Create a text file item for the notes minigame + const textFileItem = { + scenarioData: { + type: 'text_file', + name: notebookTitle, + text: notebookContent, + observations: notebookObservations, + important: true // Mark as important since it's from a file + } + }; + + // Start notes minigame - it will handle returning to text file via returnToTextFileAfterNotes + window.startNotesMinigame( + textFileItem, + notebookContent, + notebookObservations, + null, // Let notes minigame auto-navigate to the newly added note + false, // Don't auto-add to inventory + false // Don't auto-close + ); + + this.showSuccess("Added file content to notebook", false, 2000); + } else { + this.showFailure("Notebook not available", false, 2000); + } + } + + formatContentForNotebook() { + let content = `Text File: ${this.textFileData.fileName}\n`; + 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 += `FILE CONTENTS:\n`; + content += `${'-'.repeat(20)}\n\n`; + content += this.textFileData.fileContent; + content += `\n\n${'='.repeat(50)}\n`; + content += `End of File: ${this.textFileData.fileName}`; + + return content; + } + + start() { + // Call parent start + super.start(); + + console.log("Text file minigame started"); + console.log("File:", this.textFileData.fileName); + console.log("Content length:", this.textFileData.fileContent.length); + } + + cleanup() { + // Call parent cleanup (handles event listeners) + super.cleanup(); + } +} + +// Function to return to text file after notes minigame (similar to container pattern) +export function returnToTextFileAfterNotes() { + console.log('Returning to text file after notes minigame'); + + // Check if there's a pending text file return + if (window.pendingTextFileReturn) { + const textFileState = window.pendingTextFileReturn; + + // Clear the pending return state + window.pendingTextFileReturn = null; + + // Start the text file minigame with the stored state + if (window.MinigameFramework) { + window.MinigameFramework.startMinigame('text-file', null, { + title: `Text File - ${textFileState.fileName}`, + fileName: textFileState.fileName, + fileContent: textFileState.fileContent, + fileType: textFileState.fileType, + observations: textFileState.observations, + source: textFileState.source, + onComplete: (success, result) => { + console.log('Text file minigame completed:', success, result); + } + }); + } + } else { + console.warn('No pending text file return state found'); + } +} diff --git a/js/systems/interactions.js b/js/systems/interactions.js index af060f8..ea50632 100644 --- a/js/systems/interactions.js +++ b/js/systems/interactions.js @@ -302,6 +302,33 @@ export function handleObjectInteraction(sprite) { } } + // For text_file type objects, use the text file minigame + if (data.type === 'text_file' && data.text) { + console.log('Text file object detected:', { type: data.type, name: data.name, text: data.text }); + // Start the text file minigame + if (window.MinigameFramework) { + // Initialize the framework if not already done + if (!window.MinigameFramework.mainGameScene && window.game) { + window.MinigameFramework.init(window.game); + } + + const minigameParams = { + title: `Text File - ${data.name || 'Unknown File'}`, + fileName: data.name || 'Unknown File', + fileContent: data.text, + fileType: data.fileType || 'text', + observations: data.observations, + source: data.source || 'Unknown Source', + onComplete: (success, result) => { + console.log('Text file minigame completed:', success, result); + } + }; + + window.MinigameFramework.startMinigame('text-file', null, minigameParams); + return; // Exit early since minigame handles the interaction + } + } + if (data.readable && data.text) { message += `Text: ${data.text}\n`; diff --git a/password-minigame-example.json b/password-minigame-example.json index f38e988..ee49765 100644 --- a/password-minigame-example.json +++ b/password-minigame-example.json @@ -85,8 +85,10 @@ "lockType": "password", "requires": "desktop123", "showHint": false, - "showKeyboard": false, + "showKeyboard": true, "maxAttempts": 3, + "postitNote": "Password: desktop123", + "showPostit": true, "locked": true, "contents": [ { @@ -112,7 +114,7 @@ "observations": "A digital encryption key file." } ], - "observations": "An office computer with desktop access. Desktop mode will be automatically enabled." + "observations": "An office computer with desktop access. Desktop mode will be automatically enabled. Password is on the sticky note!" }, { "id": "tablet_device", @@ -127,6 +129,8 @@ "showHint": false, "showKeyboard": true, "maxAttempts": 3, + "postitNote": "Tablet password: tablet2024", + "showPostit": true, "locked": true, "contents": [ { @@ -145,7 +149,7 @@ "observations": "Sensitive financial information." } ], - "observations": "An executive tablet device. Desktop mode will be automatically enabled." + "observations": "An executive tablet device. Desktop mode will be automatically enabled. Password is written on the sticky note." } ], "victoryConditions": [ diff --git a/scenarios/ceo_exfil.json b/scenarios/ceo_exfil.json index 3deeea9..ca9fb76 100644 --- a/scenarios/ceo_exfil.json +++ b/scenarios/ceo_exfil.json @@ -36,23 +36,17 @@ "showKeyboard": true, "maxAttempts": 3, "locked": true, - "requires": "password", + "requires": "secret123", "observations": "The reception's computer, currently locked", + "postitNote": "Password: secret123", + "showPostit": true, "contents": [ { - "type": "notes", - "name": "Private Note", - "takeable": true, + "type": "text_file", + "name": "Private", + "takeable": false, "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" + "text": "Closet keypad code: 7391 - Must move evidence to safe before audit" } ] }, @@ -144,8 +138,14 @@ "type": "pc", "name": "Office Computer", "takeable": false, - "requires": "password", - "observations": "A standard office computer" + "lockType": "password", + "requires": "office2024", + "showKeyboard": true, + "maxAttempts": 3, + "locked": true, + "postitNote": "Password: office2024", + "showPostit": true, + "observations": "A standard office computer with a sticky note on the monitor" }, { "type": "notes", @@ -212,7 +212,14 @@ "type": "pc", "name": "CEO Computer", "takeable": false, - "observations": "The CEO's laptop, still warm - recently used" + "lockType": "password", + "requires": "ceo2024", + "showKeyboard": true, + "maxAttempts": 3, + "locked": true, + "postitNote": "Password: ceo2024", + "showPostit": true, + "observations": "The CEO's laptop, still warm - recently used. A sticky note is attached to the screen." }, { "type": "suitcase", diff --git a/test-container-interactive-items.html b/test-container-interactive-items.html new file mode 100644 index 0000000..6c9880c --- /dev/null +++ b/test-container-interactive-items.html @@ -0,0 +1,269 @@ + + + + + + Container Interactive Items Test + + + + + + +
+

📦 Container Interactive Items Test

+ +
+

Test Instructions:

+

1. Click the test buttons below to launch different container scenarios

+

2. Test clicking on interactive items within containers

+

3. Verify that interactive items trigger their respective minigames

+

4. Test that takeable items still go to inventory

+
+ + + + + + + + + +
+

Expected Behavior:

+

• Interactive items (notes, text_file, phone, workstation) should trigger their minigames

+

• Takeable items (keys, etc.) should go to inventory

+

• After minigame completion, should return to container

+

• Container should show remaining items after interaction

+
+
+ + + + + + + diff --git a/test-password-minigame.html b/test-password-minigame.html index 3af199b..a0421d0 100644 --- a/test-password-minigame.html +++ b/test-password-minigame.html @@ -8,6 +8,7 @@ + + + +
+

📄 Text File Minigame Test

+ +
+

Test Instructions:

+

1. Click the test buttons below to launch different text file scenarios

+

2. Test the "Add to Notebook" functionality

+

3. Test copy and select all features

+

4. Test keyboard shortcuts (Ctrl+A, Ctrl+C, Escape)

+
+ + + + + + + + + + + +
+

Expected Behavior:

+

• Text file minigame should open with Mac-style window decorations

+

• VT323 font should be used for all text content

+

• Black text on white background (clean, readable interface)

+

• Window controls (close, minimize, maximize) should be functional

+

• "Add to Notebook" button should work (if notes minigame is available)

+

• Copy and Select All buttons should function

+

• Escape key should close the minigame

+
+
+ + + + + + + diff --git a/verify-css.html b/verify-css.html index 35dd20b..d1b0955 100644 --- a/verify-css.html +++ b/verify-css.html @@ -8,6 +8,7 @@ +