diff --git a/assets/Web437_IBM_BIOS.woff b/assets/Web437_IBM_BIOS.woff new file mode 100644 index 0000000..9daecb3 Binary files /dev/null and b/assets/Web437_IBM_BIOS.woff differ diff --git a/assets/Web437_IBM_VGA_8x16.woff b/assets/Web437_IBM_VGA_8x16.woff new file mode 100644 index 0000000..1f1508d Binary files /dev/null and b/assets/Web437_IBM_VGA_8x16.woff differ diff --git a/index.html b/index.html index 98bd407..60f3573 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,8 @@ canvas {image-rendering: pixelated; margin: auto; position:absolute; left:0; right:0; top:0; bottom:0} @font-face { font-family: 'text'; src:url(assets/font.ttf) } + @font-face { font-family: 'ibm-vga'; src:url(assets/Web437_IBM_VGA_8x16.woff) } + @font-face { font-family: 'ibm-bios'; src:url(assets/Web437_IBM_BIOS.woff) }
diff --git a/src/constants.js b/src/constants.js index b6d3672..5fe5015 100644 --- a/src/constants.js +++ b/src/constants.js @@ -121,7 +121,7 @@ export const PLAYER_STARTING_TOOLS = [ export const PANEL_WIDTH = 400; export const PANEL_HEIGHT = 300; -export const PANEL_FONT_SIZE = 10; +export const PANEL_FONT_SIZE = 8; export const PANEL_TITLE_FONT_SIZE = 16; export const PANEL_BG_COLOR = '#666'; export const PANEL_BORDER_COLOR = '#333'; diff --git a/src/gfx.js b/src/gfx.js index 806a208..271d14d 100644 --- a/src/gfx.js +++ b/src/gfx.js @@ -163,8 +163,12 @@ export function drawParagraph(ctx, text, x, y, w, h, color) { } export function loop(fn) { + let lastTime = performance.now(); (function looped() { - fn(); + const now = performance.now(); + const dt = Math.min((now - lastTime) / 1000, 0.1); + lastTime = now; + fn(dt); requestAnimationFrame(looped); })(); } diff --git a/src/main.js b/src/main.js index 9aa1fc8..5132ff9 100644 --- a/src/main.js +++ b/src/main.js @@ -6,7 +6,7 @@ import {clamp, deg2rad, distance, parable, random, randomInt, vec, wrap} from '. import {PROJECTILE_TYPES} from './projectiles.js?v=5'; import {generateSky} from './sky.js?v=5'; import {playTickSound} from './sound.js?v=5'; -import {clipTerrain, closestLand, collapseTerrain, generateTerrain, isTerrain, landHeight} from './terrain.js?v=5'; +import {clipTerrain, closestLand, collapseTerrain, generateTerrain, isTerrain, landHeight, startCollapseTerrain, collapseTerrainStep} from './terrain.js?v=5'; import {sample, shuffle} from './utils.js?v=5'; import {EXPLOSION_TYPES} from './weapons.js?v=5'; import {drawPanelBg, drawPanelTitle, drawPanelText, drawPanelDivider, drawPanelMenu, getPanelBounds} from './panel.js?v=5'; @@ -22,6 +22,8 @@ let particles = []; let screenShake = 0; let trajectories = []; let idle = false; +let dt = 0; +let collapseState = null; let winner; let score = 0; @@ -162,6 +164,7 @@ function initAllPlayerStats() { deaths: 0, shotsFired: 0, wins: 0, + lastDamageSource: null, }); } } @@ -200,7 +203,7 @@ function updateMarket() { const item = marketItems[menuState.selected]; const itemData = MARKET_ITEMS[item]; const humanPlayer = players.find(p => !p.ai); - if (humanPlayer && score >= itemData.price && item !== 'babyMissile') { + if (humanPlayer && score >= itemData.price && item !== 'babyMissile' && WEAPON_TYPES[item]) { score -= itemData.price; let existingWeapon = humanPlayer.weapons.find(w => w.type === item); if (existingWeapon) { @@ -285,6 +288,7 @@ function initPlayers() { player.kills = 0; player.deaths = 0; player.shotsFired = 0; + player.lastDamageSource = null; } players = existingPlayers; } else { @@ -310,6 +314,7 @@ function initPlayers() { deaths: 0, shotsFired: 0, wins: 0, + lastDamageSource: null, }); } } @@ -435,7 +440,7 @@ function update() { for (let i=projectiles.length-1; i>=0; i--) { const projectile = projectiles[i]; const projectileType = PROJECTILE_TYPES[projectile.type]; - if (projectileType.update(projectile, terrain, projectiles, trajectories, explosions)) continue; + if (projectileType.update(projectile, terrain, projectiles, trajectories, explosions, dt)) continue; projectileType.stop(projectile); projectiles.splice(i, 1); } @@ -463,6 +468,8 @@ function update() { let remainingDamage = damage; if (!damage) continue; + if (explosion.source) player.lastDamageSource = explosion.source; + if (player.shield) { remainingDamage = clamp(0, damage-player.shield.energy, Infinity); player.shield.energy = clamp(0, player.shield.energy-damage, Infinity); @@ -478,8 +485,11 @@ function update() { } else if (state === 'land-collapse') { - collapseTerrain(terrain); - state = 'land-players'; + if (!collapseState) collapseState = startCollapseTerrain(terrain); + if (collapseTerrainStep(terrain, collapseState)) { + collapseState = null; + state = 'land-players'; + } } else if (state === 'land-players') { @@ -518,6 +528,9 @@ function update() { createParticles(x, y, PLAYER_EXPLOSION_PARTICLE_POWER, c); dyingPlayer.dead = true; dyingPlayer.deaths++; + if (dyingPlayer.lastDamageSource && dyingPlayer.lastDamageSource !== dyingPlayer) { + dyingPlayer.lastDamageSource.kills++; + } state = 'explosions'; } @@ -912,7 +925,8 @@ function drawStatus() { drawText(foreground, `Round: ${round}/${totalRounds} Score: ${score}`, W-8, 18, 'white', 'right'); } -loop(() => { +loop((deltaTime) => { + dt = deltaTime; update(); draw(); }); diff --git a/src/panel.js b/src/panel.js index cbca199..4d0aef6 100644 --- a/src/panel.js +++ b/src/panel.js @@ -20,7 +20,7 @@ export function drawPanelBg(ctx) { export function drawPanelTitle(ctx, text) { const x = PANEL_X + PANEL_WIDTH / 2; const y = PANEL_Y + 20; - ctx.font = `${PANEL_TITLE_FONT_SIZE}px text`; + ctx.font = `${PANEL_TITLE_FONT_SIZE}px ibm-vga`; ctx.textBaseline = 'top'; ctx.textAlign = 'center'; ctx.fillStyle = 'black'; @@ -30,7 +30,7 @@ export function drawPanelTitle(ctx, text) { } export function drawPanelText(ctx, text, x, y, color = 'white', align = 'left') { - ctx.font = `${PANEL_FONT_SIZE}px text`; + ctx.font = `${PANEL_FONT_SIZE}px ibm-bios`; ctx.textBaseline = 'top'; ctx.textAlign = align; ctx.fillStyle = 'black'; diff --git a/src/projectiles.js b/src/projectiles.js index 85bea0f..5ee9a15 100644 --- a/src/projectiles.js +++ b/src/projectiles.js @@ -23,13 +23,14 @@ export const PROJECTILE_TYPES = { stop(projectile) { projectile.osc.stop(0); }, - update(projectile, terrain, projectiles, trajectories, explosions) { + update(projectile, terrain, projectiles, trajectories, explosions, dt) { const prevProjectile = {...projectile}; const {weapon, player, wind} = projectile; const weaponType = WEAPON_TYPES[weapon.type]; let exploded = false; + const iterations = Math.max(1, Math.round(PROJECTILE_ITERATIONS_PER_FRAME * dt * 60)); - for (let i=0; i