JUMP INTO JAVASCRIPT PART 13

In which we end on an absolute unit.

  • Trihan
  • 06/16/2021 10:59 PM
  • 2447 views
Hello sports fans! It's that time again. Let's get right on with it and


Jump into Javascript

Game_Actor last week was a huge class to get through, so I didn't have room in the episode to do Game_Enemy too. Now, we rectify that.

Game_Enemy
Having all your actors would be a bit boring if they had nothing to fight. Enter Game_Enemy, the class that handles all your beasties and bosses, from the lowliest slime to the Demon King at the end of the game. It inherits, as did Game_Actor, from Game_Battler.

Game_Enemy.prototype.initialize = function(enemyId, x, y) {
    Game_Battler.prototype.initialize.call(this);
    this.setup(enemyId, x, y);
};


The initialize method takes three parameters: enemyId, the ID of the enemy from the database; x, the battler's x position on the screen; and y, the battler's y position. First we all Game_Battler's prototype initialize function, and then we call the setup function passing in all 3 parameters.

Game_Enemy.prototype.initMembers = function() {
    Game_Battler.prototype.initMembers.call(this);
    this._enemyId = 0;
    this._letter = '';
    this._plural = false;
    this._screenX = 0;
    this._screenY = 0;
};


We're also overwriting initMembers to add a few extra enemy-specific properties. So first, as is tradition, we call the parent function. Then we're setting _enemyId (tracks the ID of the enemy) to 0, _letter (used when there are multiples of the same enemy in battle) to '', _plural (also used for multiples) to false, _screenX (x coordinate on screen) to 0 and _screenY (y coordinate on screen) to 0.

Game_Enemy.prototype.setup = function(enemyId, x, y) {
    this._enemyId = enemyId;
    this._screenX = x;
    this._screenY = y;
    this.recoverAll();
};


setup is a function that just takes the parameters from initialize and stuffs them into internal variables instead. So we're setting _enemyId to enemyId, _screenX to x, _screenY to y, and then calling recoverAll so that our enemy starts out at full capacity.

Game_Enemy.prototype.isEnemy = function() {
    return true;
};


Remember when in Game_Actor, isEnemy returned false because actors weren't enemies? Well now we're having it return true, because...well, enemies *are* enemies.

Game_Enemy.prototype.friendsUnit = function() {
    return $gameTroop;
};


Consequently, in friendsUnit we now return $gameTroop rather than $gameParty...

Game_Enemy.prototype.opponentsUnit = function() {
    return $gameParty;
};


...and the opposite for opponentsUnit, where we now return $gameParty instead of $gameTroop.

Game_Enemy.prototype.index = function() {
    return $gameTroop.members().indexOf(this);
};


This function gets us the *index* of the current enemy instance, which is not the same thing as _enemyId. The ID is the kind of monster, that is to say the entry from the database, whereas the index is the position of the enemy in the battle lineup, from 0 to 7 (since you can have up to 8 enemies in a troop). And to do that, we quite simply return the result of calling indexOf on $gameTroop's members array, passing in "this". This will search the array for the Game_Enemy matching the current one, and return the index in the array where it was found, or -1 if it wasn't there.

Game_Enemy.prototype.isBattleMember = function() {
    return this.index() >= 0;
};


This function determines whether the enemy is a battle member or not. Here, we return whether index() returns a value greater than or equal to 0.

Game_Enemy.prototype.enemyId = function() {
    return this._enemyId;
};


This function is just a wrapper for the internal _enemyId variable, so obviously calling it will return the ID of the enemy.

Game_Enemy.prototype.enemy = function() {
    return $dataEnemies[this._enemyId];
};


This one, on the other hand, gets the actual data object for the enemy. We return the element of $dataEnemies with an index matching _enemyId.

Game_Enemy.prototype.traitObjects = function() {
    return Game_Battler.prototype.traitObjects.call(this).concat(this.enemy());
};


We also need to overwrite traitObjects, since enemies can have traits on them too. We return Game_Battler's prototype function concatenated with the result of the enemy() function, which will add the enemy object to the array of trait-providing objects.

Game_Enemy.prototype.paramBase = function(paramId) {
    return this.enemy().params[paramId];
};


This function gets the base parameter value for a given parameter, represented by paramId. We return the element of enemy()'s params at index paramId. This maps directly to the parameter settings in the Enemies tab, so params will get the "Attack" value.

