Battles in Kallisor 2.0

index.css

td {
    height: 10px;
    vertical-align: top;
    width: 50%;
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Battles in Kallisor</title>
    <link href="index.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <h1>Battles in Kallisor</h1>
    
    <div id="startUp" style="display:block;">
      <p id="intro"></p>
      Name: <input type="text" id="heroName" value="Dariak" />
      <p></p><br /><button id="startButton" onclick="StartGame()">Start the Game!</button>
    </div>
    
    <div id="battle" style="display:none;">
    <table>
  <tr>
    <td>
      <p id="heroStats">Hero</p>
    </td>
    <td>
      <div id="monsterStats"></div>
    </td>
  </tr>
  <tr> </tr>
  <tr>
    <td>
      <div id="actions">
        <p><button id="fightButton" onclick="Fight()">Fight</button>
        </p>
        <p><button id="herbButton" onclick="Herb()">Use Herb</button>
        </p>
        <p><button id="runButton" onclick="Run()">Run Away</button>
        </p>
        <p><button id="quitButton" onclick="Quit()">Quit</button>
        </p>
      </div>
    </td>
    <td>
      <textarea rows="15" cols="50" id="battleLog"></textarea>
    </td>
  </tr>
  <tr >
    <td></td>
    <td id="buyHerb" style="display:none;">
      <button onclick="BuyHerb('yes')">Buy an Herb</button> 
      <button onclick="BuyHerb('no')">Do Not Buy</button>
    </td>
  </tr>

  <tr>
    <td></td>
    <td>
      <button id="nextBattle" onclick="NextBattle()" style="display:none;">Begin the Next Battle</button>
      <button id="okButton" onclick="BuyHerb(null)" style="display:none;">OK</button>
      
    </td>
  </tr>

</table>

    </div>
    
    
    
    <script src="index.js"></script>
  </body>
</html>

index.js

//initiate variables.
var herbPower = 10;
var herbCost = 25;
var numberBattles = 0;
//var monstersFought = [];
var monsters = GetMonsterList();
var hero;
var monster;

//Welcome the player to the game
var display = "Welcome to Battles in Kallisor.";
display += "<p>In this simulation, you will battle against a random creature. ";
display += "If you win, you get some gold and maybe an item or two. ";
display += "If you lose, it's game over. </p>";
display += "<p>Herbs restore about " + herbPower + " Hit Points. You have a limited supply.</p>";
display += "<p>In this version, there are " + monsters.length + " types of monsters you may battle.<p>";

document.getElementById("intro").innerHTML = display;
//Show(display);


function StartGame() 
{
  document.getElementById("startUp").style.display = "none";
  document.getElementById("battle").style.display = "block";

  hero = GetHero();
  
  NextBattle();
}


function BeginBattle(hero) 
{
  if (hero.level > monsters.length && Math.random() < 0.3) 
  {
    monster = MakeRandomMonster(hero);
  } 
  else 
  {
    var maxRange = Math.min(monsters.length, hero.level + 2)
    var rand = Math.floor(Math.random() * maxRange);
    monster = CloneMonster(monsters[rand]);
  }

  hero.monstersFought.push(monster.Name);

  Show("You are attacked by a " + monster.Name + "!", true);
  UpdateStats(hero, monster);

  var monsterFirst = false;
  var monsterRun = false;
  var monSum = monster.HPMax + monster.attack + monster.defense;
  var heroSum = hero.HPMax + hero.attack + monster.defense;
  var statDifference = monSum - heroSum;
  var chance = Math.floor(Math.random() * 500)
  
  if (chance > 0 && chance < statDifference) 
  {
    monsterFirst = true;
    Show("The " + monster.Name + " surprises you and attacks first!");
    MonsterAttack();
  }
  
  if (chance < 0 && Math.abs(chance) < statDifference) 
  {
    MonsterAttack(true);
  }

}


/*
function DoAction(action) 
{
  UpdateStats();
  
  document.getElementById("battleLog").innerHTML = display;
}
*/

  function Fight()
  {
    display = "You are battling a";
    var first = monster.Name.substring(0, 1).toLowerCase();
    if (first === "a" || first === "e" || first === "i" || first === "o" || first === "u") 
    {
      display += "n";
    }
    display += " " + monster.Name + ".";

    Show(display , true);
    DoAttack(hero, monster);
    
    if (monster.HP <= 0) 
    {
      EndBattle(true);
    }
    else
    {
      MonsterAttack();
      UpdateStats();
    }
  } 
  
  
  function Herb()
  {
    if (hero.herbs >= 1) 
    {
      var herbHeal = UseHerb(hero);
      Show("The herb healed you " + herbHeal + " points. Your HP is now " + hero.HP + ".");
      MonsterAttack();
      UpdateStats();
    } 
    else 
    {
      Show("You don't have any herbs.");
    }
  }


  function Run()
  {
  UpdateStats();
  Show("You try to run away from the " + monster.Name + ".");
    if (RunFromBattle(hero)) 
    {
      Show("You successfully escaped that battle!");
      EndBattle(false);
    } 
    else 
    {
      Show("You were not able to run away.");
      MonsterAttack();
      UpdateStats();
    }
  }
  
  function Quit()
  {
  UpdateStats();
  GameOver();
  }
  
  



function MonsterAttack(monsterRun = false) 
{
  //Monster fights back
  
    if (monster.HP < (monster.HPMax / 2) && 
       (Math.random() * 1000) < monster.herbs) 
    {
      UseHerb(monster);
      Show("The " + monster.Name + " used an herb to heal.");
    } 
    else if (monsterRun && (Math.random() * 500 < 10)) 
    {
      Show("The monster ran away!");
      EndBattle(false);
    } 
    else 
    {
      DoAttack(monster, hero);
      
      if (hero.HP <= 0) //oh no, you died
      {
         GameOver();
      }
    }
}

function NextBattle()
{
  document.getElementById("nextBattle").style.display = "none";
  document.getElementById("buyHerb").style.display = "none";
  document.getElementById("actions").style.display = "block";
  BeginBattle(hero);
}

function EndBattle(reward) 
  {
    
    if (reward) 
    {
      AwardPrizes(hero, monster);
    }
    
    UpdateStats();
    //BuyHerb(hero);
    
  document.getElementById("nextBattle").style.display = "none";
  document.getElementById("buyHerb").style.display = "none";
  document.getElementById("actions").style.display = "none";
  document.getElementById("okButton").style.display = "block";
    
  }

function GameOver() 
{
    if (hero.HP <= 0) 
    {
      display = "<p>You have died.</p>";
    } 
    else 
    {
      display = "<p>You live to battle another day.</p>";
    }

    display += "<p>" + hero.Name + "'s final stats:<br />";
    display += "HP: " + hero.HP + "/" + hero.HPMax;
    display += "<br />Unused Herbs: " + hero.herbs;

    display += "<br />Total experience points: " + hero.exp;
    display += "<br />Total gold accumulated: " + hero.gold;
    display += "<br />Attack power: " + hero.attack;
    display += "<br />Defense power: " + hero.defense;

    display += "<br />Number of battles fought: " + hero.monstersFought.length;
    display += "<br />Monsters battled:<br />";
    display += hero.monstersFought;
    display += "</p>";
    
    display += "<p>Be sure to play again!</p>";

    document.getElementById("startUp").style.display = "block";
    document.getElementById("battle").style.display = "none";

    document.getElementById("intro").innerHTML = display;

    //Show(display);

    //End of Game
  
}


function AwardPrizes(h, m) 
{
  var disp = "The " + m.Name + " was defeated.\r\r";
  disp += h.Name + " earns " + m.exp + " experience points and " + m.gold + " gold.\r\r";
  h.exp += m.exp;
  h.gold += m.gold;

  //see if you find an herb from the monster
  if ((Math.random() * 100) < m.herbs) 
  {
    disp += h.Name + " found an herb!";
    h.herbs++;
  }

  Show(disp, true);

  //see if you get stronger
  LevelUp(h);


  //let's have after-battle recovery
  var healUp = Math.floor(Math.random() * (hero.HPMax - hero.HP));
  hero.HP += healUp;
  hero.HP = Math.min(hero.HP, hero.HPMax);

  if (healUp > 1) 
  {
    Show("You get some rest after the battle and recover " + healUp + " hit points.");
  } 
  else if (healUp === 1) 
  {
    Show("You get a little rest after the battle and recover " + healUp + " hit point.");
  }
}


function DoAttack(attacker, defender) 
{
  var display = "";

  var attName = attacker.Name;
  var defName = defender.Name;

  if (attacker.Name === monster.Name) 
  {
    attName = "The " + attacker.Name;
  } 
  else 
  {
    defName = "the " + defender.Name;
  }

  display += attName + " attacks! \r";

  var range = 5;
  var att = attacker.attack + Math.floor(Math.random() * range) - (Math.floor(range / 2));
  var def = defender.defense + Math.floor(Math.random() * range) - (Math.floor(range / 2));

  var damage = Math.max(0, (att - def))

  if (damage === 0) 
  {
    damage = Math.floor(Math.random() * 2);
  }

  if (damage > 0) 
  {
    display += attName + " does " + damage + " point";

    if (damage > 1) 
    {
      display += "s";
    }

    display += " of damage to " + defName + "!";
    defender.HP -= damage;
    defender.HP = Math.max(0, defender.HP);
  } 
  else 
  {
    display += attName + " misses! No damage is scored."
  }
  Show(display);
}

function BuyHerb(h) 
{
  document.getElementById("actions").style.display = "none";
  document.getElementById("nextBattle").style.display = "none";
  document.getElementById("buyHerb").style.display = "block";
  document.getElementById("okButton").style.display = "none";
  
  if (hero.gold < herbCost || h === "no") 
  {
    document.getElementById("buyHerb").style.display = "none";
    document.getElementById("nextBattle").style.display = "block";
    //Show("", true);
    return;
  }
  
  display = "An herb costs " + herbCost + " gold. \r";
  display += "Do you want to buy an herb?";
  
  
  if (h === "yes") 
  {
    hero.gold -= herbCost;
    hero.herbs++;
    UpdateStats();
    display = "You purchased an herb. ";
  
    if (hero.gold < herbCost)
   {
      document.getElementById("buyHerb").style.display = "none";
      document.getElementById("nextBattle").style.display = "block";
    }
    else
    {
      display += "\r\rWould you like to purchase another herb for ";
      display += herbCost + " gold pieces?";
    }
  }
  Show(display, true);
}


function UseHerb(h) 
{
  var range = 5;
  var herbHeal = Math.floor(Math.random() * range) - (Math.floor(range / 2));
  herbHeal = herbPower + herbHeal;

  h.HP += herbHeal;
  h.HP = Math.min(h.HP, h.HPMax);
  h.herbs--;

  return herbHeal;
}


function RunFromBattle(h) {
  var runChecker = Math.floor(Math.random() * 100);
  return (runChecker < h.runRate);
}


function Show(text, clr = false) 
{
  console.log(text)
  
  if (clr)
  {
    document.getElementById("battleLog").innerHTML = "";
  }
  document.getElementById("battleLog").innerHTML += text + "\r\r";

}


function GetHero() {
  //var n = prompt("What is your character's name?", "Dariak");

  var n = document.getElementById("heroName").value;

  var h = {
    Name: n,
    HPMax: 20,
    herbs: 2,
    gold: 0,
    exp: 0,
    attack: 5,
    defense: 3,
    runRate: 25,
    level: 1,
    sightScope: false,
    monstersFought: []
  };

  h.HP = h.HPMax;

  return h;
}


//Decide if the hero levels up!
function LevelUp(h) {
  var text = "";
  var levelChanged = true;
  var HPInc = 0;
  var attInc = 0;
  var defInc = 0;
  var totalAttInc = 0;
  var totalDefInc = 0;

  while (levelChanged) {
    levelChanged = false; //we want the loop to run at least once (instead of using a do/while loop)

    switch (h.level) {
      case 1:
        {
          if (h.exp > 8) {
            levelChanged = true;
            HPInc = 2;
            attInc = 1;
            defInc = 1;
          }
          break;
        }
      case 2:
        {
          if (h.exp > 22) {
            levelChanged = true;
            HPInc = 4;
            attInc = 1;
            defInc = 1;
            h.sightScope = true;
          }
          break;
        }
      case 3:
        {
          if (h.exp > 40) {
            levelChanged = true;
            HPInc = 5;
            attInc = 2;
            defInc = 1;
          }
          break;
        }
      case 4:
        {
          if (h.exp > 62) {
            levelChanged = true;
            HPInc = 10;
            attInc = 1;
            defInc = 2;
          }
          break;
        }
      default: //keep growing if specific levels are not set
        {
          if (h.exp > (25 * h.level)) {
            levelChanged = true;
            HPInc = h.level * 2;
            attInc = (h.level + 1) % 4;
            defInc = (h.level + 0) % 4;
            break;
          }
        }

    }

    //totalAttInc += attInc;
    //totalDefInc += defInc;

    if (levelChanged) {
      h.level++;
      h.attack += attInc;
      h.defense += defInc;
      h.HPMax += HPInc;
      h.HP += HPInc; //give some HP for levelling up
      text = "\rCongratulations! You leveled up!\r\r";
      text += "You're now level " + h.level + ".\r\r";
      text += "Your hit points increased by " + HPInc + " and is now " + h.HPMax + ".\r";
      text += "Your attack power increased by " + attInc + " and is now " + h.attack + ".\r";
      text += "Your defense power increased by " + defInc + " and is now " + h.defense + ".\r";
      if (h.sightScope) {
        text += "\r\rYou've learned a lot about your foes. You can now see their remaining HP.";
      }
      Show(text);
    }
  }
  return;
}

function UpdateStats() 
{
  var heroString = "";

  heroString += "<h2>" + hero.Name + "</h2>";
  heroString += "<p>HP: " + hero.HP + "/" + hero.HPMax;
  heroString += "<br />Herbs: " + hero.herbs;
  heroString += "<br />Level: " + hero.level;
  heroString += "<br />Attack Power: " + hero.attack;
  heroString += "<br />Defense Power: " + hero.defense;
  heroString += "<br />Experience: " + hero.exp;
  heroString += "<br />Gold: " + hero.gold;
  heroString += "<br />Monsters Fought: " + hero.monstersFought.length;
  heroString += "</p>";

  var monsterString = "";
  var monName = monster.Name.substring(0, 1).toUpperCase() + monster.Name.substring(1).toLowerCase();

  monsterString += "<h2>" + monName + "</h2>";

  if (hero.sightScope) {
    monsterString += "<p>HP: " + monster.HP + "/" + monster.HPMax;
    monsterString += "<br />Attack Power: " + monster.attack;
    monsterString += "<br />Defense Power: " + monster.defense;
    monsterString += "<br />Rewards:"
    monsterString += "<br />Experience: " + monster.exp;
    monsterString += "<br />Gold: " + monster.gold;
    monsterString += "<br />Herb Drop Chance: " + monster.herbs + "%";
  }

  var monsterCounter = 0;
  for (var i = 0; i < hero.monstersFought.length; i++) {
    if (hero.monstersFought[i] === monster.Name) {
      monsterCounter++;
    }
  }
  monsterString += "<br /># Times Against This Foe: " + monsterCounter;
  monsterString += "</p>";

  document.getElementById("heroStats").innerHTML = heroString;
  document.getElementById("heroStats").style.backgroundColor = GetHPColor(hero);
  document.getElementById("monsterStats").innerHTML = monsterString;
  document.getElementById("monsterStats").style.backgroundColor = GetHPColor(monster);
}

function GetHPColor(creature)
{
  var color = "white";
  
  if (creature.HP === 0)
  {
    color = "gray";
  }
  else if (creature.HP <= Math.ceil(creature.HPMax * .1))
  {
    color = "red";
  }
  else if (creature.HP <= Math.ceil(creature.HPMax * .3))
  {
    color = "yellow";
  }
  
  return color;
}

//This creates a copy of the monster so the original monster data is not overwritten
function CloneMonster(m) {
  var cm = {
    Name: m.Name,
    HPMax: m.HPMax,
    HP: m.HPMax,
    herbs: m.herbs,
    gold: m.gold,
    exp: m.exp,
    attack: m.attack,
    defense: m.defense
  }
  return cm;
}

function PlusMinusHalf(number) {
  var newNumber = 0;
  newNumber = Math.floor(Math.random() * number) - (number / 2);
  newNumber = number + newNumber;
  newNumber = Math.floor(newNumber);
  return newNumber;
}

function MakeRandomMonster(h) {
  var monHP = PlusMinusHalf(h.HPMax);
  var monHerbs = h.level * 5;
  var monAttack = PlusMinusHalf(h.attack);
  var monDefense = PlusMinusHalf(h.defense);
  var monExp = Math.ceil(Math.random() * h.level + PlusMinusHalf(h.level)) + 1;
  var monGold = Math.ceil(Math.random() * h.level + PlusMinusHalf(h.level)) + 1;
  var monName = GenerateRandomName();

  var mon = {
    Name: monName,
    HPMax: monHP,
    HP: monHP,
    herbs: monHerbs,
    gold: monGold,
    exp: monExp,
    attack: monAttack,
    defense: monDefense
  };
  return mon;
}

function GenerateRandomName() {
  var makeName = "";
  var nameSnippets = ["dra", "con", "us", "mon", "ty", "mer", "lin",
    "ti", "ger", "max", "bus", "ter", "bud", "dy", "med", "usa"
  ];

  var rand = Math.ceil(Math.random() * 3) + 1;
  var rand2;

  for (var i = 0; i < rand; i++) {
    rand2 = Math.floor(Math.random() * nameSnippets.length);
    makeName += nameSnippets[rand2];
  }
  return makeName;
}

//This sets the list of monsters with all their stats.
function GetMonsterList() {
  var m = [];

  var mon = ////to add a monster, copy from this line down...
    {
      Name: "squirret", //monster's name
      HPMax: 5, //monster's Max and starting hit points
      herbs: 10, //likelihood of monster using an herb 
      gold: 2, //amount of gold won for defeating monster
      exp: 2, //amount of experience for defeating monster
      attack: 2, //monster's attack strength
      defense: 2 //monster's defense rate
    };
  m.push(mon); ////...to here to add a monster

  mon = {
    Name: "swallomer",
    HPMax: 10,
    herbs: 25,
    gold: 3,
    exp: 3,
    attack: 4,
    defense: 3
  };
  m.push(mon);

  mon = {
    Name: "gleese",
    HPMax: 12,
    herbs: 30,
    gold: 6,
    exp: 6,
    attack: 3,
    defense: 6
  };
  m.push(mon);

  mon = {
    Name: "rodia",
    HPMax: 14,
    herbs: 20,
    gold: 8,
    exp: 7,
    attack: 5,
    defense: 8
  };
  m.push(mon);

  mon = {
    Name: "eaglon",
    HPMax: 18,
    herbs: 50,
    gold: 10,
    exp: 8,
    attack: 5,
    defense: 8
  };
  m.push(mon);

  mon = {
    Name: "lupino",
    HPMax: 25,
    herbs: 50,
    gold: 15,
    exp: 11,
    attack: 8,
    defense: 9
  };
  m.push(mon);

  mon = {
    Name: "tigroar",
    HPMax: 28,
    herbs: 60,
    gold: 18,
    exp: 10,
    attack: 9,
    defense: 10
  };
  m.push(mon);


  //after all the monsters have been added, send them onward
  return m;
}