Indie Dev

Hello Guest!. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, sell your games, upload content, as well as connect with other members through your own private inbox!

Basic knowledge to the default RMMV battle flow implementations

DoubleX

Adventurer
Xy$
1.12
This topic aims to share my understandings to the default RMMV battle flow implementations, and I'll only share basic stuffs which are simplified here. However, you're still assumed to have at least:
- Little javascript coding proficiency(having written at least few rudimentary Javascript codes)
- Basic knowledge to the default RMMV battle flow on the user level(At least you need to know what's going on on the surface when playing it as a player)

Flow Overview
Consider the below 2 simplified flowcharts(in the same picture):
The one at the lower right corner will be run per frame, and for each frame, will decide if the other flowchart will be run at that frame.
This flow will only be run when the battle isn't in the Action Input Phase, meaning that part of the other flowchart will always be run when the battle's in that phase.
Basically, the other flowchart(except the parts for the Action Input Phase, which will always be run when the battle's in that phase) will only be stopped when the battle should end, which is due to being aborted(event commands, successful escape attempts, Special Effect Escape, etc), lost(all troop members are dead) or won(all party members are dead).

That other flowchart mainly consists of the Action Input Phase, Action Execution Phase and their transitions, Turn Start(Action Input Phase to Action Execution Phase) and Turn End Phase(Action Execution Phase to Action Input Phase), although there are something else that's relatively minor to the battle flow(Battle Initialization Phase, which starts from Battle Start and ends with Start Input, is also the case as it only affects the battle flow by determining if the battle will starts with preemptive/surprise, which is relatively minor to the battle flow.).

Action Input Phase
It basically starts from Start Input and ends with Turn Start. It's the phase where players can either try to escape the battle, or input actions for all inputable actors.
An actor's inputable if he/she/it's alive and has no Restrictions enforced by states and no special flag Auto Battle.
The Action Input Phase will proceed as follows:
1. A party command window shows, letting players choose between fight and escape.
2a. If they choose to escape, then the party will either escape if the attempt's successful, or wasted its turn otherwise, as players won't be able to input anything for that turn. If the party escapes, the battle will proceed to the Abort Battle Phase before actually quitting the battle.
2b. If they choose to fight, then they can input actions for all inputable actors.
3. They'll first input actions for the 1st inputable party member, then the 2nd, 3rd, and finally the last inputable party member. Players won't be able to break that input sequence.
4. Each alive actor has his/her/its own action slots, with its size determined by his/her/its Action Times+. As long as he/she/it's also inputable, players will first input actions for the 1st action slot, then the 2nd, 3rd, ..., and finally the last action slot. Again, players won't be able to break that input sequence.
5. When inputting actions for inputable actors, players can either use the ok or the cancel command. Using the former and the latter eventually proceeds to the Next Command and Prior Command respectively, which eventually proceeds to the Action Execution Phase and the party command window respectively.

Turn Start
It basically starts from Turn Start and ends with Update Turn. It's the transition from Action Input Phase to Action Execution Phase.
The Turn Start will mainly do the following impacting the battle flow:
1. Changes the phase from Action Input Phase to Action Execution Phase.
2. Runs all battle events which should be run when specified number of turns are passed.
3. Increases the battle turn count by 1.
4. Makes an Action Order Queue including all battlers. The higher the speed of the battler at that moment, the more up front that battler will be positioned in that queue. The speed of the battler's determined by his/her/its agi and all his/her/its inputted actions' speed.

Action Execution Phase
It basically starts from Update Turn and ends with Turn End. It's the phase where all battlers' actions are processed and all valid ones are executed.
An action is said to be valid upon execution if it's forced or its user can use it upon execution.
The Action Execution Phase will proceed as follows:
1. The most up front battler in the Action Order Queue will become the Action Execution Subject and process all of his/her/its actions, and then leave that queue. This process repeats until the Action Order Queue's empty.
2. As long as the Action Execution Subject's movable(unless he/she/it's using force actions), he/she/it'll first process the action in his/her/its 1st action slot, then the one in 2nd, 3rd, ..., and finally the one in the last action slot. All actions that are valid at that moment will be executed.
3. When beginning to execute an action, its Action Target Queue, which stores all targets of the action, will be made, then the action's global stuffs(reserve common events to be executed at the next frame if the skill/item has Effect Common Event) will be applied. Bear in mind that the Currently Executing Action will be set as that action before removing the Action Execution Subject's current action.
4. The 1st target in the current Action Target Queue will leave that queue, then the Currently Executing Action will be invoked on that target. This process repeats until that Action Target Queue's empty.
On a side note: The battle flow and damage flow are completely separation of concerns, meaning the latter won't be covered here.
5. When the current Action Target Queue's empty, the Currently Executing Action will be ended and the next action of the Action Execution Subject will be processed.
6. When the last action of that battler's executed, action end processing will be done, and stuffs needing to happen upon all actions' ends(removing state with Auto-removal Timing Action and 0 remaining turns, remove buffs with 0 remianing turn, setc) will be triggered there.
7. When the Action Order Queue's empty, the battle will proceed to the Turn End Phase.

Turn End Phase
It basically starts from Turn End and ends with Start Input. It's the phase serving as the transition from Action Execution Phase to Action Input Phase.
The Turn End Phase will mainly do the following impacting the battle flow:
1. Changes the phase from Action Execution Phase to Turn End Phase.
2. Runs all battlers' stuffs that should be run now(Regenerate hp/mp/tp, updating state turns with Auto-removal Timing Turn and removing those with 0 remaining turns, updating buff turns, etc).
3. Runs all battle events which should be run now.
4. Changes the phase from Turn End Phase to Action Input Phase.

Related Codes
For the basics, you just need to know which functions are directly involved in which parts of the battle flow. You just need to know what they do locally, but not how they work locally, let alone how they work as a whole.

Frame Update
Scene_Battle.prototype.updateBattleProcess determines if the battle flow(except the Action Input Phase part) should be run(which will never run in the Action input Phase):
JavaScript:
Scene_Battle.prototype.updateBattleProcess = function() {
    if (!this.isAnyInputWindowActive() || BattleManager.isAborting() ||
            BattleManager.isBattleEnd()) {
        BattleManager.update();
        this.changeInputWindow();
    }
};
BattleManager.update determines if the battle flow(except the Action Input Phase part) should be run:
JavaScript:
BattleManager.update = function() {
    if (!this.isBusy() && !this.updateEvent()) {
        switch (this._phase) {
        case 'start':
            this.startInput();
            break;
        case 'turn':
            this.updateTurn();
            break;
        case 'action':
            this.updateAction();
            break;
        case 'turnEnd':
            this.updateTurnEnd();
            break;
        case 'battleEnd':
            this.updateBattleEnd();
            break;
        }
    }
};
BattleManager.updateEvent updates forced actions, events and checks if the battle should be aborted:
JavaScript:
BattleManager.updateEvent = function() {
    switch (this._phase) {
    case 'start':
    case 'turn':
    case 'turnEnd':
        if (this.isActionForced()) {
            this.processForcedAction();
            return true;
        } else {
            return this.updateEventMain();
        }
    }
    return this.checkAbort();
};
BattleManager.updateEventMain updates events and checks if the battle should be ended:
JavaScript:
BattleManager.updateEventMain = function() {
    $gameTroop.updateInterpreter();
    $gameParty.requestMotionRefresh();
    if ($gameTroop.isEventRunning() || this.checkBattleEnd()) {
        return true;
    }
    $gameTroop.setupBattleEvent();
    if ($gameTroop.isEventRunning() || SceneManager.isSceneChanging()) {
        return true;
    }
    return false;
};

Action Input Phase
BattleManager.startInput changes the phase to the Action Input Phase:
JavaScript:
BattleManager.startInput = function() {
    this._phase = 'input';
    $gameParty.makeActions();
    $gameTroop.makeActions();
    this.clearActor();
    if (this._surprise || !$gameParty.canInput()) {
        this.startTurn();
    }
};
BattleManager.isInputting checks if the battle's in the Action Input Phase:
JavaScript:
BattleManager.isInputting = function() {
    return this._phase === 'input';
};
Scene_Battle.prototype.changeInputWindow setups the party/actor command window when the battle's in the Action Input Phase or closes them all otherwise:
JavaScript:
Scene_Battle.prototype.changeInputWindow = function() {
    if (BattleManager.isInputting()) {
        if (BattleManager.actor()) {
            this.startActorCommandSelection();
        } else {
            this.startPartyCommandSelection();
        }
    } else {
        this.endCommandSelection();
    }
};
Scene_Battle.prototype.startPartyCommandSelection setups the party command window:
JavaScript:
Scene_Battle.prototype.startPartyCommandSelection = function() {
    this.refreshStatus();
    this._statusWindow.deselect();
    this._statusWindow.open();
    this._actorCommandWindow.close();
    this._partyCommandWindow.setup();
};
Scene_Battle.prototype.commandFight implements the fight command:
JavaScript:
Scene_Battle.prototype.commandFight = function() {
    this.selectNextCommand();
};
Scene_Battle.prototype.commandEscape implements the escape command:
JavaScript:
Scene_Battle.prototype.commandEscape = function() {
    BattleManager.processEscape();
    this.changeInputWindow();
};
BattleManager.processEscape runs the party escape attempt and determines if it succeed:
JavaScript:
BattleManager.processEscape = function() {
    $gameParty.removeBattleStates();
    $gameParty.performEscape();
    SoundManager.playEscape();
    var success = this._preemptive ? true : (Math.random() < this._escapeRatio);
    if (success) {
        this.displayEscapeSuccessMessage();
        this._escaped = true;
        this.processAbort();
    } else {
        this.displayEscapeFailureMessage();
        this._escapeRatio += 0.1;
        $gameParty.clearActions();
        this.startTurn();
    }
    return success;
};
Scene_Battle.prototype.startActorCommandSelection setups the actor command window:
JavaScript:
Scene_Battle.prototype.startActorCommandSelection = function() {
    this._statusWindow.select(BattleManager.actor().index());
    this._partyCommandWindow.close();
    this._actorCommandWindow.setup(BattleManager.actor());
};
Scene_Battle.prototype.selectNextCommand, BattleManager.selectNextCommand and Game_Actor.prototype.selectNextCommand implements the Next Command:
JavaScript:
Scene_Battle.prototype.selectNextCommand = function() {
    BattleManager.selectNextCommand();
    this.changeInputWindow();
};
JavaScript:
BattleManager.selectNextCommand = function() {
    do {
        if (!this.actor() || !this.actor().selectNextCommand()) {
            this.changeActor(this._actorIndex + 1, 'waiting');
            if (this._actorIndex >= $gameParty.size()) {
                this.startTurn();
                break;
            }
        }
    } while (!this.actor().canInput());
};
JavaScript:
Game_Actor.prototype.selectNextCommand = function() {
    if (this._actionInputIndex < this.numActions() - 1) {
        this._actionInputIndex++;
        return true;
    } else {
        return false;
    }
};
Scene_Battle.prototype.selectPreviousCommand, BattleManager.selectPreviousCommand and Game_Actor.prototype.selectPreviousCommand implements the Prior Command:
JavaScript:
Scene_Battle.prototype.selectPreviousCommand = function() {
    BattleManager.selectPreviousCommand();
    this.changeInputWindow();
};
JavaScript:
BattleManager.selectPreviousCommand = function() {
    do {
        if (!this.actor() || !this.actor().selectPreviousCommand()) {
            this.changeActor(this._actorIndex - 1, 'undecided');
            if (this._actorIndex < 0) {
                return;
            }
        }
    } while (!this.actor().canInput());
};
JavaScript:
Game_Actor.prototype.selectPreviousCommand = function() {
    if (this._actionInputIndex > 0) {
        this._actionInputIndex--;
        return true;
    } else {
        return false;
    }
};

Turn Start
BattleManager.startTurn changes the battle from the Action Input Phase to Action Execution Phase:
JavaScript:
BattleManager.startTurn = function() {
    this._phase = 'turn';
    this.clearActor();
    $gameTroop.increaseTurn();
    this.makeActionOrders();
    $gameParty.requestMotionRefresh();
    this._logWindow.startTurn();
};
BattleManager.makeActionOrders makes the Action Order Queue:
JavaScript:
BattleManager.makeActionOrders = function() {
    var battlers = [];
    if (!this._surprise) {
        battlers = battlers.concat($gameParty.members());
    }
    if (!this._preemptive) {
        battlers = battlers.concat($gameTroop.members());
    }
    battlers.forEach(function(battler) {
        battler.makeSpeed();
    });
    battlers.sort(function(a, b) {
        return b.speed() - a.speed();
    });
    this._actionBattlers = battlers;
};

Action Execution Phase
BattleManager.updateTurn sets the Action Execution Subject as the 1st battler in the Action Order Queue:
JavaScript:
BattleManager.updateTurn = function() {
    $gameParty.requestMotionRefresh();
    if (!this._subject) {
        this._subject = this.getNextSubject();
    }
    if (this._subject) {
        this.processTurn();
    } else {
        this.endTurn();
    }
};
BattleManager.processTurn picks the current action of the Action Execution Subject, prepares that action and checks its validity if an action's found(then remove that action regardless of its validity), or ends all actions of that battler and changes the Action Execution Subject:
JavaScript:
BattleManager.processTurn = function() {
    var subject = this._subject;
    var action = subject.currentAction();
    if (action) {
        action.prepare();
        if (action.isValid()) {
            this.startAction();
        }
        subject.removeCurrentAction();
    } else {
        subject.onAllActionsEnd();
        this.refreshStatus();
        this._logWindow.displayAutoAffectedStatus(subject);
        this._logWindow.displayCurrentState(subject);
        this._logWindow.displayRegeneration(subject);
        this._subject = this.getNextSubject();
    }
};
BattleManager.startAction makes the Action Target Queue for the currently executing action, sets the Currently Executing Action as the Action Execution Subject's current action and apply global stuffs of that action:
JavaScript:
BattleManager.startAction = function() {
    var subject = this._subject;
    var action = subject.currentAction();
    var targets = action.makeTargets();
    this._phase = 'action';
    this._action = action;
    this._targets = targets;
    subject.useItem(action.item());
    this._action.applyGlobal();
    this.refreshStatus();
    this._logWindow.startAction(subject, action, targets);
};
BattleManager.updateAction removes the 1st target in the Action Target Queue and invokes the currently executing action to that target:
JavaScript:
BattleManager.updateAction = function() {
    var target = this._targets.shift();
    if (target) {
        this.invokeAction(this._subject, target);
    } else {
        this.endAction();
    }
};
BattleManager.endAction ends the execution of the currently executing action:
JavaScript:
BattleManager.endAction = function() {
    this._logWindow.endAction(this._subject);
    this._phase = 'turn';
};

Turn End Phase
BattleManager.endTurn changes the battle form Action Execution Phase to Turn End Phase and runs all battler stuffs that should be run upon turn end:
JavaScript:
BattleManager.endTurn = function() {
    this._phase = 'turnEnd';
    this._preemptive = false;
    this._surprise = false;
    this.allBattleMembers().forEach(function(battler) {
        battler.onTurnEnd();
        this.refreshStatus();
        this._logWindow.displayAutoAffectedStatus(battler);
        this._logWindow.displayRegeneration(battler);
    }, this);
};
BattleManager.updateTurnEnd changes the battle from Turn End Phase to Action Input Phase:
JavaScript:
BattleManager.updateTurnEnd = function() {
    this.startInput();
};

That's all for now. I hope this can help you grasp these basic knowledge. For those comprehending the default RM<V battle flow implementations, feel free to correct me if there's anything wrong :D

For those wanting to have a solid understanding to the default RMMV battle flow implementations, I might open a more advanced topic for that later :)
 

MinisterJay

Administrator
Staff member
Administrator
Awesome tutorial. I loved how you used a flow chart diagram to explain the conditional branches (if/thens) of the flow. By using multiple
s and
s within
s it allows the reader to assimilate the information is tiny bits instead of large chunks.
 
Top