Game_Enemy.prototype.exp = function() {
    return this.enemy().exp;
};


This function gets the experience that the party will receive for defeating this enemy, and here we return the "exp" property from enemy(). This maps directly to the EXP setting in enemy rewards.

Game_Enemy.prototype.gold = function() {
    return this.enemy().gold;
};


This one is just the same but for gold, and so obviously it maps to the "Gold" setting.

Game_Enemy.prototype.makeDropItems = function() {
    return this.enemy().dropItems.reduce(function(r, di) {
        if (di.kind > 0 && Math.random() * di.denominator < this.dropItemRate()) {
            return r.concat(this.itemObject(di.kind, di.dataId));
        } else {
            return r;
        }
    }.bind(this), []);
};


This is a fun one; here we have the source of all your game's loot. This is the function that returns a list of items the enemy drops after battle.

So for the return value, we're looking at enemy()'s dropItems, which is an array of objects describing the drops, each of which has three properties: kind, dataId, and denominator. We run a reduce function on that array, which will execute the function we pass to it against each element (di) and add the result of the function to an accumulator (r).

In the function we're passing here, we check whether the object's kind is greater than 0 AND a random number between 0 and 1 multiplied by the object's denominator is less than dropItemRate(); if so, we concatenate r with the result of calling the itemObject function, passing in the object's kind and dataId. Otherwise, we return r as no changes are needed to it. The function is bound to "this" so that inner this keywords still refer to the enemy and not the function, and initialise r as an empty array.

Even with as much code as we've been over up until now this might look a little intimidating to some, so I'll go over it in more detail once we get to itemObject.

Game_Enemy.prototype.dropItemRate = function() {
    return $gameParty.hasDropItemDouble() ? 2 : 1;
};


Here's another little piece of the puzzle. The dropItemRate function we called above returns either 2 if $gameParty's hasDropItemDouble() function returns true, or 1 otherwise. In other words, 1x drop rate unless the party has something equipped with the "drop rate double" trait on it, which which case it's 2x.

Game_Enemy.prototype.itemObject = function(kind, dataId) {
    if (kind === 1) {
        return $dataItems[dataId];
    } else if (kind === 2) {
        return $dataWeapons[dataId];
    } else if (kind === 3) {
        return $dataArmors[dataId];
    } else {
        return null;
    }
};


And here's the last piece. itemObject takes parameters kind (the sort of item it is) and dataId (the database ID of the item). All we're doing here is returning the appropriate $data class for the item type. So if kind is 1, we use $dataItems. If kind is 2, we use $dataWeapons. If kind is 3, we use $dataArmors. If for whatever reason it's any other value, we do not have an item, so we just return null.

Okay, so back to makeDropItems. Let's say you have an enemy which drops Potion at 1/3 probability, and Leather Armour at 1/10 without any double drop rate equipment. The dropItems array will be something like .

In the first loop, r is an empty array, and di is {kind: 1, dataId: 7, denominator: 3}. di.kind is greater than 0, so that part of the condition is met. Let's say the random number generated is somewhere around 0.2589385; that's multiplied by denominator, giving us 0.7768155. That is less than the dropItemRate() of 1, so we successfully snag us a potion. itemObject(di.kind, di.dataId) then gives us $dataItems, and this is concatenated into the r array, so r is now [$dataItems].

In the next loop, r is [$dataItems], and di is {kind: 3, dataId: 9, denominator: 10}. di.kind is greater than 0, so far so good. Now let's say the random number generated is about 0.7982743; multiply that by denominator and we get 7.982743. That is not less than 1, so we just return r, which is [$dataItems].

That final r array will be the return value, so when the rest of the code looks at what items dropped, that will tell us that we got a Potion but not the Leather Armour.

Game_Enemy.prototype.isSpriteVisible = function() {
    return true;
};


This function determines whether the enemy sprite is visible or not. We return true, because enemies will be visible regardless of whether we're using front-view or side-view battles.

Game_Enemy.prototype.screenX = function() {
    return this._screenX;
};

Game_Enemy.prototype.screenY = function() {
    return this._screenY;
};


Nothing new or exciting here, just a pair of wrapper functions to access the internal _screenX and _screenY properties.

Game_Enemy.prototype.battlerName = function() {
    return this.enemy().battlerName;
};

