cb1edf774ed71e0d59a48fffee32c5edc9afbd3a
[interactive-pacbot.git] / game / states / play.js
1 'use strict';
2
3 var Player = require('../entities/player');
4 var Pill = require('../entities/pill');
5 var BonusPill = require('../entities/bonusPill');
6 var Wall = require('../entities/wall');
7 var PoisonPill = require('../entities/poisonPill');
8 var Hud = require('../entities/hud');
9
10 var CollisionMap = require('../entities/collisionMap')
11
12 function Play() {}
13
14 Play.prototype = {
15   create: function() {
16     this.readLevelFile();
17     this.addGameHud();
18
19     this.world.scale = {x:50, y:50}; //manually tweaked based on size of level file
20     this.world.bounds = {x: -425, y:-25, width: this.game.width, height: this.game.height};
21     this.world.camera.setBoundsToWorld();
22
23     this.setupPlayerControls();
24
25     this.gameWon = false;
26   },
27   update: function() {
28     this.checkForPlayerPillCollisions();
29     this.checkForPlayerPoisonPillCollisions();
30
31     if (this.playerA.canBeEaten && this.playerB.canBeEaten &&
32       Phaser.Rectangle.intersects(this.playerA.getBounds(), this.playerB.getBounds())) {
33       this.playerPlayerCollision(this.playerA, this.playerB);
34     }
35
36     if (!this.gameWon && this.pills.total === 0) {
37       this.gameWon = true;
38       this.displayVictoryText();
39
40       this.game.time.events.add(5000, function() {
41         this.game.state.start('play');
42       }, this);
43     }
44
45     this.pollPlayerInput();
46   },
47   readLevelFile: function() {
48     this.pills = this.game.add.group();
49     this.players = this.game.add.group();
50     this.walls = this.game.add.group();
51     this.poisonPills = this.game.add.group();
52     this.map = new CollisionMap();
53
54     var levelText = this.game.cache.getText('level');
55     var splitRows = levelText.split('\n');
56
57     this.gameHeight = splitRows.length;
58     this.gameWidth = splitRows.reduce(function(currentMax, nextRow) {
59       return Math.max(currentMax, nextRow.trim().length);
60     }, 0);
61     
62
63
64     for (var y=0; y<splitRows.length; y++) {
65       for (var x=0; x<splitRows[y].length; x++) {
66         switch(splitRows[y][x]) {
67           case '#':
68             this.walls.add(new Wall(this.game, x, y));
69             this.map.addToMap(x, y);
70             break;
71           case '.':
72             this.pills.add(new Pill(this.game, x, y));
73             break;
74           case '*':
75             this.pills.add(new BonusPill(this.game, x, y));
76             break;
77           case 'A':
78             this.playerA = new Player(this.game, x, y, 'player-a', 'omSound');
79             this.players.add(this.playerA);
80             break;
81           case 'B':
82             this.playerB = new Player(this.game, x, y, 'player-b', 'nomSound');
83             this.players.add(this.playerB);
84             break;
85           case '|':
86             this.map.addToMap(x, y);
87             break;
88           }
89       }
90     }
91
92     
93     var totalScore = this.pills.children.reduce(function(score, nextPill) {
94       return score + nextPill.score;
95     }, 0);
96
97
98     this.players.forEach(function(player) {
99       player.maxScore = totalScore;
100     }, this);
101
102     this.respawnX = Math.ceil(this.gameWidth/2)-1;
103     this.respawnY = Math.ceil(this.gameHeight/2)-1;
104
105     this.playerB.isMyTurn = true;
106   },
107   addGameHud: function() {
108     this.hudA = new Hud(this.game, this.playerA, this.gameWidth-0.5, -0.5, 'spaced-scorefont-a', 'keys-a');
109     this.hudB = new Hud(this.game, this.playerB, -8.5, -0.5, 'spaced-scorefont-b', 'keys-b');
110   },
111   setupPlayerControls: function() {
112     this.playerBControls = {
113       up: Phaser.Keyboard.W,
114       left: Phaser.Keyboard.A,
115       down: Phaser.Keyboard.S,
116       right: Phaser.Keyboard.D,
117       poison: Phaser.Keyboard.Q
118     };
119     this.playerAControls = {
120       up: Phaser.Keyboard.UP,
121       left: Phaser.Keyboard.LEFT,
122       down: Phaser.Keyboard.DOWN,
123       right: Phaser.Keyboard.RIGHT,
124       poison: Phaser.Keyboard.ENTER
125     };
126     this.controls = {
127       reset: Phaser.Keyboard.ESC
128     };
129
130     function addKeyCaptures(controls, keyboard) {
131       for (var index in controls) {
132         if (controls.hasOwnProperty(index)) {
133           keyboard.addKeyCapture(controls[index]);
134         }
135       }
136     }
137     addKeyCaptures(this.playerAControls, this.game.input.keyboard);
138     addKeyCaptures(this.playerBControls, this.game.input.keyboard);
139     addKeyCaptures(this.controls, this.game.input.keyboard);
140
141     this.game.input.gamepad.start();
142
143     this.game.input.keyboard.addKey(this.controls.reset).onDown.add(function() {
144       this.game.state.start('play');
145     },this);
146   },
147   checkForPlayerPillCollisions: function() {
148     var pillCollisions = [];
149     this.players.forEach(function(player) {
150       var playerBounds = player.getBounds();
151       this.pills.forEach(function(pill) {
152         var pillBounds = pill.getBounds();
153         if (Phaser.Rectangle.intersects(playerBounds, pillBounds)) {
154           pillCollisions.push({player:player, pill:pill});
155         }
156       }, this);
157     }, this);
158     for (var i=0; i<pillCollisions.length; i++) {
159       this.playerPillCollision(pillCollisions[i].player, pillCollisions[i].pill);
160     }
161   },
162   checkForPlayerPoisonPillCollisions: function() {
163     var pillCollisions = [];
164     this.players.forEach(function(player) {
165       var playerBounds = player.getBounds();
166       this.poisonPills.forEach(function(pill) {
167         var pillBounds = pill.getBounds();
168         if (Phaser.Rectangle.intersects(playerBounds, pillBounds)) {
169           pillCollisions.push({player:player, pill:pill});
170         }
171       }, this);
172     }, this);
173     for (var i=0; i<pillCollisions.length; i++) {
174       this.playerPoisonPillCollision(pillCollisions[i].player, pillCollisions[i].pill);
175     }
176   },
177   playerPillCollision: function(player, pill) {
178     player.score += pill.score;
179     pill.destroy();
180     player.scoreSound.play();
181   },
182   playerPoisonPillCollision: function(player, poisonPill) {
183     if (player.lastTween) {
184       player.lastTween.onComplete.add(player.teleport.bind(player, this.respawnX, this.respawnY), player);
185     }
186     else {
187       player.teleport(this.respawnX, this.respawnY);
188     }
189
190     poisonPill.destroy();
191     player.respawnSound.play();
192   },
193   playerPlayerCollision: function(playerA, playerB) {
194     var eatenPlayer = playerA.isMyTurn ? playerB : playerA;
195
196     if (eatenPlayer.lastTween) {
197       eatenPlayer.lastTween.onComplete.add(eatenPlayer.teleport.bind(eatenPlayer, this.respawnX, this.respawnY), eatenPlayer);
198     }
199     else {
200       eatenPlayer.teleport(this.respawnX, this.respawnY);
201     }
202     eatenPlayer.canBeEaten = false;
203     eatenPlayer.respawnSound.play();
204   },
205   pollPlayerInput: function() {
206     if (this.game.input.gamepad.pad2.connected) {
207       this.pollGamepadForPlayer(this.game.input.gamepad.pad2, this.playerA);
208     }
209     if (this.game.input.gamepad.pad1.connected) {
210       this.pollGamepadForPlayer(this.game.input.gamepad.pad1, this.playerB);
211     }
212
213     this.pollKeyboardForPlayer(this.playerAControls, this.playerA);
214     this.pollKeyboardForPlayer(this.playerBControls, this.playerB);    
215   },
216   pollGamepadForPlayer: function(pad, player) {
217     if (pad.isDown(Phaser.Gamepad.XBOX360_DPAD_LEFT) || pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_X) < -0.2) {
218       this.movePlayer(player, -1, 0);
219     }
220     if (pad.isDown(Phaser.Gamepad.XBOX360_DPAD_RIGHT) || pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_X) > 0.2) {
221       this.movePlayer(player, 1, 0);
222     }
223     if (pad.isDown(Phaser.Gamepad.XBOX360_DPAD_UP) || pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_Y) < -0.2) {
224       this.movePlayer(player, 0, -1);
225     }
226     if (pad.isDown(Phaser.Gamepad.XBOX360_DPAD_DOWN) || pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_Y) > 0.2) {
227       this.movePlayer(player, 0, 1);
228     }
229
230     var poisonButton = pad.getButton(Phaser.Gamepad.XBOX360_A);
231     if (poisonButton.isDown && player.gamepadPoisonLastPressed < poisonButton.timeDown) {
232       player.gamepadPoisonLastPressed = poisonButton.timeDown;
233       this.togglePoisonPill(player);
234     }
235   },
236   pollKeyboardForPlayer: function(controls, player) {
237     if (this.game.input.keyboard.isDown(controls.up)) {
238       this.movePlayer(player, 0, -1);
239     }
240     if (this.game.input.keyboard.isDown(controls.down)) {
241       this.movePlayer(player, 0, 1);
242     }
243     if (this.game.input.keyboard.isDown(controls.left)) {
244       this.movePlayer(player, -1, 0);
245     }
246     if (this.game.input.keyboard.isDown(controls.right)) {
247       this.movePlayer(player, 1, 0);
248     }
249
250     var poisonKey = this.game.input.keyboard.addKey(controls.poison);
251     if (poisonKey.isDown && player.keyboardPoisonLastPressed < poisonKey.timeDown) {
252       player.keyboardPoisonLastPressed = poisonKey.timeDown;
253       this.togglePoisonPill(player);
254     }
255   },
256   
257   togglePoisonPill: function(player) {
258     if (player.hasPoisonPill) {
259       player.poisonPillActive = !player.poisonPillActive;
260     }
261   },
262   movePlayer: function(player, deltaX, deltaY) {
263     var newX = player.x + deltaX;
264     var newY = player.y + deltaY;
265     var placePoisonPill = player.hasPoisonPill && player.poisonPillActive &&
266         (Math.abs(this.respawnX-player.x)>1.1 || Math.abs(this.respawnY-player.y)>1.1);
267
268     //cannot move into walls, when it isn't your turn, or when you're already moving
269     if (this.map.checkMap(newX, newY) || !player.isMyTurn || player.moving) {
270       return;
271     }
272
273     //special rules about moving around respawn area
274     if (Math.abs(newX-this.respawnX)<=1 && Math.abs(newY-this.respawnY)<=1) {
275       //cannot move into respawn area (only outwards)
276       if (Math.abs(newX-this.respawnX)<Math.abs(player.x-this.respawnX) ||
277           Math.abs(newY-this.respawnY)<Math.abs(player.y-this.respawnY)) {
278         return;
279       }
280
281       //cannot eat opponent when leaving respawn block
282       for (var playerIndex=0; playerIndex<this.players.children.length; ++playerIndex) {
283         var opponent = this.players.children[playerIndex];
284         if (Math.abs(newX-opponent.x)<0.1 && Math.abs(newY-opponent.y)<0.1) {
285           return;
286         }
287       }
288     }
289
290     var posionPillX = player.x;
291     var posionPillY = player.y;
292     if (placePoisonPill) {
293       player.hasPoisonPill = false;
294     }
295
296     //intermediate and teleport are used for a 'multistep' move when wrapping around the screen.
297     var intermediateX = newX;
298     var teleportX = newX;
299     var intermediateY = newY;
300     var teleportY = newY;
301
302     if (newX >= this.gameWidth) {
303       newX = 0;
304       teleportX = -1;
305     }
306     if (newX < 0) {
307       newX = this.gameWidth-1;
308       teleportX = this.gameWidth;
309     }
310     if (newY >= this.gameHeight) {
311       newY = 0;
312       teleportY = -1;
313     }
314     if (newY < 0) {
315       newY = this.gameHeight-1;
316       teleportY = this.gameHeight;
317     }
318
319     if (newX === intermediateX && newY === intermediateY) {
320       player.move(newX, newY, function() {
321         if (placePoisonPill) {
322           this.poisonPills.add(new PoisonPill(this.game, posionPillX, posionPillY));
323           player.poisonPillActive = false;
324         }
325         this.togglePlayerTurn();
326       }, this);
327     }
328     else {
329       player.multistepMove(intermediateX, intermediateY, teleportX, teleportY, newX, newY, function() {
330         if (placePoisonPill) {
331           this.poisonPills.add(new PoisonPill(this.game, posionPillX, posionPillY));
332           player.poisonPillActive = false;
333         }
334         this.togglePlayerTurn();
335       }, this);
336     }
337   },
338   
339   togglePlayerTurn: function() {
340     for (var i=0; i<this.players.children.length; ++i) {
341       this.players.children[i].isMyTurn = !this.players.children[i].isMyTurn;
342       this.players.children[i].canBeEaten = true;
343     }
344     this.players.sort('isMyTurn');
345   },
346   displayVictoryText: function() {
347     if (this.playerA.score > this.playerB.score) {
348       this.createVictoryText('RED WINS', 'a');
349     }
350     else if (this.playerA.score < this.playerB.score) {
351       this.createVictoryText('YELLOW WINS', 'b');
352     }
353     else {
354       var victoryColor = this.playerA.isMyTurn ? 'b' : 'a';
355       this.createVictoryText('DRAW', victoryColor);
356     }
357   },
358   createVictoryText: function(newText, winnerLetter) {
359     this.victoryText = this.game.add.bitmapText(this.world.width/2/this.world.scale.x, 2, 'scorefont-'+winnerLetter, newText, 2);
360     this.victoryText.position.x = (this.world.width/this.world.scale.x)/2 - 8 - this.victoryText.textWidth/2 - 0.5;
361   },
362   shutdown: function() {
363     function removeKeyCaptures(controls, keyboard) {
364       for (var index in controls) {
365         if (controls.hasOwnProperty(index)) {
366           keyboard.removeKey(controls[index]);
367         }
368       }
369     }
370     removeKeyCaptures(this.playerAControls, this.game.input.keyboard);
371     removeKeyCaptures(this.playerBControls, this.game.input.keyboard);
372     removeKeyCaptures(this.controls, this.game.input.keyboard);
373   }
374 };
375
376 module.exports = Play;