JUMP INTO JAVASCRIPT PART 14

In which we demystify parties and troops.

  • Trihan
  • 02/25/2022 06:18 PM
  • 519 views
Hello sports fans (except ATT_Turan)! After a lengthier hiatus than intended, especially since I haven't actually finished the Touch the Stars update I was working on yet, it's finally, finally, time for the next


Jump into Javascript

So last time we broke down Game_Enemy, Game_Actors and Game_Unit. Time for the juggernaut that is Game_Party!

Game_Party
Actors are all well and good, but no RPG is worth its salt if they don't team up occasionally. Game_Party is the class that pulls together all the functions a well-oiled group of ragtag adventurers could need. It inherits from Game_Unit.

Game_Party.ABILITY_ENCOUNTER_HALF    = 0;
Game_Party.ABILITY_ENCOUNTER_NONE    = 1;
Game_Party.ABILITY_CANCEL_SURPRISE   = 2;
Game_Party.ABILITY_RAISE_PREEMPTIVE  = 3;
Game_Party.ABILITY_GOLD_DOUBLE       = 4;
Game_Party.ABILITY_DROP_ITEM_DOUBLE  = 5;


Before we get to the meat of initialisation, there are some constants to set up. So we're mapping ABILITY_ENCOUNTER_NONE to 1, ABILITY_CANCEL_SURPRISE to 2, ABILITY_RAISE_PREEMPTIVE to 3, ABILITY_GOLD_DOUBLE to 4 and ABILITY_DROP_ITEM_DOUBLE to 5. These should be pretty self-explanatory; they're not really for the engine, they're for us. It's easier to remember a constant than it is the numerical value it corresponds to.

Game_Party.prototype.initialize = function() {
    Game_Unit.prototype.initialize.call(this);
    this._gold = 0;
    this._steps = 0;
    this._lastItem = new Game_Item();
    this._menuActorId = 0;
    this._targetActorId = 0;
    this._actors = [];
    this.initAllItems();
};


The initialize function first calls the super function, we we've seen many times before. We also initialise a number of instance variables: _gold to 0, _steps to 0, _lastItem to a new instance of Game_Item, _menuActorId to 0, _targetActorId to 0, and _actors to an empty array. Then we call the initAllItems function, which we'll see in a sec.

Game_Party.prototype.initAllItems = function() {
    this._items = {};
    this._weapons = {};
    this._armors = {};
};


The initAllItems is exactly what it says on the tin; it initialises all of the different item types. So we set _items, _weapon and _armors to empty arrays.

Game_Party.prototype.exists = function() {
    return this._actors.length > 0;
};


The exists function basically tells us whether there even is a party, and is used anywhere a coder might want to make sure there are actors before doing something. We return true if the _actors property has a length greater than 0, which would mean there is at least 1 actor in the party. If not, obviously there isn't, and the function will return false.

Game_Party.prototype.size = function() {
    return this.members().length;
};