Game_Enemy.prototype.battlerHue = function() {
    return this.enemy().battlerHue;
};


These two are also wrappers, though rather than internal properties we're returning the battlerName or battlerHue property of the enemy() object.

Game_Enemy.prototype.originalName = function() {
    return this.enemy().name;
};


originalName will give us the name property from enemy().

Game_Enemy.prototype.name = function() {
    return this.originalName() + (this._plural ? this._letter : '');
};


name, on the other hand, returns originalName plus the _letter property if _plural is true, or a blank string otherwise. This is what gives us things like 'Goblin A' or 'Bat C'.

Game_Enemy.prototype.isLetterEmpty = function() {
    return this._letter === '';
};


This function determines whether the letter is empty or not. Here we just return whether _letter is equal to a blank string.

Game_Enemy.prototype.setLetter = function(letter) {
    this._letter = letter;
};


This is a setter for letter (lol) and takes letter as a parameter. And quite simply, we set _letter to the passed-in letter value.

Game_Enemy.prototype.setPlural = function(plural) {
    this._plural = plural;
};


This one's exactly the same, but for plural.

Game_Enemy.prototype.performActionStart = function(action) {
    Game_Battler.prototype.performActionStart.call(this, action);
    this.requestEffect('whiten');
};


Here we're overwriting the performActionStart function, which takes action as a parameter. We call the parent function, and then requestEffect, passing in 'whiten'. This will tell the battler's sprite that we want a white pulse when the enemy starts taking an action.

Game_Enemy.prototype.performAction = function(action) {
    Game_Battler.prototype.performAction.call(this, action);
};


Here we overwrite performAction to...do what it would have done anyway.

Game_Enemy.prototype.performActionEnd = function() {
    Game_Battler.prototype.performActionEnd.call(this);
};


...and the same for performActionEnd. I genuinely can't see any reason for the developer doing this. I may just lack some nuanced programming knowledge, but if you write a function where all it does is call the function of the object's parent, you don't need to overwrite it because it already has access to that function. As far as I know, neither of these needed to be written and I have no idea why they were.

Game_Enemy.prototype.performDamage = function() {
    Game_Battler.prototype.performDamage.call(this);
    SoundManager.playEnemyDamage();
    this.requestEffect('blink');
};


This one on the other hand does do something useful. After calling the parent performDamage function, we call playEnemyDamage() from SoundManager and then requestEffect passing in 'blink', which blinks the enemy into transparency for a moment when they take damage.

Game_Enemy.prototype.performCollapse = function() {
    Game_Battler.prototype.performCollapse.call(this);
    switch (this.collapseType()) {
    case 0:
        this.requestEffect('collapse');
        SoundManager.playEnemyCollapse();
        break;
    case 1:
        this.requestEffect('bossCollapse');
        SoundManager.playBossCollapse1();
        break;
    case 2:
        this.requestEffect('instantCollapse');
        break;
    }
};


This function handles the "collapse" effect, which is a fancy way of saying the enemy died. After calling the parent function, we run a switch statement against collapseType(): if it's 0, we request the 'collapse' effect, which is your basic "turn sprite red and fade it out" and call playEnemyCollapse() from SoundManager. If it's 1, we request the 'bossCollapse' effect, the one where the fade takes longer and the sprite "shudders", and call playBossCollapse1(). If it's 2, we request the 'instantCollapse' effect, where they just disappear instantly.

Game_Enemy.prototype.transform = function(enemyId) {
    var name = this.originalName();
    this._enemyId = enemyId;
    if (this.originalName() !== name) {
        this._letter = '';
        this._plural = false;
    }
    this.refresh();
    if (this.numActions() > 0) {
        this.makeActions();
    }
};


This function handles the enemy transforming into a different one, and takes enemyId as a parameter. First we set a temporary variable called name to the enemy's originalName(), and the internal _enemyId property to enemyId. If originalName() is not equal to name (which it won't be if the enemy is now different), we set _letter to a blank string and _plural to false. Then we call refresh() and if numActions() is greater than 0 we call makeActions(), as the new enemy probably won't have the same action set as the old one.

