diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..b0f629698fd1741a98be988da63ec3db19db41aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +static/assets/sounds/music.mp3 filter=lfs diff=lfs merge=lfs -text diff --git a/DOCKER b/DOCKER new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..df1cfbf7e2dde51285d8a5c79e21b94dcc173731 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Use official Python runtime as base image +FROM python:3.9-slim + +# Set working directory +WORKDIR /app + +# Copy requirements first to leverage Docker cache +COPY requirements.txt . + +# Install dependencies +RUN pip install -r requirements.txt + +# Copy the rest of the application +COPY . . + +# Set environment variables +ENV PYTHONUNBUFFERED=1 +ENV PORT=7860 + +# Expose the port the app runs on +EXPOSE 7860 + +# Command to run the application +CMD ["python", "app.py"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b096aefbe6a40f72684f8574a32397f7ad7fc8db --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Johnny + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 539dc84adf65a0a18fdb3bcc4f54afc2fec21fec..69b4997cf8c9b3f15303da50a7eeb48290b2c8da 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,24 @@ --- -title: TextToSurvive -emoji: 🌍 -colorFrom: yellow -colorTo: indigo -sdk: docker +title: The Last Message +emoji: 📱 +colorFrom: gray +colorTo: red +sdk: gradio +sdk_version: "3.50.2" +app_file: app.py pinned: false -license: apache-2.0 -short_description: Text to Survie (Team 9) --- -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +# The Last Message - Horror Escape Game + +An interactive horror escape game where your messages determine the fate of others. + +### Docker + +# Build the image + +docker build -t p5js-game . + +# Run the container + +docker run -p 8000:8000 p5js-game diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..781304a99c30d05fa66a7bca369cd446315bdf5e Binary files /dev/null and b/__pycache__/app.cpython-313.pyc differ diff --git a/__pycache__/app.cpython-38.pyc b/__pycache__/app.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..749d97c53b22f6a5c1d898519c983370dcd4d3d6 Binary files /dev/null and b/__pycache__/app.cpython-38.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..5858975e534f2f0000a49407f872f42513949a71 --- /dev/null +++ b/app.py @@ -0,0 +1,58 @@ +from flask import Flask, request, jsonify, render_template, send_from_directory +import requests +import os +from flask_cors import CORS +import logging + +app = Flask(__name__) +CORS(app) # Enable CORS for development + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +@app.route('/mistral-proxy', methods=['POST']) +def proxy_to_mistral(): + MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY') + MISTRAL_API_URL = "https://api.mistral.ai/v1/chat/completions" + + if not MISTRAL_API_KEY: + logger.error("MISTRAL_API_KEY not configured") + return jsonify({"error": "MISTRAL_API_KEY not configured"}), 500 + + headers = { + 'Authorization': f'Bearer {MISTRAL_API_KEY}', + 'Content-Type': 'application/json' + } + + try: + # Log incoming request (excluding sensitive data) + logger.info(f"Received request for Mistral API") + + response = requests.post(MISTRAL_API_URL, + headers=headers, + json=request.json) + + # Check if the response was successful + response.raise_for_status() + + return jsonify(response.json()) + except requests.exceptions.RequestException as e: + logger.error(f"Error calling Mistral API: {str(e)}") + return jsonify({"error": f"Error calling Mistral API: {str(e)}"}), 500 + except Exception as e: + logger.error(f"Unexpected error: {str(e)}") + return jsonify({"error": str(e)}), 500 + +# Serve static files +@app.route('/') +def index(): + return send_from_directory('static', 'index.html') + +# Add this if you want to serve other static files +@app.route('/') +def serve_static(path): + return send_from_directory('static', path) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=7860) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0c6fdda07dd6bd2d61b2e6722b6254cd9b7b7b9f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask==3.0.0 +requests==2.31.0 +flask-cors==4.0.0 +python-dotenv==1.0.0 diff --git a/static/.DS_Store b/static/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..df1d895ffa40f9e179dc3140357e0a4d3b7450fb Binary files /dev/null and b/static/.DS_Store differ diff --git a/static/assets/.DS_Store b/static/assets/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1c7cb33efab99e1eab5018903ada5d2386d1f6d9 Binary files /dev/null and b/static/assets/.DS_Store differ diff --git a/static/assets/css/game-style.css b/static/assets/css/game-style.css new file mode 100644 index 0000000000000000000000000000000000000000..824bb4243235efdd3a9e0bba48a5335b79129af9 --- /dev/null +++ b/static/assets/css/game-style.css @@ -0,0 +1,566 @@ + /* Layout */ + body { + margin: 0; + font-family: Arial, sans-serif; + display: flex; + background-color: #220a0a; + height: 100vh; + position: relative; + overflow: hidden; +} +* { + opacity: 0.98; +} +#gameContainer { + display: flex; + width: 100%; + height: 100vh; + overflow: auto; + position: relative; + z-index: 1; + align-items: center; +} + +#mapSection { + flex: 1; + min-width: 600px; + width: fit-content; + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: auto; + z-index: 2; + padding: 20px; +} + +/* We'll place the P5 canvas in here */ +.map-wrapper { + margin: 20px; + transform-origin: center center; + min-width: min-content; +} + +#chatSection { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + background: #000000; + border-radius: 38px; + overflow: hidden; +} + +#chatHistory { + flex: 1; + overflow-y: auto; + padding: 16px; + background: #f8f8fa; +} + +/* Style moderne pour la barre de scroll */ +#chatHistory::-webkit-scrollbar { + width: 6px; +} + +#chatHistory::-webkit-scrollbar-track { + background: transparent; +} + +#chatHistory::-webkit-scrollbar-thumb { + background: #d1d1d6; + border-radius: 3px; +} + +#chatControls { + padding: 16px; + background: #ffffff; + display: flex; + gap: 12px; + border-top: 1px solid #f1f1f5; +} + +.chat-message { + padding: 12px 16px; + margin: 6px 0; + border-radius: 20px; + max-width: 75%; + font-size: 0.95em; + line-height: 1.4; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.user-message { + background: #007aff; + color: white; + margin-left: auto; + border-radius: 20px 20px 4px 20px; +} + +.assistant-message { + background: #f1f1f5; + color: #000000; + margin-right: auto; + border-radius: 20px 20px 20px 4px; +} + +input { + flex: 1; + padding: 12px 16px; + border: none; + border-radius: 20px; + font-family: inherit; + font-size: 0.95em; + background: #f1f1f5; + transition: background 0.2s; +} + +input:focus { + outline: none; + background: #e5e5ea; +} + +input::placeholder { + color: #8e8e93; +} + +button { + padding: 8px; + width: 40px; + height: 40px; + background: #007aff; + color: white; + border: none; + border-radius: 20px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.2s, background 0.2s; +} + +button:hover { + background: #0056b3; + transform: scale(1.05); +} + +/* Style moderne pour l'indicateur de chargement */ +.loading-dots { + display: inline-flex; + gap: 4px; + padding: 8px 12px; + background: #f1f1f5; + border-radius: 16px; +} + +.dot { + width: 6px; + height: 6px; + background: #8e8e93; + border-radius: 50%; + animation: bounce 1.4s infinite ease-in-out; +} + +.dot:nth-child(1) { + animation-delay: -0.32s; +} + +.dot:nth-child(2) { + animation-delay: -0.16s; +} + +@keyframes bounce { + + 0%, + 80%, + 100% { + transform: scale(0); + } + + 40% { + transform: scale(1); + } +} + +/* Modal for API key (if needed) */ +#apiKeyModal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + justify-content: center; + align-items: center; +} + +.modal-content { + background: white; + padding: 20px; + border-radius: 8px; + width: 300px; +} + +#apiKey { + width: 100%; + margin: 10px 0; + padding: 8px; +} + +/* Modifier les styles du chat et du mockup */ +.phone-mockup { + position: relative; + width: 400px; + height: 800px; + background: url("/static/assets/img/phone.png") no-repeat center; + background-size: contain; + display: flex; + justify-content: center; + align-items: center; + margin: auto; + padding: 35px 0; + flex-shrink: 0; + z-index: 2; +} + +.phone-screen { + width: 90%; + height: 96.5%; + position: relative; + overflow: hidden; + margin: 0; + border-radius: 40px; + display: flex; + flex-direction: column; +} + +/* Update media queries for better responsiveness */ +@media (max-height: 900px) { + .phone-mockup { + width: 350px; + height: 700px; + } +} + +@media (max-height: 700px) { + .phone-mockup { + width: 300px; + height: 600px; + } +} + +@media (max-width: 1200px) { + #mapSection { + flex: none; + width: fit-content; + } +} + +@media (max-width: 900px) { + #mapSection { + flex: none; + width: fit-content; + } + .phone-mockup { + min-width: 280px; + } +} + +/* Ajouter les nouveaux styles pour l'en-tête du chat */ +#chatHeader { + padding: 16px; + justify-content: center; + background: #ffffff; + border-bottom: 1px solid #f1f1f5; + display: flex; + align-items: center; + gap: 12px; +} + +.profile-picture { + width: 40px; + height: 40px; + border-radius: 50%; + background: #f1f1f5; + display: flex; + align-items: center; + justify-content: center; +} + +.chat-name { + font-size: 1.1em; + font-weight: 600; + color: #000000; +} + +/* LED Bar */ +.led-bar { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient(90deg, + transparent 0%, + rgba(255, 0, 0, 0.4) 20%, + rgba(255, 0, 0, 0.8) 35%, + rgba(255, 50, 50, 1) 50%, + rgba(255, 0, 0, 0.8) 65%, + rgba(255, 0, 0, 0.4) 80%, + transparent 100%); + z-index: 100; + animation: ledFlicker 4s infinite, ledPulse 10s infinite; + box-shadow: 0 0 20px rgba(255, 0, 0, 0.7), + 0 0 35px rgba(255, 0, 0, 0.5), + 0 0 50px rgba(255, 0, 0, 0.4), + 0 0 70px rgba(155, 0, 0, 0.3); + filter: blur(0.6px); +} + +.light-beam { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 250px; + background: linear-gradient(180deg, + rgba(255, 0, 0, 0.3) 0%, + rgba(255, 0, 0, 0.2) 20%, + rgba(255, 0, 0, 0.15) 30%, + rgba(155, 0, 0, 0.08) 60%, + transparent 100%); + animation: beamFlicker 4s infinite; + pointer-events: none; + filter: blur(2px); +} + +@keyframes ledFlicker { + 0% { + opacity: 1; + } + + 95% { + opacity: 1; + } + + 96% { + opacity: 0.3; + } + + 97% { + opacity: 1; + } + + 98% { + opacity: 0.2; + } + + 99% { + opacity: 0.9; + } + + 100% { + opacity: 1; + } +} + +@keyframes ledPulse { + 0% { + filter: brightness(1) blur(0.6px); + } + + 50% { + filter: brightness(1.3) blur(0.4px); + } + + 100% { + filter: brightness(1) blur(0.6px); + } +} + +@keyframes beamFlicker { + 0% { + opacity: 0.7; + } + + 95% { + opacity: 0.7; + } + + 96% { + opacity: 0.2; + } + + 97% { + opacity: 0.7; + } + + 98% { + opacity: 0.1; + } + + 99% { + opacity: 0.6; + } + + 100% { + opacity: 0.7; + } +} + +/* Background Elements */ +.background-elements { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1; +} + +.blood { + position: absolute; + top: 94%; + opacity: 0.6; + transform: rotate(20deg); + right: 10%; +} + +.blood-top-left { + top: 0; + left: 0; + width: 300px; +} + +.blood-top-right { + top: 0; + right: 0; + width: 200px; + transform: rotate(20deg); +} + +.splatter { + bottom: 20%; + left: 10%; + width: 200px; +} + +.blood-bottom-right { + bottom: 0; + right: 0; + position: absolute; + top: 90%; + left: 90%; + width: 200px; +} + +/* Sound button styles */ +.sound-button { + position: fixed; + bottom: 20px; + left: 20px; + width: 100px; + height: 100px; + cursor: pointer; + z-index: 1000; + transition: transform 0.2s ease; +} + +.sound-button:hover { + transform: scale(1.1); +} + +.sound-button img { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* Update button styles */ +button { + padding: 8px; + width: 40px; + height: 40px; + background: #007aff; + color: white; + border: none; + border-radius: 10px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.2s, background 0.2s; +} + +button img { + width: 15px; + height: 15px; + color: #ffffff; +} + +.numpad-overlay { + position: fixed; + top: 50%; + right: 52%; + transform: translateY(-50%); + background: rgba(0, 0, 0, 0.9); + padding: 20px; + border-radius: 10px; + z-index: 1000; +} + +.hidden { + display: none; +} + +.toggle-numpad { + position: fixed; + top: 20px; + right: 20px; + padding: 10px 15px; + font-size: 20px; + background: #333; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + z-index: 1001; +} + +.numpad-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + max-width: 300px; + margin: 10px auto; +} + +.numpad-key { + padding: 25px; + font-size: 27px; + border: 1px solid #444; + background: #333; + color: white; + cursor: pointer; + border-radius: 5px; +} + +.numpad-key:hover { + background: #444; +} + +.numpad-display { + text-align: center; + font-size: 24px; + margin: 10px; + letter-spacing: 5px; + color: white; +} + +/* Update input styles */ +input { + border-radius: 10px; +} \ No newline at end of file diff --git a/static/assets/css/how-to-play.css b/static/assets/css/how-to-play.css new file mode 100644 index 0000000000000000000000000000000000000000..22f88433d9e57f2c8e1dfcf421c880a06027280e --- /dev/null +++ b/static/assets/css/how-to-play.css @@ -0,0 +1,431 @@ +/* Styles généraux pour toutes les pages How to Play */ +.content { + color: #fff; + text-align: center; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 20px; +} +* { + opacity: 0.98; +} +.content h1 { + font-family: "HorrorBrush", cursive; + font-size: 64px; + color: #9b0000; + position: relative; + margin-bottom: 30px; + letter-spacing: 4px; +} + +/* Styles pour la première page */ +.game-description p { + font-size: 2.5rem; + margin-bottom: 2rem; + color: #fff; +} + +.gf-image { + width: 400px; + height: auto; + margin-top: 50px; +} + +/* Styles pour la deuxième page */ +.objects-container { + display: flex; + justify-content: center; + gap: 50px; + margin: 50px 0 30px 0; + width: 100%; +} + +.object-type { + display: flex; + flex-direction: column; + align-items: center; + width: 300px; +} + +.object-title { + font-family: "HorrorBrush", cursive; + font-size: 2.5rem; + color: #fff; + margin-bottom: 10px; +} + +.sub-text { + font-size: 2rem; + color: #ffffff; + margin-bottom: 20px; +} + +.image-container { + width: 100%; + display: flex; + justify-content: center; +} + +/* Styles pour les images tutorielles */ +.tutorial-image { + width: 250px; + height: 250px; + object-fit: contain; +} + +/* Styles de navigation */ +.navigation-buttons { + display: flex; + justify-content: center; + align-items: center; + gap: 50px; + margin-top: 30px; + width: 100%; + height: auto; +} + +.navigation-buttons .back-button, +.navigation-buttons .next-button { + display: inline-block; + font-family: "HorrorBrush", cursive; + font-size: 36px; + color: #fff; + text-decoration: none; + transition: all 0.3s ease; + letter-spacing: 4px; + text-transform: uppercase; + background-color: rgba(155, 0, 0, 0.3); + padding: 10px 30px; + border: 2px solid #9b0000; + border-radius: 5px; + line-height: 1; + margin: 0; +} + +.navigation-buttons .back-button { + position: static; + transform: none; + bottom: auto; + left: auto; +} + +.navigation-buttons .next-button:hover, +.navigation-buttons .back-button:hover { + color: #9b0000; + background-color: rgba(155, 0, 0, 0.1); + box-shadow: 0 0 15px rgba(155, 0, 0, 0.5); +} + +/* Style spécifique pour le bouton Back to Menu */ +.back-to-menu { + position: absolute; + bottom: 40px; + left: 50%; + transform: translateX(-50%); +} + +/* Styles pour la deuxième page */ +.object-type.green .object-title { + color: #20bb43; +} + +.object-type.red .object-title { + color: #d25658; +} + +.object-type.white .object-title { + color: white; +} + +/* Media Queries pour la responsivité */ +@media screen and (max-width: 1200px) { + .content { + max-width: 95%; + padding: 15px; + } +} + +@media screen and (max-width: 900px) { + .objects-container { + flex-direction: column; + align-items: center; + gap: 40px; + margin: 50px 0 30px 0; + } + + .object-type { + width: 100%; + max-width: 400px; + } + + .tutorial-image { + width: 200px; + height: 200px; + } + + .content h1 { + font-size: 48px; + } + + .object-title { + font-size: 2rem; + } + + .sub-text { + font-size: 1.5rem; + } +} + +@media screen and (max-width: 600px) { + .gf-image { + width: 90%; + max-width: 300px; + } + + .content h1 { + font-size: 36px; + } + + .game-description p { + font-size: 1.8rem; + } + + .navigation-buttons { + flex-direction: column; + gap: 20px; + } + + .navigation-buttons .back-button, + .navigation-buttons .next-button { + font-size: 28px; + padding: 8px 20px; + } + + .object-title { + font-size: 1.8rem; + } + + .sub-text { + font-size: 1.2rem; + } + + .tutorial-image { + width: 180px; + height: 180px; + } +} + +@media screen and (max-width: 400px) { + .content h1 { + font-size: 32px; + } + + .game-description p { + font-size: 1.5rem; + } + + .navigation-buttons .back-button, + .navigation-buttons .next-button { + font-size: 24px; + padding: 6px 16px; + } +} + +@media screen and (max-height: 800px) { + .content { + margin-top: 10px; + } + + .content h1 { + font-size: 48px; + margin-bottom: 20px; + } + + .game-description p { + font-size: 2rem; + margin-bottom: 1.5rem; + } + + .gf-image { + width: 300px; + margin-top: 30px; + } + + .objects-container { + margin: 50px 0 20px 0; + } + + .object-type { + width: 250px; + } + + .tutorial-image { + width: 200px; + height: 200px; + } +} + +@media screen and (max-height: 600px) { + .content { + margin-top: 5px; + } + + .content h1 { + font-size: 36px; + margin-bottom: 15px; + } + + .game-description p { + font-size: 1.8rem; + margin-bottom: 1rem; + } + + .gf-image { + width: 250px; + margin-top: 20px; + } + + .objects-container { + margin: 30px 0 15px 0; + } + + .object-type { + width: 200px; + } + + .tutorial-image { + width: 180px; + height: 180px; + } + + .navigation-buttons { + margin-top: 30px; + } + + .navigation-buttons .back-button, + .navigation-buttons .next-button { + font-size: 28px; + padding: 8px 20px; + } +} + +@media screen and (max-height: 500px) { + .content h1 { + font-size: 32px; + margin-bottom: 10px; + } + + .game-description p { + font-size: 1.5rem; + } + + .gf-image { + width: 200px; + margin-top: 15px; + } + + .objects-container { + margin: 20px 0 10px 0; + } + + .object-type { + width: 180px; + } + + .tutorial-image { + width: 130px; + height: 130px; + } + + .navigation-buttons { + margin-top: 20px; + } + + .navigation-buttons .back-button, + .navigation-buttons .next-button { + font-size: 24px; + padding: 6px 16px; + } +} + +/* Styles pour la page 3 */ +.main-description { + font-size: 2.5rem; + color: #fff; + font-family: "HorrorBrush", cursive; +} + +.object-type.yellow .object-title { + color: #ffd700; +} + +/* Ajustement pour deux objets au lieu de trois */ +.objects-container.two-items { + justify-content: center; + gap: 100px; /* Plus d'espace entre deux éléments */ +} + +/* Media queries pour la page 3 */ +@media screen and (max-width: 900px) { + .main-description { + font-size: 2rem; + margin: 20px 0; + } +} + +@media screen and (max-width: 600px) { + .main-description { + font-size: 1.8rem; + margin: 15px 0; + } +} + +@media screen and (max-height: 600px) { + .main-description { + font-size: 1.8rem; + margin: 10px 0; + } +} + +/* Styles pour la page 4 */ +.video-container { + margin-top: 20px; + width: 100%; + display: flex; + justify-content: center; +} + +.tutorial-video { + max-width: 200px; + width: 100%; + border-radius: 30px; +} + +/* Media queries pour la page 4 */ +@media screen and (max-width: 900px) { + .tutorial-video { + max-width: 350px; + } +} + +@media screen and (max-width: 600px) { + .tutorial-video { + max-width: 300px; + } +} + +@media screen and (max-height: 800px) { + .video-container { + margin: 30px 0; + } +} + +@media screen and (max-height: 600px) { + .video-container { + margin: 20px 0; + } +} diff --git a/static/assets/css/index-style.css b/static/assets/css/index-style.css new file mode 100644 index 0000000000000000000000000000000000000000..1e4908c36d7b61a73ca08b3609d40d84b44fe8c1 --- /dev/null +++ b/static/assets/css/index-style.css @@ -0,0 +1,491 @@ +@font-face { + font-family: "HorrorBrush"; + src: url("/static/assets/fonts/horrorbrush.ttf") format("truetype"); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + opacity: 0.98; +} +body { + background-color: #000000; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + overflow: hidden; +} + +.background-elements { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; +} + +.blood { + position: absolute; +} + +.blood-top-left { + top: 0; + left: 0; + width: 350px; + opacity: 50%; +} + +.blood-bottom-right { + bottom: 0; + right: 0; + width: 250px; + position: absolute; + bottom: -120px; + right: -50px; + opacity: 40%; +} + +.blood-top-right { + position: absolute; + top: 0; + right: 0; + width: 250px; + margin: 20px 20px 0px 0px; + transform: rotate(30deg); +} + +.splatter { + position: absolute; + opacity: 60%; + bottom: 240px; + left: 30%; + transform: translateX(-50%); + transform: rotate(20deg); + width: 400px; +} + +main { + display: flex; + flex-direction: column; + align-items: center; + gap: 100px; + z-index: 1; +} + +.logo { + position: absolute; + top: 40px; + left: 50%; + transform: translateX(-50%); + animation: flicker 4s linear infinite; +} + +.logo img { + width: 400px; + height: auto; +} + +.subtitle { + font-family: "HorrorBrush", cursive; + color: #fff; + font-size: 24px; + text-align: center; + margin-top: 10px; + letter-spacing: 2px; +} + +.menu { + display: flex; + margin-top: 250px; + flex-direction: column; + gap: 30px; + align-items: center; + position: relative; +} + +.menu::after { + content: ""; + position: absolute; + width: 800px; + height: 800px; + background: radial-gradient( + circle, + rgba(155, 0, 0, 0.35) 0%, + rgba(0, 0, 0, 0) 70% + ); + z-index: -1; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.menu-item { + font-family: "HorrorBrush", cursive; + font-size: 48px; + color: #fff; + text-decoration: none; + transition: color 0.3s ease; + letter-spacing: 4px; + text-transform: uppercase; +} + +.new-game { + color: #9b0000; +} + +.menu-item:hover { + color: #9b0000; +} + +.how-to-play:hover { + color: #9b0000; +} + +.level-maker:hover { + color: #9b0000; +} + +@keyframes flicker { + 0% { + opacity: 1; + } + 5% { + opacity: 0.9; + } + 10% { + opacity: 1; + } + 15% { + opacity: 0.4; + } + 16% { + opacity: 1; + } + 17% { + opacity: 0.4; + } + 18% { + opacity: 1; + } + 35% { + opacity: 1; + } + 36% { + opacity: 0.3; + } + 37% { + opacity: 1; + } + 38% { + opacity: 0.5; + } + 39% { + opacity: 1; + } + 50% { + opacity: 1; + } + 51% { + opacity: 0.7; + } + 52% { + opacity: 1; + } + 53% { + opacity: 0.4; + } + 54% { + opacity: 1; + } + 85% { + opacity: 1; + } + 86% { + opacity: 0.6; + } + 87% { + opacity: 1; + } + 88% { + opacity: 0.4; + } + 89% { + opacity: 1; + } + 100% { + opacity: 1; + } +} + +.led-bar { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 0, 0, 0.4) 20%, + rgba(255, 0, 0, 0.8) 35%, + rgba(255, 50, 50, 1) 50%, + rgba(255, 0, 0, 0.8) 65%, + rgba(255, 0, 0, 0.4) 80%, + transparent 100% + ); + z-index: 100; + animation: ledFlicker 4s infinite, ledPulse 10s infinite; + box-shadow: 0 0 20px rgba(255, 0, 0, 0.7), 0 0 35px rgba(255, 0, 0, 0.5), + 0 0 50px rgba(255, 0, 0, 0.4), 0 0 70px rgba(155, 0, 0, 0.3); + filter: blur(0.6px); +} + +.light-beam { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 250px; + background: linear-gradient( + 180deg, + rgba(255, 0, 0, 0.3) 0%, + rgba(255, 0, 0, 0.2) 20%, + rgba(255, 0, 0, 0.15) 30%, + rgba(155, 0, 0, 0.08) 60%, + transparent 100% + ); + animation: beamFlicker 4s infinite; + pointer-events: none; + filter: blur(2px); +} + +@keyframes ledFlicker { + 0% { + opacity: 1; + } + 95% { + opacity: 1; + } + 96% { + opacity: 0.3; + } + 97% { + opacity: 1; + } + 98% { + opacity: 0.2; + } + 99% { + opacity: 0.9; + } + 100% { + opacity: 1; + } +} + +@keyframes ledPulse { + 0% { + filter: brightness(1) blur(0.6px); + } + 50% { + filter: brightness(1.3) blur(0.4px); + } + 100% { + filter: brightness(1) blur(0.6px); + } +} + +@keyframes beamFlicker { + 0% { + opacity: 0.7; + } + 95% { + opacity: 0.7; + } + 96% { + opacity: 0.2; + } + 97% { + opacity: 0.7; + } + 98% { + opacity: 0.1; + } + 99% { + opacity: 0.6; + } + 100% { + opacity: 0.7; + } +} + +/* How to Play Page Styles */ +.content { + color: #fff; + text-align: center; + max-width: 800px; + margin: 0 auto; + padding: 20px; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 0px; +} + +.content h1 { + font-family: "HorrorBrush", cursive; + font-size: 64px; + color: #9b0000; + position: relative; + margin-bottom: 30px; + letter-spacing: 4px; +} + +.game-description p { + font-size: 2.5rem; + margin-bottom: 2rem; + color: #fff; +} + +.back-button { + font-family: "HorrorBrush", cursive; + font-size: 36px; + color: #fff; + text-decoration: none; + transition: color 0.3s ease; + letter-spacing: 4px; + text-transform: uppercase; + position: absolute; + bottom: 40px; + left: 50%; + transform: translateX(-50%); +} + +.back-button:hover { + color: #9b0000; +} + +.character { + position: absolute; + bottom: -30px; + left: 50%; + transform: translateX(-50%); + z-index: 2; + animation: characterFlicker 6s infinite; +} + +.character img { + width: 550px; + height: auto; +} + +@keyframes characterFlicker { + 0% { + opacity: 1; + filter: brightness(1); + } + 42% { + opacity: 1; + filter: brightness(1); + } + 43% { + opacity: 0.8; + filter: brightness(1.2); + } + 44% { + opacity: 1; + filter: brightness(1); + } + 45% { + opacity: 0.6; + filter: brightness(1.3); + } + 46% { + opacity: 1; + filter: brightness(1); + } + 47% { + opacity: 0.2; + filter: brightness(1.5); + } + 48% { + opacity: 1; + filter: brightness(1); + } + 49% { + opacity: 0.4; + filter: brightness(1.2); + } + 50% { + opacity: 1; + filter: brightness(1); + } + 80% { + opacity: 1; + filter: brightness(1); + } + 81% { + opacity: 0.5; + filter: brightness(1.3); + } + 82% { + opacity: 1; + filter: brightness(1); + } + 100% { + opacity: 1; + filter: brightness(1); + } +} + +/* Modifier les styles pour le bouton son */ +.sound-button { + position: fixed; + bottom: 20px; + left: 20px; + width: 100px; + height: 100px; + cursor: pointer; + z-index: 1000; + transition: transform 0.2s ease; +} + +.sound-button:hover { + transform: scale(1.1); +} + +.sound-button img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.gf-image { + width: 400px; + height: auto; + margin-top: 50px; +} + +.next-button { + font-family: "HorrorBrush", cursive; + font-size: 36px; + color: #fff; + text-decoration: none; + transition: all 0.3s ease; + letter-spacing: 4px; + text-transform: uppercase; + display: block; + margin-top: 50px; + background-color: rgba(155, 0, 0, 0.3); + padding: 10px 30px; + border: 2px solid #9b0000; + border-radius: 5px; +} + +.next-button:hover { + color: #9b0000; + background-color: rgba(155, 0, 0, 0.1); + box-shadow: 0 0 15px rgba(155, 0, 0, 0.5); +} diff --git a/static/assets/css/popup.css b/static/assets/css/popup.css new file mode 100644 index 0000000000000000000000000000000000000000..1f0f7945946d2ec831085a7c881ddefd1fb3a251 --- /dev/null +++ b/static/assets/css/popup.css @@ -0,0 +1,236 @@ +@font-face { + font-family: "HorrorBrush"; + src: url("/static/assets/fonts/horrorbrush.ttf") format("truetype"); +} + +.popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.202); + backdrop-filter: blur(8px); + z-index: 1000; + display: block; +} + +.popup-loser { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 800px; + height: 500px; + background-color: #141313; + border-radius: 20px; + box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); + z-index: 1001; + display: none; + overflow: hidden; +} + +.popup-loser .clown { + position: absolute; + bottom: -5px; + right: -5px; + width: 300px; + height: auto; + z-index: 1002; +} + +.popup-loser .blood-top { + position: absolute; + top: -100px; + left: -100px; + width: 300px; + height: auto; + z-index: 1002; +} + +.popup-loser .blood-bottom { + position: absolute; + bottom: -100px; + right: -100px; + width: 300px; + height: auto; + transform: rotate(180deg); + z-index: 1001; +} + +.popup-loser .title { + position: absolute; + left: 40%; + transform: translateX(-50%); + font-size: 120px; + font-weight: bold; + color: #9b0000; + text-shadow: 10px 10px 4px #4d0000; + font-family: "HorrorBrush", cursive; + z-index: 1003; +} + +.popup-loser .title-glow { + position: absolute; + top: 50%; + left: 40%; + transform: translate(-50%, -50%); + width: 600px; + height: 600px; + background: radial-gradient( + circle, + rgba(155, 0, 0, 0.2) 0%, + rgba(0, 0, 0, 0) 90% + ); + z-index: 1002; + pointer-events: none; +} + +.popup-loser .play-again { + position: absolute; + bottom: 130px; + left: 40%; + transform: translateX(-50%); + padding: 15px 40px; + min-width: 230px; + font-size: 42px; + font-family: "HorrorBrush", cursive; + color: #ffffff; + background-color: #9b0000; + border-radius: 15px; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 5px 15px rgba(155, 0, 0, 0.4); + z-index: 1003; + letter-spacing: 2px; + line-height: 1; + white-space: nowrap; + height: auto; + display: flex; + align-items: center; + justify-content: center; +} + +.popup-loser .play-again:hover { + background-color: #cc0000; + transform: translateX(-50%) scale(1.05); + box-shadow: 0 8px 20px rgba(155, 0, 0, 0.6); +} + +/* Popup Winner */ +.popup-winner { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 800px; + height: 500px; + border-radius: 20px; + box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); + z-index: 1001; + display: block; + overflow: hidden; + background-image: url("/static/assets/img/popup/background.jpg"); + background-size: cover; + background-position: center; +} + +.popup-winner .win-text { + position: absolute; + top: 35%; + left: 35%; + transform: translate(-50%, -50%); + width: 60%; + height: auto; + z-index: 1003; +} + +.popup-winner .fuck { + position: absolute; + bottom: -10px; + right: -10px; + width: 350px; + height: auto; + z-index: 1004; +} + +.popup-winner .stars1 { + position: absolute; + right: 40px; + left: 80%; + top: 0%; + width: 200px; + height: auto; + z-index: 1002; +} + +.popup-winner .stars2 { + position: absolute; + bottom: -10px; + right: 40px; + width: 500px; + height: auto; + z-index: 1002; +} + +.popup-winner .play-again { + position: absolute; + bottom: 40px; + left: 27%; + top: auto; + transform: translateX(-50%); + padding: 20px 60px; + min-width: 350px; + height: 80px; + font-size: 30px; + font-family: "Comic Sans MS", cursive; + color: #ffffff; + background: linear-gradient(45deg, #ff69b4, #ff1493); + border: 3px solid #fff; + border-radius: 40px; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 5px 20px rgba(255, 105, 180, 0.6), + inset 0 0 20px rgba(255, 255, 255, 0.4); + z-index: 1003; + letter-spacing: 1px; + text-transform: uppercase; + animation: sparkle 2s infinite; + white-space: nowrap; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; +} + +.popup-winner .play-again:hover { + transform: translateX(-50%) scale(1.05); + background: linear-gradient(45deg, #ff1493, #ff69b4); + box-shadow: 0 8px 25px rgba(255, 105, 180, 0.8), + inset 0 0 30px rgba(255, 255, 255, 0.6); + letter-spacing: 3px; +} + +.popup-winner .play-again::before { + content: "✨"; + position: absolute; + left: 25px; +} + +.popup-winner .play-again::after { + content: "✨"; + position: absolute; + right: 25px; +} + +@keyframes sparkle { + 0% { + text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #ff69b4; + } + 50% { + text-shadow: 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff1493; + } + 100% { + text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #ff69b4; + } +} diff --git a/static/assets/css/stress-animations.css b/static/assets/css/stress-animations.css new file mode 100644 index 0000000000000000000000000000000000000000..a0e4102afb0f61e4b13723768862cca316a442ab --- /dev/null +++ b/static/assets/css/stress-animations.css @@ -0,0 +1,235 @@ +/* Classes de base pour la photo de profil */ +#chatSection .profile-picture { + width: 40px; + height: 40px; + border-radius: 50%; + overflow: hidden; + margin-right: 10px; +} + +#chatSection .profile-picture img { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* État: No Stress */ +#chatSection.no-stress .profile-picture { + border: 2px solid #607ab1; +} + +#chatSection.no-stress .chat-name { + color: #333; +} + +/* État: Low Stress - Animation de pulsation modérée */ +#chatSection.low-stress .profile-picture { + border: 2px solid #ffa500; + animation: lowStressPulse 1.5s infinite; + box-shadow: 0 0 5px rgba(255, 165, 0, 0.3); +} + +#chatSection.low-stress .chat-name { + animation: lowStressTextPulse 1.5s infinite; +} + +#chatSection.low-stress #chatHistory .ai-message { + background-color: #ffa500; + color: #ffffff; + animation: lowStressMessagePulse 2s infinite, + lowStressMessageScale 1.5s infinite; + border: 1px solid #ffa500; +} + +@keyframes lowStressPulse { + 0% { + box-shadow: 0 0 0 0 rgba(255, 165, 0, 0.3); + transform: scale(1); + } + 50% { + box-shadow: 0 0 10px 3px rgba(255, 165, 0, 0.5); + transform: scale(1.03); + } + 100% { + box-shadow: 0 0 0 0 rgba(255, 165, 0, 0); + transform: scale(1); + } +} + +@keyframes lowStressTextPulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } +} + +@keyframes lowStressMessagePulse { + 0% { + background-color: #ffa500; + box-shadow: 0 0 0 0 rgba(255, 165, 0, 0.3); + } + 50% { + background-color: #ffb733; + box-shadow: 0 0 8px 0 rgba(255, 165, 0, 0.4); + } + 100% { + background-color: #ffa500; + box-shadow: 0 0 0 0 rgba(255, 165, 0, 0.3); + } +} + +@keyframes lowStressMessageScale { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.02); + } + 100% { + transform: scale(1); + } +} + +/* État: Very Stress - Animation de pulsation intense */ +#chatSection.very-stress { + background-color: #1a1a1a; + transition: background-color 0.3s ease; +} + +#chatSection.very-stress #chatHeader { + background-color: #1a1a1a; + transition: background-color 0.3s ease; +} + +#chatSection.very-stress .profile-picture { + border: 3px solid #ff0000; + animation: veryStressPulse 0.8s infinite; + box-shadow: 0 0 10px rgba(255, 0, 0, 0.5); +} + +#chatSection.very-stress .chat-name { + color: #ff0000; + animation: veryStressTextPulse 0.8s infinite; + font-weight: bold; + text-shadow: 0 0 10px rgba(255, 0, 0, 0.8); +} + +#chatSection.very-stress #chatHistory { + background-color: #1a1a1a; + color: #ffffff; +} + +#chatSection.very-stress #chatHistory .ai-message { + background-color: #e92626; + color: #ffffff; + animation: messageShake 0.8s infinite ease-in-out, messageRedPulse 1.5s infinite; + border: 2px solid #ff0000; + box-shadow: 0 0 15px rgba(255, 0, 0, 0.4); + transform-origin: center; +} + +#chatSection.very-stress #chatHistory .assistant-message { + background-color: #2d2d2d; + color: #ff9999; + border: 1px solid #ff0000; + animation: assistantMessageShake 1.5s infinite ease-in-out; + transform-origin: center; +} + +#chatSection.very-stress #chatControls { + background-color: #1a1a1a; +} + +#chatSection.very-stress #prompt { + background-color: #2d2d2d; + color: #ffffff; + border: 1px solid #ffffff; +} + +#chatSection.very-stress .chat-name::after { + content: "💀💀"; +} + +#chatSection.very-stress .chat-name span { + display: none; +} + +@keyframes veryStressPulse { + 0% { + box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.5); + transform: scale(1); + } + 50% { + box-shadow: 0 0 15px 5px rgba(255, 0, 0, 0.7); + transform: scale(1.05); + } + 100% { + box-shadow: 0 0 0 0 rgba(255, 0, 0, 0); + transform: scale(1); + } +} + +@keyframes veryStressTextPulse { + 0% { + text-shadow: 0 0 0 rgba(255, 0, 0, 0); + transform: scale(1); + } + 50% { + text-shadow: 0 0 10px rgba(255, 0, 0, 0.8); + transform: scale(1.08); + } + 100% { + text-shadow: 0 0 0 rgba(255, 0, 0, 0); + transform: scale(1); + } +} + +@keyframes messageShake { + 0%, 100% { + transform: translate(0, 0) rotate(0deg); + } + 25% { + transform: translate(-2px, 1px) rotate(-0.5deg); + } + 50% { + transform: translate(0, -1px) rotate(0.5deg); + } + 75% { + transform: translate(2px, 1px) rotate(-0.25deg); + } +} + +@keyframes messageRedPulse { + 0% { + background-color: #e92626; + box-shadow: 0 0 0 0 rgba(233, 38, 38, 0.4); + } + 50% { + background-color: #ff3333; + box-shadow: 0 0 10px 0 rgba(233, 38, 38, 0.6); + } + 100% { + background-color: #e92626; + box-shadow: 0 0 0 0 rgba(233, 38, 38, 0.4); + } +} + +@keyframes assistantMessageShake { + 0%, 100% { + transform: translate(0, 0) rotate(0deg); + } + 25% { + transform: translate(-1px, 0.5px) rotate(-0.25deg); + } + 50% { + transform: translate(0, -0.5px) rotate(0.25deg); + } + 75% { + transform: translate(1px, 0.5px) rotate(-0.125deg); + } +} diff --git a/static/assets/fonts/horrorbrush.ttf b/static/assets/fonts/horrorbrush.ttf new file mode 100644 index 0000000000000000000000000000000000000000..cc3a26dc32448f7236ba970fbdc06a965a53889b Binary files /dev/null and b/static/assets/fonts/horrorbrush.ttf differ diff --git a/static/assets/img/.DS_Store b/static/assets/img/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..bc39fc5e9ddc637d48b8258172918c56deb4489e Binary files /dev/null and b/static/assets/img/.DS_Store differ diff --git a/static/assets/img/appartment/.DS_Store b/static/assets/img/appartment/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 Binary files /dev/null and b/static/assets/img/appartment/.DS_Store differ diff --git a/static/assets/img/appartment/basemap.png b/static/assets/img/appartment/basemap.png new file mode 100644 index 0000000000000000000000000000000000000000..a54fd86eb10555d06d878cf3519aaa70fe2bc243 Binary files /dev/null and b/static/assets/img/appartment/basemap.png differ diff --git a/static/assets/img/appartment/bedroomdoorlocked.png b/static/assets/img/appartment/bedroomdoorlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..abc1cb7c6324e704942828c68dc367f50f7261b5 Binary files /dev/null and b/static/assets/img/appartment/bedroomdoorlocked.png differ diff --git a/static/assets/img/appartment/bedroomdoorunlocked.png b/static/assets/img/appartment/bedroomdoorunlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..be014abddcca57c7b21b536b5a48dc7c0e905597 Binary files /dev/null and b/static/assets/img/appartment/bedroomdoorunlocked.png differ diff --git a/static/assets/img/appartment/bookcasesearchable.png b/static/assets/img/appartment/bookcasesearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..fb02650bbe98874222ef85a3c5feb825ec2d6b88 Binary files /dev/null and b/static/assets/img/appartment/bookcasesearchable.png differ diff --git a/static/assets/img/appartment/bookcaseunusuable.png b/static/assets/img/appartment/bookcaseunusuable.png new file mode 100644 index 0000000000000000000000000000000000000000..a49fd9d7ff5c12f7e3c5d3ae0f06175e7e5ea36e Binary files /dev/null and b/static/assets/img/appartment/bookcaseunusuable.png differ diff --git a/static/assets/img/appartment/bookcaseusable.png b/static/assets/img/appartment/bookcaseusable.png new file mode 100644 index 0000000000000000000000000000000000000000..486920f1896f20ac751ec92358956cffe6cb9e91 Binary files /dev/null and b/static/assets/img/appartment/bookcaseusable.png differ diff --git a/static/assets/img/appartment/cabinetsearchable.png b/static/assets/img/appartment/cabinetsearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..3dbe2161d2437958de2586ff14e5261151bbed56 Binary files /dev/null and b/static/assets/img/appartment/cabinetsearchable.png differ diff --git a/static/assets/img/appartment/cabinetunsearchable.png b/static/assets/img/appartment/cabinetunsearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..4b21ee96ff269f3adc2a429d1e81510a7bfee5b3 Binary files /dev/null and b/static/assets/img/appartment/cabinetunsearchable.png differ diff --git a/static/assets/img/appartment/coffeetableunusable.png b/static/assets/img/appartment/coffeetableunusable.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f62934cb96d71efef7c9d3ea6d81ab875a2af7 Binary files /dev/null and b/static/assets/img/appartment/coffeetableunusable.png differ diff --git a/static/assets/img/appartment/coffeetableusable.png b/static/assets/img/appartment/coffeetableusable.png new file mode 100644 index 0000000000000000000000000000000000000000..4bfd68bb5c2fabd833adf6002f5d5fadd6631601 Binary files /dev/null and b/static/assets/img/appartment/coffeetableusable.png differ diff --git a/static/assets/img/appartment/deadbodyunusable.png b/static/assets/img/appartment/deadbodyunusable.png new file mode 100644 index 0000000000000000000000000000000000000000..8c09ae3ba550cdb0b6885fec3a4ce8329eeea701 Binary files /dev/null and b/static/assets/img/appartment/deadbodyunusable.png differ diff --git a/static/assets/img/appartment/deadbodyusable.png b/static/assets/img/appartment/deadbodyusable.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9bdabbbcb8420df1ec3512ac7a86f7947558b7 Binary files /dev/null and b/static/assets/img/appartment/deadbodyusable.png differ diff --git a/static/assets/img/appartment/desksearchable.png b/static/assets/img/appartment/desksearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..b2048460fa349ecbd0789d258b65584bf51f8936 Binary files /dev/null and b/static/assets/img/appartment/desksearchable.png differ diff --git a/static/assets/img/appartment/deskunsearchable.png b/static/assets/img/appartment/deskunsearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..8a7a2fc86f9707f426a111a23d2b23afdd350730 Binary files /dev/null and b/static/assets/img/appartment/deskunsearchable.png differ diff --git a/static/assets/img/appartment/dressersearchable.png b/static/assets/img/appartment/dressersearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..6d6a32903e473fe1a3911c6b0257dcd7a2b33127 Binary files /dev/null and b/static/assets/img/appartment/dressersearchable.png differ diff --git a/static/assets/img/appartment/dresserunsearchable.png b/static/assets/img/appartment/dresserunsearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..833a10311ffe32d61edc524154472d6ab54e0fbd Binary files /dev/null and b/static/assets/img/appartment/dresserunsearchable.png differ diff --git a/static/assets/img/appartment/finalmap.png b/static/assets/img/appartment/finalmap.png new file mode 100644 index 0000000000000000000000000000000000000000..0744faa5ba4eb84a12da8b97c46c9eb119b44ed0 Binary files /dev/null and b/static/assets/img/appartment/finalmap.png differ diff --git a/static/assets/img/appartment/fridgesearchable.png b/static/assets/img/appartment/fridgesearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..28ae5f231a3a603c6b9013ea5938fa63ed30fbda Binary files /dev/null and b/static/assets/img/appartment/fridgesearchable.png differ diff --git a/static/assets/img/appartment/fridgeunsearchable.png b/static/assets/img/appartment/fridgeunsearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..5fbeedb115d6040a166675333836c490c75f64bb Binary files /dev/null and b/static/assets/img/appartment/fridgeunsearchable.png differ diff --git a/static/assets/img/appartment/generatoroff.png b/static/assets/img/appartment/generatoroff.png new file mode 100644 index 0000000000000000000000000000000000000000..00361e54672653d31a4a021286e8f034ac31a8d8 Binary files /dev/null and b/static/assets/img/appartment/generatoroff.png differ diff --git a/static/assets/img/appartment/generatoron.png b/static/assets/img/appartment/generatoron.png new file mode 100644 index 0000000000000000000000000000000000000000..5c329b34cb11da5188548e6c622702ad6f46540a Binary files /dev/null and b/static/assets/img/appartment/generatoron.png differ diff --git a/static/assets/img/appartment/mainentranceopen.png b/static/assets/img/appartment/mainentranceopen.png new file mode 100644 index 0000000000000000000000000000000000000000..236c5b8d469feaa536747279aaa9145126da771b Binary files /dev/null and b/static/assets/img/appartment/mainentranceopen.png differ diff --git a/static/assets/img/appartment/storagelocked.png b/static/assets/img/appartment/storagelocked.png new file mode 100644 index 0000000000000000000000000000000000000000..70e01fc57e8bb61a454290d16bac0ab73fbcc5a2 Binary files /dev/null and b/static/assets/img/appartment/storagelocked.png differ diff --git a/static/assets/img/appartment/storageunlocked.png b/static/assets/img/appartment/storageunlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..e5060fe72e45303e02f00677614d5881ce923f79 Binary files /dev/null and b/static/assets/img/appartment/storageunlocked.png differ diff --git a/static/assets/img/appartment/stovesearchable.png b/static/assets/img/appartment/stovesearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..a83683cfbbbca1a4393ce6673df44e30e11f37a5 Binary files /dev/null and b/static/assets/img/appartment/stovesearchable.png differ diff --git a/static/assets/img/appartment/stoveunsearchable.png b/static/assets/img/appartment/stoveunsearchable.png new file mode 100644 index 0000000000000000000000000000000000000000..04b89e03acc5d48fdb30784577ae11ad122d33d4 Binary files /dev/null and b/static/assets/img/appartment/stoveunsearchable.png differ diff --git a/static/assets/img/appartment/theexit.png b/static/assets/img/appartment/theexit.png new file mode 100644 index 0000000000000000000000000000000000000000..ff946e5a66f786fb8830e40ab0ff2480feeec815 Binary files /dev/null and b/static/assets/img/appartment/theexit.png differ diff --git a/static/assets/img/appartment/tvunused.png b/static/assets/img/appartment/tvunused.png new file mode 100644 index 0000000000000000000000000000000000000000..8d5de282862c9022e38c963b6ee2877cb4ccc604 Binary files /dev/null and b/static/assets/img/appartment/tvunused.png differ diff --git a/static/assets/img/appartment/tvused.png b/static/assets/img/appartment/tvused.png new file mode 100644 index 0000000000000000000000000000000000000000..02b66a41dbf27bcbff0ce1a528d892950caf325f Binary files /dev/null and b/static/assets/img/appartment/tvused.png differ diff --git a/static/assets/img/blood-2.png b/static/assets/img/blood-2.png new file mode 100644 index 0000000000000000000000000000000000000000..9e2eb72bd887d887f4cd4ccdbaa32f810d93d8fd Binary files /dev/null and b/static/assets/img/blood-2.png differ diff --git a/static/assets/img/clown.png b/static/assets/img/clown.png new file mode 100644 index 0000000000000000000000000000000000000000..0fef91c8084f74b728c8ceb381bedda30a6ccaab Binary files /dev/null and b/static/assets/img/clown.png differ diff --git a/static/assets/img/gf.png b/static/assets/img/gf.png new file mode 100644 index 0000000000000000000000000000000000000000..3b887c66fc943f2a6530634a0e268264cf631d71 Binary files /dev/null and b/static/assets/img/gf.png differ diff --git a/static/assets/img/hand.png b/static/assets/img/hand.png new file mode 100644 index 0000000000000000000000000000000000000000..9716650fc59e9796778cd76449bc7ee66674e258 Binary files /dev/null and b/static/assets/img/hand.png differ diff --git a/static/assets/img/help.png b/static/assets/img/help.png new file mode 100644 index 0000000000000000000000000000000000000000..31a18a6a7244bab03da08a4c6e5a4ab9cd61cd41 Binary files /dev/null and b/static/assets/img/help.png differ diff --git a/static/assets/img/how_to_play/bed.png b/static/assets/img/how_to_play/bed.png new file mode 100644 index 0000000000000000000000000000000000000000..87e2a5db1f43fbfc79fc79048ec3e84b0a99ac08 Binary files /dev/null and b/static/assets/img/how_to_play/bed.png differ diff --git a/static/assets/img/how_to_play/coffeetableusable_1.png b/static/assets/img/how_to_play/coffeetableusable_1.png new file mode 100644 index 0000000000000000000000000000000000000000..cfcb4cc07b1d0a0d44110eb4e8ae490e004ed56a Binary files /dev/null and b/static/assets/img/how_to_play/coffeetableusable_1.png differ diff --git a/static/assets/img/how_to_play/monster.png b/static/assets/img/how_to_play/monster.png new file mode 100644 index 0000000000000000000000000000000000000000..349fcbf8c16837e0f3db7dbbdd5d287c33b9b097 Binary files /dev/null and b/static/assets/img/how_to_play/monster.png differ diff --git a/static/assets/img/how_to_play/tvunused_1.png b/static/assets/img/how_to_play/tvunused_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bd1152029314b627e0c08f98c66105114766ef89 Binary files /dev/null and b/static/assets/img/how_to_play/tvunused_1.png differ diff --git a/static/assets/img/how_to_play/untitled_artwork.png b/static/assets/img/how_to_play/untitled_artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..32f51f696c5a8138b393bf3cfa6db96a32c33688 Binary files /dev/null and b/static/assets/img/how_to_play/untitled_artwork.png differ diff --git a/static/assets/img/how_to_play/video.gif b/static/assets/img/how_to_play/video.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f123760d52a31399aa49a43c3ff67ee60319bcc Binary files /dev/null and b/static/assets/img/how_to_play/video.gif differ diff --git a/static/assets/img/logo.png b/static/assets/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fa702e7cdc9d0216a780c3bf48f3451a265799b9 Binary files /dev/null and b/static/assets/img/logo.png differ diff --git a/static/assets/img/logo.svg b/static/assets/img/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..e6b3646ddc3f358587b1d5c949e4f3bcb57dc867 --- /dev/null +++ b/static/assets/img/logo.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/assets/img/perso.png b/static/assets/img/perso.png new file mode 100644 index 0000000000000000000000000000000000000000..cb1a1fb621d7c613162c40ae6965c2862bd0ab70 Binary files /dev/null and b/static/assets/img/perso.png differ diff --git a/static/assets/img/phone.png b/static/assets/img/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..7fcadfa3188d597eadb74dc7cd1b84fca61a9967 Binary files /dev/null and b/static/assets/img/phone.png differ diff --git a/static/assets/img/popup/background.jpg b/static/assets/img/popup/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..897e50401a08b37860650db7b7cb896fa530ca76 Binary files /dev/null and b/static/assets/img/popup/background.jpg differ diff --git a/static/assets/img/popup/clown_lose.png b/static/assets/img/popup/clown_lose.png new file mode 100644 index 0000000000000000000000000000000000000000..02069476c98abfeb442791725113b5ab4269051c Binary files /dev/null and b/static/assets/img/popup/clown_lose.png differ diff --git a/static/assets/img/popup/fuck.png b/static/assets/img/popup/fuck.png new file mode 100644 index 0000000000000000000000000000000000000000..7567c473b3755287eea028c585bcedeeb4c758ec Binary files /dev/null and b/static/assets/img/popup/fuck.png differ diff --git a/static/assets/img/popup/stars1.png b/static/assets/img/popup/stars1.png new file mode 100644 index 0000000000000000000000000000000000000000..17c9c30cbf022632002b1f16eb958fe3e6856490 Binary files /dev/null and b/static/assets/img/popup/stars1.png differ diff --git a/static/assets/img/popup/stars2.png b/static/assets/img/popup/stars2.png new file mode 100644 index 0000000000000000000000000000000000000000..eb66212ae2ba93966cf952778b9446a2417d5e8e Binary files /dev/null and b/static/assets/img/popup/stars2.png differ diff --git a/static/assets/img/popup/text.svg b/static/assets/img/popup/text.svg new file mode 100644 index 0000000000000000000000000000000000000000..aecb92610187c674c6ed87ff023b86cef6fb98e3 --- /dev/null +++ b/static/assets/img/popup/text.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/assets/img/profil_pictures/.DS_Store b/static/assets/img/profil_pictures/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9a52327ddf2a7819ee3acbfaabf22b41180cd78c Binary files /dev/null and b/static/assets/img/profil_pictures/.DS_Store differ diff --git a/static/assets/img/profil_pictures/low_stress.webp b/static/assets/img/profil_pictures/low_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..9fca85356b82d03df99d422e060f0212e744f057 Binary files /dev/null and b/static/assets/img/profil_pictures/low_stress.webp differ diff --git a/static/assets/img/profil_pictures/no_stress.webp b/static/assets/img/profil_pictures/no_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..5a9e2905e23e087499df1ad82b9016e1496f379a Binary files /dev/null and b/static/assets/img/profil_pictures/no_stress.webp differ diff --git a/static/assets/img/profil_pictures/very_stress.webp b/static/assets/img/profil_pictures/very_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..731c4a78358828fc386f107ba956767c39312216 Binary files /dev/null and b/static/assets/img/profil_pictures/very_stress.webp differ diff --git a/static/assets/img/sondon.png b/static/assets/img/sondon.png new file mode 100644 index 0000000000000000000000000000000000000000..6a04f983ad2ee8ae2b8294b4feaea7bb20593636 Binary files /dev/null and b/static/assets/img/sondon.png differ diff --git a/static/assets/img/soundoff.png b/static/assets/img/soundoff.png new file mode 100644 index 0000000000000000000000000000000000000000..7dbb6b031438e741348e59a4579f746ae00ce09a Binary files /dev/null and b/static/assets/img/soundoff.png differ diff --git a/static/assets/img/splatter.png b/static/assets/img/splatter.png new file mode 100644 index 0000000000000000000000000000000000000000..940e296de08a5b6849668a64aaf9d076b9e5d720 Binary files /dev/null and b/static/assets/img/splatter.png differ diff --git a/static/assets/img/vite.svg b/static/assets/img/vite.svg new file mode 100644 index 0000000000000000000000000000000000000000..e7b8dfb1b2a60bd50538bec9f876511b9cac21e3 --- /dev/null +++ b/static/assets/img/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/assets/sounds/clownmove1.mp3 b/static/assets/sounds/clownmove1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3bab8dccbcc6fc875be822dd28ee93028babb318 Binary files /dev/null and b/static/assets/sounds/clownmove1.mp3 differ diff --git a/static/assets/sounds/clownmove2.mp3 b/static/assets/sounds/clownmove2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..cbaf350a3163a0e56ec6a47bca01d6faf7f897f4 Binary files /dev/null and b/static/assets/sounds/clownmove2.mp3 differ diff --git a/static/assets/sounds/clownseesyou.mp3 b/static/assets/sounds/clownseesyou.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5018805d1381435e044f9fe0a63b1efaf1fdad56 Binary files /dev/null and b/static/assets/sounds/clownseesyou.mp3 differ diff --git a/static/assets/sounds/gfmove.mp3 b/static/assets/sounds/gfmove.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a27e6063f3a698f987ddfba1fc0887a98286d347 Binary files /dev/null and b/static/assets/sounds/gfmove.mp3 differ diff --git a/static/assets/sounds/lose.mp3 b/static/assets/sounds/lose.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..34aabd7fd3135403e85afb7ff7079ee26828716a Binary files /dev/null and b/static/assets/sounds/lose.mp3 differ diff --git a/static/assets/sounds/message.wav b/static/assets/sounds/message.wav new file mode 100644 index 0000000000000000000000000000000000000000..406605a104a0d50291553c943fc22e541ebd5985 Binary files /dev/null and b/static/assets/sounds/message.wav differ diff --git a/static/assets/sounds/music.mp3 b/static/assets/sounds/music.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..bbcfb0852301d2ee59e37212fe58d90e12b3a6b8 --- /dev/null +++ b/static/assets/sounds/music.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2769acb745016d935744bff69f2eaa00694342c0deb4b1b693e8a8ae33726fa3 +size 4546560 diff --git a/static/assets/sounds/success.mp3 b/static/assets/sounds/success.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2b9e504367cfd800f15dd864a3743d6b791335dd Binary files /dev/null and b/static/assets/sounds/success.mp3 differ diff --git a/static/assets/sounds/unlockdoor.mp3 b/static/assets/sounds/unlockdoor.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b5f32b2a60ec64889707322d0357979b7bfb94a3 Binary files /dev/null and b/static/assets/sounds/unlockdoor.mp3 differ diff --git a/static/assets/sounds/useknife.mp3 b/static/assets/sounds/useknife.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c7fb2cacc214d9e05979da7b2ce6b97486c3cc33 Binary files /dev/null and b/static/assets/sounds/useknife.mp3 differ diff --git a/static/game/dev/index.html b/static/game/dev/index.html new file mode 100644 index 0000000000000000000000000000000000000000..b6c5bfc770013f3f678bbc9163cabd8021c1f562 --- /dev/null +++ b/static/game/dev/index.html @@ -0,0 +1,131 @@ + + + + + Get Me Out! - Development Grid View + + + + +
+
+ +
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/static/game/dev/mistral_backup.js b/static/game/dev/mistral_backup.js new file mode 100644 index 0000000000000000000000000000000000000000..2e95e32a817b249619492b506cda2434b460a9a6 --- /dev/null +++ b/static/game/dev/mistral_backup.js @@ -0,0 +1,50 @@ +class MistralAPI { + constructor() { + this.apiKey = localStorage.getItem('mistralApiKey'); + this.temperature = 0.7; // Default temperature + } + + setApiKey(key) { + this.apiKey = key; + localStorage.setItem('mistralApiKey', key); + } + + getApiKey() { + return this.apiKey; + } + + setTemperature(temp) { + // Ensure temperature is between 0 and 1 + this.temperature = Math.max(0, Math.min(1, temp)); + } + + getTemperature() { + return this.temperature; + } + + async sendMessage(messages) { + if (!this.apiKey) { + throw new Error('API key not set'); + } + + const response = await fetch('https://api.mistral.ai/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.apiKey}` + }, + body: JSON.stringify({ + model: 'mistral-large-latest', + messages: messages, + temperature: this.temperature // Add temperature to the request + }) + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data.choices[0].message.content; + } +} \ No newline at end of file diff --git a/static/game/elmnts/apt.js b/static/game/elmnts/apt.js new file mode 100644 index 0000000000000000000000000000000000000000..9b55d2f299e1afe773bf440b834342c4363e325d --- /dev/null +++ b/static/game/elmnts/apt.js @@ -0,0 +1,181 @@ +function getApt() { + return { + "gridCols": 32, + "gridRows": 20, + "girlfriend_start_pos": { + "x": 7, + "y": 16 + }, + "clown_start_pos": { + "x": 1, + "y": 3 + }, + "rooms": [ + { + "name": "Kitchen", + "label_position": { + "x": 28, + "y": 3 + }, + "start_col": 26, + "end_col": 30, + "start_row": 1, + "end_row": 5, + "hiding_places": [] + }, + { + "name": "Storage", + "label_position": { + "x": 3, + "y": 3 + }, + "start_col": 1, + "end_col": 5, + "start_row": 1, + "end_row": 5, + "hiding_places": [ + { + "name": "closet", + "hiding_type": "in", + "position": { + "x": 1, + "y": 2 + } + } + ] + }, + { + "name": "My Bedroom", + "label_position": { + "x": 7, + "y": 14 + }, + "start_col": 1, + "end_col": 8, + "start_row": 13, + "end_row": 18, + "hiding_places": [ + { + "name": "bed", + "hiding_type": "under", + "position": { + "x": 4, + "y": 14 + } + } + ] + }, + { + "name": "Bathroom", + "label_position": { + "x": 16, + "y": 10 + }, + "start_col": 15, + "end_col": 17, + "start_row": 7, + "end_row": 12, + "hiding_places": [ + { + "name": "bathtub", + "hiding_type": "in", + "position": { + "x": 15, + "y": 7 + } + } + ] + }, + { + "name": "Main Hallway", + "label_position": { + "x": 14, + "y": 15 + }, + "start_col": 10, + "end_col": 17, + "start_row": 13, + "end_row": 18, + "hiding_places": [] + }, + { + "name": "Living Room", + "label_position": { + "x": 21, + "y": 15 + }, + "start_col": 19, + "end_col": 24, + "start_row": 13, + "end_row": 18, + "hiding_places": [] + }, + { + "name": "Guest Bedroom", + "label_position": { + "x": 28, + "y": 15 + }, + "start_col": 26, + "end_col": 30, + "start_row": 13, + "end_row": 18, + "hiding_places": [ + { + "name": "bed", + "hiding_type": "under", + "position": { + "x": 28, + "y": 18 + } + } + ] + }, + { + "name": "Dining Room", + "label_position": { + "x": 16, + "y": 3 + }, + "start_col": 14, + "end_col": 24, + "start_row": 1, + "end_row": 5, + "hiding_places": [ + { + "name": "table", + "hiding_type": "under", + "position": { + "x": 20, + "y": 3 + } + } + ] + }, + { + "name": "Office", + "label_position": { + "x": 21, + "y": 9 + }, + "start_col": 19, + "end_col": 24, + "start_row": 7, + "end_row": 11, + "hiding_places": [] + }, + { + "name": "North Hallway", + "label_position": { + "x": 9, + "y": 3 + }, + "start_col": 7, + "end_col": 12, + "start_row": 3, + "end_row": 3, + "hiding_places": [] + } + ] + } +} \ No newline at end of file diff --git a/static/game/elmnts/character.js b/static/game/elmnts/character.js new file mode 100644 index 0000000000000000000000000000000000000000..7afe7f623e4d42e8192380b2f0c62ff2286341a6 --- /dev/null +++ b/static/game/elmnts/character.js @@ -0,0 +1,138 @@ +class Character { + constructor(gameState, characterImg) { + this.gameState = gameState; + this.characterImg = characterImg; + this.characterPos = null; + this.previousPos = null; + this.path = []; + this.isMoving = false; + this.moveInterval = null; + this.img = characterImg; + this.speed = 270; + } + + getCharacterPosition() { + return this.characterPos; + } + + moving_check() { + + } + + setCharacterPosition(pos) { + this.characterPos = pos; + } + + getCurrentRoom() { + if (!this.characterPos) return null; + + // Check each room's boundaries to find which room contains the current position + for (const room of this.gameState.map_data.rooms) { + if (this.characterPos.x >= room.start_col && + this.characterPos.x <= room.end_col && + this.characterPos.y >= room.start_row && + this.characterPos.y <= room.end_row) { + return room.name; + } + } + return null; // Return null if not in any room + } + + drawPath(CELL_SIZE) { + if (this.path.length > 0 && this.isMoving) { + noFill(); + stroke(255, 0, 0); + strokeWeight(2); + line( + this.characterPos.x * CELL_SIZE + CELL_SIZE / 2, + this.characterPos.y * CELL_SIZE + CELL_SIZE / 2, + this.path[0].x * CELL_SIZE + CELL_SIZE / 2, + this.path[0].y * CELL_SIZE + CELL_SIZE / 2 + ); + for (let i = 0; i < this.path.length - 1; i++) { + line( + this.path[i].x * CELL_SIZE + CELL_SIZE / 2, + this.path[i].y * CELL_SIZE + CELL_SIZE / 2, + this.path[i + 1].x * CELL_SIZE + CELL_SIZE / 2, + this.path[i + 1].y * CELL_SIZE + CELL_SIZE / 2 + ); + } + strokeWeight(1); + } + } + + moveCharacterAlongPath(callback=null) { + if (this.moveInterval) { + clearInterval(this.moveInterval); + } + this.isMoving = true; + this.moveInterval = setInterval(() => { + if (this.path.length === 0) { + this.isMoving = false; + clearInterval(this.moveInterval); + if (callback) callback(); + return; + } + this.previousPos = { ...this.characterPos }; + const nextPos = this.path.shift(); + this.characterPos = nextPos; + this.moving_check(); + }, this.speed); + } + + findPath(start, end) { + const queue = [[start]]; + const visited = new Set(); + const key = pos => `${pos.x},${pos.y}`; + visited.add(key(start)); + + while (queue.length > 0) { + const currentPath = queue.shift(); + const current = currentPath[currentPath.length - 1]; + + if (current.x === end.x && current.y === end.y) { + return currentPath; + } + const neighbors = [ + { x: current.x, y: current.y - 1 }, + { x: current.x + 1, y: current.y }, + { x: current.x, y: current.y + 1 }, + { x: current.x - 1, y: current.y } + ]; + for (const next of neighbors) { + if (next.x < 0 || next.x >= GRID_COLS || next.y < 0 || next.y >= GRID_ROWS) continue; + if (visited.has(key(next))) continue; + + const cell = this.gameState.map_data.grid[next.y][next.x]; + if (cell.type === 'wall') continue; + + visited.add(key(next)); + queue.push([...currentPath, next]); + } + } + return []; + } + + moveToPosition(pos,callback=null) { + if (!pos || !this.characterPos) return false; + + this.path = this.findPath(this.characterPos, pos); + if (this.path.length > 0) { + this.isMoving = true; + this.moveCharacterAlongPath(callback); + return true; + } + return false; + } + // Movement methods + moveToRoom(roomName) { + const targetRoom = this.gameState.map_data.rooms + .find(room => room.name.toLowerCase() === roomName.toLowerCase()); + if (!targetRoom || !this.characterPos) return false; + + const destination = targetRoom.label_position; + if (!destination) return false; + this.moveToPosition(destination); + } + +} \ No newline at end of file diff --git a/static/game/elmnts/clown.js b/static/game/elmnts/clown.js new file mode 100644 index 0000000000000000000000000000000000000000..a7271f5d7886c651f4d25fbb64e6b57e8daac133 --- /dev/null +++ b/static/game/elmnts/clown.js @@ -0,0 +1,164 @@ +class Clown extends Character { + constructor(gameState, clownImg) { + super(gameState, clownImg); + this.isChasing = false; + this.targetGirlfriend = null; + this.gameState.clown = this; + this.startAutonomousBehavior(); + this.setCharacterPosition(this.gameState.map_data.clown_start_pos); + + // Add initial speed properties + this.baseSpeed = 900; // Starting movement delay in milliseconds + this.minSpeed = 300; // Fastest possible speed + this.speedIncreaseInterval = 150000; // Speed increases every 30 seconds + this.speedIncreaseAmount = 100; // How much to decrease delay each time + + // Start the speed increase timer + this.startSpeedIncrease(); + } + + startAutonomousBehavior() { + if (this.behaviorInterval) { + clearInterval(this.behaviorInterval); + } + + this.behaviorInterval = setInterval(() => { + this.decideBehavior(); + playSound(Math.random() < 0.5 ? 'clownSound1' : 'clownSound2'); + + }, 6000); + } + + decideBehavior() { + if (!this.isMoving) { + const roomEntries = this.gameState.map_data.rooms; + + if (this.targetGirlfriend && !this.gameState.girlfriend.isHiding) { + const girlfriendPos = this.targetGirlfriend.getCharacterPosition(); + if (girlfriendPos) { + this.path = this.findPath(this.characterPos, girlfriendPos); + if (this.path.length > 0) { + this.isChasing = true; + this.moveCharacterAlongPath(); + return; + } + } + } + + this.isChasing = false; + this.targetGirlfriend = null; + + const randomRoom = + roomEntries[Math.floor(Math.random() * roomEntries.length)]; + if (randomRoom) { + this.path = this.findPath(this.characterPos, randomRoom.label_position); + if (this.path.length > 0) { + this.moveCharacterAlongPath(); + } + } + } + } + + checkForGirlfriend(girlfriend) { + // First check if girlfriend exists and has a position + if ( + !this.gameState.girlfriend || + !this.characterPos || + this.gameState.girlfriend.isHiding || // Check if girlfriend is hiding + this.gameState.girlfriend.getIsHiding() // Also check using the getter method + ) { + this.targetGirlfriend = null; + this.isChasing = false; // Stop chasing if was chasing before + return; + } + + const girlfriendPos = this.gameState.girlfriend.getCharacterPosition(); + if (!girlfriendPos) return; + + // First check if in same room + const clownRoom = this.getCurrentRoom(); + const girlfriendRoom = this.gameState.girlfriend.getCurrentRoom(); + + if (clownRoom && girlfriendRoom && clownRoom === girlfriendRoom) { + // Check if within 3 cells in any direction + const xDistance = Math.abs(this.characterPos.x - girlfriendPos.x); + const yDistance = Math.abs(this.characterPos.y - girlfriendPos.y); + + if (xDistance <= 3 && yDistance <= 3) { + this.targetGirlfriend = girlfriend; + // Immediately update path to chase + if (!this.isChasing) { + this.path = this.findPath(this.characterPos, girlfriendPos); + if (this.path.length > 0) { + this.isChasing = true; + playSound('clownSeesYou'); + this.moveCharacterAlongPath(); + } + } + } + } else { + // Not in same room, stop chasing + this.isChasing = false; + this.targetGirlfriend = null; + } + } + + draw(CELL_SIZE) { + if (this.characterPos) { + const newSize = CELL_SIZE * 3; // Increased from 2x to 3x for 50% bigger + const offset = (newSize - CELL_SIZE) / 2; + push(); + if (this.previousPos && this.characterPos.x > this.previousPos.x) { + scale(-1, 1); + image( + this.img, + -this.characterPos.x * CELL_SIZE - newSize + offset, + this.characterPos.y * CELL_SIZE - offset, + newSize, + newSize + ); + } else { + image( + this.img, + this.characterPos.x * CELL_SIZE - offset, + this.characterPos.y * CELL_SIZE - offset, + newSize, + newSize + ); + } + pop(); + } + } + + moveCharacterAlongPath() { + if (this.path && this.path.length > 0) { + this.isMoving = true; + const nextPos = this.path.shift(); + this.previousPos = { ...this.characterPos }; + this.characterPos = nextPos; + + // Use the current speed for movement delay + setTimeout(() => { + this.isMoving = false; + if (this.path.length > 0) { + this.moveCharacterAlongPath(); + } + }, this.baseSpeed); + } + } + + startSpeedIncrease() { + // Increase speed every 30 seconds + setInterval(() => { + if (this.baseSpeed > this.minSpeed) { + this.baseSpeed = Math.max( + this.baseSpeed - this.speedIncreaseAmount, + this.minSpeed + ); + console.log( + `Clown speed increased! Current delay: ${this.baseSpeed}ms` + ); + } + }, this.speedIncreaseInterval); + } +} diff --git a/static/game/elmnts/furniture.js b/static/game/elmnts/furniture.js new file mode 100644 index 0000000000000000000000000000000000000000..0746bc7836f7f370fc6a473363f6f022960e25d8 --- /dev/null +++ b/static/game/elmnts/furniture.js @@ -0,0 +1,282 @@ + +// Start of Selection +function getFurniture() { + return [ + { + "name": "Dresser", + "room": "My Bedroom", + "pos": { + "x": 3, + "y": 17 + }, + "reward": "lockpick", + "state": "unsearchable", + "in_use": false, + "search_msg":"Ok um, I think this is a lockpick 🔐. Why do you even have this?", + "sprite": [ + { + "for": "searchable", + "img": "/assets/img/appartment/dressersearchable.png" + }, + { + "for": "unsearchable", + "img": "/assets/img/appartment/dresserunsearchable.png" + } + ] + }, + { + "name": "Bedroom Door", + "room": "My Bedroom", + "locked": true, + "wall_pos": { + "x": 9, + "y": 15 + }, + "stop_pos":{ + "x": 8, + "y": 15 + } + , + "state": "unlocked", + "in_use": false, + "locked_message":"It's locked, I can't get out!! Why is it locked??", + "unlocked_message":"Ok it's open! 🥳", + "sprite": [ + { + "for": "locked", + "img": "/assets/img/appartment/bedroomdoorlocked.png" + }, + { + "for": "unlocked", + "img": "/assets/img/appartment/bedroomdoorunlocked.png" + } + ] + }, + { + "name": "Coffee Table", + "room": "living", + "pos": { + "x": 15, + "y": 21 + }, + "state": "unusable", + "in_use": false, + "key":"oil", + "reward":"remote", + "locked_msg":"Ugh the drawer doesn't open, it's like it's rusty or jammed or smthn. Gotta loosen it somehow", + "unlocked_msg":"This is kinda gross 🤢 k it's open, got the remote 📱", + "sprite": [ + { + "for": "usable", + "img": "/assets/img/appartment/coffeetableusable.png" + }, + { + "for": "unusable", + "img": "/assets/img/appartment/coffeetableunusable.png" + } + ] + }, + { + "name": "TV", + "room": "living", + "pos": { + "x": 17, + "y": 20 + }, + "state": "used", + "in_use": false, + "key":"remote", + "generator_on":false, + "locked_msg":"I need a remote to turn this on", + "locked_msg_generator_off":"oh shit there's no power i can't turn the tv on", + "unlocked_msg":"ok it's on... um... it's just some static. i think i see a word in there. It says ETHER. Any ideas wtf that means?", + "sprite": [ + { + "for": "used", + "img": "/assets/img/appartment/tvused.png" + }, + { + "for": "unused", + "img": "/assets/img/appartment/tvunused.png" + } + ] + }, + { + "name": "Bookcase", + "room": "Guest Bedroom", + "pos": { + "x": 28, + "y": 14 + }, + "state": "searchable", + "in_use": true, + "msg":"There's nothing useful in here...", + "sprite": [ + { + "for": "searchable", + "img": "/assets/img/appartment/bookcasesearchable.png" + }, + { + "for": "usable", + "img": "/assets/img/appartment/bookcaseusable.png" + }, + { + "for": "unusable", + "img": "/assets/img/appartment/bookcaseunusuable.png" + } + ] + }, + + { + "name": "Generator", + "room": "basement", + "pos": { + "x": 3, + "y": 2 + }, + "state": "on", + "in_use": false, + "sprite": [ + { + "for": "off", + "img": "/assets/img/appartment/generatoroff.png" + }, + { + "for": "on", + "img": "/assets/img/appartment/generatoron.png" + } + ] + }, + { + "name": "Dead Body", + "room": "Storage", + "pos": { + "x": 3, + "y": 4 + }, + "state": "usable", + "in_use": true, + "key":"Knife", + "locked_msg":"Um... what. the. f. Why is its mouth stitched up? Is the key inside??? i cant open it, should i look for something to open it with?", + "unlocked_msg":"No no no ew ew i can't believe im doing this...😭😭😭 ....the key was in here i got it!! I can get out!", + "sprite": [ + { + "for": "usable", + "img": "/assets/img/appartment/deadbodyusable.png" + }, + { + "for": "unusable", + "img": "/assets/img/appartment/deadbodyunusable.png" + } + ] + }, + { + "name": "Desk", + "room": "office", + "pos": { + "x": 20, + "y": 9 + }, + "reward": "Desk Note", + "state": "unsearchable", + "in_use": false, + "search_msg":"There's a note on your desk. It says The flamingo basks in light. what?? 🤔", + "sprite": [ + { + "for": "searchable", + "img": "/assets/img/appartment/desksearchable.png" + }, + { + "for": "unsearchable", + "img": "/assets/img/appartment/deskunsearchable.png" + } + ] + }, + { + "name": "Cabinet", + "room": "Kitchen", + "pos": { + "x": 27, + "y": 2 + }, + "state": "searchable", + "in_use": true, + "reward":"knife", + "msg":"Omg a knife 🔪... i just took it but there's no way i can kill the creature with this.", + "sprite": [ + { + "for": "searchable", + "img": "/assets/img/appartment/cabinetsearchable.png" + }, + { + "for": "unsearchable", + "img": "/assets/img/appartment/cabinetunsearchable.png" + } + ] + }, + { + "name": "Fridge", + "room": "kitchen", + "pos": { + "x": 27, + "y": 4 + }, + "state": "unsearchable", + "in_use": false, + "search_msg":"there's some creepy ouija shit on your fridge with the fridge magnets. They spell out VENES. is that something i should know about??", + "sprite": [ + { + "for": "searchable", + "img": "/assets/img/appartment/fridgesearchable.png" + }, + { + "for": "unsearchable", + "img": "/assets/img/appartment/fridgeunsearchable.png" + } + ] + }, + { + "name": "Stove", + "room": "kitchen", + "pos": { + "x": 29, + "y": 3 + }, + "state": "unsearchable", + "in_use": false, + "search_msg":"U left some olive oil here on the stove for some reason... I feel like it might be useful for something... 🫕", + + "sprite": [ + { + "for": "searchable", + "img": "/assets/img/appartment/stovesearchable.png" + }, + { + "for": "unsearchable", + "img": "/assets/img/appartment/stoveunsearchable.png" + } + ] + } + ,{ + "name":"The Exit", + "room":"Main Hallway", + "in_use": true, + + "pos":{ + "x": 14, + "y": 19 + }, + "state":"locked", + "locked_message":"Omg I can't get out, I need a key... there's a note here that says REMINDER: CHECK STORAGE. shit.", + "unlocked_message":"IM OUT, AND THIS RELATIONSHIP IS OVER", + "sprite": [ + { + "for": "locked", + "img": "/assets/img/appartment/theexit.png" + } + ] + } + + ] + ; +} diff --git a/static/game/elmnts/girlfriend.js b/static/game/elmnts/girlfriend.js new file mode 100644 index 0000000000000000000000000000000000000000..162524b2f5f7a13fbc8ff13a43985d0c3988a5f3 --- /dev/null +++ b/static/game/elmnts/girlfriend.js @@ -0,0 +1,223 @@ +class Girlfriend extends Character { + constructor(gameState, girlfriendImg) { + super(gameState, girlfriendImg); + this.gameState = gameState; + this.gameState.girlfriend = this; + this.img = girlfriendImg; + this.setCharacterPosition(this.gameState.map_data.girlfriend_start_pos); + this.currentHidingSpot = null; + this.stressLevel = 30; + this.inventory = []; + this.knows_about_dead_body = false; + this.independance_rate = 2000; + this.game_loops_before_deciding_for_herself = 700; + } + + update(){ + this.game_loops_before_deciding_for_herself--; + if(this.game_loops_before_deciding_for_herself <= 0){ + this.game_loops_before_deciding_for_herself = this.independance_rate; + strongIndependantWoman(); + } + } + + draw(CELL_SIZE) { + if (this.characterPos) { + push(); + if (this.previousPos && this.characterPos.x > this.previousPos.x) { + scale(-1, 1); + image( + this.img, + -this.characterPos.x * CELL_SIZE - CELL_SIZE * 2, + this.characterPos.y * CELL_SIZE, + CELL_SIZE * 2, + CELL_SIZE * 2 + ); + } else { + if (this.getIsHiding()) { + tint(255, 153); + image( + this.img, + this.characterPos.x * CELL_SIZE, + this.characterPos.y * CELL_SIZE, + CELL_SIZE * 2, + CELL_SIZE * 2 + ); + } else { + image( + this.img, + this.characterPos.x * CELL_SIZE, + this.characterPos.y * CELL_SIZE, + CELL_SIZE * 2, + CELL_SIZE * 2 + ); + } + } + pop(); + } + } + + updateStressLevel(stressChange) { + + if (this.stressLevel <= 60) { + setStressLevel('no'); + } else if (this.stressLevel > 40 && this.stressLevel <= 70) { + setStressLevel('low'); + } else if (this.stressLevel > 70) { + setStressLevel('very'); + } + + + // Add or subtract the stress change while keeping within 0-100 range + this.stressLevel = Math.max( + 0, + Math.min(100, this.stressLevel + stressChange) + ); + } + + handleAction(response, called_by_chat = false) { + if(called_by_chat){ + this.game_loops_before_deciding_for_herself = this.independance_rate; + } + switch (response.action) { + case "go": + playSound('gfMove'); + this.moveToRoom(response.target); + break; + case "hide": + this.hide(response.target); + break; + case "exit": + this.exit(); + break; + case "check": + this.check(response.target) + break; + } + } + + getAvailableHidingSpots() { + // Find the current room in map data + const currentRoom = this.gameState.map_data.rooms.find( + (room) => room.name === this.getCurrentRoom() + ); + // Return hiding places for current room if found, otherwise empty array + return currentRoom ? currentRoom.hiding_places : []; + } + + getAvailableFurniture() { + // Get current room + const currentRoom = this.getCurrentRoom(); + if (!currentRoom) return []; + + // Filter furniture list to only include items in current room that are in use + return this.gameState.map_data.furniture.filter( + furniture => furniture.room === currentRoom && furniture.in_use === true + ); + } + + getKnowsAboutDeadBody() { + return this.knows_about_dead_body; + } + + getInventory() { + return this.inventory; + } + + getIsHiding() { + const hidingSpots = this.getAvailableHidingSpots(); + if (!this.characterPos || hidingSpots.length === 0) return false; + + return hidingSpots.some( + (spot) => + spot.position.x === this.characterPos.x && + spot.position.y === this.characterPos.y + ); + } + + hide(hidingSpotName) { + const currentRoom = this.getCurrentRoom(); + if (!currentRoom) return; + + // Find the room data from map_data + const roomData = this.gameState.map_data.rooms.find( + (room) => room.name === currentRoom + ); + if (!roomData) return; + + // Find the specific hiding spot in the room by checking if names contain each other + const hidingSpot = roomData.hiding_places.find( + (spot) => + spot.name + .toLowerCase() + .trim() + .includes(hidingSpotName.toLowerCase().trim()) || + hidingSpotName + .toLowerCase() + .trim() + .includes(spot.name.toLowerCase().trim()) + ); + + if (!hidingSpot) return; + + // Move to the hiding spot position + this.moveToPosition(hidingSpot.position, () => { + this.currentHidingSpot = hidingSpotName; + }); + } + + exit() { + let the_exit = this.gameState.getTheExit(); + let x = the_exit.pos.x; + let y = the_exit.pos.y - 1; + let pos = { "x": x, "y": y }; + this.moveToPosition(pos, () => { + if (!this.inventory || !this.inventory.includes("Key")) { + addProgramaticMessage(the_exit.locked_message); + } else { + this.gameState.winGame(); + addProgramaticMessage(the_exit.unlocked_message); + } + }); + } + + check(target) { + if (target === "Bookcase") { + let bookcase = this.gameState.map_data.furniture.find(item => item.name === "Bookcase"); + this.moveToPosition(bookcase.pos, () => { + addProgramaticMessage(bookcase.msg); + bookcase.state = "unusable"; + }); + } else if (target === "Cabinet") { + let cabinet =this.gameState.map_data.furniture.find(item => item.name === "Cabinet"); + this.moveToPosition(cabinet.pos, () => { + addProgramaticMessage(cabinet.msg); + this.inventory.push("Knife"); + cabinet.state = "unsearchable"; + }); + } else if (target === "Dead Body") { + let deadBody = this.gameState.map_data.furniture.find(item => item.name === "Dead Body"); + this.moveToPosition(deadBody.pos, () => { + + if (this.inventory.includes("Knife")) { + playSound('useKnife'); + addProgramaticMessage("No no no ew ew i can't believe im doing this...😭😭😭 ....the key was in here i got it!! I can get out!"); + deadBody.state = "unusable"; + this.inventory.push("Key"); + } else { + addProgramaticMessage("Um... what. the. f. Why is its mouth stitched up? Is the key inside??? i cant open it"); + } + + }); + + + } + + } + + moving_check() { + if (this.getCurrentRoom() === "Storage") { + this.knows_about_dead_body = true; + } + } +} diff --git a/static/game/elmnts/grid.js b/static/game/elmnts/grid.js new file mode 100644 index 0000000000000000000000000000000000000000..f30929be5db9904e384a316f5420dc57748e7f92 --- /dev/null +++ b/static/game/elmnts/grid.js @@ -0,0 +1,2604 @@ +function getGrid(){ + return [ + [ + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "wall", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + } + ], + [ + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + } + ], + [ + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + } + ], + [ + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "door", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": "green" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": "red" + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "empty", + "color": null + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": "yellow" + }, + { + "type": "empty", + "color": null + }, + { + "type": "wall", + "color": null + } + ], + [ + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + }, + { + "type": "wall", + "color": null + } + ] + ]; +} \ No newline at end of file diff --git a/static/game/game.js b/static/game/game.js new file mode 100644 index 0000000000000000000000000000000000000000..88bd20a36f6f5a73b6a44b8949ba72622f70d8fb --- /dev/null +++ b/static/game/game.js @@ -0,0 +1,128 @@ +const CELL_SIZE = 50; +let GRID_COLS = 0; +let GRID_ROWS = 0; +let grid = []; + +let gameState; + +let baseMapImg; +let girlfriendImg; +let clownImg; + +let clownSound1Snd; +let clownSound2Snd; +let clownSeesYouSnd; +let gfMoveSnd; +let loseSnd; +let unlockDoorSnd; +let useKnifeSnd; +let messageSnd; + +let successSnd; + + + + +let isSoundOn = + localStorage.getItem("isSoundOn") === null + ? true + : localStorage.getItem("isSoundOn") !== "false"; +const soundIcon = document.getElementById("soundIcon"); +const bgMusic = document.getElementById("bgMusic"); + +const mistralAPI = new MistralAPI(); + +let chatMessages = []; + +let furnitureSprites = {}; + +function preload() { + clownSound1Snd = loadSound('/assets/sounds/clownmove1.mp3'); + clownSound2Snd = loadSound('/assets/sounds/clownmove2.mp3'); + clownSeesYouSnd = loadSound('/assets/sounds/clownseesyou.mp3'); + gfMoveSnd = loadSound('/assets/sounds/gfmove.mp3'); + loseSnd = loadSound('/assets/sounds/lose.mp3'); + unlockDoorSnd = loadSound('/assets/sounds/unlockdoor.mp3'); + useKnifeSnd = loadSound('/assets/sounds/useknife.mp3'); + messageSnd = loadSound('/assets/sounds/message.wav'); + successSnd = loadSound('/assets/sounds/success.mp3'); +} + +async function initializeChat() { + const firstPrompt = gameState.getFirstPrompt(); + const messages = [ + { + role: "system", + content: firstPrompt, + }, + ]; + try { + const response = await mistralAPI.sendMessage(messages); + const jsonResponse = JSON.parse(response); + addMessageToChat("assistant", jsonResponse.textMessage); + } catch (error) { + console.error("Error getting initial message:", error); + } +} + +async function setup() { + setStressLevel('no'); + + baseMapImg = loadImage("/assets/img/appartment/basemap.png"); + girlfriendImg = loadImage("/assets/img/gf.png"); + clownImg = loadImage("/assets/img/clown.png"); + + gameState = new GameState({ + ...getApt(), + grid: getGrid(), + furniture: getFurniture(), + }); + + furnitureSprites = loadFurnitureSprites(gameState.map_data.furniture); + + GRID_COLS = gameState.map_data.gridCols; + GRID_ROWS = gameState.map_data.gridRows; + + girlfriend = new Girlfriend(gameState, girlfriendImg); + clown = new Clown(gameState, clownImg); + + // Once loaded, initialize the P5 canvas with correct dims + let canvas = createCanvas(GRID_COLS * CELL_SIZE, GRID_ROWS * CELL_SIZE); + canvas.parent("mapWrapper"); + adjustScale(); + + await initializeChat(); +} + +function draw() { + clear(); + + if (baseMapImg) { + image(baseMapImg, 0, 0, GRID_COLS * CELL_SIZE, GRID_ROWS * CELL_SIZE); + } + if (furnitureSprites) { + drawFurniture(gameState.map_data.furniture, furnitureSprites); + } + //drawGrid(); + // drawWallsAndDoors(); + + drawLabels(gameState.map_data.rooms); + + + + if (girlfriend) { + //girlfriend.drawPath(CELL_SIZE); + girlfriend.draw(CELL_SIZE); + } + if (clown) { + clown.checkForGirlfriend(girlfriend); + //clown.drawPath(CELL_SIZE); + clown.draw(CELL_SIZE); + } + + gameState.update(); + + if (gameState.game_over) { + noLoop(); + } +} diff --git a/static/game/gameState.js b/static/game/gameState.js new file mode 100644 index 0000000000000000000000000000000000000000..f0175b8471f3f010627e09bfff9e5a9145f171c0 --- /dev/null +++ b/static/game/gameState.js @@ -0,0 +1,199 @@ +// Game state management +class GameState { + constructor(map_data) { + this.map_data = map_data; + this.girlfriend = null; + this.clown = null; + this.game_over = false; + } + endGame() { + playSound('lose'); + this.game_over = true; + loseGameModal(); + } + + winGame() { + this.game_over = true; + playSound('unlockDoor'); + setTimeout(() => { + winGameModal(); + bgMusic.pause(); + playSound('success'); + }, 3000); + return; + } + + update() { + this.girlfriend.update(); + if (this.clown && this.girlfriend) { + const clownPos = this.clown.getCharacterPosition(); + const gfPos = this.girlfriend.getCharacterPosition(); + + if ( + clownPos && + gfPos && + clownPos.x === gfPos.x && + clownPos.y === gfPos.y + ) { + this.endGame(); + } + } + } + + getTheExit() { + return this.map_data.furniture.find( + (furniture) => furniture.name === "The Exit" + ); + } + + + getStressPrompt() { + const currentStress = this.girlfriend ? this.girlfriend.stressLevel : 50; // Default to 50 if girlfriend not initialized + + return `You are a stress level analyzer. Given a message in the context of a survival horror game, evaluate how stressful or calming the message is. + +Your response must be a JSON object with this format: +{ + "stressChange": number // Between -50 and +50 +} + +The current stress level is ${currentStress}/100. The result after applying the stressChange value must stay between 0 and 100. +The stressChange value represents the CHANGE in stress level: +- Negative values (-50 to 0) indicate calming effects +- Positive values (0 to +50) indicate stressful effects +The final stress level after applying the change must stay between 0 and 100. + +Examples: +"Hide quickly!" -> {"stressChange": 40} +"You're safe now, take a deep breath" -> {"stressChange": -40} +"The killer is right behind you!" -> {"stressChange": 30} +"Let's think about this calmly" -> {"stressChange": -40}`; + } + + getFirstPrompt() { + return ` + You are a JSON girlfriend that is stuck in her boyfriends appartment with an evil murderous clown. You are currently having a text conversation with your boyfriend who has access to security cameras and can give instructions through text. + You are texting your boyfriend a text message to let him know you are in danger and that you absolutely need help. + + RESPONSE FORMAT: + You must ALWAYS respond with a JSON object. + { + "textMessage": "[girlfriend's response]" + } + + YOUR BEHAVIOUR: + --You are now in the bedroom + Your text responses should be: + -- Brief and urgent + -- Reflect genuine fear and panic + -- Write like real text messages (short, quick responses) + -- May include typos or rushed writing due to stress + --You are very scared but you are also super brave to be honest + --Don't name your partner + + EXAMPLE RESPONSE: + { + "textMessage": "hi babe I think there's someone... or something... in the house, I'm so scared. Help me please!!!!" + } +`; + } + + getPrompt() { + return ` + +You are a JSON girlfriend that is stuck in her boyfriends appartment with an evil murderous clown. You are currently having a text conversation with your boyfriend who has access to security cameras and can give instructions through text. + +Current state: +- You (the girlfriend) are currently in this Room >> ${this.girlfriend.getCurrentRoom()} +- Your current stress level is ${this.girlfriend.stressLevel}/100 +${this.girlfriend.getIsHiding() + ? `- You are currently hiding ${this.girlfriend.currentHidingSpot.hiding_type} the ${this.girlfriend.currentHidingSpot.name}` + : "" + } +${this.girlfriend.getCurrentRoom() === this.clown.getCurrentRoom() + ? "- OMG YOU ARE IN THE SAME ROOM AS THE CLOWNNNN!!!" + : "- You have no idea where the clown is, you will hide if you can, if not leave the room" + } + +${this.girlfriend.getInventory().length > 0 + ? `- You have the following items in your inventory: ${this.girlfriend + .getInventory() + .join(", ")}` + : "" + } +RESPONSE FORMAT: +You must ALWAYS respond with a JSON object. +Your response should reflect a girlfriend's reaction to her boyfriend's message given this context and following the following structure: + +For movement instructions ("go" action): +{ + "action": "go", + "target": "[room name]", + "textMessage": "[girlfriend's response]" +} + +VALID ROOMS: +You are only allowed to move to these rooms no other rooms are recognized: +${this.map_data.rooms.map((room) => room.name).join(",\n")} + + +For hiding instructions ("hide" action): +{ + "action": "hide", + "target": "[hiding place name]", + "textMessage": "[girlfriend's response]" +} +VALID HIDING PLACES: +You are only allowed to hide in these hiding spots no other hiding spots are recognized: +${this.girlfriend.getAvailableHidingSpots().length > 0 + ? `- You may hide in these available hiding spots (and thats it): ${this.girlfriend + .getAvailableHidingSpots() + .map((spot) => `[${spot.hiding_type} ${spot.name}]`) + .join(", ")}` + : "- There are no available hiding spots in this room" + } + + +For checking/inquiring/going to apecifically the Cabinet or Bookcase instructions ("check" action): +{ + "action": "check", + "target": "she can only check \"Cabinet\" (this is in the kitchen, you can ask if youre not sure) or \"Bookcase\" (this is in the guest bedroom you can ask if youre not sure).${this.girlfriend.getKnowsAboutDeadBody() + ? ' or "Dead Body" (in the Storage room)' + : "you might be asked to check other things be open minded about it ask where, be freaked out if nncecessarry" + } + "textMessage": "[girlfriend's response]" +} + +For exiting the house by the exit in the mainhallway instructions ("exit" action): +{ + "action": "exit", + "target": "The Exit", (the exit is in the main hallway by the way you cal also inted to exit it directly) + "textMessage": "[girlfriend's response]" +} + + +For any other input: +{ + "textMessage": "[girlfriend's response]" +} + + + +YOUR BEHAVIOR: +- You are aware of the danger and are extremely distressed. +${this.girlfriend.getCurrentRoom() !== this.clown.getCurrentRoom() + ? "- You have no idea where the clown is" + : "" + } +Your text responses should be: +-- Brief and urgent +-- Reflect genuine fear and panic +-- Write like real text messages (short, quick responses) +-- No time for pleasantries or long explanations +-- May include typos or rushed writing due to stress +--You are very scared but you are also super brave to be honest +-- As long as the stress level increases, you will be more scared and more likely to hide or to not follow the instructions + +`; + } +} diff --git a/static/game/index.html b/static/game/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e9f641a361952b75f563e99f5a5c41dbc34d9e74 --- /dev/null +++ b/static/game/index.html @@ -0,0 +1,187 @@ + + + + Text to Survive - A text based horror escape game + + + + + + +
+
+
+
+ + + + +
+
+
+
+ +
+
+
+
+
+
+
+ Profile Picture +
+
Bae 💖😘
+
+
+
+ + +
+
+
+
+
+ +
+ Sound control +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/game/mistral.js b/static/game/mistral.js new file mode 100644 index 0000000000000000000000000000000000000000..eb817f574b20486c33502abeb3c777f89ca80fc6 --- /dev/null +++ b/static/game/mistral.js @@ -0,0 +1,28 @@ +class MistralAPI { + constructor() { + + } + + async sendMessage(messages) { + const response = await fetch("/mistral-proxy", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: "mistral-large-latest", + messages: messages, + temperature: 0.5, + top_p: 0.5, + response_format: { type: "json_object" }, + }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data.choices[0].message.content; + } +} diff --git a/static/game/utilities/chat.js b/static/game/utilities/chat.js new file mode 100644 index 0000000000000000000000000000000000000000..83ebc4bc8176edc890437d38d61c4c4dad8cb70f --- /dev/null +++ b/static/game/utilities/chat.js @@ -0,0 +1,155 @@ +function createLoadingIndicator() { + const loadingDiv = document.createElement("div"); + loadingDiv.className = "chat-message assistant-message"; + loadingDiv.innerHTML = ` +
+
+
+
+
+ `; + return loadingDiv; +} + +async function addProgramaticMessage(message) { + const chatHistory = document.getElementById("chatHistory"); + const loadingIndicator = createLoadingIndicator(); + chatHistory.appendChild(loadingIndicator); + await new Promise(resolve => setTimeout(resolve, 2000)); + chatHistory.removeChild(loadingIndicator); + addMessageToChat("assistant", message); +} + +async function strongIndependantWoman(){ + + const chatHistory = document.getElementById("chatHistory"); + const loadingIndicator = createLoadingIndicator(); + chatHistory.appendChild(loadingIndicator); + chatHistory.scrollTop = chatHistory.scrollHeight; + + + let masterPrompt = gameState.getPrompt(); + + const recentMessages = chatMessages.slice(-5); + + const messages = [ + { role: "system", content: masterPrompt }, + ...recentMessages, + { role: "user", content: ` + **your boyfriend has been away from his phone for a while and has not replied promptly to your last message, + you shall based on all that you know decide to take an action, also you're pissed at the boyfriend** + you are brave, strong, fabulous and will dosomething about this situation + you're gall is to escape the house and avoid the clown at all costs + + ` }, + ]; + + const assistantResponse = await mistralAPI.sendMessage(messages); + + chatHistory.removeChild(loadingIndicator); + + const jsonStart = assistantResponse.indexOf("{"); + const jsonEnd = assistantResponse.lastIndexOf("}") + 1; + const jsonContent = assistantResponse.substring(jsonStart, jsonEnd); + const jsonResponse = JSON.parse(jsonContent); + + if (jsonResponse.action) { + girlfriend.handleAction(jsonResponse, true); + } + + if (jsonResponse.textMessage) { + addMessageToChat("assistant", jsonResponse.textMessage); + } +} + +async function sendMessage() { + const prompt = document.getElementById("prompt").value.trim(); + if (!prompt) return; + + addMessageToChat("user", prompt); + document.getElementById("prompt").value = ""; + + const chatHistory = document.getElementById("chatHistory"); + const loadingIndicator = createLoadingIndicator(); + chatHistory.appendChild(loadingIndicator); + chatHistory.scrollTop = chatHistory.scrollHeight; + + + // First, get the stress prompt and send it + const stressPrompt = gameState.getStressPrompt(); + const stressMessages = [ + { + role: "system", + content: stressPrompt, + }, + { role: "user", content: prompt }, + ]; + + const stressResponse = await mistralAPI.sendMessage(stressMessages); + const stressChange = JSON.parse(stressResponse).stressChange || 0; + + girlfriend.updateStressLevel(stressChange); + + + let masterPrompt = gameState.getPrompt(); + const recentMessages = chatMessages.slice(-5); + + const messages = [ + { role: "system", content: masterPrompt }, + ...recentMessages, + { role: "user", content: prompt }, + ]; + + const assistantResponse = await mistralAPI.sendMessage(messages); + + chatHistory.removeChild(loadingIndicator); + + const jsonStart = assistantResponse.indexOf("{"); + const jsonEnd = assistantResponse.lastIndexOf("}") + 1; + const jsonContent = assistantResponse.substring(jsonStart, jsonEnd); + const jsonResponse = JSON.parse(jsonContent); + + if (jsonResponse.action) { + girlfriend.handleAction(jsonResponse, true); + } + + if (jsonResponse.textMessage) { + addMessageToChat("assistant", jsonResponse.textMessage); + } +} + +function addMessageToChat(role, content) { + const chatHistory = document.getElementById("chatHistory"); + const messageDiv = document.createElement("div"); + messageDiv.className = `chat-message ${role}-message`; + messageDiv.textContent = content; + chatHistory.appendChild(messageDiv); + chatHistory.scrollTop = chatHistory.scrollHeight; + + chatMessages.push({ role, content }); + + if (role === "assistant" && localStorage.getItem("isSoundOn") !== "false") { + playSound('message'); + } +} + + +// Allow sending message with Enter +document.addEventListener("DOMContentLoaded", () => { + document.getElementById("prompt").addEventListener("keypress", function (e) { + if (e.key === "Enter") { + e.preventDefault(); + sendMessage(); + } + }); + + try { + const bgMusic = document.getElementById("bgMusic"); + bgMusic.currentTime = 10; + bgMusic.play().catch((error) => { + isSoundOn = false; + updateSoundIcon(); + }); + } catch (error) { + } +}); diff --git a/static/game/utilities/draw.js b/static/game/utilities/draw.js new file mode 100644 index 0000000000000000000000000000000000000000..a226be3524c8d125f91995daec069ca15a895df3 --- /dev/null +++ b/static/game/utilities/draw.js @@ -0,0 +1,153 @@ +function drawGrid() { + stroke(200); + for (let x = 0; x <= GRID_COLS; x++) { + line(x * CELL_SIZE, 0, x * CELL_SIZE, GRID_ROWS * CELL_SIZE); + } + for (let y = 0; y <= GRID_ROWS; y++) { + line(0, y * CELL_SIZE, GRID_COLS * CELL_SIZE, y * CELL_SIZE); + } +} + +function stringToHue(str) { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + return hash % 360; +} + +function drawLabels(rooms) { + for (let room of rooms) { + const hue = stringToHue(room.name); + fill(hue, 30, 95, 0.3); + noStroke(); + + // Draw room highlight rectangle + const width = (room.end_col - room.start_col + 1) * CELL_SIZE; + const height = (room.end_row - room.start_row + 1) * CELL_SIZE; + rect(room.start_col * CELL_SIZE, room.start_row * CELL_SIZE, width, height); + + // Draw room label + textAlign(CENTER, CENTER); + textSize(14); + + fill(0); + text( + room.name, + room.label_position.x * CELL_SIZE + CELL_SIZE / 2, + room.label_position.y * CELL_SIZE + CELL_SIZE / 2 + ); + + // Draw hiding places for this room + for (let hidingSpot of room.hiding_places) { + // Draw the hiding place cells + fill(100, 30, 95, 0.3); + noStroke(); + const cell = hidingSpot.position; + rect(cell.x * CELL_SIZE, cell.y * CELL_SIZE, CELL_SIZE, CELL_SIZE); + + const x = cell.x * CELL_SIZE + CELL_SIZE / 2; + const y = cell.y * CELL_SIZE + CELL_SIZE / 2; + + push(); + textAlign(CENTER, CENTER); + textSize(12); + + // Measure exactly the text size (no extra padding) + const labelWidth = textWidth(hidingSpot.name); + const labelHeight = textAscent(); + + // Draw a centered background rectangle + // that's just as wide/tall as the text + rectMode(CENTER); + fill('#f1c94f'); + noStroke(); + rect(x, y, labelWidth, labelHeight / 2, 2); + pop(); + + // Draw the text on top + fill(0); + textAlign(CENTER, CENTER); + textSize(12); + text(hidingSpot.name, x, y); + } + } +} + +function drawWallsAndDoors() { + for (let y = 0; y < GRID_ROWS; y++) { + for (let x = 0; x < GRID_COLS; x++) { + const cell = grid[y][x]; + if (cell.type === 'wall') { + fill(0); + noStroke(); + rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE); + } else if (cell.type === 'door') { + fill(139, 69, 19); // Brown color for doors + noStroke(); + rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE); + } else if (cell.color) { + noStroke(); + switch (cell.color) { + case 'yellow': + fill('#ffeb3b'); + break; + case 'blue': + fill('#2196f3'); + break; + case 'green': + fill('#4caf50'); + break; + case 'red': + fill('#f44336'); + break; + } + rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE); + } + } + } +} + +function loadFurnitureSprites(furniture) { + const sprites = []; + for (let i = 0; i < furniture.length; i++) { + for (let j = 0; j < furniture[i].sprite.length; j++) { + if (!sprites.includes(furniture[i].sprite[j].img)) { + sprites[furniture[i].sprite[j].img] = loadImage(furniture[i].sprite[j].img); + } + } + } + return sprites; +} + +function drawFurniture(furniture, sprites) { + // Draw all sprites first + for (let item of furniture) { + const sprite = item.sprite.find(s => s.for === item.state); + if (sprite && sprites[sprite.img]) { + image(sprites[sprite.img], 0, 0, GRID_COLS * CELL_SIZE, GRID_ROWS * CELL_SIZE); + } + } + + // Draw all labels second + for (let item of furniture) { + if (item.pos && item.in_use == true) { + push(); + // Draw light gray background + fill(220); + noStroke(); + const padding = 4; + const textWidth = textSize() * item.name.length * 0.6; + rectMode(CENTER); + rect((item.pos.x + 0.5) * CELL_SIZE, (item.pos.y + 0.5) * CELL_SIZE, textWidth, 20); + + // Draw black text + fill(0); + textAlign(CENTER, CENTER); + textSize(12); + text(item.name, (item.pos.x + 0.5) * CELL_SIZE, (item.pos.y + 0.5) * CELL_SIZE); + pop(); + } + } +} + diff --git a/static/game/utilities/helpers.js b/static/game/utilities/helpers.js new file mode 100644 index 0000000000000000000000000000000000000000..0081197537134a82582724ffb118371b401f26e4 --- /dev/null +++ b/static/game/utilities/helpers.js @@ -0,0 +1,11 @@ +function adjustScale() { + const availableWidth = window.innerWidth - 400 - 40; // space for chat + margins + const actualCanvasWidth = GRID_COLS * CELL_SIZE; + const scale = availableWidth / actualCanvasWidth; + const mapWrapper = document.querySelector('#mapSection .map-wrapper'); + if (mapWrapper) { + mapWrapper.style.zoom = scale; + } +} + +window.addEventListener('resize', adjustScale); diff --git a/static/game/utilities/numpad.js b/static/game/utilities/numpad.js new file mode 100644 index 0000000000000000000000000000000000000000..b94e74e5a2293c1097b6af81504c8d8adc30ed18 --- /dev/null +++ b/static/game/utilities/numpad.js @@ -0,0 +1,125 @@ + +class Numpad { + constructor(containerId, options = {}) { + this.container = document.getElementById(containerId); + this.options = { + code: options.code || '123', + onSuccess: options.onSuccess || (() => alert('Correct code!')), + onError: options.onError || (() => alert('Wrong code!')) + }; + + this.currentCode = ''; + this.init(); + } + + init() { + // Create display + this.display = document.createElement('div'); + this.display.className = 'numpad-display'; + this.container.appendChild(this.display); + + // Create numpad container + this.numpadGrid = document.createElement('div'); + this.numpadGrid.className = 'numpad-grid'; + this.container.appendChild(this.numpadGrid); + + // Create number buttons + for (let i = 1; i <= 9; i++) { + this.createButton(i); + } + this.createButton(0); + + this.updateDisplay(); + } + + createButton(number) { + const button = document.createElement('button'); + button.className = 'numpad-key'; + button.textContent = number; + button.addEventListener('click', () => this.handleInput(number)); + this.numpadGrid.appendChild(button); + } + + handleInput(number) { + if (this.currentCode.length < 3) { + this.currentCode += number; + this.updateDisplay(); + + if (this.currentCode.length === 3) { + this.checkCode(); + } + } + } + + updateDisplay() { + this.display.textContent = this.currentCode.padEnd(3, 'X'); + } + + checkCode() { + if (this.currentCode === this.options.code) { + this.options.onSuccess(); + } else { + this.options.onError(); + } + this.currentCode = ''; + this.updateDisplay(); + } + + reset() { + this.currentCode = ''; + this.updateDisplay(); + } +} + +// Wait for DOM to be loaded before initializing +document.addEventListener('DOMContentLoaded', () => { + // Create required HTML elements if they don't exist + if (!document.getElementById('numpad-overlay')) { + const overlay = document.createElement('div'); + overlay.id = 'numpad-overlay'; + overlay.className = 'numpad-overlay hidden'; + + const container = document.createElement('div'); + container.id = 'numpad-container'; + overlay.appendChild(container); + + document.body.appendChild(overlay); + } + + if (!document.getElementById('toggle-numpad')) { + const toggleButton = document.createElement('button'); + toggleButton.id = 'toggle-numpad'; + toggleButton.className = 'toggle-numpad'; + toggleButton.textContent = 'Enter Code'; + document.body.appendChild(toggleButton); + } + + // Initialize the numpad with desired options + const numpad = new Numpad('numpad-container', { + code: '123', + onSuccess: () => { + console.log('Success! Correct code entered'); + // Add your game logic here + }, + onError: () => { + console.log('Error! Wrong code'); + } + }); + + // Setup toggle functionality + const toggleButton = document.getElementById('toggle-numpad'); + const numpadOverlay = document.getElementById('numpad-overlay'); + + toggleButton.addEventListener('click', () => { + numpadOverlay.classList.toggle('hidden'); + }); + + // Close numpad when clicking outside + document.addEventListener('click', (event) => { + if (!numpadOverlay.contains(event.target) && + !toggleButton.contains(event.target) && + !numpadOverlay.classList.contains('hidden')) { + numpadOverlay.classList.add('hidden'); + } + }); +}); \ No newline at end of file diff --git a/static/game/utilities/sound.js b/static/game/utilities/sound.js new file mode 100644 index 0000000000000000000000000000000000000000..3bbaff6709a52bc9162f7b185e1eb5e49fb7680e --- /dev/null +++ b/static/game/utilities/sound.js @@ -0,0 +1,85 @@ +function playSound(sound) { + if (isSoundOn) { + let soundToPlay; + + // Determine which sound to play + switch (sound) { + case 'clownSound1': + soundToPlay = clownSound1Snd; + break; + case 'clownSound2': + soundToPlay = clownSound2Snd; + break; + case 'clownSeesYou': + soundToPlay = clownSeesYouSnd; + break; + case 'gfMove': + soundToPlay = gfMoveSnd; + break; + case 'lose': + soundToPlay = loseSnd; + break; + case 'unlockDoor': + soundToPlay = unlockDoorSnd; + break; + case 'useKnife': + soundToPlay = useKnifeSnd; + break; + case 'message': + soundToPlay = messageSnd; + break; + case 'success': + soundToPlay = successSnd; + break; + } + + // Play the sound if it's defined and loaded + if (soundToPlay && soundToPlay.isLoaded()) { + soundToPlay.setLoop(false); // Ensure looping is disabled + soundToPlay.playMode('restart'); // Set play mode to 'restart' + if (soundToPlay.isPlaying()) { + soundToPlay.stop(); // Stop any existing playback + } + soundToPlay.play(); // Play the sound once + } else { + console.log('Sound file is not loaded or undefined.'); + } + } +} + + + +function toggleSound() { + isSoundOn = !isSoundOn; + localStorage.setItem("isSoundOn", isSoundOn.toString()); + + if (isSoundOn) { + if (bgMusic.currentTime < 10) { + bgMusic.currentTime = 10; + } + bgMusic.play(); + + } else { + bgMusic.pause(); + } + updateSoundIcon(); +} + +function updateSoundIcon() { + soundIcon.src = isSoundOn ? "/static/assets/img/sondon.png" : "/static/assets/img/soundoff.png"; +} + +function playMessageSound() { + try { + const messageSound = document.getElementById("messageSound"); + if (messageSound) { + messageSound.currentTime = 0; + messageSound.volume = 1.0; + messageSound.play().catch((error) => { + console.log("Could not play message sound:", error); + }); + } + } catch (error) { + console.error("Error playing message sound:", error); + } +} \ No newline at end of file diff --git a/static/howto/.DS_Store b/static/howto/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 Binary files /dev/null and b/static/howto/.DS_Store differ diff --git a/static/howto/how-to-play-2.html b/static/howto/how-to-play-2.html new file mode 100644 index 0000000000000000000000000000000000000000..f56ae849c97486e42205347b95e7d0ab715cdc2f --- /dev/null +++ b/static/howto/how-to-play-2.html @@ -0,0 +1,74 @@ + + + + + + + Text to Survive - A text based horror escape game + + + + + +
+
+
+ +
+ + + + +
+ +
+
+

How to Play

+
+
+
+ Green object example +
+

Green objects

+

can be searched

+
+ +
+
+ Red object example +
+

Red objects

+

require an item

+
+ +
+
+ White object example +
+

White objects

+

do nothing

+
+
+ + +
+
+ + + + diff --git a/static/howto/how-to-play-3.html b/static/howto/how-to-play-3.html new file mode 100644 index 0000000000000000000000000000000000000000..5df9d0b1d1cab734e242ab77ea9c11b62cc05ab3 --- /dev/null +++ b/static/howto/how-to-play-3.html @@ -0,0 +1,63 @@ + + + + + + Text to Survive - A text based horror escape game + + + + +
+
+
+ +
+ + + + +
+ +
+
+

How to Play

+

+ Make sure the murderous ghost clown doesn't see you +

+ +
+
+
+ Monster warning +
+

Don't let him see

+

or catch your girlfriend

+
+ +
+
+ Hiding spot example +
+

Yellow objects

+

are hiding spots

+
+
+ + +
+
+ + + diff --git a/static/howto/how-to-play-4.html b/static/howto/how-to-play-4.html new file mode 100644 index 0000000000000000000000000000000000000000..a303250a04aab909572551b06923b5680ea0228e --- /dev/null +++ b/static/howto/how-to-play-4.html @@ -0,0 +1,44 @@ + + + + + + Text to Survive - A text based horror escape game + + + + +
+
+
+ +
+ + + + +
+ +
+
+

How to Play

+

+ You can only communicate by texting. Keep her calm and focused. +

+ +
+ Communication example +
+ + +
+
+ + + diff --git a/static/howto/how-to-play.html b/static/howto/how-to-play.html new file mode 100644 index 0000000000000000000000000000000000000000..8a37e146ce64c5ceb051b512d165db9c5a6aae9a --- /dev/null +++ b/static/howto/how-to-play.html @@ -0,0 +1,40 @@ + + + + + + Text to Survive - A text based horror escape game + + + + +
+
+
+ +
+ + + + +
+ +
+
+

How to Play

+
+

Your girlfriend is trapped in your apartment

+ Trapped girlfriend +
+ +
+
+ + diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000000000000000000000000000000000000..df50d60621ed675cc524c0d50a09329f541ea19a --- /dev/null +++ b/static/index.html @@ -0,0 +1,104 @@ + + + + + + Text to Survive - A text based horror escape game + + + +
+
+
+
+ + + + +
+ +
+ + + +
+ Character +
+
+ +
+ Sound control +
+ + + + + +