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/static/.DS_Store b/static/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..7ccddc90ebba382fdec81b52e2d0effff29e4866
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..e8a9d331f978a801724362b7d9c863fdf3a5ac44
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/index-style.css b/static/assets/css/index-style.css
new file mode 100644
index 0000000000000000000000000000000000000000..af4955694973568ebd35c34e2edb5f72aeaa36af
--- /dev/null
+++ b/static/assets/css/index-style.css
@@ -0,0 +1,446 @@
+@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;
+ margin-top: 200px;
+}
+
+.logo {
+ position: absolute;
+ top: 40px;
+ left: 50%;
+ transform: translateX(-50%);
+ animation: flicker 4s linear infinite;
+}
+
+.logo img {
+ width: 230px;
+ height: auto;
+}
+
+.menu {
+ display: flex;
+ 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: 40px auto;
+ padding: 20px;
+}
+
+.content h1 {
+ font-family: "HorrorBrush", cursive;
+ font-size: 64px;
+ color: #9b0000;
+ position: absolute;
+ top: 40px;
+ left: 50%;
+ transform: translateX(-50%);
+ letter-spacing: 4px;
+}
+
+.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;
+}
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..725a257e0e67d0ddb2a3f703bbd8bfa7a9710151
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..ed53d85b57af3f59a874e1f6e6f1c5e4fad4bca8
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/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/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/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..918f6df68e2de6e8597fbf4184f8be1efeb20dfd
--- /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 = 600; // Starting movement delay in milliseconds
+ this.minSpeed = 100; // 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..ff1c9c567233cc1431864eaa7ebf09f5b21d52db
--- /dev/null
+++ b/static/game/elmnts/furniture.js
@@ -0,0 +1,305 @@
+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":"Entrance",
+ "room":"Main Hallway",
+ "pos":{
+ "x": 15,
+ "y": 18
+ },
+ "state":"locked",
+ "locked_message":"I seem to need a key to get out of here",
+ "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..2b09b2a131bc3fe656534d5254906273b72b3438
--- /dev/null
+++ b/static/game/elmnts/girlfriend.js
@@ -0,0 +1,117 @@
+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;
+ }
+
+ 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;
+ }
+ }
+
+ 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 : [];
+ }
+
+ 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())
+ );
+ console.log(hidingSpot);
+ if (!hidingSpot) return;
+
+ // Move to the hiding spot position
+ this.moveToPosition(hidingSpot.position, () => {
+ this.currentHidingSpot = hidingSpotName;
+ });
+ }
+}
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..87c46b1172079edfce7811743081fc9bd7f80abb
--- /dev/null
+++ b/static/game/game.js
@@ -0,0 +1,70 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/static/game/gameState.js b/static/game/gameState.js
new file mode 100644
index 0000000000000000000000000000000000000000..2c624b8b25e0565f6aaa015d21ecaeacd435cae4
--- /dev/null
+++ b/static/game/gameState.js
@@ -0,0 +1,140 @@
+// 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;
+ }
+
+ 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:
+
+1. For movement instructions ("go" action):
+{
+ "action": "go",
+ "target": "[room name]",
+ "textMessage": "[girlfriend's response]"
+}
+
+2. For hiding instructions ("hide" action):
+{
+ "action": "hide",
+ "target": "[hiding place name]",
+ "textMessage": "[girlfriend's response]"
+}
+
+3. 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..827f341571e897a9fc35ab0db4730fe7dc95c94a
--- /dev/null
+++ b/static/game/index.html
@@ -0,0 +1,86 @@
+
+
+
+
+ The Last Message - A text based horror game
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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..a12788e166ad318577acaa12b82861b0f3e4c870
--- /dev/null
+++ b/static/game/utilities/chat.js
@@ -0,0 +1,120 @@
+function createLoadingIndicator() {
+ const loadingDiv = document.createElement("div");
+ loadingDiv.className = "chat-message assistant-message";
+ loadingDiv.innerHTML = `
+
+ `;
+ return loadingDiv;
+}
+
+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);
+
+ // Get stress level change
+ const stressResponse = await mistralAPI.sendMessage(stressMessages);
+ const stressChange = JSON.parse(stressResponse).stressChange || 0;
+
+ // Update girlfriend's stress level
+ girlfriend.updateStressLevel(stressChange);
+ console.log("girlfriend.stressLevel", girlfriend.stressLevel);
+
+ console.log("stressChange", stressChange);
+
+ // Now continue with the regular chat flow
+ 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..a60f0e5e02429a6a5843b5371485e1141764d3df
--- /dev/null
+++ b/static/game/utilities/draw.js
@@ -0,0 +1,151 @@
+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
+ fill(0);
+ textAlign(CENTER, CENTER);
+ textSize(14);
+ 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() + textDescent();
+
+ // 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) {
+ console.log(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();
+ }
+ }
+}
\ 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/how-to-play.html b/static/how-to-play.html
new file mode 100644
index 0000000000000000000000000000000000000000..30ef1bab904b9ec359efb35150635a6be41f122c
--- /dev/null
+++ b/static/how-to-play.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Through Their Eyes - How to Play
+
+
+
+
+
+
+
+
+
+
How to Play
+
+
+
+ Back to Menu
+
+
+
+
+
diff --git a/static/index.html b/static/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..7f654620d7d8612cc7cdec67dc64f46a9c1cfe12
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,103 @@
+
+
+
+
+
+ Through Their Eyes
+
+
+
+
+
+
+
+
+

+
+
+
+
+

+
+
+
+
+
+
+
+
+
+