Game_Enemy.prototype.meetsCondition = function(action) {
    var param1 = action.conditionParam1;
    var param2 = action.conditionParam2;
    switch (action.conditionType) {
    case 1:
        return this.meetsTurnCondition(param1, param2);
    case 2:
        return this.meetsHpCondition(param1, param2);
    case 3:
        return this.meetsMpCondition(param1, param2);
    case 4:
        return this.meetsStateCondition(param1);
    case 5:
        return this.meetsPartyLevelCondition(param1);
    case 6:
        return this.meetsSwitchCondition(param1);
    default:
        return true;
    }
};


This function determines whether an action meets its defined conditions, taking action as a parameter. We set temp variables param1 to the action's conditionParam1, and param2 to conditionParam2. Then we run a switch statement against the action's conditionType: if it's 1, we return meetsTurnCondition passing in param1 and param 2; if it's 2, we return meetsHpCondition passing in param1 and param2; if it's 3, we return meetsMpCondition passing in param1 and param2; if it's 4, we return meetsStateCondition passing in param1; if it's 5, we return meetsPartyLevelCondition passing in param1; if it's 6, we return meetsSwitchCondition passing in param1; otherwise we return true.

Game_Enemy.prototype.meetsTurnCondition = function(param1, param2) {
    var n = $gameTroop.turnCount();
    if (param2 === 0) {
        return n === param1;
    } else {
        return n > 0 && n >= param1 && n % param2 === param1 % param2;
    }
};


This function determines whether the turn condition is met, taking param1 and param2 as parameters. These map to the "+" and "* X" inputs in the enemy's action editor.

We set variable n to the turnCount() value from $gameTroop, which gets the current turn number. If param2 is 0, we return whether n is equal to param1. Otherwise, we return whether n is greater than 0 AND n is greater than or equal to param1 AND whether n modulus param2 is equal to param1 modulus param2.

Example time! Let's say we've added an action which the enemy starts using on turn 5 and then again every 3 turns after that. param1 will be 5 and param2 will be 3.

If we're currently on turn 4, n will be 4. param2 is greater than 0, so we hit the else. n is greater than 0, but it's not greater than or equal to param1 so the condition fails.

If we're on turn 7, n will be 7. param2 is greater than 0, so we hit the else. n is greater than 0 and greater than or equal to param1, but 7 % 3 is not equal to 5 % 3 so the condition fails.

If we're on turn 8, n will be 8. param2 is greater than 0, so we hit the else. n is greater than 0, and greater than or equal to param1, and 8 % 3 is equal to 5 % 3 (2) so the condition is met and the action can be used this turn.

Game_Enemy.prototype.meetsHpCondition = function(param1, param2) {
    return this.hpRate() >= param1 && this.hpRate() <= param2;
};


This functions determines whether an HP condition is met, taking param1 and param2 as parameters. These map to the lower and upper percentage inputs.

We return true if hpRate() is greater than or equal to param1 AND less than or equal to param2. So if param1 is 5 and param2 is 20, the enemy's HP needs to be between 5 and 20% to use this action.

Game_Enemy.prototype.meetsMpCondition = function(param1, param2) {
    return this.mpRate() >= param1 && this.mpRate() <= param2;
};


This one's exactly the same but for mpRate() instead.

Game_Enemy.prototype.meetsStateCondition = function(param) {
    return this.isStateAffected(param);
};


This function determines whether a state condition is met. It takes param as a parameter, which maps to the ID from the state selector in the dialog. Here we return the result of isStateAffected passing in param.

Game_Enemy.prototype.meetsPartyLevelCondition = function(param) {
    return $gameParty.highestLevel() >= param;
};


This function determines whether a party level condition is met. It takes param as a parameter, which maps to the X or above input. Here we return whether $gameParty's highestLevel() function returns a value greater than or equal to param.

Game_Enemy.prototype.meetsSwitchCondition = function(param) {
    return $gameSwitches.value(param);
};


This function determines whether a switch condition is met. It takes param as a parameter, which maps to the ID of the switch input. This one is pretty simply as well, as all we're doing is returning the result of calling $gameSwitches' value function with param passed in, which will give us the true/false setting of the given switch ID.

Game_Enemy.prototype.isActionValid = function(action) {
    return this.meetsCondition(action) && this.canUse($dataSkills[action.skillId]);
};


This function determines whether a given action is valid for use, taking action as a parameter. We return true if meetsCondition returns true with action passed to it AND if canUse returns true if we pass in the element of $dataSkills at an index of the action's skillId.

