kill counter fixed. 8x8 anf 8x16 fonts added. terrain slides are aminated.
This commit is contained in:
1 parent
43ee0d9150
commit
e8a79c2a81
9 files changed
+79
-19
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,8 @@
|
|||||||
canvas {image-rendering: pixelated; margin: auto;
|
canvas {image-rendering: pixelated; margin: auto;
|
||||||
position:absolute; left:0; right:0; top:0; bottom:0}
|
position:absolute; left:0; right:0; top:0; bottom:0}
|
||||||
@font-face { font-family: 'text'; src:url(assets/font.ttf) }
|
@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) }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+1
-1
@@ -121,7 +121,7 @@ export const PLAYER_STARTING_TOOLS = [
|
|||||||
|
|
||||||
export const PANEL_WIDTH = 400;
|
export const PANEL_WIDTH = 400;
|
||||||
export const PANEL_HEIGHT = 300;
|
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_TITLE_FONT_SIZE = 16;
|
||||||
export const PANEL_BG_COLOR = '#666';
|
export const PANEL_BG_COLOR = '#666';
|
||||||
export const PANEL_BORDER_COLOR = '#333';
|
export const PANEL_BORDER_COLOR = '#333';
|
||||||
|
|||||||
+5
-1
@@ -163,8 +163,12 @@ export function drawParagraph(ctx, text, x, y, w, h, color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function loop(fn) {
|
export function loop(fn) {
|
||||||
|
let lastTime = performance.now();
|
||||||
(function looped() {
|
(function looped() {
|
||||||
fn();
|
const now = performance.now();
|
||||||
|
const dt = Math.min((now - lastTime) / 1000, 0.1);
|
||||||
|
lastTime = now;
|
||||||
|
fn(dt);
|
||||||
requestAnimationFrame(looped);
|
requestAnimationFrame(looped);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
+19
-5
@@ -6,7 +6,7 @@ import {clamp, deg2rad, distance, parable, random, randomInt, vec, wrap} from '.
|
|||||||
import {PROJECTILE_TYPES} from './projectiles.js?v=5';
|
import {PROJECTILE_TYPES} from './projectiles.js?v=5';
|
||||||
import {generateSky} from './sky.js?v=5';
|
import {generateSky} from './sky.js?v=5';
|
||||||
import {playTickSound} from './sound.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 {sample, shuffle} from './utils.js?v=5';
|
||||||
import {EXPLOSION_TYPES} from './weapons.js?v=5';
|
import {EXPLOSION_TYPES} from './weapons.js?v=5';
|
||||||
import {drawPanelBg, drawPanelTitle, drawPanelText, drawPanelDivider, drawPanelMenu, getPanelBounds} from './panel.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 screenShake = 0;
|
||||||
let trajectories = [];
|
let trajectories = [];
|
||||||
let idle = false;
|
let idle = false;
|
||||||
|
let dt = 0;
|
||||||
|
let collapseState = null;
|
||||||
let winner;
|
let winner;
|
||||||
|
|
||||||
let score = 0;
|
let score = 0;
|
||||||
@@ -162,6 +164,7 @@ function initAllPlayerStats() {
|
|||||||
deaths: 0,
|
deaths: 0,
|
||||||
shotsFired: 0,
|
shotsFired: 0,
|
||||||
wins: 0,
|
wins: 0,
|
||||||
|
lastDamageSource: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +203,7 @@ function updateMarket() {
|
|||||||
const item = marketItems[menuState.selected];
|
const item = marketItems[menuState.selected];
|
||||||
const itemData = MARKET_ITEMS[item];
|
const itemData = MARKET_ITEMS[item];
|
||||||
const humanPlayer = players.find(p => !p.ai);
|
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;
|
score -= itemData.price;
|
||||||
let existingWeapon = humanPlayer.weapons.find(w => w.type === item);
|
let existingWeapon = humanPlayer.weapons.find(w => w.type === item);
|
||||||
if (existingWeapon) {
|
if (existingWeapon) {
|
||||||
@@ -285,6 +288,7 @@ function initPlayers() {
|
|||||||
player.kills = 0;
|
player.kills = 0;
|
||||||
player.deaths = 0;
|
player.deaths = 0;
|
||||||
player.shotsFired = 0;
|
player.shotsFired = 0;
|
||||||
|
player.lastDamageSource = null;
|
||||||
}
|
}
|
||||||
players = existingPlayers;
|
players = existingPlayers;
|
||||||
} else {
|
} else {
|
||||||
@@ -310,6 +314,7 @@ function initPlayers() {
|
|||||||
deaths: 0,
|
deaths: 0,
|
||||||
shotsFired: 0,
|
shotsFired: 0,
|
||||||
wins: 0,
|
wins: 0,
|
||||||
|
lastDamageSource: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,7 +440,7 @@ function update() {
|
|||||||
for (let i=projectiles.length-1; i>=0; i--) {
|
for (let i=projectiles.length-1; i>=0; i--) {
|
||||||
const projectile = projectiles[i];
|
const projectile = projectiles[i];
|
||||||
const projectileType = PROJECTILE_TYPES[projectile.type];
|
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);
|
projectileType.stop(projectile);
|
||||||
projectiles.splice(i, 1);
|
projectiles.splice(i, 1);
|
||||||
}
|
}
|
||||||
@@ -463,6 +468,8 @@ function update() {
|
|||||||
let remainingDamage = damage;
|
let remainingDamage = damage;
|
||||||
if (!damage) continue;
|
if (!damage) continue;
|
||||||
|
|
||||||
|
if (explosion.source) player.lastDamageSource = explosion.source;
|
||||||
|
|
||||||
if (player.shield) {
|
if (player.shield) {
|
||||||
remainingDamage = clamp(0, damage-player.shield.energy, Infinity);
|
remainingDamage = clamp(0, damage-player.shield.energy, Infinity);
|
||||||
player.shield.energy = clamp(0, player.shield.energy-damage, Infinity);
|
player.shield.energy = clamp(0, player.shield.energy-damage, Infinity);
|
||||||
@@ -478,9 +485,12 @@ function update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (state === 'land-collapse') {
|
else if (state === 'land-collapse') {
|
||||||
collapseTerrain(terrain);
|
if (!collapseState) collapseState = startCollapseTerrain(terrain);
|
||||||
|
if (collapseTerrainStep(terrain, collapseState)) {
|
||||||
|
collapseState = null;
|
||||||
state = 'land-players';
|
state = 'land-players';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (state === 'land-players') {
|
else if (state === 'land-players') {
|
||||||
let stable = true;
|
let stable = true;
|
||||||
@@ -518,6 +528,9 @@ function update() {
|
|||||||
createParticles(x, y, PLAYER_EXPLOSION_PARTICLE_POWER, c);
|
createParticles(x, y, PLAYER_EXPLOSION_PARTICLE_POWER, c);
|
||||||
dyingPlayer.dead = true;
|
dyingPlayer.dead = true;
|
||||||
dyingPlayer.deaths++;
|
dyingPlayer.deaths++;
|
||||||
|
if (dyingPlayer.lastDamageSource && dyingPlayer.lastDamageSource !== dyingPlayer) {
|
||||||
|
dyingPlayer.lastDamageSource.kills++;
|
||||||
|
}
|
||||||
state = 'explosions';
|
state = 'explosions';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -912,7 +925,8 @@ function drawStatus() {
|
|||||||
drawText(foreground, `Round: ${round}/${totalRounds} Score: ${score}`, W-8, 18, 'white', 'right');
|
drawText(foreground, `Round: ${round}/${totalRounds} Score: ${score}`, W-8, 18, 'white', 'right');
|
||||||
}
|
}
|
||||||
|
|
||||||
loop(() => {
|
loop((deltaTime) => {
|
||||||
|
dt = deltaTime;
|
||||||
update();
|
update();
|
||||||
draw();
|
draw();
|
||||||
});
|
});
|
||||||
+2
-2
@@ -20,7 +20,7 @@ export function drawPanelBg(ctx) {
|
|||||||
export function drawPanelTitle(ctx, text) {
|
export function drawPanelTitle(ctx, text) {
|
||||||
const x = PANEL_X + PANEL_WIDTH / 2;
|
const x = PANEL_X + PANEL_WIDTH / 2;
|
||||||
const y = PANEL_Y + 20;
|
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.textBaseline = 'top';
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.fillStyle = 'black';
|
ctx.fillStyle = 'black';
|
||||||
@@ -30,7 +30,7 @@ export function drawPanelTitle(ctx, text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function drawPanelText(ctx, text, x, y, color = 'white', align = 'left') {
|
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.textBaseline = 'top';
|
||||||
ctx.textAlign = align;
|
ctx.textAlign = align;
|
||||||
ctx.fillStyle = 'black';
|
ctx.fillStyle = 'black';
|
||||||
|
|||||||
+13
-10
@@ -23,13 +23,14 @@ export const PROJECTILE_TYPES = {
|
|||||||
stop(projectile) {
|
stop(projectile) {
|
||||||
projectile.osc.stop(0);
|
projectile.osc.stop(0);
|
||||||
},
|
},
|
||||||
update(projectile, terrain, projectiles, trajectories, explosions) {
|
update(projectile, terrain, projectiles, trajectories, explosions, dt) {
|
||||||
const prevProjectile = {...projectile};
|
const prevProjectile = {...projectile};
|
||||||
const {weapon, player, wind} = projectile;
|
const {weapon, player, wind} = projectile;
|
||||||
const weaponType = WEAPON_TYPES[weapon.type];
|
const weaponType = WEAPON_TYPES[weapon.type];
|
||||||
let exploded = false;
|
let exploded = false;
|
||||||
|
const iterations = Math.max(1, Math.round(PROJECTILE_ITERATIONS_PER_FRAME * dt * 60));
|
||||||
|
|
||||||
for (let i=0; i<PROJECTILE_ITERATIONS_PER_FRAME; i++) {
|
for (let i=0; i<iterations; i++) {
|
||||||
const {ox, oy, a, p, t} = projectile;
|
const {ox, oy, a, p, t} = projectile;
|
||||||
|
|
||||||
const [x, y] = parable(
|
const [x, y] = parable(
|
||||||
@@ -69,7 +70,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
) {
|
) {
|
||||||
const explosionSpec = weaponType.explosion;
|
const explosionSpec = weaponType.explosion;
|
||||||
const explosionType = EXPLOSION_TYPES[explosionSpec.type];
|
const explosionType = EXPLOSION_TYPES[explosionSpec.type];
|
||||||
explosions.push(explosionType.create(explosionSpec, x, y));
|
explosions.push({...explosionType.create(explosionSpec, x, y), source: player});
|
||||||
// @ts-ignore: canvas color hack
|
// @ts-ignore: canvas color hack
|
||||||
createParticles(x, y, p, terrain.color);
|
createParticles(x, y, p, terrain.color);
|
||||||
exploded = true;
|
exploded = true;
|
||||||
@@ -108,7 +109,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
stop(projectile) {
|
stop(projectile) {
|
||||||
projectile.osc.stop(0);
|
projectile.osc.stop(0);
|
||||||
},
|
},
|
||||||
update(projectile, terrain, projectiles, trajectories, explosions) {
|
update(projectile, terrain, projectiles, trajectories, explosions, dt) {
|
||||||
const prevProjectile = {...projectile};
|
const prevProjectile = {...projectile};
|
||||||
const {state, weapon, player, wind} = projectile;
|
const {state, weapon, player, wind} = projectile;
|
||||||
const weaponType = WEAPON_TYPES[weapon.type];
|
const weaponType = WEAPON_TYPES[weapon.type];
|
||||||
@@ -116,7 +117,8 @@ export const PROJECTILE_TYPES = {
|
|||||||
|
|
||||||
if (state === 'flying') {
|
if (state === 'flying') {
|
||||||
const {ox, oy, a, p, t} = projectile;
|
const {ox, oy, a, p, t} = projectile;
|
||||||
for (let i=0; i<PROJECTILE_ITERATIONS_PER_FRAME; i++) {
|
const iterations = Math.max(1, Math.round(PROJECTILE_ITERATIONS_PER_FRAME * dt * 60));
|
||||||
|
for (let i=0; i<iterations; i++) {
|
||||||
let [x, y] = parable(
|
let [x, y] = parable(
|
||||||
t, ox, oy, deg2rad(180+a),
|
t, ox, oy, deg2rad(180+a),
|
||||||
p / PROJECTILE_POWER_REDUCTION_FACTOR,
|
p / PROJECTILE_POWER_REDUCTION_FACTOR,
|
||||||
@@ -183,12 +185,13 @@ export const PROJECTILE_TYPES = {
|
|||||||
|
|
||||||
else if (state === 'rolling') {
|
else if (state === 'rolling') {
|
||||||
const {x, y, d} = projectile;
|
const {x, y, d} = projectile;
|
||||||
const nextY = landHeight(terrain, x+d);
|
const rollSpeed = Math.max(1, Math.round(dt * 60));
|
||||||
|
const nextY = landHeight(terrain, x + d * rollSpeed);
|
||||||
|
|
||||||
if (nextY < y || y > H || isTank(x, y)) {
|
if (nextY < y || y > H || isTank(x, y)) {
|
||||||
projectile.state = 'explode';
|
projectile.state = 'explode';
|
||||||
} else {
|
} else {
|
||||||
projectile.x += d;
|
projectile.x += d * rollSpeed;
|
||||||
projectile.y = nextY;
|
projectile.y = nextY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,7 +200,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
const {x, y, p} = projectile;
|
const {x, y, p} = projectile;
|
||||||
const explosionSpec = weaponType.explosion;
|
const explosionSpec = weaponType.explosion;
|
||||||
const explosionType = EXPLOSION_TYPES[explosionSpec.type];
|
const explosionType = EXPLOSION_TYPES[explosionSpec.type];
|
||||||
explosions.push(explosionType.create(explosionSpec, x, y));
|
explosions.push({...explosionType.create(explosionSpec, x, y), source: player});
|
||||||
// @ts-ignore: canvas color hack
|
// @ts-ignore: canvas color hack
|
||||||
createParticles(x, y, p, terrain.color);
|
createParticles(x, y, p, terrain.color);
|
||||||
finished = true;
|
finished = true;
|
||||||
@@ -255,7 +258,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
}];
|
}];
|
||||||
},
|
},
|
||||||
stop() {},
|
stop() {},
|
||||||
update(projectile, terrain, projectiles, trajectories, explosions) {
|
update(projectile, terrain, projectiles, trajectories, explosions, dt) {
|
||||||
const {player, weapon, ox, oy, a, p, wind, n, s} = projectile;
|
const {player, weapon, ox, oy, a, p, wind, n, s} = projectile;
|
||||||
const projectileType = PROJECTILE_TYPES.normal;
|
const projectileType = PROJECTILE_TYPES.normal;
|
||||||
|
|
||||||
@@ -268,7 +271,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const alive = projectileType.update(
|
const alive = projectileType.update(
|
||||||
projectile.payload, terrain, projectiles, trajectories, explosions
|
projectile.payload, terrain, projectiles, trajectories, explosions, dt
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!alive) {
|
if (!alive) {
|
||||||
|
|||||||
@@ -72,6 +72,43 @@ export function collapseTerrain(ctx, ) {
|
|||||||
cacheImageData(ctx);
|
cacheImageData(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function startCollapseTerrain(terrain) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collapseTerrainStep(terrain, state) {
|
||||||
|
const {width, height} = terrain.canvas;
|
||||||
|
const imageData = terrain.getImageData(0, 0, width, height);
|
||||||
|
const data = imageData.data;
|
||||||
|
let anyMoved = false;
|
||||||
|
|
||||||
|
for (let y = height - 2; y >= 0; y--) {
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
const idx = (y * width + x) * 4;
|
||||||
|
const belowIdx = ((y + 1) * width + x) * 4;
|
||||||
|
if (data[idx + 3] > 0 && data[belowIdx + 3] === 0) {
|
||||||
|
data[belowIdx] = data[idx];
|
||||||
|
data[belowIdx + 1] = data[idx + 1];
|
||||||
|
data[belowIdx + 2] = data[idx + 2];
|
||||||
|
data[belowIdx + 3] = data[idx + 3];
|
||||||
|
data[idx] = 0;
|
||||||
|
data[idx + 1] = 0;
|
||||||
|
data[idx + 2] = 0;
|
||||||
|
data[idx + 3] = 0;
|
||||||
|
anyMoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyMoved) {
|
||||||
|
cacheImageData(terrain);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
terrain.putImageData(imageData, 0, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export function isTerrain(ctx, x, y) {
|
export function isTerrain(ctx, x, y) {
|
||||||
const index = coords2index(ctx.canvas.width, x, y) * 4;
|
const index = coords2index(ctx.canvas.width, x, y) * 4;
|
||||||
return cachedImageData.data[index+3] > 0;
|
return cachedImageData.data[index+3] > 0;
|
||||||
|
|||||||
Reference in new issue
Block a user