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