4810116a51023e074e355e27fcfa641d990459d4
[wgj58.git] / wgj58.js
1 var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: function() { this.state.add('GamePlay', GamePlay, true); } });
2 var logic;
3 var cursors;
4
5 const PLAYER_SPEED = 200;
6 const WAIT_MENUSTEP = 100;
7 const WAIT_TALK = 250;
8
9 const MENUITEM_TALK = 0;
10 const MENUITEM_LEAVE = 1;
11 const MENUITEM_TAKE = 2;
12
13 const GUI_MENUITEM_DISTANCE = 20;
14
15 window.addEventListener("keydown", function(e) {
16 // Prevent default browser action for arrows and spacebar
17 if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
18 e.preventDefault();
19 }
20 }, false);
21
22 function hasTimePassed(time, delay) {
23 return (game.time.now - time) > delay;
24 }
25
26 class Dialogue {
27 // dialogue is array of {actor, text} records.
28 constructor(dialogue) {
29 this.dialogue = dialogue;
30 this.state = 0;
31 }
32
33 actual() {
34 return this.dialogue[this.state];
35 }
36
37 advance() {
38 this.state++;
39 }
40 }
41
42 class Door extends Phaser.TileSprite {
43 constructor(x, y, name, rotation, vector, longpanel) {
44 super(game, x, y, 64, 64, 'objects');
45 this.name = name;
46 this.longpanel = longpanel;
47 if (!longpanel) {
48 this.anchor = new Phaser.Point(0.5, 0.5);
49 this.tilePosition = new Phaser.Point(-64, -64);
50 this.openvector = Phaser.Point.multiply(vector, new Phaser.Point(56, 56));
51 }
52 else {
53 this.width *= 2;
54 this.anchor = new Phaser.Point(0.75, 0.5);
55 this.tilePosition = new Phaser.Point(0, -64);
56 this.openvector = Phaser.Point.multiply(vector, new Phaser.Point(116, 116));
57 }
58 if (rotation === undefined) rotation = 0;
59 this.rotation = rotation * (Math.PI / 180);
60 this.closetween = game.add.tween(this).to({ x: this.position.x, y: this.position.y }, 1000, Phaser.Easing.Sinusoidal.InOut, false, 0, 0, false);
61 this.openposition = Phaser.Point.add(this.position, this.openvector);
62 this.opentween = game.add.tween(this).to({ x: this.openposition.x, y: this.openposition.y }, 1000, Phaser.Easing.Sinusoidal.InOut, false, 0, 0, false);
63 this.isOpen = false;
64 game.physics.arcade.enable(this);
65 this.body.immovable = true;
66 this.setBody(rotation);
67 }
68
69 setBody(rotation) {
70 switch(rotation) {
71 case 0:
72 case 180:
73 this.body.setSize(this.width, 10, this.longpanel * (rotation / 180) * 64, 27);
74 break;
75 case 90:
76 case 270:
77 this.body.setSize(10, this.width, 27 + (this.longpanel * 64), this.longpanel * ((rotation - 270) / 180) * 64);
78 break;
79 default:
80 console.log("Unable to set body due to unknown rotation:", rotation);
81 }
82 }
83
84 open() {
85 if (!this.isOpen) {
86 this.opentween.start();
87 this.isOpen = true;
88 }
89 }
90
91 close() {
92 if (this.isOpen) {
93 this.closetween.start();
94 this.isOpen = false;
95 }
96 }
97
98 update() {
99 game.debug.body(this);
100 }
101 }
102
103 class Player extends Phaser.Sprite {
104 constructor(x, y) {
105 super(game, x, y, 'player');
106 this.y -= this.height;
107 this.shortname = "John";
108 this.fullname = "John Evals";
109 this.offerInteraction(null);
110 this.unfreeze();
111 }
112
113 enablePhysics() {
114 game.physics.arcade.enable(this);
115 this.body.bounce.y = 0.2;
116 this.body.bounce.x = 0.2;
117 this.body.collideWorldBounds = true;
118 }
119
120 control() {
121 if ((this.alive) && (!this.freezed))
122 {
123 if (cursors.left.isDown)
124 {
125 this.body.velocity.x = -PLAYER_SPEED;
126 }
127 else if (cursors.right.isDown)
128 {
129 this.body.velocity.x = PLAYER_SPEED;
130 }
131 if (cursors.up.isDown)
132 {
133 this.body.velocity.y = -PLAYER_SPEED;
134 }
135 else if (cursors.down.isDown)
136 {
137 this.body.velocity.y = PLAYER_SPEED;
138 }
139 }
140 }
141
142 update() {
143 this.body.velocity.x = this.body.velocity.y = 0;
144 this.control();
145 }
146
147 freeze() {
148 this.freezed = true;
149 }
150
151 unfreeze() {
152 this.freezed = false;
153 }
154
155 offerInteraction(npc) {
156 this.interactablenpc = npc;
157 }
158
159 takeMe(npc) {
160 this.loadTexture(npc.key);
161 npc.kill();
162 this.disguise = npc;
163 logic.gameinterface.dropNoticeTimeout(Phaser.Timer.SECOND * 5, "You are now identified as " + npc.fullname + "!");
164 }
165 }
166
167 class GameNPC extends Phaser.Sprite {
168 constructor(x, y, key, shortname, fullname, interaction_distance) {
169 super(game, x, y, key);
170 this.y -= this.height;
171 this.shortname = shortname;
172 this.fullname = fullname;
173 this.interaction_distance = interaction_distance;
174 this.interactable = false;
175 this.talkcount = 0;
176 this.happy = false;
177 }
178
179 kill() {
180 super.kill();
181 this.exists = false;
182 this.happy = false; // Dead people are not happy.
183 }
184
185 update() {
186 super.update();
187 if ((!this.interactable) && (game.physics.arcade.distanceBetween(this, logic.player) < this.interaction_distance)) {
188 logic.gameinterface.dropNotice("(ENTER) Interact with " + this.shortname + "!");
189 logic.player.offerInteraction(this);
190 this.interactable = true;
191 }
192 else if ((this.interactable) && (game.physics.arcade.distanceBetween(this, logic.player) > this.interaction_distance)) {
193 logic.gameinterface.clearNotice();
194 logic.player.offerInteraction(null);
195 this.interactable = false;
196 }
197 if ((this.teleport_instructions) && (game.physics.arcade.distanceBetween(this, logic.player) > (game.width + 200))) {
198 this.position = this.teleport_instructions.newPosition;
199 this.teleport_instructions.callback(this.teleport_instructions.callbackValue);
200 this.teleport_instructions = null;
201 }
202 }
203
204 actionTalk() {
205 this.talkcount++;
206 }
207
208 actionLeave() {
209 }
210
211 actionTake() {
212 logic.player.offerInteraction(null);
213 logic.player.takeMe(this);
214 return true;
215 }
216
217 endTalk() {
218 return true;
219 }
220
221 makeHappy() {
222 this.happy = true;
223 console.log(this.shortname, "is happy!");
224 }
225
226 offscreenTeleportation(newPosition, callback, callbackValue) {
227 this.teleport_instructions = { newPosition: newPosition, callback: callback, callbackValue: callbackValue };
228 console.log("Offscreen teleport request registered:", this.teleport_instructions);
229 }
230 }
231
232 class NPC_Clara extends GameNPC {
233 actionTalk() {
234 switch (this.talkcount) {
235 case 0:
236 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "What a morning..." },
237 { actor: logic.player, text: "Hi Clara! What happened?" },
238 { actor: this, text: "No one cares to tell. I just know that I have to log on people manually, as the access control system doesn't work properly." },
239 { actor: logic.player, text: "Hmm... I'll look into it. Maybe it's just that everyone is fired." },
240 { actor: this, text: "Haha! Wouldn't joke about this, though, due to the recent layoffs." },
241 { actor: logic.player, text: "Well, maybe if we dare to joke about it, it won't happen to us..." },
242 { actor: this, text: "Wish it worked like that... Is it some superstition like the belief that having an umbrella with you prevents rain?" },
243 { actor: logic.player, text: "Nah, that actually works; it's not a superstition, but Murphy's Law!" },
244 { actor: this, text: "If you say so..." },
245 { actor: this, text: "Anyway, I logged you in and opened the door for you." } ] ));
246 logic.openDoor("cutedoor");
247 break;
248 case 1:
249 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "John, have you ever thought about losing your employee card?" },
250 { actor: logic.player, text: "Yeah, you'd just get a new one." },
251 { actor: this, text: "You don't understand me, John!" },
252 { actor: this, text: "..." },
253 { actor: this, text: "I mean... Losing it for real..." },
254 { actor: this, text: "Doesn't it feel like it's the culmination of your being?" },
255 { actor: this, text: "If I had no card, would I still exist?" },
256 { actor: logic.player, text: "..." },
257 { actor: logic.player, text: "You sound very philosophical today." } ] ));
258 break;
259 case 2:
260 case 3:
261 case 4:
262 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Would you like to hear a random fun fact?" },
263 { actor: logic.player, text: "Of course!" },
264 { actor: this, text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s," },
265 { actor: this, text: "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap" },
266 { actor: this, text: "into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem" },
267 { actor: this, text: "Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." },
268 { actor: logic.player, text: "That's very interesting, I didn't know that!" },
269 { actor: logic.player, text: "Thanks for sharing, Clara!" } ] ));
270 break;
271 case 5:
272 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Would you like to hear a random fun fact?" },
273 { actor: logic.player, text: "Of course!..." },
274 { actor: logic.player, text: "But first..." },
275 { actor: this, text: "What is it?" },
276 { actor: logic.player, text: "..." },
277 { actor: logic.player, text: "Never mind, go on with what you wanted to say." },
278 { actor: this, text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry..." },
279 { actor: logic.player, text: "I knew that already..." },
280 { actor: logic.player, text: "But..." },
281 { actor: logic.player, text: "Would you go out on a date with me?" },
282 { actor: this, text: "..." },
283 { actor: this, text: "No." } ] ));
284 // FIXME: Clara should have another happy condition.
285 this.makeHappy();
286 break;
287 default:
288 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "..." } ] ));
289 }
290 super.actionTalk();
291 }
292
293 actionLeave() {
294 logic.gameinterface.dropNotice(this.shortname + ": Have a great day, John!");
295 }
296
297 actionTake() {
298 logic.openDoor("cutedoor");
299 return super.actionTake();
300 }
301
302 endTalk() {
303 return (this.talkcount < 6);
304 }
305 }
306
307 class NPC_Carlos extends GameNPC {
308 actionTalk() {
309 if (logic.clara.alive) {
310 switch (this.talkcount) {
311 case 0:
312 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Good morning, Carlos! What's up?" },
313 { actor: this, text: "Hey, John! Not too much, I'm just trying to sell some shit to this new client." },
314 { actor: logic.player, text: "And so? Is it hard to convince them?" },
315 { actor: this, text: "Of course, they don't want to buy. It's shit." },
316 { actor: this, text: "I mean, who would want to buy shit?" },
317 { actor: logic.player, text: "Yeah. Relatable... But why are you trying to sell shit? I mean, why not one of our cutting edge technologies?" },
318 { actor: this, text: "They couldn't afford it." },
319 { actor: logic.player, text: "Then why are you trying to sell them... anything at all? They'd better off with a cheaper company." },
320 { actor: this, text: "Company policies. Plus the management instructed me to sell to them, no matter what." },
321 { actor: logic.player, text: "That sucks... Well... good luck!" },
322 { actor: this, text: "Thanks..." } ] ));
323 logic.closeDoor("cutedoor");
324 logic.openDoor("carlosdoor");
325 break;
326 case 1:
327 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "*Sigh.* I'll probably not work here for long, if I can't sell this shit to the customer." },
328 { actor: this, text: "These layoffs are terrifying. They let go of anyone for even the slightest mistake." },
329 { actor: logic.player, text: "But it's not a mistake when our client doesn't buy something they don't need." },
330 { actor: this, text: "Tell this to my boss..." } ] ));
331 break;
332 case 2:
333 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "You know, generally, I think it sucks being a child. One thing I miss though, if you're a child, you're allowed to be silly." },
334 { actor: this, text: "..." },
335 { actor: this, text: "For example, when my Dad told me that semen is the seed of animals, I theoretized, if I'd bury some pig semen in the garden, a pig would grow out of it..." },
336 { actor: this, text: "I was totally fascinated by the thought, because I really wanted to have a pet pig, but Dad wouldn't let me have one." },
337 { actor: this, text: "So I thought I could grow my own pig in the garden from pig seeds!" },
338 { actor: logic.player, text: "Interesting. Where are you going with this?" },
339 { actor: this, text: "I don't know... sometimes I feel like I'd rather sell pig semen to our clients, heh!" },
340 { actor: this, text: "Uhm..." },
341 { actor: this, text: "Totally don't tell this to Saiki!" } ] ));
342 this.pigsemen = true;
343 break;
344 case 3:
345 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Weren't you heading to the IT department to the right?" },
346 { actor: this, text: "You know... the green department which looks way better than blue!" },
347 { actor: this, text: "..." },
348 { actor: this, text: "Damn, why do I have to work in blue? I just feel... so blue about it." } ] ));
349 break;
350 case 4:
351 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Weren't you heading to the conveniently green department to the right? I guess Saiki is waiting for you." },
352 { actor: this, text: "You're so lucky that you can work with her every day!" } ] ));
353 break;
354 default:
355 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Guess you'll stay here then. It's cool, you're great company anyway." } ] ));
356 }
357 }
358 else {
359 switch (this.talkcount) {
360 case 0:
361 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Good morning, Carlos!" },
362 { actor: this, text: "Whoa, Clara! You have changed so much! I barely recognize you. What's up?" },
363 { actor: logic.player, text: "I need to talk to Saiki at the IT department, but... my card doesn't allow me to enter there." },
364 { actor: this, text: "Well... I can certainly help you with that..." },
365 { actor: this, text: "..." } ] ));
366 break;
367 case 1:
368 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Carlos. You promised to help me." },
369 { actor: this, text: "Uhm... It's just... Please give me 5 mins to finish this e-mail." },
370 { actor: logic.player, text: "I don't have much time, Carlos." },
371 { actor: this, text: "See, it's just 5 mins, please be patient. I have a chocolate bar in the fridge. I give it to you. Feel free to have it meanwhile." },
372 { actor: logic.player, text: "..." } ] ));
373 break;
374 case 2:
375 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Time is up, Carlos!" },
376 { actor: this, text: "Please give me just another min." },
377 { actor: logic.player, text: "Fine. Please just give me your card and I'll help myself!" },
378 { actor: this, text: "But... you know that company policies forbid that, Clara! Please just be a little more patient!" },
379 { actor: logic.player, text: "You will give me your card now or you will regret!" },
380 { actor: this, text: "...?" },
381 { actor: this, text: "You're not gonna treat me like this. Forget it!" },
382 { actor: logic.player, text: "You'll not work here tomorrow." } ] ));
383 break;
384 default:
385 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "..." } ] ));
386 }
387 }
388 super.actionTalk();
389 }
390
391 actionLeave() {
392 if (logic.clara.alive) {
393 logic.gameinterface.dropNotice(this.shortname + ": Bye, John! Pass on my greetings to Saiki!");
394 }
395 }
396
397 actionTake() {
398 logic.closeDoor("cutedoor");
399 logic.openDoor("carlosdoor");
400 return super.actionTake();
401 }
402
403 endTalk() {
404 return (logic.clara.alive) || (this.talkcount < 3);
405 }
406 }
407
408 class NPC_Saiki extends GameNPC {
409 actionTalk() {
410 if (logic.carlos.alive) {
411 switch (this.talkcount) {
412 case 0:
413 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Hi Saiki!" },
414 { actor: this, text: "Hi John! Glad you arrived, here's some work for you!" },
415 { actor: logic.player, text: "Awesome! What is it?" },
416 { actor: this, text: "Haha, joking! Actually, our queue is suspiciously empty for today." },
417 { actor: logic.player, text: "Yeah... That's really suspicious!" },
418 { actor: logic.player, text: "Maybe I can get home today in time?" },
419 { actor: this, text: "You mean, you wouldn't spend the mandatory free extra working hours?" },
420 { actor: this, text: "You steal from the company! The company needs your unpaid hours of work! Otherwise, how would we finance the trash can I ordered for the department?" },
421 { actor: this, text: "*Whispers with wink.* I hope you get my sarcasm." },
422 { actor: logic.player, text: "Haha! I always get it, Saiki!" } ] ));
423 logic.closeDoor("carlosdoor");
424 logic.openDoor("saikidoor");
425 logic.openDoor("peterdoor");
426 break;
427 case 1:
428 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Now that I think about it, there's still one thing. You could look into why the access control system is acting so funny today." },
429 { actor: this, text: "Clara has to enter people manually." },
430 { actor: logic.player, text: "Yeah, I'll definitely check that." } ] ));
431 break;
432 case 2:
433 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "So... what's up with the trash can?" },
434 { actor: this, text: "I'm always up for such deep conversation" },
435 { actor: this, text: "about trash cans." },
436 { actor: this, text: "While we only have one trash can, other departments seem to be abundant of them. And ours is always filled. So I requested an additional trash can." },
437 { actor: this, text: "Now the ticket is in PENDING status. The latest work note is from two months ago, telling there is no budget at the moment." },
438 { actor: logic.player, text: "Hmm... Maybe it's too much to ask. Can we take one from another department?" },
439 { actor: this, text: "No, everyone holds on to their trash cans." },
440 { actor: logic.player, text: "You certainly did your research." },
441 { actor: this, text: "Of course. It's an important subject." } ] ));
442 break;
443 case 3:
444 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "My parents named me after a Japanese rock singer." },
445 { actor: logic.player, text: "Cool! Which band?" },
446 { actor: this, text: "I don't remember the band name. But the particular singer has a blue rose in her hair most of the time." } ] ));
447 break;
448 default:
449 if ((logic.carlos.pigsemen) && (this.talkcount == 4)) {
450 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Did you know that Carlos thought he can grow a pig by burying pig semen in the ground? What do you think about this?" },
451 { actor: this, text: "Hmm..." },
452 { actor: this, text: "I guess it didn't work." },
453 { actor: this, text: "So do you want to know what I think about this?" },
454 { actor: this, text: "..." },
455 { actor: this, text: "IT'S BRILLIANT!!!" },
456 { actor: this, text: "You know, it really sparkled my artistic vision! Now I must draw a pig with its legs rooted into the ground!" },
457 { actor: logic.player, text: "That's gross. Poor pig." },
458 { actor: this, text: "Indeed. But this is the point. He's stuck in one place, cannot move. Like you in life. So you feel sympathy for him." },
459 { actor: logic.player, text: "You have a point." },
460 { actor: this, text: "I think I'll catch Carlos and talk to him. I always liked that guy but I didn't know he's such imaginative." } ] ));
461 this.offscreenTeleportation(new Phaser.Point(logic.carlos.x, logic.carlos.y + 64), this.tpDone, this);
462 }
463 else {
464 if (Phaser.Math.random() < 0.5) {
465 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "I've found an orchestrated rendition of „Potential for Anything”. You'll have to check it out, it's breathtaking!" } ] ));
466 }
467 else {
468 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "I don't like that our department is green. The blue one, where Carlos works, looks much better." } ] ));
469 }
470 }
471 }
472 }
473 else {
474 switch (this.talkcount) {
475 case 0:
476 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "I love you, Saiki!" },
477 { actor: this, text: "Hi Carlos..." },
478 { actor: logic.player, text: "I'm infatuated by the mere thought of you." },
479 { actor: this, text: "Well... it sounds pretty much creepy." },
480 { actor: logic.player, text: "Will you go out on a date with me?" },
481 { actor: this, text: "..." },
482 { actor: this, text: "Sorry to hurt your feelings, but... even though I like you... Your sudden, unprecedented confession makes me scared." },
483 { actor: this, text: "Especially with that facial expression." },
484 { actor: this, text: "I mean... you're not even joking." },
485 { actor: this, text: "So sorry... I won't date you." } ] ));
486 break;
487 case 1:
488 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "I thought I could impress you." },
489 { actor: this, text: "I'm sorry." } ] ));
490 break;
491 case 2:
492 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "You will give me your card." },
493 { actor: this, text: "Geez, what for? Go back to your department and work!" },
494 { actor: logic.player, text: "I need it." },
495 { actor: this, text: "..." } ] ));
496 break;
497 default:
498 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Don't you have anything better to do, Carlos?" } ] ));
499 }
500 }
501 super.actionTalk();
502 }
503
504 actionLeave() {
505 if (logic.carlos.alive) {
506 logic.gameinterface.dropNotice(this.shortname + ": It was nice talking to you!");
507 }
508 }
509
510 actionTake() {
511 logic.closeDoor("carlosdoor");
512 logic.openDoor("saikidoor");
513 logic.openDoor("peterdoor");
514 logic.carlos.kill();
515 return super.actionTake();
516 }
517
518 endTalk() {
519 return (logic.carlos.alive);
520 }
521
522 tpDone(value) {
523 console.log("Woot-woot! Teleport complete:", value);
524 value.makeHappy();
525 logic.carlos.makeHappy();
526 // FIXME: Need to find a prettier way than this.
527 logic.carlos.update = function() { }; // Disable Carlos' interaction. (Saiki will talk for him.)
528 }
529 }
530
531 class NPC_Peter extends GameNPC {
532 actionTalk() {
533 if (logic.carlos.alive) {
534 switch (this.talkcount) {
535 case 0:
536 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Hi Peter! What's up?" },
537 { actor: this, text: "Hi John! Bianca has locked herself in the large meeting room." },
538 { actor: logic.player, text: "Why would she do that?" },
539 { actor: this, text: "I have no idea." },
540 { actor: this, text: "Normally, I would give a fuck about what she's doing and her problems, but I want to hold a meeting in an hour." },
541 { actor: logic.player, text: "That was rude. I think you should care more about your colleagues." },
542 { actor: this, text: "Ahh... You're right. I didn't mean it. I'm just so frustrated about all the shit going on here." } ] ));
543 logic.closeDoor("saikidoor");
544 logic.openDoor("peterdoor");
545 logic.openDoor("biancadoor");
546 break;
547 case 1:
548 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "See... Would you talk to Bianca? We don't understand each other pretty well. That's why I'd rather wouldn't make a shot." } ] ));
549 break;
550 default:
551 if ((logic.clara.talkcount >= 5) && (this.talkcount == 2)) {
552 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Do you know Clara from the Reception?" },
553 { actor: this, text: "Of course I do." },
554 { actor: logic.player, text: "I think she's mad at me..." },
555 { actor: this, text: "Why would she?" },
556 { actor: logic.player, text: "I kind of... asked her out." },
557 { actor: logic.player, text: "She said no." },
558 { actor: this, text: "What the heck, dude?" },
559 { actor: this, text: "..." },
560 { actor: this, text: "You should know it's against the company etiquette..." },
561 { actor: this, text: "Regardless, I don't think she's really mad. You're a cool guy." },
562 { actor: logic.player, text: "I hope this will be sorted out... I totally accept her answer, but I wouldn't like her to have negative feelings towards me." } ] ));
563 }
564 else {
565 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "I'm kind of busy, man." } ] ));
566 }
567 }
568 }
569 else {
570 switch (this.talkcount) {
571 case 0:
572 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "What a wonderful day, Peter!" },
573 { actor: this, text: "Yikes! Saiki! You scared me!" },
574 { actor: logic.player, text: "I didn't even say \"boo\"." },
575 { actor: this, text: "Are you OK? You look horrible! You should really see a doctor." },
576 { actor: logic.player, text: "I'm fine, Peter. And you?" },
577 { actor: this, text: "No... Not at all." },
578 { actor: logic.player, text: "I'm sorry to hear that..." } ] ));
579 break;
580 case 1:
581 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "Don't come closer to me!" },
582 { actor: logic.player, text: "Why?" },
583 { actor: this, text: "I don't mean to offend you, but... you really creep me out..." },
584 { actor: logic.player, text: "Why do you say that?" },
585 { actor: this, text: "I honestly can't tell what's wrong with you, but... you look like that ghost girl from The Ring!" } ] ));
586 break;
587 case 2:
588 logic.gameinterface.talk(new Dialogue( [ { actor: logic.player, text: "Now that was pretty much racist..." },
589 { actor: logic.player, text: "Just because I have a Japanese name and long black hair, that makes me immediately look like the Japanese girl from Ring when I'm sick?" },
590 { actor: this, text: "Geez... that's not even a Japanese movie! And the actress is American..." },
591 { actor: logic.player, text: "The original movie is Japanese." },
592 { actor: this, text: "I don't care... Just... keep distance, please!" } ] ));
593 break;
594 default:
595 if (Phaser.Math.random() < 0.5) {
596 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "STAY AWAY FROM ME!" } ] ));
597 }
598 else {
599 logic.gameinterface.talk(new Dialogue( [ { actor: this, text: "DON'T COME ANY CLOSER!" } ] ));
600 }
601 }
602 }
603 super.actionTalk();
604 }
605
606 actionTake() {
607 logic.closeDoor("saikidoor");
608 logic.openDoor("peterdoor");
609 logic.openDoor("biancadoor");
610 return super.actionTake();
611 }
612
613 endTalk() {
614 return (logic.saiki.alive) && (this.talkcount < 3);
615 }
616 }
617
618 class NPC_Bianca extends GameNPC {
619 actionTake() {
620 // All doors open for you if you have all the cards.
621 logic.openDoor("cutedoor");
622 logic.openDoor("carlosdoor");
623 logic.openDoor("saikidoor");
624 return super.actionTake();
625 }
626 }
627
628
629 class GameInterface extends Phaser.Group {
630 constructor(game, parent) {
631 super(game, parent, 'GUI', false, false, 0);
632 this.back_notice = new Phaser.Graphics(game, 0, game.height - 50);
633 this.back_notice.beginFill(0x000000);
634 this.back_notice.drawRect(0, 0, game.width, 30);
635 this.back_notice.endFill();
636 this.add(this.back_notice);
637 this.text_notice = new Phaser.Text(game, 0, this.back_notice.y, null, { align: 'left', fill: 'white', font: 'Ubuntu Mono', fontSize: 18, fontWeight: 'bold' });
638 this.text_notice.anchor.setTo(-0.5, -0.2);
639 this.add(this.text_notice);
640 this.clearNotice();
641
642 this.back_menu = new Phaser.Graphics(game, 150, (game.height / 2) + 40);
643 this.back_menu.beginFill(0x000000);
644 this.back_menu.drawRect(0, 0, 155, 105);
645 this.back_menu.endFill();
646 this.back_menu.lineStyle(3, Phaser.Color.YELLOW, 1);
647 this.back_menu.moveTo(10, 35);
648 this.back_menu.lineTo(this.back_menu.width - 10, 35);
649 this.add(this.back_menu);
650 var style = { align: 'left', fill: 'yellow', font: 'Ubuntu Mono', fontSize: 22, fontWeight: 'bold' };
651 this.text_menutitle = new Phaser.Text(game, this.back_menu.x, this.back_menu.y, null, style);
652 this.text_menutitle.anchor.setTo(-0.2, -0.2);
653 style = { align: 'left', fill: 'white', font: 'Ubuntu Mono', fontSize: 18, fontWeight: 'bold' };
654 this.text_menuitem_Talk = new Phaser.Text(game, this.back_menu.x + 35, this.back_menu.y + 40, "Talk", style);
655 this.text_menuitem_Leave = new Phaser.Text(game, this.back_menu.x + 35, this.text_menuitem_Talk.y + GUI_MENUITEM_DISTANCE, "Leave", style);
656 this.text_menuitem_Take = new Phaser.Text(game, this.back_menu.x + 35, this.text_menuitem_Leave.y + GUI_MENUITEM_DISTANCE, "Take ID", style);
657 style.fill = 'yellow';
658 this.text_menucursor = new Phaser.Text(game, this.back_menu.x + 15, this.text_menuitem_Talk.y, "→", style);
659 this.add(this.text_menutitle);
660 this.add(this.text_menuitem_Talk);
661 this.add(this.text_menuitem_Leave);
662 this.add(this.text_menuitem_Take);
663 this.add(this.text_menucursor);
664 this.leaveMenu();
665 this.last_menustep = 0;
666
667 this.back_talk = new Phaser.Graphics(game, 100, game.height - 120);
668 this.back_talk.beginFill(0x000000);
669 this.back_talk.drawRect(0, 0, game.width - 200, 105);
670 this.back_talk.endFill();
671 this.back_talk.lineStyle(3, Phaser.Color.YELLOW, 1);
672 this.back_talk.moveTo(10, 35);
673 this.back_talk.lineTo(this.back_talk.width - 10, 35);
674 this.add(this.back_talk);
675 style = { align: 'left', fill: 'yellow', font: 'Ubuntu Mono', fontSize: 22, fontWeight: 'bold' };
676 this.text_talktitle = new Phaser.Text(game, this.back_talk.x + 15, this.back_talk.y + 6, null, style);
677 //this.text_talktitle.anchor.setTo(-0.2, -0.2);
678 this.add(this.text_talktitle);
679 this.strikethrough_talktitle = new Phaser.Graphics(game, this.back_talk.x, this.back_talk.y);
680 this.strikethrough_talktitle.lineStyle(3, Phaser.Color.RED, 1);
681 this.strikethrough_talktitle.moveTo(12, 20);
682 this.strikethrough_talktitle.lineTo(67, 20);
683 this.add(this.strikethrough_talktitle);
684 style = { align: 'left', fill: 'white', font: 'Ubuntu Mono', fontSize: 18, fontWeight: 'bold' };
685 this.text_talk = new Phaser.Text(game, this.back_talk.x + 35, this.back_talk.y + 40, null, style);
686 this.text_talk.wordWrap = true;
687 this.text_talk.wordWrapWidth = this.back_talk.width - 70;
688 this.text_talk.lineSpacing = -5;
689 this.add(this.text_talk);
690 this.leaveTalk();
691 this.last_talk = 0;
692 }
693
694 dropNotice(text) {
695 this.text_notice.text = text;
696 this.back_notice.visible = true;
697 this.text_notice.visible = true;
698 }
699
700 dropNoticeTimeout(timeout, text) {
701 this.dropNotice(text);
702 game.time.events.add(timeout, this.clearNotice, this);
703 }
704
705 clearNotice() {
706 this.back_notice.visible = false;
707 this.text_notice.visible = false;
708 }
709
710 npcMenu(npc) {
711 if (!this.inMenu) {
712 this.inMenu = true;
713 this.clearNotice();
714 this.text_menutitle.text = npc.shortname;
715 this.back_menu.visible = true;
716 this.text_menutitle.visible = true;
717 this.text_menuitem_Talk.visible = true;
718 this.text_menuitem_Leave.visible = true;
719 this.text_menuitem_Take.visible = true;
720 this.currentmenuitem = MENUITEM_TALK;
721 this.actualizeCursorPosition();
722 this.text_menucursor.visible = true;
723 this.last_menustep = game.time.now;
724 }
725 }
726
727 leaveMenu() {
728 this.back_menu.visible = false;
729 this.text_menutitle.visible = false;
730 this.text_menuitem_Talk.visible = false;
731 this.text_menuitem_Leave.visible = false;
732 this.text_menuitem_Take.visible = false;
733 this.text_menucursor.visible = false;
734 this.inMenu = false;
735 }
736
737 talk(dialogue) {
738 this.inTalk = true;
739 this.dialogue = dialogue;
740 this.advanceTalk();
741 this.back_talk.visible = true;
742 this.text_talktitle.visible = true;
743 this.text_talk.visible = true;
744 }
745
746 leaveTalk() {
747 this.inTalk = false;
748 this.dialogue = null;
749 this.back_talk.visible = false;
750 this.text_talktitle.visible = false;
751 this.strikethrough_talktitle.visible = false;
752 this.text_talk.visible = false;
753 }
754
755 advanceTalk() {
756 console.log(this.dialogue);
757 console.log(this.dialogue.actual());
758 var actualdialogue = this.dialogue.actual();
759 if (actualdialogue) {
760 if (actualdialogue.actor.disguise) {
761 this.text_talktitle.text = actualdialogue.actor.shortname + " " + actualdialogue.actor.disguise.shortname;
762 this.strikethrough_talktitle.visible = true;
763 }
764 else {
765 this.text_talktitle.text = actualdialogue.actor.shortname;
766 this.strikethrough_talktitle.visible = false;
767 }
768 this.text_talk.text = actualdialogue.text;
769 this.dialogue.advance();
770 }
771 else {
772 this.leaveTalk();
773 logic.endTalk();
774 }
775 this.last_talk = game.time.now;
776 }
777
778 actualizeCursorPosition() {
779 this.text_menucursor.y = this.text_menuitem_Talk.y + (this.currentmenuitem * GUI_MENUITEM_DISTANCE);
780 }
781
782 update() {
783 if ((this.inMenu) && (hasTimePassed(this.last_menustep, WAIT_MENUSTEP))) {
784 if (cursors.up.isDown)
785 {
786 this.currentmenuitem--;
787 if (this.currentmenuitem < MENUITEM_TALK) this.currentmenuitem = MENUITEM_TAKE;
788 this.actualizeCursorPosition();
789 }
790 else if (cursors.down.isDown)
791 {
792 this.currentmenuitem++;
793 if (this.currentmenuitem > MENUITEM_TAKE) this.currentmenuitem = MENUITEM_TALK;
794 this.actualizeCursorPosition();
795 }
796 if (game.input.keyboard.isDown(Phaser.Keyboard.ENTER)) {
797 logic.callMenu(this.currentmenuitem);
798 }
799 this.last_menustep = game.time.now;
800 }
801 if ((this.inTalk) && (game.input.keyboard.isDown(Phaser.Keyboard.ENTER)) && (hasTimePassed(this.last_talk, WAIT_TALK))) {
802 this.advanceTalk();
803 }
804 }
805 }
806
807 class GameLogic {
808
809 constructor() {
810 this.player = this.clara = this.carlos = this.saiki = this.peter = this.bianca = null;
811 this.gameinterface = new GameInterface(game, game.stage);
812 //this.doors = game.add.group(game.world, "doors");
813 this.last_menuselect = 0;
814 }
815
816 createObject(object) {
817 switch (object.type) {
818 case 'spawnpoint': this.createCharacter(object); break;
819 case 'door': this.createDoor(object); break;
820 case '': console.error("Object type is empty:", object); break;
821 default: console.error("Unknown object type:", object);
822 }
823 }
824
825 createCharacter(object) {
826 var newChar;
827 switch (object.name) {
828 case 'john':
829 newChar = new Player(object.x, object.y, 'player');
830 this.player = newChar;
831 this.player.enablePhysics();
832 game.camera.follow(this.player);
833 break;
834 case 'clara':
835 newChar = new NPC_Clara(object.x, object.y, 'clara', "Clara", "Clara Tnavelerri", 200);
836 this.clara = newChar;
837 break;
838 case 'carlos':
839 newChar = new NPC_Carlos(object.x, object.y, 'carlos', "Carlos", "Carlos Elbacalper", 150);
840 this.carlos = newChar;
841 break;
842 case 'saiki':
843 newChar = new NPC_Saiki(object.x, object.y, 'saiki', "Saiki", "Saiki Ytpme", 150);
844 this.saiki = newChar;
845 break;
846 case 'peter':
847 newChar = new NPC_Peter(object.x, object.y, 'peter', "Peter", "Peter Tluaf", 225);
848 this.peter = newChar;
849 break;
850 case 'bianca':
851 newChar = new NPC_Bianca(object.x, object.y, 'bianca', "Bianca", "Bianca Gnihton", 150);
852 this.bianca = newChar;
853 break;
854 default:
855 console.error("Unknown character:", object);
856 }
857 newChar.name = object.name;
858 game.add.existing(newChar);
859 console.log(newChar);
860 }
861
862 createDoor(object) {
863 /* Calculate movement vector and correct position (32 = half tile width/height). */
864 var vector;
865 switch (object.rotation) {
866 case undefined:
867 case 0: vector = new Phaser.Point( -1, 0); object.x += 32; object.y -= 32; break;
868 case 90: vector = new Phaser.Point( 0, -1); object.x += 32; object.y += 32; break;
869 case 180: vector = new Phaser.Point( 1, 0); object.x -= 32; object.y += 32; break;
870 case 270: vector = new Phaser.Point( 0, 1); object.x -= 32; object.y -= 32; break;
871 default: console.error("Invalid rotation:", object.rotation);
872 }
873 this.doors.add(new Door(object.x, object.y, object.name, object.rotation, vector, object.properties.longpanel));
874 }
875
876 callMenu(menuitem) {
877 console.log("Menu callback received:", menuitem);
878 this.last_menuselect = game.time.now;
879 this.gameinterface.leaveMenu();
880 switch (menuitem) {
881 case MENUITEM_TALK:
882 this.player.interactablenpc.actionTalk();
883 break;
884 case MENUITEM_LEAVE:
885 this.player.interactablenpc.actionLeave();
886 this.player.unfreeze();
887 break;
888 case MENUITEM_TAKE:
889 if (this.player.interactablenpc.actionTake())
890 this.player.unfreeze();
891 break;
892 }
893 }
894
895 endTalk() {
896 if (this.player.interactablenpc) {
897 if (this.player.interactablenpc.endTalk()) {
898 this.gameinterface.npcMenu(this.player.interactablenpc);
899 }
900 else {
901 this.player.unfreeze();
902 this.last_menuselect = game.time.now;
903 }
904 }
905 }
906
907 openDoor(doorname) {
908 this.doors.children.forEach(function(o) { if (o.name == doorname) o.open(); });
909 }
910
911 closeDoor(doorname) {
912 this.doors.children.forEach(function(o) { if (o.name == doorname) o.close(); });
913 }
914
915 checkAllHappy() {
916 return (this.clara.happy) && (this.carlos.happy) && (this.saiki.happy);
917 }
918
919 update() {
920 if ((game.input.keyboard.isDown(Phaser.Keyboard.ENTER)) && (hasTimePassed(this.last_menuselect, WAIT_MENUSTEP))) {
921 if ((this.player.interactablenpc) && (this.player.interactablenpc.interactable) && (!this.gameinterface.inMenu) && (!this.gameinterface.inTalk)) {
922 console.log("Starting interaction with", this.player.interactablenpc.fullname);
923 this.player.freeze();
924 this.gameinterface.npcMenu(this.player.interactablenpc);
925 }
926 }
927 }
928
929 }
930
931 class GamePlay extends Phaser.State {
932
933 constructor() {
934
935 super();
936 logic = new GameLogic();
937 game.world.updateOnlyExistingChildren = true;
938 game.state.add('GameOver', GameOver, false);
939
940 }
941
942 preload() {
943
944 game.load.image('player', 'john.png');
945 game.load.image('clara', 'clara.png');
946 game.load.image('carlos', 'carlos.png');
947 game.load.image('saiki', 'saiki.png');
948 game.load.image('peter', 'peter.png');
949 game.load.image('bianca', 'bianca.png');
950 game.load.image('tileset', 'tileset.png');
951 game.load.image('objects', 'objects.png');
952 game.load.tilemap('gamemap', 'tilemap.json', null, Phaser.Tilemap.TILED_JSON);
953
954 }
955
956 create() {
957
958 game.world.setBounds(0, 0, 800, 600);
959 game.stage.backgroundColor = '#000000';
960 game.physics.startSystem(Phaser.Physics.ARCADE);
961 console.log("Debug enabled:", !game.debug.isDisabled);
962
963 var map = game.add.tilemap('gamemap');
964 map.addTilesetImage('tileset', 'tileset');
965 map.addTilesetImage('objects', 'objects');
966 var layer_floor = map.createLayer('floor');
967 layer_floor.resizeWorld();
968 this.layer_walls = map.createLayer('walls');
969 map.setCollisionBetween(1, 100, true, this.layer_walls);
970 this.layer_furniture = map.createLayer('furniture');
971 map.setCollisionBetween(1, 100, true, this.layer_furniture);
972
973 // FIXME: Don't create a group here like this, it's too ugly! Now I have it here due to the display order.
974 logic.doors = game.add.group(game.world, "doors");
975
976 map.objects['objects'].forEach(function(o) { logic.createObject(o); });
977 console.log(map.objects);
978
979 cursors = game.input.keyboard.createCursorKeys();
980 console.log(logic);
981
982 }
983
984 update() {
985
986 game.physics.arcade.collide(logic.player, this.layer_walls);
987 game.physics.arcade.collide(logic.player, this.layer_furniture);
988 game.physics.arcade.collide(logic.player, logic.doors);
989 logic.update();
990
991 }
992 }
993
994 class GameOver extends Phaser.State {
995
996 create() {
997
998 console.log("Entered GameOver state.");
999 game.stage.removeChildren(1);
1000 game.camera.follow(logic.player);
1001 logic.player.freeze();
1002
1003 var g = game.add.group(game.stage, "GAMEOVER");
1004 this.back_gameover = new Phaser.Graphics(game, 100, game.height / 2 - 200);
1005 this.back_gameover.beginFill(0x000000);
1006 this.back_gameover.drawRect(0, 0, game.width - 200, 400);
1007 this.back_gameover.endFill();
1008 this.back_gameover.lineStyle(3, Phaser.Color.YELLOW, 1);
1009 this.back_gameover.moveTo(10, 35);
1010 this.back_gameover.lineTo(this.back_gameover.width - 10, 35);
1011 g.add(this.back_gameover);
1012 var style = { align: 'left', fill: 'yellow', font: 'Ubuntu Mono', fontSize: 22, fontWeight: 'bold' };
1013 this.text_title = new Phaser.Text(game, this.back_gameover.x + 15, this.back_gameover.y + 6, "ABRUPT GAME OVER", style);
1014 g.add(this.text_title);
1015 style = { align: 'left', fill: 'white', font: 'Ubuntu Mono', fontSize: 18, fontWeight: 'bold' };
1016 this.text_gameover = new Phaser.Text(game, this.back_gameover.x + 35, this.back_gameover.y + 60, null, style);
1017 this.text_gameover.wordWrap = true;
1018 this.text_gameover.wordWrapWidth = this.back_gameover.width - 70;
1019 this.text_gameover.lineSpacing = -5;
1020 g.add(this.text_gameover);
1021
1022 this.text_gameover.text = this.assess();
1023
1024 }
1025
1026 assess() {
1027 if (logic.checkAllHappy()) {
1028 return "You listened to all of your colleagues' and affected them to make their day a little better. Next day, the company fired Clara, Carlos, Saiki, and you, to comply with its productivity requirements. The company's internal code demand unconditional and illimitable loyalty from employees to maximize productivity. This includes requisites for employees not to exercise human qualities or express personal needs within and outside the corporate environment. The company has determined that neither of the mentioned people complied to these guidelines, due to the amount of socialization they pursued during the work day.\n\nIronically, the layoffs turned everyone's life for the better.";
1029 }
1030 else if (logic.player.disguise) {
1031 this.text_gameover.style.fill = 'red';
1032 return "You went on your day to accomplish your tasks without caring about anyone. Where are your colleagues now? Clara, Carlos, Saiki? You don't know. But you don't even care. You have never seen them after they served your needs.\n\nNext day, the company promoted you for your effectiveness and loyalty.";
1033 }
1034 else {
1035 return "The company is concerned that you may develop other needs (like wanting to pursue hobbies, spend time with friends and family) those compete for the time you devote for your job. The company reminds you that they demand your entire being to exclusively serve the company's interests. Socializing with your colleagues outside the dedicated team building occasions may hurt your performance. To comply with the productivity requirements, the company has terminated your employment.\n\nClara, Carlos, Saiki are still loyal employees of the company. Probably you could have done something different to find a better outcome.";
1036 }
1037 }
1038
1039 }