Baby K'tah

Massively stripped-down simulation of the famous K'tah

by Ray Toal

HTML

<h1>Baby K'tah</h1>
<div><progress id="health" max=100></progress></div>
<canvas id="canvas" width=600 height=600>Get a better browser</canvas>

CSS

@import url(https://fonts.googleapis.com/css?family=Nosifer);

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  background-color: black;
  text-align:center;
  color: white;
}

progress {
  width: 400px;
  height: 20px;
}

canvas {
  background-color: #ddffdd;
  margin: 0 auto;
  display: inline;
}

JavaScript

// This is a rough basis from which to build a more sophisticated game.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const progress = document.getElementById('health');

function clamp(value, low, high) {
  return Math.max(low, Math.min(high, value));
}

function createEventListeners() {
  document.body.onmousemove = (e) => {
    const canvasRect = canvas.getBoundingClientRect();
    mouse.x = e.clientX - canvasRect.left;
    mouse.y = e.clientY - canvasRect.top;
  }
}

class Agent {
  constructor(x, y, color, radius, speed) {
    Object.assign(this, { x, y, color, radius, speed });
  }
  draw() {
    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 2*Math.PI, false);
    ctx.fill();
    ctx.closePath();
  }
  distanceTo(other) {
    return Math.hypot(other.x - this.x, other.y - this.y);
  }
  collidedWith(other) {
    return this.distanceTo(other) <= this.radius+other.radius;
  }
  moveToward(target) {
    const dx = target.x - this.x;
    const dy = target.y - this.y;
    var distance = Math.hypot(dx, dy);
    if (distance < 1) return this;
    this.x = clamp(this.x + this.speed*dx/distance, 0, canvas.width);
    this.y = clamp(this.y + this.speed*dy/distance, 0, canvas.height);
    return this;
  }
}

class Player extends Agent {
  constructor(x, y) {
    super(x, y, 'green', 15, 3)
    this.health = 100;
  }
  isAlive() {
    return this.health > 0;
  }
  takeHit(callback) {
    this.health--;
    callback(this.health);
  }
}

class Zombie extends Agent {
  constructor(x, y, speed) {
    super(x, y, 'rgba(225,128,70,0.5)', 18, speed);
  }
}

function drawBackground() {
  ctx.fillStyle = 'lightgreen';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
}

function advanceGameState() {
  player.moveToward(mouse).draw(ctx);
  for (let zombie of zombies) {
    zombie.moveToward(player).draw(ctx);
    if (zombie.collidedWith(player)) {
      player.takeHit(health => progress.value =...