Battle System Tutorial by Flik

From Spherical
Revision as of 00:44, 8 June 2013 by Apollolux (talk | contribs) (created from http://web.archive.org/web/20120911000900/http://spheredev.org/wiki/Battle_System_Tutorial_by_Flik)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

How on Earth do you create a battle system in Sphere? Simple, you script it. (Okay, so it can be a bit difficult.)

Introduction

We're going to break the battle system down into separate bits so that it's easier to create.

Since most RPG battle systems are menu-driven, you may want to learn how to use a menu object. (Creating a menu using flik_menu.js)

We'll be using objects too, if you want to read up on that. (Simple OOP in JavaScript)

You don't need to read those to understand this, though.

Combatants: things that can fight

Okay, the people or things or whatever that are fighting? We'll call them 'combatants'.

We'll have a list of combatants in an array.

var combatants = new Array();

So whenever a combatant appears, they go into that array. A player is the same as a monster, okay? :)

function Combatant(name) {
  this.name = name;
  this.x = 50;
  this.y = 50;
 
  this.min_hp = 0;  // don't ask why
  this.hp = 10000;
  this.max_hp = 50;
  this.str = 10;
 
  this.type = "monster";
}
 
Combatant.prototype.clone = function() {
  var obj = new Combatant();
  for (var i in this)
    obj[i] = this[i];
  return obj;
}

Sidetrack: Why clone the object?

We clone the object so that we don't accidentally change the original.


Putting our combatants in a battle

So this creates a Combatant object when we do:

var freddy = new Combatant("Freddy");  // create the object
    freddy.type = "player";            // set the type to "player"
    freddy.x = 200;
 
var blob = new Combatant("Blob");
    blob.hp = 5000;
 
combatants.push(freddy.clone());  // freddy has joined the party
combatants.push(blob.clone());    // uh oh, we have a monster to fight

Sidetrack: Why the huge hp values?

Because there's no delaying code between attacks,so it goes really fast.


Figuring out if the battle is over

Now, how do we check to see if we need to fight someone? Well, we loop through the combatants array and check the type of each combatant.

function GetTotalMonsters() {
  var total_monsters = 0;
  for (var i = 0; i < combatants.length; ++i)
    if (combatants[i].type == "monster")
      total_monsters += 1;
  return total_monsters;
}
 
function GetTotalPlayers() {
  var total_players = 0;
  for (var i = 0; i < combatants.length; ++i)
    if (combatants[i].type == "player")
      total_players += 1;
  return total_players;
}

Drawing the combatants

Okay, so let's add some rendering code to the Combatant object.

Combatant.prototype.render = function() {
  var font = GetSystemFont();
  font.drawText(this.x, this.y, this.name);
  font.drawText(this.x, this.y - 20, this.hp);
}

Figuring out turns

We also need some sort of system for finding out who's turn it is. Enter... the turn list:

var turn_list = new Array();

AI

Also, now let's add some AI to them:

function GetAliveMonstersArray() {
  var total_alive = new Array();
  for (var i = 0; i < combatants.length; ++i)
    if (combatants[i].type == "monster" && combatants[i].hp > 0)
      total_alive.push(i);
  return total_alive;
}
 
function GetAlivePlayersArray() {
  var total_alive = new Array();
  for (var i = 0; i < combatants.length; ++i)
    if (combatants[i].type == "player" && combatants[i].hp > 0)
      total_alive.push(i);
  return total_alive;
}
 
function Attack(from_who, on_who) {
  combatants[on_who].hp -= combatants[from_who].str;
}
 
Combatant.prototype.AI = function() {
  if (this.type == "player") {
    // create menus here
    var attack_selection = GetAliveMonstersArray()[0];  // we attack 1st monster
    Attack(turn_list[0], attack_selection);
  } else if (this.type == "monster") {
    // just normal attacking here
    var attack_selection = GetAlivePlayersArray()[0];  // they attack 1st player
    Attack(turn_list[0], attack_selection);
  }
}

Drawing the battle

Finally, we do:

function RenderBattle() {
  for (var i = 0; i < combatants.length; ++i)
    combatants[i].render();
}

Bringing it all together

function Battle() {
  var run_away = false;
 
  while (GetAlivePlayersArray().length > 0
      && GetAliveMonstersArray().length > 0
      && !run_away) {
 
    if (IsKeyPressed(KEY_R))
      run_away = true;
 
    // deal turns
    if (turn_list.length == 0) {
      for (var i = 0; i < combatants.length; ++i)
        turn_list.push(i);
    }
 
    // process the current turn
    if (turn_list.length > 0) {
      combatants[turn_list[0]].AI();
      turn_list.shift();
    }
 
    RenderBattle();
    FlipScreen();
  }
}

Press 'R' to run from battle.

Trying it out

Let's say that all of that is in a file called freddyvsblob.js. In your main/startup script:

RequireScript("freddyvsblob.js");
 
function game() {
  SetFrameRate(60);
  Battle();
}

If you ever need to do it again, just do this:

combatants.push(freddy.clone());
combatants.push(blob.clone());
Battle();

The whole script

You can find all the battle system code at Flikky's site: http://sphere.sourceforge.net/flik/files/freddyvsblob.js .

By Flikky, updated for the wiki by Tunginobi.