Game_Enemy.prototype.selectAction = function(actionList, ratingZero) {
    var sum = actionList.reduce(function(r, a) {
        return r + a.rating - ratingZero;
    }, 0);
    if (sum > 0) {
        var value = Math.randomInt(sum);
        for (var i = 0; i < actionList.length; i++) {
            var action = actionList[i];
            value -= action.rating - ratingZero;
            if (value < 0) {
                return action;
            }
        }
    } else {
        return null;
    }
};


This function is the one that actually selects an action from the list of potential ones, taking actionList and ratingZero as parameters. actionList should be fairly self-explanatory; ratingZero is effectively "the rating considered to be zero based on the ratings of the actions in the list" which I'll explain further shortly.

The first thing we do is reduce actionList, starting at 0, and store the result in variable "sum". The reduce function uses accumulator "r" and current item "a", with the function passed returning r plus a's rating minus ratingZero. Then if sum is greater than 0, we set value to a random integer from 0 to sum (exclusive) and run a for loop with iteration variable "i", starting at 0 and incrementing as long as i is less then actionList's length. We set variable "action" to the i element of actionList, and subtract action's rating minus ratingZero from value. If value is less than 0, we return action. If sum is not greater than 0, we return null.

Game_Enemy.prototype.selectAllActions = function(actionList) {
    var ratingMax = Math.max.apply(null, actionList.map(function(a) {
        return a.rating;
    }));
    var ratingZero = ratingMax - 3;
    actionList = actionList.filter(function(a) {
        return a.rating > ratingZero;
    });
    for (var i = 0; i < this.numActions(); i++) {
        this.action(i).setEnemyAction(this.selectAction(actionList, ratingZero));
    }
};


This function selects all actions an enemy is going to use on their turn, taking actionList as a parameter. First we set ratingMax to the maximum value from mapping actionList to return an array of each action's rating (giving us the max rating of all actions in the list). We set ratingZero to ratingMax minus 3. Then we filter actionList to return any item where its rating is greater than ratingZero. Finally, we run a for loop with iteration variable "i" starting at 0 and incrementing as long as i is less than the result of numActions(). In this loop, we call the action function passing in i to get the action object at that index, then call setEnemyAction on it passing in the result of calling selectAction passing in actionList and ratingZero.

Let's say we have an enemy which attacks twice per turn. It can always use a standard attack with priority 7. It can also use a weak attack that blinds at priority 5, and it has in its list a devastating attack which inflicts death on the entire party, with priority 3.

The actionList map will give us and the Math.max.apply will return 7 as that's the highest value, so ratingMax becomes 7. ratingZero is then 4. When we filter actionList, the first and second actions have a rating higher than 4 so they will be included, while the last one won't. For each of the 2 actions, the enemy will queue up attack or attack+blind, with the former having a much greater chance of being chosen. How much greater? Let's take a look.

So each time we call selectAction, the first thing that happens is we get "sum" as the sum of all action ratings after we've subtracted ratingZero from them. This effectively gives us 0 + 7 - 4 + 5 - 4, resulting in 4. sum is greater than 0, so the if condition succeeds. value will be 0, 1, 2 or 3. Then we start the for loop. action is currently the normal attack, and we subtract its rating minus ratingZero (3) from value. The result here can either be -3, -2, -1 or 0. That means there are 3 cases where this action will be used, and 1 where it won't. The second skill won't even be considered unless the original random number was 3.

Game_Enemy.prototype.makeActions = function() {
    Game_Battler.prototype.makeActions.call(this);
    if (this.numActions() > 0) {
        var actionList = this.enemy().actions.filter(function(a) {
            return this.isActionValid(a);
        }, this);
        if (actionList.length > 0) {
            this.selectAllActions(actionList);
        }
    }
    this.setActionState('waiting');
};


And then we have makeActions, which gets the actual action list ready. First we call the parent function, then if numActions() is greater than 0, we set actionList to enemy()'s actions filtered to only include an action for which isActionValid returns true. If actionList's length is greater than 0, we call selectAllActions passing in actionList. Whether numActions is greater than 0 or not, we call setActionState passing in 'waiting' to let the engine know the enemy is done choosing its turn.

Thus ends Game_Enemy. Next!

Game_Actors
This is just a wrapper class for an array of actors. It doesn't have a prototype because it doesn't need one.

Game_Actors.prototype.initialize = function() {
    this._data = [];
};


In the initialize function, we set the internal _data variable to an empty array.

