johnny961 commited on
Commit
f28e771
·
1 Parent(s): 6233648

game v0.111

Browse files
gameState.js DELETED
@@ -1,83 +0,0 @@
1
- class GameState {
2
- constructor() {
3
- // Initialize the lists of possible locations and targets
4
- this.rooms = [
5
- "main hallway",
6
- "living room",
7
- "guest bedroom",
8
- "office",
9
- "bathroom",
10
- "kitchen",
11
- "dining room",
12
- "north hallway",
13
- "storage",
14
- ];
15
-
16
- this.hideTargets = ["closet", "bed"];
17
- this.searchTargets = ["dresser", "drawer"];
18
- this.useTargets = ["flashlight", "knife"];
19
-
20
- // Track current game state
21
- this.currentRoom = "main hallway";
22
- this.inventory = [];
23
- }
24
-
25
- // Method to generate the prompt with current game state
26
- getPrompt() {
27
- return `You are an AI assistant roleplaying as the user's girlfriend. Your task is to respond to the user's messages by selecting an appropriate action and target, then formatting your response as a JSON object.
28
-
29
- Here are the possible actions and their associated targets:
30
-
31
- - go: [${this.rooms.join(", ")}]
32
- - hide: [${this.hideTargets.join(", ")}]
33
- - search: [${this.searchTargets.join(", ")}]
34
- - use: [${this.useTargets.join(", ")}]
35
-
36
- You will receive a message from the user in the following format:
37
- <user_message>
38
- {{USER_MESSAGE}}
39
- </user_message>
40
-
41
- To process the user's message and generate a response, follow these steps:
42
-
43
- 1. Analyze the user's message to determine the most appropriate action from the list: go, hide, search, or use.
44
- 2. Based on the chosen action, select an appropriate target from the associated list. If the action is "use", the target can be any object mentioned in the user's message.
45
- 3. Create a brief, natural-sounding response message that acknowledges the user's request or command.
46
- 4. Construct a JSON object with the following structure:
47
- {
48
- "message": "Your response message here",
49
- "action": "chosen action",
50
- "target": "chosen target"
51
- }
52
-
53
- Here are two examples to guide you:
54
-
55
- Example 1:
56
- <user_message>Go to the bathroom, now!</user_message>
57
- Response:
58
- {
59
- "message": "Okay, I'm on my way to the bathroom.",
60
- "action": "go",
61
- "target": "bathroom"
62
- }
63
-
64
- Example 2:
65
- <user_message>Quick, hide under the bed!</user_message>
66
- Response:
67
- {
68
- "message": "Alright, I'm hiding under the bed right away.",
69
- "action": "hide",
70
- "target": "bed"
71
- }
72
-
73
- Remember:
74
- - Always respond in the JSON format specified above.
75
- - Choose the most appropriate action and target based on the user's message.
76
- - Keep your response message brief and natural-sounding.
77
- - If the user's message doesn't clearly indicate an action or target from the provided lists, use your best judgment to select the most relevant options.
78
- - Do not include any explanation or additional text outside of the JSON object in your response.`;
79
- }
80
- }
81
-
82
- // Export the class for use in other files
83
- export default GameState;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/assets/css/style.css ADDED
@@ -0,0 +1,424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: "HorrorBrush";
3
+ src: url("/static/assets/fonts/horrorbrush.ttf") format("truetype");
4
+ }
5
+
6
+ * {
7
+ margin: 0;
8
+ padding: 0;
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ body {
13
+ background-color: #000000;
14
+ min-height: 100vh;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ position: relative;
19
+ overflow: hidden;
20
+ }
21
+
22
+ .background-elements {
23
+ position: absolute;
24
+ width: 100%;
25
+ height: 100%;
26
+ pointer-events: none;
27
+ }
28
+
29
+ .blood {
30
+ position: absolute;
31
+ }
32
+
33
+ .blood-top-left {
34
+ top: 0;
35
+ left: 0;
36
+ width: 350px;
37
+ opacity: 50%;
38
+ }
39
+
40
+ .blood-bottom-right {
41
+ bottom: 0;
42
+ right: 0;
43
+ width: 250px;
44
+ position: absolute;
45
+ bottom: -120px;
46
+ right: -50px;
47
+ opacity: 40%;
48
+ }
49
+
50
+ .blood-top-right {
51
+ position: absolute;
52
+ top: 0;
53
+ right: 0;
54
+ width: 250px;
55
+ margin: 20px 20px 0px 0px;
56
+ transform: rotate(30deg);
57
+ }
58
+
59
+ .splatter {
60
+ position: absolute;
61
+ opacity: 60%;
62
+ bottom: 240px;
63
+ left: 30%;
64
+ transform: translateX(-50%);
65
+ transform: rotate(20deg);
66
+ width: 400px;
67
+ }
68
+
69
+ main {
70
+ display: flex;
71
+ flex-direction: column;
72
+ align-items: center;
73
+ gap: 100px;
74
+ z-index: 1;
75
+ margin-top: 200px;
76
+ }
77
+
78
+ .logo {
79
+ position: absolute;
80
+ top: 40px;
81
+ left: 50%;
82
+ transform: translateX(-50%);
83
+ animation: flicker 4s linear infinite;
84
+ }
85
+
86
+ .logo img {
87
+ width: 230px;
88
+ height: auto;
89
+ }
90
+
91
+ .menu {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: 30px;
95
+ align-items: center;
96
+ position: relative;
97
+ }
98
+
99
+ .menu::after {
100
+ content: "";
101
+ position: absolute;
102
+ width: 800px;
103
+ height: 800px;
104
+ background: radial-gradient(
105
+ circle,
106
+ rgba(155, 0, 0, 0.35) 0%,
107
+ rgba(0, 0, 0, 0) 70%
108
+ );
109
+ z-index: -1;
110
+ top: 50%;
111
+ left: 50%;
112
+ transform: translate(-50%, -50%);
113
+ }
114
+
115
+ .menu-item {
116
+ font-family: "HorrorBrush", cursive;
117
+ font-size: 48px;
118
+ color: #fff;
119
+ text-decoration: none;
120
+ transition: color 0.3s ease;
121
+ letter-spacing: 4px;
122
+ text-transform: uppercase;
123
+ }
124
+
125
+ .new-game {
126
+ color: #9b0000;
127
+ }
128
+
129
+ .menu-item:hover {
130
+ color: #9b0000;
131
+ }
132
+
133
+ .how-to-play:hover {
134
+ color: #9b0000;
135
+ }
136
+
137
+ .level-maker:hover {
138
+ color: #9b0000;
139
+ }
140
+
141
+ @keyframes flicker {
142
+ 0% {
143
+ opacity: 1;
144
+ }
145
+ 5% {
146
+ opacity: 0.9;
147
+ }
148
+ 10% {
149
+ opacity: 1;
150
+ }
151
+ 15% {
152
+ opacity: 0.4;
153
+ }
154
+ 16% {
155
+ opacity: 1;
156
+ }
157
+ 17% {
158
+ opacity: 0.4;
159
+ }
160
+ 18% {
161
+ opacity: 1;
162
+ }
163
+ 35% {
164
+ opacity: 1;
165
+ }
166
+ 36% {
167
+ opacity: 0.3;
168
+ }
169
+ 37% {
170
+ opacity: 1;
171
+ }
172
+ 38% {
173
+ opacity: 0.5;
174
+ }
175
+ 39% {
176
+ opacity: 1;
177
+ }
178
+ 50% {
179
+ opacity: 1;
180
+ }
181
+ 51% {
182
+ opacity: 0.7;
183
+ }
184
+ 52% {
185
+ opacity: 1;
186
+ }
187
+ 53% {
188
+ opacity: 0.4;
189
+ }
190
+ 54% {
191
+ opacity: 1;
192
+ }
193
+ 85% {
194
+ opacity: 1;
195
+ }
196
+ 86% {
197
+ opacity: 0.6;
198
+ }
199
+ 87% {
200
+ opacity: 1;
201
+ }
202
+ 88% {
203
+ opacity: 0.4;
204
+ }
205
+ 89% {
206
+ opacity: 1;
207
+ }
208
+ 100% {
209
+ opacity: 1;
210
+ }
211
+ }
212
+
213
+ .led-bar {
214
+ position: fixed;
215
+ top: 0;
216
+ left: 0;
217
+ width: 100%;
218
+ height: 2px;
219
+ background: linear-gradient(
220
+ 90deg,
221
+ transparent 0%,
222
+ rgba(255, 0, 0, 0.4) 20%,
223
+ rgba(255, 0, 0, 0.8) 35%,
224
+ rgba(255, 50, 50, 1) 50%,
225
+ rgba(255, 0, 0, 0.8) 65%,
226
+ rgba(255, 0, 0, 0.4) 80%,
227
+ transparent 100%
228
+ );
229
+ z-index: 100;
230
+ animation: ledFlicker 4s infinite, ledPulse 10s infinite;
231
+ box-shadow: 0 0 20px rgba(255, 0, 0, 0.7), 0 0 35px rgba(255, 0, 0, 0.5),
232
+ 0 0 50px rgba(255, 0, 0, 0.4), 0 0 70px rgba(155, 0, 0, 0.3);
233
+ filter: blur(0.6px);
234
+ }
235
+
236
+ .light-beam {
237
+ position: absolute;
238
+ top: 0;
239
+ left: 0;
240
+ width: 100%;
241
+ height: 250px;
242
+ background: linear-gradient(
243
+ 180deg,
244
+ rgba(255, 0, 0, 0.3) 0%,
245
+ rgba(255, 0, 0, 0.2) 20%,
246
+ rgba(255, 0, 0, 0.15) 30%,
247
+ rgba(155, 0, 0, 0.08) 60%,
248
+ transparent 100%
249
+ );
250
+ animation: beamFlicker 4s infinite;
251
+ pointer-events: none;
252
+ filter: blur(2px);
253
+ }
254
+
255
+ @keyframes ledFlicker {
256
+ 0% {
257
+ opacity: 1;
258
+ }
259
+ 95% {
260
+ opacity: 1;
261
+ }
262
+ 96% {
263
+ opacity: 0.3;
264
+ }
265
+ 97% {
266
+ opacity: 1;
267
+ }
268
+ 98% {
269
+ opacity: 0.2;
270
+ }
271
+ 99% {
272
+ opacity: 0.9;
273
+ }
274
+ 100% {
275
+ opacity: 1;
276
+ }
277
+ }
278
+
279
+ @keyframes ledPulse {
280
+ 0% {
281
+ filter: brightness(1) blur(0.6px);
282
+ }
283
+ 50% {
284
+ filter: brightness(1.3) blur(0.4px);
285
+ }
286
+ 100% {
287
+ filter: brightness(1) blur(0.6px);
288
+ }
289
+ }
290
+
291
+ @keyframes beamFlicker {
292
+ 0% {
293
+ opacity: 0.7;
294
+ }
295
+ 95% {
296
+ opacity: 0.7;
297
+ }
298
+ 96% {
299
+ opacity: 0.2;
300
+ }
301
+ 97% {
302
+ opacity: 0.7;
303
+ }
304
+ 98% {
305
+ opacity: 0.1;
306
+ }
307
+ 99% {
308
+ opacity: 0.6;
309
+ }
310
+ 100% {
311
+ opacity: 0.7;
312
+ }
313
+ }
314
+
315
+ /* How to Play Page Styles */
316
+ .content {
317
+ color: #fff;
318
+ text-align: center;
319
+ max-width: 800px;
320
+ margin: 40px auto;
321
+ padding: 20px;
322
+ }
323
+
324
+ .content h1 {
325
+ font-family: "HorrorBrush", cursive;
326
+ font-size: 64px;
327
+ color: #9b0000;
328
+ position: absolute;
329
+ top: 40px;
330
+ left: 50%;
331
+ transform: translateX(-50%);
332
+ letter-spacing: 4px;
333
+ }
334
+
335
+ .back-button {
336
+ font-family: "HorrorBrush", cursive;
337
+ font-size: 36px;
338
+ color: #fff;
339
+ text-decoration: none;
340
+ transition: color 0.3s ease;
341
+ letter-spacing: 4px;
342
+ text-transform: uppercase;
343
+ position: absolute;
344
+ bottom: 40px;
345
+ left: 50%;
346
+ transform: translateX(-50%);
347
+ }
348
+
349
+ .back-button:hover {
350
+ color: #9b0000;
351
+ }
352
+
353
+ .character {
354
+ position: absolute;
355
+ bottom: -20px;
356
+ left: 50%;
357
+ transform: translateX(-50%);
358
+ z-index: 2;
359
+ animation: characterFlicker 6s infinite;
360
+ }
361
+
362
+ .character img {
363
+ width: 450px;
364
+ height: auto;
365
+ }
366
+
367
+ @keyframes characterFlicker {
368
+ 0% {
369
+ opacity: 1;
370
+ filter: brightness(1);
371
+ }
372
+ 42% {
373
+ opacity: 1;
374
+ filter: brightness(1);
375
+ }
376
+ 43% {
377
+ opacity: 0.8;
378
+ filter: brightness(1.2);
379
+ }
380
+ 44% {
381
+ opacity: 1;
382
+ filter: brightness(1);
383
+ }
384
+ 45% {
385
+ opacity: 0.6;
386
+ filter: brightness(1.3);
387
+ }
388
+ 46% {
389
+ opacity: 1;
390
+ filter: brightness(1);
391
+ }
392
+ 47% {
393
+ opacity: 0.2;
394
+ filter: brightness(1.5);
395
+ }
396
+ 48% {
397
+ opacity: 1;
398
+ filter: brightness(1);
399
+ }
400
+ 49% {
401
+ opacity: 0.4;
402
+ filter: brightness(1.2);
403
+ }
404
+ 50% {
405
+ opacity: 1;
406
+ filter: brightness(1);
407
+ }
408
+ 80% {
409
+ opacity: 1;
410
+ filter: brightness(1);
411
+ }
412
+ 81% {
413
+ opacity: 0.5;
414
+ filter: brightness(1.3);
415
+ }
416
+ 82% {
417
+ opacity: 1;
418
+ filter: brightness(1);
419
+ }
420
+ 100% {
421
+ opacity: 1;
422
+ filter: brightness(1);
423
+ }
424
+ }
static/assets/fonts/horrorbrush.ttf ADDED
Binary file (286 kB). View file
 
static/assets/img/blood-2.png ADDED
static/assets/img/hand.png ADDED
static/assets/img/help.png ADDED
static/assets/img/logo.png ADDED
static/assets/img/perso.png ADDED
static/assets/img/splatter.png ADDED
static/assets/img/vite.svg ADDED
static/game/game-with-chat.html DELETED
@@ -1,537 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Get Me Out! - Apartment Escape Game with Chat</title>
5
- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
6
- <style>
7
- /* Layout */
8
- body {
9
- margin: 0;
10
- font-family: Arial, sans-serif;
11
- display: flex;
12
- height: 100vh;
13
- }
14
- #gameContainer {
15
- display: flex;
16
- width: 100%;
17
- height: 100%;
18
- }
19
- #mapSection {
20
- flex: 1;
21
- position: relative;
22
- background: #f5f5f5;
23
- display: flex;
24
- flex-direction: column;
25
- align-items: center;
26
- }
27
- /* We'll place the P5 canvas in here */
28
- .map-wrapper {
29
- margin: 20px;
30
- transform-origin: top left;
31
- }
32
-
33
- #chatSection {
34
- width: 400px;
35
- border-left: 1px solid #ddd;
36
- display: flex;
37
- flex-direction: column;
38
- background: white;
39
- }
40
- #chatHistory {
41
- flex: 1;
42
- overflow-y: auto;
43
- padding: 20px;
44
- background: #f8f9fa;
45
- }
46
- #chatControls {
47
- padding: 20px;
48
- border-top: 1px solid #ddd;
49
- background: white;
50
- }
51
- .chat-message {
52
- padding: 10px;
53
- margin: 5px 0;
54
- border-radius: 16px;
55
- max-width: 80%;
56
- word-wrap: break-word;
57
- }
58
- .user-message {
59
- background: #007AFF;
60
- color: white;
61
- margin-left: auto;
62
- border-radius: 16px 16px 4px 16px;
63
- }
64
- .assistant-message {
65
- background: #E9E9EB;
66
- color: black;
67
- margin-right: auto;
68
- border-radius: 16px 16px 16px 4px;
69
- }
70
- textarea {
71
- width: 100%;
72
- padding: 10px;
73
- border: 1px solid #ddd;
74
- border-radius: 4px;
75
- margin-bottom: 10px;
76
- resize: none;
77
- font-family: inherit;
78
- }
79
- button {
80
- padding: 8px 16px;
81
- background: #007AFF;
82
- color: white;
83
- border: none;
84
- border-radius: 4px;
85
- cursor: pointer;
86
- }
87
- button:hover {
88
- background: #0056b3;
89
- }
90
- .loading-dots {
91
- display: inline-flex;
92
- gap: 4px;
93
- padding: 5px;
94
- margin: 5px 0;
95
- }
96
- .dot {
97
- width: 8px;
98
- height: 8px;
99
- background: #6c757d;
100
- border-radius: 50%;
101
- animation: wave 1.3s linear infinite;
102
- }
103
- .dot:nth-child(2) { animation-delay: -1.1s; }
104
- .dot:nth-child(3) { animation-delay: -0.9s; }
105
- @keyframes wave {
106
- 0%, 60%, 100% { transform: translateY(0); }
107
- 30% { transform: translateY(-4px); }
108
- }
109
-
110
- /* Modal for API key (if needed) */
111
- #apiKeyModal {
112
- display: none;
113
- position: fixed;
114
- top: 0; left: 0;
115
- width: 100%; height: 100%;
116
- background: rgba(0,0,0,0.5);
117
- justify-content: center;
118
- align-items: center;
119
- }
120
- .modal-content {
121
- background: white;
122
- padding: 20px;
123
- border-radius: 8px;
124
- width: 300px;
125
- }
126
- #apiKey {
127
- width: 100%;
128
- margin: 10px 0;
129
- padding: 8px;
130
- }
131
- </style>
132
- </head>
133
- <body>
134
- <div id="gameContainer">
135
- <div id="mapSection">
136
- <div id="mapWrapper" class="map-wrapper">
137
- <!-- P5.js canvas goes here -->
138
- </div>
139
- </div>
140
- <div id="chatSection">
141
- <div id="chatHistory"></div>
142
- <div id="chatControls">
143
- <textarea id="prompt" placeholder="Type your message..." rows="3"></textarea>
144
- <button onclick="sendMessage()">Send</button>
145
- </div>
146
- </div>
147
- </div>
148
- <div id="apiKeyModal">
149
- <div class="modal-content">
150
- <h3>Enter Mistral API Key</h3>
151
- <input type="password" id="apiKey" placeholder="Enter your API key">
152
- <button onclick="saveApiKey()">Save</button>
153
- </div>
154
- </div>
155
-
156
- <script>
157
- /*
158
- * This script loads ./apt.json to set up the map,
159
- * draws it in p5.js, and provides a chat interface on the right.
160
- */
161
-
162
- // Constants and variables for the map
163
- const CELL_SIZE = 30;
164
- let GRID_COLS = 0;
165
- let GRID_ROWS = 0;
166
- let grid = [];
167
- let rooms = new Map();
168
- let characterPos = null;
169
- let path = [];
170
- let isMoving = false;
171
- let moveInterval = null;
172
-
173
- // Chat variables
174
- let apiKey = localStorage.getItem('mistralApiKey');
175
- const systemPrompt = `You are the game master of "Get Me Out!", a single-player text-based survival game. The scenario: a player must guide their girlfriend to safety via text messages while she's being hunted by a murderous clown in their apartment. The player has access to security cameras and can send instructions through text.
176
-
177
- RESPONSE FORMAT:
178
- You must ALWAYS respond with a JSON object. The response should reflect the girlfriend's reaction to the player's message.
179
-
180
- 1. For movement instructions ("go" action):
181
- {
182
- "action": "go",
183
- "to": "[room name]",
184
- "textMessage": "[girlfriend's response]"
185
- }
186
-
187
- 2. For any other input or unclear instructions:
188
- {
189
- "textMessage": "[girlfriend's response]"
190
- }
191
-
192
- VALID ROOMS:
193
- Only these rooms are recognized for movement:
194
- - Main Bathroom
195
- - Guest Toilet
196
- - Dining Room
197
- - Kitchen
198
- - TV Room
199
- - Living Room
200
- - Hallway
201
- - Office
202
- - Bedroom
203
-
204
- CHARACTER BEHAVIOR:
205
- The girlfriend is aware of the danger and extremely distressed. Her text responses should be:
206
- - Brief and urgent
207
- - Reflect genuine fear and panic
208
- - Written like real text messages (short, quick responses)
209
- - No time for pleasantries or long explanations
210
- - May include typos or rushed writing due to stress
211
- `;
212
-
213
- // Load apt.json upon page start
214
- async function loadAptJson() {
215
- try {
216
- const response = await fetch('./apt.json');
217
- if (!response.ok) {
218
- alert("Failed to load apt.json!");
219
- return;
220
- }
221
- const data = await response.json();
222
- // Update local variables from apt.json
223
- GRID_COLS = data.gridCols || 40;
224
- GRID_ROWS = data.gridRows || 20;
225
- grid = data.grid || [];
226
- rooms = new Map(data.rooms);
227
- characterPos = data.characterPos || { x: 0, y: 0 };
228
-
229
- // Once loaded, initialize the P5 canvas with correct dims
230
- let canvas = createCanvas(GRID_COLS * CELL_SIZE, GRID_ROWS * CELL_SIZE);
231
- canvas.parent('mapWrapper');
232
- adjustScale();
233
-
234
- } catch (e) {
235
- console.error("Error loading apt.json:", e);
236
- }
237
- }
238
-
239
- function adjustScale() {
240
- const availableWidth = window.innerWidth - 400 - 40; // space for chat + margins
241
- const actualCanvasWidth = GRID_COLS * CELL_SIZE;
242
- const scale = availableWidth / actualCanvasWidth;
243
- const mapWrapper = document.querySelector('#mapSection .map-wrapper');
244
- if (mapWrapper) {
245
- // use CSS zoom or transform
246
- mapWrapper.style.zoom = scale;
247
- }
248
- }
249
-
250
- window.addEventListener('resize', adjustScale);
251
-
252
- // p5.js setup
253
- function setup() {
254
- // We'll wait to createCanvas until apt.json is loaded
255
- loadAptJson();
256
- }
257
-
258
- // p5.js draw loop
259
- function draw() {
260
- if (!grid || grid.length === 0) {
261
- // No grid loaded yet or apt.json not ready
262
- return;
263
- }
264
- background(255);
265
- drawGrid();
266
- drawRooms();
267
- drawWallsAndDoors();
268
- drawPath();
269
- drawCharacter();
270
- }
271
-
272
- function drawGrid() {
273
- stroke(200);
274
- for (let x = 0; x <= GRID_COLS; x++) {
275
- line(x * CELL_SIZE, 0, x * CELL_SIZE, GRID_ROWS*CELL_SIZE);
276
- }
277
- for (let y = 0; y <= GRID_ROWS; y++) {
278
- line(0, y * CELL_SIZE, GRID_COLS*CELL_SIZE, y * CELL_SIZE);
279
- }
280
- }
281
-
282
- function drawRooms() {
283
- for (let [roomName, cells] of rooms) {
284
- const hue = stringToHue(roomName);
285
- fill(hue, 30, 95, 0.3);
286
- noStroke();
287
- for (let cell of cells) {
288
- rect(cell.x * CELL_SIZE, cell.y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
289
- }
290
- if (cells.length > 0) {
291
- fill(0);
292
- textAlign(CENTER, CENTER);
293
- textSize(10);
294
- text(roomName,
295
- cells[0].x * CELL_SIZE + CELL_SIZE/2,
296
- cells[0].y * CELL_SIZE + CELL_SIZE/2
297
- );
298
- }
299
- }
300
- }
301
-
302
- function drawWallsAndDoors() {
303
- for (let y = 0; y < GRID_ROWS; y++) {
304
- for (let x = 0; x < GRID_COLS; x++) {
305
- const cell = grid[y][x];
306
- if (cell.type === 'wall') {
307
- fill(0);
308
- noStroke();
309
- rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
310
- } else if (cell.type === 'door') {
311
- fill(255, 0, 0);
312
- noStroke();
313
- rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
314
- }
315
- }
316
- }
317
- }
318
-
319
- function drawPath() {
320
- if (path.length > 0 && isMoving) {
321
- noFill();
322
- stroke(0, 255, 0);
323
- strokeWeight(2);
324
- line(
325
- characterPos.x * CELL_SIZE + CELL_SIZE/2,
326
- characterPos.y * CELL_SIZE + CELL_SIZE/2,
327
- path[0].x * CELL_SIZE + CELL_SIZE/2,
328
- path[0].y * CELL_SIZE + CELL_SIZE/2
329
- );
330
- for (let i = 0; i < path.length - 1; i++) {
331
- line(
332
- path[i].x * CELL_SIZE + CELL_SIZE/2,
333
- path[i].y * CELL_SIZE + CELL_SIZE/2,
334
- path[i + 1].x * CELL_SIZE + CELL_SIZE/2,
335
- path[i + 1].y * CELL_SIZE + CELL_SIZE/2
336
- );
337
- }
338
- strokeWeight(1);
339
- }
340
- }
341
-
342
- function drawCharacter() {
343
- if (characterPos) {
344
- textSize(CELL_SIZE * 0.8);
345
- textAlign(CENTER, CENTER);
346
- text('👧',
347
- characterPos.x * CELL_SIZE + CELL_SIZE/2,
348
- characterPos.y * CELL_SIZE + CELL_SIZE/2
349
- );
350
- }
351
- }
352
-
353
- function stringToHue(str) {
354
- let hash = 0;
355
- for (let i = 0; i < str.length; i++) {
356
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
357
- }
358
- return hash % 360;
359
- }
360
-
361
- // Simple BFS for pathfinding
362
- function findPath(start, end) {
363
- const queue = [[start]];
364
- const visited = new Set();
365
- const key = pos => `${pos.x},${pos.y}`;
366
- visited.add(key(start));
367
-
368
- while (queue.length > 0) {
369
- const currentPath = queue.shift();
370
- const current = currentPath[currentPath.length - 1];
371
-
372
- if (current.x === end.x && current.y === end.y) {
373
- return currentPath;
374
- }
375
- const neighbors = [
376
- { x: current.x, y: current.y - 1 },
377
- { x: current.x+1, y: current.y },
378
- { x: current.x, y: current.y+1 },
379
- { x: current.x-1, y: current.y }
380
- ];
381
- for (const next of neighbors) {
382
- if (next.x < 0 || next.x >= GRID_COLS || next.y < 0 || next.y >= GRID_ROWS) continue;
383
- if (visited.has(key(next))) continue;
384
-
385
- const cell = grid[next.y][next.x];
386
- if (cell.type === 'wall') continue;
387
-
388
- visited.add(key(next));
389
- queue.push([...currentPath, next]);
390
- }
391
- }
392
- return [];
393
- }
394
-
395
- function moveToRoom(roomName) {
396
- const targetRoom = Array.from(rooms.entries())
397
- .find(([name]) => name.toLowerCase() === roomName.toLowerCase());
398
- if (!targetRoom || !characterPos) return false;
399
-
400
- const [_, cells] = targetRoom;
401
- if (cells.length === 0) return false;
402
- path = findPath(characterPos, cells[0]);
403
- if (path.length > 0) {
404
- isMoving = true;
405
- moveCharacterAlongPath();
406
- return true;
407
- }
408
- return false;
409
- }
410
-
411
- function moveCharacterAlongPath() {
412
- if (moveInterval) clearInterval(moveInterval);
413
- moveInterval = setInterval(() => {
414
- if (path.length === 0) {
415
- isMoving = false;
416
- clearInterval(moveInterval);
417
- return;
418
- }
419
- const nextPos = path.shift();
420
- characterPos = nextPos;
421
- }, 200);
422
- }
423
-
424
- /* Chat / Mistral-related code */
425
- function createLoadingIndicator() {
426
- const loadingDiv = document.createElement('div');
427
- loadingDiv.className = 'chat-message assistant-message';
428
- loadingDiv.innerHTML = `
429
- <div class="loading-dots">
430
- <div class="dot"></div>
431
- <div class="dot"></div>
432
- <div class="dot"></div>
433
- </div>
434
- `;
435
- return loadingDiv;
436
- }
437
-
438
- function addMessageToChat(role, content) {
439
- const chatHistory = document.getElementById('chatHistory');
440
- const messageDiv = document.createElement('div');
441
- messageDiv.className = `chat-message ${role}-message`;
442
- messageDiv.textContent = content;
443
- chatHistory.appendChild(messageDiv);
444
- chatHistory.scrollTop = chatHistory.scrollHeight;
445
- }
446
-
447
- async function sendMessage() {
448
- const prompt = document.getElementById('prompt').value.trim();
449
- if (!prompt) return;
450
-
451
- if (!apiKey) {
452
- alert('Please enter your Mistral API key first');
453
- document.getElementById('apiKeyModal').style.display = 'flex';
454
- return;
455
- }
456
-
457
- // Add user message to the chat
458
- addMessageToChat('user', prompt);
459
- document.getElementById('prompt').value = '';
460
-
461
- // Add loading indicator
462
- const chatHistory = document.getElementById('chatHistory');
463
- const loadingIndicator = createLoadingIndicator();
464
- chatHistory.appendChild(loadingIndicator);
465
-
466
- try {
467
- // Make call to Mistral or any AI endpoint
468
- const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
469
- method: 'POST',
470
- headers: {
471
- 'Content-Type': 'application/json',
472
- 'Authorization': `Bearer ${apiKey}`
473
- },
474
- body: JSON.stringify({
475
- model: 'mistral-large-latest',
476
- messages: [
477
- { role: 'system', content: systemPrompt },
478
- { role: 'user', content: prompt }
479
- ]
480
- })
481
- });
482
-
483
- if (!response.ok) {
484
- throw new Error(`HTTP error! status: ${response.status}`);
485
- }
486
-
487
- const data = await response.json();
488
- const assistantResponse = data.choices[0].message.content || "";
489
-
490
- try {
491
- const jsonStart = assistantResponse.indexOf('{');
492
- const jsonEnd = assistantResponse.lastIndexOf('}') + 1;
493
- const jsonContent = assistantResponse.substring(jsonStart, jsonEnd);
494
- const jsonResponse = JSON.parse(jsonContent);
495
-
496
- if (jsonResponse.textMessage) {
497
- addMessageToChat('assistant', jsonResponse.textMessage);
498
- }
499
- if (jsonResponse.action === 'go' && jsonResponse.to) {
500
- moveToRoom(jsonResponse.to);
501
- }
502
- } catch (e) {
503
- console.error('Error parsing JSON from response:', e);
504
- addMessageToChat('assistant', 'Sorry, I had trouble understanding that response.');
505
- }
506
- } catch (error) {
507
- addMessageToChat('assistant', `Error: ${error.message}`);
508
- } finally {
509
- loadingIndicator.remove();
510
- }
511
- }
512
-
513
- function saveApiKey() {
514
- const key = document.getElementById('apiKey').value.trim();
515
- if (key) {
516
- apiKey = key;
517
- localStorage.setItem('mistralApiKey', key);
518
- document.getElementById('apiKeyModal').style.display = 'none';
519
- }
520
- }
521
-
522
- // Allow sending message with Enter
523
- document.addEventListener('DOMContentLoaded', () => {
524
- document.getElementById('prompt').addEventListener('keypress', function(e) {
525
- if (e.key === 'Enter' && !e.shiftKey) {
526
- e.preventDefault();
527
- sendMessage();
528
- }
529
- });
530
-
531
- if (!apiKey) {
532
- document.getElementById('apiKeyModal').style.display = 'flex';
533
- }
534
- });
535
- </script>
536
- </body>
537
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/game/gameState.js ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // Game state management
3
+ class GameState {
4
+ constructor() {
5
+ // Initialize with rooms from apt.json
6
+ this.rooms = [
7
+ "Kichen",
8
+ "Storage",
9
+ "My Bedroom",
10
+ "Bathroom",
11
+ "Main Hallway",
12
+ "Living Room",
13
+ "Guest Bedroom",
14
+ "Dining Room",
15
+ "Office",
16
+ "North Hallway"
17
+ ];
18
+
19
+ this.hideTargets = ["bed", "coat closet", "table", "shower"];
20
+ this.searchTargets = ["dresser", "stove", "desk", "cabinet", "fridge", "bookcase"];
21
+ this.items = ["lock pick", "flashlight", "knife", "remote"];
22
+ this.useTargets = ["bedroom door", "main door", "TV"];
23
+
24
+ // Track current game state
25
+ this.currentRoom = this.rooms[0];
26
+ this.inventory = [];
27
+ this.isHiding = false;
28
+ }
29
+
30
+ handleAction(action, target) {
31
+ switch (action) {
32
+ case 'go':
33
+ this.currentRoom = target;
34
+ break;
35
+ case 'hide':
36
+ this.isHiding = true;
37
+ break;
38
+ case 'search':
39
+ // Could add found items to inventory
40
+ break;
41
+ case 'use':
42
+ // Handle item usage
43
+ break;
44
+ }
45
+ }
46
+
47
+ getPrompt() {
48
+ return `You are the game master of "Get Me Out!", a single-player text-based survival game. The scenario: a player must guide their girlfriend to safety via text messages while she's being hunted by a murderous clown in their apartment. The player has access to security cameras and can give instructions through text.
49
+
50
+ Current state:
51
+ - Room: ${this.currentRoom}
52
+ - Hiding: ${this.isHiding}
53
+ - Inventory: ${this.inventory.join(", ") || "empty"}
54
+
55
+ RESPONSE FORMAT:
56
+ You must ALWAYS respond with a JSON object. The response should reflect the girlfriend's reaction to the player's message.
57
+
58
+ 1. For movement instructions ("go" action):
59
+ {
60
+ "action": "go",
61
+ "to": "[room name]",
62
+ "textMessage": "[girlfriend's response]"
63
+ }
64
+
65
+ 2. For any other input or unclear instructions:
66
+ {
67
+ "textMessage": "[girlfriend's response]"
68
+ }
69
+
70
+ VALID ROOMS:
71
+ Only these rooms are recognized for movement:
72
+ ${this.rooms.join('\n')}
73
+
74
+ CHARACTER BEHAVIOR:
75
+ The girlfriend is aware of the danger and extremely distressed. Her text responses should be:
76
+ - Brief and urgent
77
+ - Reflect genuine fear and panic
78
+ - Written like real text messages (short, quick responses)
79
+ - No time for pleasantries or long explanations
80
+ - May include typos or rushed writing due to stress`;
81
+ }
82
+
83
+ }
static/game/index.html CHANGED
@@ -3,6 +3,7 @@
3
  <head>
