Some hero thing

by Sam Fereday

HTML

<p>
  Current State:
</p>
<div data-bind="text: hero.mode" id="currentState">
  ???
</div>
<p>
Overall Health:
</p>
<div data-bind="text: hero.health">
 ???
</div>
<p>
Tiredness:
</p>
<div data-bind="text: hero.tiredness">
 ???
</div>
<p>
Hunger:
</p>
<div data-bind="text: hero.hunger">
 ???
</div>
<p>
Forage:
</p>
<div data-bind="visible: hero.lookingForFood()">
    Looking for something to eat...
</div>
<div data-bind="visible: !hero.lookingForFood()">
    Not looking.
</div>
<p>
Training:
</p>
<div data-bind="text: hero.combatSkill">
 ???
</div>
<p>
Combat Proficiency:
</p>
<div data-bind="visible: hero.combatSkill() < 5">
    Idiot
</div>
<div data-bind="visible: hero.combatSkill() > 5 && hero.combatSkill() < 15">
    Trainee
</div>
<div data-bind="visible: hero.combatSkill() > 15 && hero.combatSkill() < 25">
    Promising
</div>
<div data-bind="visible: hero.combatSkill() > 25 && hero.combatSkill() < 35">
    Well Seasoned
</div>
<div data-bind="visible: hero.combatSkill() > 35 && hero.combatSkill() < 45">
    Skilled
</div>
<div data-bind="visible: hero.combatSkill() > 45 && hero.combatSkill() < 55">
    Expert
</div>
<div data-bind="visible: hero.combatSkill() > 55 && hero.combatSkill() < 65">
    Slayer
</div>
<div data-bind="visible: hero.combatSkill() > 65 && hero.combatSkill() < 75">
    Hero
</div>
<div data-bind="visible: hero.combatSkill() > 75">
    Legendary
</div>
<button data-bind="hunt">
  Hunt
</button>
<button data-bind="forage">
  Forage
</button>
<button data-bind="eat">
  Eat
</button>
<button data-bind="sleep">
  Sleep
</button>

CSS

body {
  padding: 2em;
  margin: 0;
  font: 75%/1.4em arial;
}

p {
  margin: 0;
  padding: 0;
}

div {
  padding: 1em;
}

.bar {
  width: 100%;
  padding: 1em;
  box-sizing: border-box;
  background: #333;
}

JavaScript

//
console.clear();
var gameloop = (function() {
  'use strict';
  var reqAnimFrame = window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    function(callback) {
      window.setTimeout(callback, 1000 / 60);
    };

  return function(callback) {
    var lastUpdate = +new Date();
    (function loop() {
      callback(((+new Date()) - lastUpdate) / 1000);
      reqAnimFrame(loop);
      lastUpdate = +new Date();
    }());
  };
}());
//
let fsm = function() {
  this.states = [];
  this.currentState = [];
  this.initialized = false;
}

fsm.prototype.update = function() {

  this.currentState[this.currentState.length - 1].fn();

}

fsm.prototype.init = function(name) {
  if (this.initialized)
    return;

  let st = this.states.find(function(item) {
    return item.name === name;
  });

  this.currentState.push(st);

  this.initialized = true;
}

fsm.prototype.push = function(name) {

  if (this.currentState.length > 0 && this.currentState[this.currentState.length - 1].name === name)
    return;

  let st = this.states.find(function(item) {
    return item.name === name;
  });

  this.currentState.push(st);

}

fsm.prototype.pop = function() {
  if (this.currentState.length > 1)
    this.currentState.pop();
}

fsm.prototype.addState = function(name, state) {
    this.states.push({
      name: name,
      fn: state
    });
  }
  //
var Entity = function() {

  var self = this;

	this.health = ko.observable(100);
  this.tiredness = ko.observable(0);
  this.hunger = ko.observable(0);
  this.forage = ko.observable(false);
  this.lookingForFood = ko.observable(false);
  this.combatSkill = ko.observable(0);

  this.modes = {
    IDLE: 0,
    SLEEP: 1,
    FORAGE: 2,
    EAT: 3,
    TRAIN: 4
  }

  this.mode = ko.observable(this.modes.IDLE);

  this.fsm = new fsm();

  // Push idle state (you need this first)
  this.fsm.addState('idle', function() {

    console.log("Idle state.");
    
    let health =...