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 cb9d8cc9af5e8bd1c84920d495c5a7b55bc10cf2..69b4997cf8c9b3f15303da50a7eeb48290b2c8da 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ --- -title: TestSpace -emoji: 🦀 -colorFrom: purple +title: The Last Message +emoji: 📱 +colorFrom: gray colorTo: red -sdk: docker +sdk: gradio +sdk_version: "3.50.2" +app_file: app.py pinned: false -short_description: test --- -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/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/front_edits/.DS_Store b/front_edits/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..24300a81018b82dd1c553a8681cadb53cabd58d0 Binary files /dev/null and b/front_edits/.DS_Store differ diff --git a/front_edits/CSS/game-style.css b/front_edits/CSS/game-style.css new file mode 100644 index 0000000000000000000000000000000000000000..ab2a539bfbed26326707d4d3179a1234f16f5973 --- /dev/null +++ b/front_edits/CSS/game-style.css @@ -0,0 +1,550 @@ +/* Layout */ +body { + margin: 0; + font-family: Arial, sans-serif; + display: flex; + background-color: #220a0a; + height: 100vh; + position: relative; + overflow: hidden; +} + +#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); + transition: background-color 0.3s ease; +} + +.user-message { + background: #007aff; + color: white; + margin-left: auto; + border-radius: 20px 20px 4px 20px; +} + +.assistant-message { + color: black; + 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; +} + +/* Update input styles */ +input { + border-radius: 10px; +} + +@keyframes lightShake { + 0% { + transform: translateX(0); + } + 25% { + transform: translateX(2px); + } + 50% { + transform: translateX(-2px); + } + 75% { + transform: translateX(2px); + } + 100% { + transform: translateX(0); + } +} + +@keyframes heavyShake { + 0% { + transform: translateX(0); + } + 25% { + transform: translateX(4px); + } + 50% { + transform: translateX(-4px); + } + 75% { + transform: translateX(4px); + } + 100% { + transform: translateX(0); + } +} + +.light-shake { + animation: lightShake 0.5s ease-in-out infinite; +} + +.heavy-shake { + animation: heavyShake 0.5s ease-in-out infinite; +} diff --git a/front_edits/CSS/how-to-play.css b/front_edits/CSS/how-to-play.css new file mode 100644 index 0000000000000000000000000000000000000000..d0ec455b92db586dce3874554b6ebb13b6ae108f --- /dev/null +++ b/front_edits/CSS/how-to-play.css @@ -0,0 +1,429 @@ +/* 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; +} + +.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/front_edits/CSS/index-style.css b/front_edits/CSS/index-style.css new file mode 100644 index 0000000000000000000000000000000000000000..2ad3c5666ca1f1e5c0bf623f8ebcbc34fd676f2f --- /dev/null +++ b/front_edits/CSS/index-style.css @@ -0,0 +1,482 @@ +@font-face { + font-family: "HorrorBrush"; + src: url("/static/assets/fonts/horrorbrush.ttf") format("truetype"); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +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: 230px; + height: auto; +} + +.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/front_edits/Img/.DS_Store b/front_edits/Img/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f68f9823375a6746717c3b8ef9bb29e7bf880d80 Binary files /dev/null and b/front_edits/Img/.DS_Store differ diff --git a/front_edits/Img/how_to_play/CoffeeTableUsable_1.png b/front_edits/Img/how_to_play/CoffeeTableUsable_1.png new file mode 100644 index 0000000000000000000000000000000000000000..cfcb4cc07b1d0a0d44110eb4e8ae490e004ed56a Binary files /dev/null and b/front_edits/Img/how_to_play/CoffeeTableUsable_1.png differ diff --git a/front_edits/Img/how_to_play/TVUnused_1.png b/front_edits/Img/how_to_play/TVUnused_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bd1152029314b627e0c08f98c66105114766ef89 Binary files /dev/null and b/front_edits/Img/how_to_play/TVUnused_1.png differ diff --git a/front_edits/Img/how_to_play/Untitled_Artwork.png b/front_edits/Img/how_to_play/Untitled_Artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..32f51f696c5a8138b393bf3cfa6db96a32c33688 Binary files /dev/null and b/front_edits/Img/how_to_play/Untitled_Artwork.png differ diff --git a/front_edits/Img/how_to_play/bed.png b/front_edits/Img/how_to_play/bed.png new file mode 100644 index 0000000000000000000000000000000000000000..87e2a5db1f43fbfc79fc79048ec3e84b0a99ac08 Binary files /dev/null and b/front_edits/Img/how_to_play/bed.png differ diff --git a/front_edits/Img/how_to_play/monster.png b/front_edits/Img/how_to_play/monster.png new file mode 100644 index 0000000000000000000000000000000000000000..349fcbf8c16837e0f3db7dbbdd5d287c33b9b097 Binary files /dev/null and b/front_edits/Img/how_to_play/monster.png differ diff --git a/front_edits/Img/how_to_play/video.gif b/front_edits/Img/how_to_play/video.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f123760d52a31399aa49a43c3ff67ee60319bcc Binary files /dev/null and b/front_edits/Img/how_to_play/video.gif differ diff --git a/front_edits/Img/profil_pictures/.DS_Store b/front_edits/Img/profil_pictures/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9a52327ddf2a7819ee3acbfaabf22b41180cd78c Binary files /dev/null and b/front_edits/Img/profil_pictures/.DS_Store differ diff --git a/front_edits/Img/profil_pictures/bit_stress.webp b/front_edits/Img/profil_pictures/bit_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..9fca85356b82d03df99d422e060f0212e744f057 Binary files /dev/null and b/front_edits/Img/profil_pictures/bit_stress.webp differ diff --git a/front_edits/Img/profil_pictures/no_stress.webp b/front_edits/Img/profil_pictures/no_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..5a9e2905e23e087499df1ad82b9016e1496f379a Binary files /dev/null and b/front_edits/Img/profil_pictures/no_stress.webp differ diff --git a/front_edits/Img/profil_pictures/very_stress.webp b/front_edits/Img/profil_pictures/very_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..731c4a78358828fc386f107ba956767c39312216 Binary files /dev/null and b/front_edits/Img/profil_pictures/very_stress.webp differ diff --git a/front_edits/howto/.DS_Store b/front_edits/howto/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 Binary files /dev/null and b/front_edits/howto/.DS_Store differ diff --git a/front_edits/howto/how-to-play-2.html b/front_edits/howto/how-to-play-2.html new file mode 100644 index 0000000000000000000000000000000000000000..84733d8070a7e11209daed5d2c0630b8aeeeaf4a --- /dev/null +++ b/front_edits/howto/how-to-play-2.html @@ -0,0 +1,72 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

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/front_edits/howto/how-to-play-3.html b/front_edits/howto/how-to-play-3.html new file mode 100644 index 0000000000000000000000000000000000000000..9e7802154457e76676feb81aef883af72d8aabb0 --- /dev/null +++ b/front_edits/howto/how-to-play-3.html @@ -0,0 +1,64 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

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/front_edits/howto/how-to-play-4.html b/front_edits/howto/how-to-play-4.html new file mode 100644 index 0000000000000000000000000000000000000000..8873b03086df4859a1c0c5f46b8feff3c2a05248 --- /dev/null +++ b/front_edits/howto/how-to-play-4.html @@ -0,0 +1,45 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

How to Play

+

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

+ +
+ Communication example +
+ + +
+
+ + + + diff --git a/front_edits/howto/how-to-play.html b/front_edits/howto/how-to-play.html new file mode 100644 index 0000000000000000000000000000000000000000..5a002ba05b26d24cb4056fef3ca12daa2a47fd82 --- /dev/null +++ b/front_edits/howto/how-to-play.html @@ -0,0 +1,42 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

How to Play

+
+

Your girlfriend is trapped in your apartment

+ Trapped girlfriend +
+ +
+
+ + + + diff --git a/front_edits/index.html b/front_edits/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e6ee32aebdef42959ad77b8ac2ad7cc557584464 --- /dev/null +++ b/front_edits/index.html @@ -0,0 +1,103 @@ + + + + + + Through Their Eyes + + + +
+
+
+
+ + + + +
+ +
+ + + +
+ Character +
+
+ +
+ Sound control +
+ + + + + + 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..f50ee149f9ef83c774f3e1bf913607e7458906a1 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..10fb9b27340fe5cea148006275ef916eaa4e8885 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..5fb4ddf23e4b4cc733e5112db7908c38cc515dfb --- /dev/null +++ b/static/assets/css/game-style.css @@ -0,0 +1,564 @@ + /* Layout */ + body { + margin: 0; + font-family: Arial, sans-serif; + display: flex; + background-color: #220a0a; + height: 100vh; + position: relative; + overflow: hidden; +} + +#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..d0ec455b92db586dce3874554b6ebb13b6ae108f --- /dev/null +++ b/static/assets/css/how-to-play.css @@ -0,0 +1,429 @@ +/* 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; +} + +.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..2ad3c5666ca1f1e5c0bf623f8ebcbc34fd676f2f --- /dev/null +++ b/static/assets/css/index-style.css @@ -0,0 +1,482 @@ +@font-face { + font-family: "HorrorBrush"; + src: url("/static/assets/fonts/horrorbrush.ttf") format("truetype"); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +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: 230px; + height: auto; +} + +.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/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/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/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/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/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/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/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/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/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/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/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/bit_stress.webp b/static/assets/img/profil_pictures/bit_stress.webp new file mode 100644 index 0000000000000000000000000000000000000000..9fca85356b82d03df99d422e060f0212e744f057 Binary files /dev/null and b/static/assets/img/profil_pictures/bit_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/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/walking.mp3 b/static/assets/sounds/walking.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..25516f159034a4bbca0a0e370f4172350b9dc75c Binary files /dev/null and b/static/assets/sounds/walking.mp3 differ diff --git a/static/game/.DS_Store b/static/game/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..41e020c8a49ed3c28c9e9376e5a074e8fcaa49a7 Binary files /dev/null and b/static/game/.DS_Store differ diff --git a/static/game/dev/index.html b/static/game/dev/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c1acf56f4f0ed35ed5fddf0516c79f81210d1f49 --- /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..b3bed138c340ea4083cafecff556b4fe6364e4f9 --- /dev/null +++ b/static/game/elmnts/character.js @@ -0,0 +1,133 @@ +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; + } + + 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.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..5b636831ff341728629de93754a9072710b43b7e --- /dev/null +++ b/static/game/elmnts/clown.js @@ -0,0 +1,161 @@ +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(); + }, 3000); + } + + 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; + 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..1dfee737cc53f1c5be5e41b078c5d9163db526bf --- /dev/null +++ b/static/game/elmnts/furniture.js @@ -0,0 +1,307 @@ +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, + "key":"Desk Note", + "reward":"Flashlight", + "locked_msg":"There's like a shit ton of books in here in a bunch of different colors, i have no idea what i'm looking for... 😵‍💫", + "unlocked_msg":"Got the pink book. Looks like it's hollowed out and there's a flashlight inside 🔦. WEIRD?? there's also some writing here that says the mouth holds the key. omg that's so creepy wtf", + "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": "off", + "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. fuck. IS THIS A DEAD BODY?? WHO ARE YOU??", + "unlocked_msg":"no no no no no ew ew ew ew whyyyyyyy...😭😭😭 theres a note here that says NEO. why the f was there a note in its mouth??", + "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", + "search_msg":"Omg a knife 🔪... ill take it but there's no way i'm fighting that thing u can't make me ok?? ", + "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":"Storage Shade", + "room":"Storage", + "wall_pos": { + "x": 6, + "y": 3 + }, + "stop_pos":{ + "x": 7, + "y": 3 + }, + "state":"locked", + "in_use": false, + "locked_message":"No no no theres no way im going in there its dark as shit and its already fking scary out here ", + "unlocked_message":"Ok here goes...", + "sprite": [ + { + "for": "locked", + "img": "/assets/img/appartment/StorageLocked.png" + }, + { + "for": "unlocked", + "img": "/assets/img/appartment/StorageUnlocked.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..4798c722a0be9efafd5331bd84bad367a44acbe0 --- /dev/null +++ b/static/game/elmnts/girlfriend.js @@ -0,0 +1,146 @@ +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 = []; + } + + 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) { + // 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) { + switch (response.action) { + case "go": + this.moveToRoom(response.target); + break; + case "hide": + this.hide(response.target); + break; + case "exit": + this.exit(); + 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 + ); + } + + 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 { + addProgramaticMessage(the_exit.unlocked_message); + } + }); + } +} 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..16693b87400df0c8637839de62ebfeb3b33f609d --- /dev/null +++ b/static/game/game.js @@ -0,0 +1,73 @@ +const CELL_SIZE = 50; +let GRID_COLS = 0; +let GRID_ROWS = 0; +let grid = []; + +let gameState; + +let baseMapImg; +let girlfriendImg; +let clownImg; + +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 setup() { + 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(); +} + +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); + } +} diff --git a/static/game/gameState.js b/static/game/gameState.js new file mode 100644 index 0000000000000000000000000000000000000000..8bec84285879586ce71caffe4a317404fc862c7c --- /dev/null +++ b/static/game/gameState.js @@ -0,0 +1,155 @@ +// Game state management +class GameState { + constructor(map_data) { + this.map_data = map_data; + this.girlfriend = null; + this.clown = null; + + this.searchTargets = [ + "dresser", + "desk", + "bookcase", + "cabinet", + "dead body", + "fridge", + "stove", + "coffee table", + ]; + + this.items = [ + "lock pick", + "book note", + "flashlight", + "knife", + "oil", + "remote", + ]; + + this.useTargets = [ + "bedroom door", + "bookcase", + "storage door", + "dead body", + "coffee table", + "TV", + ]; + + this.inventory = []; + this.isHiding = false; + } + + getTheExit() { + return this.map_data.furniture.find(furniture => furniture.name === "The Exit"); + } + + getStorage() { + return this.map_data.furniture.find(furniture => furniture.name === "Storage"); + } + + 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": -35} +"The killer is right behind you!" -> {"stressChange": 50} +"Let's think about this calmly" -> {"stressChange": -30}`; + } + + 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" +} + + +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]" +} + +For hiding instructions ("hide" action): +{ + "action": "hide", + "target": "[hiding place name]", + "textMessage": "[girlfriend's response]" +} + +For exiting the house by the exit in the mainhallway instructions ("exit" action): +{ + "action": "exit", + "target": "The Exit", + "textMessage": "[girlfriend's response]" +} + +For any other input: +{ + "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")} + +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" +} + +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..c167137671b4ba1dd449b1d3e870fd79876e1d34 --- /dev/null +++ b/static/game/index.html @@ -0,0 +1,85 @@ + + + + + The Last Message - A text based horror game + + + + +
+
+
+
+ + + + +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
Bae 💖😘
+
+
+
+ + +
+
+
+
+
+ +
+ Sound control +
+ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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..7516a184a4d91da1ca7d5c6e52c9baea441e4868 --- /dev/null +++ b/static/game/utilities/chat.js @@ -0,0 +1,124 @@ +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 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); + + // First, get the stress prompt and send it + const stressPrompt = gameState.getStressPrompt(); + const stressMessages = [ + { + role: "system", + content: stressPrompt, + }, + { role: "user", content: prompt }, + ]; + console.log("stressMessages", stressMessages); + + 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); + } + + 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") { + playMessageSound(); + } +} + +// Update Message function to use girlfriend instance +function Message() { + if (girlfriend) { + // Play message sound when sending a message + if (localStorage.getItem("isSoundOn") !== "false") { + playMessageSound(); + } + sendMessage(); + } +} + +// Allow sending message with Enter +document.addEventListener("DOMContentLoaded", () => { + document.getElementById("prompt").addEventListener("keypress", function (e) { + if (e.key === "Enter") { + e.preventDefault(); + Message(); + } + }); + + try { + const bgMusic = document.getElementById("bgMusic"); + bgMusic.currentTime = 10; + bgMusic.play().catch((error) => { + console.log("Autoplay prevented:", error); + isSoundOn = false; + updateSoundIcon(); + }); + } catch (error) { + console.error("Error playing audio:", error); + } +}); diff --git a/static/game/utilities/draw.js b/static/game/utilities/draw.js new file mode 100644 index 0000000000000000000000000000000000000000..fc502fc3edbd1f88981fcd5fd7880452c50ab053 --- /dev/null +++ b/static/game/utilities/draw.js @@ -0,0 +1,174 @@ +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); + + if (room.name === 'Storage') { + // For Storage room, add white background + const labelWidth = textWidth(room.name); + const labelHeight = textAscent() + textDescent(); + push(); + rectMode(CENTER); + fill(255); // White background + noStroke(); + rect( + room.label_position.x * CELL_SIZE + CELL_SIZE / 2, + room.label_position.y * CELL_SIZE + CELL_SIZE / 2, + labelWidth, + labelHeight + ); + pop(); + } + + 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(); + } + } +} + +function drawStorage(storage) { + let storage = gameState.getStorage(); + +} \ No newline at end of file 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..0d7ff64d400d94d40741400d976fbb5bf5baaff4 --- /dev/null +++ b/static/game/utilities/sound.js @@ -0,0 +1,54 @@ +// Make handleWalkingSound globally available +function handleWalkingSound (isWalking) { + const walkingSound = document.getElementById("walkingSound"); + if (!walkingSound) return; + + if (isWalking && localStorage.getItem("isSoundOn") !== "false") { + walkingSound.currentTime = 0; + walkingSound.volume = 0.3; + walkingSound.play().catch((error) => { + console.log("Could not play walking sound:", error); + }); + } else { + walkingSound.pause(); + } +} + +// Update toggleSound to check girlfriend's movement status +function toggleSound() { + isSoundOn = !isSoundOn; + localStorage.setItem("isSoundOn", isSoundOn.toString()); + + if (isSoundOn) { + if (bgMusic.currentTime < 10) { + bgMusic.currentTime = 10; + } + bgMusic.play(); + if (girlfriend && girlfriend.isMoving) { + handleWalkingSound(true); + } + } else { + bgMusic.pause(); + handleWalkingSound(false); + } + 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..c6e513bdce36f6b5fac1d94d545475fbaff6c4ad --- /dev/null +++ b/static/howto/how-to-play-2.html @@ -0,0 +1,71 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

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..d32ffa9b148bb60ccc614ee0602235f216007c0c --- /dev/null +++ b/static/howto/how-to-play-3.html @@ -0,0 +1,63 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

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..c4e511caa3b9ac187bad8d00687f6a6c28348be9 --- /dev/null +++ b/static/howto/how-to-play-4.html @@ -0,0 +1,44 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

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..30ffaa16e1f9227347e7e856fa4cff1e3776f758 --- /dev/null +++ b/static/howto/how-to-play.html @@ -0,0 +1,40 @@ + + + + + + Through Their Eyes - How to Play + + + + +
+
+
+ +
+ + + + +
+ +
+
+

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..2a1b46726e1af3844de9796030e2d7494ddcf993 --- /dev/null +++ b/static/index.html @@ -0,0 +1,103 @@ + + + + + + Through Their Eyes + + + +
+
+
+
+ + + + +
+ +
+ + + +
+ Character +
+
+ +
+ Sound control +
+ + + + + +