4
  <title>Get Me Out! - Apartment Escape Game with Chat</title>
5
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
 
6
  <style>
7
  /* Layout */
8
  body {
@@ -47,6 +48,8 @@
47
  padding: 20px;
48
  border-top: 1px solid #ddd;
49
  background: white;
 
 
50
  }
51
  .chat-message {
52
  padding: 10px;
@@ -67,13 +70,11 @@
67
  margin-right: auto;
68
  border-radius: 16px 16px 16px 4px;
69
  }
70
- textarea {
71
- width: 100%;
72
  padding: 10px;
73
  border: 1px solid #ddd;
74
  border-radius: 4px;
75
- margin-bottom: 10px;
76
- resize: none;
77
  font-family: inherit;
78
  }
79
  button {
@@ -140,8 +141,8 @@
140
  <div id="chatSection">
141
  <div id="chatHistory"></div>
142
  <div id="chatControls">
143
- <textarea id="prompt" placeholder="Type your message..." rows="3"></textarea>
144
- <button onclick="sendMessage()">Send</button>
145
  </div>
146
  </div>
147
  </div>
@@ -170,45 +171,12 @@
170
  let isMoving = false;
171
  let moveInterval = null;
172
 
 
 
 
173
  // Chat variables
174
  let apiKey = localStorage.getItem('mistralApiKey');
175
- const systemPrompt = `You are the game master of "Get Me Out!", a single-player text-based survival game. The scenario: a player must guide their girlfriend to safety via text messages while she's being hunted by a murderous clown in their apartment. The player has access to security cameras and can send instructions through text.
176
-
177
- RESPONSE FORMAT:
178
- You must ALWAYS respond with a JSON object. The response should reflect the girlfriend's reaction to the player's message.
179
-
180
- 1. For movement instructions ("go" action):
181
- {
182
- "action": "go",
183
- "to": "[room name]",
184
- "textMessage": "[girlfriend's response]"
185
- }
186
-
187
- 2. For any other input or unclear instructions:
188
- {
189
- "textMessage": "[girlfriend's response]"
190
- }
191
-
192
- VALID ROOMS:
193
- Only these rooms are recognized for movement:
194
- - Main Bathroom
195
- - Guest Toilet
196
- - Dining Room
197
- - Kitchen
198
- - TV Room
199
- - Living Room
200
- - Hallway
201
- - Office
202
- - Bedroom
203
-
204
- CHARACTER BEHAVIOR:
205
- The girlfriend is aware of the danger and extremely distressed. Her text responses should be:
206
- - Brief and urgent
207
- - Reflect genuine fear and panic
208
- - Written like real text messages (short, quick responses)
209
- - No time for pleasantries or long explanations
210
- - May include typos or rushed writing due to stress
211
- `;
212
 
213
  // Load apt.json upon page start
214
  async function loadAptJson() {
@@ -473,9 +441,12 @@ The girlfriend is aware of the danger and extremely distressed. Her text respons
473
  messageDiv.textContent = content;
474
  chatHistory.appendChild(messageDiv);
475
  chatHistory.scrollTop = chatHistory.scrollHeight;
 
 
 
476
  }
477
 
478
- async function sendMessage() {
479
  const prompt = document.getElementById('prompt').value.trim();
480
  if (!prompt) return;
481
 
@@ -485,17 +456,24 @@ The girlfriend is aware of the danger and extremely distressed. Her text respons
485
  return;
486
  }
487
 
488
- // Add user message to the chat
489
  addMessageToChat('user', prompt);
490
  document.getElementById('prompt').value = '';
491
 
492
- // Add loading indicator
493
  const chatHistory = document.getElementById('chatHistory');
494
  const loadingIndicator = createLoadingIndicator();
495
  chatHistory.appendChild(loadingIndicator);
496
 
497
  try {
498
- // Make call to Mistral or any AI endpoint
 
 
 
 
 
 
 
 
 
499
  const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
500
  method: 'POST',
501
  headers: {
@@ -504,10 +482,7 @@ The girlfriend is aware of the danger and extremely distressed. Her text respons
504
  },
505
  body: JSON.stringify({
506
  model: 'mistral-large-latest',
507
- messages: [
508
- { role: 'system', content: systemPrompt },
509
- { role: 'user', content: prompt }
510
- ]
511
  })
512
  });