We also have a size function to check how big the party is, in which we just return the length of the result from calling the members() function (which we'll see shortly). A lot of people forget this function exists (me included until now!) and often end up using $gameParty.members().length in code when we don't actually need to.

Game_Party.prototype.isEmpty = function() {
    return this.size() === 0;
};


Another often-forgotten gem is isEmpty, which is just a shorthand function for returning whether size() gives us a value of 0.

Game_Party.prototype.members = function() {
    return this.inBattle() ? this.battleMembers() : this.allMembers();
};


Now we see the members function. If inBattle() returns true, we return the result of battleMembers(). Otherwise we return the result of allMembers().

Game_Party.prototype.allMembers = function() {
    return this._actors.map(function(id) {
        return $gameActors.actor(id);
    });
};


This function returns all members of the party. to do so, we map the _actors array to a function using iteration variable "id" which returns $gameActors.actor() with id passed in. This basically takes the array of actor IDs and gives us a new array of corresponding actor objects.

Game_Party.prototype.battleMembers = function() {
    return this.allMembers().slice(0, this.maxBattleMembers()).filter(function(actor) {
        return actor.isAppeared();
    });
};


This function narrows the field a bit by only returning battle members. We do this by calling allMembers() then slicing it from index 0 to the result from maxBattleMembers(), then filtering that array using iteration variable "actor" and returning any for whom the isAppeared() function returns true.

Game_Party.prototype.maxBattleMembers = function() {
    return 4;
};


In maxBattleMembers by default we simply return 4, since that's the largest number of party members who can be in battle at any one time. If you want more/fewer, this is the value to edit.

Game_Party.prototype.leader = function() {
    return this.battleMembers()[0];
};


Here we have a shortcut function for getting the 0-index element of the array returned by battleMembers(). It basically just saves us from having to use $gameParty.battleMembers() every time we want to do something dependent on the leader, and can just do $gameParty.leader() instead.

Game_Party.prototype.reviveBattleMembers = function() {
    this.battleMembers().forEach(function(actor) {
        if (actor.isDead()) {
            actor.setHp(1);
        }
    });
};


This function, as its name implies, revives battle members. We run a forEach loop on battleMembers(), using iteration variable "actor". Inside the block, if the actor returns true for isDead(), we call the actor's setHp function, passing in 1 as the argument. This will, as you may have guessed, set their HP to 1 (which will then cause the refresh function for Game_Battler to remove the death state.)

Game_Party.prototype.items = function() {
    var list = [];
    for (var id in this._items) {
        list.push($dataItems[id]);
    }
    return list;
};


This function gets a list of items held by the party. First we initialise an empty array called list, then we run a for loop through each elements in the _items array using iteration variable "id". In that loop, we push the element of $dataItems at index id to list. Finally, we return list. In effect, this turns an array of item IDs into an array of item objects.

Game_Party.prototype.weapons = function() {
    var list = [];
    for (var id in this._weapons) {
        list.push($dataWeapons[id]);
    }
    return list;
};


This is the same thing, but for the party's weapons. So instead of _items we're using _weapons, and instead of $dataItems we're using $dataWeapons. Other than that, it works identically.

Game_Party.prototype.armors = function() {
    var list = [];
    for (var id in this._armors) {
        list.push($dataArmors[id]);
    }
    return list;
};


And this is the same thing again but for armours.

Game_Party.prototype.equipItems = function() {
    return this.weapons().concat(this.armors());
};


This function returns an array of all equipment objects. To do so, we returns the result of weapons() concatenated with the result of armors(). concat simply appends one array onto another one.

Game_Party.prototype.allItems = function() {
    return this.items().concat(this.equipItems());
};


This is the same for all items, and for that we take the result of items() and concatenate the result of equipItems(). So this just gives us one big mishmashed array of objects representing every item, weapon and piece of armour the party possesses.

Game_Party.prototype.itemContainer = function(item) {
    if (!item) {
        return null;
    } else if (DataManager.isItem(item)) {
        return this._items;
    } else if (DataManager.isWeapon(item)) {
        return this._weapons;
    } else if (DataManager.isArmor(item)) {
        return this._armors;
    } else {
        return null;
    }
};


This function gets the container property appropriate to a given item, taking an item object as a parameter. If no item exists, we return null. Otherwise if DataManager's isItem function returns true, we return the _items property. Otherwise if DataManager's isWeapon function returns true, we return the _weapons property. Otherwise if DataManager's isArmor function returns true, we return the _armors property. And in any other case, we return null.

Game_Party.prototype.setupStartingMembers = function() {
    this._actors = [];
    $dataSystem.partyMembers.forEach(function(actorId) {
        if ($gameActors.actor(actorId)) {
            this._actors.push(actorId);
        }
    }, this);
};


This function does the initial party setup for the starting members. We initialise an empty array called _actors, then run a forEach loop on $dataSystem's partyMembers property using iteration variable "actorId". In the forEach function, we check whether the actor function of $gameActors returns a value with actorId passed in, and if so we push that actorId to the _actors array. The partyMembers property in $dataSystem maps to the "Starting Party" lists in the system tab of the database.

Game_Party.prototype.name = function() {
    var numBattleMembers = this.battleMembers().length;
    if (numBattleMembers === 0) {
        return '';
    } else if (numBattleMembers === 1) {
        return this.leader().name();
    } else {
        return TextManager.partyName.format(this.leader().name());
    }
};


This function determines the name of the party for the purposes of displaying it on victory, defeat or escape. First, we set a variable called numBattleMembers to the length of the battleMembers() array. Then, if numBattleMembers is 0, we return a blank string because there's no party. If numBattleMembers is 1, we return the leader()'s name(), which just gives us the name of the only party member. Otherwise, we return TextManager's partyName property formatted with the leader's name. partyName maps to the Party Name message from the Terms tab of the database, which by default is "%1's Party" so this will replace %1 with the party leader's name. For example, if Reid is in the top party slot, this will return "Reid's Party".

Game_Party.prototype.setupBattleTest = function() {
    this.setupBattleTestMembers();
    this.setupBattleTestItems();
};


This function sets everything up for a test battle. In this setup function, all we're doing is calling setupBattleTestMembers() and setupBattleTestItems(), which we'll see in a second.

Game_Party.prototype.setupBattleTestMembers = function() {
    $dataSystem.testBattlers.forEach(function(battler) {
        var actor = $gameActors.actor(battler.actorId);
        if (actor) {
            actor.changeLevel(battler.level, false);
            actor.initEquips(battler.equips);
            actor.recoverAll();
            this.addActor(battler.actorId);
        }
    }, this);
};


This function sets up the actors who will be participating in the test battle. We run a forEach loop on $dataSystem's testBattlers array, using iteration variable "battler". We set a variable called actor to the actor in $gameActors with an ID of the battle's actorId property, then if the actor exists, we call their changeLevel function passing in the battler's level for the "level" parameter and false for the "show" parameter; the initEquips function passing in the battler's equips property; the recoverAll function; and then we call this.addActor passing in the battle's actor ID.

It's important to note that normally, in this forEach loop, "this" would refer to the same object as "battler" does. That would have caused an error, because obviously battlers don't have an addActor function. That's why the closing curly bracket of the loop is followed by ", this"; what that does is modifies the execution context of the loop so that "this" used inside the loop block will now refer to the current instance of Game_Party instead of the battler.

Game_Party.prototype.setupBattleTestItems = function() {
    $dataItems.forEach(function(item) {
        if (item && item.name.length > 0) {
            this.gainItem(item, this.maxItems(item));
        }
    }, this);
};


This function is used during a test battle and just gives the party the maximum possible number of every item in the game.

We run a forEach loop on $dataItems, using the iteration variable "item". In each loop, if item exists and item's name property has a length greater than 0, we call the gainItem function, passing in item and the result of the maxItems function (with item passed to it) as arguments.

Game_Party.prototype.highestLevel = function() {
    return Math.max.apply(null, this.members().map(function(actor) {
        return actor.level;
    }));
};


This function gives us the level of the highest-level party member. To do this, we return Math.max.apply, passing in null as the first argument and a map of the party members as the second, where the map function returns the actor's level.

To break this down with a worked example, let's say we have 4 party members and their levels are 4, 8, 3, and 5. We start with an array of Game_Actor objects, but the .map gives us a new array where each element is that actor's level, so we're left with . Math.max then gets the highest value from there, so the function will return 8. If you've forgotten from before, we need to use .apply because the Math.max function doesn't natively support arrays as arguments.

Game_Party.prototype.addActor = function(actorId) {
    if (!this._actors.contains(actorId)) {
        this._actors.push(actorId);
        $gamePlayer.refresh();
        $gameMap.requestRefresh();
    }
};


This function adds an actor to the party, taking actorId as a parameter. We check whether the _actors property does not contain the passed-in actorId (because we don't want the same actor to be added twice); if it doesn't, we push the actorId to the _actors property, then call $gamePlayer's refresh() function (which will refresh followers and show our new member behind the leader if necessary) and then call $gmeMap's requestRefresh() function, which refreshes events and common events.

Game_Party.prototype.removeActor = function(actorId) {
    if (this._actors.contains(actorId)) {
        this._actors.splice(this._actors.indexOf(actorId), 1);
        $gamePlayer.refresh();
        $gameMap.requestRefresh();
    }
};


This is the counterpart function to remove an actor, and takes the same actorId parameter. The only difference is that we're checking for _actors containing the actorId instead of not containing it, and instead of Array.push() were calling the splice() function, passing in the index of _actors that contains actorId, and removing 1 element.

Game_Party.prototype.gold = function() {
    return this._gold;
};


This is just a wrapper function that returns the _gold property.

Game_Party.prototype.gainGold = function(amount) {
    this._gold = (this._gold + amount).clamp(0, this.maxGold());
};


This function allows the party to gain gold, and takes amount as a parameter. We set the _gold property to equal its current value plus amount, clamped to a value between 0 and the result of the maxGold() function.

Game_Party.prototype.loseGold = function(amount) {
    this.gainGold(-amount);
};


The counterpart function is loseGold, which takes gold away. To do this, we just call gainGold and pass the inverse of amount as the argument.

Game_Party.prototype.maxGold = function() {
    return 99999999;
};


This function determines the maximum amount of gold the party can have at any one time. In the default engine, we just return 99,999,999.

Game_Party.prototype.steps = function() {
    return this._steps;
};


This is just a wrapper function for the party's number of steps, and here we return the value of the _steps property.

Game_Party.prototype.increaseSteps = function() {
    this._steps++;
};


This function increases the step count, and do to this we simply increment _steps.

Game_Party.prototype.numItems = function(item) {
    var container = this.itemContainer(item);
    return container ? container[item.id] || 0 : 0;
};


This function gets the number of a particular item the party is currently holding, and takes item as a parameter. We set a temporary variable called container to the result of calling itemContainer() with item as an argument, and then if container exists we return the element of that container at an index of the item's id property (or 0 if that value is falsy) or 0 if container doesn't exist.

Game_Party.prototype.maxItems = function(item) {
    return 99;
};


This function gets the maximum number of a particular item the party can have, taking item as a parameter. And here we simply return 99.

Game_Party.prototype.hasMaxItems = function(item) {
    return this.numItems(item) >= this.maxItems(item);
};


This function checks whether the party has the maximum possible number of a particular item, taking item as a parameter. We return whether the result of numItems() is greater than or equal to the result of maxItems(), passing item as an argument to both.

Game_Party.prototype.hasItem = function(item, includeEquip) {
    if (includeEquip === undefined) {
        includeEquip = false;
    }
    if (this.numItems(item) > 0) {
        return true;
    } else if (includeEquip && this.isAnyMemberEquipped(item)) {
        return true;
    } else {
        return false;
    }
};


This function checks whether the party possesses a particular item, and takes two parameters: item and includeEquip, a boolean flag that determines whether we will consider equipment on party members in the count or not.

The first thing we do here is check whether includeEquip has a value of "undefined" (meaning it was never passed to the function) and if so we set its value to false.

Then, if the return value of numItems() is greater than 0 with item as an argument, we return true (since that means the party has it). Otherwise, if includeEquip is true and the result of isAnyMemberEquipped() is true with item as an argument, we return true. Otherwise, we return false.

Game_Party.prototype.isAnyMemberEquipped = function(item) {
    return this.members().some(function(actor) {
        return actor.equips().contains(item);
    });
};


This function checks whether any member of the party has a particular item equipped, taking item as a parameter. Our return value calls the members() function to get an array of party members, then calls some() on that array passing a function as the argument that uses iteration variable "actor" and returns whether actor's equips() array contains the item. This will return true if any party member results in a return value of true, and false if none do.

Game_Party.prototype.gainItem = function(item, amount, includeEquip) {
    var container = this.itemContainer(item);
    if (container) {
        var lastNumber = this.numItems(item);
        var newNumber = lastNumber + amount;
        container[item.id] = newNumber.clamp(0, this.maxItems(item));
        if (container[item.id] === 0) {
            delete container[item.id];
        }
        if (includeEquip && newNumber < 0) {
            this.discardMembersEquip(item, -newNumber);
        }
        $gameMap.requestRefresh();
    }
};


This function adds an item to the party's inventory, and takes three parameters: item (an item object), amount (the number to be added) and includeEquip (boolean flag for whether to include equipment). It may seem odd to have includeEquip here, but the reason for it is that losing an item just calls gainItem with the inverse amount value (just like loseGold did).

First of all, we set temporary variable "container" to the result of itemContainer() with item as an argument. If the container exists, we set lastNumber to the result of numItems() with item passed to it, and newNumber to lastNumber plus amount. Then the element of container at an index of the item's id is set to newNumber, clamped to a value between 0 and the result of maxItems() with item passed to it.

If that element of container is now 0, we delete the element.

If includeEquip is true and newNumber is less than 0 (meaning we're reducing the item count by more than the party has in inventory) we call discardMembersEquip(), passing in item and the inverse of newNumber as the arguments.

Finally, we call the requestRefresh() function of $gameMap.

Game_Party.prototype.discardMembersEquip = function(item, amount) {
    var n = amount;
    this.members().forEach(function(actor) {
        while (n > 0 && actor.isEquipped(item)) {
            actor.discardEquip(item);
            n--;
        }
    });
};


This function, as the name suggests, discards equipment from party members. It takes two parameters, item and amount.

We set temporary variable n to the passed-in amount value. Then we get the array of members by calling members() and call forEach on that array passing in a function that uses iteration variable "actor". In that function, while n is greater than 0 and actor's isEquipped() function returns true with item as an argument, we call actor's discardEquip() function passing in item as the item to discard. Then we decrement n.

Game_Party.prototype.loseItem = function(item, amount, includeEquip) {
    this.gainItem(item, -amount, includeEquip);
};


This function removes items from inventory, and takes three parameters: item, amount and includeEquip. All we do here is call gainItem() passing in the received arguments, but inverting amount.

Game_Party.prototype.consumeItem = function(item) {
    if (DataManager.isItem(item) && item.consumable) {
        this.loseItem(item, 1);
    }
};


This function processes the consumption of a consumable item, taking item as a parameter. If DataManager's isItem() function returns true with item as its argument AND the item's consumable property is true, we call loseItem, passing in item as the item and 1 as the amount.

Game_Party.prototype.canUse = function(item) {
    return this.members().some(function(actor) {
        return actor.canUse(item);
    });
};


This function checks whether the party is able to use a particular item, taking item as a parameter. Our return value gets the members array by calling members() then calls some() on that array, with the passed-in function using iteration variable "actor" and returning that actor's canUse() function with item passed to it. This will return true if any actor can use the item, and false if none can.

Game_Party.prototype.canInput = function() {
    return this.members().some(function(actor) {
        return actor.canInput();
    });
};


This function is just exactly the same thing but using canInput() instead, which tells us whether any party member is capable of inputting actions.

Game_Party.prototype.isAllDead = function() {
    if (Game_Unit.prototype.isAllDead.call(this)) {
        return this.inBattle() || !this.isEmpty();
    } else {
        return false;
    }
};


This function checks whether all party members are dead. If a call to Game_Unit's prototype's isAllDead() function returns true, we return true if inBattle() returns true OR isEmpty() returns true, meaning the party is either in battle or the party has no members in it. Otherwise, we return false.

Game_Party.prototype.onPlayerWalk = function() {
    this.members().forEach(function(actor) {
        return actor.onPlayerWalk();
    });
};


This is a function that runs when the player walks, and here we just run a forEach function on the party members using iteration variable "actor" which calls that actor's onPlayerWalk() function.

Game_Party.prototype.menuActor = function() {
    var actor = $gameActors.actor(this._menuActorId);
    if (!this.members().contains(actor)) {
        actor = this.members()[0];
    }
    return actor;
};


This function gets the current menu actor. First we set temporary variable actor to the result of calling $gameActors' actor() function with the value of the _menuActorId property as its argument. Then if members() does not contain actor, we set actor to the 0th index of members() (the party leader), and finally we return actor.

Game_Party.prototype.setMenuActor = function(actor) {
    this._menuActorId = actor.actorId();
};


This function sets the menu actor, taking an actor as a parameter. We set the value of the _menuActorId property to the return value of the actor's actorId() function.

Game_Party.prototype.makeMenuActorNext = function() {
    var index = this.members().indexOf(this.menuActor());
    if (index >= 0) {
        index = (index + 1) % this.members().length;
        this.setMenuActor(this.members()[index]);
    } else {
        this.setMenuActor(this.members()[0]);
    }
};


This function sets the menu actor to the next member of the party from the current one. First, we set temporary variable "index" to the index of menuActor() in the members() array. If index is greater than or equal to 0, meaning the menu actor is present in the party, we set the index to itself plus 1, modulus the length of members(), then call setMenuActor passing in the element of members() at index. Otherwise, if index is not greater than or equal to 0 (meaning the menu actor is not present in the party) we call setMenuActor but this time pass in the 0th element of members() (the party leader).

Game_Party.prototype.makeMenuActorPrevious = function() {
    var index = this.members().indexOf(this.menuActor());
    if (index >= 0) {
        index = (index + this.members().length - 1) % this.members().length;
        this.setMenuActor(this.members()[index]);
    } else {
        this.setMenuActor(this.members()[0]);
    }
};


This is just the same thing but selecting the previous actor rather than the next one. The only difference is in the calculation for index. Say the party has 4 members and index is 2, it then becomes (2 + 4 - 1) % 4; 5 % 4 gives us 1.

Game_Party.prototype.targetActor = function() {
    var actor = $gameActors.actor(this._targetActorId);
    if (!this.members().contains(actor)) {
        actor = this.members()[0];
    }
    return actor;
};


This function gets the target actor. We set temporary variable "actor" to the result of calling $gameActors' actor() function with _targetActorId as an argument. If members() does NOT contain actor, we set actor to the 0th element of members() (the party leader). Then we return actor.

Game_Party.prototype.setTargetActor = function(actor) {
    this._targetActorId = actor.actorId();
};


This function sets the target actor, taking actor as a parameter. Here, we just set the value of the _targetActorId property to actor's actorId().

Game_Party.prototype.lastItem = function() {
    return this._lastItem.object();
};


This function gets the last item used. We return the result of calling the object() function of the _lastItem property.

Game_Party.prototype.setLastItem = function(item) {
    this._lastItem.setObject(item);
};


This function sets the last used item, taking item as a parameter. Here, we just call the setObject() function of _lastItem, passing item as the argument. This will cause _lastItem to become the item we passed to it.

Game_Party.prototype.swapOrder = function(index1, index2) {
    var temp = this._actors[index1];
    this._actors[index1] = this._actors[index2];
    this._actors[index2] = temp;
    $gamePlayer.refresh();
};


This function swaps around the order of two party members, and takes two parameters: index1 and index2. We set a temporary variable called temp to the "index1"th element of _actors. Then we set that element of actors to the "index2"th element, and then set that element to temp. Effectively we're putting the first actor in a box, moving the second actor into their place, and then taking the first actor out of the box and putting them where the second actor was.

After this, we call the refresh() function of $gamePlayer (as we'll see when we get to covering that class, this is done in case the swapping changes who the party leader is, in which case the map graphic needs to change).

Game_Party.prototype.charactersForSavefile = function() {
    return this.battleMembers().map(function(actor) {
        return [actor.characterName(), actor.characterIndex()];
    });
};


This function gets an array of character information for use in the save file scene. Here, we return a .map() on battleMembers(), where the callback function returns an array consisting of the actor's characterName() and characterIndex(). In other words, we're getting an iterable list of graphic filenames and the index in those files containing each party member's character set.

Game_Party.prototype.facesForSavefile = function() {
    return this.battleMembers().map(function(actor) {
        return [actor.faceName(), actor.faceIndex()];
    });
};


This is just the same thing but for the save file faces. So instead of characterName() and characterIndex(), we're calling faceName() and faceIndex(). It's otherwise identical.

Game_Party.prototype.partyAbility = function(abilityId) {
    return this.battleMembers().some(function(actor) {
        return actor.partyAbility(abilityId);
    });
};


This function checks whether the party posses a particular ability, taking abilityId as a parameter. Here we're calling the .some() function on battleMembers(), with a callback function that returns the result of calling the actor's partyAbility function with abilityId as the argument. This function generally isn't called directly, as there are a number of more specific functions that use it:

Game_Party.prototype.hasEncounterHalf = function() {
    return this.partyAbility(Game_Party.ABILITY_ENCOUNTER_HALF);
};

Game_Party.prototype.hasEncounterNone = function() {
    return this.partyAbility(Game_Party.ABILITY_ENCOUNTER_NONE);
};

Game_Party.prototype.hasCancelSurprise = function() {
    return this.partyAbility(Game_Party.ABILITY_CANCEL_SURPRISE);
};

Game_Party.prototype.hasRaisePreemptive = function() {
    return this.partyAbility(Game_Party.ABILITY_RAISE_PREEMPTIVE);
};

Game_Party.prototype.hasGoldDouble = function() {
    return this.partyAbility(Game_Party.ABILITY_GOLD_DOUBLE);
};

Game_Party.prototype.hasDropItemDouble = function() {
    return this.partyAbility(Game_Party.ABILITY_DROP_ITEM_DOUBLE);
};


This set of functions checks for, respectively, the "Encounter Half", "Encounter None", "Cancel Surprise", "Raise Preemptive", "Gold Double" and Drop Item Double" traits. If you remember from the constant declarations at the beginning of this class, the values evaluate to 0, 1, 2, 3, 4 and 5.

Game_Party.prototype.ratePreemptive = function(troopAgi) {
    var rate = this.agility() >= troopAgi ? 0.05 : 0.03;
    if (this.hasRaisePreemptive()) {
        rate *= 4;
    }
    return rate;
};


This function determines the pre-emptive battle chance, taking troopAgi as a parameter. We set a temporary variable called rate to a ternary value based on whether the agility() of the party (which if you remember from Game_Unit gives us the party's average agility value) is greater than or equal to troopAgi. If it is, we set rate to 0.05 (5%). Otherwise, we set it to 0.03 (3%). In other words, the base pre-emptive rate is 2% higher if the party's average agility matches or exceeds that of the enemy.

Then, if hasRaisePreemptive returns true (meaning at least one party member has the "Raise Preemptive" trait), we multiply rate by 4. This will give us either 0.2 (20%) or 0.12 (12%), resulting in this trait having a much bigger impact if our agility is higher.

Finally, we return rate.

Game_Party.prototype.rateSurprise = function(troopAgi) {
    var rate = this.agility() >= troopAgi ? 0.03 : 0.05;
    if (this.hasCancelSurprise()) {
        rate = 0;
    }
    return rate;
};


This function does the same thing for the rate of a surprise attack from the enemy, again taking troopAgi as a parameter. This time, if agility() is greater than or equal to troopAgi we're setting rate to 0.03 (3%), and otherwise it's 0.05 (5%). If hasCancelSurprise() returns true, meaning a party member has the "Cancel Surprise" trait, we set rate to 0 (because obviously that trait means no chance of surprise attacks at all). Then we return rate.

Game_Party.prototype.performVictory = function() {
    this.members().forEach(function(actor) {
        actor.performVictory();
    });
};

Game_Party.prototype.performEscape = function() {
    this.members().forEach(function(actor) {
        actor.performEscape();
    });
};

Game_Party.prototype.removeBattleStates = function() {
    this.members().forEach(function(actor) {
        actor.removeBattleStates();
    });
};

Game_Party.prototype.requestMotionRefresh = function() {
    this.members().forEach(function(actor) {
        actor.requestMotionRefresh();
    });
};


The final set of functions are just wrappers for calling the respective function on each party member. performVictory for when the party successfully wins a battle, performEscape for when they run away from one, removeBattleStates to remove any states that expire at the end of battle, and requestMotionRefresh to mark that the actor's battler motion needs to be updated.

And that's all for Game_Party!

Game_Troop
This game object class handles troops and battle data. It uses Game_Unit as its prototype.

function Game_Troop() {
    this.initialize.apply(this, arguments);
}

Game_Troop.prototype = Object.create(Game_Unit.prototype);
Game_Troop.prototype.constructor = Game_Troop;


Nothing special from the constructor or prototype setting, there's nothing here we haven't seen many times now.

Game_Troop.LETTER_TABLE_HALF = [
    ' A',' B',' C',' D',' E',' F',' G',' H',' I',' J',' K',' L',' M',
    ' N',' O',' P',' Q',' R',' S',' T',' U',' V',' W',' X',' Y',' Z'
];
Game_Troop.LETTER_TABLE_FULL = [
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
];


These constants define letter tables for both Chinese/Japanese/Korean display (FULL) or others (HALF).

Game_Troop.prototype.initialize = function() {
    Game_Unit.prototype.initialize.call(this);
    this._interpreter = new Game_Interpreter();
    this.clear();
};


The initialize function calls the parent function, as usual, then sets the _interpreter property to a new instance of Game_Interpreter(), and then calls the clear() function, which we'll see in a second. Obviously Game_Troop needs an interpreter instance, otherwise troop events wouldn't be able to run.

Game_Troop.prototype.isEventRunning = function() {
    return this._interpreter.isRunning();
};;


This function checks whether an event is currently running, and simply returns the result of calling _interpreter's isRunning() function.

Game_Troop.prototype.updateInterpreter = function() {
    this._interpreter.update();
};


This one's exactly what it says on the tin. We update the interpreter by calling the _interpreter's update() function. Simples!

Game_Troop.prototype.turnCount = function() {
    return this._turnCount;
};


This function is just a wrapper for the _turnCount property.

Game_Troop.prototype.members = function() {
    return this._enemies;
};


And this one is the same; our overwritten implementation of Game_Unit's members function returns the _enemies property.

Game_Troop.prototype.clear = function() {
    this._interpreter.clear();
    this._troopId = 0;
    this._eventFlags = {};
    this._enemies = [];
    this._turnCount = 0;
    this._namesCount = {};
};


This function clears the troop data. First we call the clear() function of _interpreter, then set _troopId to 0, _eventFlags to an empty object, _enemies to an empty array, _turnCount to 0, and _namesCount to an empty object.

Game_Troop.prototype.troop = function() {
    return $dataTroops[this._troopId];
};


This function gets the troop data object associated with the current troop. To do so, we return the "_troopId"th element of $dataTroops.

Game_Troop.prototype.setup = function(troopId) {
    this.clear();
    this._troopId = troopId;
    this._enemies = [];
    this.troop().members.forEach(function(member) {
        if ($dataEnemies[member.enemyId]) {
            var enemyId = member.enemyId;
            var x = member.x;
            var y = member.y;
            var enemy = new Game_Enemy(enemyId, x, y);
            if (member.hidden) {
                enemy.hide();
            }
            this._enemies.push(enemy);
        }
    }, this);
    this.makeUniqueNames();
};


This function sets up a troop, taking troopId as a parameter.

First we call the clear() function, to initialise all of our properties. We set _troopId to the passed troopId value, and _enemies to a blank array (this is technically redundant since clear already did that). Then we get the troop data with troop(). and on its members property run a forEach loop, with the callback function using an iteration variable called "member". In the callback function:

If there is an entry in $dataEnemies for the member's enemyId, we set enemyId to that property value. We set x to the member's x, and y to the member's y. Then we assign "enemy" with a new instance of Game_Enemy, passing in enemyId as the enemyId argument, x as the x, and y as the y. If the member's hidden property is true (meaning it was set to "Appear Mid-Battle") we call the enemy's hide() function. Then we push enemy to the _enemies array. We pass "this" as the second parameter to the forEach loop so that when we use that keyword inside the callback function, it references Game_Troop rather than the current troop member.

Game_Troop.prototype.makeUniqueNames = function() {
    var table = this.letterTable();
    this.members().forEach(function(enemy) {
        if (enemy.isAlive() && enemy.isLetterEmpty()) {
            var name = enemy.originalName();
            var n = this._namesCount[name] || 0;
            enemy.setLetter(table[n % table.length]);
            this._namesCount[name] = n + 1;
        }
    }, this);
    this.members().forEach(function(enemy) {
        var name = enemy.originalName();
        if (this._namesCount[name] >= 2) {
            enemy.setPlural(true);
        }
    }, this);
};


This function creates unique names for enemies where the troop has multiple copies of the same enemy. This is how you end up with "Goblin A", "Goblin B", "Goblin C" etc.

First, we set temporary variable to the return value of letterTable(), which we'll see in a second. Then we have a forEach() loop running on the members() array, with the callback function using iteration variable "enemy":

If the enemy's isAlive() function returns true AND their isLetterEmpty() function returns true, we set name to the enemt's originalName() return value, and n to the "name"th element of _namesCount if it exists, or 0 otherwise. Then we call the enemy's setLetter() function passing in the "n % table length"th element of table, and set the "name"th element of _namesCount to n plus 1. We pass "this" as the second argument to forEach so that using that keyword inside the callback function will reference Game_Troop and not the current enemy.

After this we run another forEach loop; in the callback for this one we again set name to enemy's originalName(), then if the "name"th element of _namesCount is greater than or equal to 2, we call enemy's setPlural function, passing true as the argument to the "plural" parameter.

The reason for doing this in separate forEach loops is that in the first one, an enemy doesn't know it has plurals if the plural enemies come after it in the list since they haven't updated _namesCount yet, so we need to go through them all again to check.

The setLetter argument might need a bit of extra explanation with a worked example. Let's say we have a troop of 2 Goblins. The first time we run through this, name is "Goblin" and n is 0. setLetter's argument is table, which evaluates to table. 0 divided by anything is 0, so this'll just be table, which gives us ' A'. _namesCount is incremented by 1, and is now 1.

The next loop, n is 1. setLetter's argument becomes table. 1 divided by 26 is 0 remainder 1, so we take table, which is ' B'._namesCount is incremented by 1, and is now 2.

In theory the % table.length isn't needed here since it's never possible to have enough enemies to use up the entire table anyway (since the maximum we can put in a troop is 8) though it does provide some futureproofing support for developers who use reinforcement plugins that programmatically add additional enemies to the troop.

Game_Troop.prototype.letterTable = function() {
    return $gameSystem.isCJK() ? Game_Troop.LETTER_TABLE_FULL :
            Game_Troop.LETTER_TABLE_HALF;
};


This function gets the appropriate letter table for your system's locale: as I said above, if the locale is either Chinese, Japanese or Korean (which is what $gameSystem's isCJK() function checks) we return the LETTER_TABLE_FULL constant, and otherwise we return the LETTER_TABLE_HALF constant.

Game_Troop.prototype.enemyNames = function() {
    var names = [];
    this.members().forEach(function(enemy) {
        var name = enemy.originalName();
        if (enemy.isAlive() && !names.contains(name)) {
            names.push(name);
        }
    });
    return names;
};


This function gets the names of each enemy in the troop as an array. First we set temporary variable "names" to an empty array. Then we have our good friend forEach() running a loop on members(). The callback function uses iteration variable "enemy": we set temporary variable name" to the enemy's originalName(), then is the enemy's isAlive() function returns true AND names does NOT contain name, we push name to names. This ensures that we only include each enemy's name once.

Finally, we return names.

Game_Troop.prototype.meetsConditions = function(page) {
    var c = page.conditions;
    if (!c.turnEnding && !c.turnValid && !c.enemyValid &&
            !c.actorValid && !c.switchValid) {
        return false;  // Conditions not set
    }
    if (c.turnEnding) {
        if (!BattleManager.isTurnEnd()) {
            return false;
        }
    }
    if (c.turnValid) {
        var n = this._turnCount;
        var a = c.turnA;
        var b = c.turnB;
        if ((b === 0 && n !== a)) {
            return false;
        }
        if ((b > 0 && (n < 1 || n < a || n % b !== a % b))) {
            return false;
        }
    }
    if (c.enemyValid) {
        var enemy = $gameTroop.members()[c.enemyIndex];
        if (!enemy || enemy.hpRate() * 100 > c.enemyHp) {
            return false;
        }
    }
    if (c.actorValid) {
        var actor = $gameActors.actor(c.actorId);
        if (!actor || actor.hpRate() * 100 > c.actorHp) {
            return false;
        }
    }
    if (c.switchValid) {
        if (!$gameSwitches.value(c.switchId)) {
            return false;
        }
    }
    return true;
};


This function checks whether a troop event page meets its conditions for execution, taking page as a parameter.

First, we set temporary variable "c" to the page's conditions property. If c's turnEnding property is false AND the turnValid property is false AND the enemyValid property is false AND the actorValid property is false AND the switchValid property is false, we return false (since no conditions have been set).

If turnEnding is true, we check whether BattleManager's isTurnEnd() function returns false. If it does, we return false.

If turnValid is true, we set temp variable "n" to the value of the _turnCount property, "a" to c's turnA property, and "b" to c's turnB property. If b is equal to 0 OR n is not equal to a, we return false. If b is greater than 0 AND (n is less than 1 OR n is less than a OR n divided by b gives a different remainder to a divided by b) we return false. I'll come back to that in a second.

If enemyValid is true, we set "enemy" to the "c's enemyIndex"th member of the troop. Then if enemy does not exist OR the enemy's hpRate() multiplied by 100 is greater than c's enemyHp property, we return false.

If actorValid is true, we do the same thing as above but with $gameActors instead of $gameTroop and actorId instead of enemyIndex. The rest works the same way.

If switchValid is true, we check whether the value of $gameSwitches at an index of c's switchId value returns false. If so, we return false.

Finally, we return true.

Effective what this function is doing is checking every possible scenario in which the conditions might not be met, eliminating each possibility in sequence.

Okay, so back to that turnValid thing as it might be a bit confusing.

Let's say we have a troop event set to "Turn" with 1 in the first input (which maps to the turnA property) and 5 in the second (which maps to turnB), or in other words, our event runs every 5 turns starting with turn 1. On turn 4, n is set to 4, a is set to 1 and b is set to 5. 5 is not equal to 0, so the first if statement doesn't run. 5 is greater than 0, so the first part of the second one does. 4 is not less than 1 and it's not less than 1. 4 divided by 5 gives us a remainder of 4, and 1 divided by 5 gives us a remainder of 1. Since these values are different, we run that if statement and return false.

When we get to turn 6, n is 6, a is 1 and b is 5. Again the first if condition fails as 5 is not equal to 0. 5 is greater than 0 so the first condition of the second if passes, then 6 is not less than 1, 6 is not less than 1, 6 divided by 5 gives a remainder of 1 and 1 divided by 5 gives a remainder of 1. The values are the same, so this part won't return false (and therefore the page condition will be met assuming that was the only condition).

Game_Troop.prototype.setupBattleEvent = function() {
    if (!this._interpreter.isRunning()) {
        if (this._interpreter.setupReservedCommonEvent()) {
            return;
        }
        var pages = this.troop().pages;
        for (var i = 0; i < pages.length; i++) {
            var page = pages[i];
            if (this.meetsConditions(page) && !this._eventFlags[i]) {
                this._interpreter.setup(page.list);
                if (page.span <= 1) {
                    this._eventFlags[i] = true;
                }
                break;
            }
        }
    }
};


This function, as the name suggest, sets up a battle event. First, we check whether _interpreter's isRunning() function returns false (or in other words, make sure an event isn't already being run). If this is the case, we check whether _interpreter's setupReservedCommonEvent() function returns true. If so, we return as we don't want to run a troop event page while a common event is reserved.

If these checks are passed, we set temp variable "pages" to the troop()'s pages property. Then we run a for loop with iteration variable "i" starting at 0; running for as long as i is less than the length of the pages array; and incrementing i by 1 each iteration.

Inside the loop, we set variable "page" to the "i"th element of pages. If calling meetsConditions() returns true with page passed to it, AND the "i"th element of _eventFlags is false, we call _interpreter's setup() function with page's list property passed to it, then if page's span is less than or equal to 1 (meaning it's "Battle" or "Turn" rather than "Moment") we set the "i"th element of _eventFlags to true. Then we break out of the loop (since we don't want to set up any more pages after one meets its conditions)

Game_Troop.prototype.increaseTurn = function() {
    var pages = this.troop().pages;
    for (var i = 0; i < pages.length; i++) {
        var page = pages[i];
        if (page.span === 1) {
            this._eventFlags[i] = false;
        }
    }
    this._turnCount++;
};


This function processes the battle turn increasing. As before, we assign troop()'s pages to a temporary variable called "pages" and run a for loop that goes through each page. Inside the for loop, after assigning the current page to "page", we check whether page's span is equal to 1 (meaning it's "Turn"), we set the "i"th element of _eventFlags to false, so that the event can run again next time its conditions are met.

Game_Troop.prototype.expTotal = function() {
    return this.deadMembers().reduce(function(r, enemy) {
        return r + enemy.exp();
    }, 0);
};


This function gets the total experience attainable for the party given the enemies in the troop that have been defeated. To do this, we get the defeated members by calling deadMembers() then call reduce() on that array. In the callback to reduce we're using "r" for the running total and "enemy" for the current element, with a starting value of 0. Inside the callback, we return r plus the enemy's exp() value.

Game_Troop.prototype.goldTotal = function() {
    return this.deadMembers().reduce(function(r, enemy) {
        return r + enemy.gold();
    }, 0) * this.goldRate();
};


This is more or less the same thing but for gold total rather than exp. The only difference, besides calling gold() instead of exp() in the callback function, is that the reduced total is multiplied by the return value of goldRate(), which we'll see now.

Game_Troop.prototype.goldRate = function() {
    return $gameParty.hasGoldDouble() ? 2 : 1;
};


This function gets the rate for gold, returning 2 if the hasGoldDouble() function of $gameParty returns true, and 1 otherwise. This is needed to account for the "Double Gold" party trait.

Game_Troop.prototype.makeDropItems = function() {
    return this.deadMembers().reduce(function(r, enemy) {
        return r.concat(enemy.makeDropItems());
    }, []);
};


Finally, this function does something similar for the possible enemy drops. Rather than 0, our initial value is an empty array. In the callback function, we're returning r concatenated with the return value from enemy's makeDropItems() function, which in the end will give us an array of all items dropped by all dead enemies.

And that brings our exploration of Game_Party and Game_Troop to an end! Thanks as always for joining me. Hopefully not long from now, we'll dive into Game_Map and beyond!

Until next time.