Game_Actors.prototype.actor = function(actorId) {
    if ($dataActors[actorId]) {
        if (!this._data[actorId]) {
            this._data[actorId] = new Game_Actor(actorId);
        }
        return this._data[actorId];
    }
    return null;
};


The actor function returns a given actor, taking actorId as a parameter. If $dataActors has an entry at an index matching actorId, we check whether _data does NOT have an entry at that ID. If not, we set the element of _data at that index to a new instance of Game_Actor, passing in actorId as the actor ID argument. Either way, we return the element of _data at index actorId. If $dataActors does not contain data at the given index, we return null since that actor doesn't exist.

This tells us something quite interesting: $gameActors only contains an object for an actor after the first time the .actor function is called for them.

And..uh...that's it for Game_Actors! It literally consists of nothing but this. Its only purpose in life is to save us a few lines of code, and for that we salute it.

Game_Unit
This is a bit meatier. Game_Unit is the superclass of the classes for the party and enemy troop.

Game_Unit.prototype.initialize = function() {
    this._inBattle = false;
};


initialize is nothing special; we just set the internal _inBattle flag to false. As the name suggests, this will be used to track whether the unit is in battle or not.

Game_Unit.prototype.inBattle = function() {
    return this._inBattle;
};


And surprising nobody, we now have a nice shiny getter for it.

Game_Unit.prototype.members = function() {
    return [];
};


In the members function, we return an empty array. This will be changed in the child classes.

Game_Unit.prototype.aliveMembers = function() {
    return this.members().filter(function(member) {
        return member.isAlive();
    });
};


aliveMembers gives us an array of all members who haven't bit the big one yet. To do this, we return members(), filtered by a function which returns the result of calling isAlive() on the member. So if that returns true, the member is included in the returned array. If not, they will be unceremoniously discarded. The RPG is a harsh mistress.

Game_Unit.prototype.deadMembers = function() {
    return this.members().filter(function(member) {
        return member.isDead();
    });
};


deadMembers does the exact opposite: we filter members() as before, but this time we filter on isDead() so we'll be left with an array of members who have met their maker.

Game_Unit.prototype.movableMembers = function() {
    return this.members().filter(function(member) {
        return member.canMove();
    });
};