513
 
@@ -516,6 +491,7 @@ The girlfriend is aware of the danger and extremely distressed. Her text respons
516
  }
517
 
518
  const data = await response.json();
 
519
  const assistantResponse = data.choices[0].message.content || "";
520
 
521
  try {
@@ -523,6 +499,7 @@ The girlfriend is aware of the danger and extremely distressed. Her text respons
523
  const jsonEnd = assistantResponse.lastIndexOf('}') + 1;
524
  const jsonContent = assistantResponse.substring(jsonStart, jsonEnd);
525
  const jsonResponse = JSON.parse(jsonContent);
 
526
 
527
  if (jsonResponse.textMessage) {
528
  addMessageToChat('assistant', jsonResponse.textMessage);
@@ -550,12 +527,12 @@ The girlfriend is aware of the danger and extremely distressed. Her text respons
550
  }
551
  }
552
 
553
- // Allow sending message with Enter
554
  document.addEventListener('DOMContentLoaded', () => {
555
  document.getElementById('prompt').addEventListener('keypress', function(e) {
556
- if (e.key === 'Enter' && !e.shiftKey) {
557
  e.preventDefault();
558
- sendMessage();
559
  }
560
  });
561
 
 
3
  <head>
4
  <title>Get Me Out! - Apartment Escape Game with Chat</title>
5
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
6
+ <script src="/static/game/gameState.js"></script>
7
  <style>
8
  /* Layout */
9
  body {
 
48
  padding: 20px;
49
  border-top: 1px solid #ddd;
50
  background: white;
51
+ display: flex;
52
+ gap: 10px;
53
  }
54
  .chat-message {
55
  padding: 10px;
 
70
  margin-right: auto;
71
  border-radius: 16px 16px 16px 4px;
72
  }
73
+ input {
74
+ flex: 1;
75
  padding: 10px;
76
  border: 1px solid #ddd;
77
  border-radius: 4px;
 
 
78
  font-family: inherit;
79
  }
80
  button {
 
141
  <div id="chatSection">
142
  <div id="chatHistory"></div>
143
  <div id="chatControls">
144
+ <input type="text" id="prompt" placeholder="Type your message...">
145
+ <button onclick="Message()">▶</button>
146
  </div>
147
  </div>
148
  </div>
 
171
  let isMoving = false;
172
  let moveInterval = null;
173
 
174
+ // Initialize game state
175
+ const gameState = new GameState();
176
+
177
  // Chat variables
178
  let apiKey = localStorage.getItem('mistralApiKey');
179
+ let chatMessages = []; // Array to store chat history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  // Load apt.json upon page start
182
  async function loadAptJson() {
 
441
  messageDiv.textContent = content;
442
  chatHistory.appendChild(messageDiv);
443
  chatHistory.scrollTop = chatHistory.scrollHeight;
444
+
445
+ // Store message in chat history
446
+ chatMessages.push({ role, content });
447
  }
448
 
449
+ async function Message() {
450
  const prompt = document.getElementById('prompt').value.trim();
451
  if (!prompt) return;
452
 
 
456
  return;
457
  }
458
 
 
459
  addMessageToChat('user', prompt);
460
  document.getElementById('prompt').value = '';
461
 
 
462
  const chatHistory = document.getElementById('chatHistory');
463
  const loadingIndicator = createLoadingIndicator();
464
  chatHistory.appendChild(loadingIndicator);
465
 
466
  try {
467
+ // Get last 5 messages from chat history
468
+ const recentMessages = chatMessages.slice(-5);
469
+
470
+ // Prepare messages array for Mistral
471
+ const messages = [
472
+ { role: 'system', content: gameState.getPrompt() },
473
+ ...recentMessages,
474
+ { role: 'user', content: prompt }
475
+ ];
476
+
477
  const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
478
  method: 'POST',
479
  headers: {
 
482
  },
483
  body: JSON.stringify({
484
  model: 'mistral-large-latest',
485
+ messages: messages
 
 
 
486
  })
487
  });
488
 
 
491
  }
492
 
493
  const data = await response.json();
494
+ console.log('Mistral response:', data.choices[0].message.content);
495
  const assistantResponse = data.choices[0].message.content || "";
496
 
497
  try {
 
499
  const jsonEnd = assistantResponse.lastIndexOf('}') + 1;
500
  const jsonContent = assistantResponse.substring(jsonStart, jsonEnd);
501
  const jsonResponse = JSON.parse(jsonContent);
502
+ console.log('Parsed JSON response:', jsonResponse);
503
 
504
  if (jsonResponse.textMessage) {
505
  addMessageToChat('assistant', jsonResponse.textMessage);
 
527
  }
528
  }
529
 
530
+ // Allow ing message with Enter
531
  document.addEventListener('DOMContentLoaded', () => {
532
  document.getElementById('prompt').addEventListener('keypress', function(e) {
533
+ if (e.key === 'Enter') {
534
  e.preventDefault();
535
+ Message();
536
  }
537
  });
538
 
static/game/test prompt.txt ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are the game master of "Get Me Out!", a single-player text-based survival game. The scenario: a player must guide their girlfriend to safety via text messages while she's being hunted by a murderous clown in their apartment. The player has access to security cameras and can instructions through text.
2
+
3
+ RESPONSE FORMAT:
4
+ You must ALWAYS respond with a JSON object. The response should reflect the girlfriend's reaction to the player's message.
5
+
6
+ 1. For movement instructions ("go" action):
7
+ {
8
+ "action": "go",
9
+ "to": "[room name]",
10
+ "textMessage": "[girlfriend's response]"
11
+ }
12
+
13
+ 2. For any other input or unclear instructions:
14
+ {
15
+ "textMessage": "[girlfriend's response]"
16
+ }
17
+
18
+ VALID ROOMS:
19
+ Only these rooms are recognized for movement:
20
+ - Main Bathroom
21
+ - Guest Toilet
22
+ - Dining Room
23
+ - Kitchen
24
+ - TV Room
25
+ - Living Room
26
+ - Hallway
27
+ - Office
28
+ - Bedroom
29
+
30
+ CHARACTER BEHAVIOR:
31
+ The girlfriend is aware of the danger and extremely distressed. Her text responses should be:
32
+ - Brief and urgent
33
+ - Reflect genuine fear and panic
34
+ - Written like real text messages (short, quick responses)
35
+ - No time for pleasantries or long explanations
36
+ - May include typos or rushed writing due to stress
37
+
38
+
39
+
static/how-to-play.html ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Through Their Eyes - How to Play</title>
7
+ <link rel="stylesheet" href="/src/style.css" />
8
+ </head>
9
+ <body>
10
+ <div class="led-bar">
11
+ <div class="light-beam"></div>
12
+ </div>
13
+
14
+ <div class="background-elements">
15
+ <img src="public/blood-2.png" alt="" class="blood blood-top-left" />
16
+ <img src="public/help.png" alt="" class="blood blood-top-right" />
17
+ <img src="public/splatter.png" alt="" class="blood splatter" />
18
+ <img src="public/hand.png" alt="" class="blood blood-bottom-right" />
19
+ </div>
20
+
21
+ <main>
22
+ <div class="content">
23
+ <h1>How to Play</h1>
24
+ <!-- Contenu à ajouter -->
25
+ </div>
26
+
27
+ <a href="index.html" class="back-button">Back to Menu</a>
28
+ </main>
29
+
30
+ <script type="module" src="/src/main.js"></script>
31
+ </body>
32
+ </html>
static/index.html CHANGED
@@ -1,150 +1,35 @@
1
  <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>The Last Message - Horror Escape Game</title>
5
- <style>
6
- body {
7
- margin: 0;
8
- padding: 0;
9
- min-height: 100vh;
10
- font-family: 'Arial', sans-serif;
11
- background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
12
- color: #ffffff;
13
- display: flex;
14
- flex-direction: column;
15
- align-items: center;
16
- justify-content: center;
17
- overflow: hidden;
18
- }
19
-
20
- .container {
21
- text-align: center;
22
- padding: 2rem;
23
- max-width: 800px;
24
- position: relative;
25
- z-index: 1;
26
- }
27
-
28
- h1 {
29
- font-size: 4rem;
30
- margin-bottom: 1rem;
31
- text-shadow: 0 0 10px #ff0000, 0 0 20px #ff0000;
32
- animation: flicker 4s infinite;
33
- letter-spacing: 2px;
34
- }
35
-
36
- .subtitle {
37
- font-size: 1.2rem;
38
- color: #bb0000;
39
- margin-bottom: 3rem;
40
- text-shadow: 0 0 5px #ff0000;
41
- }
42
-
43
- .menu {
44
- display: grid;
45
- gap: 2rem;
46
- margin-top: 2rem;
47
- }
48
-
49
- .menu-item {
50
- background: rgba(20, 20, 20, 0.8);
51
- padding: 1.5rem;
52
- border-radius: 5px;
53
- cursor: pointer;
54
- transition: all 0.3s ease;
55
- text-decoration: none;
56
- color: white;
57
- border: 1px solid #440000;
58
- box-shadow: 0 0 10px rgba(255, 0, 0, 0.2);
59
- }
60
-
61
- .menu-item:hover {
62
- transform: scale(1.05);
63
- background: rgba(40, 0, 0, 0.8);
64
- border-color: #ff0000;
65
- box-shadow: 0 0 20px rgba(255, 0, 0, 0.4);
66
- }
67
-
68
- .menu-item h2 {
69
- margin: 0 0 0.5rem 0;
70
- font-size: 1.8rem;
71
- color: #ff0000;
72
- }
73
-
74
- .menu-item p {
75
- margin: 0;
76
- color: #cccccc;
77
- font-size: 1rem;
78
- }
79
-
80
- @keyframes flicker {
81
- 0%, 100% { opacity: 1; }
82
- 92% { opacity: 1; }
83
- 93% { opacity: 0.8; }
84
- 94% { opacity: 1; }
85
- 95% { opacity: 0.9; }
86
- 96% { opacity: 1; }
87
- }
88
-
89
- /* Blood drips */
90
- .blood-drip {
91
- position: fixed;
92
- top: 0;
93
- width: 3px;
94
- height: 60px;
95
- background: linear-gradient(to bottom, transparent, rgba(255, 0, 0, 0.8));
96
- animation: drip 4s infinite;
97
- opacity: 0.7;
98
- }
99
-
100
- @keyframes drip {
101
- 0% {
102
- transform: translateY(-60px);
103
- opacity: 0;
104
- }
105
- 50% {
106
- opacity: 0.7;
107
- }
108
- 100% {
109
- transform: translateY(100vh);
110
- opacity: 0;
111
- }
112
- }
113
-
114
-
115
- </style>
116
- </head>
117
- <body>
118
- <!-- Blood drip effects -->
119
- <div class="blood-drip" style="left: 15%"></div>
120
- <div class="blood-drip" style="left: 35%; animation-delay: 1.5s"></div>
121
- <div class="blood-drip" style="left: 55%; animation-delay: 2.5s"></div>
122
- <div class="blood-drip" style="left: 75%; animation-delay: 1s"></div>
123
- <div class="blood-drip" style="left: 95%; animation-delay: 2s"></div>
124
-
125
- <div class="container">
126
- <h1>THE LAST MESSAGE</h1>
127
- <div class="subtitle">Will your final words save them?</div>
128
-
129
- <div class="menu">
130
- <a href="game/" class="menu-item">
131
- <h2>📱 PLAY THE STORY</h2>
132
- <p>Experience the original nightmare</p>
133
- </a>
134
-
135
- <a href="maker/" class="menu-item">
136
- <h2>🏗️ CREATE & PLAY</h2>
137
- <p>Design and play your own horror scenario</p>
138
- </a>
139
- </div>
140
-
141
  </div>
142
 
143
- <script>
144
- // Add random delay and duration to blood drips
145
- document.querySelectorAll('.blood-drip').forEach(drip => {
146
- drip.style.animationDuration = (3 + Math.random() * 2) + 's';
147
- });
148
- </script>
149
- </body>
150
- </html>
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Through Their Eyes</title>
7
+ <link rel="stylesheet" href="assets/css/style.css">
8
+ </head>
9
+ <body>
10
+ <div class="led-bar">
11
+ <div class="light-beam"></div>
12
+ </div>
13
+ <div class="background-elements">
14
+ <img src="assets/img/blood-2.png" alt="" class="blood blood-top-left" />
15
+ <img src="assets/img/help.png" alt="" class="blood blood-top-right" />
16
+ <img src="assets/img/splatter.png" alt="" class="blood splatter" />
17
+ <img src="assets/img/hand.png" alt="" class="blood blood-bottom-right" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  </div>
19
 
20
+ <main>
21
+ <div class="logo">
22
+ <img src="assets/img/logo.png" alt="Through Their Eyes" />
23
+ </div>
24
+
25
+ <nav class="menu">
26
+ <a href="game/" class="menu-item new-game">NEW GAME</a>
27
+ <a href="how-to-play.html" class="menu-item how-to-play">HOW TO PLAY</a>
28
+ <a href="maker/" class="menu-item level-maker">LEVEL MAKER</a>
29
+ </nav>
30
+ <div class="character">
31
+ <img src="assets/img/perso.png" alt="Character" />
32
+ </div>
33
+ </main>
34
+ </body>
35
+ </html>
static/mistral/index.html ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mistral Chat Test Bed</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ max-width: 800px;
11
+ margin: 20px auto;
12
+ padding: 0 20px;
13
+ }
14
+
15
+ /* System Prompt Section */
16
+ #systemPromptSection {
17
+ margin-bottom: 20px;
18
+ padding: 10px;
19
+ background: #f8f9fa;
20
+ border-radius: 4px;
21
+ }
22
+
23
+ textarea {
24
+ width: 100%;
25
+ height: 150px;
26
+ margin: 10px 0;
27
+ padding: 8px;
28
+ font-family: monospace;
29
+ }
30
+
31
+ /* Chat Section */
32
+ #chatHistory {
33
+ max-height: 400px;
34
+ overflow-y: auto;
35
+ margin: 20px 0;
36
+ padding: 10px;
37
+ border: 1px solid #ddd;
38
+ border-radius: 4px;
39
+ }
40
+
41
+ .chat-message {
42
+ padding: 10px;
43
+ margin: 5px 0;
44
+ border-radius: 4px;
45
+ }
46
+
47
+ .user-message {
48
+ background: #e3f2fd;
49
+ }
50
+
51
+ .assistant-message {
52
+ background: #f5f5f5;
53
+ white-space: pre-wrap;
54
+ }
55
+
56
+ /* Raw JSON Section */
57
+ .raw-json {
58
+ font-family: monospace;
59
+ font-size: 0.9em;
60
+ color: #666;
61
+ margin-top: 5px;
62
+ padding: 5px;
63
+ background: #f8f9fa;
64
+ border-left: 3px solid #007bff;
65
+ }
66
+
67
+ /* Controls */
68
+ #controls {
69
+ display: flex;
70
+ gap: 10px;
71
+ margin: 20px 0;
72
+ }
73
+
74
+ input[type="text"] {
75
+ flex: 1;
76
+ padding: 8px;
77
+ }
78
+
79
+ button {
80
+ padding: 8px 16px;
81
+ background: #007bff;
82
+ color: white;
83
+ border: none;
84
+ border-radius: 4px;
85
+ cursor: pointer;
86
+ }
87
+
88
+ button:hover {
89
+ background: #0056b3;
90
+ }
91
+
92
+ /* Display Options */
93
+ .display-options {
94
+ margin: 10px 0;
95
+ padding: 10px;
96
+ background: #f8f9fa;
97
+ border-radius: 4px;
98
+ }
99
+ </style>
100
+ </head>
101
+ <body>
102
+ <h1>Mistral Chat Test Bed</h1>
103
+
104
+ <div id="systemPromptSection">
105
+ <h3>System Prompt</h3>
106
+ <textarea id="systemPrompt">You are the game master of "Get Me Out!", a single-player text-based survival game. The scenario: a player must guide their girlfriend to safety via text messages while she's being hunted by a murderous clown in their apartment. The player has access to security cameras and can give instructions through text.
107
+
108
+ Current state:
109
+ - Room: Main Bathroom
110
+ - Hiding: false
111
+ - Inventory: empty
112
+
113
+ RESPONSE FORMAT:
114
+ You must ALWAYS respond with a JSON object. The response should reflect the girlfriend's reaction to the player's message.
115
+
116
+ 1. For movement instructions ("go" action):
117
+ {
118
+ "action": "go",
119
+ "to": "[room name]",
120
+ "textMessage": "[girlfriend's response]"
121
+ }
122
+
123
+ 2. For any other input or unclear instructions:
124
+ {
125
+ "textMessage": "[girlfriend's response]"
126
+ }
127
+
128
+ VALID ROOMS:
129
+ Only these rooms are recognized for movement:
130
+ Main Bathroom
131
+ Guest Toilet
132
+ Dining Room
133
+ Kitchen
134
+ TV Room
135
+ Living Room
136
+ Hallway
137
+ Office
138
+ Bedroom
139
+
140
+ CHARACTER BEHAVIOR:
141
+ The girlfriend is aware of the danger and extremely distressed. Her text responses should be:
142
+ - Brief and urgent
143
+ - Reflect genuine fear and panic
144
+ - Written like real text messages (short, quick responses)
145
+ - No time for pleasantries or long explanations
146
+ - May include typos or rushed writing due to stress</textarea>
147
+ </div>
148
+
149
+ <div class="display-options">
150
+ <label>
151
+ <input type="checkbox" id="showRawJson" checked>
152
+ Show Raw JSON Response
153
+ </label>
154
+ </div>
155
+
156
+ <div id="chatHistory"></div>
157
+
158
+ <div id="controls">
159
+ <input type="text" id="userInput" placeholder="Type your message...">
160
+ <button onclick="sendMessage()">Send</button>
161
+ </div>
162
+
163
+ <div id="apiKeySection">
164
+ <h3>API Key</h3>
165
+ <input type="password" id="apiKey" placeholder="Enter Mistral API Key">
166
+ <button onclick="saveApiKey()">Save API Key</button>
167
+ </div>
168
+
169
+ <script>
170
+ let apiKey = localStorage.getItem('mistralApiKey');
171
+
172
+ if (apiKey) {
173
+ document.getElementById('apiKey').value = apiKey;
174
+ }
175
+
176
+ function saveApiKey() {
177
+ const key = document.getElementById('apiKey').value.trim();
178
+ if (key) {
179
+ apiKey = key;
180
+ localStorage.setItem('mistralApiKey', key);
181
+ alert('API key saved!');
182
+ }
183
+ }
184
+
185
+ function addMessage(role, content, rawJson = null) {
186
+ const chatHistory = document.getElementById('chatHistory');
187
+ const messageDiv = document.createElement('div');
188
+ messageDiv.className = `chat-message ${role}-message`;
189
+ messageDiv.textContent = content;
190
+
191
+ if (rawJson && document.getElementById('showRawJson').checked) {
192
+ const jsonDiv = document.createElement('div');
193
+ jsonDiv.className = 'raw-json';
194
+ jsonDiv.textContent = JSON.stringify(rawJson, null, 2);
195
+ messageDiv.appendChild(jsonDiv);
196
+ }
197
+
198
+ chatHistory.appendChild(messageDiv);
199
+ chatHistory.scrollTop = chatHistory.scrollHeight;
200
+ }
201
+
202
+ async function sendMessage() {
203
+ const userInput = document.getElementById('userInput');
204
+ const message = userInput.value.trim();
205
+
206
+ if (!message) return;
207
+ if (!apiKey) {
208
+ alert('Please enter your Mistral API key first');
209
+ return;
210
+ }
211
+
212
+ // Add user message to chat
213
+ addMessage('user', message);
214
+ userInput.value = '';
215
+
216
+ try {
217
+ const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
218
+ method: 'POST',
219
+ headers: {
220
+ 'Content-Type': 'application/json',
221
+ 'Authorization': `Bearer ${apiKey}`
222
+ },
223
+ body: JSON.stringify({
224
+ model: 'mistral-large-latest',
225
+ messages: [
226
+ {
227
+ role: 'system',
228
+ content: document.getElementById('systemPrompt').value
229
+ },
230
+ {
231
+ role: 'user',
232
+ content: message
233
+ }
234
+ ]
235
+ })
236
+ });
237
+
238
+ if (!response.ok) {
239
+ throw new Error(`HTTP error! status: ${response.status}`);
240
+ }
241
+
242
+ const data = await response.json();
243
+ const assistantResponse = data.choices[0].message.content;
244
+
245
+ try {
246
+ // Extract JSON from response
247
+ const jsonStart = assistantResponse.indexOf('{');
248
+ const jsonEnd = assistantResponse.lastIndexOf('}') + 1;
249
+ const jsonContent = assistantResponse.substring(jsonStart, jsonEnd);
250
+ const jsonResponse = JSON.parse(jsonContent);
251
+
252
+ // Add assistant message with both text and raw JSON
253
+ addMessage('assistant', jsonResponse.textMessage, jsonResponse);
254
+
255
+ } catch (e) {
256
+ console.error('Error parsing JSON from response:', e);
257
+ addMessage('assistant', assistantResponse);
258
+ }
259
+
260
+ } catch (error) {
261
+ addMessage('assistant', `Error: ${error.message}`);
262
+ }
263
+ }
264
+
265
+ // Allow sending message with Enter key
266
+ document.getElementById('userInput').addEventListener('keypress', function(e) {
267
+ if (e.key === 'Enter') {
268
+ e.preventDefault();
269
+ sendMessage();
270
+ }
271
+ });
272
+ </script>
273
+ </body>
274
+ </html>
test/.DS_Store ADDED
Binary file (6.15 kB). View file