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