movableMembers gets us an array of members who can move (aren't restricted by a state etc.) and here we're using canMove() in the filter. It's otherwise identical to the last two.

Game_Unit.prototype.clearActions = function() {
    return this.members().forEach(function(member) {
        return member.clearActions();
    });
};


clearActions just gives us a handy way to clear the actions of all members of a unit at once. So we're running a forEach loop on members(), and the function passed in is returning each member's clearActions() function.

Game_Unit.prototype.agility = function() {
    var members = this.members();
    if (members.length === 0) {
        return 1;
    }
    var sum = members.reduce(function(r, member) {
        return r + member.agi;
    }, 0);
    return sum / members.length;
};


agility gets the average agility among all members. So to start with we're setting a variable called members to members(). Then if the length of members is 0, we return 1. If it's greater than 0, we set variable sum to the result of running a reduce on members which starts at 0 and adds the member's agi to the accumulator r (which will give us the combined total of all agility values). Then we return sum divided by the length of members, since that's how averages work.

Game_Unit.prototype.tgrSum = function() {
    return this.aliveMembers().reduce(function(r, member) {
        return r + member.tgr;
    }, 0);
};


tgrSum is effectively the first part of agility but for the TGR (target rate) parameter, though unlike agility it only takes alive members into account rather than all of them. So here we're returning a reduce function run on aliveMembers(), adding each member's tgr value to accumulator r. This will result in us having a combined total of all TGRs in the unit.

Game_Unit.prototype.randomTarget = function() {

var tgrRand = Math.random() * this.tgrSum();
var target = null;
this.aliveMembers().forEach(function(member) {
tgrRand -= member.tgr;
if (tgrRand <= 0 && !target) {
target = member;
}
});
return target;
};


This function gets a random target from the alive members of the unit. We set tgrRand to a random number between 0 and 1 multiplied by tgrSum(), and target to null. Then we run a forEach loop on aliveMembers() where we subtract each member's tgr from tgrRand and then if it's less than or equal to 0 AND target is null, we set target to member. Finally, we return target.

So let's say we have a character with 100% TGR, a tank with 300%, and a rogue with 25% TGR due to her sneakiness. tgrSum is going to be 1.0 + 3.0 + 0.25, for a sum of 4.25. Let's say our random number is in the region of 0.35873; tgrRand will be 1.5246025. The first loop of the forEach will subtract 1 from the value, giving us 0.5246025. tgrRand is not less than or equal to 0, so we go back around to the next loop. This time, we subtract 3 from tgrRand, making it -2.4753975. This *is* less than or equal to 0, and we don't have a target yet, so target is set to the tank character. And then it'll do a loop for the rogue as well, but it doesn't matter at this point since target won't be set again after one is chosen.

Game_Unit.prototype.randomDeadTarget = function() {

var members = this.deadMembers();
if (members.length === 0) {
return null;
}
return members[Math.floor(Math.random() * members.length)];
};


This function gets a random dead target. We set variable members to the result of deadMembers(). If the length is 0, we return null. Otherwise, we return the element of members at an index of a random number from 0 to 1 multiplied by the length of the array, rounded down.

Game_Unit.prototype.smoothTarget = function(index) {
    if (index < 0) {
        index = 0;
    }
    var member = this.members()[index];
    return (member && member.isAlive()) ? member : this.aliveMembers()[0];
};


This function smooths out the target, which is to say it helps ensure that we're never trying to do something to a dead enemy. It takes index as a parameter.

If index is less than 0, we set it to 0. We set variable member to the element of members() at an index of "index". Then if the member exists and is alive, we return member, and otherwise we return the element of aliveMembers() at index 0.

Game_Unit.prototype.smoothDeadTarget = function(index) {
    if (index < 0) {
        index = 0;
    }
    var member = this.members()[index];
    return (member && member.isDead()) ? member : this.deadMembers()[0];
};


This one is just the same but for smoothing to a dead target. The only difference is that we're calling isDead() and deadMembers() instead.

Game_Unit.prototype.clearResults = function() {
    this.members().forEach(function(member) {
        member.clearResult();
    });
};


As with clearActions, this function is just a wrapper to clear the results of all the members at once. So, naturally, we run a forEach loop on members() and in the function that's passed in, we call each member's clearResult() function. Simples!

Game_Unit.prototype.onBattleStart = function() {
    this.members().forEach(function(member) {
        member.onBattleStart();
    });
    this._inBattle = true;
};


This function is called upon starting a battle. First, we run a forEach loop on members() calling onBattleStart() for each member, and then we set _inBattle to true.

Game_Unit.prototype.onBattleEnd = function() {
    this._inBattle = false;
    this.members().forEach(function(member) {
        member.onBattleEnd();
    });
};


And its counterpart, onBattleEnd, is called upon ending a battle. So we set _inBattle to false, and have another forEach loop where we call each member's onBattleEnd() function.

Game_Unit.prototype.makeActions = function() {
    this.members().forEach(function(member) {
        member.makeActions();
    });
};


We also have a unit-wide function for making actions! So here, we run yet another forEach loop on members() and call each member's makeActions() function.

Game_Unit.prototype.select = function(activeMember) {
    this.members().forEach(function(member) {
        if (member === activeMember) {
            member.select();
        } else {
            member.deselect();
        }
    });
};


This function selects the active member of the unit, and takes activeMember as a parameter. Yet again, we run a forEach loop on members(). If the current member is the same object as activeMember, we call select() on it. Otherwise, we call deselect() on it.

Game_Unit.prototype.isAllDead = function() {
    return this.aliveMembers().length === 0;
};


This function checks whether everyone in the unit is dead. To do this, we return whether aliveMembers() has a length of 0.

Game_Unit.prototype.substituteBattler = function() {
    var members = this.members();
    for (var i = 0; i < members.length; i++) {
        if (members[i].isSubstitute()) {
            return members[i];
        }
    }
};


This function determines the battler who will be used as a stand-in for low-HP battlers, for the purposes of the "substitute" trait. We set variable members to members(), then have a for loop using iteration variable "i", incrementing as long as i is less than the length of members. If the element i of members returns true on isSubstitute(), meaning they possess the substitute trait, we return that member.

And that's it for Game_Unit! I'm over my line count now and Game_Party is quite a big one, so I think we'll leave it there for this week. I'll be back next Wednesday and we'll break that puppy down too. No stone left unturned, folks!

Until next time!