Spaces:
Sleeping
Sleeping
Upload 114 files
Browse files- static/.DS_Store +0 -0
- static/assets/.DS_Store +0 -0
- static/assets/css/game-style.css +3 -1
- static/assets/css/how-to-play.css +3 -1
- static/assets/css/index-style.css +1 -1
- static/game/elmnts/character.js +5 -0
- static/game/elmnts/furniture.js +7 -34
- static/game/elmnts/girlfriend.js +191 -140
- static/game/game.js +0 -1
- static/game/gameState.js +71 -19
- static/game/index.html +92 -42
- static/game/utilities/draw.js +4 -44
static/.DS_Store
CHANGED
Binary files a/static/.DS_Store and b/static/.DS_Store differ
|
|
static/assets/.DS_Store
CHANGED
Binary files a/static/assets/.DS_Store and b/static/assets/.DS_Store differ
|
|
static/assets/css/game-style.css
CHANGED
@@ -8,7 +8,9 @@
|
|
8 |
position: relative;
|
9 |
overflow: hidden;
|
10 |
}
|
11 |
-
|
|
|
|
|
12 |
#gameContainer {
|
13 |
display: flex;
|
14 |
width: 100%;
|
|
|
8 |
position: relative;
|
9 |
overflow: hidden;
|
10 |
}
|
11 |
+
* {
|
12 |
+
opacity: 0.98;
|
13 |
+
}
|
14 |
#gameContainer {
|
15 |
display: flex;
|
16 |
width: 100%;
|
static/assets/css/how-to-play.css
CHANGED
@@ -10,7 +10,9 @@
|
|
10 |
align-items: center;
|
11 |
margin-top: 20px;
|
12 |
}
|
13 |
-
|
|
|
|
|
14 |
.content h1 {
|
15 |
font-family: "HorrorBrush", cursive;
|
16 |
font-size: 64px;
|
|
|
10 |
align-items: center;
|
11 |
margin-top: 20px;
|
12 |
}
|
13 |
+
* {
|
14 |
+
opacity: 0.98;
|
15 |
+
}
|
16 |
.content h1 {
|
17 |
font-family: "HorrorBrush", cursive;
|
18 |
font-size: 64px;
|
static/assets/css/index-style.css
CHANGED
@@ -7,8 +7,8 @@
|
|
7 |
margin: 0;
|
8 |
padding: 0;
|
9 |
box-sizing: border-box;
|
|
|
10 |
}
|
11 |
-
|
12 |
body {
|
13 |
background-color: #000000;
|
14 |
min-height: 100vh;
|
|
|
7 |
margin: 0;
|
8 |
padding: 0;
|
9 |
box-sizing: border-box;
|
10 |
+
opacity: 0.98;
|
11 |
}
|
|
|
12 |
body {
|
13 |
background-color: #000000;
|
14 |
min-height: 100vh;
|
static/game/elmnts/character.js
CHANGED
@@ -15,6 +15,10 @@ class Character {
|
|
15 |
return this.characterPos;
|
16 |
}
|
17 |
|
|
|
|
|
|
|
|
|
18 |
setCharacterPosition(pos) {
|
19 |
this.characterPos = pos;
|
20 |
}
|
@@ -72,6 +76,7 @@ class Character {
|
|
72 |
this.previousPos = { ...this.characterPos };
|
73 |
const nextPos = this.path.shift();
|
74 |
this.characterPos = nextPos;
|
|
|
75 |
}, this.speed);
|
76 |
}
|
77 |
|
|
|
15 |
return this.characterPos;
|
16 |
}
|
17 |
|
18 |
+
moving_check() {
|
19 |
+
|
20 |
+
}
|
21 |
+
|
22 |
setCharacterPosition(pos) {
|
23 |
this.characterPos = pos;
|
24 |
}
|
|
|
76 |
this.previousPos = { ...this.characterPos };
|
77 |
const nextPos = this.path.shift();
|
78 |
this.characterPos = nextPos;
|
79 |
+
this.moving_check();
|
80 |
}, this.speed);
|
81 |
}
|
82 |
|
static/game/elmnts/furniture.js
CHANGED
@@ -110,10 +110,7 @@ function getFurniture() {
|
|
110 |
},
|
111 |
"state": "searchable",
|
112 |
"in_use": true,
|
113 |
-
"
|
114 |
-
"reward":"Flashlight",
|
115 |
-
"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... 😵💫",
|
116 |
-
"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",
|
117 |
"sprite": [
|
118 |
{
|
119 |
"for": "searchable",
|
@@ -137,7 +134,7 @@ function getFurniture() {
|
|
137 |
"x": 3,
|
138 |
"y": 2
|
139 |
},
|
140 |
-
"state": "
|
141 |
"in_use": false,
|
142 |
"sprite": [
|
143 |
{
|
@@ -160,8 +157,8 @@ function getFurniture() {
|
|
160 |
"state": "usable",
|
161 |
"in_use": true,
|
162 |
"key":"Knife",
|
163 |
-
"locked_msg":"
|
164 |
-
"unlocked_msg":"
|
165 |
"sprite": [
|
166 |
{
|
167 |
"for": "usable",
|
@@ -205,7 +202,7 @@ function getFurniture() {
|
|
205 |
"state": "searchable",
|
206 |
"in_use": true,
|
207 |
"reward":"knife",
|
208 |
-
"
|
209 |
"sprite": [
|
210 |
{
|
211 |
"for": "searchable",
|
@@ -259,32 +256,8 @@ function getFurniture() {
|
|
259 |
"img": "/assets/img/appartment/stoveunsearchable.png"
|
260 |
}
|
261 |
]
|
262 |
-
}
|
263 |
-
|
264 |
-
"room":"Storage",
|
265 |
-
"wall_pos": {
|
266 |
-
"x": 6,
|
267 |
-
"y": 3
|
268 |
-
},
|
269 |
-
"stop_pos":{
|
270 |
-
"x": 7,
|
271 |
-
"y": 3
|
272 |
-
},
|
273 |
-
"state":"locked",
|
274 |
-
"in_use": false,
|
275 |
-
"locked_message":"No no no theres no way im going in there its dark as shit and its already fking scary out here ",
|
276 |
-
"unlocked_message":"Ok here goes...",
|
277 |
-
"sprite": [
|
278 |
-
{
|
279 |
-
"for": "locked",
|
280 |
-
"img": "/assets/img/appartment/storagelocked.png"
|
281 |
-
},
|
282 |
-
{
|
283 |
-
"for": "unlocked",
|
284 |
-
"img": "/assets/img/appartment/storageunlocked.png"
|
285 |
-
}
|
286 |
-
]
|
287 |
-
},{
|
288 |
"name":"The Exit",
|
289 |
"room":"Main Hallway",
|
290 |
"in_use": true,
|
|
|
110 |
},
|
111 |
"state": "searchable",
|
112 |
"in_use": true,
|
113 |
+
"msg":"There's nothing useful in here...",
|
|
|
|
|
|
|
114 |
"sprite": [
|
115 |
{
|
116 |
"for": "searchable",
|
|
|
134 |
"x": 3,
|
135 |
"y": 2
|
136 |
},
|
137 |
+
"state": "on",
|
138 |
"in_use": false,
|
139 |
"sprite": [
|
140 |
{
|
|
|
157 |
"state": "usable",
|
158 |
"in_use": true,
|
159 |
"key":"Knife",
|
160 |
+
"locked_msg":"Um... what. the. f. Why is its mouth stitched up? Is the key inside??? i cant open it",
|
161 |
+
"unlocked_msg":"No no no ew ew i can't believe im doing this...😭😭😭 ....the key was in here i got it!! I can get out!",
|
162 |
"sprite": [
|
163 |
{
|
164 |
"for": "usable",
|
|
|
202 |
"state": "searchable",
|
203 |
"in_use": true,
|
204 |
"reward":"knife",
|
205 |
+
"msg":"Omg a knife 🔪... i just took it but there's no way i can kill the creature with this.",
|
206 |
"sprite": [
|
207 |
{
|
208 |
"for": "searchable",
|
|
|
256 |
"img": "/assets/img/appartment/stoveunsearchable.png"
|
257 |
}
|
258 |
]
|
259 |
+
}
|
260 |
+
,{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
"name":"The Exit",
|
262 |
"room":"Main Hallway",
|
263 |
"in_use": true,
|
static/game/elmnts/girlfriend.js
CHANGED
@@ -1,146 +1,197 @@
|
|
1 |
class Girlfriend extends Character {
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
);
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
}
|
44 |
-
|
45 |
-
pop();
|
46 |
}
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
0,
|
53 |
-
Math.min(100, this.stressLevel + stressChange)
|
54 |
-
);
|
55 |
-
}
|
56 |
-
|
57 |
-
handleAction(response) {
|
58 |
-
switch (response.action) {
|
59 |
-
case "go":
|
60 |
-
this.moveToRoom(response.target);
|
61 |
-
break;
|
62 |
-
case "hide":
|
63 |
-
this.hide(response.target);
|
64 |
-
break;
|
65 |
-
case "exit":
|
66 |
-
this.exit();
|
67 |
-
break;
|
68 |
}
|
69 |
-
}
|
70 |
-
|
71 |
-
getAvailableHidingSpots() {
|
72 |
-
// Find the current room in map data
|
73 |
-
const currentRoom = this.gameState.map_data.rooms.find(
|
74 |
-
(room) => room.name === this.getCurrentRoom()
|
75 |
-
);
|
76 |
-
// Return hiding places for current room if found, otherwise empty array
|
77 |
-
return currentRoom ? currentRoom.hiding_places : [];
|
78 |
-
}
|
79 |
-
|
80 |
-
getAvailableFurniture() {
|
81 |
-
// Get current room
|
82 |
-
const currentRoom = this.getCurrentRoom();
|
83 |
-
if (!currentRoom) return [];
|
84 |
-
|
85 |
-
// Filter furniture list to only include items in current room that are in use
|
86 |
-
return this.gameState.map_data.furniture.filter(
|
87 |
-
furniture => furniture.room === currentRoom && furniture.in_use === true
|
88 |
-
);
|
89 |
-
}
|
90 |
-
|
91 |
-
getIsHiding() {
|
92 |
-
const hidingSpots = this.getAvailableHidingSpots();
|
93 |
-
if (!this.characterPos || hidingSpots.length === 0) return false;
|
94 |
-
|
95 |
-
return hidingSpots.some(
|
96 |
-
(spot) =>
|
97 |
-
spot.position.x === this.characterPos.x &&
|
98 |
-
spot.position.y === this.characterPos.y
|
99 |
-
);
|
100 |
-
}
|
101 |
-
|
102 |
-
hide(hidingSpotName) {
|
103 |
-
const currentRoom = this.getCurrentRoom();
|
104 |
-
if (!currentRoom) return;
|
105 |
-
|
106 |
-
// Find the room data from map_data
|
107 |
-
const roomData = this.gameState.map_data.rooms.find(
|
108 |
-
(room) => room.name === currentRoom
|
109 |
-
);
|
110 |
-
if (!roomData) return;
|
111 |
-
|
112 |
-
// Find the specific hiding spot in the room by checking if names contain each other
|
113 |
-
const hidingSpot = roomData.hiding_places.find(
|
114 |
-
(spot) =>
|
115 |
-
spot.name
|
116 |
-
.toLowerCase()
|
117 |
-
.trim()
|
118 |
-
.includes(hidingSpotName.toLowerCase().trim()) ||
|
119 |
-
hidingSpotName
|
120 |
-
.toLowerCase()
|
121 |
-
.trim()
|
122 |
-
.includes(spot.name.toLowerCase().trim())
|
123 |
-
);
|
124 |
-
|
125 |
-
if (!hidingSpot) return;
|
126 |
-
|
127 |
-
// Move to the hiding spot position
|
128 |
-
this.moveToPosition(hidingSpot.position, () => {
|
129 |
-
this.currentHidingSpot = hidingSpotName;
|
130 |
-
});
|
131 |
-
}
|
132 |
-
|
133 |
-
exit() {
|
134 |
-
let the_exit = this.gameState.getTheExit();
|
135 |
-
let x = the_exit.pos.x;
|
136 |
-
let y = the_exit.pos.y-1;
|
137 |
-
let pos = {"x": x, "y": y};
|
138 |
-
this.moveToPosition(pos, () => {
|
139 |
-
if (!this.inventory || !this.inventory.includes("Key")) {
|
140 |
-
addProgramaticMessage(the_exit.locked_message);
|
141 |
-
} else {
|
142 |
-
addProgramaticMessage(the_exit.unlocked_message);
|
143 |
-
}
|
144 |
-
});
|
145 |
-
}
|
146 |
}
|
|
|
1 |
class Girlfriend extends Character {
|
2 |
+
constructor(gameState, girlfriendImg) {
|
3 |
+
super(gameState, girlfriendImg);
|
4 |
+
this.gameState = gameState;
|
5 |
+
this.gameState.girlfriend = this;
|
6 |
+
this.img = girlfriendImg;
|
7 |
+
this.setCharacterPosition(this.gameState.map_data.girlfriend_start_pos);
|
8 |
+
this.currentHidingSpot = null;
|
9 |
+
this.stressLevel = 30;
|
10 |
+
this.inventory = [];
|
11 |
+
this.knows_about_dead_body = false;
|
12 |
+
}
|
13 |
+
|
14 |
+
draw(CELL_SIZE) {
|
15 |
+
if (this.characterPos) {
|
16 |
+
push();
|
17 |
+
if (this.previousPos && this.characterPos.x > this.previousPos.x) {
|
18 |
+
scale(-1, 1);
|
19 |
+
image(
|
20 |
+
this.img,
|
21 |
+
-this.characterPos.x * CELL_SIZE - CELL_SIZE * 2,
|
22 |
+
this.characterPos.y * CELL_SIZE,
|
23 |
+
CELL_SIZE * 2,
|
24 |
+
CELL_SIZE * 2
|
25 |
+
);
|
26 |
+
} else {
|
27 |
+
if (this.getIsHiding()) {
|
28 |
+
tint(255, 153);
|
29 |
+
image(
|
30 |
+
this.img,
|
31 |
+
this.characterPos.x * CELL_SIZE,
|
32 |
+
this.characterPos.y * CELL_SIZE,
|
33 |
+
CELL_SIZE * 2,
|
34 |
+
CELL_SIZE * 2
|
35 |
+
);
|
36 |
+
} else {
|
37 |
+
image(
|
38 |
+
this.img,
|
39 |
+
this.characterPos.x * CELL_SIZE,
|
40 |
+
this.characterPos.y * CELL_SIZE,
|
41 |
+
CELL_SIZE * 2,
|
42 |
+
CELL_SIZE * 2
|
43 |
+
);
|
44 |
+
}
|
45 |
+
}
|
46 |
+
pop();
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
updateStressLevel(stressChange) {
|
51 |
+
// Add or subtract the stress change while keeping within 0-100 range
|
52 |
+
this.stressLevel = Math.max(
|
53 |
+
0,
|
54 |
+
Math.min(100, this.stressLevel + stressChange)
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
+
handleAction(response) {
|
59 |
+
switch (response.action) {
|
60 |
+
case "go":
|
61 |
+
this.moveToRoom(response.target);
|
62 |
+
break;
|
63 |
+
case "hide":
|
64 |
+
this.hide(response.target);
|
65 |
+
break;
|
66 |
+
case "exit":
|
67 |
+
this.exit();
|
68 |
+
break;
|
69 |
+
case "check":
|
70 |
+
this.check(response.target)
|
71 |
+
break;
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
getAvailableHidingSpots() {
|
76 |
+
// Find the current room in map data
|
77 |
+
const currentRoom = this.gameState.map_data.rooms.find(
|
78 |
+
(room) => room.name === this.getCurrentRoom()
|
79 |
+
);
|
80 |
+
// Return hiding places for current room if found, otherwise empty array
|
81 |
+
return currentRoom ? currentRoom.hiding_places : [];
|
82 |
+
}
|
83 |
+
|
84 |
+
getAvailableFurniture() {
|
85 |
+
// Get current room
|
86 |
+
const currentRoom = this.getCurrentRoom();
|
87 |
+
if (!currentRoom) return [];
|
88 |
+
|
89 |
+
// Filter furniture list to only include items in current room that are in use
|
90 |
+
return this.gameState.map_data.furniture.filter(
|
91 |
+
furniture => furniture.room === currentRoom && furniture.in_use === true
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
getKnowsAboutDeadBody() {
|
96 |
+
return this.knows_about_dead_body;
|
97 |
+
}
|
98 |
+
|
99 |
+
getInventory() {
|
100 |
+
return this.inventory;
|
101 |
+
}
|
102 |
+
|
103 |
+
getIsHiding() {
|
104 |
+
const hidingSpots = this.getAvailableHidingSpots();
|
105 |
+
if (!this.characterPos || hidingSpots.length === 0) return false;
|
106 |
+
|
107 |
+
return hidingSpots.some(
|
108 |
+
(spot) =>
|
109 |
+
spot.position.x === this.characterPos.x &&
|
110 |
+
spot.position.y === this.characterPos.y
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
hide(hidingSpotName) {
|
115 |
+
const currentRoom = this.getCurrentRoom();
|
116 |
+
if (!currentRoom) return;
|
117 |
+
|
118 |
+
// Find the room data from map_data
|
119 |
+
const roomData = this.gameState.map_data.rooms.find(
|
120 |
+
(room) => room.name === currentRoom
|
121 |
);
|
122 |
+
if (!roomData) return;
|
123 |
+
|
124 |
+
// Find the specific hiding spot in the room by checking if names contain each other
|
125 |
+
const hidingSpot = roomData.hiding_places.find(
|
126 |
+
(spot) =>
|
127 |
+
spot.name
|
128 |
+
.toLowerCase()
|
129 |
+
.trim()
|
130 |
+
.includes(hidingSpotName.toLowerCase().trim()) ||
|
131 |
+
hidingSpotName
|
132 |
+
.toLowerCase()
|
133 |
+
.trim()
|
134 |
+
.includes(spot.name.toLowerCase().trim())
|
135 |
+
);
|
136 |
+
|
137 |
+
if (!hidingSpot) return;
|
138 |
+
|
139 |
+
// Move to the hiding spot position
|
140 |
+
this.moveToPosition(hidingSpot.position, () => {
|
141 |
+
this.currentHidingSpot = hidingSpotName;
|
142 |
+
});
|
143 |
+
}
|
144 |
+
|
145 |
+
exit() {
|
146 |
+
let the_exit = this.gameState.getTheExit();
|
147 |
+
let x = the_exit.pos.x;
|
148 |
+
let y = the_exit.pos.y - 1;
|
149 |
+
let pos = { "x": x, "y": y };
|
150 |
+
this.moveToPosition(pos, () => {
|
151 |
+
if (!this.inventory || !this.inventory.includes("Key")) {
|
152 |
+
addProgramaticMessage(the_exit.locked_message);
|
153 |
+
} else {
|
154 |
+
addProgramaticMessage(the_exit.unlocked_message);
|
155 |
+
}
|
156 |
+
});
|
157 |
+
}
|
158 |
+
|
159 |
+
check(target) {
|
160 |
+
if (target === "Bookcase") {
|
161 |
+
let bookcase = this.gameState.getBookcase();
|
162 |
+
this.moveToPosition(bookcase.pos, () => {
|
163 |
+
addProgramaticMessage(bookcase.msg);
|
164 |
+
bookcase.state = "unusable";
|
165 |
+
});
|
166 |
+
} else if (target === "Cabinet") {
|
167 |
+
let cabinet = this.gameState.getCabinet();
|
168 |
+
this.moveToPosition(cabinet.pos, () => {
|
169 |
+
addProgramaticMessage(cabinet.msg);
|
170 |
+
this.inventory.push("Knife");
|
171 |
+
cabinet.state = "unsearchable";
|
172 |
+
});
|
173 |
+
} else if (target === "Dead Body") {
|
174 |
+
let deadBody = this.gameState.map_data.furniture.find(item => item.name === "Dead Body");
|
175 |
+
this.moveToPosition(deadBody.pos, () => {
|
176 |
+
|
177 |
+
if (this.inventory.includes("Knife")) {
|
178 |
+
addProgramaticMessage("No no no ew ew i can't believe im doing this...😭😭😭 ....the key was in here i got it!! I can get out!");
|
179 |
+
deadBody.state = "unusable";
|
180 |
+
this.inventory.push("Key");
|
181 |
+
} else {
|
182 |
+
addProgramaticMessage("Um... what. the. f. Why is its mouth stitched up? Is the key inside??? i cant open it");
|
183 |
+
}
|
184 |
+
|
185 |
+
});
|
186 |
+
|
187 |
+
|
188 |
}
|
189 |
+
|
|
|
190 |
}
|
191 |
+
|
192 |
+
moving_check() {
|
193 |
+
if (this.getCurrentRoom() === "Storage") {
|
194 |
+
this.knows_about_dead_body = true;
|
195 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
}
|
static/game/game.js
CHANGED
@@ -61,7 +61,6 @@ function draw() {
|
|
61 |
|
62 |
drawLabels(gameState.map_data.rooms);
|
63 |
|
64 |
-
drawStorage()
|
65 |
|
66 |
|
67 |
if (girlfriend) {
|
|
|
61 |
|
62 |
drawLabels(gameState.map_data.rooms);
|
63 |
|
|
|
64 |
|
65 |
|
66 |
if (girlfriend) {
|
static/game/gameState.js
CHANGED
@@ -39,11 +39,15 @@ class GameState {
|
|
39 |
}
|
40 |
|
41 |
getTheExit() {
|
42 |
-
return this.map_data.furniture.find(
|
|
|
|
|
43 |
}
|
44 |
|
45 |
getStorage() {
|
46 |
-
return this.map_data.furniture.find(
|
|
|
|
|
47 |
}
|
48 |
|
49 |
getStressPrompt() {
|
@@ -64,11 +68,38 @@ The final stress level after applying the change must stay between 0 and 100.
|
|
64 |
|
65 |
Examples:
|
66 |
"Hide quickly!" -> {"stressChange": 40}
|
67 |
-
"You're safe now, take a deep breath" -> {"stressChange": -
|
68 |
-
"The killer is right behind you!" -> {"stressChange":
|
69 |
-
"Let's think about this calmly" -> {"stressChange": -
|
70 |
}
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
getPrompt() {
|
73 |
return `
|
74 |
|
@@ -88,7 +119,13 @@ ${
|
|
88 |
: "- You have no idea where the clown is, you will hide if you can, if not leave the room"
|
89 |
}
|
90 |
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
RESPONSE FORMAT:
|
93 |
You must ALWAYS respond with a JSON object.
|
94 |
Your response should reflect a girlfriend's reaction to her boyfriend's message given this context and following the following structure:
|
@@ -100,12 +137,39 @@ For movement instructions ("go" action):
|
|
100 |
"textMessage": "[girlfriend's response]"
|
101 |
}
|
102 |
|
|
|
|
|
|
|
|
|
|
|
103 |
For hiding instructions ("hide" action):
|
104 |
{
|
105 |
"action": "hide",
|
106 |
"target": "[hiding place name]",
|
107 |
"textMessage": "[girlfriend's response]"
|
108 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
|
110 |
For exiting the house by the exit in the mainhallway instructions ("exit" action):
|
111 |
{
|
@@ -114,25 +178,13 @@ For exiting the house by the exit in the mainhallway instructions ("exit" action
|
|
114 |
"textMessage": "[girlfriend's response]"
|
115 |
}
|
116 |
|
|
|
117 |
For any other input:
|
118 |
{
|
119 |
"textMessage": "[girlfriend's response]"
|
120 |
}
|
121 |
|
122 |
-
VALID ROOMS:
|
123 |
-
You are only allowed to move to these rooms no other rooms are recognized:
|
124 |
-
${this.map_data.rooms.map((room) => room.name).join(",\n")}
|
125 |
|
126 |
-
VALID HIDING PLACES:
|
127 |
-
You are only allowed to hide in these hiding spots no other hiding spots are recognized:
|
128 |
-
${
|
129 |
-
this.girlfriend.getAvailableHidingSpots().length > 0
|
130 |
-
? `- You may hide in these available hiding spots (and thats it): ${this.girlfriend
|
131 |
-
.getAvailableHidingSpots()
|
132 |
-
.map((spot) => `${spot.hiding_type} ${spot.name}`)
|
133 |
-
.join(", ")}`
|
134 |
-
: "- There are no available hiding spots in this room"
|
135 |
-
}
|
136 |
|
137 |
YOUR BEHAVIOR:
|
138 |
- You are aware of the danger and are extremely distressed.
|
|
|
39 |
}
|
40 |
|
41 |
getTheExit() {
|
42 |
+
return this.map_data.furniture.find(
|
43 |
+
(furniture) => furniture.name === "The Exit"
|
44 |
+
);
|
45 |
}
|
46 |
|
47 |
getStorage() {
|
48 |
+
return this.map_data.furniture.find(
|
49 |
+
(furniture) => furniture.name === "Storage"
|
50 |
+
);
|
51 |
}
|
52 |
|
53 |
getStressPrompt() {
|
|
|
68 |
|
69 |
Examples:
|
70 |
"Hide quickly!" -> {"stressChange": 40}
|
71 |
+
"You're safe now, take a deep breath" -> {"stressChange": -40}
|
72 |
+
"The killer is right behind you!" -> {"stressChange": 30}
|
73 |
+
"Let's think about this calmly" -> {"stressChange": -40}`;
|
74 |
}
|
75 |
|
76 |
+
getFirstPrompt() {
|
77 |
+
return `
|
78 |
+
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.
|
79 |
+
You are texting your boyfriend a text message to let him know you are in danger and that you absolutely need help.
|
80 |
+
|
81 |
+
RESPONSE FORMAT:
|
82 |
+
You must ALWAYS respond with a JSON object.
|
83 |
+
{
|
84 |
+
"textMessage": "[girlfriend's response]"
|
85 |
+
}
|
86 |
+
|
87 |
+
YOUR BEHAVIOUR:
|
88 |
+
--You are now in the bedroom
|
89 |
+
Your text responses should be:
|
90 |
+
-- Brief and urgent
|
91 |
+
-- Reflect genuine fear and panic
|
92 |
+
-- Write like real text messages (short, quick responses)
|
93 |
+
-- May include typos or rushed writing due to stress
|
94 |
+
--You are very scared but you are also super brave to be honest
|
95 |
+
--Don't name your partner
|
96 |
+
|
97 |
+
EXAMPLE RESPONSE:
|
98 |
+
{
|
99 |
+
"textMessage": "hi babe I think there's someone... or something... in the house, I'm so scared. Help me please!!!!"
|
100 |
+
}
|
101 |
+
`;
|
102 |
+
}
|
103 |
getPrompt() {
|
104 |
return `
|
105 |
|
|
|
119 |
: "- You have no idea where the clown is, you will hide if you can, if not leave the room"
|
120 |
}
|
121 |
|
122 |
+
${
|
123 |
+
this.girlfriend.getInventory().length > 0
|
124 |
+
? `- You have the following items in your inventory: ${this.girlfriend
|
125 |
+
.getInventory()
|
126 |
+
.join(", ")}`
|
127 |
+
: ""
|
128 |
+
}
|
129 |
RESPONSE FORMAT:
|
130 |
You must ALWAYS respond with a JSON object.
|
131 |
Your response should reflect a girlfriend's reaction to her boyfriend's message given this context and following the following structure:
|
|
|
137 |
"textMessage": "[girlfriend's response]"
|
138 |
}
|
139 |
|
140 |
+
VALID ROOMS:
|
141 |
+
You are only allowed to move to these rooms no other rooms are recognized:
|
142 |
+
${this.map_data.rooms.map((room) => room.name).join(",\n")}
|
143 |
+
|
144 |
+
|
145 |
For hiding instructions ("hide" action):
|
146 |
{
|
147 |
"action": "hide",
|
148 |
"target": "[hiding place name]",
|
149 |
"textMessage": "[girlfriend's response]"
|
150 |
}
|
151 |
+
VALID HIDING PLACES:
|
152 |
+
You are only allowed to hide in these hiding spots no other hiding spots are recognized:
|
153 |
+
${
|
154 |
+
this.girlfriend.getAvailableHidingSpots().length > 0
|
155 |
+
? `- You may hide in these available hiding spots (and thats it): ${this.girlfriend
|
156 |
+
.getAvailableHidingSpots()
|
157 |
+
.map((spot) => `[${spot.hiding_type} ${spot.name}]`)
|
158 |
+
.join(", ")}`
|
159 |
+
: "- There are no available hiding spots in this room"
|
160 |
+
}
|
161 |
+
|
162 |
+
|
163 |
+
For checking/inquiring/going to apecifically the Cabinet or Bookcase instructions ("check" action):
|
164 |
+
{
|
165 |
+
"action": "check",
|
166 |
+
"target": "she can only check \"Cabinet\" (this is in the kitchen, you can ask if youre not sure) or \"Bookcase\" (this is in the guest bedroom you can ask if youre not sure).${
|
167 |
+
this.girlfriend.getKnowsAboutDeadBody()
|
168 |
+
? ' or "Dead Body" (in the Storage room)'
|
169 |
+
: "you might be asked to check other things be open minded about it ask where, be freaked out if nncecessarry"
|
170 |
+
}
|
171 |
+
"textMessage": "[girlfriend's response]"
|
172 |
+
}
|
173 |
|
174 |
For exiting the house by the exit in the mainhallway instructions ("exit" action):
|
175 |
{
|
|
|
178 |
"textMessage": "[girlfriend's response]"
|
179 |
}
|
180 |
|
181 |
+
|
182 |
For any other input:
|
183 |
{
|
184 |
"textMessage": "[girlfriend's response]"
|
185 |
}
|
186 |
|
|
|
|
|
|
|
187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
|
189 |
YOUR BEHAVIOR:
|
190 |
- You are aware of the danger and are extremely distressed.
|
static/game/index.html
CHANGED
@@ -1,70 +1,98 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
-
|
4 |
-
<head>
|
5 |
<title>The Last Message - A text based horror game</title>
|
6 |
<link rel="stylesheet" href="/static/assets/css/game-style.css" />
|
7 |
-
</head>
|
8 |
|
9 |
-
<body>
|
10 |
<div class="led-bar">
|
11 |
-
|
12 |
</div>
|
13 |
<div class="background-elements">
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
</div>
|
19 |
<div id="gameContainer">
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
</div>
|
24 |
</div>
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
</div>
|
|
|
48 |
</div>
|
|
|
49 |
</div>
|
50 |
|
51 |
<div class="sound-button" onclick="toggleSound()">
|
52 |
-
|
|
|
|
|
|
|
|
|
53 |
</div>
|
54 |
|
55 |
<audio id="bgMusic" loop>
|
56 |
-
|
57 |
</audio>
|
58 |
|
59 |
<audio id="messageSound">
|
60 |
-
|
61 |
</audio>
|
62 |
|
63 |
<audio id="walkingSound" loop>
|
64 |
-
|
65 |
</audio>
|
66 |
|
67 |
-
|
68 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
69 |
<script src="/static/game/elmnts/furniture.js"></script>
|
70 |
<script src="/static/game/gameState.js"></script>
|
@@ -80,6 +108,28 @@
|
|
80 |
<script src="/static/game/utilities/draw.js"></script>
|
81 |
<script src="/static/game/game.js"></script>
|
82 |
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
+
<head>
|
|
|
4 |
<title>The Last Message - A text based horror game</title>
|
5 |
<link rel="stylesheet" href="/static/assets/css/game-style.css" />
|
6 |
+
</head>
|
7 |
|
8 |
+
<body>
|
9 |
<div class="led-bar">
|
10 |
+
<div class="light-beam"></div>
|
11 |
</div>
|
12 |
<div class="background-elements">
|
13 |
+
<img
|
14 |
+
src="/static/assets/img/blood-2.png"
|
15 |
+
alt=""
|
16 |
+
class="blood blood-top-left"
|
17 |
+
/>
|
18 |
+
<img
|
19 |
+
src="/static/assets/img/help.png"
|
20 |
+
alt=""
|
21 |
+
class="blood blood-top-right"
|
22 |
+
/>
|
23 |
+
<img
|
24 |
+
src="/static/assets/img/splatter.png"
|
25 |
+
alt=""
|
26 |
+
class="blood splatter"
|
27 |
+
/>
|
28 |
+
<img
|
29 |
+
src="/static/assets/img/hand.png"
|
30 |
+
alt=""
|
31 |
+
class="blood blood-bottom-right"
|
32 |
+
/>
|
33 |
</div>
|
34 |
<div id="gameContainer">
|
35 |
+
<div id="mapSection">
|
36 |
+
<div id="mapWrapper" class="map-wrapper">
|
37 |
+
<!-- P5.js canvas goes here -->
|
|
|
38 |
</div>
|
39 |
+
</div>
|
40 |
+
<div class="phone-mockup">
|
41 |
+
<div class="phone-screen">
|
42 |
+
<div id="chatSection">
|
43 |
+
<div id="chatHeader">
|
44 |
+
<div class="profile-picture">
|
45 |
+
<!-- L'image sera ajoutée ici plus tard -->
|
46 |
+
</div>
|
47 |
+
<div class="chat-name">Bae 💖😘</div>
|
48 |
+
</div>
|
49 |
+
<div id="chatHistory"></div>
|
50 |
+
<div id="chatControls">
|
51 |
+
<input
|
52 |
+
type="text"
|
53 |
+
id="prompt"
|
54 |
+
placeholder="Type your message..."
|
55 |
+
/>
|
56 |
+
<button onclick="Message()">
|
57 |
+
<svg
|
58 |
+
width="683"
|
59 |
+
height="683"
|
60 |
+
viewBox="0 0 683 683"
|
61 |
+
fill="none"
|
62 |
+
xmlns="http://www.w3.org/2000/svg"
|
63 |
+
>
|
64 |
+
<path
|
65 |
+
d="M22.2666 1.86666C19.5999 2.93333 16.5333 4.39999 15.4666 5.19999C11.9999 8.13333 9.33325 15.7333 9.33325 22.5333C9.33325 28.1333 65.4666 283.333 66.9333 284.667C67.3333 285.067 339.333 332.267 372.4 337.733C381.733 339.333 389.333 340.933 389.333 341.333C389.333 341.733 381.733 343.333 372.4 344.933C339.2 350.4 67.3333 397.6 66.9333 398C65.4666 399.333 9.33325 654.533 9.33325 660.133C9.33325 675.867 21.0666 685.333 35.8666 681.333C42.6666 679.467 662.267 362 666.4 358.133C671.333 353.733 673.333 348.8 673.333 341.333C673.333 333.867 671.333 328.933 666.4 324.533C662.533 320.933 43.0666 3.19999 36.2666 1.33333C29.9999 -0.400007 28.5333 -0.266674 22.2666 1.86666Z"
|
66 |
+
fill="white"
|
67 |
+
/>
|
68 |
+
</svg>
|
69 |
+
</button>
|
70 |
</div>
|
71 |
+
</div>
|
72 |
</div>
|
73 |
+
</div>
|
74 |
</div>
|
75 |
|
76 |
<div class="sound-button" onclick="toggleSound()">
|
77 |
+
<img
|
78 |
+
src="/static/assets/img/sondon.png"
|
79 |
+
alt="Sound control"
|
80 |
+
id="soundIcon"
|
81 |
+
/>
|
82 |
</div>
|
83 |
|
84 |
<audio id="bgMusic" loop>
|
85 |
+
<source src="/static/assets/sounds/music.mp3" type="audio/mp3" />
|
86 |
</audio>
|
87 |
|
88 |
<audio id="messageSound">
|
89 |
+
<source src="/static/assets/sounds/message.wav" type="audio/wav" />
|
90 |
</audio>
|
91 |
|
92 |
<audio id="walkingSound" loop>
|
93 |
+
<source src="/static/assets/sounds/walking.mp3" type="audio/mp3" />
|
94 |
</audio>
|
95 |
|
|
|
96 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
97 |
<script src="/static/game/elmnts/furniture.js"></script>
|
98 |
<script src="/static/game/gameState.js"></script>
|
|
|
108 |
<script src="/static/game/utilities/draw.js"></script>
|
109 |
<script src="/static/game/game.js"></script>
|
110 |
|
111 |
+
<script>
|
112 |
+
window.onload = async function () {
|
113 |
+
// Get the initial prompt from gameState
|
114 |
+
const firstPrompt = gameState.getFirstPrompt();
|
115 |
+
|
116 |
+
console.log("firstPrompt", firstPrompt);
|
117 |
+
|
118 |
+
const messages = [
|
119 |
+
{
|
120 |
+
role: "system",
|
121 |
+
content: firstPrompt,
|
122 |
+
},
|
123 |
+
];
|
124 |
|
125 |
+
try {
|
126 |
+
const response = await mistralAPI.sendMessage(messages);
|
127 |
+
const jsonResponse = JSON.parse(response);
|
128 |
+
addMessageToChat("assistant", jsonResponse.textMessage);
|
129 |
+
} catch (error) {
|
130 |
+
console.error("Error getting initial message:", error);
|
131 |
+
}
|
132 |
+
};
|
133 |
+
</script>
|
134 |
+
</body>
|
135 |
+
</html>
|
static/game/utilities/draw.js
CHANGED
@@ -30,24 +30,7 @@ function drawLabels(rooms) {
|
|
30 |
// Draw room label
|
31 |
textAlign(CENTER, CENTER);
|
32 |
textSize(14);
|
33 |
-
|
34 |
-
if (room.name === 'Storage') {
|
35 |
-
// For Storage room, add white background
|
36 |
-
const labelWidth = textWidth(room.name);
|
37 |
-
const labelHeight = textAscent() + textDescent();
|
38 |
-
push();
|
39 |
-
rectMode(CENTER);
|
40 |
-
fill(255); // White background
|
41 |
-
noStroke();
|
42 |
-
rect(
|
43 |
-
room.label_position.x * CELL_SIZE + CELL_SIZE / 2,
|
44 |
-
room.label_position.y * CELL_SIZE + CELL_SIZE / 2,
|
45 |
-
labelWidth,
|
46 |
-
labelHeight
|
47 |
-
);
|
48 |
-
pop();
|
49 |
-
}
|
50 |
-
|
51 |
fill(0);
|
52 |
text(
|
53 |
room.name,
|
@@ -72,14 +55,14 @@ function drawLabels(rooms) {
|
|
72 |
|
73 |
// Measure exactly the text size (no extra padding)
|
74 |
const labelWidth = textWidth(hidingSpot.name);
|
75 |
-
const labelHeight = textAscent()
|
76 |
|
77 |
// Draw a centered background rectangle
|
78 |
// that's just as wide/tall as the text
|
79 |
rectMode(CENTER);
|
80 |
fill('#f1c94f');
|
81 |
noStroke();
|
82 |
-
rect(x, y, labelWidth, labelHeight/2, 2);
|
83 |
pop();
|
84 |
|
85 |
// Draw the text on top
|
@@ -148,7 +131,7 @@ function drawFurniture(furniture, sprites) {
|
|
148 |
|
149 |
// Draw all labels second
|
150 |
for (let item of furniture) {
|
151 |
-
if (item.pos && item.in_use == true
|
152 |
push();
|
153 |
// Draw light gray background
|
154 |
fill(220);
|
@@ -168,26 +151,3 @@ function drawFurniture(furniture, sprites) {
|
|
168 |
}
|
169 |
}
|
170 |
|
171 |
-
function drawStorage() {
|
172 |
-
let storage = gameState.getStorage();
|
173 |
-
if (storage && storage.pos && storage.in_use == true) {
|
174 |
-
push();
|
175 |
-
// Draw light gray background
|
176 |
-
fill(220);
|
177 |
-
noStroke();
|
178 |
-
const padding = 4;
|
179 |
-
const textWidth = textSize() * storage.name.length * 0.6;
|
180 |
-
rectMode(CENTER);
|
181 |
-
rect((storage.pos.x + 0.5) * CELL_SIZE, (storage.pos.y + 0.5) * CELL_SIZE, textWidth, 20);
|
182 |
-
|
183 |
-
// Draw black text
|
184 |
-
fill(0);
|
185 |
-
textAlign(CENTER, CENTER);
|
186 |
-
textSize(12);
|
187 |
-
text(storage.name, (storage.pos.x + 0.5) * CELL_SIZE, (storage.pos.y + 0.5) * CELL_SIZE);
|
188 |
-
|
189 |
-
// Draw second "Storage" text on top
|
190 |
-
text(storage.name, (storage.pos.x + 0.5) * CELL_SIZE, (storage.pos.y + 0.5) * CELL_SIZE - 20);
|
191 |
-
pop();
|
192 |
-
}
|
193 |
-
}
|
|
|
30 |
// Draw room label
|
31 |
textAlign(CENTER, CENTER);
|
32 |
textSize(14);
|
33 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
fill(0);
|
35 |
text(
|
36 |
room.name,
|
|
|
55 |
|
56 |
// Measure exactly the text size (no extra padding)
|
57 |
const labelWidth = textWidth(hidingSpot.name);
|
58 |
+
const labelHeight = textAscent();
|
59 |
|
60 |
// Draw a centered background rectangle
|
61 |
// that's just as wide/tall as the text
|
62 |
rectMode(CENTER);
|
63 |
fill('#f1c94f');
|
64 |
noStroke();
|
65 |
+
rect(x, y, labelWidth, labelHeight / 2, 2);
|
66 |
pop();
|
67 |
|
68 |
// Draw the text on top
|
|
|
131 |
|
132 |
// Draw all labels second
|
133 |
for (let item of furniture) {
|
134 |
+
if (item.pos && item.in_use == true) {
|
135 |
push();
|
136 |
// Draw light gray background
|
137 |
fill(220);
|
|
|
151 |
}
|
152 |
}
|
153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|