Add 'GameOver' game state
[ld40.git] / ld40.js
1 var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: function() { this.state.add('GamePlay', GamePlay, true); } });
2 var cursors;
3 var player;
4 var walls;
5 var determinations; // I know it's grammatically incorrect. :P
6 var monsters;
7 var nexttime_monsterspawn;
8 var nexttime_determination;
9
10 const WAIT_KEY = 200;
11 const WAIT_SHOOT = 200;
12 const WAIT_INVINCIBILITY = 800;
13 const WAIT_SOULMODE = 10000;
14 const WAIT_MONSTERSHOOT = 500;
15 const WAIT_MONSTERSPAWN = 20000;
16 const WAIT_DETERMINATION = 4000;
17 const WALL_THICKNESS = 16;
18 const WALL_BORDER = 8;
19 const WALL_BORDERBOTTOM = 48;
20 const HB_THICKNESS = 20;
21 const MAX_HEALTH = 20;
22 const MONSTER_HEALTH = 20;
23 const MODE_DETERMINATION = 0;
24 const MODE_JUSTICE = 1;
25 const SPEED_PLAYER = 150;
26 const SPEED_PPROJECTILE = 500;
27 const SPEED_MPROJECTILE = 200;
28
29 window.addEventListener("keydown", function(e) {
30 // Prevent default browser action for arrows and spacebar
31 if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
32 e.preventDefault();
33 }
34 }, false);
35
36 function sign(n) {
37 if (n >= 0) { return 1 } else { return -1 };
38 }
39
40 class Player extends Phaser.Text {
41 constructor(x, y) {
42 super(game, x, y, "♥️", { align: 'center', fill: 'red', font: 'Ubuntu Mono', fontSize: 32, fontWeight: 'bold' });
43 this.anchor.setTo(0.5, 0.5);
44 this.maxHealth = MAX_HEALTH;
45 this.health = this.maxHealth;
46 this.love = 1;
47 this.lastmovetime = 0;
48 this.lasttime_shoot = 0;
49 this.lasttime_mode = 0;
50 this.lasttime_damage = 0;
51 this.speed = SPEED_PLAYER;
52 this.switchmode(MODE_DETERMINATION);
53 this.weapon = new JusticeBlaster(game, this);
54 this.weapon.bulletSpeed = SPEED_PPROJECTILE;
55 this.healthBar = new HealthBar(60, game.world.height - (WALL_BORDERBOTTOM / 2));
56 }
57
58 enablePhysics() {
59 game.physics.arcade.enable(this);
60 this.body.bounce.y = 0.2;
61 this.body.bounce.x = 0.2;
62 this.body.collideWorldBounds = true;
63 }
64
65 overlap_determination(determination) {
66 determination.kill();
67 this.heal(2);
68 this.speed += 50;
69 this.switchmode(MODE_JUSTICE);
70 }
71
72 damage(amount) {
73 if ((game.time.now - this.lasttime_damage) > WAIT_INVINCIBILITY)
74 {
75 super.damage(amount);
76 this.lasttime_damage = game.time.now;
77 }
78 }
79
80 kill() {
81 this.health = 0;
82 this.healthBar.update(this.love, this.maxHealth, this.health);
83 super.kill();
84 this.game.state.start('GameOver');
85 }
86
87 loveUp() {
88 this.love++
89 this.maxHealth += this.love;
90 this.heal(this.love);
91 }
92
93 control() {
94 if (this.alive)
95 {
96 if (cursors.left.isDown)
97 {
98 player.body.velocity.x = -player.speed;
99 player.lastmovetime = game.time.now;
100 }
101 else if (cursors.right.isDown)
102 {
103 player.body.velocity.x = player.speed;
104 player.lastmovetime = game.time.now;
105 }
106 if (cursors.up.isDown)
107 {
108 player.body.velocity.y = -player.speed;
109 player.lastmovetime = game.time.now;
110 }
111 else if (cursors.down.isDown)
112 {
113 player.body.velocity.y = player.speed;
114 player.lastmovetime = game.time.now;
115 }
116 if ((this.mode == MODE_JUSTICE) && game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR) && ((game.time.now - this.lasttime_shoot) > WAIT_SHOOT))
117 {
118 this.shoot();
119 }
120 }
121 }
122
123 switchmode(soulmode) {
124 switch(soulmode)
125 {
126 case MODE_DETERMINATION:
127 this.addColor('red', 0);
128 break;
129 case MODE_JUSTICE:
130 this.addColor('yellow', 0);
131 this.lasttime_mode = game.time.now;
132 break;
133 }
134 this.mode = soulmode;
135 }
136
137 shoot() {
138 this.weapon.fire(this, 0, this.y);
139 this.weapon.fire(this, this.x, 0);
140 this.weapon.fire(this, this.x, game.world.height);
141 this.weapon.fire(this, game.world.width, this.y);
142 this.lasttime_shoot = game.time.now;
143 }
144
145 update() {
146 super.update();
147 this.control();
148 this.healthBar.update(this.love, this.maxHealth, this.health);
149 game.physics.arcade.overlap(monsters, this.weapon.bullets, function(m,b) { b.overlap_monster(m); });
150 if ((game.time.now - this.lasttime_mode) > WAIT_SOULMODE) { this.switchmode(MODE_DETERMINATION); }
151 }
152 }
153
154 class Monster extends Phaser.Sprite {
155 constructor(x, y) {
156 super(game, x, y, 'monster');
157 this.anchor.setTo(0.5, 0.5);
158 this.health = MONSTER_HEALTH;
159 this.nexttime_shoot = game.time.now;
160 this.weapon = new JusticeBlaster(game, this);
161 this.weapon.bulletSpeed = SPEED_MPROJECTILE;
162 this.weapon.trackSprite(this);
163 this.shaketween = game.add.tween(this).to({ x: this.x + (sign(Math.random() - 0.5)) * 5 }, 10, Phaser.Easing.Sinusoidal.InOut, false, 0, 8, true);
164 }
165
166 enablePhysics() {
167 game.physics.arcade.enable(this);
168 }
169
170 damage(amount) {
171 super.damage(amount);
172 if (this.alive) { this.shaketween.start(); }
173 }
174
175 kill() {
176 super.kill();
177 player.loveUp();
178 }
179
180 overlap_player(player) {
181 player.damage(10);
182 player.switchmode(MODE_DETERMINATION);
183 }
184
185 update() {
186 super.update();
187 if (this.alive && player.alive && (game.time.now > this.nexttime_shoot))
188 {
189 this.weapon.fireAtSprite(player);
190 this.nexttime_shoot = game.time.now + (WAIT_MONSTERSHOOT / 2) + (Math.random() * WAIT_MONSTERSHOOT);
191 }
192 game.physics.arcade.overlap(player, this.weapon.bullets, function(p,b) { b.overlap_player(p); });
193 }
194 }
195
196 class Determination extends Phaser.Sprite {
197 constructor(x, y) {
198 super(game, x, y, 'determination');
199 this.anchor.setTo(0.5, 0.5);
200 }
201
202 enablePhysics() {
203 game.physics.arcade.enable(this);
204 }
205 }
206
207 class Justice extends Phaser.Bullet {
208 constructor(game, x, y, key, frame) {
209 super(game, x, y, 'projectile');
210 this.anchor.setTo(0.5, 0.5);
211 }
212
213 overlap_monster(entity) {
214 entity.damage(player.love);
215 this.kill();
216 }
217
218 overlap_player(player) {
219 player.damage(player.love);
220 this.kill();
221 }
222 }
223
224 class JusticeBlaster extends Phaser.Weapon {
225 constructor(game, parent) {
226 super(game, parent);
227 this.bulletClass = Justice;
228 this.multiFire = true;
229 this.createBullets(100, null);
230 }
231 }
232
233 class HealthBar {
234 constructor(x, y) {
235 this.mhealth = game.add.tileSprite(x + 10, y, 0, HB_THICKNESS, 'wall');
236 this.ahealth = game.add.tileSprite(x + 10, y, 0, HB_THICKNESS, 'wall');
237 this.mhealth.anchor.setTo(0, 0.5);
238 this.ahealth.anchor.setTo(0, 0.5);
239 this.mhealth.tint = '0xff0000';
240 this.ahealth.tint = '0xffff00';
241 this.text_love = new Phaser.Text(game, x - 50, y + 2, null, { align: 'center', fill: 'white', font: 'Ubuntu Mono', fontSize: 16, fontWeight: 'bold' });
242 this.text_love.anchor.setTo(0, 0.5);
243 game.add.existing(this.text_love);
244 }
245
246 update(love, mhealth, ahealth) {
247 this.mhealth.width = mhealth;
248 this.ahealth.width = ahealth;
249 this.text_love.text = "LV " + love;
250 }
251 }
252
253
254 class GamePlay extends Phaser.State {
255
256 constructor() {
257
258 super();
259 game.state.add('GameOver', GameOver, false);
260
261 }
262
263 preload() {
264
265 game.load.image('wall', 'wall.png');
266 game.load.image('player', 'player.png');
267 game.load.image('monster', 'monster.png');
268 game.load.image('determination', 'determination.png');
269 game.load.image('projectile', 'projectile.png');
270
271 }
272
273 create() {
274
275 game.world.setBounds(0, 0, 800, 600);
276 game.stage.backgroundColor = '#000000';
277 game.physics.startSystem(Phaser.Physics.ARCADE);
278
279 walls = game.add.group();
280 walls.classType = Phaser.TileSprite;
281 walls.enableBody = true;
282
283 walls.add(game.add.tileSprite(WALL_BORDER, WALL_BORDER, game.world.width - (WALL_BORDER * 2), WALL_THICKNESS, 'wall'));
284 walls.add(game.add.tileSprite(WALL_BORDER, game.world.height - WALL_THICKNESS - WALL_BORDERBOTTOM, game.world.width - (WALL_BORDER * 2), WALL_THICKNESS, 'wall'));
285 walls.add(game.add.tileSprite(WALL_BORDER, WALL_BORDER, WALL_THICKNESS, game.world.height - WALL_BORDER - WALL_BORDERBOTTOM, 'wall'));
286 walls.add(game.add.tileSprite(game.world.width - WALL_THICKNESS - WALL_BORDER, WALL_BORDER, WALL_THICKNESS, game.world.height - WALL_BORDER - WALL_BORDERBOTTOM, 'wall'));
287 walls.children.forEach(function(wall) { wall.body.immovable = true; });
288
289 determinations = game.add.group();
290 nexttime_determination = Math.random() * WAIT_DETERMINATION;
291
292 monsters = game.add.group();
293 nexttime_monsterspawn = WAIT_MONSTERSPAWN / 4;
294
295 player = new Player(game.world.width / 2, game.world.height / 2);
296 player.enablePhysics();
297 game.add.existing(player);
298 game.camera.follow(player);
299 cursors = game.input.keyboard.createCursorKeys();
300
301 }
302
303 update() {
304
305 game.physics.arcade.collide(player, walls);
306 game.physics.arcade.overlap(player, determinations, function(p,d) { p.overlap_determination(d); });
307 game.physics.arcade.overlap(player, monsters, function(p,m) { m.overlap_player(p); });
308
309 if (game.time.now > nexttime_determination)
310 {
311 var determination = new Determination((Math.random() * (game.world.width - ((WALL_THICKNESS + WALL_BORDER) * 4))) + ((WALL_THICKNESS + WALL_BORDER) * 2), (Math.random() * (game.world.height - ((WALL_THICKNESS + WALL_BORDER) * 4) - WALL_BORDERBOTTOM)) + ((WALL_THICKNESS + WALL_BORDER) * 2));
312 determination.enablePhysics();
313 determinations.add(determination);
314 nexttime_determination = game.time.now + (WAIT_DETERMINATION / 2) + (Math.random() * WAIT_DETERMINATION);
315 }
316
317 if (game.time.now > nexttime_monsterspawn)
318 {
319 var monster = new Monster(Math.random() * game.world.width, Math.random() * game.world.height);
320 monster.enablePhysics();
321 monsters.add(monster);
322 var waittime = WAIT_MONSTERSPAWN - ((Math.random() * 2000) * player.love);
323 nexttime_monsterspawn = game.time.now + waittime;
324 console.log('Next monster in ' + waittime);
325 }
326
327 if ((game.time.now - player.lastmovetime) > WAIT_KEY) player.body.velocity.x = player.body.velocity.y = 0;
328
329 }
330 }
331
332 class GameOver extends Phaser.State {
333
334 create() {
335
336 this.game.add.text(this.game.world.width / 2, 0, "GAME\nOVER", { align: 'center', fill: 'white', font: 'Ubuntu Mono', fontSize: 160, fontWeight: 'bold' }).anchor.setTo(0.5, 0);
337 this.game.add.text(this.game.world.width / 2, this.game.world.height * 0.75, "Stay determined...", { align: 'center', fill: 'white', font: 'Ubuntu Mono', fontSize: 24, fontWeight: 'bold' }).anchor.setTo(0.5, 0);
338
339 }
340
341 update() {
342
343 if (this.game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR))
344 {
345 this.game.state.start('GamePlay');
346 }
347
348 }
349
350 }