summaryrefslogtreecommitdiffstats
path: root/static/games
diff options
context:
space:
mode:
Diffstat (limited to 'static/games')
-rw-r--r--static/games/pong/pong.js2
-rw-r--r--static/games/stallman-shooter/assets/binary_blob.webpbin0 -> 1178 bytes
-rw-r--r--static/games/stallman-shooter/assets/gates.webpbin0 -> 2638 bytes
-rw-r--r--static/games/stallman-shooter/assets/introduction.webpbin0 -> 622752 bytes
-rw-r--r--static/games/stallman-shooter/assets/loss.webpbin0 -> 276316 bytes
-rw-r--r--static/games/stallman-shooter/assets/player_pepp.webpbin0 -> 2094 bytes
-rw-r--r--static/games/stallman-shooter/assets/stallman.webpbin0 -> 3654 bytes
-rw-r--r--static/games/stallman-shooter/assets/victory.webpbin0 -> 418448 bytes
-rw-r--r--static/games/stallman-shooter/constants.js2
-rw-r--r--static/games/stallman-shooter/gates.js143
-rw-r--r--static/games/stallman-shooter/main.js173
-rw-r--r--static/games/stallman-shooter/player.js88
-rw-r--r--static/games/stallman-shooter/state.js82
13 files changed, 489 insertions, 1 deletions
diff --git a/static/games/pong/pong.js b/static/games/pong/pong.js
index b8bdc2d..bdf95ac 100644
--- a/static/games/pong/pong.js
+++ b/static/games/pong/pong.js
@@ -161,7 +161,7 @@ function updateCanvasSize() {
canvas.height = window.innerHeight;
}
-const pingAudio = new Audio('/static/pong/ping.mp3');
+const pingAudio = new Audio('/static/games/pong/ping.mp3');
function ping() {
pingAudio.pause();
diff --git a/static/games/stallman-shooter/assets/binary_blob.webp b/static/games/stallman-shooter/assets/binary_blob.webp
new file mode 100644
index 0000000..b769114
--- /dev/null
+++ b/static/games/stallman-shooter/assets/binary_blob.webp
Binary files differ
diff --git a/static/games/stallman-shooter/assets/gates.webp b/static/games/stallman-shooter/assets/gates.webp
new file mode 100644
index 0000000..6be319e
--- /dev/null
+++ b/static/games/stallman-shooter/assets/gates.webp
Binary files differ
diff --git a/static/games/stallman-shooter/assets/introduction.webp b/static/games/stallman-shooter/assets/introduction.webp
new file mode 100644
index 0000000..f9e2f3d
--- /dev/null
+++ b/static/games/stallman-shooter/assets/introduction.webp
Binary files differ
diff --git a/static/games/stallman-shooter/assets/loss.webp b/static/games/stallman-shooter/assets/loss.webp
new file mode 100644
index 0000000..88bc4d6
--- /dev/null
+++ b/static/games/stallman-shooter/assets/loss.webp
Binary files differ
diff --git a/static/games/stallman-shooter/assets/player_pepp.webp b/static/games/stallman-shooter/assets/player_pepp.webp
new file mode 100644
index 0000000..a4332a7
--- /dev/null
+++ b/static/games/stallman-shooter/assets/player_pepp.webp
Binary files differ
diff --git a/static/games/stallman-shooter/assets/stallman.webp b/static/games/stallman-shooter/assets/stallman.webp
new file mode 100644
index 0000000..3b71b82
--- /dev/null
+++ b/static/games/stallman-shooter/assets/stallman.webp
Binary files differ
diff --git a/static/games/stallman-shooter/assets/victory.webp b/static/games/stallman-shooter/assets/victory.webp
new file mode 100644
index 0000000..17240b9
--- /dev/null
+++ b/static/games/stallman-shooter/assets/victory.webp
Binary files differ
diff --git a/static/games/stallman-shooter/constants.js b/static/games/stallman-shooter/constants.js
new file mode 100644
index 0000000..dc856d4
--- /dev/null
+++ b/static/games/stallman-shooter/constants.js
@@ -0,0 +1,2 @@
+export const x_dim = 800;
+export const y_dim = 600;
diff --git a/static/games/stallman-shooter/gates.js b/static/games/stallman-shooter/gates.js
new file mode 100644
index 0000000..c307f1e
--- /dev/null
+++ b/static/games/stallman-shooter/gates.js
@@ -0,0 +1,143 @@
+import * as constants from '/static/games/stallman-shooter/constants.js';
+
+export class Gates {
+ constructor() {
+ this.x = 200;
+ this.y = 40;
+ this.v_x = 0;
+ this.v_y = 0;
+ this.a_x = 0;
+ this.a_y = 0;
+ this.target_x = 200;
+ this.target_y = 40;
+ this.health = 1;
+ }
+
+ radius() {
+ return 30;
+ }
+
+ choose_position() {
+ const [x, y] = random_position();
+ this.target_x = x;
+ this.target_y = y;
+ }
+
+ act(count) {
+ const max_accel = (1 - this.health) * 0.75 + 0.25;
+ const max_velocity = (1 - this.health) * 5 + 5;
+ let dx = this.target_x - this.x;
+ let dy = this.target_y - this.y;
+ /* If we've arrived (close and stopped) */
+ if (Math.sqrt(dx * dx + dy * dy) < max_velocity / max_accel && Math.sqrt(this.v_x * this.v_x + this.v_y * this.v_y) < 0.01) {
+ /* Then pick a new target */
+ this.choose_position();
+
+ }
+
+ dx = this.target_x - this.x;
+ dy = this.target_y - this.y;
+ /* if we're close */
+ if (Math.sqrt(dx * dx + dy * dy) < max_velocity / max_accel) {
+ /* Then slow down */
+ const [x, y] = shorten(-this.v_x, -this.v_y, max_accel);
+ this.a_x = x;
+ this.a_y = y;
+ } else {
+ /* Otherwise, speed up */
+ const [x, y] = shorten(dx, dy, max_accel);
+ this.a_x = x;
+ this.a_y = y;
+ }
+
+ /* If we're going too fast */
+ if (Math.sqrt(this.v_x * this.v_x + this.v_y * this.v_y) > max_velocity) {
+ /* Then don't go faster */
+ if (Math.sign(this.v_x) == Math.sign(this.a_x)) {
+ this.a_x = 0;
+ }
+ if (Math.sign(this.v_y) == Math.sign(this.a_y)) {
+ this.a_y = 0;
+ }
+ }
+
+ this.v_x += this.a_x;
+ this.x += this.v_x;
+ this.v_y += this.a_y;
+ this.y += this.v_y;
+ if (count % 20 === 0) {
+ let num_bullets;
+ if (this.health >= 0.8) {
+ num_bullets = 5;
+ } else if (this.health >= 0.7) {
+ num_bullets = 6;
+ } else if (this.health >= 0.5) {
+ num_bullets = 7;
+ } else if (this.health >= 0.4) {
+ num_bullets = 8;
+ } else if (this.health >= 0.3) {
+ num_bullets = 9;
+ } else if (this.health >= 0.2) {
+ num_bullets = 10;
+ } else {
+ num_bullets = 11;
+ }
+ const result = [];
+ for (let i = 0; i < num_bullets; i++) {
+ result.push(random_bullet(this));
+ }
+ return result;
+ } else {
+ return [];
+ }
+ }
+
+ injure(damage) {
+ this.health -= damage;
+ }
+
+ center() {
+ return [16, 23];
+ }
+}
+
+export class GatesBullet {
+ constructor(x, d_x, y, d_y) {
+ this.x = x;
+ this.y = y;
+ this.d_x = d_x;
+ this.d_y = d_y;
+ }
+
+ radius() {
+ return 10;
+ }
+
+ center() {
+ return [20, 20];
+ }
+}
+
+function random_position() {
+ return [
+ Math.random() * (constants.x_dim - 200) + 100,
+ Math.random() * 100,
+ ];
+}
+
+function random_bullet(enemy) {
+ const multiplier = 2 + 3 * (1 - enemy.health);
+ const theta = Math.random() * 2 * Math.PI / 3 + Math.PI / 6;
+ const dx = Math.cos(theta) * multiplier;
+ const dy = Math.sin(theta) * multiplier;
+ return new GatesBullet(enemy.x, dx, enemy.y, dy);
+}
+
+function shorten(x, y, maxlen) {
+ const len = Math.sqrt(x * x + y * y);
+ if (len > maxlen) {
+ return [x / len / 2, y / len / 2];
+ } else {
+ return [x, y];
+ }
+}
diff --git a/static/games/stallman-shooter/main.js b/static/games/stallman-shooter/main.js
new file mode 100644
index 0000000..d3a3581
--- /dev/null
+++ b/static/games/stallman-shooter/main.js
@@ -0,0 +1,173 @@
+import * as state from '/static/games/stallman-shooter/state.js';
+import * as constants from '/static/games/stallman-shooter/constants.js';
+
+let events = [];
+
+window.addEventListener('keydown', (e) => events.push(['keydown', e.key]));
+window.addEventListener('keyup', (e) => events.push(['keyup', e.key]));
+
+const duration = 1000 / 60;
+
+function frame(ctx, st, events, textures) {
+ const new_bullets = [];
+ const count = st.count;
+ for (const enemy of st.enemies) {
+ new_bullets.push(...enemy.act(count));
+ }
+ for (const bullet of st.bullets) {
+ state.straight_bullet_act(bullet);
+ }
+ for (const bullet of st.player_bullets) {
+ state.straight_bullet_act(bullet);
+ }
+ st.player.act(events);
+ if (count % st.player.fire_rate === 0) {
+ st.player_bullets.push(new state.PBullet(st.player.x, 0, st.player.y, -3.5));
+ }
+ for (const bullet of st.bullets) {
+ if (state.overlaps(st.player, bullet)) {
+ return 'loss';
+ }
+ }
+ for (const bullet of new_bullets) {
+ if (state.overlaps(st.player, bullet)) {
+ return 'loss';
+ }
+ }
+ for (const enemy of st.enemies) {
+ for (const bullet of st.player_bullets) {
+ if (state.overlaps(enemy, bullet)) {
+ enemy.injure(bullet.damage());
+ state.straight_bullet_delete(bullet);
+ }
+ }
+ }
+ st.enemies = st.enemies.filter((enemy) => enemy.health > 0);
+ if (st.enemies.length === 0) {
+ return 'win';
+ }
+ st.bullets = st.bullets.filter((bullet) => !state.outside(bullet));
+ st.bullets.push(...new_bullets);
+ st.player_bullets = st.player_bullets.filter((bullet) => !state.outside(bullet));
+
+ for (const bullet of st.player_bullets) {
+ state.draw(bullet, ctx, textures.pbullet);
+ }
+ for (const enemy of st.enemies) {
+ state.draw(enemy, ctx, textures.enemy);
+ }
+ state.draw(st.player, ctx, textures.player);
+ for (const bullet of st.bullets) {
+ state.draw(bullet, ctx, textures.ebullet);
+ }
+ ctx.strokeStyle = "red";
+ ctx.strokeRect(0, 0, Math.round(st.enemies[0].health * constants.x_dim), 20);
+ st.count++;
+ return 'nothing';
+}
+
+async function get_bitmap(id) {
+ const image = document.getElementById(id);
+ await image.decode();
+ return await window.createImageBitmap(image);
+}
+
+async function get_player_bitmap() {
+ return get_bitmap('stallman');
+}
+
+async function get_enemy_bitmap() {
+ return get_bitmap('gates');
+}
+
+async function get_ebullet_bitmap() {
+ return get_bitmap('binary-blob');
+}
+
+async function get_pbullet_surface() {
+ return get_bitmap('player-pepp');
+}
+
+async function generic_message(ctx, image, okay) {
+ const bitmap = await get_bitmap(image);
+ while (true) {
+ await new Promise(window.requestAnimationFrame);
+ const ev = events;
+ events = [];
+ for (const event of ev) {
+ const [type, keycode] = event;
+ if (type === 'keydown' && keycode === okay) {
+ return;
+ }
+ }
+ ctx.drawImage(bitmap, 0, 0);
+ }
+}
+
+async function introduction(ctx) {
+ await generic_message(ctx, 'introduction', 'Enter');
+}
+
+async function loss(ctx) {
+ await generic_message(ctx, 'loss', 'r');
+}
+
+async function victory(ctx) {
+ await generic_message(ctx, 'victory', 'r');
+}
+
+async function main() {
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+
+ await introduction(ctx);
+
+ while (true) {
+ const st = new state.State();
+ const [
+ player_bitmap,
+ enemy_bitmap,
+ ebullet_bitmap,
+ pbullet_bitmap,
+ ] = await Promise.all([
+ get_bitmap('stallman'),
+ get_bitmap('gates'),
+ get_bitmap('binary-blob'),
+ get_bitmap('player-pepp'),
+ ]);
+ const textures = {
+ player: player_bitmap,
+ enemy: enemy_bitmap,
+ ebullet: ebullet_bitmap,
+ pbullet: pbullet_bitmap,
+ };
+ let last_frame_time = null;
+ let accumulator = 0;
+ game: while (true) {
+ const now = await new Promise(requestAnimationFrame);
+ if (last_frame_time !== null) {
+ accumulator += now - last_frame_time;
+ }
+ last_frame_time = now;
+
+ while (accumulator >= duration) {
+ accumulator -= duration;
+
+ ctx.fillStyle = "white";
+ ctx.fillRect(0, 0, constants.x_dim, constants.y_dim);
+ const result = frame(ctx, st, events, textures);
+ events = [];
+ if (result === 'loss') {
+ await loss(ctx);
+ break game;
+ }
+ if (result === 'win') {
+ await victory(ctx);
+ break game;
+ }
+ }
+ }
+ }
+}
+
+main()
diff --git a/static/games/stallman-shooter/player.js b/static/games/stallman-shooter/player.js
new file mode 100644
index 0000000..83144e0
--- /dev/null
+++ b/static/games/stallman-shooter/player.js
@@ -0,0 +1,88 @@
+import * as constants from '/static/games/stallman-shooter/constants.js';
+
+export class Player {
+ constructor() {
+ this.x = 400;
+ this.y = 500;
+ this.x_d = 0;
+ this.y_d = 0;
+ this.down_p = false;
+ this.left_p = false;
+ this.right_p = false;
+ this.up_p = false;
+ this.fire_rate = 15;
+ this.shift_p = false;
+ }
+
+ act(events) {
+ for (const event of events) {
+ const [kind, key] = event;
+ if (kind === 'keydown' && key === 'ArrowDown') {
+ this.down_p = true;
+ this.y_d = -1;
+ } else if (kind === 'keyup' && key === 'ArrowDown') {
+ this.down_p = false;
+ } else if (kind === 'keydown' && key === 'ArrowUp') {
+ this.up_p = true;
+ this.y_d = 1;
+ } else if (kind === 'keyup' && key === 'ArrowUp') {
+ this.up_p = false;
+ } else if (kind === 'keydown' && key === 'ArrowLeft') {
+ this.left_p = true;
+ this.x_d = -1;
+ } else if (kind === 'keyup' && key === 'ArrowLeft') {
+ this.left_p = false;
+ } else if (kind === 'keydown' && key === 'ArrowRight') {
+ this.right_p = true;
+ this.x_d = 1;
+ } else if (kind === 'keyup' && key === 'ArrowRight') {
+ this.right_p = false;
+ } else if (kind === 'keydown' && key === 'Shift') {
+ this.shift_p = true;
+ } else if (kind === 'keyup' && key === 'Shift') {
+ this.shift_p = false;
+ }
+ }
+ if (this.left_p) {
+ if (!this.right_p) {
+ this.x_d = -1;
+ }
+ } else if (this.right_p) {
+ this.x_d = 1;
+ } else {
+ this.x_d = 0;
+ }
+ if (this.up_p) {
+ if (!this.down_p) {
+ this.y_d = -1;
+ }
+ } else if (this.down_p) {
+ this.y_d = 1;
+ } else {
+ this.y_d = 0;
+ }
+ /* Don't go off the side */
+ if (this.x_d < 0 && this.x <= 0 || this.x >= constants.x_dim && this.x_d > 0) {
+ this.x_d = 0;
+ }
+ if (this.y <= 0 && this.y_d < 0 || this.y >= constants.y_dim && this.y_d > 0) {
+ this.y_d = 0;
+ }
+
+ if (this.shift_p) {
+ this.x += this.x_d;
+ this.y += this.y_d;
+ } else {
+ this.x += this.x_d * 3;
+ this.y += this.y_d * 3;
+ }
+ }
+
+ radius() {
+ return 3;
+ }
+
+ center() {
+ return [33, 21];
+ }
+}
diff --git a/static/games/stallman-shooter/state.js b/static/games/stallman-shooter/state.js
new file mode 100644
index 0000000..f184c3e
--- /dev/null
+++ b/static/games/stallman-shooter/state.js
@@ -0,0 +1,82 @@
+import * as player from '/static/games/stallman-shooter/player.js';
+import * as gates from '/static/games/stallman-shooter/gates.js';
+import * as constants from '/static/games/stallman-shooter/constants.js';
+
+export function straight_bullet_act(straight_bullet) {
+ const x = straight_bullet.x;
+ const d_x = straight_bullet.d_x;
+ straight_bullet.x += d_x;
+ const y = straight_bullet.y;
+ const d_y = straight_bullet.d_y;
+ straight_bullet.y += d_y;
+}
+
+export function straight_bullet_delete(straight_bullet) {
+ straight_bullet.x = -1000; // LMAO
+}
+
+export class PBullet {
+ constructor(x, d_x, y, d_y) {
+ this.x = x;
+ this.y = y;
+ this.d_x = d_x;
+ this.d_y = d_y;
+ }
+
+ damage() {
+ return 0.01
+ }
+
+ radius() {
+ return 10;
+ }
+
+ center() {
+ return [16, 22];
+ }
+}
+
+export class State {
+ constructor() {
+ this.player = new player.Player();
+ this.enemies = [new gates.Gates()];
+ this.bullets = [];
+ this.count = 0;
+ this.player_bullets = [];
+ }
+}
+
+function dist(x1, y1, x2, y2) {
+ const xdist = x1 - x2;
+ const ydist = y1 - y2;
+ return Math.sqrt(xdist * xdist + ydist * ydist);
+}
+
+export function overlaps(self, other) {
+ const sx = self.x;
+ const ox = other.x;
+ const sy = self.y;
+ const oy = other.y;
+ const threshold = self.radius() + other.radius();
+ if (Math.abs(sx - ox) >= threshold || Math.abs(sy - oy) >= threshold) {
+ return false;
+ }
+ const d = dist(sx, sy, ox, oy);
+ return d < threshold;
+}
+
+export function outside(positioned) {
+ const x = positioned.x;
+ const y = positioned.y;
+ return x < -100
+ || x > constants.x_dim + 100
+ || y < -100
+ || y > constants.y_dim + 100;
+}
+
+export function draw(positioned, ctx, bitmap) {
+ const [cx, cy] = positioned.center();
+ const x0 = Math.round(positioned.x) - cx;
+ const y0 = Math.round(positioned.y) - cy;
+ ctx.drawImage(bitmap, x0, y0);
+}