import * as state from '/static/games/rms/state.js'; import * as constants from '/static/games/rms/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()