Basic knowledge on writing ATB(Active Time Battle) system plugins

Discussion in 'Learning Javascript' started by DoubleX, Jan 10, 2016.

  1. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Related topics


    Solid understanding on writing ATB(Active Time Battle) system plugins


    Thorough Comprehension on writing ATB(Active Time Battle) system plugins(Possibly upcoming)


    Goal


    Shares my experiences and understandings on writing a basic ATB system plugin and their addons, meaning easy, simple and small cases will be the focus.


    Targeting Audiences


    Those having some battle related javascript coding proficiency(inexperienced junior plugin developer having written several easy, simple ans small battle related plugins without nontrivial bugs), and:


    1. Basic knowledge to the default RMMV battle flow implementations


    2. Basic knowledge on how the fundamental ATB system concepts work on the user level


    Contents


    I. Fundamental ATB system plugin implementation concepts


    1. ATB Frame Update


    2. ATB Wait Condition


    3. Battle Turn Clock


    4. Battler ATB Clock


    5. ATB Flow Overview


    6. Summary


    II. Differences between the default RMMV battle flow and the ATB flow


    1. Stuffs to be gone in the default RMMV battle flow to become the ATB flow


    2. Stuffs to be edited in the default RMMV battle flow to become the ATB flow


    3. Stuffs to be added in the default RMMV battle flow to become the ATB flow


    4. Summary


    III. Ways to write the easiest, simplest and smallest ATB system ever


    1. Clarification


    2. Stuffs to be implemented


    3. Writing DoubleX RMMV Minimalized ATB from scratch


    4. Summary


    IV. Ways to write a basic ATB system having all core functionalities


    1. Clarification


    2. Stuffs to be implemented


    3. Writing DoubleX RMMV Popularized ATB Core from scratch


    4. Summary


    V. Comparisons between DoubleX RMMV Minimalized ATB and DoubleX RMMV Popularized ATB Core


    1. Shortcuts taken in writing DoubleX RMMV Minimalized ATB


    2. Testing various ATB Wait Conditions


    3. Summary


    VI. Ways to write an easy, simple and small ATB system addon


    1. The Core Addon Approach basics


    2. Interactions between the core and an addon


    3. Some easy, simple and small ATB system addon examples


    4. Summary


    VII. Ways to study some other ATB system plugins


    1. Ellye's ATB(also includes CTB option)


    2. YEP.24 - Battle System - Active Time Battle


    3. Summary


    VIII. Summary


    With the framework's set, let's get started. I'm going to talk about Fundamental ATB system plugin implementation concepts:

    Spoiler



    ATB Frame Update



    Spoiler



    While you know the battle system is run per frame, you may wonder what's the difference between Battle Frame Update and ATB Frame Update.


    Basically, the difference is that, the former will always run while the latter won't run when the ATB Wait Condition's met, and the former's universal to all battle systems while the latter's unique to ATB systems.


    The latter mainly does the following(these details will be explained later):


    1. Runs each battler's Battler ATB Clocks


    2. Runs the Battle Turn Clock


    3. Set a battler that becomes able to execute actions to be the Action Execution Subject when there's none


    4. Setups an actor that becomes inputable when there's none


    5. Updates all actors' atb bars(unless you don't even implement actor atb bars)


    Bear in mind that the action execution itself's updated by the Battle Frame Update but not the ATB Frame Update.



    ATB Wait Condition

    Spoiler



    Unless one can flawlessly implement executing more than 1 actions at the same time, no ATB system can have absolutely no waits, meaning every ATB system has an ATB Wait Condition determining if a Battle Frame Update should run an ATB Frame Update.


    Usually, the ATB Wait Condition's 1 of the below:


    1. Wait when the ATB Frame Update can be run without inducing bugs(situations like having a message displayed can induce bugs if the ATB Frame Update's run)


    2. Wait when an action's animation's playing as well


    3. Wait when an actor's picking the action's targets as well


    4. Wait when an actor's picking a skill/item as well


    5. Wait when an actor's can input actions as well



    Battle Turn Clock

    Spoiler



    Unless you're going to abandon the whole battle turn number concept entirely, you'll need a global atb clock to run it, as its implementation used by the default RMMV battle system won't work in any ATB system anymore. It's because both the party and troop have the action input phase and action execution phase in battles, and its turn number mechanics's built around it, while no ATB system will ever divide a battle that way(details will be explained in Differences between the default RMMV battle flow and the ATB flow). As the battle turn clock must be run by the battle itself, a Battle Turn Clock will be needed to run it.


    The Battle Turn Clock normally uses 1 one of the below 2 units:


    - Frames. Each battle turn consists of a specific number of ATB Frame Updates


    - Actions. Each battle turn consists of a specific number of executed actions


    For both units, when that specific number's reached in a battle turn, the battle turn number will be increased by 1 and the Battle Turn Clock will be reset.



    Battler ATB Clock

    Spoiler



    In an ATB system, each battler has an independent action input phase and action execution phase, as well as an ATB refill phase, meaning each battler needs to have an independent Battler ATB Clock.


    The Battler ATB Clock's usually used in 1 one of the below 2 ways:


    - Checks if the battler should be able to act. When it's true, that battler can input then execute actions, and the Battler ATB Clock will be reset afterwards; When it's false, that battler will need to wait until the aforementioned check returns true.


    - Checks if the battler's inputted actions should be executed. When it's true, that action will be executed and then that battler can input actions again; When it's false, that action will need to wait until the aforementioned check returns true.


    For both ways, the battler's speed must be used to run that Battler ATB Clock, as the essence of an atb system is to make the some battlers act faster and more frequently than the others. Using the default RMMV settings, the battler's speed's determined by that battler's agi. This also implies that the skill/item invocation speed's mostly useless in the 1st way of using the battler atb clock.



    ATB Flow Overview

    Spoiler



    Consider the below simplified flowchart:



    Spoiler



    [​IMG]

    Basically, the ATB system only has 2 battle phase - Not Executing Actions and Executing Actions, and 3 battler phase per battler - Refilling ATB, Full ATB, Executing Actions.


    The ATB system battle phase's divided that way because of the below 2 of the most fundamental Scene_Battle based foundations(too advanced to be explained here):


    1. Only 1 battler, the Action Execution Subject, can execute actions at a time.


    2. Only 1 action, the Currently Executing Action, can be executed at a time.


    So it's vital to check if any battler's already executing actions before letting any other battler that can execute actions to execute them.


    On a side note: Some addons need to add some more battler phases in order to work, but those will be covered later.


    The below explains how the simplified ATB flow works:


    Battle Frame Update

    Spoiler



    It'll always run per frame(technically there are some exceptions but let's ignore them now for the sake of simplicity) and is always run by Scene_Battle(It can't be run by BattleManager.update or the ATB Wait Conditions will be restricted to wait when an actor's inputable, which will be explained in Differences between the default RMMV battle flow and the ATB flow).


    The Battle Frame Update will first check if the ATB Wait Condition's met.


    If that's the case, the ATB Frame Update will be run.


    Otherwise the Battle Frame Update will continue to execute any executing action.


    If the action execution reaches its end, the Action Execution Subject's Battler ATB Clock will be reset, and the battler will be marked as Unactable(impossible to be the Action Execution Subject right now) and shift from Executing Actions to Refilling ATB(And the battle phase will shift from Executing Actions to Not Executing Actions as well).

    ATB Frame Update

    Spoiler



    It won't run if the ATB Wait Condition's met.


    It'll first update each battler's Battler ATB Clock.


    If a battler's Battler ATB Clock becomes full, that battler will make actions, be marked as Actable(possible to be the Action Execution Subject right now), and shift from Refilling ATB to Full ATB.


    Then the ATB Frame Update will check if any action's executing. If that's not the case, exactly 1 Actable battler that has finished inputting actions will become the Action Execution Subject and shift from Full ATB to Executing Actions(And the battle phase will shift from Not Executing Actions to Executing Actions as well). For either case, the action will begin/continue to be executed.


    If the action execution reaches its end, the Action Execution Subject's Battler ATB Clock will be reset, and the battler will be marked as Unactable(impossible to be the Action Execution Subject right now) and shift from Executing Actions to Refilling ATB(And the battle phase will shift from Executing Actions to Not Executing Actions as well).


    Recall that the action execution itself's updated by the Battle Frame Update but not the ATB Frame Update. The latter just marks that the former needs to/need not update action executions by manipulating the battle phase.


    The ATB Frame Update will then check if any actor becomes inputable, and will setups the actor command window for that actor if that's the case and no other actors are inputable.


    Finally, the ATB Frame Update will update all actors' atb bars. Nothing else will be updated in most cases to boost time performance(too advanced to be explained here).







    Summary

    Spoiler



    1. The ATB system consists of 2 battle phase - Not Executing Actions and Executing Actions, and 3 battler phase per battler - Refilling ATB, Full ATB, Executing Actions.


    2. The Battle Frame Update, which updates the action execution itself, will always be run per frame, while the ATB Frame Update won't run when the ATB Wait Condition's met.


    3. The ATB Frame Update runs each battler's Battler ATB Clocks and the Battle Turn Clock, and sets an Actable battler that has finished inputting actions to be the Action Execution Subject when there's none.


    4. A battler becomes Actable when that battler shifts from Refilling ATB to Full ATB, becomes the Action Execution Subject when that battler shifts from Full ATB to Executing Actions, and becomes Unactable when that battler shifts from Executing Actions to Refilling ATB, which occurs when that battler has finished executing the Currently Executing Action.







    That's all for now. The remaining parts will be covered later :)
     
    Last edited by a moderator: Apr 10, 2016
    #1
  2. Moogle_X

    Moogle_X Veteran Veteran

    Messages:
    157
    Likes Received:
    310
    First Language:
    English
    I always love topic like this! Definitely going to keep tab on this bad boy. BD
     
    #2
  3. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Now I'm going to talk about Differences between the default RMMV battle flow and the ATB flow. But before that, let's cite the simplified default RMMV battle flowchart and the simplified ATB flowchart:


    Simplified default RMMV battle flowchart -

    Spoiler



    [​IMG]

    Simplified ATB flowchart -

    Spoiler



    [​IMG]



    Stuffs to be gone in the default RMMV battle flow to become the ATB flow

    Spoiler



    BattleManager.update



    Spoiler



    Recall that BattleManager.update, which run the Battle Frame Update for the default RMMV battle system, will only be called when the battle isn't in the Action Input Phase(too advanced to be explained here).


    This has to be gone completely unless the ATB Wait Condition will always be true whenever an actor's inputable.

    Start Input

    Spoiler



    Recall that Start Input in the default RMMV battle system will ask all battlers to make actions upon the start of the Action Input Phase.


    This has to be gone completely because:


    1. The ATB system has no Action Input Phase at all.


    2. For each battler, make actions must be only caused by that battler becoming Actable, which is absolutely unrelated to what the battle phase is.


    If making actions for a battler can occur at any timing that's not controlled by that battler, the battler phase can be severely disrupted, as the previously made actions can be overridden with the new ones even before the formers are executed at all.

    Make Action Order Queue/Get Next Subject

    Spoiler



    Recall that the Action Order Queue in the default RMMV battle system will be made upon the start of the Action Execution Phase.


    All these have to be gone completely because:


    1. The ATB system has no Action Execution Phase at all, as that phase's meant to process all inputted actions in the Action Input Phase as a single batch by making an Action Order Queue, while ATB always processes inputted actions in real time.


    It also means Get Next Subject has to be gone, as its sole purpose is to extract the 1st battler from the Action Order Queue.


    The Action Order Queue itself, however, can be edited instead of having to be gone altogether(more on that below).







    Stuffs to be edited in the default RMMV battle flow to become the ATB flow

    Spoiler



    Turn Start/Turn End



    Spoiler



    Recall that in the default RMMV battle system, Turn Start is the transition from the Action Input Phase to the Action Execution Phase, while Turn End is the opposite transition.


    All these have to be edited because:


    1. The ATB system has neither Action Input Phase nor Action Execution Phase.


    Neither of these can be gone completely unless the ATB system's going to abandon the whole battle turn concept altogether.


    Also, recall that in the ATB system, a Battle Turn Clock will be used to run the battle turn.


    All these mean that Turn Start and Turn End in the ATB system's essentially the same thing, as the former will come immediately after the latter, which comes when the Battle Turn Clock's reset due to reaching its maximum. These also mean that Turn Start can't be triggered by Next Command and Turn End can't be triggered by failing to find a new Action Execution Subject.


    Technically speaking, however, both Turn Start and Turn End will need to exist to preserve a vital Scene_Battle based battle system(having battle turn) invariant - A Turn Start always lead to a Turn End before leading to another Turn Start, and a Turn End always lead to a Turn Start before leading to another Turn End(too advanced to be explained here).

    Action Order Queue

    Spoiler



    As mentioned, the Action Order Queue can be edited instead of having to be gone altogether.


    It can(but not required) mark which battlers are Actable, thus easing the checking when finding a battler to become the new Action Execution Subject and setting an inputable actor's command window, as both Action Execution Subject and inputable actors must be Actable.







    Stuffs to be added in the default RMMV battle flow to become the ATB flow

    Spoiler



    ATB Wait Condition



    Spoiler



    Recall that the ATB Wait Condition determines if the ATB Frame Update should be run for each Battle Frame Update.


    This has to be added because:


    1. The default RMMV battle system has neither ATB Wait Condition nor anything that can be edited to be just that, unless it's always true whenever an actor's inputable.


    2. Without it, the ATB Frame Update would always run for each Battle Frame Update, even in cases where doing so could cause the game to crash(like while a message's displaying).

    ATB Frame Update

    Spoiler



    Recall that the ATB Frame Update runs each battler's Battler ATB Clock, the Battle Turn Clock(unless the ATB system abandons the whole battle turn concept altogether), setups a new Action Execution Subject, setups a newly inputable actor and updates all actors' ATB bars(unless the ATB system isn't going to implement that).


    This has to be added because:


    1. The default RMMV battle system has neither ATB Frame Update nor anything that can be edited to be just that.


    2. Without it, the ATB system wouldn't even run, as all Battler ATB Clock, setups for new Action Execution Subjects and inputable actors, which are all integral to implementing all the essential ATB system concepts, must be run by the ATB Frame Update(explained in Ways to write the easiest, simplest and smallest ATB system ever).

    Battler ATB Clock/Reset Battler ATB

    Spoiler



    Recall that the Battler ATB Clock uses the battler's speed to control the time needed for that battler to stay in Refilling ATB before transiting to Full ATB, and Reset Battler resets the Action Execution Subject's Battler ATB Clock and causes that battler to transit from Executing Actions to Refilling ATB.


    All these have to be added because:


    1. The default RMMV battle system has neither Battler ATB Clock, Reset Battler ATB, nor anything that can be edited to be just either of those.


    2. Without both of them, the essence of the whole ATB system concept - Letting speedy battlers act more frequently, couldn't be implemented at all, causing the entire ATB system to be effectively meaningless and pointless.

    Setup Newly Inputable Actor

    Spoiler



    Recall that Setup Newly Inputable Actor, which setups the actor command window for the newly inputable actor, will be called whenever such actor's found while running the ATB Frame Update.


    This has to be added because:


    1. The default RMMV battle system has neither Setup Newly Inputable Actor in real time(it's always done as a single batch at the start of the Action input Phase), nor anything that can be edited to be just that.


    2. Without it, players wouldn't be able to input actions for inputable actors, effectively taking all control and freedom from those players in the ATB system, and hence causing it to be useless.

    On a side note: both the Battle Turn Clock and updates of all actors' ATB bars should be added too, but strictly speaking, they're not absolutely needed in the ATB system, even though such ATB systems would be generally considered mostly broken.







    Summary

    Spoiler



    The below have to be gone from the default RMMV battle system:


    1. BattleManager.update


    2. Start Input


    3. Make Action Order Queue/Get Next Subject


    The below have to be edited from the default RMMV battler system to be used in the ATB system:


    1. Turn Start/Turn End to be called by the Battle Turn Clock instead


    2. Action Order Queue to mark all Actable battlers instead(not required)


    The below has to be added to the ATB system:


    1. ATB Wait Condition


    2. ATB Frame Update


    3. Battler ATB Clock/Reset Battler ATB


    4. Setup Newly Inputable Actor(in real time instead of as a single batch)


    (Battle Turn clock and updates of all actors' ATB bars should be added too)



    P.S.: You might wonder why Next Command/Prior Command can remain intact(neither gone nor edited) during the transformation from the default RMMV battle system to the ATB system, but it's too advanced to be covered here :)
     
    Last edited by a moderator: Feb 3, 2016
    #3
  4. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Now I'm going to talk about Ways to write the easiest, simplest and smallest ATB system ever, but before that, let's preview what DoubleX RMMV Minimalized ATB can deliver:


    https://www.youtube.com/watch?v=zkrXZTQnjFc


    Clarification

    Spoiler





    As it's attempted to be the easiest, simplest and smallest ATB system ever, it'll only have the least amount of control and freedom needed by the users(complete absence of configurations that can be hardcoded, plugin calls that aren't used by the plugin implementation itself, and notetags), and 100% essential features(to ensure the plugin's still completely functional and standalone without being considered to be broken). In short, it's intended to be the minimum viable product(MVP).


    The below are some key aspects of such an ATB system demonstrating its MVP nature(besides the obvious condition that nothing optional will be implemented):


    ATB Wait Condition



    Spoiler





    It'll be hardcoded(so users can't change that without changing the plugin implementation itself) to be always wait whenever an actor's inputable, which is the loosest ATB Wait Condition(easiest to be met) in most cases(there are some exceptions like letting players to press specific keys to make the ATB Wait Condition to be always met). In general, the looser the ATB Wait Condition, the easier, simpler and smaller the ATB system can be(will be explained in Comparisons between DoubleX RMMV Minimalized ATB and DoubleX RMMV Popularized ATB Core).





    Battle Turn Clock

    Spoiler





    While it doesn't have to be implemented if the whole battle turn concept's abandoned entirely, doing so will likely lead to compatibility issues that are too severe to be ignored and too costly to be solved.


    Nevertheless, its unit, which is typically in the number of ATB Frame Updates or that of executed actions, can still be hardcoded. Here the former unit will be used to reduce the chance of complete halt of battles in cases where no battlers can execute actions forever. The only paremeters that can't be hardcoded(and thus has to be set by users) is the number of ATB Frame Updates constituting a battle turn.



    Battler ATB Clock

    Spoiler





    It'll be used in the below way:


    - Checks if the battler should be able to act. When it's true, that battler can input then execute actions, and the Battler ATB Clock will be reset afterwards; When it's false, that battler will need to wait until the aforementioned check returns true.


    It's because its implementation will be easier, simpler and smaller than that of the other way(will be demonstrated in Ways to write a basic ATB system having all core functionalities).







    Stuffs to be implemented

    Spoiler





    Besides those covered in Fundamental ATB system plugin implementation concepts and , the below stuffs also have to be implemented:


    Feature Set



    Spoiler








    Spoiler








    * @param max_turn_unit
    * @desc Sets the maximum battle turn clock unit as max_turn_unit
    * max_turn_unit must return a Number and should return a positive one
    * @default 5
    *
    * @param actor_name_w
    * @desc Sets the maximum width allocated for actor name display on the status
    * window as actor_name_w
    * actor_name_w must return a positive Number
    * @default 123
    *
    * @param actor_icon_ox
    * @desc Sets the actor icon display area x offset relative to the actor name
    * display area on the status window as actor_icon_ox
    * actor_icon_ox must return a positive Number
    * @default 6
    *
    * @param hp_bar_ox
    * @desc Sets the actor hp bar display area x offset relative to the actor icon
    * display area on the status window as hp_bar_ox
    * hp_bar_ox must return a positive Number
    * @default 6
    *
    * @param hp_bar_w
    * @desc Sets the maximum width allocated for actor hp bar display on the status
    * window as hp_bar_w
    * hp_bar_w must return a positive Number
    * @default 87
    *
    * @param mp_bar_ox
    * @desc Sets the actor mp bar display area x offset relative to the actor hp
    * bar display area on the status window as mp_bar_ox
    * mp_bar_ox must return a positive Number
    * @default 6
    *
    * @param mp_bar_w
    * @desc Sets the maximum width allocated for actor mp bar display on the status
    * window as mp_bar_w
    * mp_bar_w must return a positive Number
    * @default 87
    *
    * @param tp_bar_ox
    * @desc Sets the actor tp bar display area x offset relative to the actor mp
    * bar display area on the status window as tp_bar_ox
    * tp_bar_ox must return a positive Number
    * @default 6
    *
    * @param tp_bar_w
    * @desc Sets the maximum width allocated for actor tp bar display on the status
    * window as tp_bar_w
    * tp_bar_w must return a positive Number
    * @default 87
    *
    * @param atb_bar_ox
    * @desc Sets the actor atb bar display area x offset relative to the actor
    * mp/tp bar display area on the status window as atb_bar_ox
    * atb_bar_ox must return a positive Number
    * @default 6
    *
    * @param atb_bar_w
    * @desc Sets the maximum width allocated for actor atb bar display on the
    * status window as atb_bar_w
    * atb_bar_w must return a positive Number
    * @default 87
    *
    * @param atb_c1
    * @desc Sets the 1st atb bar color as text color atb_c1
    * atb_c1 must return a valid text color code
    * atb_c1 should return the same value during the same battle to ensure
    * proper atb bar color displays
    * @default 7
    *
    * @param atb_c2
    * @desc Sets the 2nd atb bar color as text color atb_c2
    * atb_c2 must return a valid text color code
    * atb_c2 should return the same value during the same battle to ensure
    * proper atb bar color displays
    * @default 8







    All configurations besides the max Battle Turn Clock unit, are to let users to rearrange the actor info display in the status window. Recall that in the default RMMV battle status window, each actor takes a row, with his/her/its name displayed on the left, followed by state icons, hp, mp and finally tp bars displayed on the right. To change as little as possible and to ensure coherent info display, the atb bars should be displayed at the right of the tp bars. As each of those info displays(including the newly added atb bars) takes some space in the row used by its actor and there are spacings between them while the total length of that row remains unchanged, adding the atb bar means all those space and spacing allocations have to be reconfigured by the users.


    Starting ATB Value

    Spoiler





    Unless the whole preemptive/surprise concept's abandoned entirely, which could lead to unnecessary yet significant compatibility issues, the Starting ATB Value should depend on whether the battle starts normally, preemptively or under surprise.


    Besides the obvious case that its must be 0% for unmovable battlers, the below setup should lead to the easiest, simplest and smallest implementation:


    Normal - 0% for all battlers


    Preemptive - 100% for all actors and 0% for all enemies


    Surprise - 0% for all actors and 100% for all enemies


    Also, if a battler has 100% Starting ATB Value, that battler should also immediately make actions upon battle start to avoid unlikely bugs(basically about cases where battlers starting preemptively not being able to act before the rest).



    Update state turns

    Spoiler





    In the default RMMV battle system, states with both Auto-removal Timing Action End and Turn End update their turns upon turn end. The difference between them is that, when their remaining turns become 0, those of the former will be removed right after the battler has executed all actions, while those of the latter will be removed upon turn end.


    Such setup makes sense in the default RMMV battle system as each battler can only have 1 moment where all that battler's actions become completely executed per turn, while this invariant doesn't hold in any ATB system anymore as the Battle Turn Clock and each battler's Battler ATB Clock are completely independent of each other. So in order to keep the Auto-removal Timing Action End making sense in ATB systems, the turns of such states should be updated whenever their battlers become finished executing all their actions.



    Battler inputability check

    Spoiler





    In the default RMMV battle system, a battler's inputable if he/she/it's in the battle and not having any state Restriction nor the Autobattle flag.


    In ATB systems, such inputability check won't be enough, as it's used in the below ways:


    - Checks if there are inputable actors


    - Setups an inputable actor's command window


    - Setups a new Action Execution Subject


    For enemies, they'll immediately input all their action slots whenever they become Actable, meaning that they're never inputable.


    For actors, if they're all inputable using the default RMMV check while they're all still Refilling ATB, they'll be regarded as inputable in the ATB system. Such result's obviously false, however. To ensure such error won't occur, the inputability check has to check whether the actor has empty action slots as well, which also ensure that actors having inputted all actions slots are regarded as inputable in order to be the Action Execution Subject, which can't be inputable.



    Failed party escape attempts

    Spoiler





    In the default RMMV battle system, failed party escape attempts will lead to an immediate Turn Start, shifting the battle from the Action Input Phase to the Action Execution Phase.


    Such setup has to be gone in ATB systems, however, as it's neither Action Input Phase nor Action Execution Phase, and it'd cause Turn Start, which should be triggered by the Battle Turn Clock only, to be triggered by failed party escape attempts as well, which would lead to unstable battle turn mechanics. Instead, failed party escape attempts should shifts all actors from their current phases to Refilling ATB with 0% ATB value, otherwise players can simply repeatedly try to escape the battle immediately afterwards until they succeed, making party escape attempt failures meaningless, pointless and useless.







    Writing DoubleX RMMV Minimalized ATB from scratch

    Spoiler





    Recall that ATB system only has 2 battle phases - Executing Actions and Not Executing Actions, meaning that BattleManager._phase will be either 'turn' referring to the former or 'action' referring to the latter.


    The below are all the key parts of the plugin implementation:


    Battle Frame Update



    Spoiler





    Scene_Battle.prototype.updateBattleProcess in the default RMMV codebase calls BattleManager.update:



    Spoiler








    Scene_Battle.prototype.updateBattleProcess = function() {
    if (!this.isAnyInputWindowActive() || BattleManager.isAborting() ||
    BattleManager.isBattleEnd()) {
    BattleManager.update();
    this.changeInputWindow();
    }
    };







    While ATB systems with the loosest ATB Wait Condition can still use BattleManager.update, which is used by Ellye's ATB(also includes CTB option) and YEP.24 - Battle System - Active Time Battle, it's still abandoned entirely here in order to close the gap between DoubleX RMMV Minimalized ATB and DoubleX RMMV Popularized ATB Core. Also, as the Battle Frame Update, which still updates the action executions when the battle's Executing Actions, calls the ATB Wait Condition to check if the ATB Frame Update should be called, Scene_Battle.prototype.updateBattleProcess can be rewritten into this:

    Spoiler






    Scene_Battle.prototype.updateBattleProcess = function() { // Rewrite; Hotspot
        // Rewritten to reconstruct the action input and execution flows for atb
        if (BattleManager.isAborting() || BattleManager.isBattleEnd()) {
            return BattleManager.update();
        }
        if (BattleManager.isBusy() || BattleManager.updateEvent()) { return; }
        if (BattleManager.can_update_matb() && !this.isAnyInputWindowActive()) {
            this.update_matb();
        }
        if (BattleManager.phase === 'action') { BattleManager.updateAction(); }
        //
    }; // Scene_Battle.prototype.updateBattleProcess



    Note that BattleManager.update has to be called upon aborting battle, which is checked by BattleManager.isAborting, or battle end, which is checked by BattleManager.isBattleEnd, to handle the respective cases.


    ATB Wait Condition

    Spoiler





    As the loosest ATB Wait Condition - wait if there are inputable actors, is used, using Scene_Battle.prototype.isAnyInputWindowActive() will suffice.


    Also, BattleManager.can_update_matb, which checks for cases never letting the ATB Frame Update to be called, can be written this way:



    Spoiler







    BattleManager.can_update_matb = function() { // New; Hotspot
        return this._phase && this._phase !== 'init' && this._phase !== 'action';
    }; // BattleManager.can_update_matb





    Calling the ATB Frame Update before the battle's initialized, while updating events or the message's displaying, or after the battle's ended can lead to serious bugs(too advanced to be explained here). It also can't be called when Executing Actions due to the loosest ATB Wait Condition being used.


    On a side note: If the loosest ATB Wait Condition's used, it doesn't have to be implemented at all, as Scene_Battle.prototype.changeInputWindow already does just that. Instead, BattleManager.update can be directly rewritten to implement the ATB Frame Update.


    ATB Frame Update

    Spoiler





    As it only does all the essentials - updates each battler's Battler ATB Clock, setups new Action Execution Subject, updates the Battle Turn Clock, setups new inputable actors and updates each actor's atb bar, Scene_Battle.prototype.update_matb can be written this way:



    Spoiler







    Scene_Battle.prototype.update_matb = function() { // New; Hotspot
        BattleManager.update_matb();
        var actor_indices = BattleManager.action_battlers.filter(function(battler) {
            return battler.canInput();
        }).map(function(battler) { return battler.index(); });
        if (actor_indices.indexOf(this._statusWindow.index()) < 0) {
            this.update_matb_actor_selection(actor_indices);
        }
        this._statusWindow.refresh_matb_bars();
    }; // Scene_Battle.prototype.update_matb



    BattleManager.update_matb can be written this way:




    BattleManager.update_matb = function() { // New; Hotspot
        $gameParty.aliveMembers().forEach(function(mem) { mem.update_matb(); });
        $gameTroop.aliveMembers().forEach(function(mem) { mem.update_matb(); });
        if (this._phase !== 'action') { this.process_matb_turn(); }
        this.update_matb_turn();
    }; // BattleManager.update_matb





    The idea behind the sequence:


    - Battler data should be updated first, as both setting up a new Action Execution Subject and inputable actors need the latest battler data.


    - The Battle Turn Clock should be updated afterwards, as it should record a call of an ATB Frame Update after it's done something else.


    - The windows should be updated last, as it's possible that the info to be displayed can be changed due to the trigger of turn end which is immediately followed by Turn Start, right after updating the Battle Turn Clock.


    Battler ATB Clock

    Spoiler





    As it'll be used in the below way:


    - Checks if the battler should be able to act. When it's true, that battler can input then execute actions, and the Battler ATB Clock will be reset afterwards; When it's false, that battler will need to wait until the aforementioned check returns true.


    Game_Battler.prototype.initMembers can be extended to initialize the Battler ATB Clock and its change notification flag(the latter's too advanced to be explained here):



    Spoiler







    Game_Battler.prototype.initMembersMatb = Game_Battler.prototype.initMembers;
    Game_Battler.prototype.initMembers = function() {
        Game_Battler.prototype.initMembersMatb.call(this); // Prevents name clashes
        this.init_matb(); // Added
    }; // Game_Battler.prototype.initMembers


    Code:
    
    
    Game_Battler.prototype.init_matb = function() { // New
        this._matb_val = 0;
        this._matb_val_ch = true;
    }; // Game_Battler.prototype.init_matb
    





    Note that Game_Battler.prototype.initMembersMatb.call(this) instead of this.initMembersMatb is used to prevent infinite recursive call due to this:

    Spoiler






    /*----------------------------------------------------------------------------
    * Ensures actors added during battles are properly initialized as well
    *----------------------------------------------------------------------------*/
    Game_Actor.prototype.initMembersMatb = Game_Actor.prototype.initMembers;
    Game_Actor.prototype.initMembers = function() {
    this.initMembersMatb();
    this._matb_reserve = true; // Added
    }; // Game_Actor.prototype.initMembers



    Game_Battler.prototype.update_matb can be written this way:

    Spoiler






    BattleManager.update_matb = function() { // New; Hotspot
        $gameParty.aliveMembers().forEach(function(mem) { mem.update_matb(); });
        $gameTroop.aliveMembers().forEach(function(mem) { mem.update_matb(); });
        if (this._phase !== 'action') { this.process_matb_turn(); }
        this.update_matb_turn();
    }; // BattleManager.update_matb



    Note that:


    - A battler's ATB value can't be updated when it's full or that battler's unmovable.


    - The Battler ATB Rate is defined as that battler's agi / the max ATB value, which is 100 here, per frame.


    - That battler will only make actions when that battler's ATB value becomes full, which is the shift from Refilling ATB to Full ATB.


    - When that battler's ATB value becomes full, it'll be reset to its full value, which is 100 here, to ensure proper actor ATB bar displays.


    Setting up new Action Execution Subject

    Spoiler





    BattleManager.process_matb_turn can be written this way:



    Spoiler







    BattleManager.process_matb_turn = function() { // New; Hotspot
        this._subject = this._subject || this._actionBattlers.filter(function(b) {
            return !b.can_input_matb();
        })[0];
        if (this._subject) { this.processTurn(); }
    }; // BattleManager.process_matb_turn





    Note that:


    - An Action Execution Subject can be any uninputable Actable battler.


    - BattleManager.processTurn can be used, without being modified, to process all actions of the Action Execution Subject.


    Battle Turn Clock

    Spoiler





    BattleManager.initMembers can be extended to initialize the Battle Turn Clock upon battle start:



    Spoiler







    BattleManager.initMembersMatb = BattleManager.initMembers;
    BattleManager.initMembers = function() {
        this.initMembersMatb();
        this._matb_turn_clock = 0; // Added
    }; // BattleManager.initMembers





    BattleManager.update_matb_turn can be written this way:

    Spoiler






    BattleManager.update_matb_turn = function() { // New; Hotspot
        // The fps is assumed to be always 60
        if ((this._matb_turn_clock += 1) >= $gameSystem.matb.max_turn_unit * 60) {
            this._matb_turn_clock = 0;
            this.end_matb_turn();
        }
        //
    }; // BattleManager.update_matb_turn



    Note that the targeted number of frames per second's hardcoded to be 60 by the RMMV.


    Turn Start/Eurn End

    Spoiler





    BattleManager.end_matb_turn can be written this way:



    Spoiler







    BattleManager.end_matb_turn = function() { // New
        var members = $gameParty.battleMembers().concat($gameTroop.members());
        members.forEach(function(mem) {
            mem.onTurnEnd();
            this.refreshStatus();
            this._logWindow.displayAutoAffectedStatus(mem);
            this._logWindow.displayRegeneration(mem);
        }, this);
        $gameTroop.increaseTurn();
    }; // BattleManager.end_matb_turn





    Recall that Turn Start always comes immediately after turn end in ATB systems.


    Setup new inputable actors

    Spoiler





    BattleManager.isInputting can be rewritten from this:



    Spoiler







    BattleManager.isInputting = function() {
    return this._phase === 'input';
    };





    Into this:

    Spoiler






    /*----------------------------------------------------------------------------
     *    Keeps the appropriate input window opened if there are inputable actors
     *----------------------------------------------------------------------------*/
    BattleManager.isInputting = function() { // Rewrite; Hotspot
        return $gameParty.canInput(); // Rewritten
    }; // BattleManager.isInputting



    Note that BattleManager._phase will no longer be 'input', meaning players would never be able to input actions for inputable actors if that function remained intact.


    Such rewrite's to stop setting up the party command window when no actors are inputable:

    Spoiler






    Scene_Battle.prototype.changeInputWindow = function() {
    if (BattleManager.isInputting()) {
    if (BattleManager.actor()) {
    this.startActorCommandSelection();
    } else {
    this.startPartyCommandSelection();
    }
    } else {
    this.endCommandSelection();
    }
    };



    Scene_Battle.prototype.update_matb_actor_selection can be written this way:

    Spoiler






    // actor_indices: The indices of all currently inputable actors
    Scene_Battle.prototype.update_matb_actor_selection = function(actor_indices) {
    // New; Hotspot
        if (this._statusWindow.index() > 0) { this._statusWindow.deselect(); }
        if (actor_indices.length <= 0) { return; }
        BattleManager.actor_index = actor_indices[0];
        this.startActorCommandSelection();
    }; // Scene_Battle.prototype.update_matb_actor_selection



    Recall that it'll only be called when there are no inputable actors or the currently setup actor becomes uninputable.


    Updates actor ATB bars

    Spoiler





    Window_BattleStatus.prototype.refresh_matb_bars can be written this way:



    Spoiler







    Window_BattleStatus.prototype.refresh_matb_bars = function() { // New; Hotspot
        var matb = $gameSystem.matb, actor, rect;
        var ox = matb.hp_bar_w + matb.mp_bar_ox + matb.mp_bar_w + matb.atb_bar_ox;
        if ($dataSystem.optDisplayTp) { ox += matb.tp_bar_ox + matb.tp_bar_w; }
        for (var index = 0, max = this.maxItems(); index < max; index++) {
            actor = $gameParty.battleMembers()[index];
            if (!actor || !actor.matb_val_ch) { continue; }
            rect = this.gaugeAreaRect(index);
            this.draw_actor_matb(actor, rect.x + ox, rect.y);
            actor.matb_val_ch = false;
        }
    }; // Window_BattleStatus.prototype.refresh_matb_bars



    Window_BattleStatus.prototype.draw_actor_matb can be written this way:




    /* actor: The actor using the atb bar
     * x: The atb bar x position
     * y: The atb bar y position
     */
    Window_BattleStatus.prototype.draw_actor_matb = function(actor, x, y) {
    // New; Hotspot
        var w = $gameSystem.matb.atb_bar_w;
        var color_0 = this.textColor($gameSystem.matb.atb_c1);
        var color_1 = this.textColor($gameSystem.matb.atb_c2);
        this.drawGauge(x, y, w, actor.matb_val / 100, color_0, color_1);
        this.changeTextColor(this.systemColor());
        this.drawText("AP", x, y, this.textWidth("AP"));
    }; // Window_BattleStatus.prototype.draw_actor_matb





    Note that:


    - Never redraw the whole status window per frame to reduce the chance for this plugin to cause constant lag and fps drop(too advanced to be explained here).


    - An actor's ATB bar should only be redrawn when that actor's ATB value changes(too advanced to be explained here).


    - The actor ATB bar description text's hardcoded as AP here.


    Starting ATB Value

    Spoiler





    BattleManager.startBattle can be extended to setups each battler's Starting ATB Value:



    Spoiler







    BattleManager.startBattleMatb = BattleManager.startBattle;
    BattleManager.startBattle = function() {
        this.startBattleMatb();
        this.start_matb_battle(); // Added
    }; // BattleManager.startBattle





    BattleManager.start_matb_battle can be written this way:

    Spoiler






    BattleManager.start_matb_battle = function() { // New
        var start;
        this._phase = 'turn';
        start = this._preemptive ? "preempt" : this._surprise ? "surprise" : "norm";
        $gameParty.battleMembers().forEach(function(mem) {
            mem.set_start_matb_val(start);
        });
        $gameTroop.members().forEach(function(m) { m.set_start_matb_val(start); });
    }; // BattleManager.start_matb_battle



    Game_Battler.prototype.set_start_matb_val can be written this way:

    Spoiler






    // start: The battle start type
    Game_Battler.prototype.set_start_matb_val = function(start) { // New
        this.init_matb();
        Game_BattlerBase.prototype.set_start_matb_val.call(this, start);
        if (this._matb_val >= 100) { this.makeActions(); }
        this._matb_val_ch = true;
    }; // Game_Battler.prototype.set_start_matb_val



    Note that the Battler ATB Clock change notification flag will be raised here to ensure proper actor ATB bar display(too advanced to be explained here).


    Game_BattlerBase.prototype.set_start_matb_val can be written this way:

    Spoiler






    // start: The battle start type
    Game_BattlerBase.prototype.set_start_matb_val = function(start) { // New
        if (!this.canMove()) { return this._matb_val = 0; }
        if (start === "preempt" && this.isActor()) { return this._matb_val = 100; }
        if (start === "surprise" && this.isEnemy()) { return this._matb_val = 100; }
        this._matb_val = 0;
    }; // Game_BattlerBase.prototype.set_start_matb_val





    Update State Turns/Reset Battler ATB Clock

    Spoiler





    Game_BattlerBase.prototype.updateStateTurns can be rewritten from this:



    Spoiler







    Game_BattlerBase.prototype.updateStateTurns = function() {
    this._states.forEach(function(stateId) {
    if (this._stateTurns[stateId] > 0) {
    this._stateTurns[stateId]--;
    }
    }, this);
    };





    Into this:

    Spoiler






    /*----------------------------------------------------------------------------
     *    Updates state turns with different removal timings at different timings
     *----------------------------------------------------------------------------*/
    Game_BattlerBase.prototype.updateStateTurns = function() { // Rewrite
        this.update_matb_state_turns(2); // Rewritten
    }; // Game_BattlerBase.prototype.updateStateTurns



    Game_Battler.prototype.update_matb_state_turns can be written this way:

    Spoiler






    // timing: The state auto removal timing
    Game_Battler.prototype.update_matb_state_turns = function(timing) { // New
        this.states().forEach(function(s) {
            if (s.autoRemovalTiming === timing && this._stateTurns[s.id] > 0) {
                this._stateTurns[s.id] -= 1;
            }
        }, this);
    }; // Game_Battler.prototype.update_matb_state_turns



    Game_Battler.prototype.onAllActionsEnd can be extended to update turns of states with Auto-removal Timing Action End:

    Spoiler






    Game_Battler.prototype.onAllActionsEndMatb =
    Game_Battler.prototype.onAllActionsEnd;
    Game_Battler.prototype.onAllActionsEnd = function() {
        this.onAllActionsEndMatb();
        // Added
        this.update_matb_state_turns(1);
        this.reset_matb();
        //
    }; // Game_Battler.prototype.onAllActionsEnd



    Bear in mind that it resets the battler's Battler ATB Clock and shifts that battler from Executing Actions to Refilling ATB by calling Game_Battler.prototype.reset_matb:

    Spoiler






    Game_Battler.prototype.reset_matb = function() { // New
        this._matb_val = 0;
        this._matb_val_ch = true;
        this.clearActions();
        var index = BattleManager.action_battlers.indexOf(this);
        if (index >= 0) { BattleManager.action_battlers.splice(index, 1); }
        if (BattleManager.actor() === this) { BattleManager.clearActor(); }
    }; // Game_Battler.prototype.reset_matb



    Note that it sets the battler ATB value to 0% and raise its change notification flag to ensure proper actor ATB bar display(too advanced to be explained here).


    Recall that it needs to:


    - Removes all that battler's action slots.


    - Marks that battler as Unactable.


    Battler inputability check

    Spoiler





    Game_BattlerBase.prototype.canInput can be extended into this:



    Spoiler







    Game_BattlerBase.prototype.canInputMatb = Game_BattlerBase.prototype.canInput;
    Game_BattlerBase.prototype.canInput = function() { // Hotspot
        return this.canInputMatb() && this.can_input_matb(); // Rewritten
    }; // Game_BattlerBase.prototype.canInput





    Game_BattlerBase.prototype.can_input_matb can be written this way:

    Spoiler






    Game_BattlerBase.prototype.can_input_matb = function() { // New; Hotspot
        if (this._actions.length <= 0 || !this.isActor()) { return false; }
        return !this._actions.every(function(act) { return act.matb_confirm; });
    }; // Game_BattlerBase.prototype.can_input_matb



    Note that matb_confirm is a flag indicating the action's completely inputted. It'll be raised when its action's completely inputted, which occurs in the below timings:

    Spoiler






    Game_Action.prototype.setSkillMatb = Game_Action.prototype.setSkill;
    Game_Action.prototype.setSkill = function(skillId) {
    this.setSkillMatb(skillId);
    this._matb_confirm = this._matb_confirm || !this.needsSelection(); // Added
    }; // Game_Action.prototype.setSkill

    Game_Action.prototype.setItemMatb = Game_Action.prototype.setItem;
    Game_Action.prototype.setItem = function(itemId) {
    this.setItemMatb(itemId);
    this._matb_confirm = this._matb_confirm || !this.needsSelection(); // Added
    }; // Game_Action.prototype.setItem


    Code:
    
    Scene_Battle.prototype.commandGuardMatb = Scene_Battle.prototype.commandGuard;
    Scene_Battle.prototype.commandGuard = function() {
        this.confirm_matb_act(); // Added
        this.commandGuardMatb();
    }; // Scene_Battle.prototype.commandGuard
    
    Scene_Battle.prototype.onActorOkMatb = Scene_Battle.prototype.onActorOk;
    Scene_Battle.prototype.onActorOk = function() {
        this.confirm_matb_act(); // Added
        this.onActorOkMatb();
    }; // Scene_Battle.prototype.onActorOk
    
    Scene_Battle.prototype.onEnemyOkMatb = Scene_Battle.prototype.onEnemyOk;
    Scene_Battle.prototype.onEnemyOk = function() {
        this.confirm_matb_act(); // Added
        this.onEnemyOkMatb();
    }; // Scene_Battle.prototype.onEnemyOk
    
    Code:
    
    Scene_Battle.prototype.confirm_matb_act = function() { // New
        var act = BattleManager.inputtingAction();
        if (act) { act.matb_confirm = true; }
    }; // Scene_Battle.prototype.confirm_matb_act
    



    Such flag's to ensure the actor remains inputable when he/she/it isn't finished inputting all actions yet.


    Failed party escape attempts

    Spoiler





    Game_Party.prototype.clearActions can be written this way:



    Spoiler







    Game_Party.prototype.clearActions = function() { // New
        Game_Unit.prototype.clearActions.call(this);
        // Added to reset all party members' atb values upon failed party escapes
        this.aliveMembers().forEach(function(mem) { mem.reset_matb(); });
        //
    }; // Game_Party.prototype.clearActions







    Stuffs to be gone

    Spoiler





    Both BattleManager.startInput, which transits the battle phase to the Action Input Phase, and BattleManager.startTurn, which transits the battle phase to the Action Execution Phase, are completely disabled.


    BattleManager.startTurn is rewritten from this:



    Spoiler







    BattleManager.startTurn = function() {
    this._phase = 'turn';
    this.clearActor();
    $gameTroop.increaseTurn();
    this.makeActionOrders();
    $gameParty.requestMotionRefresh();
    this._logWindow.startTurn();
    };





    Into this:

    Spoiler






    BattleManager.startTurn = function() {}; // Rewrite



    Recall that BattleManager.startTurn will be called whenever the Next Command is called when the last action slot of the last inputable actor's finished inputting.


    Note that BattleManager.startInput can remain intact as it won't be called anymore, due to BattleManager._phase being restricted to either 'turn' or 'action'(too advanced to be explained here).


    Bug Fixes

    Spoiler





    Game_BattlerBase.prototype.hide and Game_Battler.prototype.onRestrict are extended this way to ensure a hidden or restricted battler's indeed Unactable:



    Spoiler







    Game_BattlerBase.prototype.hideMatb = Game_BattlerBase.prototype.hide;
    Game_BattlerBase.prototype.hide = function() {
        this.hideMatb();
        this.reset_matb(); // Added
    }; // Game_BattlerBase.prototype.hide


    Code:
    
    
    Game_Battler.prototype.onRestrictMatb = Game_Battler.prototype.onRestrict;
    Game_Battler.prototype.onRestrict = function() {
        this.onRestrictMatb();
        // Added to fix nil action battlers bugs and edge cases as well
        if (BattleManager.action_battlers) { this.reset_matb(); }
        //
    }; // Game_Battler.prototype.onRestrict
    

    Note that it's possible for Game_Battler.prototype.onRestrict to be called before BattleManager.action_battlers is initialized.





    Game_Battler.prototype.makeActions is extended to ensure battlers that are already Actable won't remake their actions:

    Spoiler






    Game_Battler.prototype.makeActionsMatb = Game_Battler.prototype.makeActions;
    Game_Battler.prototype.makeActions = function() {
        // Rewritten
        if (BattleManager.action_battlers.indexOf(this) >= 0) { return; }
        this.makeActionsMatb();
        BattleManager.action_battlers.push(this);
        //
    }; // Game_Battler.prototype.makeActions



    Game_Battler.prototype.onTurnEnd is extended to remove all buffs/debuffs having 0 remaining turn upon turn end:

    Spoiler






    Game_Battler.prototype.onTurnEndMatb = Game_Battler.prototype.onTurnEnd;
    Game_Battler.prototype.onTurnEnd = function() {
        this.onTurnEndMatb();
        this.removeBuffsAuto(); // Added
    }; // Game_Battler.prototype.onTurnEnd



    Game_Battler.prototype.onBattleEnd is extended to prevent battlers added in the next battle after it starts will carry the statuses in the last battle over:

    Spoiler






    /*----------------------------------------------------------------------------
     *    Ensures battlers added after the battle starts won't carry statuses over
     *----------------------------------------------------------------------------*/
    Game_Battler.prototype.onBattleEndMatb = Game_Battler.prototype.onBattleEnd;
    Game_Battler.prototype.onBattleEnd = function() {
        this.onBattleEndMatb();
        this.reset_matb(); // Added
    }; // Game_Battler.prototype.onBattleEnd



    Game_Actor is edited to initialize data for actors added after the battle starts:

    Spoiler






    /*----------------------------------------------------------------------------
     *    New private instance variable                                           
     *----------------------------------------------------------------------------*/
    // _matb_reserve: The actor added during battles flag

    /*----------------------------------------------------------------------------
     *    Ensures actors added during battles are properly initialized as well    
     *----------------------------------------------------------------------------*/
    Game_Actor.prototype.initMembersMatb = Game_Actor.prototype.initMembers;
    Game_Actor.prototype.initMembers = function() {
        this.initMembersMatb();
        this._matb_reserve = true; // Added
    }; // Game_Actor.prototype.initMembers

    /*----------------------------------------------------------------------------
     *    Ensures actors added during battles are properly initialized as well    
     *----------------------------------------------------------------------------*/
    Game_Actor.prototype.onBattleEnd = function() { // New
        Game_Battler.prototype.onBattleEnd.call(this);
        this._matb_reserve = true; // Added
    }; // Game_Actor.prototype.onBattleEnd

    /*----------------------------------------------------------------------------
     *    Ensures actors added during battles are properly initialized as well    
     *----------------------------------------------------------------------------*/
    // start: The battle start type
    Game_Actor.prototype.set_start_matb_val = function(start) { // New
        Game_Battler.prototype.set_start_matb_val.call(this, start);
        this._matb_reserve = false; // Added
    }; // Game_Actor.prototype.set_start_matb_val

    /*----------------------------------------------------------------------------
     *    Ensures actors added during battles are properly initialized as well    
     *----------------------------------------------------------------------------*/
    Game_Actor.prototype.update_matb = function() { // New; Hotspot
        // Added
        if (this._matb_reserve) {
            this.init_matb();
            this._matb_reserve = false;
        }
        //
        Game_Battler.prototype.update_matb.call(this);
    }; // Game_Actor.prototype.update_matb



    Scene_Battle.prototype.updateWindowPositions is extended to ensure the actor window will always completely cover the status window when it's active:

    Spoiler






    Scene_Battle.prototype.updateWindowPositionsMatb =
    Scene_Battle.prototype.updateWindowPositions;
    Scene_Battle.prototype.updateWindowPositions = function() { // Hotspot
    this.updateWindowPositionsMatb();
    // Added to ensure the actor window will complete cover the status window
    if (!this._actorWindow.active) { return; }
    this._actorWindow.x = this._statusWindow.x;
    //
    }; // Scene_Battle.prototype.updateWindowPositions



    The rest is just implementing the configurations letting users to rearrange the actor info display in the status window, which should be easy, simple and small enough for you to thoroughly comprehend yourselves.


    Summary

    Spoiler





    The easiest, simplest and smallest ATB system ever is that as the minimum viable product, having the loosest ATB Wait Condition, a straightforward Battle Turn Clock and Battler ATB Clock.


    It only has all the configurations, which covers all the absolute ATB system essentials that gives the users the least control and freedom needed, that can't be hardcoded. Such configurations includes the max Battle Turn Clock unit and those letting users to rearrange the actor info display in the status window due to the addition of the actor atb bars to the right of the tp bars.


    It's also added the Starting ATB Value, which depends on whether the battle starts normally, preemptively or under surprise, and Battler ATB Rate, both of which are hardcoded and needed.


    Its implementations involves the below:


    - Removing the battle phase other than 'turn', which refers to Not Executing Actions, and 'action', which refers to Executing Actions.


    - Removing the transitions to the Action Input Phase and Action Execution Phase respectively.


    - Editing the Battle Frame Update, which still updates the action executions when the battle's Executing Actions, to use the ATB Wait Condition, which also guards against cases leading to bugs, to check if the ATB Frame Update should be run.


    - Editing the state turn updates to make states with Auto-removal Timing Action End update their turns when their battlers become finished executing all actions.


    - Editing the battler inputability check to ensure enemies are never inputable, inputable battlers are Actable and only uninputable battlers can be the Action Execution Subject.


    - Editing the failed party escape attempts to reset all actors' Battler ATB Clock to prevent allowing subsequent party escape attempt immediately afterwards.


    - Adding the ATB Wait Condition, ATB Frame Update, Battler ATB Clock(including its reset), setups for the new Action Execution Subject which must be Actable, Battle Turn Clock which triggers turn end immediately followed by Turn Start upon reaching its max unit, setups for new inputable actors and actor ATB bar updates without updating the whole status window per frame.


    - Fixing various bugs, including resetting the Battler ATB Clock upon hiding, being restricted and battle end, initializing data for battlers added after the battle starts, removing buffs/debuffs with 0 remaining turns upon turn end, preventing already Actable battlers from remaking actions, and ensuring the actor window always completely covering the status window when it's active.
     
    Last edited by a moderator: Feb 3, 2016
    #4
  5. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    As I'm still debugging DoubleX RMMV Popularized ATB Core and recently I'm quite busy, I'll probably have to delay writing Ways to write a basic ATB system having all core functionalities, which will be so long that it might make the reply too long and I might have to spend a whole day just to finish it :D


    I'd recommend highly recommend those wishing to learn how ATB system works to read Tsukihime's ATB tutorial series, as it's just so well-written that everyone should be able to follow effortlessly [​IMG]
     
    Last edited by a moderator: Jan 26, 2016
    #5
  6. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Now I'm going to talk about Ways to write a basic ATB system having all core functionalities, but before that, let's preview what DoubleX RMMV Popularized ATB Core can deliver:


    https://www.youtube.com/watch?v=6KTV1k6ohfE


    Be warned: While the targeting audience of this topic's those having some javascript coding proficiency(inexperienced junior plugin developer having written several easy, simple ans small battle related plugins without nontrivial bugs), trying to thoroughly comprehend DoubleX RMMV Popularized ATB Core is expected to be very hard for you without any external help, as that plugin's implementation's almost reached the next level - decent javascript coding proficiency(experienced plugin developer having written dozens of decent battle related plugins). Nevertheless, I still think you can do it :)


    Clarification

    Spoiler





    The popularized ATB system


    Actually, DoubleX RMMV Popularized ATB Core isn't the whole system itself, but only its core plugin.


    Basically, the popularized ATB system will consist of this core plugin having all essential functionalities, as well as a bunch of addon plugins having optional functionalities and needing this core to be used.(The Core Addon Approach basics will be covered in Ways to write an easy, simple and small ATB system addon).


    Also, this system tries to be an easy, simple and user-friendly, yet powerful ATB system, meaning as much control and freedom will be given to users, as long as doing so won't make this plugin significantly more demanding to the less able users.


    While the amount of control and freedom given to users is a completely separate topic, here such amount will be limited to only letting users writing simple values and changing them on the fly. This matches our goal here - writing a basic ATB system.


    Core functionalities


    The details will also be covered in Ways to write an easy, simple and small ATB system addon, but for now, they include:


    1. All functionalities that are needed for any ATB system(Written as "Serves as the core plugin implementing all atb system essentials" in the core plugin)


    2. All functionalities that will be included in the whole system and would be much less desirable if they were implemented by addons instead(too advanced to be covered here)





    Stuffs to be implemented

    Spoiler





    Besides those mentioned when implementing DoubleX RMMV Minimalized ATB, the below stuffs also have to be implemented(those already mentioned stuffs conflicting with the below will be discarded here):


    Feature Set



    Spoiler





    Configurations:




    * @param battle_system_code
    * @desc Sets the code indicating if ATB, CTB or the default battle system will
    * be used as battle_system_code
    * Available codes for battle_system_code:
    * atb - ATB will be used
    * The default battle system will be used if battle_system_code doesn't
    * return any available code
    * battle_system_code must not be changed from the available codes to the
    * unavailable ones during the same battle(the same for the vice versa) or
    * this plugin will fail extremely badly
    * @default atb
    *
    * @param base_fill_time
    * @desc Sets the base atb fill time from empty to full as base_fill_time
    * seconds
    * base_fill_time must return a Number and should return a positive one
    * @default 5
    *
    * @param turn_unit_code
    * @desc Sets the code of the unit of the battle turn clock as turn_unit_code
    * Available code for the unit of the battle turn clock:
    * act - Number of executed actions
    * sec - Seconds
    * The battle turn clock won't run if turn_unit_code doesn't return an
    * available code
    * @default sec
    *
    * @param max_turn_unit
    * @desc Sets the maximum battle turn clock unit as max_turn_unit
    * max_turn_unit must return a Number and should return a positive one
    * @default 5
    *
    * @param wait_cond_code
    * @desc Sets the atb wait condition code as wait_cond_code
    * Available atb wait condition codes:
    * full - Wait only when a message's showing or an event's running
    * act - Wait when an action's executing as well
    * target - Wait when the target selection window's shown as well
    * item - Wait when the skill/item selection window's shown as well
    * "Wait when players can input actions as well" will be used if
    * wait_cond_code doesn't return any available code
    * Only 1 battler can execute actions at a time
    * Only 1 action can be executed at a time
    * @default full
    *
    * @param atb_fill_code
    * @desc Sets the atb fill code as atb_fill_code
    * Available atb fill code:
    * delay - Lets battlers input actions when that battler's atb's empty
    * Refills that battler's atb right after executing actions
    * If atb_fill_code doesn't return an available atb fill code, "Lets
    * battlers input and execute actions when the battler's atb's full and
    * empties the battler's atb right after executing actions" will be used
    * atb_fill_code should return the same code during the same battle to
    * ensure proper atb fillings and action executions
    * @default fill
    *
    * @param max_atb_val
    * @desc Sets the minimum atb value regarded as overlay(also the maximum atb
    * value displayed on the atb bars) with atb fill code delay, and the
    * maximum atb value with other codes, as max_atb_val
    * max_atb_val must return a nonzero Number and should return a positive
    * one
    * max_atb_val should return the same value during the same battle to
    * ensure proper atb fillings and action executions
    * max_atb_val must be at least 10000 times larger than 2 ^ -52
    * @default 100
    *
    * @param atb_rate_code
    * @desc Sets the atb refill rate code as atb_rate_code
    * Available atb refill rate codes:
    * abs_agi - agi
    * agi - agi * max_atb_val / base_fill_time / 60
    * avg_agi - (formula in agi) / average of all alive battlers' agi
    * The fps is assumed to be always 60
    * If atb_rate_code returns an unavailable atb refill rate code, it'll be
    * the same as avg_agi, except BattleManager.patb_avg_agi will always be
    * reevalauted when it can change instead of just upon battle start
    * atb gain rate must be at least 2 ^ -52 * max atb value * 100 per frame
    * @default update_avg_agi
    *
    * @param atb_start_code
    * @desc Sets the starting atb value code as atb_start_code
    * The starting atb value is always 0 for:
    * - unmovable battlers with non-delay atb fill code
    * - actors with non-delay atb fill code and start type as surprise
    * - enemies with non-delay atb fill code and start type as preemptive
    * - actors with atb fill code delay and start type as preemptive
    * - enemies with atb fill code delay and start type as surprise
    * The starting atb value is always max_atb_val for:
    * - unmovable battlers with atb fill code delay
    * - actors with non-delay atb fill code and start type as preemptive
    * - enemies with non-delay atb fill code and start type as surprise
    * - actors with atb fill code delay and start type as surprise
    * - enemies with atb fill code delay and start type as preemptive
    * Available starting atb value code for normal start:
    * agi - max_atb_val * agi / max agi in non-delay atb fill code
    * max_atb_val * (1 - agi / this.paramMax(6)) in atb fill code
    * delay
    * The starting atb value will be 0 and max_atb_val in non-delay and
    * delay atb fill code respectively if atb_start_code doesn't return an
    * available starting atb value code for normal starts
    * @default agi
    *
    * @param atb_reset_code
    * @desc Sets the code of the reset atb mechanism as atb_reset_code for
    * additional effects
    * It'll only be used with non-delay atb_reset_code
    * The atb refill will always be freezed while not movable
    * Available code for the atb reset mechanism for additional effects:
    * clear - The atb will be emptied when becoming unmovable as well
    * No additional effects will trigger if atb_reset_code doesn't return
    * an available code
    * @default clear
    *
    * @param actor_name_w
    * @desc Sets the maximum width allocated for actor name display on the status
    * window as actor_name_w
    * actor_name_w must return a positive Number
    * @default 123
    *
    * @param actor_icon_ox
    * @desc Sets the actor icon display area x offset relative to the actor name
    * display area on the status window as actor_icon_ox
    * actor_icon_ox must return a positive Number
    * @default 6
    *
    * @param hp_bar_ox
    * @desc Sets the actor hp bar display area x offset relative to the actor icon
    * display area on the status window as hp_bar_ox
    * hp_bar_ox must return a positive Number
    * @default 6
    *
    * @param hp_bar_w
    * @desc Sets the maximum width allocated for actor hp bar display on the status
    * window as hp_bar_w
    * hp_bar_w must return a positive Number
    * @default 87
    *
    * @param mp_bar_ox
    * @desc Sets the actor mp bar display area x offset relative to the actor hp
    * bar display area on the status window as mp_bar_ox
    * mp_bar_ox must return a positive Number
    * @default 6
    *
    * @param mp_bar_w
    * @desc Sets the maximum width allocated for actor mp bar display on the status
    * window as mp_bar_w
    * mp_bar_w must return a positive Number
    * @default 87
    *
    * @param tp_bar_ox
    * @desc Sets the actor tp bar display area x offset relative to the actor mp
    * bar display area on the status window as tp_bar_ox
    * tp_bar_ox must return a positive Number
    * @default 6
    *
    * @param tp_bar_w
    * @desc Sets the maximum width allocated for actor tp bar display on the status
    * window as tp_bar_w
    * tp_bar_w must return a positive Number
    * @default 87
    *
    * @param atb_bar_ox
    * @desc Sets the actor atb bar display area x offset relative to the actor
    * mp/tp bar display area on the status window as atb_bar_ox
    * atb_bar_ox must return a positive Number
    * @default 6
    *
    * @param atb_bar_w
    * @desc Sets the maximum width allocated for actor atb bar display on the
    * status window as atb_bar_w
    * atb_bar_w must return a positive Number
    * @default 87
    *
    * @param atb_c1
    * @desc Sets the 1st atb bar color as text color atb_c1
    * atb_c1 must return a valid text color code
    * atb_c1 should return the same value during the same battle to ensure
    * proper atb bar color displays
    * @default 7
    *
    * @param atb_c2
    * @desc Sets the 2nd atb bar color as text color atb_c2
    * atb_c2 must return a valid text color code
    * atb_c2 should return the same value during the same battle to ensure
    * proper atb bar color displays
    * @default 8
    *
    * @param atb_overlay_c1
    * @desc Sets the 1st atb bar overlay color as text color atb_overlay_c1
    * It'll only be used with atb value above max_atb_val
    * atb_overlay_c1 must return a valid text color code
    * atb_overlay_c1 should return the same value during the same battle to
    * ensure proper atb bar color displays
    * @default 19
    *
    * @param atb_overlay_c2
    * @desc Sets the 2nd atb bar overlay color as text color atb_overlay_c2
    * It'll only be used with atb value above max_atb_val
    * atb_overlay_c2 must return a valid text color code
    * atb_overlay_c2 should return the same value during the same battle to
    * ensure proper atb bar color displays
    * @default 26
    *
    * @param atb_bar_text
    * @desc Sets the atb bar description text as atb_bar_text
    * atb_bar_text should return the same value during the same battle to
    * ensure proper atb bar text displays
    * @default AP



    Notetags:




    * # Actor/Class/Weapon/Armor/Enemy/State Notetags:
    * State notetags take the highest priority, followed by enemy, weapon,
    * armor, class and actor
    * 1. <operator max patb val: val>
    * - Assigns val to the battler's maximum atb value via operator
    * - operator can be either =, +, -, *, / or %, meaning set to, add
    * by, subtract by, multiply by, divide by or modulo by respectively
    * - All instances of this notetag will be used sequentially
    * - The final max atb_value must be at least 10000 times larger than
    * 2 ^ -52
    * 2. <patb colors: text color 1, text color 2>
    * - Changes the atb bar color 1 and 2 to text color 1 and 2
    * respectively when this notetag's used
    * - The 1st notetag that's being read by the battler will be used
    * 3. <patb overlay colors: text color 1, text color 2>
    * - Changes the atb overlay color 1 and 2 to text color 1 and 2
    * respectively when this notetag's used
    * - The 1st notetag that's being read by the battler will be used



    Plugin Calls:




    * # Configuration manipulations
    * 1. $gameSystem.patb.param
    * - Returns the value of param listed in the plugin manager
    * 2. $gameSystem.patb.param = val
    * - Sets the value of param listed in the plugin manager as val
    * - All $gameSystem.patb.param changes will be saved
    * 3. $gameSystem.is_patb()
    * - Returns if battle_system_code returns an available code
    * 4. $gameSystem.max_patb_turn_unit(unit)
    * - Returns the maximum global battle turn counter with unit as unit
    * # Data actor/class/weapon/armor/enemy/state manipulations
    * 1. meta.max_patb_val
    * - Returns the maximum atb value with the operator stored in
    * <operator max patb val: val> in the form of [opeartor, val]
    * 2. meta.max_patb_val = [opeartor, val]
    * - Sets the maximum atb value with the operator stored in
    * <operator max patb val: val> as string operator and Number val
    * - The final max atb value must be at least 10000 times larger than
    * 2 ^ -52
    * - All meta.max_patb_val changes can be saved if
    * DoubleX RMMV Dynamic Data is used
    * 3. meta.patb_colors
    * - Returns the text colors stored in
    * <patb colors: text color 1, text color 2> in the form of
    * [text color 1, text color 2]
    * 4. meta.patb_colors = [text color 1, text color 2]
    * - Sets the text colors stored in
    * <patb colors: text color 1, text color 2> as text color 1 and 2
    * - All meta.patb_colors changes can be saved if
    * DoubleX RMMV Dynamic Data is used
    * 5. meta.patb_overlay_colors
    * - Returns the text colors stored in
    * <patb overlay colors: text color 1, text color 2> in the form of
    * [text color 1, text color 2]
    * 6. meta.patb_overlay_colors = [text color 1, text color 2]
    * - Sets the text colors stored in
    * <patb overlay colors: text color 1, text color 2> as text color 1
    * and 2
    * - All meta.patb_overlay_colors changes can be saved if
    * DoubleX RMMV Dynamic Data is used
    * # Battler manipulations
    * 1. max_patb_val
    * - Returns the battler's maximum atb value for all atb types
    * 2. max_patb_val = val
    * - Sets the battler's maximum atb value for all atb types as val
    * - It'll be reevaluated if it can be changed without plugin calls
    * 3. patb_val.atb
    * - Returns the battler's atb value
    * 4. patb_val.atb = val
    * - Set the battler's atb value as val
    * 5. patb_rate.atb
    * - Returns the battler's atb rate
    * 6. patb_rate.atb = rate
    * - Set the battler's atb rate as rate
    * - It'll be reevaluated if it can be changed without plugin calls
    * 7. patb_val_change.atb = true
    * - Notifies that the atb value's changed
    * - It must be used right after the atb bar length changed
    * 8. patb_note_change.note = true
    * - Notifies that the values of at least 1 notetag instance of
    * notetag note or the corresponding configuration value has changed
    * - Note can be max_atb_val, atb_color or atb_overlay_color,
    * referring to <operator max patb val: val>,
    * <patb colors: text color 1, text color 2>, and
    * <patb overlay colors: text color 1, text color 2> respectively
    * 9. reset_patb(reset, val)
    * - Clears all battler's actions
    * - Resets the battler's atb value as val as well if reset returns
    * true
    * 10. patb_type()
    * - Returns the current atb type, which can be either atb, charge or
    * cooldown
    * - charge and cooldown will be available when corresponding addons
    * are used as well
    * 11. patb_colors(type)
    * - Returns the text colors of the atb bars of type type, which can
    * be either atb, charge or cooldown
    * - The overlay colors will be returned instead if the atb value of
    * type type is greater than max_atb_val
    * - charge and cooldown will be available when corresponding addons
    * are used as well
    * # Battle manipulations
    * 1. BattleManager.patb_turn_clock
    * - Returns the current global battle turn clock with both second and
    * action units in the form of { act: act, sec: sec }
    * - sec is stored with the number of frames as the unit
    * - The fps is assumed to be always 60
    * 2. BattleManager.patb_turn_clock.act = act
    * - Sets the current global battle turn clock with action unit as act
    * 3. BattleManager.patb_turn_clock.sec = sec
    * - Sets the current global battle turn clock with second unit as sec
    * - sec is stored with the number of frames as the unit
    * - The fps is assumed to be always 60
    * 4. BattleManager.patb_base
    * - Returns the global base atb rate
    * 5. BattleManager.need_patb_refresh = true
    * - Indicates that at least 1 battler has refreshed
    * - All windows will be refreshed and the sum of all battler's agi
    * will be reevaluated



    The sheer size of the feature set might already scare you off, but you can first imagine yourselves as users of this plugin(and/or even try to really use it beforehand) before placing yourselves on the plugin developer's perspective. Thoroughly comprehending a plugin's implementations should be much easier after thoroughly comprehending that plugin on the user level.





    Notetag Management

    Spoiler





    While it's too advanced to be covered here, I'll still briefly introduce it here, as it's used in the plugin.


    Here the notetags will only store simple values although some plugin calls can be used to change those values on the fly. It's to keep this plugin easy, simple and user-friendly while make this plugin reasonably powerful.


    Also, those notetags will all be read only once upon game starts, to avoid reading them right before using them, which can be significantly more unperformant when there are lots of notetags(too advanced to be covered here).



    Change Notification Flags

    Spoiler





    While it's a topic that's too advanced to be covered here, I'll still briefly introduce it here, as it's quite heavily used in the plugin.


    Simply put, it's mainly here to boost time performance. It can be used to avoid repeatedly evaluating the same things over and over again when their results can be cached and reused instead. When the cached results can be incorrect, it'll be raised to notify that those results need to be reevaluated. So it can reduce redundant executions while preserving correctness.



    Battle System Checker

    Spoiler





    As the plugin lets users change the battle system on the fly(except during battles), it'll behave very differently depending on the current battle system code.


    Although right now there's only 2 ATB system and the default RMMV battle system, they're so drastically different that a battle system checker can come into handy to decide which parts of the codes should be executed to deliver desired behaviors.



    Input Window Closing/Refresh

    Spoiler





    In both the default RMMV battle system and DoubleX RMMV Minimalized ATB, no input window will ever need to be checked for the need to refresh/be closed, as it's impossible for the Battle Frame Update in the former and the ATB Frame Update in the latter respectively to be run, due to the fact that the former won't run in the Action Input Phase and the latter won't run when the ATB Wait Condition's met. That's no longer the case for DoubleX RMMV Popularized ATB Core, however, since the ATB Frame Update can be run while an input window's active.


    Let's use the skill window as an example:


    Suppose there's a skill needing x mp to be used, and the actor currently has more than x mp, meaning that skill's usable.


    But say, while the skill window's still active, that actor receives mp damage, causing his/her/its mp to be below x. Now that skill should be no longer usable.


    This means the skill window needs to be refreshed because it needs to change from displaying that skill as usable to displaying it as unusable.



    Autobattle Check

    Spoiler





    Similar to Input Window Closing/Refresh, the ATB Frame Update can be run while an actor's inputable, meaning he/she/it can become Autobattle at that moment. Then he/she/its should become uninputable and then input actions automatically. This needs the help with Input Window Closing/Refresh.



    Party Escape Attempt Availability

    Spoiler





    Similar to Input Window Closing/Refresh and Autobattle Check, party escape attempts can be executed when the ATB Frame Update's running.


    If a party escape attempt's executed while an action's executing, be it executed by or targeting a party member, it's possible to be interrupted by the party escape attempt.


    If it succeeds, it can lead to the Action Execution Subject/target becoming null before the action's completely executed, which can outright crash the game.


    Therefore, choosing to trigger party escape attempts should be completed disabled when an action's executing.



    Maximum ATB Value

    Spoiler





    In DoubleX RMMV Minimalized ATB, the Maximum ATB Value's hardcoded as 100.


    Here, it's not only not hardcoded anymore, but can even be changed during battles.


    This can lead to be below problems:


    1. If the ATB value was full and the Maximum ATB Value increases, the ATB value will become not full, meaning the battler will transit from Full ATB to Refilling ATB - a new transition here.


    2. If the ATB value wasn't full and the Maximum ATB Value decreases, the ATB value will become full, meaning the battler will transit from Refilling ATB to Full ATB - a new cause of this transition here.


    3. If the Maximum ATB Value changes, the ATB fill percent can also change even if the ATB value hasn't changed, meaning the actor ATB bar will need to be redrawn - a new cause to redraw it.


    4. If the Maximum ATB Value becomes smaller that the current ATB value, the latter will need to be corrected to be the same as the former, otherwise the actor ATB bar can screw up - a new cause of ATB value overflow.



    On Restrict With Full ATB

    Spoiler





    Similar to Input Window Closing/Refresh, Autobattle Check and Party Escape Attempt Availability, a battler can become restricted when he/she/it's in Full ATB.


    As the plugin lets users set the reset Battler ATB Clock to not reset the ATB value upon restrictions, this can lead to battlers with Full ATB being Unactable, thus breaking an ATB system invariant.


    To avoid this, when resetting the Battler ATB Clock with this setting, the ATB value should be set as a value trivially smaller than the Maximum ATB Value although the different should still be detectable. This ensures battlers with Full ATB to be always Actable while approximately mimicking the effect the Battler ATB Clock hasn't been reset at all.



    Next Command

    Spoiler





    Suppose a player's inputting actions for actor with index x, and upon finishing inputting that actor's actions, some actors with index smaller than x are inputable while none larger than x are.


    Now, Next Command will be called, which will try to find an inputable actor with index larger than x. As long as there are inputable actors:


    1. If such actor's found, an actor command window will be setup for that actor.


    2. If no such actor's found, the party command window will be setup instead(too advanced to be explained here).


    So in this case, the party command window will setup, even when it's supposed to setup the actor command window for another inputable actor. In fact, Next Command should be changed to have no chance to setup the party command window at all.







    Writing DoubleX RMMV Popularized ATB Core from scratch

    Spoiler





    It's similar to Writing DoubleX RMMV Minimalized ATB from scratch, except that many previous hardcoded configurations will now become configurable by users, and there are some new features as well.


    The below are all the key parts of the plugin implementation:


    Battle System Checker



    Spoiler





    Game_System.prototype.is_patb is added to check whether the battle system's an available one(right now only Active Time Battle's available):



    Spoiler








    Game_System.prototype.is_patb = function() { // New; Hotspot
    // Loosens the coupling among the core and ctb plugins
    return this._patb.battle_system_code === "atb";
    //
    }; // Game_System.prototype.is_patb







    Note that:


    1. Returning false means the default RMMV battle system's used.


    2. Coupling in ATB system is too advanced to be explained here.


    Battle Frame Update

    Spoiler





    Scene_Battle.prototype.updateBattleProcess is extended to handle the Battle Frame Update for both the default RMMV battle system and the ATB system:



    Spoiler







    /*----------------------------------------------------------------------------
    * Abandons the default battle system action input and execution flows
    *----------------------------------------------------------------------------*/
    Scene_Battle.prototype.updateBattleProcessPatb =
    Scene_Battle.prototype.updateBattleProcess;
    Scene_Battle.prototype.updateBattleProcess = function() { // Hotspot
    if ($gameSystem.is_patb()) { return this.update_patb_process(); } // Added
    this.updateBattleProcessPatb();
    }; // Scene_Battle.prototype.updateBattleProcess





    Scene_Battle.prototype.update_patb_process is added to handle the Battle Frame Update for the ATB system:

    Spoiler






    /*----------------------------------------------------------------------------
    * Reconstructs the battle system action input and execution flows for atb
    *----------------------------------------------------------------------------*/
    Scene_Battle.prototype.update_patb_process = function() { // New; Hotspot
    if (!BattleManager.can_update_patb_process()) { return; }
    // Loosens the coupling among the core and force plugins
    if (BattleManager.can_update_patb() && this.can_update_patb()) {
    this.update_patb();
    }
    //
    BattleManager.update_patb_process();
    }; // Scene_Battle.prototype.update_patb_process



    BattleManager.can_update_patb_process is added to check if the Battle Frame Update can be run for the ATB system:

    Spoiler






    /*----------------------------------------------------------------------------
    * Checks if the atb frame update or the action execution can be processed
    *----------------------------------------------------------------------------*/
    BattleManager.can_update_patb_process = function() { // v1.00a+; New; Hotspot
    if (this.isAborting() || this.isBattleEnd()) {
    this.update();
    return false;
    }
    return !$gameMessage.isBusy() && !this.updateEvent();
    }; // BattleManager.can_update_patb_process



    BattleManager.update_patb_process updates the action executions:

    Spoiler






    /*----------------------------------------------------------------------------
     *    Updates the current action when finished execution on the current target
     *----------------------------------------------------------------------------*/
    BattleManager.update_patb_process = function() { // v1.00a+; New; Hotspot
        if (this._phase === 'action' && !this.isBusy()) { this.updateAction(); }
    }; // BattleManager.update_patb_process



    /spoiler]


    ATB Wait Condition

    Spoiler





    BattleManager.can_update_patb is added to check if the ATB Frame Update can be run even when the ATB Wait Condition will be met:



    Spoiler







    /*----------------------------------------------------------------------------
    * Checks if cases always stopping the global atb frame update aren't met
    *----------------------------------------------------------------------------*/
    BattleManager.can_update_patb = function() { // New; Hotspot
    return this._phase && this._phase !== 'init';
    }; // BattleManager.can_update_patb





    Scene_Battle.prototype.can_update_patb is added to check if the ATB Wait Condition isn't met:

    Spoiler






    /*----------------------------------------------------------------------------
    * Runs the global atb wait conditions
    *----------------------------------------------------------------------------*/
    Scene_Battle.prototype.can_update_patb = function() { // New; Hotspot
    var code = $gameSystem.patb.wait_cond_code;
    if (code === "full") { return true; }
    if (BattleManager.phase === 'action') { return false; }
    if (code === "act") { return true; }
    if (this._actorWindow.active || this._enemyWindow.active) { return false; }
    if (code === "target") { return true; }
    if (this._skillWindow.active || this._itemWindow.active) { return false; }
    if (code === "item") { return true; }
    return !this._actorCommandWindow.active && !this._partyCommandWindow.active;
    }; // Scene_Battle.prototype.can_update_patb





    ATB Frame Update

    Spoiler





    Scene_Battle.prototype.update_patb runs the ATB Frame Update:



    Spoiler







    /*----------------------------------------------------------------------------
     *    Runs the global atb frame update                                        
     *----------------------------------------------------------------------------*/
    Scene_Battle.prototype.update_patb = function() { // New; Hotspot
        // Loosens the coupling among the core and event plugins
        BattleManager.update_patb();
        var actor_indices = BattleManager.action_battlers.filter(function(battler) {
            return battler.canInput();
        }).map(function(battler) { return battler.index(); });
        if (actor_indices.indexOf(this._statusWindow.index()) < 0) {
            this.update_patb_actor_selection(actor_indices);
        }
        if (BattleManager.need_patb_refresh) {
            BattleManager.need_patb_refresh = false;
            return this.refreshStatus();
        }
        this._statusWindow.refresh_patb_bars();
        //
    }; // Scene_Battle.prototype.update_patb





    Note that BattleManager._need_patb_refresh is a change notification flag used to check if the windows need to be refreshed and the Base ATB Rate needs to be recalculated.


    BattleManager.update_patb updates the Base ATB Rate, each battler's Battler ATB Clock, all Autobattle battlers, setups the new Action Execution Subject when there's none, and updates the Battle Turn Clock:

    Spoiler






    /*----------------------------------------------------------------------------
    * Runs the global and battler atb clocks and sets action execution subject
    *----------------------------------------------------------------------------*/
    BattleManager.update_patb = function() { // New; Hotspot
    this.set_patb_base();
    $gameParty.aliveMembers().forEach(function(mem) { mem.update_patb(); });
    $gameTroop.aliveMembers().forEach(function(mem) { mem.update_patb(); });
    this.update_patb_auto_battle();
    if (this._phase !== 'action') { this.process_patb_turn(); }
    this.update_patb_turn("sec");
    }; // BattleManager.update_patb





    Battler ATB Clock

    Spoiler





    BattleManager.set_patb_base sets the Base ATB Rate:



    Spoiler







    /*----------------------------------------------------------------------------
    * Sets the global base atb rate
    *----------------------------------------------------------------------------*/
    BattleManager.set_patb_base = function() { // New; Hotspot
    var last_base = this._patb_base;
    // Fps's assumed to be always 60
    this._patb_base = $gameSystem.patb.base_fill_time * 60;
    //
    if ($gameSystem.patb.atb_rate_code !== "agi") {
    this._patb_base *= this.patb_avg_agi();
    }
    this._patb_base_change = last_base !== this._patb_base;
    }; // BattleManager.set_patb_base





    Note that this._patb_base_change is the change notification flag used to inform battlers that the Base ATB Rate's changed.


    BattleManager.patb_avg_agi sets and returns the average agi of all battlers used to calculate the Base ATB Rate:

    Spoiler






    BattleManager.patb_avg_agi = function() { // New; Hotspot
    if (this._patb_agi_sum) {
    if ($gameSystem.patb.atb_rate_code === "avg_agi") {
    return this._patb_agi_sum;
    }
    if (!this._need_patb_refresh) { return this._patb_agi_sum; }
    }
    this.set_patb_avg_agi();
    return this._patb_agi_sum;
    }; // BattleManager.patb_avg_agi



    Note that this._need_patb_refresh is the change notification flag informing whether any battler's agi might be changed.


    BattleManager.set_patb_avg_agi actually calculates the average agi of all battlers:

    Spoiler






    BattleManager.set_patb_avg_agi = function() { // v0.00e+; New; Potential Hotspot
    var mems = $gameParty.aliveMembers().concat($gameTroop.aliveMembers());
    this._patb_agi_sum = mems.reduce(function(sum, mem) {
    return sum + mem.agi;
    }, 0) / mems.length;
    }; // BattleManager.set_patb_avg_agi



    Game_Battler.prototype.update_patb updates the battler's Battler ATB Clock:

    Spoiler






    /*----------------------------------------------------------------------------
    * Runs the battler atb clock
    *----------------------------------------------------------------------------*/
    Game_Battler.prototype.update_patb = function() { // New; Hotspot
    this.update_max_patb_val();
    if (this.canInput() || this.restriction() > 3) { return; }
    if ($gameSystem.patb.atb_fill_code === "delay") {
    var c = 0, s = -1;
    } else {
    var c = this._max_patb_val, s = 1;
    }
    if (this._patb_val.atb * s < c * s) { this.update_patb_val(c, s); }
    }; // Game_Battler.prototype.update_patb



    Note that:


    1. The Maximum ATB Value has to be updated before updating the ATB value.


    2. Only uninputable movable battlers can update the ATB value here.


    3. Both the normal and delay ATB update case are handled here.


    Game_Battler.prototype.update_patb_val updates the ATB value and makes actions if the battler transits from Refilling ATB to Full ATB:

    Spoiler






    /* cap: The atb value cap stopping further atb value updates
    * sign: The atb value update direction sign
    */
    Game_Battler.prototype.update_patb_val = function(cap, sign) { // New; Hotspot
    var last_val = this._patb_val.atb;
    this._patb_val.atb += this.get_patb_rate() * sign;
    this._patb_val_change.atb = last_val !== this._patb_val.atb;
    if (this._patb_val.atb * sign < cap * sign) { return; }
    this._patb_val.atb = cap;
    this.makeActions();
    }; // Game_Battler.prototype.update_patb_val



    Game_Battler.prototype.get_patb_rate updates and returns the cached ATB Rate:

    Spoiler






    /*----------------------------------------------------------------------------
     *    Reevaluates the atb rate only if any of its determinators can change    
     *----------------------------------------------------------------------------*/
    Game_Battler.prototype.get_patb_rate = function() { // New; Hotspot
        if ($gameSystem.patb.atb_rate_code === "abs_agi") { return this.agi; }
        if (this.can_patb_rate_change()) {
            this._patb_rate.atb = this.agi * this._max_patb_val;
            this._patb_rate.atb /= BattleManager.patb_base;
            this._max_patb_val_change = false;
            if (this._patb_rate.atb < Number.EPSILON * this._max_patb_val * 100) {
                console.log("Atb rate of " + this.name() + " is too small");
            }
        }
        return this._patb_rate.atb;
    }; // Game_Battler.prototype.get_patb_rate



    Note that this._patb_rate.atb < Number.EPSILON * this._max_patb_val * 100 is to ensure the ATB Rate won't be so small that it takes more than 1 frame to have detectable ATB value gains.


    Game_Battler.prototype.can_patb_rate_change checks if the ATB Rate needs to be reevalauted:

    Spoiler






    /*----------------------------------------------------------------------------
    * Fixes bugs from changing battler agi without changing the base atb rate
    *----------------------------------------------------------------------------*/
    Game_Battler.prototype.can_patb_rate_change = function() {
    // v1.00c+; New; Hotspot
    if (this._max_patb_val_change) { return true; }
    return BattleManager.patb_base_change || BattleManager.need_patb_refresh;
    }; // Game_Battler.prototype.can_patb_rate_change



    Note that:


    1. The ATB Rate is based on the Maximum ATB Value.


    2. BattleManager.need_patb_refresh will be set as true whenever the battler's agi changes.


    3. It's possible that the Base ATB Rate, which is based on the sum of all battlers' agi, can remain unchanged while a batter's agi changes.


    Battle Turn Clock

    Spoiler





    While the ATB Frame Update updates the Battle Turn Clock with second as the unit, BattleManager.endAction updates that with the number of executed actions as unit:



    Spoiler







    BattleManager.endActionPatb = BattleManager.endAction;
    BattleManager.endAction = function() {
    this.endActionPatb();
    if ($gameSystem.is_patb()) { this.update_patb_turn("act"); } // Added
    }; // BattleManager.endAction





    BattleManager.update_patb_turn updates the Battle Turn Clock:

    Spoiler






    /*----------------------------------------------------------------------------
    * Runs the global battle turn clock
    *----------------------------------------------------------------------------*/
    // unit: The battle turn counter unit
    BattleManager.update_patb_turn = function(unit) { // New; Hotspot
    if (unit !== $gameSystem.patb.turn_unit_code) { return; }
    this._patb_turn_clock[unit] += 1;
    // Loosens the coupling among the core and turn plugins
    if (this._patb_turn_clock[unit] >= $gameSystem.max_patb_turn_unit(unit)) {
    this._patb_turn_clock[unit] = 0;
    this.end_patb_turn();
    }
    //
    }; // BattleManager.update_patb_turn



    Game_System.prototype.max_patb_turn_unit returns the maximum value of the Battle Turn Clock for the current unit:

    Spoiler






    /*----------------------------------------------------------------------------
    * Loosen the coupling among the core and turn plugins
    *----------------------------------------------------------------------------*/
    // unit: The battle turn counter unit
    Game_System.prototype.max_patb_turn_unit = function(unit) {
    // v0.00c+; New; Hotspot
    // Fps's assumed to be always 60
    if (unit === "sec") { return this._patb.max_turn_unit * 60; }
    //
    return this._patb.max_turn_unit;
    }; // Game_System.prototype.max_patb_turn_unit



    Note that the unit "second" is actually run in frames, so the number of frames per second, which is hardcoded as 60 by the RMMV default, is multiplied here.


    Setup new inputable actors

    Spoiler





    Scene_Battle.prototype.update_patb_actor_selection updates the input windows when the current ones are outdated:



    Spoiler







    /*----------------------------------------------------------------------------
    * Setups new inputable actors and deselects selected uninputable ones
    *----------------------------------------------------------------------------*/
    // actor_indices: The indices of all currently inputable actors
    Scene_Battle.prototype.update_patb_actor_selection = function(actor_indices) {
    // New; Hotspot
    // Deactivates the active input windows that should be no longer active
    if (this._statusWindow.index() >= 0) {
    this.close_patb_selection_windows();
    } else if (this._partyCommandWindow.active) {
    if (actor_indices.length <= 0) {
    this._partyCommandWindow.deactivate();
    this._partyCommandWindow.close();
    }
    return;
    }
    //
    if (actor_indices.length <= 0) { return; }
    BattleManager.actor_index = actor_indices[0];
    this.startActorCommandSelection();
    }; // Scene_Battle.prototype.update_patb_actor_selection





    Note that this function will only be called when the status window isn't selecting an inputable actor.


    Also, there are 6 cases to consider:


    1. The status window was selecting a previously inputable actor and there are some other inputable actors


    - In this case, all input windows of the previously inputable actor should be closed and a new inputable actor should be selected with an actor command window being setup instead.


    2. The status window was selecting a previously inputable actor and there are no inputable actors


    - In this case, all input windows of the previously inputable actor should be closed and the status window should select nothing.


    3. The party command window was active and there are some other inputable actors


    - In this case, nothing should be done here.


    4. The party command window was active and there are no inputable actors


    - In this case, the party command window should be closed.


    5. The status window wasn't selecting anything and the party command window wasn't active, and there are some other inputable actors


    - In this case, a new inputable actor should be selected with an actor command window being setup instead.


    6. The status window wasn't selecting anything and the party command window wasn't active, and there are no inputable actors


    - In this case, nothing should be done here.


    Scene_Battle.prototype.close_patb_selection_windows closes all the input windows and shows the status window:

    Spoiler






    /*----------------------------------------------------------------------------
    * Closes all selection windows for the selected uninputable actor
    *----------------------------------------------------------------------------*/
    Scene_Battle.prototype.close_patb_selection_windows = function() { // New
    var windows = [this._actorWindow, this._enemyWindow, this._skillWindow];
    windows.push(this._itemWindow);
    if (windows.some(function(window) { return window.visible; })) {
    this._statusWindow.open();
    this._statusWindow.show();
    }
    windows.concat([this._actorCommandWindow]).forEach(function(window) {
    if (!window.active) { return; }
    window.hide();
    window.deactivate();
    window.close();
    });
    this._statusWindow.deselect();
    }; // Scene_Battle.prototype.close_patb_selection_windows



    Note that using all open, show, hide, deactivate and close is just to play safe here.


    Updates Actor ATB bars

    Spoiler





    Recall that now it's possible for any ATB Frame Update to refresh all windows, including the status window, due to the fact that the former can be run while the latters are shown.


    Also, bear in mind that BattleManager.need_patb_refresh is the change notification flag to mark that those windows need to be refreshed.


    So if it's raised, Scene_Battle.prototype.refreshStatus, which is extended to refresh all input windows as well, should be called:



    Spoiler







    Scene_Battle.prototype.refreshStatusPatb = Scene_Battle.prototype.refreshStatus;
    Scene_Battle.prototype.refreshStatus = function() {
    if ($gameSystem.is_patb()) { this.refresh_patb_windows(); } // Added
    this.refreshStatusPatb();
    }; // Scene_Battle.prototype.refreshStatus





    Scene_Battle.prototype.refresh_patb_windows refreshes all input windows:

    Spoiler






    Scene_Battle.prototype.refresh_patb_windows = function() { // New
    var windows = [this._actorWindow, this._enemyWindow, this._skillWindow];
    windows = windows.concat([this._itemWindow, this._actorCommandWindow]);
    windows.forEach(function(w) { if (w.visible) { w.refresh(); } });
    }; // Scene_Battle.prototype.refresh_patb_windows



    On the other hand, if that flag's not raised, just updating all actor ATB bars, which is done in Window_BattleStatus.prototype.refresh_patb_bars, will suffice:

    Spoiler






    /*----------------------------------------------------------------------------
    * Refreshes the atb bars only instead of the whole status window per frame
    *----------------------------------------------------------------------------*/
    Window_BattleStatus.prototype.refresh_patb_bars = function() { // New; Hotspot
    var patb = $gameSystem.patb, actor, rect, type;
    var ox = patb.hp_bar_w + patb.mp_bar_ox + patb.mp_bar_w + patb.atb_bar_ox;
    if ($dataSystem.optDisplayTp) { ox += patb.tp_bar_ox + patb.tp_bar_w; }
    for (var index = 0, max = this.maxItems(); index < max; index++) {
    actor = $gameParty.battleMembers()[index];
    if (!actor) { continue; }
    type = actor.patb_type();
    // Refreshes the atb bar only if the atb value changed
    if (!actor.patb_val_change[type]) { continue; }
    rect = this.gaugeAreaRect(index);
    this.draw_actor_patb(actor, rect.x + ox, rect.y, type);
    actor.patb_val_change[type] = false;
    //
    }
    }; // Window_BattleStatus.prototype.refresh_patb_bars



    Recall that patb_val_change is the ATB value change notification flag which will be raised if the ATB value or the Maximum ATB Value changes.


    Window_BattleStatus.prototype.draw_actor_patb actually redraws the actor ATB bars:

    Spoiler






    /* actor: The actor using the atb bar
    * x: The atb bar x position
    * y: The atb bar y position
    * type: The current atb type
    */
    Window_BattleStatus.prototype.draw_actor_patb = function(actor, x, y, type) {
    // New; Hotspot
    var max = actor.max_patb_val, text, w = $gameSystem.patb.atb_bar_w;
    var colors = actor.patb_colors(type);
    var c0 = this.textColor(colors[0]), c1 = this.textColor(colors[1]);
    // Ensures the atb bar won't fill over its max length in delay
    this.drawGauge(x, y, w, Math.min(actor.patb_val[type], max) / max, c0, c1);
    //
    this.changeTextColor(this.systemColor());
    text = $gameSystem.patb.atb_bar_text;
    this.drawText(text, x, y, this.textWidth(text));
    }; // Window_BattleStatus.prototype.draw_actor_patb



    Note that Math.min(actor.patb_val[type], max) is used to prevent the ATB value from exceeding the Maximum ATB Value in the delay ATB fill mode.


    Starting ATB Value

    Spoiler





    BattleManager.startBattle is extended to set all battlers' Starting ATB Value upon battle start:



    Spoiler







    /*----------------------------------------------------------------------------
    * Sets the starting atb value for all battlers as well
    *----------------------------------------------------------------------------*/
    BattleManager.startBattlePatb = BattleManager.startBattle;
    BattleManager.startBattle = function() {
    this.startBattlePatb();
    if ($gameSystem.is_patb()) { this.start_patb_battle(); } // Added
    }; // BattleManager.startBattle





    BattleManager.start_patb_battle asks each battler to set his/her/its Starting ATB Value according to the battle start type:

    Spoiler






    BattleManager.start_patb_battle = function() { // New
    this._phase = 'turn';
    var start = this._preemptive ? "preempt" : this._surprise ? "surprise" : "";
    $gameParty.members().forEach(function(m) { m.set_start_patb_val(start); });
    $gameTroop.members().forEach(function(m) { m.set_start_patb_val(start); });
    }; // BattleManager.start_patb_battle



    Game_Battler.prototype.set_start_patb_val initializes all ATB parameters, including the Maximum ATB Value, before setting the Starting ATB Value, which causes the battler to make actions if it leads to Full ATB:

    Spoiler






    // start: The battle start type
    Game_Battler.prototype.set_start_patb_val = function(start) { // New
    this.init_patb();
    this.update_max_patb_val();
    Game_BattlerBase.prototype.set_start_patb_val.call(this, start);
    if (this._patb_val.atb >= this._max_patb_val) { this.makeActions(); }
    if ($gameSystem.patb.atb_fill_code === "delay") {
    this._patb_val.atb = this._max_patb_val - this._patb_val.atb;
    }
    this._patb_val_change.atb = true;
    }; // Game_Battler.prototype.set_start_patb_val



    Note that the ATB value will be reversed if the ATB fill mode is delay.


    Game_BattlerBase.prototype.set_start_patb_val actually sets the battler's Starting ATB Value:

    Spoiler






    // start: The battle start type
    Game_BattlerBase.prototype.set_start_patb_val = function(start) { // New
    if(!this.canMove() || start === "preempt" && this.isEnemy()) {
    this._patb_val.atb = 0;
    } else if (start === "surprise" && this.isActor()) {
    this._patb_val.atb = 0;
    } else if (start === "preempt" && this.isActor()) {
    this._patb_val.atb = this._max_patb_val;
    } else if (start === "surprise" && this.isEnemy()) {
    this._patb_val.atb = this._max_patb_val;
    } else if ($gameSystem.patb.atb_start_code === "agi") {
    this._patb_val.atb = this._max_patb_val * this.agi / this.paramMax(6);
    } else {
    this._patb_val.atb = 0;
    }
    }; // Game_BattlerBase.prototype.set_start_patb_val





    Reset Battler ATB Clock

    Spoiler





    Game_Battler.prototype.on_reset_patb, which serves as a helper function, handles both the delay ATB fill mode and the default one:



    Spoiler







    /*----------------------------------------------------------------------------
    * Helper function easing the use of reset_patb with various atb fill codes
    *----------------------------------------------------------------------------*/
    // reset: The battler action reset flag
    Game_Battler.prototype.on_reset_patb = function(reset) { // New
    var val;
    val = $gameSystem.patb.atb_fill_code === "delay" ? this._max_patb_val : 0;
    this.reset_patb(reset, val);
    }; // Game_Battler.prototype.on_reset_patb





    Game_Battler.prototype.reset_patb resets the Battler ATB Clock:

    Spoiler






    /* reset: The battler action reset flag
    * val: The atb reset value
    */
    Game_Battler.prototype.reset_patb = function(reset, val) { // New
    if (reset) { this.reset_patb_val(val); }
    this.corr_patb_val();
    this.clearActions();
    var index = BattleManager.action_battlers.indexOf(this);
    if (index >= 0) { BattleManager.action_battlers.splice(index, 1); }
    if (BattleManager.actor() === this) { BattleManager.clearActor(); }
    this.set_patb_refresh();
    }; // Game_Battler.prototype.reset_patb



    Game_Battler.prototype.reset_patb_val actually resets the ATB value:

    Spoiler






    // val: The atb reset value
    Game_Battler.prototype.reset_patb_val = function(val) { // v0.00e+; New
    if ($gameSystem.patb.atb_fill_code === "delay") {
    this._patb_val.atb += val;
    } else {
    this._patb_val.atb = val;
    }
    this._patb_val_change.atb = true;
    }; // Game_Battler.prototype.reset_patb_val



    Game_Battler.prototype.corr_patb_val ensures an Unactable battler won't have Full ATB:

    Spoiler






    /*----------------------------------------------------------------------------
    * Prevents the atb value of an unactable battler from reaching its cap
    *----------------------------------------------------------------------------*/
    Game_Battler.prototype.corr_patb_val = function() { // v1.00b+; New
    if ($gameSystem.patb.atb_fill_code === "delay") {
    var cap = 0, sign = -1;
    } else {
    var cap = this._max_patb_val, sign = 1;
    }
    if (this._patb_val.atb * sign < cap * sign) { return; }
    // Multiplies the difference by 100% to work with very small max atb value
    var max = Math.min(this._max_patb_val, 1);
    this._patb_val.atb -= Number.EPSILON * max * 100 * sign;
    //
    this._patb_val_change.atb = true;
    }; // Game_Battler.prototype.corr_patb_val



    Note that Number.EPSILON * max * 100 is derived via extensive testings :D


    Battler inputability check

    Spoiler





    Game_BattlerBase.prototype.canInput is extended to handle the battler inputability check for both the default RMMV battle system and the ATB system:



    Spoiler







    Game_BattlerBase.prototype.canInputPatb = Game_BattlerBase.prototype.canInput;
    Game_BattlerBase.prototype.canInput = function() { // Hotspot
    // Rewritten
    if (!this.canInputPatb()) { return false; }
    return !$gameSystem.is_patb() || this.can_input_patb();
    //
    }; // Game_BattlerBase.prototype.canInput





    Game_BattlerBase.prototype.can_input_patb handles the battler inputability check for the ATB system:

    Spoiler






    Game_BattlerBase.prototype.can_input_patb = function() { // New; Hotspot
    if (this._actions.length <= 0 || !this.isActor()) { return false; }
    return !this._actions.every(function(act) { return act.patb_confirm; });
    }; // Game_BattlerBase.prototype.can_input_patb



    Note the patb_confirm is the action confirmation flag.


    Party Escape Attempt Availability

    Spoiler





    Scene_Battle.prototype.commandEscape is extended to handle party escapes for both the default RMMV battle system and the ATB system:



    Spoiler







    Scene_Battle.prototype.commandEscapePatb = Scene_Battle.prototype.commandEscape;
    Scene_Battle.prototype.commandEscape = function() { // v1.00a+
    // Added to ensure party escape attempt won't trigger when the battle's busy
    if ($gameSystem.is_patb() && !BattleManager.can_patb_esc()) { return; }
    //
    this.commandEscapePatb();
    }; // Scene_Battle.prototype.commandEscape





    BattleManager.can_patb_esc checks if the party escape attempt's alloowed to be triggered:

    Spoiler






    BattleManager.can_patb_esc = function() { // v1.00a+; New
    if (this.isBusy()) { return false; }
    return this._phase && this._phase !== 'init' && this._phase !== 'action';
    }; // BattleManager.can_patb_esc



    Note that actions can only be executed when the battle's Executing Actions.


    Autobattle Check

    Spoiler





    BattleManager.update_patb_auto_battle ensures battlers becoming Autobattle when they've Full ATB will immediately make Autobattle actions:



    Spoiler







    /*----------------------------------------------------------------------------
    * Ensures inputable battler becoming autobattle will make autobattle acts
    *----------------------------------------------------------------------------*/
    BattleManager.update_patb_auto_battle = function() { // v1.00b+; New; Hotspot
    var reset = $gameSystem.patb.atb_reset_code !== "clear";
    this._actionBattlers.forEach(function(battler) {
    if (!battler.isAutoBattle() || !battler.can_input_patb()) { return; }
    battler.on_reset_patb(reset);
    });
    }; // BattleManager.update_patb_auto_battle





    Note that can_input_patb is used instead of using canInput as the latter always returns false for Autobattle battlers.


    Maximum ATB Value

    Spoiler





    Game_Battler.prototype.update_max_patb_val updates the Maximum ATB Value:



    Spoiler







    /*----------------------------------------------------------------------------
    * Rereads the effective max atb val notetag only if its values can change
    *----------------------------------------------------------------------------*/
    Game_Battler.prototype.update_max_patb_val = function() { // New; Hotspot
    if (!this.are_patb_battler_changed("max_patb_val")) { return; }
    var f = this._patb_val.atb >= this._max_patb_val;
    var max = $gameSystem.patb.max_atb_val;
    this._max_patb_val = this.set_multi_patb_notes(max, "max_patb_val");
    if (this._max_patb_val < Number.EPSILON * 10000) {
    console.log("Max atb value of " + this.name() + " is too small");
    }
    this._max_patb_val_change = true;
    if ($gameSystem.patb.atb_fill_code !== "delay") { this.corr_patb(f); }
    this.mark_patb_val_change();
    }; // Game_Battler.prototype.update_max_patb_val





    Note that this._max_patb_val < Number.EPSILON * 10000 is to ensure the Maximum ATB Value won't be so small that the ATB value correction in Reset Battler ATB Clock won't work well.


    Game_Battler.prototype.corr_patb ensures battlers becoming Full ATB will also be Actable by making actions, those becoming Refilling ATB will also be Unactable by resetting the Battler ATB Clock, and the ATB value won't exceed the Maximum ATB Value:

    Spoiler






    /*----------------------------------------------------------------------------
    * Corrects the actability and all atb values after changing max atb value
    *----------------------------------------------------------------------------*/
    // full: The full atb before changing the max atb value flag
    Game_Battler.prototype.corr_patb = function(full) { // New; Potential Hotspot
    if (!full && this._patb_val.atb >= this._max_patb_val) {
    this.makeActions();
    } else if (full && this._patb_val.atb < this._max_patb_val) {
    this.reset_patb(true, 0);
    }
    Object.keys(this._patb_val).forEach(function(type) {
    if (this._patb_val[type] <= this._max_patb_val) { return; }
    this._patb_val[type] = this._max_patb_val;
    }, this);
    }; // Game_Battler.prototype.corr_patb



    Note that this function doesn't need to be called when the ATB fill mode is delay, as ATB value overflow's normal and the battle phase won't transits due to the change of the Maximum ATB Value in that case.


    Game_Battler.prototype.mark_patb_val_change ensures all actor atb bars will be redrawn if the Maximum ATB Value changed:

    Spoiler






    /*----------------------------------------------------------------------------
    * Notifies the atb bars to be redrawn
    *----------------------------------------------------------------------------*/
    Game_Battler.prototype.mark_patb_val_change = function() {
    // v1.00b+; New; Potential Hotspot
    Object.keys(this._patb_val_change).forEach(function(type) {
    this._patb_val_change[type] = true;
    }, this);
    }; // Game_Battler.prototype.mark_patb_val_change





    Notetag Management

    Spoiler





    This itself is a topic that's too advanced to be covered here, so I'll only quote the related codes.


    Game_Battler.prototype.set_multi_patb_notes combines the values of notetags that can be used together:



    Spoiler







    /* val: The configuration value as the initial notetag value
    * note: The notetag type with its values to be set
    */
    Game_Battler.prototype.set_multi_patb_notes = function(val, note) {
    // New; Potential Hotspot
    // Loosens the coupling among the core, delay, rate and start plugins
    this.patb_note_data().forEach(function(type) {
    type.forEach(function(data) {
    data.meta[note].forEach(function(vals) {
    val = this.operate_patb_notes(val, vals[0], vals[1]);
    }, this);
    }, this);
    }, this);
    return val;
    //
    }; // Game_Battler.prototype.set_multi_patb_notes





    Game_Battler.prototype.operate_patb_notes uses operators stored in notetags to combine their values:

    Spoiler






    /* current: The current value to be operated
    * operator: The notetag operator
    * val: The notetag value to be operated with the current one
    */
    Game_Battler.prototype.operate_patb_notes = function(current, operator, val) {
    // New; Potential Hotspot
    switch (operator) {
    case "=": return val;
    case "+": return current + val;
    case "-": return current - val;
    case "*": return current * val;
    case "/": return current / val;
    case "%": return current % val;
    default:
    console.log("Invalid operator " + operator + " as notetag values");
    return current;
    }
    }; // Game_Battler.prototype.operate_patb_notes



    Game_Battler.prototype.set_patb_notes reads the value of the 1st found effective notetag:

    Spoiler






    // note: The notetag type with its values to be set
    Game_Battler.prototype.set_patb_notes = function(note) {
    // New; Potential Hotspot
    // Loosens the coupling among the core, order and se plugins
    var data = this.patb_note_data();
    for (var index = 0, length = data.length; index < length; index++) {
    for (var d = data[index], i = 0, l = d.length; i < l; i++) {
    if (d && d.meta[note]) { return d.meta[note]; }
    }
    }
    return null;
    //
    }; // Game_Battler.prototype.set_patb_notes



    Game_Battler.prototype.patb_note_data combines all data storing notetags used by the a battler:

    Spoiler







    Game_Battler.prototype.patb_note_data = function() {
    // v0.00e+; New; Potential Hotspot
    // Increases the chance for this plugin to work when the battler's neither
    if (this.isEnemy()) {
    return [this.states(), [this.enemy()]];
    } else if (this.isActor()) {
    var d = [this.states()];
    d.push(this.equips().filter(function(equip) { return equip; }));
    return d.concat([[this.currentClass()], [this.actor()]]);
    }
    return [this.states()];
    //
    }; // Game_Battler.prototype.patb_note_data



    The rest is unrelated to ATB system and should be easy, simple and small enough for you to thoroughly comprehend it yourselves.


    Change Notification Flags

    Spoiler





    This itself is a topic that's too advanced to be covered here, so I'll only quote the related codes.


    Game_Battler.prototype.refresh is extended to raise all relavant change notification flags upon battler refresh:



    Spoiler








    Game_Battler.prototype.refreshPatb = Game_Battler.prototype.refresh;
    Game_Battler.prototype.refresh = function() {
    this.refreshPatb();
    if ($gameSystem.is_patb()) { this.set_patb_refresh(); } // Added
    }; // Game_Battler.prototype.refresh





    Game_Battler.prototype.set_patb_refresh raises all relavant change notification flags:

    Spoiler







    Game_Battler.prototype.set_patb_refresh = function() { // New
    Object.keys(this._patb_battler_change).forEach(function(note) {
    this._patb_battler_change[note] = true;
    }, this);
    BattleManager.need_patb_refresh = true;
    }; // Game_Battler.prototype.set_patb_refresh



    Game_Battler.prototype.are_patb_battler_changed checks if a change notification flag's raised and reset it if that's the case:

    Spoiler







    /*----------------------------------------------------------------------------
    * Checks if the effective notetag list or any of their values changed
    *----------------------------------------------------------------------------*/
    // note: The notetags to be checked for changes
    Game_Battler.prototype.are_patb_battler_changed = function(note) {
    // New; Hotspot
    var ch = this._patb_battler_change[note] || this._patb_note_change[note];
    this._patb_battler_change[note] = this._patb_note_change[note] = false;
    return ch;
    }; // Game_Battler.prototype.are_patb_battler_changed





    Next Command

    Spoiler





    Scene_Battle.prototype.selectNextCommand is extended to handle Next Command for both the default RMMV battle system and the ATB system:



    Spoiler








    Scene_Battle.prototype.selectNextCommandPatb =
    Scene_Battle.prototype.selectNextCommand;
    Scene_Battle.prototype.selectNextCommand = function() { // v1.00a+
    // Added to avoid setting up the party command window upon next command
    if ($gameSystem.is_patb()) { return this.select_next_patb_command(); }
    //
    this.selectNextCommandPatb();
    }; // Scene_Battle.prototype.selectNextCommand





    Scene_Battle.prototype.select_next_patb_command ensures it's impossible for Next Command to setup the party command window:

    Spoiler







    Scene_Battle.prototype.select_next_patb_command = function() { // v1.00b+; New
    BattleManager.selectNextCommand();
    if (BattleManager.isInputting() && BattleManager.actor()) {
    return this.startActorCommandSelection();;
    }
    this.endCommandSelection();
    }; // Scene_Battle.prototype.select_next_patb_command





    ATB Bar Colors

    Spoiler





    Game_Battler.prototype.patb_colors sets and returns the ATB bar colors with respect of the current ATB fill mode:



    Spoiler








    /*----------------------------------------------------------------------------
    * Rereads the effective atb color notetag only if its values can change
    *----------------------------------------------------------------------------*/
    // type: The current atb type
    Game_Battler.prototype.patb_colors = function(type) { // New; Hotspot
    if (this._patb_val[type] > this._max_patb_val) { type += "_overlay"; }
    if (this.are_patb_battler_changed(type + "_color")) {
    this.set_patb_colors(type);
    }
    return this._patb_colors[type];
    }; // Game_Battler.prototype.patb_colors





    Note that there's no need to raise any change notification flag here when the ATB bar colors changed(too advanced to be explained here).


    Game_Battler.prototype.set_patb_colors sets the ATB bar colors with respect of the current ATB fill mode:

    Spoiler







    // type: The current atb type
    Game_Battler.prototype.set_patb_colors = function(type) {
    // New; Potential Hotspot
    this._patb_colors[type] = this.set_patb_notes("p" + type + "_colors");
    if (this._patb_colors[type]) { return; }
    var patb = $gameSystem.patb;
    this._patb_colors[type] = [patb[type + "_c1"], patb[type + "_c2"]];
    }; // Game_Battler.prototype.set_patb_colors





    Input Window Closing/Refresh

    Spoiler





    BattleManager.processVictory and BattleManager.processDefeat is extended to close all windows in the ATB system upon battle end:



    Spoiler








    BattleManager.processVictoryPatb = BattleManager.processVictory;
    BattleManager.processVictory = function() {
    // Added
    if ($gameSystem.is_patb() && $gameParty.inBattle()) {
    SceneManager.scene.close_patb_windows();
    }
    //
    this.processVictoryPatb();
    }; // BattleManager.processVictory

    BattleManager.processDefeatPatb = BattleManager.processDefeat;
    BattleManager.processDefeat = function() {
    // Added
    if ($gameSystem.is_patb() && $gameParty.inBattle()) {
    SceneManager.scene.close_patb_windows();
    }
    //
    this.processDefeatPatb();
    }; // BattleManager.processDefeat





    Scene_Battle.prototype.close_patb_windows closes all input windows:

    Spoiler







    Scene_Battle.prototype.close_patb_windows = function() { // New
    var windows = [this._actorWindow, this._enemyWindow, this._skillWindow];
    windows = windows.concat([this._itemWindow, this._actorCommandWindow]);
    windows.concat([this._partyCommandWindow]).forEach(function(window) {
    window.hide();
    window.deactivate();
    window.close();
    });
    }; // Scene_Battle.prototype.close_patb_windows



    BattleManager.refreshStatus and Scene_Battle.prototype.refreshStatus are extended to refresh all input windows when any of them might need to be refreshed:

    Spoiler







    BattleManager.refreshStatusPatb = BattleManager.refreshStatus;
    BattleManager.refreshStatus = function() { // v0.00e+
    // Added
    if ($gameSystem.is_patb() && $gameParty.inBattle()) {
    return SceneManager.scene.refresh_patb_windows();
    }
    //
    this.refreshStatusPatb();
    }; // BattleManager.refreshStatus


    Code:
    
    [I]
    Scene_Battle.prototype.refreshStatusPatb = Scene_Battle.prototype.refreshStatus;
    Scene_Battle.prototype.refreshStatus = function() {
        if ($gameSystem.is_patb()) { this.refresh_patb_windows(); } // Added
        this.refreshStatusPatb();
    }; // Scene_Battle.prototype.refreshStatus
    [/I]



    Scene_Battle.prototype.refresh_patb_windows refreshes all input windows:

    Spoiler







    Scene_Battle.prototype.refresh_patb_windows = function() { // New
    var windows = [this._actorWindow, this._enemyWindow, this._skillWindow];
    windows = windows.concat([this._itemWindow, this._actorCommandWindow]);
    windows.forEach(function(w) { if (w.visible) { w.refresh(); } });
    }; // Scene_Battle.prototype.refresh_patb_windows



    Scene_Battle.prototype.updateStatusWindow is extended to open all active input windows that are previously closed:

    Spoiler







    Scene_Battle.prototype.updateStatusWindowPatb =
    Scene_Battle.prototype.updateStatusWindow;
    Scene_Battle.prototype.updateStatusWindow = function() { // Hotspot
    // Added
    if ($gameSystem.is_patb() && this.isActive()) {
    this.update_patb_status_window();
    }
    //
    this.updateStatusWindowPatb();
    }; // Scene_Battle.prototype.updateStatusWindow



    This can handle for cases like, when inputting actions for inputable actors, a message suddenly popups, causing all input windows to be closed. When that message closes, all those input windows will have to be opened again as they're still active.


    Scene_Battle.prototype.update_patb_status_window checks if those active but closed input windows can be opened now:

    Spoiler







    Scene_Battle.prototype.update_patb_status_window = function() {
    // v1.00b+; New; Hotspot
    if ($gameMessage.isBusy() || this._messageWindow.isClosing()) {
    return;
    }
    if ($gameParty.isAllDead() || $gameTroop.isAllDead()) { return; }
    this.open_patb_selection_windows();
    }; // Scene_Battle.prototype.update_patb_status_window



    Scene_Battle.prototype.open_patb_selection_windows opens all active but closed input windows:

    Spoiler







    Scene_Battle.prototype.open_patb_selection_windows = function() {
    // New; Hotspot
    var windows = [this._actorWindow, this._enemyWindow, this._skillWindow];
    windows.push(this._itemWindow);
    if (windows.concat([this._actorCommandWindow]).some(function(window) {
    return window.active;
    })) { return this._actorCommandWindow.open(); }
    if (this._partyCommandWindow.active) { this._partyCommandWindow.open(); }
    }; // Scene_Battle.prototype.open_patb_selection_windows





    Bug Fixes

    Spoiler





    Scene_Battle.prototype.updateWindowPositions is extended to ensure the actor window will always completely cover the status window in the ATB system:



    Spoiler








    Scene_Battle.prototype.updateWindowPositionsPatb =
    Scene_Battle.prototype.updateWindowPositions;
    Scene_Battle.prototype.updateWindowPositions = function() { // v1.00a+; Hotspot
    this.updateWindowPositionsPatb();
    // Added to ensure the actor window will completely cover the status window
    if ($gameSystem.is_patb() && this._actorWindow.active) {
    this._actorWindow.x = this._statusWindow.x;
    }
    //
    }; // Scene_Battle.prototype.updateWindowPositions





    It's to prevent inconsistencies between those 2 windows.


    The rest are just implementing the configurations, which should be easy, simple and small enough for you to thoroughly comprehend yourselves, those already mentioned before, or those too advanced to be covered here.


     


    Summary

    Spoiler





    A basic ATB system having all core functionalities is the one having all features needed for any ATB system and giving a reasonable amount of control and freedom to users while still keeping the system easy, simple and user-friendly.


    DoubleX RMMV Popularized ATB Core is the core plugin of such system using the Core Addon Approach, meaning that plugin also has features that would be much less desirable if they were implemented by addons instead, and all the optional features will be implemented in addon plugins needing the core plugin to work.


    Configurations hardcoded in DoubleX RMMV Minimalized ATB, especially ATB Wait Condition, becomes configurable by users here, leading to the below to be needed as well:


    - Input window closing/refresh, as the ATB Frame Update can be run while some input windows are active


    - Autobattle check, as a battler with Full ATB can become AutoBattle


    - Party escape attempt availability, as it'd be possible for it to be triggered when Executing Actions


    - Next Command, as it'd be possible for it to setup a party command window


    The core plugin also lets users does the below:


    - Use notetags, leading to the need for notetag management, which is too advanced to be covered here


    - Change the battle system on the fly, leading to the need for the battle system check, as different battle system needs different codes to handle


    - Change the Maximum ATB Value on the fly, leading to the need to correct the actor ATB bar and the whole Battler ATB Clock, as it could change the ATB bar fill percent, casue ATB value overflow and battler phase transitions


    - Reset the Battler ATB Clock without resetting the ATB value as well, leading to the need to ensure Unactable battlers won't have Full ATB, to avoid breaking this invariant


    - Change the ATB bar colors on the fly, leading to the need to ensure the actor ATB bars will always show the correct colors, as they can be changed at any frame


    - Some other various stuffs


    Besides those involved in the implementation of DoubleX RMMV Minimalized ATB, its implementations also involve the below:


    - Battle system checker checking the current battle system


    - Battle Frame Update needing to handle both available and unavailable battle systems


    - ATB Wait Conditions ranging from the strictest(hardest to be met) to the loosest(easiest to be met)


    - ATB Frame Update with the added responsibility to refresh all windows when needed and update the Base ATB Rate


    - Battler ATB Clock which becomes dependent on the Base ATB Rate and the Maximum ATB Value


    - Battle Turn Clock which added the number of executed actions as the unit


    - Setup new inputable actors which becomes to have 6 cases to consider


    - Updates actor ATB bars which needs to handle the delay ATB fill mode as well


    - Starting ATB Value which changes from being hardcoded to become configurable as users


    - Reset Battler ATB Clock which is possible to not resetting the ATB value and needed to ensure Unactable battlers won't have Full ATB


    - Battler inputability check which needs to handle  both available and unavailable battle systems


    - Party escape attempt availability which ensures it won't be trigger when Executing Actions


    - Autobattle check which ensures battler having Full ATB will immediately make Autobattle actions upob becoming Autobattle


    - Maximum ATB Value which ensures its changes won't screw up the Battler ATB Clock nor the actor ATB bars


    - Notetag management and change notification flags which are both too advaned to be covered here


    - Next Command which ensures party command window won't be setup immediately afterwards


    - ATB bar colors which ensures the actor ATB bars will always correctly display them


    - Input window closing/refresh which ensures their states are always correct and up to date


    - Bug fixes including ensuring the actor window will always completely cover the status window


    Coupling, which is also an important concern of this plugin, is too advanced to be covered here.



     


     


     


     


     


     


     


     


     


     


     


     


     


     


     


     


     


     


     


     


     
     
    Last edited by a moderator: Feb 6, 2016
    #6
  7. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Now I'm going to talk about Comparisons between DoubleX RMMV Minimalized ATB and DoubleX RMMV Popularized ATB Core.


    Shortcuts taken in writing DoubleX RMMV Minimalized ATB

    I'll only talk about those essential to any ATB system here :)


    I'll also use MATB to refer to DoubleX RMMV Minimalized ATB and PATB Core to refer to DoubleX RMMV Popularized ATB Core :D


    ATB Wait Condition




    As the ATB Wait Condition in MATB will always be met whenever there are inputable actors, which is the same as there are active input windows, it can just use Scene_Battle.prototype.isAnyInputWindowActive.


    That's no longer the case in PATB Core, however, as its ATB Wait Condition can be something other than the loosest one(easiest to be met). Even if it were hardcoded, as long as it wouldn't be the loosest one, some new codes would have to be written, albeit it could be as simple as always returning true in the case of the strictest ATB Wait Condition(hardest to be met).



    Setup new inputable actors

    In MATB, setting up new inputable actors just involves the following:


    1. Checks if the status window is selecting an inputable actor and skip 2 and 3 if that's the case


    2. Deselects the status window if it's selecting an actor that's no longer inputable


    3. Selects an actor and setups an actor command window for that actor if there are inputable ones


    That's because MATB can only have the below 3 cases when the ATB Frame Update's running:


    1. The status window isn't selecting any actor before the ATB Frame Update and there's no inputable one after so


    2. The status window isn't selecting any actor before the ATB Frame Update and there are inputable ones after so


    3. The status window is selecting an actor before the ATB Frame Update and that actor's no longer inputable after so


    On a side note: Strictly speaking, 3 isn't technically correct, as the actor will become uninputable as soon as all actions are completed inputted, which won't happen during an ATB Frame Update.


    Also note that the party command window's completely unrelated in the aforementioned concerns, as absolutely nothing can happen as long as it's active.


    Anyway, in PATB Core, the number of cases to consider will increase from 3 to 6:


    1. The status window was selecting a previously inputable actor and there are some other inputable actors


    - In this case, all input windows of the previously inputable actor should be closed and a new inputable actor should be selected with an actor command window being setup instead.


    2. The status window was selecting a previously inputable actor and there are no inputable actors


    - In this case, all input windows of the previously inputable actor should be closed and the status window should select nothing.


    3. The party command window was active and there are some other inputable actors


    - In this case, nothing should be done here.


    4. The party command window was active and there are no inputable actors


    - In this case, the party command window should be closed.


    5. The status window wasn't selecting anything and the party command window wasn't active, and there are some other inputable actors


    - In this case, a new inputable actor should be selected with an actor command window being setup instead.


    6. The status window wasn't selecting anything and the party command window wasn't active, and there are no inputable actors


    - In this case, nothing should be done here.


    The difference between MATB and PATB Core on this aspect's due to the fact that the ATB Frame Update can run even when some input window's active in the latter while that's impossible for the former.


    Party Escape Attempt Availability

    In MATB, it's nearly impossible for party escape attempts to be triggered when Executing Actions, as the latter can only be transited from Not Executing Actions during an ATB Frame Update, which won't run when the party command window's active, which is the only way to trigger the party escape attempts.


    Technically speaking, it's not outright impossible, but just improbable enough to be negligible. Any detailed explanation for this is too advanced to be covered here, but I'll still briefly talk about it:


    Such rare cause is that, sometimes a non confused non Autobattle actor and an enemy can become Actable at the exact same ATB Frame Update when there's no Action Execution Subject. The enemy will make actions and immediately become the Action Execution Subject, leading to the battle transiting from Not Executing Actions to Executing Actions, while the actor will become inputable. The subsequent Battle Frame Updates will execute the enemy's actions while letting players to access the party command window and thus trigger the party escape attempt.


    It's really so seldom to the point that ignoring this bug becomes a viable choice in MATB although writing quite some extra codes to fix that's also nice, but that won't be the case for PATB Core at all, as it's rather common for some input windows to become active while Executing Actions, or an action to begin to execute while some input windows are active. So PATB Core will have to check if the battle's Executing Actions when the players try to trigger the party escape attempt.


    Autobattle Check

    In MATB, it's impossible for an actor to become Autobattle when that actor's inputable.


    In PATB Core, that's possible, as becoming Autobattle can happen during an ATB Frame Update, which can be triggered when an actor's inputable.


    So it's necessary for PATB Core to ensure inputable actors becoming Autobattle will immediately become uninputable and make Autobattle actions, while MATB doesn't have to worry about this at all.


    Next Command

    In the default RMMV battle system, Next Command will setup a party command window if there are inputable actors but all of them have smaller party member indices than the current one.


    However, Next Command is supposed to never so do in any ATB system, so ATB system plugins being possible to have such case will have to edit Next Command to eliminate this possibility.


    While such case's clearly impossible in the RMMV battle system and somewhat probable in PATB Core, it's actually impossible in MATB as well.


    Although any rigorous proof will be too advanced to be included here, I'll still try to briefly show some key points of the explanations:


    1. An actor can only become inputable during an ATB Frame Update


    2. ATB Frame Update will never trigger in MATB as long as there are inputable actors already


    3. (Derived from 1 and 2)The only possible case for having more than 1 inputable actor is that all of them become inputable in the same ATB Frame Update


    4. If more than 1 actors become inputable in the same ATB Frame Update, the one with the smallest party member index will be selected by the status window with that actor's actor command window being setup


    5. (Derived from 3 and 4)It's impossible for any other inputable actor to have a party member index smaller than the currently selected one


    Therefore MATB doesn't even have to touch Next Command at all.


    Input Window Closing/Refresh

    In both the default RMMV battle system and MATB, inputting actions and triggering party escapes are the only things that can happen when there are active input windows, so player commands are the only reasons to close and/or refresh those windows(too advanced to be explained here).


    But in PATB Core, closing and/or refreshing input windows can also be due to ATB Frame Updates, which can be run when there are input windows.


    That means closing and/or refreshing input widnows can happen at any ATB Frame Update as well, meaning ATB Frame Update needs to check whether any input window needs to be closed/refreshed.






    Testing various ATB Wait Conditions

    Relation between difficulty of testing and the ATB Wait Condition




    In general, the more control and freedom users have on configuring the ATB Wait Conditions, the more complicated, convoluted and costly to test them all. This should be crystal clear and obvious.


    What might be not so certain to you is this: Usually, the stricter the ATB Wait Condition, the harder it's to be tested. It's because:


    1. An important invariant in the default RMMV battle system is that only player commands can take place when there are active input windows(too advanced to be explained here).


    2. Normally the essence of ATB Wait Condition's to determine the conditions that can preserve the above invariant.


    3. In most cases, the stricter the ATB Wait Condition, the easier for the above invariant to be broken.


    4. The easier for the above invariant to be broken, the more different between the default RMMV battle system and the ATB system.


    5. As long as an ATB system based on the default RMMV battle system, the more different between them, the more likely for the the former to be more difficult to be tested.



    ATB Wait Condition Comparison Concepts

    I'll have to define 4 terms before going on:


    1. Condition A is Absolutely Looser than Condition B - If Condition B is met, it automatically guarantees that Condition A is also met, while it alone gives no guarantee for the converse.


    2. Condition A is Absolutely Stricter than Condition B - If Condition A is met, it automatically guarantees that Condition B is also met, while it alone gives no guarantee for the converse.


    3. Condition A is the Absolutely Loosest - Condition A is Absolutely Looser than any other possible conditions.


    4. Condition A is the Absolutely Strictest - Condition A is Absolutely Stricter than any other possible conditions.


    Now let's continue to the next part ;)


    Methodologies for testing various ATB Wait Conditions

    As mentioned, the Absolutely Stricter ATB Wait Condition is, the harder it can be tested, so it's natural to test the Absolutely Loosest one first to start small before challenging the final boss.


    Nevertheless, this approach has its own disadvantages, because:


    1. Passing a test in an Absolutely Looser ATB Wait Condition doesn't automatically guarantee that it'll also pass in an Absolutely Stricter ATB Wait Condition.


    2. Passing a test in an Absolutely Stricter ATB Wait Condition automatically guarantees that it'll also pass in an Absolutely Looser ATB Wait Condition.


    Proof of 1:


    - Just check counterexamples in Setup new inputable actors, Autobattle Check, Next Command and Input Window Closing/Refresh in the previous section of this reply. Here MATB always uses the Absolutely Loosest ATB Wait Condition while PATB Core lets users use the Absolutely Strictest ATB Wait Condition.


    Proof of 1 and 2:


    - Recall that the essence of ATB Wait Conditions is to determine conditions that can preserve an important invariant.


    - That means the looser the ATB Wait Condition, the more protections it offers to that invariant.


    - So in some cases, that invariant can be broken in an Absolutely Stricter ATB Wait Condition but will still hold in an Absolutely Looser ATB Wait Condition for those cases because the former doesn't preserve that invariant from those cases while the latter does.


    - On the other hand, if that invariant holds in an Absolutely Stricter ATB Wait Condition, it'll also hold in an Absolutely Looser ATB Wait Condition.


    On a side note:


    1. If that invariant's broken in an Absolutely Looser ATB Wait Condition, it'll also be broken in an Absolutely Stricter ATB Wait Condition.


    2. The Absolutely Loosest ATB Wait Condition do offer 100% protection to that invariant, meaning that invariant actually remains intact.


    3. The Absolutely Strictest ATB Wait Condition indeed offers 0% protection to that invariant, as it'll only be met when the ATB Frame Update has to stop, which can only be the case when no input window's active.


    All these means that:


    1. Passing all possible tests in the Absolutely Loosest ATB Wait Condition means next to nothing to any other possible ATB Wait Condition.


    2. Passing a test in the Absolutely Strictest ATB Wait Condition means it'll also pass in any other possible ATB Wait Conditions.


    Therefore just testing the Absolutely Strictest ATB Wait Condition of an ATB system is enough to test that ATB system entirely.


    Note that for ATB systems having no Absolutely Strictest ATB Wait Conditions, all those besides the Absolutely Looser ones will have to be tested.


    All these doesn't necessarily mean testing the Absolutely Looser ATB Wait Condition first is always bad, as there can be cases where they're much easier to test first and the other ATB Wait Conditions will become much easier to test afterwards. It's possible that the gain from the reduced difficulty of the subsequent tests outweighs the cost of testing more than the Absolutely Strictest ATB Wait Conditions.


    Implications on adding Absolutely Stricter ATB Wait Conditions in ATB system updates

    You may have noticed that MATB and PATB Core are extremely similar to each other if you've thoroughly comprehended them both. You might even suspect that one of them is derived from the other.


    In fact, the former's derived from the latter, even though the opposite seems to make more sense on the first glance. I'd say that adding an Absolutely Stricter ATB Wait Condition in an ATB system is much harder than removing one from it.


    As said before, passing a test in an Absolutely Stricter ATB Wait Condition automatically means passing that test in an Absolutely Looser ATB Wait Condition as well.


    So when removing an Absolutely Stricter ATB Wait Condition, no extra test will be needed and some might even become redundant. At least, just passing all the existing ones will suffice.


    On the other hand, adding an Absolutely Stricter ATB Wait Condition often needs some extra tests to guarantee the ATB system still has no nontrivial bugs. Remember that the existing tests will need to be run again to ensure they'll all still pass too.


    Nevertheless, that doesn't mean starting small isn't an option when writing ATB system. It's just that we still have to think large even when starting small, unless we'll always insist never adding Absolutely Stricter ATB Wait Condition later on.


    After all, most of us don't want to throw away codes, especially lots of them and a large proportion of them, like throwing away 2000+ LoC in a 3000+ LoC plugin, when we've better choices.


    Moreover, it'd be nice if the ATB system plugin's always ready to adapt to Absolutely Stricter ATB Wait Conditions later on, as it's a rather common request among ATB system plugins :p


    Bear in mind that it's not the same as future coding nor overengineering, because we don't need to preemptively handle Absolutely Stricter ATB Wait Conditions that aren't implemented yet. We just need to implement the ATB system to work with the current ones only, albeit in ways that it'd be easy, effortless and simple to adept to the Absolutely Stricter ones. This involves extensibility, a topic being too advanced to be covered here.


    Also, sometimes throwing a sizable portion of codes away can be the best choice, but we still want to throw as little away as possible. This can be done by making as little of the plugin to depend on the ATB Wait Condition, making those parts to depend as little as possible, and making the ATB Wait Condition implementations as concentrated as possible. This involves coupling and cohesion, topics being too advanced to be covered here.






    Summary

    DoubleX RMMV Minimalized ATB has taken shortcuts on the below aspects of the ATB system essentials:


    1. ATB Wait Condition - Making use of the default Scene_Battle.prototype.isAnyInputWindowActive


    2. Setup new inputable actors - Reducing the number of cases to be considered from 6 to 3


    3. Party Escape Attempt Availability - Becoming optional as it's just to fix an extremely unlikely bug


    4. Autobattle Check - Becoming completely unnecessary


    5. Next Command - Keeping the default implementation intact


    6. Input Window Closing/Refresh - Doing nothing extra from the default


    While DoubleX RMMV Popularized ATB Core has taken none, due to the differences of ATB Wait Conditions between these 2 plugins.


    The essence of ATB Wait Condition is to determine conditions preserving an important default RMMV battle system invariant - only player commands can take place when there are active input windows.


    As long as the ATB system's based on the default RMMV battle system, the looser the ATB Wait Condition is, the more protection it gives to that invariant, making it easier to test.


    Due to the below 2 basis:


    1. Passing a test in an Absolutely Looser ATB Wait Condition doesn't automatically guarantee that it'll also pass in an Absolutely Stricter ATB Wait Condition.


    2. Passing a test in an Absolutely Stricter ATB Wait Condition automatically guarantees that it'll also pass in an Absolutely Looser ATB Wait Condition.


    The below can be deduced:


    1. Passing all possible tests in the Absolutely Loosest ATB Wait Condition means little to any other possible ATB Wait Condition.


    2. Passing a test in the Absolutely Strictest ATB Wait Condition means it'll also pass in any other possible ATB Wait Conditions.


    Just note that for ATB systems having no Absolutely Strictest ATB Wait Conditions, all those besides the Absolutely Looser ones will have to be tested.


    Just passing all the existing tests will suffice when removing an Absolutely Stricter ATB Wait Condition, but adding one needs extra tests as well to guarantee the ATB system still has no nontrivial bugs.


    Unless we're absolutely sure we'll always insist on never moving to Absolutely Stricter ATB Wait Condition, we'll want to think about their implementations even before actually implementing them.


    To avoid throwing more codes than necessary later on, we can implement the ATB system in ways that it'd be easy, effortless and simple to adept to the Absolutely Stricter ATB Wait Conditions in the future, by making as little of the plugin to depend on the ATB Wait Condition, making those parts to depend as little as possible, and making the ATB Wait Condition implementations as concentrated as possible.
     
    Last edited by a moderator: Feb 3, 2016
    #7
  8. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Now I'm going to talk about Ways to write an easy, simple and small ATB system addon.


    The Core Addon Approach basics

    When a system's small, implementing its entirety in a single plugin makes perfect sense.


    However, when a system becomes large, implementing its entirety in a single plugin can be rather painful.


    For instance, in RMVXA, I've rewritten Yami's CATB into ECATB, which has 8000+ LoC. As the targeting audience of this topic's those having some javascript coding proficiency(inexperienced junior plugin developer having written several easy, simple ans small battle related plugins without nontrivial bugs), this sheer size alone would already make ECATB a nightmare for such plugin developers to even try to read.


    Therefore, sometimes it'd be more desirable to break down the whole system into smaller and more modular plugins. Modularity itself is a topic that's too advanced to be covered here.


    There are many ways to break down the whole system in this manner. This involves architectural patterns, which is too advanced to be covered here.


    1 such common way is the Core Addon Approach. The below are its fundamental principles:


    - The system consists of exactly 1 core and at least 1 addons


    - The core is necessary for the system to be used but all addons are optional


    - The core doesn't depend on nor need any addon but all addons need and depend on the core


    They suggest that:


    - The feature set of a system is divided into at least 2 subsets - exactly 1 essential subset and at least 1 optional subset


    - The core implements and lets users utilize the essential subset and each addon implements and lets users utilize an optional subset


    - The essential subset doesn't depend on nor need any optional subset but all optional subsets need and depend on the essential subset


    Such setup can have the below benefits for users:


    - They can freely use the features they demand and discard all the other optional ones, due to the system being broken down into more modular pieces


    - Most of them will generally more likely to feel that the system's easier, simpler and more user-friendly to use, due to it being broken down into smaller pieces


    - They can isolate and pinpoint any issues more easily, effortlessly and swiftly, due to the small size of each plugin and the optional nature of the addons


    Such setups can also have the below benefits for plugin developers:


    - Those not being comfortable enough with working a single massive plugin can work with a number of smaller ones instead


    - They won't have to worry about sometimes not clearly knowing which parts of the codes belongs to which parts of the feature set


    - If only 1 part of codes need to be updated, they only have to update 1 addon plugin instead of the single plugin having everything, thus needing fewer users to download the plugins again


    Such setups can have the below drawbacks for users:


    - If the number of addons become massive, they can bloat the plugin manager


    - If many plugins have been updated, they'll have to download all those plugins again, instead of just download a single plugin having everything


    - For addons having addon ordering rules(like addon x has to be placed above addon y), users might end up facing dilemmas regarding compatibility issues(too advanced to be covered here)


    Such setups can also have the below drawback for plugin developers:


    - If the number of addons become massive, updating them all can be a nightmare


    - Sometimes there are some inherent Interaddon Dependencies coming from the features themselves, leading to inevitable tight coupling(too advanced to be covered here)


    - Sometimes getting rid of the addon ordering rules can lead to much more complicated and convoluted codes, while preserving the addon ordering rules can lead to compatibility issues that would have been solved without those addon ordering rules(too advanced to be covered here)


    The comparisons between the Core Addon Approach and the Single Plugin Approach is too advanced to be covered here. Instead, I'll just continue on talking about the former on the next sections.




    Interactions between the core and an addon

    As mentioned, while the core doesn't need nor depend on any addon, all addons need and depend on the core.


    So when the core changes, at least some addons might have to change too.


    Therefore you'll want to avoid this rookie mistake: After updating the core, you forget to check if any addon needs to be updated accordingly, until some users inform you that some addons suddenly don't work anymore right after they've updated the core.


    Also, implementing an addon means adding an optional feature subset to work with the essential feature subset, so it's vital to have a crystal clear vision on all possible results when those subsets work altogether on both the user and developer levels. This needs a crystal clear vision on all wouldbe possible results for each of those subset working in isolation on both the user and developer levels.


    If the feature set of the whole system's carefully divided into loosely coupled components each having high cohesion, envisioning how they'll all work together will be much easier and simpler, and changing a component will have a much lower chance to impact any other component. This itself is a topic that's too advanced to be covered here.


    For now, let's assume the core's already written and isn't going to change for the sake of making writing addons easier and simpler. The only focus now is how to make an addon deliver its feature set while still working with the core. The next section will use some concrete examples to demostrate just that.




    Some easy, simple and small ATB system addon examples

    Here easy, simple and small ATB system addon refers to those completely independent on any other addon, implementing exactly 1 well-defined specific function, and straightforward enough for the targeting audience to thoroughly comprehend on their own.


    Recall that DoubleX RMMV Popularized ATB Core is the core. For the sake of simplicity, I'll use PATB to refer to DoubleX RMMV Popularized ATB, so PATB Core refers to DoubleX RMMV Popularized ATB Core.


    DoubleX RMMV Popularized ATB Clock




    Its function's to let users show the battle turn clock, unit and count in battle.


    Its feature set's this:




    * @param show_turn_clock_window
    * @desc Setups a window in battle showing the battle turn clock, unit and
    * count if show_turn_clock_window is set as true
    * @default true
    *
    * @param turn_clock_window_x
    * @desc Sets the x position of the battle turn clock window as
    * turn_clock_window_x
    * @default 0
    *
    * @param turn_clock_window_y
    * @desc Sets the y position of the battle turn clock window as
    * turn_clock_window_y
    * @default 60
    *
    * @param turn_clock_window_width
    * @desc Sets the width of the battle turn clock window as
    * turn_clock_window_width
    * @default 440
    *
    * @param turn_clock_window_height
    * @desc Sets the height of the battle turn clock window as
    * turn_clock_window_height
    * @default 60
    *
    * @param turn_clock_text_size
    * @desc Sets the size of the text shown in the battle turn clock window as
    * turn_clock_text_size
    * @default 20
    *
    * @param turn_clock_text_x
    * @desc Sets the x position of the text shown in the battle turn clock window
    * as turn_clock_text_x
    * @default 0
    *
    * @param turn_clock_text_y
    * @desc Sets the y position of the text shown in the battle turn clock window
    * as turn_clock_text_x
    * @default -8
    *
    * @param turn_clock_text_act
    * @desc Sets the text showing that the clock unit's action as
    * turn_clock_text_act
    * @default Action
    *
    * @param turn_clock_text_sec
    * @desc Sets the text showing that the clock unit's second as
    * turn_clock_text_sec
    * @default Frame
    *
    * @param turn_clock_text_unavailable
    * @desc Sets the text showing that the clock unit's unavailable as
    * turn_clock_text_unavailable
    * @default Stopped



    So it basically adds a configurable window showing the Battle Turn Clock in battle.


    It's clear that PATB Core doesn't need nor depend on PATB Clock, as PATB can work without displaying the Battle Turn Clock, and the latter doesn't do anything other than displaying something that already exists.


    On the other hand, PATB Clock needs and depends on the PATB Core, as the former needs to get the Battle Turn Clock, which is implemented by the latter only, and the latter determines how the former can get the Battle Turn Clock.


    The implementation of PATB Clock is easy, simple and small:

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

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: Game_System
    * - Stores the values of all configurations listed in the plugin manager
    *----------------------------------------------------------------------------*/

    Game_System.prototype.init_patb_clock_params =
    Game_System.prototype.init_patb_params;
    Game_System.prototype.init_patb_params = function() {
    this.init_patb_clock_params();
    // Added
    var val, params = PluginManager.parameters(DoubleX_RMMV.PATB_CLOCK_File);
    Object.keys(params).forEach(function(param) {
    val = +params[param];
    this._patb[param] = isNaN(val) ? params[param] : val;
    }, this);
    this._patb.show_turn_clock_window =
    params.show_turn_clock_window === "true";
    //
    }; // Game_System.prototype.init_patb_params

    Game_System.prototype.is_patb_clock_unit = function() { // New; Hotspot
    var unit = this._patb.turn_unit_code;
    return unit === "act" || unit === "sec";
    }; // Game_System.prototype.is_patb_clock_unit

    /*----------------------------------------------------------------------------
    * # New class: Window_Patb_Clock
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variable
    *----------------------------------------------------------------------------*/
    // _text: The cached battle system text

    Window_Patb_Clock.prototype = Object.create(Window_Base.prototype);
    Window_Patb_Clock.prototype.constructor = Window_Patb_Clock;

    Window_Patb_Clock.prototype.initialize = function() {
    var x = $gameSystem.patb.turn_clock_window_x;
    var y = $gameSystem.patb.turn_clock_window_y;
    var width = $gameSystem.patb.turn_clock_window_width;
    var height = $gameSystem.patb.turn_clock_window_height;
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    }; // Window_Patb_Clock.prototype.initialize

    Window_Patb_Clock.prototype.updateText = function() { // Potential Hotspot
    var patb = $gameSystem.patb, text = this.setText();
    if (this._text === text) { return; }
    this._text = text;
    this.contents.clear();
    var x = patb.turn_clock_text_x, y = patb.turn_clock_text_y;
    this.drawText(this._text, x, y, this.textWidth(this._text));
    }; // Window_Patb_Clock.prototype.updateText

    Window_Patb_Clock.prototype.setText = function() { // Potential Hotspot
    var text = "Turn " + $gameTroop.turnCount() + " : ";
    if ($gameSystem.is_patb_clock_unit()) {
    var unit = $gameSystem.patb.turn_unit_code;
    text += BattleManager.patb_turn_clock[unit] + " / ";
    text += $gameSystem.max_patb_turn_unit(unit) + " ";
    return text + $gameSystem.patb["turn_clock_text_" + unit];
    } else {
    return text + $gameSystem.patb.turn_clock_text_unavailable;
    }
    }; // Window_Patb_Clock.prototype.setText

    Window_Patb_Clock.prototype.updateVisible = function() { // Hotspot
    this.visible = $gameSystem.patb.show_turn_clock_window;
    }; // Window_Patb_Clock.prototype.updateVisible

    Window_Patb_Clock.prototype.standardFontSize = function() { // Potential Hotspot
    return $gameSystem.patb.turn_clock_text_size;
    }; // Window_Patb_Clock.prototype.standardFontSize

    /*----------------------------------------------------------------------------
    * # Edit class: Scene_Battle
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variable
    *----------------------------------------------------------------------------*/
    // _patb_clock_window: The battle turn clock window

    Scene_Battle.prototype.createAllWindowsPatbClock =
    Scene_Battle.prototype.createAllWindows;
    Scene_Battle.prototype.createAllWindows = function() {
    this.createAllWindowsPatbClock();
    if ($gameSystem.is_patb()) { this.create_patb_clock_window(); } // Added
    }; // Scene_Battle.prototype.createAllWindows

    Scene_Battle.prototype.update_patb_process_clock =
    Scene_Battle.prototype.update_patb_process;
    Scene_Battle.prototype.update_patb_process = function() { // Hotspot
    this.update_patb_process_clock();
    this.update_patb_clock_window(); // Added
    }; // Scene_Battle.prototype.update_patb_process

    Scene_Battle.prototype.close_patb_windows_clock =
    Scene_Battle.prototype.close_patb_windows;
    Scene_Battle.prototype.close_patb_windows = function() {
    this.close_patb_windows_clock();
    this.close_patb_clock_windows(); // Added
    }; // Scene_Battle.prototype.close_patb_windows

    Scene_Battle.prototype.create_patb_clock_window = function() { // New
    this._patb_clock_window = new Window_Patb_Clock();
    this.addWindow(this._patb_clock_window);
    }; // Scene_Battle.prototype.create_patb_clock_window

    Scene_Battle.prototype.update_patb_clock_window = function() { // New; Hotspot
    this._patb_clock_window.updateVisible();
    if (this._patb_clock_window.visible) { this._patb_clock_window.updateText(); }
    }; // Scene_Battle.prototype.update_patb_clock_window

    Scene_Battle.prototype.close_patb_clock_windows = function() { // New
    this._patb_clock_window.hide();
    this._patb_clock_window.deactivate();
    this._patb_clock_window.close();
    }; // Scene_Battle.prototype.close_patb_clock_windows

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB Clock, place it below PATB Core.");
    }



    Note that:


    - Game_System is touched to store the window configurations and check if the Battle Turn Clock can be run(it should be stopped whenver the Battle Turn Clock uses an unavailable unit)


    - Window_Patb_Clock is added to implement the window


    - Scene_Battle is touched to create the window upon battle start, update the window per Battle Frame Update(To ensure the updated window configurations and/or maximum Battle Turn Clock unit will be immediately shown even when the ATB Wait Condition's met), and close the window upon battle end




    DoubleX RMMV Popularized ATB CTB

    You're assumed to have a basic knowledge on how the fundamental CTB system concepts work on the user level.


    Its function's to let users switch the battle system to mimic charge turn battle


    Its feature set's this:




    * @param show_battle_system_window
    * @desc Setups a window in battle indicating whether atb or ctb's used if
    * show_battle_system_window is set as true
    * @default true
    *
    * @param battle_system_window_x
    * @desc Sets the x position of the battle system indicator window as
    * battle_system_window_x
    * @default 0
    *
    * @param battle_system_window_y
    * @desc Sets the y position of the battle system indicator window as
    * battle_system_window_y
    * @default 0
    *
    * @param battle_system_window_width
    * @desc Sets the width of the battle system indicator window as
    * battle_system_window_width
    * @default 220
    *
    * @param battle_system_window_height
    * @desc Sets the height of the battle system indicator window as
    * battle_system_window_height
    * @default 60
    *
    * @param battle_system_text_size
    * @desc Sets the size of the text shown in the battle system indicator window
    * as battle_system_text_size
    * @default 20
    *
    * @param battle_system_text_x
    * @desc Sets the x position of the text shown in the battle system indicator
    * window as battle_system_text_x
    * @default 0
    *
    * @param battle_system_text_y
    * @desc Sets the y position of the text shown in the battle system indicator
    * window as battle_system_text_y
    * @default -8
    *
    * @param atb_battle_system_text
    * @desc Sets the text shown in a window indicating atb's used in battle as
    * atb_battle_system_text
    * @default Active Time Battle
    *
    * @param ctb_battle_system_text
    * @desc Sets the text shown in a window indicating ctb's used in battle as
    * ctb_battle_system_text
    * @default Charge Turn Battle



    So it basically adds a configurable window showing the current battle system in battle, and adds an available code, ctb, to let the battle system mimic CTB.


    Here it's better to clarify what it means to replicate CTB in an ATB system: All consecutive ATB Frame Updates having no events to be triggered will be run in a single Battle Frame Update.


    It's clear that PATB Core doesn't need nor depend on PATB CTB, as PATB can work without the CTB replication, and the latter doesn't do anything other than displaying the current battle system and adding the capability to mimic CTB.


    On the other hand, PATB CTB needs and depends on the PATB Core, as the former needs to get the current battle system code and extend parts of the ATB Frame Update respectively, which are both implemented by the latter only, and the latter determines how the former can get the current battle system code and what parts of the ATB Frame Update to extend.


    The implementation of PATB CTB is easy, simple and small:

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

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: BattleManager
    * - Implements the ctb system replication when it's asked and allowed
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * Runs the global and battler atb clocks until they've to stop
    *----------------------------------------------------------------------------*/
    BattleManager.update_patb_ctb = BattleManager.update_patb;
    BattleManager.update_patb = function() { // Hotspot
    // Rewritten
    do { this.update_patb_ctb(); } while (this.can_update_patb_ctb());
    //
    }; // BattleManager.update_patb

    /*----------------------------------------------------------------------------
    * Checks if the atb frame updates can behave in the ctb manner
    *----------------------------------------------------------------------------*/
    BattleManager.can_update_patb_ctb = function() { // New; Hotspot
    if ($gameSystem.patb.battle_system_code !== "ctb") { return false; }
    if (this._phase === 'action' || this.isInputting()) { return false; }
    return !this.isBusy() && !this._need_patb_refresh;
    }; // BattleManager.can_update_patb_ctb

    /*----------------------------------------------------------------------------
    * # Edit class: Game_System
    * - Stores the values of all configurations listed in the plugin manager
    *----------------------------------------------------------------------------*/

    Game_System.prototype.init_patb_ctb_params =
    Game_System.prototype.init_patb_params;
    Game_System.prototype.init_patb_params = function() {
    this.init_patb_ctb_params();
    // Added
    var val, params = PluginManager.parameters(DoubleX_RMMV.PATB_CTB_File);
    Object.keys(params).forEach(function(param) {
    val = +params[param];
    this._patb[param] = isNaN(val) ? params[param] : val;
    }, this);
    this._patb.show_battle_system_window =
    params.show_battle_system_window === "true";
    //
    }; // Game_System.prototype.init_patb_params

    Game_System.prototype.is_patb_ctb = Game_System.prototype.is_patb;
    Game_System.prototype.is_patb = function() { // Hotspot
    // Rewritten
    return this.is_patb_ctb() || this._patb.battle_system_code === "ctb";
    //
    }; // Game_System.prototype.is_patb

    /*----------------------------------------------------------------------------
    * # New class: Window_Patb_Ctb
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variable
    *----------------------------------------------------------------------------*/
    // _text: The cached battle system text

    Window_Patb_Ctb.prototype = Object.create(Window_Base.prototype);
    Window_Patb_Ctb.prototype.constructor = Window_Patb_Ctb;

    Window_Patb_Ctb.prototype.initialize = function() {
    var x = $gameSystem.patb.battle_system_window_x;
    var y = $gameSystem.patb.battle_system_window_y;
    var width = $gameSystem.patb.battle_system_window_width;
    var height = $gameSystem.patb.battle_system_window_height;
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    }; // Window_Patb_Ctb.prototype.initialize

    Window_Patb_Ctb.prototype.updateText = function() { // Potential Hotspot
    var patb = $gameSystem.patb;
    var text = patb[patb.battle_system_code + "_battle_system_text"];
    if (this._text === text) { return; }
    this._text = patb[patb.battle_system_code + "_battle_system_text"];
    this.contents.clear();
    var x = patb.battle_system_text_x, y = patb.battle_system_text_y;
    this.drawText(this._text, x, y, this.textWidth(this._text));
    }; // Window_Patb_Ctb.prototype.updateText

    Window_Patb_Ctb.prototype.updateVisible = function() { // Hotspot
    this.visible = $gameSystem.patb.show_battle_system_window;
    }; // Window_Patb_Ctb.prototype.updateVisible

    Window_Patb_Ctb.prototype.standardFontSize = function() { // Potential Hotspot
    return $gameSystem.patb.battle_system_text_size;
    }; // Window_Patb_Ctb.prototype.standardFontSize

    /*----------------------------------------------------------------------------
    * # Edit class: Scene_Battle
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variable
    *----------------------------------------------------------------------------*/
    // _patb_ctb_window: The battle system indicator window

    Scene_Battle.prototype.createAllWindowsPatbCtb =
    Scene_Battle.prototype.createAllWindows;
    Scene_Battle.prototype.createAllWindows = function() {
    this.createAllWindowsPatbCtb();
    if ($gameSystem.is_patb()) { this.create_patb_ctb_window(); } // Added
    }; // Scene_Battle.prototype.createAllWindows

    Scene_Battle.prototype.update_patb_process_ctb =
    Scene_Battle.prototype.update_patb_process;
    Scene_Battle.prototype.update_patb_process = function() { // Hotspot
    this.update_patb_process_ctb();
    this.update_patb_ctb_window(); // Added
    }; // Scene_Battle.prototype.update_patb_process

    Scene_Battle.prototype.close_patb_windows_ctb =
    Scene_Battle.prototype.close_patb_windows;
    Scene_Battle.prototype.close_patb_windows = function() {
    this.close_patb_windows_ctb();
    this.close_patb_ctb_windows(); // Added
    }; // Scene_Battle.prototype.close_patb_windows

    Scene_Battle.prototype.create_patb_ctb_window = function() { // New
    this._patb_ctb_window = new Window_Patb_Ctb();
    this.addWindow(this._patb_ctb_window);
    }; // Scene_Battle.prototype.create_patb_ctb_window

    Scene_Battle.prototype.update_patb_ctb_window = function() { // New; Hotspot
    this._patb_ctb_window.updateVisible();
    if (this._patb_ctb_window.visible) { this._patb_ctb_window.updateText(); }
    }; // Scene_Battle.prototype.update_patb_ctb_window

    Scene_Battle.prototype.close_patb_ctb_windows = function() { // New
    this._patb_ctb_window.hide();
    this._patb_ctb_window.deactivate();
    this._patb_ctb_window.close();
    }; // Scene_Battle.prototype.close_patb_ctb_windows

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB CTB, place it below PATB Core.");
    }


    Note that:


    - BattleManager is touched to implement the CTB replication(BattleManager.update_patb is the only part of the consecutive ATB Frame Update that needs to be condensed into a single Battle Frame Update, because it's the only part handling battle and battler ATB states, which are the only parts that are supposed to be replicated in the CTB manner; Detailed explanation involves MVC, which is too advanced to be covered here)


    - Game_System is touched to store the window configurations and register the new battle system code "ctb" as an available one


    - Window_Patb_Ctb is added to implement the window


    - Scene_Battle is touched to create the window upon battle start, update the window per Battle Frame Update(To ensure the updated window configurations will be immediately shown even when the ATB Wait Condition's met), and close the window upon battle end


    DoubleX RMMV Popularized ATB Event

    Its function's to let users calls some common events at some specific atb timing


    Its feature set's this:




    * @param pre_input_common_event_id
    * @desc Sets the common event with id pre_input_common_event_id to be called
    * whenever a battler just becomes able to act
    * pre_input_common_event_id must return a Number
    * If pre_input_common_event_id doesn't return the id of an existing
    * common event, no common event will be called with this timing
    * @default 0
    *
    * @param post_input_common_event_id
    * @desc Sets the common event with id post_input_common_event_id to be called
    * whenever a battler just finished inputting actions
    * post_input_common_event_id must return a Number
    * If post_input_common_event_id doesn't return the id of an existing
    * common event, no common event will be called with this timing
    * @default 0
    *
    * @param pre_reset_common_event_id
    * @desc Sets the common event with id pre_reset_common_event_id to be called
    * right before resetting a battler's atb
    * pre_reset_common_event_id must return a Number
    * If pre_reset_common_event_id doesn't return the id of an existing
    * common event, no common event will be called with this timing
    * @default 0
    *
    * @param post_reset_common_event_id
    * @desc Sets the common event with id post_reset_common_event_id to be called
    * right after resetting a battler's atb
    * post_reset_common_event_id must return a Number
    * If post_reset_common_event_id doesn't return the id of an existing
    * common event, no common event will be called with this timing
    * @default 0
    *
    * @param pre_update_common_event_id
    * @desc Sets the common event with id pre_update_common_event_id to be called
    * right before running a global atb frame update
    * pre_update_common_event_id must return a Number
    * If pre_update_common_event_id doesn't return the id of an existing
    * common event, no common event will be called with this timing
    * @default 0
    *
    * @param post_update_common_event_id
    * @desc Sets the common event with id post_update_common_event_id to be called
    * right after running a global atb frame update
    * post_update_common_event_id must return a Number
    * If post_update_common_event_id doesn't return the id of an existing
    * common event, no common event will be called with this timing
    * @default 0



    So it basically adds some timings in battle unique to ATB systems for calling common events.


    It's clear that PATB Core doesn't need nor depend on PATB Event, as PATB can work without calling common events at all, and the latter doesn't do anything other than adding extra timings for that.


    On the other hand, PATB Event needs and depends on the PATB Core, as the former needs to know which parts of the implementation of the latter give those unique timings.


    The implementation of PATB Event is easy, simple and small:

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: Game_Temp
    *----------------------------------------------------------------------------*/

    // timing: The common event trigger timing
    Game_Temp.prototype.call_patb_event = function(timing) { // New
    var id = $gameSystem.patb[timing + "_common_event_id"];
    if ($dataCommonEvents[id]) { this.reserveCommonEvent(id); }
    }; // Game_Temp.prototype.call_patb_event

    /*----------------------------------------------------------------------------
    * # Edit class: Game_System
    * - Stores the values of all configurations listed in the plugin manager
    *----------------------------------------------------------------------------*/

    Game_System.prototype.init_patb_event_params =
    Game_System.prototype.init_patb_params;
    Game_System.prototype.init_patb_params = function() {
    this.init_patb_event_params();
    // Added
    var params = PluginManager.parameters(DoubleX_RMMV.PATB_Event_File);
    Object.keys(params).forEach(function(param) {
    this._patb[param] = +params[param];
    if (isNaN(this._patb[param])) {
    throw new Error(param + " must be Number but is: " + params[param]);
    }
    }, this);
    //
    }; // Game_System.prototype.init_patb_params

    /*----------------------------------------------------------------------------
    * # Edit class: Game_Battler
    *----------------------------------------------------------------------------*/

    Game_Battler.prototype.make_patb_act_event =
    Game_Battler.prototype.make_patb_act;
    Game_Battler.prototype.make_patb_act = function() {
    // Added
    if (BattleManager.action_battlers.indexOf(this) < 0) {
    $gameTemp.call_patb_event("pre_input");
    }
    //
    this.make_patb_act_event();
    }; // Game_Battler.prototype.make_patb_act

    /* reset: The battler action reset flag
    * val: The atb reset value
    */
    Game_Battler.prototype.reset_patb_event = Game_Battler.prototype.reset_patb;
    Game_Battler.prototype.reset_patb = function(reset, val) {
    var patb = $gameSystem.is_patb();
    if (patb) { $gameTemp.call_patb_event("pre_reset"); } // Added
    this.reset_patb_event(reset, val);
    if (patb) { $gameTemp.call_patb_event("post_reset"); } // Added
    }; // Game_Battler.prototype.reset_patb

    /*----------------------------------------------------------------------------
    * # Edit class: Game_Actor
    *----------------------------------------------------------------------------*/

    Game_Actor.prototype.makeAutoBattleActionsPatbEvent =
    Game_Actor.prototype.makeAutoBattleActions;
    Game_Actor.prototype.makeAutoBattleActions = function() {
    this.makeAutoBattleActionsPatbEvent();
    // Added
    if ($gameSystem.is_patb()) { $gameTemp.call_patb_event("post_input"); }
    //
    }; // Game_Actor.prototype.makeAutoBattleActions

    Game_Actor.prototype.makeConfusionActionsPatbEvent =
    Game_Actor.prototype.makeConfusionActions ;
    Game_Actor.prototype.makeConfusionActions = function() {
    this.makeConfusionActionsPatbEvent();
    // Added
    if ($gameSystem.is_patb()) { $gameTemp.call_patb_event("post_input"); }
    //
    }; // Game_Actor.prototype.makeConfusionActions

    /*----------------------------------------------------------------------------
    * # Edit class: Game_Enemy
    *----------------------------------------------------------------------------*/

    Game_Enemy.prototype.makeActionsPatbEvent = Game_Enemy.prototype.makeActions;
    Game_Enemy.prototype.makeActions = function() {
    this.makeActionsPatbEvent();
    // Added
    if ($gameSystem.is_patb()) { $gameTemp.call_patb_event("post_input"); }
    //
    }; // Game_Enemy.prototype.makeActions

    /*----------------------------------------------------------------------------
    * # Edit class: Scene_Battle
    *----------------------------------------------------------------------------*/

    Scene_Battle.prototype.update_patb_event = Scene_Battle.prototype.update_patb;
    Scene_Battle.prototype.update_patb = function() { // Hotspot
    var patb = $gameSystem.is_patb();
    if (patb) { $gameTemp.call_patb_event("pre_update"); } // Added
    this.update_patb_event();
    if (patb) { $gameTemp.call_patb_event("post_update"); } // Added
    }; // Scene_Battle.prototype.update_patb

    Scene_Battle.prototype.confirm_patb_event_act =
    Scene_Battle.prototype.confirm_patb_act;
    Scene_Battle.prototype.confirm_patb_act = function() {
    this.confirm_patb_event_act();
    // Added
    if ($gameSystem.is_patb()) { $gameTemp.call_patb_event("post_input"); }
    //
    }; // Scene_Battle.prototype.confirm_patb_act

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB Event, place it below PATB Core.");


    Note that:


    - Game_Temp is touched to add a helper function easing the call of common events


    - Game_System is touched to store the unique triming common event id configurations


    - The rest is touched to capture the unique timings


    DoubleX RMMV Popularized ATB Force

    Its function's to let users set some keys to force run/stop the atb frame update


    Its feature set's this:




    * @param show_force_atb_window
    * @desc Setups a window in battle indicating the atb force status if
    * show_force_atb_window is set as true
    * @default true
    *
    * @param force_atb_window_x
    * @desc Sets the x position of the atb force status window as
    * force_atb_window_x
    * @default 220
    *
    * @param force_atb_window_y
    * @desc Sets the y position of the atb force status window as
    * force_atb_window_y
    * @default 0
    *
    * @param force_atb_window_width
    * @desc Sets the width of the atb force status window as
    * force_atb_window_width
    * @default 180
    *
    * @param force_atb_window_height
    * @desc Sets the height of the atb force status window as
    * force_atb_window_height
    * @default 60
    *
    * @param force_atb_text_size
    * @desc Sets the size of the text shown in the atb force status window as
    * force_atb_text_size
    * @default 20
    *
    * @param force_atb_text_x
    * @desc Sets the x position of the text shown in the atb force status window
    * as force_atb_text_x
    * @default 8
    *
    * @param force_atb_text_y
    * @desc Sets the y position of the text shown in the atb force status window
    * as force_atb_text_y
    * @default -8
    *
    * @param no_force_atb_text
    * @desc Sets the text shown in a window indicating atb's not forced as
    * no_force_atb_text
    * @default No Force ATB
    *
    * @param force_run_atb_text
    * @desc Sets the text shown in a window indicating atb's forced to run as
    * force_run_atb_text
    * @default ATB Force Run
    *
    * @param force_stop_atb_text
    * @desc Sets the text shown in a window indicating atb's forced to stop as
    * force_stop_atb_text
    * @default ATB Force Stop
    *
    * @param force_run_atb_key
    * @desc Sets the key changing the atb force status from force stop to no
    * force or no force to force run as force_run_atb_key
    * @default shift
    *
    * @param force_stop_atb_key
    * @desc Sets the key changing the atb force status from force run to no force
    * or no force to force stop as force_stop_atb_key
    * @default control



    So it basically adds a key to change the ATB system from force stopping ATB to not forcing and not forcing to force running ATB and another key to do the reverse, and a configurable window to show whether the ATB's forced to stop, run or not forced. The essence of force running and stopping ATB is to override the ATB Wait Condition except for cases never letting ATB Frame Updates to be run.


    It's clear that PATB Core doesn't need nor depend on PATB Force, as PATB can work without its ATB being forced, and the latter doesn't do anything other than letting the ATB to be forced to run or stop.


    On the other hand, PATB Force needs and depends on the PATB Core, as the former needs to know which parts of the codes implement the ATB Wait Condition.


    The implementation of PATB Force is easy, simple and small:

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

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: Game_System
    * - Stores the values of all configurations listed in the plugin manager
    *----------------------------------------------------------------------------*/

    Game_System.prototype.init_patb_force_params =
    Game_System.prototype.init_patb_params;
    Game_System.prototype.init_patb_params = function() {
    this.init_patb_force_params();
    // Added
    var val, params = PluginManager.parameters(DoubleX_RMMV.PATB_Force_File);
    Object.keys(params).forEach(function(param) {
    val = +params[param];
    this._patb[param] = isNaN(val) ? params[param] : val;
    }, this);
    this._patb.show_force_atb_window =
    params.show_force_atb_window === "true";
    //
    }; // Game_System.prototype.init_patb_params

    /*----------------------------------------------------------------------------
    * # New class: Window_Patb_Force
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variable
    *----------------------------------------------------------------------------*/
    // _text: The cached battle system text

    Window_Patb_Force.prototype = Object.create(Window_Base.prototype);
    Window_Patb_Force.prototype.constructor = Window_Patb_Force;

    Window_Patb_Force.prototype.initialize = function(forceStatus) {
    this._forceStatus = forceStatus;
    var x = $gameSystem.patb.force_atb_window_x;
    var y = $gameSystem.patb.force_atb_window_y;
    var width = $gameSystem.patb.force_atb_window_width;
    var height = $gameSystem.patb.force_atb_window_height;
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    }; // Window_Patb_Force.prototype.initialize

    Window_Patb_Force.prototype.updateText = function(forceStatus) {
    // Potential Hotspot
    var patb = $gameSystem.patb;
    if (this._text === patb[forceStatus + "_atb_text"]) { return; }
    this._text = patb[forceStatus + "_atb_text"];
    this.contents.clear();
    var x = patb.force_atb_text_x, y = patb.force_atb_text_y;
    this.drawText(this._text, x, y, this.textWidth(this._text));
    }; // Window_Patb_Force.prototype.updateText

    Window_Patb_Force.prototype.updateVisible = function() { // Hotspot
    this.visible = $gameSystem.patb.show_force_atb_window;
    }; // Window_Patb_Force.prototype.updateVisible

    Window_Patb_Force.prototype.standardFontSize = function() { // Potential Hotspot
    return $gameSystem.patb.force_atb_text_size;
    }; // Window_Patb_Force.prototype.standardFontSize

    /*----------------------------------------------------------------------------
    * # Edit class: Scene_Battle
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variables
    *----------------------------------------------------------------------------*/
    // _patb_force_status: The atb force status
    // _patb_force_window: The battle system indicator window

    Scene_Battle.prototype.createPatbForce = Scene_Battle.prototype.create;
    Scene_Battle.prototype.create = function() {
    if ($gameSystem.is_patb()) { this._patb_force_status = "no_force"; } // Added
    this.createPatbForce();
    }; // Scene_Battle.prototype.create

    Scene_Battle.prototype.createAllWindowsPatbForce =
    Scene_Battle.prototype.createAllWindows;
    Scene_Battle.prototype.createAllWindows = function() {
    this.createAllWindowsPatbForce();
    if ($gameSystem.is_patb()) { this.create_patb_force_window(); } // Added
    }; // Scene_Battle.prototype.createAllWindows

    Scene_Battle.prototype.update_patb_process_force =
    Scene_Battle.prototype.update_patb_process;
    Scene_Battle.prototype.update_patb_process = function() { // Hotspot
    this.update_patb_force_status(); // Added
    this.update_patb_process_force();
    this.update_patb_force_window(); // Added
    }; // Scene_Battle.prototype.update_patb_process

    Scene_Battle.prototype.can_update_patb_force =
    Scene_Battle.prototype.can_update_patb;
    Scene_Battle.prototype.can_update_patb = function() { // Hotspot
    // Added
    if (this._patb_force_status === "force_run") { return true; }
    if (this._patb_force_status === "force_stop") { return false; }
    //
    return this.can_update_patb_force();
    }; // Scene_Battle.prototype.can_update_patb

    Scene_Battle.prototype.close_patb_windows_force =
    Scene_Battle.prototype.close_patb_windows;
    Scene_Battle.prototype.close_patb_windows = function() {
    this.close_patb_windows_force();
    this.close_patb_force_windows(); // Added
    }; // Scene_Battle.prototype.close_patb_windows

    Scene_Battle.prototype.create_patb_force_window = function() { // New
    this._patb_force_window = new Window_Patb_Force(this._patb_force_status);
    this.addWindow(this._patb_force_window);
    }; // Scene_Battle.prototype.create_patb_force_window

    Scene_Battle.prototype.update_patb_force_status = function() { // Hotspot
    if (Input.isTriggered($gameSystem.patb.force_run_atb_key)) {
    if (this._patb_force_status === "force_stop") {
    return this._patb_force_status = "no_force";
    }
    this._patb_force_status = "force_run";
    } else if (Input.isTriggered($gameSystem.patb.force_stop_atb_key)) {
    if (this._patb_force_status === "force_run") {
    return this._patb_force_status = "no_force";
    }
    this._patb_force_status = "force_stop";
    }
    }; // Scene_Battle.prototype.update_patb_force_status

    Scene_Battle.prototype.update_patb_force_window = function() { // Hotspot
    this._patb_force_window.updateVisible();
    if (this._patb_force_window.visible) {
    this._patb_force_window.updateText(this._patb_force_status);
    }
    }; // Scene_Battle.prototype.update_patb_force_window

    Scene_Battle.prototype.close_patb_force_windows = function() { // New
    this._patb_force_window.hide();
    this._patb_force_window.deactivate();
    this._patb_force_window.close();
    }; // Scene_Battle.prototype.close_patb_force_windows

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB Force, place it below PATB Core.");
    }


    Note that:


    - Game_System is touched to store the window and key configurations


    - Window_Patb_Force is added to implement the window


    - Scene_Battle is touched to implement the ATB force run/stop keys and create the window upon battle start, update the window per Battle Frame Update(To ensure the updated ATB Force status will be immediately shown even when the ATB Wait Condition's met or becomes met), and close the window upon battle end


    DoubleX RMMV Popularized ATB Rate

    Its function's to let users alter individual battlers' atb rate by data notetags


    Its feature set's this:




    *============================================================================
    * ## Notetag Info
    *----------------------------------------------------------------------------
    * # Actor/Class/Weapon/Armor/Enemy/State Notetags:
    * State notetags take the highest priority, followed by enemy, weapon,
    * armor, class and actor
    * 1. <operator patb rate: rate>
    * - Assigns rate to the battler's atb rate via operator
    * - operator can be either =, +, -, *, / or %, meaning set to, add
    * by, subtract by, multiply by, divide by or modulo by respectively
    * - All instances of this notetag will be used sequentially
    *============================================================================
    * ## Plugin Call Info
    *----------------------------------------------------------------------------
    * # Data Actor/Class/Weapon/Armor/Enemy/State manipulations
    * 1. meta.patb_rate
    * - Returns the maximum atb value with the operator stored in
    * <operator patb rate: rate> in the form of [opeartor, rate]
    * 2. meta.patb_rate = [opeartor, rate]
    * - Sets the atb rate with the operator stored in
    * <operator patb rate: rate> as string operator and Number rate
    * - All meta.patb_rate changes can be saved if
    * DoubleX RMMV Dynamic Data is used
    *============================================================================



    So it basically adds a notetag to let users change the final ATB Rate.


    It's clear that PATB Core doesn't need nor depend on PATB Rate, as PATB already has its own ATB Rate implementations, and the latter doesn't do anything other than adding some extra post processing on the ATB Rate using its notetags.


    On the other hand, PATB Rate needs and depends on the PATB Core, as the former needs to know which parts of the codes implement the ATB Rate.


    The implementation of PATB Rate is easy, simple and small:

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: DataManager
    *----------------------------------------------------------------------------*/

    // data: The data to have its notetags read
    DataManager.load_patb_rate_data_notes = DataManager.load_patb_data_notes;
    DataManager.load_patb_data_notes = function(data) {
    this.load_patb_rate_data_notes(data);
    this.load_patb_rate_notes(data); // Added
    }; // DataManager.load_patb_data_notes

    // data: The data to have its notetags read
    DataManager.load_patb_rate_notes = function(data) { // New
    var rate = /< *(.+) +patb +rate *: *(\d+) *>/i, meta = data.meta;
    meta.patb_rate = [];
    data.note.split(/[\r\n]+/).forEach(function(line) {
    if (line.match(rate)) { meta.patb_rate.push([RegExp.$1, +RegExp.$2]); }
    });
    }; // DataManager.load_patb_rate_notes

    /*----------------------------------------------------------------------------
    * # Edit class: Game_Battler
    *----------------------------------------------------------------------------*/

    Game_Battler.prototype.init_patb_rate = Game_Battler.prototype.init_patb;
    Game_Battler.prototype.init_patb = function() { // v1.00b+
    this.init_patb_rate();
    // Added
    this._patb_battler_change.atb_rate = true;
    this._patb_note_change.atb_rate = true;
    //
    }; // Game_Battler.prototype.init_patb

    Game_Battler.prototype.get_base_patb_rate = Game_Battler.prototype.get_patb_rate;
    Game_Battler.prototype.get_patb_rate = function() { // Hotspot
    // Rewritten
    var rate, last_rate = this._patb_rate.atb;
    this._patb_rate.atb = this.get_base_patb_rate();
    var change = last_rate !== this._patb_rate.atb;
    if (change || this.are_patb_battler_changed("atb_rate")) {
    rate = this._patb_rate.atb;
    this._patb_rate.atb = this.set_multi_patb_notes(rate, "patb_rate");
    }
    return this._patb_rate.atb;
    //
    }; // Game_Battler.prototype.get_patb_rate

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB Rate, place it below PATB Core.");
    }


    Note that:


    - DataManager is touched to read all ATB Rate notetags from the database


    - Game_Battler is touched to alter the final ATB Rate using each notetag effective on the battler sequentially when the final ATB Rate can change(invariants and reasons to change are too advanced to be included here)


    DoubleX RMMV Popularized ATB SE

    Its function's to let users add SE to be played when battlers become able to act


    Its feature set's this:




    * @param battler_can_act_se_file
    * @desc Sets the filename of the se to be played when battlers become able to
    * act as battler_can_act_se_file
    * It'll only be used on battlers having no effective
    * <patb can act se: file, vol, pitch, pan> notetags
    * If battler_can_act_se_file doesn't return an existing se filename,
    * the default will be not playing se when battlers become able to act
    * @default Bell3
    *
    * @param battler_can_act_se_vol
    * @desc Sets the volume of the se to be played when battlers become able to
    * act as battler_can_act_se_vol
    * It'll only be used on battlers having no effective
    * <patb can act se: file, vol, pitch, pan> notetags
    * battler_can_act_se_vol must return a Number between 0 and 100
    * @default 80
    *
    * @param battler_can_act_se_pitch
    * @desc Sets the pitch of the se to be played when battlers become able to
    * act as battler_can_act_se_pitch
    * It'll only be used on battlers having no effective
    * <patb can act se: file, vol, pitch, pan> notetags
    * battler_can_act_se_pitch must return a Number between 50 and 150
    * @default 100
    *
    * @param battler_can_act_se_pan
    * @desc Sets the pan of the se to be played when battlers become able to
    * act as battler_can_act_se_pan
    * It'll only be used on battlers having no effective
    * <patb can act se: file, vol, pitch, pan> notetags
    * battler_can_act_se_pan must return a Number between -100 and 100
    * @default 0


    Code:
     *============================================================================
     *    ## Notetag Info                                                         
     *----------------------------------------------------------------------------
     *    # Actor/Class/Weapon/Armor/Enemy/State Notetags:                        
     *      State notetags take the highest priority, followed by enemy, weapon,  
     *      armor, class and actor                                                
     *      1. <patb can act se: file, vol, pitch, pan>                         
     *         - Sets the filename, volume, pitch and pan of the se to be played  
     *           when the battler becomes able to act as file, vol, pitch and pan 
     *           respectively                                                     
     *         - The 1st notetag that's being read by the battler will be used    
     *============================================================================
     *    ## Plugin Call Info                                                     
     *----------------------------------------------------------------------------
     *    # Configuration manipulations                                           
     *      1. $gameSystem.patb.param                                             
     *         - Returns the value of param listed in the plugin manager          
     *      2. $gameSystem.patb.param = val                                       
     *         - Sets the value of param listed in the plugin manager as val      
     *         - All $gameSystem.patb.param changes will be saved                 
     *    # State manipulations                                                   
     *      1. meta.patb_can_act_se                                               
     *         - Returns the filename, volume, pitch and pan in                   
     *           <patb can act se: file, vol, pitch, pan> in the form of        
     *           [file, vol, pitch, pan]                                          
     *      2. meta.patb_can_act_se = [file, vol, pitch, pan]                     
     *         - Sets the filename, volume, pitch and pan in                      
     *           <patb can act se: file, vol, pitch, pan> as                    
     *           file, vol, pitch and pan                                         
     *         - All meta.patb_can_act_se changes can be saved if                 
     *           DoubleX RMMV Dynamic Data is used                                
     *============================================================================
    



    So it basically adds a notetag to let users set a SE to be played when the battler becomes Actable. If no such notetag's used, the default configurations will be used instead.


    It's clear that PATB Core doesn't need nor depend on PATB SE, as PATB can function without using SE to notify players that a battler becomes Actable, and the latter doesn't do anything other than just that.


    On the other hand, PATB SE needs and depends on the PATB Core, as the former needs to know which parts of the codes is the timing for the battler to become Actable.


    The implementation of PATB SE is easy, simple and small:

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: DataManager
    *----------------------------------------------------------------------------*/

    // data: The data to have its notetags read
    DataManager.load_patb_se_data_notes = DataManager.load_patb_data_notes;
    DataManager.load_patb_data_notes = function(data) {
    this.load_patb_se_data_notes(data);
    this.load_patb_se_notes(data); // Added
    }; // DataManager.load_patb_data_notes

    // data: The data to have its notetags read
    DataManager.load_patb_se_notes = function(data) { // New
    var lines = data.note.split(/[\r\n]+/), m = data.meta, se;
    se = /< *patb +can +act +se *: *(\w+) *, *(\d+) *, *(\d+) *, *(\d+) *>/i;
    for (var index = 0, length = lines.length; index < length; index++) {
    if (lines[index].match(se)) {
    m.patb_can_act_se = [RegExp.$1, +RegExp.$2, +RegExp.$3, +RegExp.$4];
    return;
    }
    }
    }; // DataManager.load_patb_se_notes

    /*----------------------------------------------------------------------------
    * # Edit class: Game_System
    * - Stores the values of all configurations listed in the plugin manager
    *----------------------------------------------------------------------------*/

    Game_System.prototype.init_patb_se_params =
    Game_System.prototype.init_patb_params;
    Game_System.prototype.init_patb_params = function() {
    this.init_patb_se_params();
    // Added
    var val, params;
    params = PluginManager.parameters(DoubleX_RMMV.PATB_SE_File);
    Object.keys(params).forEach(function(param) {
    val = +params[param];
    this._patb[param] = isNaN(val) ? params[param] : val;
    }, this);
    //
    }; // Game_System.prototype.init_patb_params

    /*----------------------------------------------------------------------------
    * # Edit class: Game_Battler
    *----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * New private instance variables
    *----------------------------------------------------------------------------*/
    // _patb_can_act_se: The array of battler can act se file, volume, pitch and pan

    Game_Battler.prototype.make_patb_act_se = Game_Battler.prototype.make_patb_act;
    Game_Battler.prototype.make_patb_act = function() {
    var s, actable = BattleManager.action_battlers.indexOf(this) >= 0; // Added
    this.make_patb_act_se();
    // Added
    if (actable) { return; }
    s = this.patb_can_act_se();
    AudioManager.playSe({ name: s[0], volume: s[1], pitch: s[2], pan: s[3] });
    //
    }; // Game_Battler.prototype.make_patb_act

    Game_Battler.prototype.init_patb_se = Game_Battler.prototype.init_patb;
    Game_Battler.prototype.init_patb = function() {
    this.init_patb_se();
    // Added
    this._patb_battler_change.can_act_se = true;
    this._patb_note_change.can_act_se = true;
    //
    }; // Game_Battler.prototype.init_patb

    /*----------------------------------------------------------------------------
    * Rereads the effective can act se notetag only if its values can change
    *----------------------------------------------------------------------------*/
    Game_Battler.prototype.patb_can_act_se = function() { // New
    if (this.are_patb_battler_changed("can_act_se")) {
    this.set_patb_can_act_se();
    }
    return this._patb_can_act_se;
    }; // Game_Battler.prototype.patb_can_act_se

    Game_Battler.prototype.set_patb_can_act_se = function() { // New
    this._patb_can_act_se = this.set_patb_notes("patb_can_act_se");
    if (this._patb_can_act_se) { return; }
    var file = $gameSystem.patb.battler_can_act_se_file;
    var vol = $gameSystem.patb.battler_can_act_se_vol;
    var pitch = $gameSystem.patb.battler_can_act_se_pitch;
    var pan = $gameSystem.patb.battler_can_act_se_pan;
    this._patb_can_act_se = [file, vol, pitch, pan];
    }; // Game_Battler.prototype.set_patb_can_act_se

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB SE, place it below PATB Core.");
    }


    Note that:


    - DataManager is touched to read all SE notetags used for notifying a battler becoming Actable from the database


    - Game_System is touched to store the default SE configurations


    - Game_Battler is touched to read the SE to be played when the battler becomes Actable and play that SE at that timing


    DoubleX RMMV Popularized ATB Start

    Its function's to let users alter individual battlers' atb value on battle start


    Its feature set's this:




    *============================================================================
    * ## Notetag Info
    *----------------------------------------------------------------------------
    * # Actor/Class/Weapon/Armor/Enemy/State Notetags:
    * State notetags take the highest priority, followed by enemy, weapon,
    * armor, class and actor
    * 1. <operator patb start: val>
    * - Assigns val to the battler's atb value on battle start via
    * operator
    * - operator can be either =, +, -, *, / or %, meaning set to, add
    * by, subtract by, multiply by, divide by or modulo by respectively
    * - All instances of this notetag will be used sequentially
    * - The final atb value on battle start will become
    * max_atb_val - atb value on battle start for atb fill code delay
    *============================================================================
    * ## Plugin Call Info
    *----------------------------------------------------------------------------
    * # Data Actor/Class/Weapon/Armor/Enemy/State manipulations
    * 1. meta.patb_start
    * - Returns the atb value on battle start with the operator stored in
    * <operator patb start: val> in the form of [opeartor, val]
    * 2. meta.patb_start = [opeartor, val]
    * - Sets the atb value on battle start with the operator stored in
    * <operator patb start: val> as string operator and Number val
    * - All meta.patb_start changes can be saved if
    * DoubleX RMMV Dynamic Data is used
    *============================================================================



    So it basically adds a notetag to let users alter the final ATB value upon battle start.


    It's clear that PATB Core doesn't need nor depend on PATB Start, as PATB already sets the ATB value for all battlers upon battle start, and the latter doesn't do anything other than adding some post processing to the final ATB value using its notetags.


    On the other hand, PATB Start needs and depends on PATB Core, as the former needs to know which variable used by the former stores the ATB value.


    The implementation of PATB Start is easy, simple and small:

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: DataManager
    *----------------------------------------------------------------------------*/

    // data: The data to have its notetags read
    DataManager.load_patb_start_data_notes = DataManager.load_patb_data_notes;
    DataManager.load_patb_data_notes = function(data) {
    this.load_patb_start_data_notes(data);
    this.load_patb_start_notes(data); // Added
    }; // DataManager.load_patb_data_notes

    // data: The data to have its notetags read
    DataManager.load_patb_start_notes = function(data) { // New
    var s = /< *(.+) +patb +start *: *(\d+) *>/i, meta = data.meta;
    meta.patb_start = [];
    data.note.split(/[\r\n]+/).forEach(function(line) {
    if (line.match(s)) { meta.patb_start.push([RegExp.$1, +RegExp.$2]); }
    });
    }; // DataManager.load_patb_start_notes

    /*----------------------------------------------------------------------------
    * # Edit class: Game_BattlerBase
    *----------------------------------------------------------------------------*/

    // start: The battle start type
    Game_BattlerBase.prototype.set_base_start_patb_val =
    Game_BattlerBase.prototype.set_start_patb_val;
    Game_BattlerBase.prototype.set_start_patb_val = function(start) { // New
    // Rewritten
    this.set_base_start_patb_val(start);
    var val = this._patb_val.atb;
    this._patb_val.atb = this.set_multi_patb_notes(val, "patb_start");
    if (this._patb_val.atb < 0) { return this._patb_val.atb = 0; }
    if (this._patb_val.atb <= this._max_patb_val) { return; }
    this._patb_val.atb = this._max_patb_val;
    //
    }; // Game_BattlerBase.prototype.set_start_patb_val

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB Start, place it below PATB Core.");
    }


    Note that:


    - DataManager is touched to read all ATB value alteration notetags used upon battle start from the database


    - Game_BattlerBase is touched to use each of its notetag effective on the battler to alter the final ATB value upon battle start

    DoubleX RMMV Popularized ATB Turn




    Its function's to let users multiply the max turn unit by the number of battlers


    Its feature set's this:




    * @param max_turn_unit_battler_code
    * @desc Sets the code indicating which battlers will be counted for
    * multiplying with max_turn_unit as max_turn_unit_battler_code
    * The number of counted battlers will be multiplied to max_turn_unit
    * Available codes for max_turn_unit_battler_code:
    * alive_battler - All alive battlers will be counted
    * alive_actor - All alive actors will be counted
    * alive_enemy - All alive enemies will be counted
    * all_battler - All battlers will be counted
    * all_actor - All actors will be counted
    * all_enemy - All enemies will be counted
    * max_turn_unit_battler_code won't be used if it doesn't return any
    * available code
    * @default alive_battler



    So it basically lets users change how the maximum Battle Turn Clock unit can be calculated.


    It's clear that PATB Core doesn't need nor depend on PATB Turn, as PATB already lets users set the maximum Battle Turn Clock unit, and the latter doesn't do anything other than giving users more control and freedom over that.


    On the other hand, PATB Turn needs and depends on PATB Core, as the former needs to know which parts of the codes of the latter returns the final maximum Battle Turn Clock unit.


    The implementation of PATB Turn is easy, simple and small:

    if (DoubleX_RMMV["PATB Core"]) {

    /*----------------------------------------------------------------------------*/

    /*----------------------------------------------------------------------------
    * # Edit class: Game_System
    * - Stores the values of all configurations listed in the plugin manager
    *----------------------------------------------------------------------------*/

    Game_System.prototype.init_patb_turn_params =
    Game_System.prototype.init_patb_params;
    Game_System.prototype.init_patb_params = function() {
    this.init_patb_turn_params();
    // Added
    var ps = PluginManager.parameters(DoubleX_RMMV.PATB_Turn_File);
    Object.keys(ps).forEach(function(p) { this._patb[p] = params[p]; }, this);
    //
    }; // Game_System.prototype.init_patb_params

    // unit: The battle turn counter unit
    Game_System.prototype.max_patb_turn_unit_count =
    Game_System.prototype.max_patb_turn_unit;
    Game_System.prototype.max_patb_turn_unit = function(unit) { // Hotspot
    // Rewritten
    var max = this.max_patb_turn_unit_count(unit);
    return max * this.max_patb_turn_unit_multiplier();
    //
    }; // Game_System.prototype.max_patb_turn_unit

    Game_System.prototype.max_patb_turn_unit_multiplier = function() {
    // New; Hotspot
    switch (this._patb.max_turn_unit_battler_code) {
    case "alive_battler":
    var count = $gameParty.aliveMembers().length;
    return (count + $gameTroop.aliveMembers().length);
    case "alive_actor": return $gameParty.aliveMembers().length;
    case "alive_enemy": return $gameTroop.aliveMembers().length;
    case "all_battler":
    var count = $gameParty.battleMembers().length;
    return (count + $gameTroop.members().length);
    case "all_actor": return $gameParty.battleMembers().length;
    case "all_enemy": return $gameTroop.members().length;
    default: return 1;
    }
    }; // Game_System.prototype.max_patb_turn_unit_multiplier

    /*----------------------------------------------------------------------------*/

    } else {
    alert("To use PATB Turn, place it below PATB Core.");
    }


    Note that:


    - Game_System is touched to store the maximum Battle Turn Clock unit multiplier configurations and use the new configuration to calculate the final maximum Battle Turn Clock unit


    Addon Independence

    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB CTB are completely independent of each other as the former doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all and the latter doesn't care about how the Battle Turn Clock's displayed at all


    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB Event are completely independent of each other as the former doesn't care about when the common events will be run at all and the latter doesn't care about the Battle Turn Clock at all


    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB Force are completely independent of each other as the former doesn't care about the ATB Wait Condition at all and the latter doesn't care about the Battle Turn Clock at all


    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB Rate are completely independent of each other as the former doesn't care about the Battler ATB Clock at all and the latter doesn't care about the Battle Turn Clock at all


    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB SE are completely independent of each other as the former doesn't care about SE at all and the latter doesn't care about the Battle Turn Clock at all


    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB Start are completely independent of each other as the former doesn't care about the Battler ATB Clock at all and the latter doesn't care about the Battle Turn Clock at all


    - DoubleX RMMV Popularized ATB Clock and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about how the maximum Battle Turn Clock unit's calculated at all and the latter doesn't care about how the Battle Turn Clock's displayed at all(this involves separation of concerns, which is too advanced to be covered here)


    - DoubleX RMMV Popularized ATB CTB and DoubleX RMMV Popularized ATB Event are completely independent of each other as the former doesn't care about when the common events will be run at all and the latter doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all


    - DoubleX RMMV Popularized ATB CTB and DoubleX RMMV Popularized ATB Force are completely independent of each other as the former doesn't care about how the ATB Wait Condition's determined to be met at all and the latter doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all(this involves separation of concerns, which is too advanced to be covered here)


    - DoubleX RMMV Popularized ATB CTB and DoubleX RMMV Popularized ATB Rate are completely independent of each other as the former doesn't care about the Battler ATB Clock at all and the latter doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all


    - DoubleX RMMV Popularized ATB CTB and DoubleX RMMV Popularized ATB SE are completely independent of each other as the former doesn't care about SE at all and the latter doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all(this involves separation of concerns, which is too advanced to be covered here)


    - DoubleX RMMV Popularized ATB CTB and DoubleX RMMV Popularized ATB Start are completely independent of each other as the former doesn't care about the Battler ATB Clock at all and the latter doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all


    - DoubleX RMMV Popularized ATB CTB and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about how the maximum Battle Turn Clock unit's calculated at all and the latter doesn't care about how many ATB Frame Updates are run for each Battle Frame Update at all


    - DoubleX RMMV Popularized ATB Event and DoubleX RMMV Popularized ATB Force are completely independent of each other as the former doesn't care about the ATB Wait condition at all and the latter doesn't care about when the common events will be run at all


    - DoubleX RMMV Popularized ATB Event and DoubleX RMMV Popularized ATB Rate are completely independent of each other as the former doesn't care about the Battler ATB Clock at all and the latter doesn't care about when the common events will be run at all


    - DoubleX RMMV Popularized ATB Event and DoubleX RMMV Popularized ATB SE are completely independent of each other as the former doesn't care about SE at all and the latter doesn't care about when the common events will be run at all


    - DoubleX RMMV Popularized ATB Event and DoubleX RMMV Popularized ATB Start are completely independent of each other as the former doesn't care about the Battler ATB Clock at all and the latter doesn't care about when the common events will be run at all


    - DoubleX RMMV Popularized ATB Event and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about  at all and the latter doesn't care about when the common events will be run at all


    - DoubleX RMMV Popularized ATB Force and DoubleX RMMV Popularized ATB Rate are completely independent of each other as the former doesn't care about Battler ATB Clock at all and the latter doesn't care about ATB Wait condition at all


    - DoubleX RMMV Popularized ATB Force and DoubleX RMMV Popularized ATB SE are completely independent of each other as the former doesn't care about SE at all and the latter doesn't care about ATB Wait condition at all


    - DoubleX RMMV Popularized ATB Force and DoubleX RMMV Popularized ATB Start are completely independent of each other as the former doesn't care about Battler ATB Clock at all and the latter doesn't care about ATB Wait condition at all


    - DoubleX RMMV Popularized ATB Force and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about Battler ATB Clock at all and the latter doesn't care about ATB Wait condition at all


    - DoubleX RMMV Popularized ATB Rate and DoubleX RMMV Popularized ATB SE are completely independent of each other as the former doesn't care about SE at all and the latter doesn't care about Battler ATB Clock at all


    - DoubleX RMMV Popularized ATB Rate and DoubleX RMMV Popularized ATB Start are completely independent of each other as the former doesn't care about ATB value at all and the latter doesn't care about ATB Rate at all


    - DoubleX RMMV Popularized ATB Rate and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about Battle Turn Clock at all and the latter doesn't care about Battler ATB Clock at all


    - DoubleX RMMV Popularized ATB SE and DoubleX RMMV Popularized ATB Start are completely independent of each other as the former doesn't care about Battler ATB Clock at all and the latter doesn't care about SE at all


    - DoubleX RMMV Popularized ATB SE and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about Battle Turn Clock at all and the latter doesn't care about SE at all


    - DoubleX RMMV Popularized ATB Start and DoubleX RMMV Popularized ATB Turn are completely independent of each other as the former doesn't care about Battle Turn Clock at all and the latter doesn't care about Battler ATB Clock at all






    Summary

    The Core Addon Approach can address the problem of working with extremely massice plugins by breaking down the system into smaller and more modular pieces, exaclty 1 of them being the core plugin implementing the essential parts of the system feature set, and at least another 1 of them being an addon plugin implementing some of the remaining optional parts of the system feature set. The core doesn't need nor depend on any addon but all addons need and depend on the core.


    The Core Addon Approach benefits the users by:


    1. Letting them freely use the features they demand and discard all the other optional ones


    2. Making them feel that the system's easier, simpler and more user-friendly to use


    3. Helping them isolate and pinpoint any issues more easily, effortlessly and swiftly


    and benefits the developers by:


    1. Letting those not comfortable enough with working with a single massive plugin to work with a number of smaller ones instead


    2. Ensuring they won't mess up which parts of the codes implements which parts of the feature set


    3. Letting them just update a smaller plugin at a time instead of having to update a single massive plugin


    Nevertheless, it has the below drawbacks for users:


    1. Having a large number of addons can bloat the plugin manager


    2. Having many updated plugins can mean having many to be downloaded again


    3. The possible presence of addon ordering rules can make some compatibility issues harder to solve


    and the below drawbacks for developers:


    1. Updating a large number of addons can be excessively painful


    2. Tight coupling might be unavoidable if the system's doomed to have Interaddon Dependencies


    3. Both getting rid of the addon ordering rules and preserving them can have their own serious troubles


    When the core changes, addons might have to be changed too.


    Adding an addon to the system needs a crystal clear vision on how the addon works with the core.


    The below addons, which are all independent of each other, of DoubleX RMMV Popularized ATB having DoubleX RMMV Popularized ATB Core as its core, are some examples on writing easy, simple and small addons:


    - DoubleX RMMV Popularized ATB Clock


    - DoubleX RMMV Popularized ATB CTB


    - DoubleX RMMV Popularized ATB Event


    - DoubleX RMMV Popularized ATB Force


    - DoubleX RMMV Popularized ATB Rate


    - DoubleX RMMV Popularized ATB SE


    - DoubleX RMMV Popularized ATB Start


    - DoubleX RMMV Popularized ATB Turn
     
    #8
  9. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    Now let's talk about Ways to study some other ATB system plugins.


    To avoid this reply being too long, I'll just talk about Ellye's ATB(also includes CTB option) for now.


    While thoroughly comprehending this plugin probably needs decent battle related javascript coding proficiency(experienced plugin developers having written dozens of decent battle related plugins), and a solid understanding of the default RMMV battle flow implementations and how that plugin works on the user level, I'll still try to briefly talk about the key parts of that plugin to help you have a basic knowledge on how its implementation  works. Nevertheless, you're still assumed to have a basic knowledge of how that plugin works on the user level.


    in general, an effective and efficient way to quickly get the big picture of a ATB system plugin is to simulate a Battle Frame Update execution, meaning you'll have to at least grasp the implementations of the Battle Frame Update, ATB Wait Condition, ATB Frame Update, Battler ATB Clock, and setting up new Action Execution Subject and inputable actors, as they're all absolute essentials for any ATB system during a Battle Frame Update simulation.


    Be warned: For those not being familiar with reading long functions(excluding the anonymous function wrapping the whole plugin implementation of course), be prepared to read dozens of them, which can be as long as having 118+ LoC :D


    Ellye's ATB(also includes CTB option)

    Feature Set




    Parameters:




    * @param ===GAMEPLAY===
    * @desc Parameters below this one are Gameplay-related options
    * @default .
    *
    * @param Agility Weight
    * @desc The higher this integer value, the more noticeable the difference of having high Agility vs. low Agility will be. Default 100.
    * @default 100
    *
    * @param Turn Timer
    * @desc Default: 150. The speed at the virtual "turn" value increases - this is relevant for battles with events that happen on the Nth turn, for example, or for monsters that use skills after the Nth turn, etc. The value entered here will be how much "Agility" the turn timer has. This is invisible to the player.
    * @default 150
    *
    * @param Average Turn Timer
    * @desc 1 = Create a Turn Timer based on the average agility of all Battlers present. 0 = Use the provided turn timer above. Default: 0
    * @default 1
    *
    * @param Base Fill Speed
    * @desc Default: 100. The base speed that the bar fills by itself, also affect Turn Timer in the same way.
    * @default 100
    *
    * @param Display Fight / Escape at beginning of the battle
    * @desc 1 = yes; 0 = no. Default: 0
    * @default 0
    *
    * @param Allow Escape During Battle
    * @desc 1 = yes; 0 = no. Default: 0
    * @default 0
    *
    * @param Starting ATB
    * @desc Multiplied by Agility and Agility Weight/100. Default: 50
    * @default 50
    *
    * @param Starting ATB Random Bonus
    * @desc Maximum random bonus for ATB at battle start. Default: 0
    * @default 0
    *
    * @param Full ATB Gauge
    * @desc The value of a full ATB Gauge. Default: 50000
    * @default 50000
    *
    * @param Instant ATB
    * @desc 1: ON, 0: OFF. Default: 0; Jumps instantly from a turn to the next. Best for CTB style.
    * @default 0
    *
    * @param Battle Event Turn
    * @desc 0: Per timer, 1: per action. Advances the Battle Turn for Events and monster AI. Default: 0
    * @default 0
    *
    * @param Battler EndTurn Effects
    * @desc 0: per timer, 1: per action. Def: 0. Whether "end of turn" for battlers is at turn timer or after they act.
    * @default 0
    *
    * @param Interrupt Compensation Pct
    * @desc What % of the ATB spent in casting is salvaged when interrupted. Default: 25
    * @default 25
    *
    * @param Interrupt Compensation Max
    * @default Maximum % of a full ATB gauge that can be given as compensation when interrupted. Default: 50
    * @default 50
    *
    * @param Maximum Delay Percent
    * @default Maximum that a skill can be delayed, as a % of its cast time. Default: 100
    * @default 100
    *
    * @param Default Delay
    * @default How much % a skill get delayed when hit by a delaying effect. Default: 10
    * @default 10
    *
    * @param Delayable by Default
    * @default 0: No, 1: Yes. Whether every skill is delayable by default.
    * @default 0
    *
    * @param Delays by Default
    * @default 0: No, 1: Yes. Whether every skill applies delay by default.
    * @default 1
    *
    * @param Default Cast Paramater
    * @default Default: agi. Other options: atk, def, mat, mdf, luk, hp, mp...
    * @default agi
    *
    * @param ===SOUND FX===
    * @desc You can override those settings per actor, view HELP.
    * @default .
    *
    * @param Play SE
    * @desc Whether to play SE when Actor is ready. 0 = OFF; 1 = ON. Default: 0
    * @default 0
    *
    * @param SE Name
    * @desc The name of the default Sound Effect file (inside /audio/se), sans extension.
    * @default Cursor3
    *
    * @param SE Volume
    * @desc The default volume for SE (from 0 to 100). Default: 75
    * @default 75
    *
    * @param SE Pitch
    * @desc The default pitch for SE (from 50 to 150). Default: 100
    * @default 100
    *
    * @param SE Pan
    * @desc The default pan for the SE (from -100 to 100). Default: 0
    * @default 0
    *
    * @param ===GAUGE HUD====
    * @desc Parameters below this one are related to the ATB Gauge interface
    * @default .
    *
    * @param Display Gauges
    * @desc 1 = ON; 0 = OFF. Default: 1. If off, all gauge settings are ignored.
    * @default 1
    *
    * @param Actor Name Width
    * @desc Default: 120.
    * @default 120
    *
    * @param Actor Name X Offset
    * @desc Default: 0.
    * @default 0
    *
    * @param Actor State Icons X Offset
    * @desc Default: 0.
    * @default 0
    *
    * @param Gauge Name
    * @desc What label to show on the ATB gauge.
    * @default AT
    *
    * @param Cast Gauge Name
    * @desc What label to show on the cast gauge.
    * @default Cast
    *
    * @param Use Skill Name for Cast Gauge
    * @desc 0: OFF, 1: ON. Default: 1
    * @default 1
    *
    * @param Reversed Cast Gauge
    * @desc 0: OFF, 1: ON. Default: 0.
    * @default 0
    *
    * @param Gauge Color 1
    * @desc First color of the gauge gradient. Default: #505060
    * @default #505060
    *
    * @param Gauge Color 2
    * @desc Second color of the gauge gradient. Default: #D5D5E0
    * @default #D5D5E0
    *
    * @param Cast Gauge Color 1
    * @desc First color of the cast gauge gradient. Default: #8E0B8A
    * @default #8E0B8A
    *
    * @param Cast Gauge Color 2
    * @desc Second color of the cast gauge gradient. Default: #EA7BD9
    * @default #EA7BD9
    *
    * @param Interruptible Gauge Color 1
    * @desc First color of the interruptible cast gauge gradient. Default: #D5D315
    * @default #D5D315
    *
    * @param Interruptible Gauge Color 2
    * @desc Second color of the interruptible cast gauge gradient. Default: #EDEE87
    * @default #EDEE87
    *
    * @param Positive Haste Gauge Color 1
    * @desc ATB Gauge when hasted. Default: #ECAA93
    * @default #ECAA93
    *
    * @param Positive Haste Gauge Color 2
    * @desc ATB Gauge when hasted. Default: #E6BA98
    * @default #E6BA98
    *
    * @param Negative Haste Gauge Color 1
    * @desc ATB Gauge when slowed. Default: #1D5E86
    * @default #1D5E86
    *
    * @param Negative Haste Gauge Color 2
    * @desc ATB Gauge when slowed. Default: #2191A1
    * @default #2191A1
    *
    * @param Zero Haste Gauge Color 1
    * @desc ATB Gauge when stopped. Default: #430F0F
    * @default #430F0F
    *
    * @param Zero Haste Gauge Color 2
    * @desc ATB Gauge when stopped. Default: #4B1618
    * @default #4B1618
    *
    * @param ATB Gauge Text Width
    * @desc Width area for the text. Default: 60
    * @default 60
    *
    * @param Gauge Area Size
    * @desc Width of the area for gauges. Default: 400
    * @default 400
    *
    * @param ==POS WITH TP==
    * @desc Those affect the positions of gauge when you have TP Display enabled.
    * @default .
    *
    * @param HP Gauge X Position (with TP)
    * @desc Default: 0
    * @default 0
    *
    * @param HP Gauge Width (with TP)
    * @desc Default: 97
    * @default 97
    *
    * @param MP Gauge X Position (with TP)
    * @desc Default: 112
    * @default 112
    *
    * @param MP Gauge Width (with TP)
    * @desc Default: 86
    * @default 86
    *
    * @param TP Gauge X Position
    * @desc Default: 213
    * @default 213
    *
    * @param TP Gauge Width
    * @desc Default: 86
    * @default 86
    *
    * @param ATB Gauge X Position (with TP)
    * @desc Default: 314
    * @default 314
    *
    * @param ATB Gauge Width (with TP)
    * @desc Default: 86
    * @default 86
    *
    * @param ==POS WITHOUT TP==
    * @desc Those affect the positions of gauge when you have TP Display disabled.
    * @default .
    *
    * @param HP Gauge X Position
    * @desc Default: 0
    * @default 0
    *
    * @param HP Gauge Width
    * @desc Default: 130
    * @default 130
    *
    * @param MP Gauge X Position
    * @desc Default: 145
    * @default 145
    *
    * @param MP Gauge Width
    * @desc Default: 120
    * @default 120
    *
    * @param ATB Gauge X Position
    * @desc Default: 280
    * @default 280
    *
    * @param ATB Gauge Width
    * @desc Default: 120
    * @default 120
    *
    * @param ===TURN ORDER HUD===
    * @desc Parameters bellow this one are related to the turn order display
    * @default .
    *
    * @param Display Predicted Turn Order
    * @desc 1 = yes, 0 = no. Default: 0
    * @default 0
    *
    * @param Display as Faces or Names
    * @desc 1 = Faces, 0 = Names. See Help. Default: 0
    * @default 0
    *
    * @param Invert TO Direction
    * @desc 0 = Left-to-Right; 1 = Right-to-Left. Default: 0
    * @default 0
    *
    * @param Opacity First TO
    * @desc Opacity of the first actor window (from 0 to 255). Default: 220
    * @default 220
    *
    * @param Opacity Last TO
    * @desc Opacity of the last actor window (from 0 to 255). Default: 160. To disable gradient, set the same value as above.
    * @default 160
    *
    * @param Resize Skill and Item Window
    * @desc 1: ON, 0: OFF. Default: 1. To allow room for the turn prediction interface (only if it's enabled)
    * @default 1
    *
    * @param Prediction Bonus
    * @desc String to show when predicting a buff to ATB. Default: �. Leave blank for none.
    * @default �
    *
    * @param Prediction Malus
    * @desc String to show when predicting a buff to ATB. Default: �. Leave blank for none.
    * @default �
    *
    * @param Prediction Cast
    * @desc String to show when predicting a buff to ATB. Default: �. Leave blank for none.
    * @default �
    *
    * @param Prediction Bonus Color
    * @desc Color for the bonus string. Default: #62D962
    * @default #62D962
    *
    * @param Prediction Malus Color
    * @desc Color for the malus string. Default: #D96262
    * @default #D96262
    *
    * @param Prediction Cast Color
    * @desc Color for the cast string. Default: #8662D9
    * @default #8662D9
    *
    * @param ===ENEMY GAUGES===
    *
    * @param Display Enemy Cast Gauges
    * @desc 0 = off; 1 = on. Default: 1
    * @default 1
    *
    * @param Display Enemy ATB Gauges
    * @desc 0 = off, 1 = on. Default: 0
    * @default 0
    *
    * @param Enemy Gauge Opacity
    * @desc From 0 to 255 (Default: 200)
    * @default 200
    *
    * @param Enemy Show Cast Name
    * @desc 0 = Show no text; 1 = Show skill name; 2 = Show gauge name. Default: 1
    * @default 1
    *
    * @param Enemy Show Name
    * @desc 0 = Show no text; 1 = Show enemy name; 2 = Show gauge name. Default: 1
    * @default 1
    *
    * @param Enemy Gauge Color1
    * @desc Default: #FA8691
    * @default #FA8691
    *
    * @param Enemy Gauge Color2
    * @desc Default: #F0B8C8
    * @default #F0B8C8
    *
    * @param ===MISC UI===
    * @desc Other UI related options
    * @default .
    *
    * @param Step Forward When Ready
    * @desc Whether actors should step forward when its their time for input. 1: ON. 0: OFF. Default: 1
    * @default 1
    *
    * @param Default Cast Motion
    * @desc 0: none, 1: chant, 2: skill, 3: item, 4: spell. Default: 1
    * @default 1
    *
    * @param Default Cast Animation
    * @desc The ID of the default Animation. 0 for none. Suggested (from default DB): 52. Default: 0
    * @default 0
    *
    * @param Interrupt Animation
    * @desc Battle animation to be played when a skill is interrupted. Suggestion: create one with flash and sound only. Default: 0
    * @default 0



    On a side note: The displays of some characters are problematic. I hope it won't matter too much :)


    Help:

    * @help Actors will act as frequently as their AGILITY attribute allows them to, instead of taking fixed turns.
    * They will have an "AT" Gauge in the interface that goes from 0 to 1000, and they will get a turn to act when it reaches 1000.
    * Gauges are paused while the player is deciding on a command, or while an animation is being played.
    * A full gauge requires 50000 ATB by default (excess ATB is not lost, and the number can be negative).
    *
    * USING THE [NOTE] FIELD TO SETUP SKILLS THAT INTERACT WITH ATB:
    * In each Skill or Combat Usable Item, you can set up [NOTE]s that interact with the ATB system.
    * There's the following notes:
    * <self_atb:FORMULA>
    * <target_atb:FORMULA>
    *
    * It's important to note that those need to be ARITHMETICAL FORMULAS, not commands.
    * The following examples are valid:
    * <self_atb:25000> #This would make a quick skill, as your next turn will come faster.
    * <self_atb:-25000> #This one would delay your next turn
    * <self_atb:50*a.agi - 20*b.agi> #This would give the user some ATB based on the difference of AGI between him and the target
    * <target_atb:25000> #This would GIVE the target some atb, making his turn come quicker.
    * <target_atb:-10000> #This would DELAY the target next turn
    * <target_atb:-50*(3*a.mat - b.mdf)> #This would DELAY the target next turn based on the difference between the attacker Magic Attack and the target Magic Defense
    *
    * So:
    * It needs to be a formula that results in a NUMBER, not a command. Do not use "a.atb+=5000;" or similar assignments.
    * You do can use methods like Math.random or Math.Max; you do can use if-else conditions; just make sure that the end result will be a number and that you aren't executing any action except calculating that number.
    * POSITIVE numbers will always GIVE ATB, no matter if for caster or target.
    * NEGATIVE numbers will always REMOVE ATB, no matter if for caster or target.
    * Important note about SKILL WITH MULTIPLE TARGET: The "self_atb" parameter applies ONCE PER TARGET HIT, so balance accordingly (this was also the case with the Formula field).
    *
    * If you use Display Faces, your enemies require to have a face configured in the Database Editor.
    * In the [Notes] field, you should add, for example: <face_name:Monster><face_id:2> - this would use the RTP Orc face.
    * In that example <face_name> receives the name of the file, and face_id the index of the face, counting left-to-right, top first, then bottom. Starts at 0.
    *
    * You can set up Actor-specific ready Sound Effects (if you enable Play SE). It's similar to the way monster faces are set up.
    * In the [Notes] field of an actor, you can enter: <se_name:Bow4><se_volume:100><se_pitch:150> to play the sound "Bow4" at those settings whenever that Actor is ready, for example.
    *
    * CASTING TIMES:
    * You can now set up casting times by adding:
    * <cast_time:NUMBER>
    * On the [Note] tag of a skill. For example, <cast_time:25000> would require the same time as half of a full default ATB gauge.
    *
    * Skills with casting time can also be marked with
    * <interruptible>
    * (or <interruptable>)
    *
    * This means that they will be broken when hit by a skill that is marked as:
    * <interrupt>
    * (or <interrupts>)
    *
    * By default, interruptable skills have a different cast gauge color.
    * You can also set up an Animation to play whenever a skill is successfully interrupted.
    * SUGGESTION: Create a new Animation with just a flash and some sound effect to use for this, most of the default VFXs are too fancy for that (keep in mind that this plays together with the hit vfx).
    *
    * For the DELAY system, you can set default delay parameters in the Plugin Paramaters, and customize them per skill with the following notes:
    *
    * <delayable>
    * Will mark a skill as delayable, regardless of default settings.
    *
    * <not_delayable>
    * Will mark a skill as not delayable, regardless of default settings.
    *
    * <delays>
    * Will make a skill apply delay regardless of default settings. If no <delay_amount> is specified, it will use the default delay amount. Will also delay even if it targets ally (by default this doesn't happen)
    *
    * <delay_amount:NUMBER>
    * The amount (a % of the total cast time) of delay to be applied. For example, 25 means that it will bring the casting bar down by 25% of its maximum value. If you set to 0, no delay will be applied (in case you're using Delays by Default parameter).
    *
    *
    * Another notes you can have for casting skills:
    * <cast_animation:NUMBER>
    * This is the number of an Animation (from the Animations tab in the database) to be cast at the start of the cast.
    *
    * <cast_motion:MOTION>
    * The movement your character does while casting. Some valid options are:
    * <cast_motion:spell>, <cast_motion:skill>, <cast_motion:item>, <cast_motion:chant>, <cast_motion:swing>, <cast_motion:thrust>, <cast_motion:missile>, <cast_motion:wait>
    *
    * <cast_param:pARAM>
    * Will use that Param instead of the default (agi, normally) for casting that skill. For example:
    * <cast_param:mat> will use MAT instead of AGI.
    *
    * STATES:
    * If you want a State to count down per action taken, you set it up as Action End.
    * If you want a State to count down per virtual turn, you set it up as Turn End.
    *
    * Optionally, you can also opt to use neither of those and instead have your state timed by ATB gauge (can also be used together with the above, whichever comes first will remove the state)
    * This might be the optimal design overall for the ATB system, at least for states that directly affect the ATB gauge:
    * To do so, use the following tag:
    * <atb_duration:NUMBER>
    * For example, 150000 would last the normal duration of three turns for that actor (not taking modifiers like haste in consideration).
    * Optionally, you can also specify which parameter is used for the speed at which the state comes out. To do so, use:
    * <atb_duration_param:atk>, <atb_duration_param:def>, <atb_duration_param:mat>, <atb_duration_param:mdf>, <atb_duration_param:agi> or <atb_duration_param:luk>
    * You can also use <atb_duration_param:turn> to make it follow Turn Timer and be independent of the target's parameters.
    *
    *
    * STARTING ATB:
    * You can use the note-tag
    * <start_atb:NUMBER>
    * On actors, classes, equips and enemies to set up bonus starting ATB (absolute vlaues).
    *
    * HASTE:
    * You can use the note-tags:
    * <haste:NUMBER>
    * <haste_atb:NUMBER>
    * <haste_cast:NUMBER>
    * This is a multiplicative percent change to all speeds, atb-only speed or cast-only speed.
    * A value of "<haste:100>" means no change. A value of "<haste:200>" would mean double speed, for example, while a value of "<haste:50>" would mean half speed.
    * Those tags can be used on Actors, Classes, Equips and Enemies.




    Battle Frame Update

    Scene_Battle.prototype.updateBattleProcess is extended to this:




    _alias_sb_updateBattleProcess = Scene_Battle.prototype.updateBattleProcess;
    Scene_Battle.prototype.updateBattleProcess = function() {
    if (!BattleManager.isBattleEnd() && !BattleManager.isAborting())
    {
    BattleManager.requestAllCastMotions();
    }
    _alias_sb_updateBattleProcess.call(this);
    };



    Note that the ATB Wait Condition is hardcoded to be always met whenever there are inputable actors, as BattleManager.update, which is the only remaining possible function running the ATB Frame Update, will only be called when Scene_Battle.prototype.isAnyInputWindowActive returns false.


    ATB Frame Update/Battle Turn Clock/Battler ATB Update/Turn Start/Turn End/Updates actor ATB bars/Update State Turns

    BattleManager.update is extended to this:




    //Changing the flow of battle
    _BattleManager_update = BattleManager.update;
    BattleManager.update = function() {
    if (!this.isBusy() && !this.updateEvent())
    {
    switch (this._phase)
    {
    case 'atb':
    this.increaseAtbGauges();
    break;
    default:
    _BattleManager_update.call(this);
    break;
    }
    }
    };



    Recall that BattleManager.update won't be called whenever there are inputable actors. In the default RMMV battle flow, it's used to handle the Action Execution Phase.


    Note that:


    1. BattleManager.increaseAtbGauges will be called when the battle phase's atb, meaning the battle's Not Executing Actions, and the original BattleManager.update will be called when the battle phase's some other values, meaning the battle's Executing Actions.


    2. BattleManager.updateEvent() is called twice instead of once per frame when the battle's Executing Actions.


    BattleManager.increaseAtbGauges will run the ATB Frame Update/Battle Turn Clock/Battler ATB Update/Turn Start/turn End/actor ATB bar updates/state turn updates when the battle phase is atb:

    //Increases the ATB gauges when idle:
    BattleManager.increaseAtbGauges = function() {
    var loopForInstantATB = instant_atb;
    var oneATBFilledAlready = false;
    //================================================
    //ATB LOOP
    //We need to be in a loop here if we're using instant ATB option, but run only once in other cases.
    //================================================
    do
    {
    //==========================================
    //Turn Timer
    //==========================================
    turn_atb += CalculateATBRate(turn_timer);
    //Turn timer finished:
    if (turn_atb >= full_atb)
    {
    turn_atb -= full_atb;
    //We advance the troops page (for events and AI) depending on parameter:
    if (battle_event_turn_mode === 0)
    {
    $gameTroop.increaseTurn();
    }
    //We apply onTurnEnd if paramaters are set so
    if (battler_end_turn_effects !== 1)
    {
    this.allBattleMembers().forEach(function(battler)
    {
    battler.onTurnEnd();
    this.refreshStatus();
    this._logWindow.displayAutoAffectedStatus(battler);
    this._logWindow.displayRegeneration(battler);
    }, this);
    }
    }
    //========================================
    //End of turn timer related stuff
    //========================================

    this.allBattleMembers().forEach(function(battler)
    {
    //States with ATB duration:
    battler._states_atb_duration.forEach(function(duration, stateID)
    {
    battler._states_atb_current[stateID] += CalculateATBRate(battler._states_atb_rate[stateID], 100);
    if (battler._states_atb_current[stateID] >= duration)
    {
    battler.removeState(stateID);
    }
    }, this);

    //Dead battler has no ATB nor cast:
    if (battler.isDead())
    {
    battler.atb = 0;
    battler.resetCast();
    }
    //====================
    //Casting time logic:
    //====================
    //Cast finished:
    else if (battler.finished_casting === true)
    {
    battler.resetCast();
    }
    //Currently casting:
    else if (battler.target_cast_atb > 0)
    {
    //Got stunned or similar while casting, auto-interrupt for now:
    if (!battler.canMove())
    {
    battler.BreakCurrentCast(true);
    }
    else
    {
    battler.current_cast_atb += battler._cast_rate * (battler.CastHaste() / 100);
    if (battler.current_cast_atb >= battler.target_cast_atb && oneATBFilledAlready === false)
    {
    battler.finished_casting = true;
    this.battlerHasFullAtb(battler, true);
    oneATBFilledAlready = true;
    loopForInstantATB = 0;
    }
    }
    }
    //===================
    //Not casting, ATB INCREASE:
    //===================
    else
    {
    battler.atb += battler.calculateATBRate();
    }
    if (battler.atb >= full_atb && oneATBFilledAlready === false && battler.target_cast_atb <= 0)
    {
    this.battlerHasFullAtb(battler);
    oneATBFilledAlready = true;
    loopForInstantATB = 0;
    }
    }, this);
    } while (loopForInstantATB === 1);
    //==============================
    //END OF THE ATB LOOP
    //=============================
    this.refreshStatus();
    };


    Note that:


    1. It's possible for 1 ATB Frame Update to trigger Turn Start/turn end more than once.


    2. The state turns can be run using the number of ATB Frame Updates triggered with the states effective as well as using the default RMMV implementations.


    3. The battler's Battler ATB Clock will be reset here instead of right after executing all actions(next frame vs same frame).


    4. If the instant ATB mode's used, then updating the Battle Turn Clock immediately followed by updating each battler's Battler ATB Clock will be repeatedly run until at least 1 battler have finished casting an action and/or become inputable in the same ATB Frame Update, otherwise the Battle Turn Clock will just be updated once immediately followed by updating each battler's Battler ATB Clock exactly once.


    5. The whole status window instead of just all actors' ATB bars will always be redrawn per ATB Frame Update.


    Also, the below timings transit the battle phase to Not Executing Actions:

    BattleManager.updateTurnEnd(rewritten) -




    //Change the end of turn:
    BattleManager.updateTurnEnd = function() {
    $gameParty.makeActions();
    $gameTroop.makeActions();
    this._phase = 'atb';
    };



    Note that updateTurnEnd will only be called right after the Action Execution Subject has executed all actions.


    BattleManager.battlerHasFullAtb(new) -

    //When a Battler (might be party or enemy) has full ATB:
    BattleManager.battlerHasFullAtb = function(battler, wasCasting)
    {
    BattleManager.refreshStatus();
    if (typeof wasCasting === 'undefined')
    {
    wasCasting = false;
    }
    this.predictTurnOrder();
    this._subject = battler;
    this._turn_end_subject = battler;
    this._pending_atb_removal = true;
    //If the character already has an action that he was casting:
    if (wasCasting)
    {
    battler.setAction(0, battler.casting_action);
    this._phase = 'turn';
    return;
    }
    if (battler.isActor())
    {
    battler.makeActions();
    if (battler.canInput())
    {
    AudioManager.playFullATB(battler);
    this._actorIndex = battler.index();
    //If the Step_Forward_When_Ready parameter is turned on:
    if (step_forward === 1)
    {
    battler.setActionState('inputting');
    if (typeof battler.spriteStepForward === 'function')
    {
    //This is for compatibility with Yanfly's.
    battler.spriteStepForward();
    }
    }
    this._phase = 'input';
    }
    else
    {
    this._phase = 'turn';
    }
    }
    else if (battler.isEnemy())
    {
    battler.makeActions();
    if (battler.CheckIfSkillHasCastingTime())
    {
    this._phase = 'atb';
    }
    else
    {
    this._phase = 'turn';
    }
    }
    };


    Note that it'll be explored further in Setting up new Action Execution Subject/inputable actors.


    BattleManager.selectNextCommand(rewrite) -

    //Process turn when we finish inputing command
    BattleManager.selectNextCommand = function() {
    do {
    if (!this.actor() || !this.actor().selectNextCommand())
    {
    $gameParty.requestMotionRefresh();
    if (this.actor() !== null && this.actor().CheckIfSkillHasCastingTime())
    {
    this.actor().setActionState('waiting');
    if (typeof this.actor().spriteStepBack === 'function')
    {
    //This is for compatibility with Yanfly's.
    this.actor().spriteStepBack();
    }
    this._phase = 'atb';
    break;
    }
    this._phase = 'turn';
    break;
    }
    } while (!this.actor().canInput());
    };


    Note that whether the battle will transit to Executing Actions or Not Executing Actions depends on whether the 1st action of the actor has casting time.


    Scene_Battle.prototype.commandFight(rewrite) -

    //Command Fight by default would send us spiraling to a "turn" end (for nobody), instead let's just send us to the ATB phase:
    Scene_Battle.prototype.commandFight = function() {
    BattleManager._phase = 'atb';
    };


    Note that selecting the fight command in the party command window will simply close it.


    To sum up, the battle phase will transit to Not Executing Actions right after:


    1. The Action Execution Subject has executed all actions


    2. An actor/enemy has inputted all actions with the 1st one having cast time


    3. The fight command in the party command window's selected


    The below timings transit the battle phase to some other values(except upon battle start):


    BattleManager.battlerHasFullAtb(mentioned before)


    BattleManager.selectNextCommand(mentioned before)


    BattleManager.endTurn(rewrite) -

    //We need to change the OnTurnEnd method, because otherwise it will apply to all battlers whenever anyone acts:
    BattleManager.endTurn = function() {
    this._phase = 'turnEnd';
    this._preemptive = false;
    this._surprise = false;
    this.predictTurnOrder();
    this.clearActor();
    if (battle_event_turn_mode !== 0)
    {
    $gameTroop.increaseTurn();
    }
    };


    Note that it'll be called right after the Action Execution Subject has executed all actions.


    In short:


    1. The battle phase transits to Executing Actions right after a battler has completely casted an action about to be executed


    2. The battle phase transits to Inputting Actions(a subphase for Not Executing Actions when Executing Actions won't take place while there are inputable actors) right after an actor becomes inputable


    3. The battle phase transits to turn end(the transition from Executing Actions to Not Executing Actions) right after the Action Execution Subject has executed all actions


    Setting up new Action Execution Subject/inputable actors

    Recall that BattleManager.increaseAtbGauges will call BattleManager.battlerHasFullAtb whenever a battler has fully casted an action about to be executed or becomes Actable.


    BattleManager.battlerHasFullAtb setups the Action Execution Subject to be a battler having fully casted an action about to be executed or selects an actor becoming Actable with his/her/its actor command window being setup(except for autobattle/confusion actors where all of their actions will always ignore the casting time):




    //When a Battler (might be party or enemy) has full ATB:
    BattleManager.battlerHasFullAtb = function(battler, wasCasting)
    {
    BattleManager.refreshStatus();
    if (typeof wasCasting === 'undefined')
    {
    wasCasting = false;
    }
    this.predictTurnOrder();
    this._subject = battler;
    this._turn_end_subject = battler;
    this._pending_atb_removal = true;
    //If the character already has an action that he was casting:
    if (wasCasting)
    {
    battler.setAction(0, battler.casting_action);
    this._phase = 'turn';
    return;
    }
    if (battler.isActor())
    {
    battler.makeActions();
    if (battler.canInput())
    {
    AudioManager.playFullATB(battler);
    this._actorIndex = battler.index();
    //If the Step_Forward_When_Ready parameter is turned on:
    if (step_forward === 1)
    {
    battler.setActionState('inputting');
    if (typeof battler.spriteStepForward === 'function')
    {
    //This is for compatibility with Yanfly's.
    battler.spriteStepForward();
    }
    }
    this._phase = 'input';
    }
    else
    {
    this._phase = 'turn';
    }
    }
    else if (battler.isEnemy())
    {
    battler.makeActions();
    if (battler.CheckIfSkillHasCastingTime())
    {
    this._phase = 'atb';
    }
    else
    {
    this._phase = 'turn';
    }
    }
    };



    Note that it's impossible for both setting up new Action Execution Subject/inputable actors to happen in the same ATB Frame Update.


    Starting ATB Value

    Game_Battler.prototype.onBattleStart is extended to set each battler's Starting ATB Value:




    //At the start of battle, ATB values are reset to 0:
    _Game_Battler_prototype_onBattleStart = Game_Battler.prototype.onBattleStart;
    Game_Battler.prototype.onBattleStart = function() {
    this.resetCast();
    this._states_atb_current = [];
    this._states_atb_duration = [];
    this._states_atb_rate = [];
    this.CalculateHaste();
    this.atb = this.bonusStartingATB() + (Math.random() * starting_atb_random) + starting_atb * this.agi * (agi_weight / 100);
    if (BattleManager._surprise && this.isEnemy() && this.isAppeared())
    {
    this.atb += full_atb;
    }
    else if (BattleManager._preemptive && this.isActor())
    {
    this.atb += full_atb;
    }
    _Game_Battler_prototype_onBattleStart.call(this);
    };



    Note that it initializes all other atb variables as well.


    Reset Battler ATB Clock

    Besides Game_Battler.prototype.onBattleStart and BattleManager.increaseAtbGauges, Game_Battler.prototype.BreakCurrentCast can also call Game_Battler.prototype.resetCast:




    //Breaks current skill cast:
    Game_Battler.prototype.BreakCurrentCast = function(forceBreak)
    {
    if (typeof forceBreak !== 'undefined')
    {
    var force = true;
    }
    if (this.IsCastInterruptible() || force)
    {
    this.atb += Math.min(this.current_cast_atb * interrupt_compensation_pct / 100, full_atb * interrupt_compensation_max / 100);
    this.atb -= full_atb;
    this.resetCast();
    if (interrupt_vfx > 0)
    {
    this.startAnimation(interrupt_vfx, 0, 0);
    }
    }
    };



    In short, Game_Battler.prototype.resetCast will be called upon battle start, for all battlers per ATB Frame Update, or those just finished casting or those casting's broken.


    Game_Battler.prototype.resetCast resets the battler's Battler ATB Clock:

    //A method to reset all cast related stuff:
    Game_Battler.prototype.resetCast = function() {
    this.setActionState('undecided');
    this.clearMotion();
    $gameParty.requestMotionRefresh();
    this.target_cast_atb = 0;
    this.current_cast_atb = 0;
    this.casting_action = null;
    this.finished_casting = false;
    this.cast_delay_current = 0;
    this.cast_delay_max = 0;
    this.calculateCastRate();
    };


    Note that the ATB value is reset by some other functions rather than this one.


    Battler inputability check

    Recall that the ATB Wait Condition will always be met whenever an actor's inputable.


    So excluding party escape attmpets, when an actor becomes inputable, that actor will always remain inputable until that actor has inputted all actions, as the only thing can happen before that is to input actions. Since the battle phase will always transit to Inputting Actions whenever an actor's inputable and from Inputting Actions to some other values whenever that actor has finished inputting all actions, there's no need for any extra battler inputability check.


    Failed party escape attempts

    Here, a failed party escape attempt will lead to the ATB Battler Clock reset for the actor selected right before triggering that party escape attempt instead of resetting all party member's ATB Battler Clocks. The below shows why -


    1. Recall that BattleManager.startTurn will be called upon failed party escape attempts:




    BattleManager.startTurn = function() {
    this._phase = 'turn';
    this.clearActor();
    $gameTroop.increaseTurn();
    this.makeActionOrders();
    $gameParty.requestMotionRefresh();
    this._logWindow.startTurn();
    };



    Note that the battle turn count will always be increased by 1 for each failed party escape attempt.


    As BattleManager._phase becomes 'turn', the original BattleManager.update will call BattleManager.updateTurn in the next Battle Frame Update, which will call the ATB Frame Update, since now the battle phase's no longer Inputting Actions:

    BattleManager.updateTurn = function() {
    $gameParty.requestMotionRefresh();
    if (!this._subject) {
    this._subject = this.getNextSubject();
    }
    if (this._subject) {
    this.processTurn();
    } else {
    this.endTurn();
    }
    };


    Note that BattleManager.getNextSubject is rewritten to always return null.


    As BattleManager._subject is set as the actor selected right before triggering that party escape attempt when BattleManager.battlerHasFullAtb is called, BattleManager.processTurn will be called:

    //Remove the ATB value when a turn is processed:
    _BattleManager_processTurn = BattleManager.processTurn;
    BattleManager.processTurn = function() {
    if (this._pending_atb_removal === true)
    {
    this._subject.atb -= full_atb;
    this._pending_atb_removal = false;
    }
    _BattleManager_processTurn.call(this);

    };


    As BattleManager._pending_atb_removal is set as true when BattleManager.battlerHasFullAtb is called, the ATB value of the actor selected right before triggering that party escape attempt, which is previously equal to the Maximum ATB Value, will be reset as 0.


    Maximum ATB Value

    Its value can only be set once, and is done in the plugin manager. Just note that it's also the Battler Turn Clock maximum unit.


    Battle Frame Update Execution Simulation

    With all the essential components covered, let's try to simulate a Battle Frame Update Execution:


    The simplified ATB flow with the ATB system essentials only:




    [​IMG]



    Strictly speaking, this flowchart isn't 100% accurate, as each loop in the loop instant ATB part in the ATB Frame Update won't stop until all battlers are updated.


    1. When the Battle Frame Update runs, the ATB Wait Conditions, which will always be met when the battle phase is Inputting Actions, will determine whether inputting actions or the ATB Frame Update will take place.


    2. If there are inputable actors, players can either trigger a party escape attempt or input actions for the currently selected actor.


    3. If players trigger a party escape attempt, the battle will be aborted if it succeeded, or the Battler ATB Clock of the currently selected actor will be reset.


    4. If players input actions for the currently selected actor, the process will continue until all actions for that actor are inputted. Then that actor will either starts to cast the 1st action if it has casting time, or will immediately execute that action otherwise.


    5. If the ATB Frame Update's to be run instead, it'll repeatedly update the Battle Turn Clock followed by each battler's state durations and Battler ATB Clock until the loop instant atb stops if the battle's Not Executing Actions, or execute actions if the battle's Executing Actions.


    6. If the battle's Executing Actions, the Action Execution Subject will execute all actions sequentially until they're all executed. Then the battle phase will transit to Not Executing Actions.


    7. When updating the Battle Turn Clock, the battle turn clock and turn end immediately followed by Turn Start will be triggered when the corresponding configurations are met and it reaches its maximum unit, which is the same as the Maximum ATB Value.


    8. When updating a battler's state durations, all state with duration reaching 0 will be removed.


    9. When updating a battler's Battler ATB Clock, it'll be reset if that battler's dead or finished casting an action.


    10. If a battler becomes finished casting an action, the battle will transit to Executing Actions, although this effect will start to take place in the next ATB Frame Update.


    11. If a battler becomes Actable, that battler will either become inputable as well or will immediately cast or execute actions. If a battler executes actions now, the battle will transit to Executing Actions, although this effect will start to take place in the next ATB Frame Update.


    12. If a battler becomes inputable, the battle will transits to Inputting Actions.


    The rest are nonessential ATB system features(i.e.: The ATB system can still function without any of them) and are too massive(and too advanced for some of them) to be covered here. After all, that plugin's written by Mellye but not me, so it's better to ask him/her if you can't thoroughly comprehend these nonessential ATB system features yourselves :p


    Summary

    Simulating a Battle Frame Update execution, which needs to grasp the implementations of the Battle Frame Update, ATB Wait Condition, ATB Frame Update, Battler ATB Clock, and Setting up new Action Execution Subject and inputable actors, is an effective and efficient way to quickly get the big picture of an ATB system.


    Ellye's ATB(also includes CTB option)




    1. When the Battle Frame Update runs, the ATB Wait Conditions, which will always be met when the battle phase is Inputting Actions, will determine whether inputting actions or the ATB Frame Update will take place.


    2. If there are inputable actors, players can either trigger a party escape attempt or input actions for the currently selected actor.


    3. If players trigger a party escape attempt, the battle will be aborted if it succeeded, or the Battler ATB Clock of the currently selected actor will be reset.


    4. If players input actions for the currently selected actor, the process will continue until all actions for that actor are inputted. Then that actor will either starts to cast the 1st action if it has casting time, or will immediately execute that action otherwise.


    5. If the ATB Frame Update's to be run instead, it'll repeatedly update the Battle Turn Clock followed by each battler's state durations and Battler ATB Clock until the loop instant atb stops if the battle's Not Executing Actions, or execute actions if the battle's Executing Actions.


    6. If the battle's Executing Actions, the Action Execution Subject will execute all actions sequentially until they're all executed. Then the battle phase will transit to Not Executing Actions.


    7. When updating the Battle Turn Clock, the battle turn clock and turn end immediately followed by Turn Start will be triggered when the corresponding configurations are met and it reaches its maximum unit, which is the same as the Maximum ATB Value.


    8. When updating a battler's state durations, all state with duration reaching 0 will be removed.


    9. When updating a battler's Battler ATB Clock, it'll be reset if that battler's dead or finished casting an action.


    10. If a battler becomes finished casting an action, the battle will transit to Executing Actions, although this effect will start to take place in the next ATB Frame Update.


    11. If a battler becomes Actable, that battler will either become inputable as well or will immediately cast or execute actions. If a battler executes actions now, the battle will transit to Executing Actions, although this effect will start to take place in the next ATB Frame Update.


    12. If a battler becomes inputable, the battle will transits to Inputting Actions.







    I've planned to talk about YEP.24 - Battle System - Active Time Battle later, but I'm still waiting for Yanfly's approval for this. Although it seems to me that its terms of use lets me do this, I still want to make sure I won't be against his/her wills ;)
     
    Last edited by a moderator: Feb 10, 2016
    #9
  10. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,462
    Likes Received:
    542
    First Language:
    Chinese
    Primarily Uses:
    N/A
    As Victor Sant has just released his VE - Active Time Battle and I've got his/her permission on using it to demonstrate how to study ATB system plugins, I'd like to do just that here.


    You're assumed to have a basic knowledge on How the ATB system plugin works on the user level. To play safe, you may also want to have a basic knowledge on how the VE - Basic Module implementation works.


    While thoroughly comprehending this plugin probably needs decent battle related RMMV plugin development proficiency(experienced plugin developers having written dozens of decent battle related plugins having decent code qualities) I'll still try to briefly talk about the key parts of that plugin to help you have a basic knowledge on how its implementation works.


    Note that this reply's based on the v1.00 version.


    VE - Active Time Battle

    Battle Frame Update



    Spoiler



    Scene_Battle.prototype.updateBattleProcess is extended to be this:



    Spoiler






    VictorEngine.ActiveTimeBattle.updateBattleProcess = Scene_Battle.prototype.updateBattleProcess;
    Scene_Battle.prototype.updateBattleProcess = function() {
    VictorEngine.ActiveTimeBattle.updateBattleProcess.call(this);
    var input = VictorEngine.ActiveTimeBattle.isAnyInputWindowActive.call(this);
    if (input && BattleManager.closeCommandWindows()) {
    this.closeInputWindows();
    }
    };

    Note that BattleManager.update is still used to run the Battle Frame Update:

    VictorEngine.ActiveTimeBattle.updateBattleManager = BattleManager.update;
    BattleManager.update = function() {
    VictorEngine.ActiveTimeBattle.updateBattleManager.call(this);
    this.updateTurnCount();
    this.updateAtb();
    };

    Note that the default BattleManager.update is used to run the original Battle Frame Update.




    ATB Wait Condition

    First, recall the ATB Wait Condition configuration documentations:



    Spoiler






    * - ATB Update Mode
    * There ate 4 modes to define when the ATB bar will be updated:
    * full wait : atb time stop to select commands and execute actions
    * semi wait : atb time stop to select commands
    * semi active : atb time stop to execute actions
    * full active : atb time don't stop
    *
    * The atb time always stops during events.



    Back to Scene_Battle.prototype.updateBattleProcess, while I've said that any ATB system plugin using BattleManager.update to run the Battle Frame Update/ATB Frame Update without changing Scene_Battle.prototype.isAnyInputWindowActive or implementing even more radical changes will mean the ATB Wait Condition is hardcoded as always being met whenever there are inputable actors, this time Scene_Battle.prototype.isAnyInputWindowActive is indeed extended to be this:

    VictorEngine.ActiveTimeBattle.isAnyInputWindowActive = Scene_Battle.prototype.isAnyInputWindowActive;
    Scene_Battle.prototype.isAnyInputWindowActive = function() {
    return !$gameSystem.isAtbCommandActive() && VictorEngine.ActiveTimeBattle.isAnyInputWindowActive.call(this);
    };

    It means that the ATB Wait Condition won't be met whenever Game_System.prototype.isAtbCommandActive returns true:

    Game_System.prototype.isAtbCommandActive = function() {
    return this.isAtbFullActive() || this.isAtbSemiActive();
    };

    Game_System.prototype.isAtbFullActive and Game_System.prototype.isAtbSemiActive corresponds to full active and semi active ATB update modes respectively:

    Game_System.prototype.isAtbSemiActive = function() {
    return this.atbMode() === 'semi active';
    };

    Game_System.prototype.isAtbFullActive = function() {
    return this.atbMode() === 'full active';
    };

    The above implementations show why the ATB Frame Update will continue to run even when players can input commands in the aforementioned ATB update modes.


    Note that all the above haven't covered all ATB Wait Condition implementations, as they're just about those involving inputting commands by players. Those involving executing actions by the Action Execution Subject haven't covered yet.


    BattleManager.canUpdateAtb just covers the rest:

    BattleManager.canUpdateAtb = function() {
    return !this._isUpdateEvent && !$gameMessage.isBusy() && ($gameSystem.isAtbActionActive() || !this._subject) &&
    ($gameSystem.isAtbCommandActive() || !this.actor());
    };

    Note that BattleManager._isUpdateEvent is the cached boolean return value of BattleManager.updateEvent:

    VictorEngine.ActiveTimeBattle.updateEvent = BattleManager.updateEvent;
    BattleManager.updateEvent = function() {
    this._isUpdateEvent = VictorEngine.ActiveTimeBattle.updateEvent.call(this);
    return this._isUpdateEvent;
    };

    It's because BattleManager.updateEvent is meant to be run exactly once per Battle Frame Update and it's not referentially transparent(too advanced to be explained here).


    As mentioned in ealier replies, the ATB Wait Condition must always stop whenever events are executing or messages are showing, or the game will probably crash rather quickly.


    While BattleManager._subject is the Action Execution Subject and BattleManager.actor returns the currently inputting actor(i.e., the players are inputting commands for that actor), Game_System.isAtbActionActive is this(note that Game_System.isAtbCommandActive is already covered):

    Game_System.prototype.isAtbActionActive = function() {
    return this.isAtbFullActive() || this.isAtbSemiWait();
    };

    Game_System.prototype.isAtbFullActive and Game_System.prototype.isAtbSemiActive(which is already covered) corresponds to full active and semi wait ATB update modes respectively:

    Game_System.prototype.isAtbSemiWait = function() {
    return this.atbMode() === 'semi wait';
    };



    Now all that's left is Game_System.prototype.isAtbFullWait which corresponds to full wait ATB update mode:

    Game_System.prototype.isAtbFullWait = function() {
    return this.atbMode() === 'full wait';
    };

    Note that it's not used by the rest of the plugin at all, meaning that it sole purpose's to let users to check whether the ATB update mode's full wait.




    ATB Frame Update

    As indicated by BattleManager.update, BattleManager.updateAtb runs the ATB Frame Update:



    Spoiler






    BattleManager.updateAtb = function() {
    if (this.canUpdateAtb()) {
    this.updateAtbInput();
    var members = this.allBattleMembers();
    for (var i = 0; i < members.length; i++) {
    members.updateAtb();
    }
    }
    };




    As mentioned earlier, BattleManager.canUpdateAtb implements the ATB Wait Condition.


    BattleManager.updateAtbInput inputs actions for battlers becoming inputable(and setups command windows for actors) if there are any:

    BattleManager.updateAtbInput = function() {
    var members = this.allBattleMembers();
    for (var i = 0; i < members.length; i++) {
    var member = members;
    if (this.isInputReady(member)) {
    this._inputSubject = member;
    this.startInput();
    } else if (this.isAtbFrozen(member)) {
    member.clearExpiredStates();
    member.onAllActionsEnd();
    this.refreshStatus();
    this.displayAtbStateMessages(member);
    }
    }
    };




    Note that:


    1. BattleManager._inputSubject marks the currently inputable battler.


    2. If there are more than 1 actors becoming inputable at the exact same frame, the actor with the smallest party member index will be setup for input, as shown in the rewritten BattleManager.startInput:

    BattleManager.startInput = function() {
    this.startAtbInput();
    };

    Because BattleManager.startAtbInput is this:

    BattleManager.startAtbInput = function() {
    if (this._inputSubject) {
    this.startActionInput();
    this._inputSubject = null;
    } else {
    this._phase = 'turn';
    }
    };

    Whereas BattleManager.startActionInput is this:

    BattleManager.startActionInput = function() {
    var subject = this._inputSubject;
    if (subject.isActor() && subject.canInput()) {
    if (!this.actor() && !this.partyCommandIsOpen()) {
    subject.clearExpiredStates();
    subject.makeActions();
    this.refreshStatus();
    this.displayAtbStateMessages(subject);
    var index = $gameParty.members().indexOf(subject);
    if (index >= 0) {
    AudioManager.playSe(VictorEngine.ActiveTimeBattle.ATBReadySound);
    this.changeActor(index, 'inputting');
    }
    }
    } else {
    subject.clearExpiredStates();
    subject.makeActions();
    this.setupAction(subject);
    this.refreshStatus();
    this.displayAtbStateMessages(subject);
    }

    };

    Note that calling BattleManager.changeActor with the party member index index will cause BattleManager.actor to return the actor with the party member index index.


    In short, the ATB Frame Update always ask battlers just becoming inputable to input actions, which is done by players via setting up command windows for non autobattle/confused actors, and automatically for the rest.




    Setting Up Inputable Actors

    BattleManager.isInputting is rewritten to be this:



    Spoiler






    BattleManager.isInputting = function() {
    return this.actor() && this.actor().canInput() || this.partyCommandIsOpen();
    };

    Recall that it's used in Scene_Battle.prototype.changeInputWindow.


    If BattleManager.isInputting returns true, then the actor command window and the party command window will be setup if BattleManager.actor returns an actor or nothing respectively.


    Note that Scene_Battle.prototype.changeInputWindow is extended to also call the original Scene_Battle.prototype.isAnyInputWindowActive as an extra check:

    VictorEngine.ActiveTimeBattle.changeInputWindow = Scene_Battle.prototype.changeInputWindow;
    Scene_Battle.prototype.changeInputWindow = function() {
    if (!VictorEngine.ActiveTimeBattle.isAnyInputWindowActive.call(this)) {
    VictorEngine.ActiveTimeBattle.changeInputWindow.call(this);
    }
    };

    Also, BattleManager.partyCommandIsOpen is this:

    BattleManager.partyCommandIsOpen = function() {
    return this._partyCommandIsOpen;
    }

    To sum up:


    1. When BattleManager.actor returns an actor, BattleManager.isInputting will return true, causing Scene_Battle.prototype.changeInputWindow to setup the actor command window for that actor.


    2. The above only applies when no input windows are already setup due to the extra Scene_Battle.prototype.isAnyInputWindowActive, otherwise the actor command window of that actor will be repeatedly setup per Battle Frame Update.




    Battle Turn Clock

    First, recall the Battle Turn Clock configuration documentations:



    Spoiler






    * - Turn Update Mode
    * The plugin parameter 'Turn Update Mode' will decide how battle turns will
    * be counted.
    * If set to 'actions' a turn will be counted after a set number of battlers
    * have take their actions, that number of battlers is decided on the plugin
    * parameter 'Turn Update Count' (this parameter allows script codes)
    * If set to 'time' a turn will be counted after a set time has passed. This
    * time is based on the average battlers atb speed and the value on the plugin
    * parameter 'Turn Update Time'



    As indicated by BattleManager.update, BattleManager.updateTurnCount runs the Battle Turn Clock:

    BattleManager.updateTurnCount = function() {
    if (this.canUpdateAtb()) {
    if (this._turnCount <= 0 && !this._subject) {
    this._turnCount = this.turnCountValue();
    $gameTroop.increaseTurn();
    this.endTurn();
    this._phase = 'turn';
    }
    if (this.isTimeTurnCount()) {
    this.turnCountUpdateValue();
    }
    }
    };

    Note that:


    1. Here the Battle Turn Clock isn't run by the ATB Frame Update, but Battle Frame Update directly, albeit with the ATB Wait Condition check, meaning that the ATB Wait Condition's checked at least twice(actually thrice for parts covered by Game_System.prototype.isAtbCommandActive used in Scene_Battle.prototype.isAnyInputWindowActive as well) instead of just once per Battle Frame Update.


    2. The Battle Turn Clock will always stop when there's the Action Execution Subject, regardless of what's the ATB Wait Conditions are.


    BattleManager.turnCountValue sets the maximum value of the Battle Turn Clock:

    BattleManager.turnCountValue = function() {
    var parameters = VictorEngine.Parameters.ActiveTimeBattle;
    if (this.isTimeTurnCount()) {
    return this.averageMaxAtb() * 100 / parameters.TurnUpdateTime;
    } else {
    return Number(eval(parameters.TurnUpdateCount)) || 8;
    }
    };

    Where BattleManager.averageMaxAtb is this:

    BattleManager.averageMaxAtb = function() {
    var members = this.allBattleMembers();
    var speed = 0
    for (var i = 0; i < members.length; i++) {
    speed += members.actionAtb();
    }
    return (speed / members.length) || 200;
    }




    BattleManager.isTimeTurnCount checks if the Battle Turn Clock's based on frames or number of executed actions in the same turn:

    BattleManager.isTimeTurnCount = function() {
    return this.turnCountMode() === 'time';
    };

    For the former case, BattleManager.turnCountUpdateValue will be called:

    BattleManager.turnCountUpdateValue = function() {
    this._turnCount -= this.isTimeTurnCount() ? this.atbUpdateRate() : 1;
    };

    It's to update the Battle Turn Clock per ATB Frame Update in this case.


    For the latter case, the Battle Turn Clock will be updated whenever Game_Battler.prototype.updateAtbTurnCount is called:

    Game_Battler.prototype.updateAtbTurnCount = function() {
    if (!BattleManager.isTimeTurnCount()) {
    BattleManager.turnCountUpdateValue();
    }
    };



    It's called by the rewritten Game_Battler.prototype.onAtbAllActionsEnd, which is called right after a battler has executed all inputted actions:

    Game_Battler.prototype.onAtbAllActionsEnd = function() {
    this.setActionState('undecided');
    this.updateAtbTurnCount();
    this.updateStateTurns();
    this.regenerateAll();
    this.clearAtb();
    };




    Battler ATB Clock

    As shown in BattleManager.updateAtb, Game_Battler.prototype.updateAtb runs the Battler ATB Clock:



    Spoiler






    Game_Battler.prototype.updateAtb = function() {
    this.refreshAtb();
    this.updateTimedStates();
    if (this.canMove()) {
    this.atb = this.atb - BattleManager.atbUpdateRate();
    this._isAtbFrozen = false;
    } else if (this.isRestricted() && this.isCasting()) {
    this.clearCasting();
    this.clearAtb();
    } else {
    if (!this.isAtbFrozen()) {
    this.ftb = this.atb
    this._isAtbFrozen = true
    }
    this.ftb = this.ftb - BattleManager.atbUpdateRate();
    }
    };



    Game_Battler.prototype.refreshAtb updates the Maximum ATB Value while keeping the ATB fill ratio's intact:

    Game_Battler.prototype.refreshAtb = function() {
    if (this.maxAtb !== this.actionAtb()) {
    this.atb = this.atb * this.maxAtb / this.actionAtb();
    this.maxAtb = this.actionAtb();
    }
    };

    Game_Battler.prototype.actionAtb returns the new Maximum ATB Value for both Refilling ATB and casting action phases:

    Game_Battler.prototype.actionAtb = function() {
    var speed = this.isCasting() ? this.castAtb() : this.baseAtb();
    return speed * this.atbSpeed() * this.castSpeed();
    };

    It's because Game_Battler.prototype.castATB and Game_Battler.prototype.baseAtb are these:

    Game_Battler.prototype.baseAtb = function() {
    var speed = Math.max(VictorEngine.Parameters.ActiveTimeBattle.ATBParamWeight, 1);
    return this.atbValue(speed, 6);
    };

    Game_Battler.prototype.castAtb = function() {
    var speed = Math.max(VictorEngine.Parameters.ActiveTimeBattle.ATBParamWeight, 1);
    return this.atbValue(speed, this._castParam - 1) / this._castSpeed;
    };

    Whereas Game_BattlerBase.prototype.atbSpeed and Game_BattlerBase.prototype.castSpeed are these:

    Game_BattlerBase.prototype.atbSpeed = function() {
    var result = this.traitObjects().reduce(function(r, obj) {
    return r * (Math.max(1 - obj.atbSpeed, 0.1) || 1);
    }, 1);
    return Math.max(result, 0.1);
    };

    Game_BattlerBase.prototype.castSpeed = function() {
    var result = this.traitObjects().reduce(function(r, obj) {
    return r * (Math.max(1 - obj.castSpeed, 0.1) || 1);
    }, 1);
    return this.isCasting() ? Math.max(result, 0.1) : 1;
    };



    Game_Battler.prototype.updateTimedStates updates states with their remaining turns based on the number of ATB Frame Updates passed:

    Game_Battler.prototype.updateTimedStates = function() {
    var expired = false;
    this.states().forEach(function(state) {
    if (this._stateDurations[state.id] && this._stateDurations[state.id] > 0 && state.atbTiming) {
    this._stateDurations[state.id] -= BattleManager.atbUpdateRate();
    }
    if (this._stateDurations[state.id] && this._stateDurations[state.id] < 0 && state.atbTiming) {
    this._stateDurations[state.id] = BattleManager.averageMaxAtb() * state.atbTiming;
    if (this._stateTurns[state.id] > 0) {
    this._stateTurns[state.id]--;
    }
    }
    if (!BattleManager.isActiveSubject() && this.isStateExpired(state.id) && state.atbTiming) {
    this.removeState(state.id);
    expired = true;
    }
    }, this);
    if (expired) {
    BattleManager.displayAtbStateMessages(this);
    }
    };

    It means that each remaining turn consists of Game_Battler.prototype._stateDurations ATB Frame Updates.


    For movable battlers, their Battler ATB Clock will be refilled(note that it'll also be marked as runnable whenever it can run by setting Game_Battler.prototype._isAtbFrozen as true), and BattleManager.atbUpdateRate returns the ATB gain rate for all battlers:

    BattleManager.atbUpdateRate = function() {
    return (this._atbUpdateRate || 1) * this.atbFastForward();
    }

    Where BattleManager.atbFastForward is this:

    BattleManager.atbFastForward = function() {
    var fast = VictorEngine.Parameters.ActiveTimeBattle.ATBFastForward;
    return (fast && !this._subject && !this.actor()) ? 2 : 1;
    }

    Note that this._atbUpdateRate is set in BattleManager.setupAtbUpdateRate which is called by BattleManager.setup:

    BattleManager.setupAtbUpdateRate = function() {
    var base = VictorEngine.Parameters.ActiveTimeBattle.ATBBaseWait;
    var speed = $gameSystem.atbSpeed() - 1;
    var maxSpeed = this.averageMaxAtb();
    var atbSpeed = Math.max(base - base * speed / 10, 1);
    this._atbUpdateRate = Math.max(maxSpeed / atbSpeed, 0);
    }

    Code:
    
    
    
        VictorEngine.ActiveTimeBattle.setup = BattleManager.setup;
        BattleManager.setup = function(troopId, canEscape, canLose) {
            VictorEngine.ActiveTimeBattle.setup.call(this, troopId, canEscape, canLose);
            this.setupAtbUpdateRate();
            this._turnCount = 0;
        };

    As Game_Battler.prototype.isCasting returns whether the battler's casting actions:

    Game_Battler.prototype.isCasting = function() {
    return !!this._castSpeed;
    };

    Game_Battler.prototype.updateAtb clears both the casting actions and resets the Battler ATB Clock when a battler casting actions becomes restricted:

    Game_Battler.prototype.clearCasting = function() {
    this._castSpeed = 0;
    this._castParam = 0;
    this._castingActions = [];
    };

    Code:
    
    
    
        Game_Battler.prototype.clearAtb = function() {
            if (this.isCasting() && !this._atbCast) {
                this._atbCast = true;
            } else if (!this.isCasting() && this._atbCast) {
                this._atbCast = false;
                this._castColor = null;
            }
            this.maxAtb = this.actionAtb();
            if (this.isAtbFrozen()) {
                this.ftb = this.maxAtb;
            } else {
                this.atb = this.maxAtb;
            }
        };

    Note that Game_Battler.prototype.isAtbFrozen returns whether a battler's Battler ATB Clock's stopped:

    Game_Battler.prototype.isAtbFrozen = function() {
    return this._isAtbFrozen;
    };



    Finally, for battlers having the Battler ATB Clock stopped, Game_Battler.prototype.ftb will be reset as the current ATB value if the Battler ATB Clock becomes stopped. Regardless whether that's the case, Game_Battler.prototype.ftb will still be updated per ATB Frame Update here.


    Note that it's used in BattleManager.isAtbFrozen:

    BattleManager.isAtbFrozen = function(member) {
    return member.isAtbFrozen() && member.ftb === 0;
    };

    It checks whether a battler's Battler ATB Clock was stopped and the stopping period has just expired.


    It's used in BattleManager.updateAtbInput, which is called per ATB Frame Update, to clear expired states and end all actions of the battler.




    Setting Up Action Execution Subject

    BattleManager.updateTurn is rewritten into this:



    Spoiler






    BattleManager.updateTurn = function() {
    this.updateAtbTurn();
    };

    BattleManager.updateAtbTurn is to setup the new Action Execution Subject if there are such eligible battlers:

    BattleManager.updateAtbTurn = function() {
    if (!this._subject) {
    this._subject = this.getNextSubject();
    }
    if (this._subject) {
    this.processTurn();
    }
    };

    It's because the original BattleManager.processTurn is to execute actions for the current Action Execution Subject.


    Such eligible battlers are found in the extended BattleManager.getNextSubject:

    VictorEngine.ActiveTimeBattle.getNextSubject = BattleManager.getNextSubject;
    BattleManager.getNextSubject = function() {
    if (this._turnCount <= 0) {
    return null
    } else {
    return VictorEngine.ActiveTimeBattle.getNextSubject.call(this);
    }
    };

    The BattleManager._turnCount check here is to prevent setting up the Action Execution Subject before the battle's ready to start(too advanced to be explained here).


    Recall that the original BattleManager.getNextSubject gets the 1st battler in the queue BattleManager._actionBattlers and removes that battler from that queue, which is setup at the start of the Action Execution Phase in the default RMMV battle system.


    As mentioned in earlier replies, BattleManager._actionBattlers can't be setup that way, meaning that this ATB system must be using new codes to set it up.


    As such(along with some other reasons already mentioned in earlier replies), BattleManager.startTurn, which calls BattleManager.makeActionOrders in the default RMMV battle system, is completely disabled here:

    BattleManager.startTurn = function() {};

    On the other hand, BattleManager.setupAction pushs a battler into BattleManager._actionBattlers:

    BattleManager.setupAction = function(battler) {
    battler.setupCastingAction();
    this._actionBattlers.push(battler);
    };

    It's called by BattleManager.startActionInput(which is already covered and called per ATB Frame Update) and BattleManager.selectNextAtbCommand:

    BattleManager.selectNextAtbCommand = function() {
    if (this.actor()) {
    this.setupAction(this.actor());
    }
    this.clearActor();
    };

    Note that:


    1. Game_BattlerBase.prototype.canInput, which is called by BattleManager.startActionInput, is extended to return false for battlers casting actions:

    VictorEngine.ActiveTimeBattle.canInput = Game_BattlerBase.prototype.canInput;
    Game_BattlerBase.prototype.canInput = function() {
    return !this.isCasting() && VictorEngine.ActiveTimeBattle.canInput.call(this);
    };

    2. BattleManager.selectNextAtbCommand is itself called by the rewritten BattleManager.selectNextCommand:

    BattleManager.selectNextCommand = function() {
    this.selectNextAtbCommand();
    };

    It means that Game_Battler.prototype.setupCastingAction is to either begin to cast all the inputted actions or mark all those casting actions as ready to execute;

    Game_Battler.prototype.setupCastingAction = function() {
    if (this.isCasting()) {
    this.executeCastAction();
    } else {
    this.prepareCastAction();
    }
    };

    Because Game_Battler.prototype.executeCastAction and Game_Battler.prototype.prepareCaseAction are these:

    Game_Battler.prototype.executeCastAction = function() {
    this._actions = this._castingActions.clone();
    this.clearCasting();
    };

    Game_Battler.prototype.prepareCastAction = function() {
    this.clearCasting();
    this._castColor = null;
    for (var i = 0; i < this._actions.length; i++) {
    var action = this._actions;
    var speed = action.castingSpeed();
    var param = action.castingParam();
    if (this._castSpeed < speed || this.higherCastParam(speed, param)) {
    this._castSpeed = speed;
    this._castParam = param;
    this._castColor = action.castingColor();
    }
    }
    if (this.isCasting()) {
    this._castingActions = this._actions.clone();
    this.clearActions();
    }
    };




    Note that the final action casting speed is the result of those of all casting actions, meaning that those actions won't be casted, executed, and then casted, ... until the last ones are executed. Instead, all actions will be casted in 1 shot, and then executed 1 by 1 immediately, without any more casting in between during action executions.


    Recall that BattleManager.processTurn will simply call Game_Battler.prototype.onAllActionsEnd for the Action Execution Subject and finds the new one via BattleManager.getNextSubject, when the current one has no actions to execute.


    So battlers having inputted actions with none of them needing to be casted can become the Action Execution Subject at the next Battle Frame Update.


    For the other case(needing to cast action), Game_Battler.prototype.onAllActionsEnd is extended to be this:

    VictorEngine.ActiveTimeBattle.onAllActionsEnd = Game_Battler.prototype.onAllActionsEnd;
    Game_Battler.prototype.onAllActionsEnd = function() {
    VictorEngine.ActiveTimeBattle.onAllActionsEnd.call(this);
    this.onAtbAllActionsEnd();
    };

    Where Game_Battler.prototype.onAtbAllActionsEnd is this:

    Game_Battler.prototype.onAtbAllActionsEnd = function() {
    this.setActionState('undecided');
    this.updateAtbTurnCount();
    this.updateStateTurns();
    this.regenerateAll();
    this.clearAtb();
    };

    Where Game_Battler.prototype.clearAtb is this:

    Game_Battler.prototype.clearAtb = function() {
    if (this.isCasting() && !this._atbCast) {
    this._atbCast = true;
    } else if (!this.isCasting() && this._atbCast) {
    this._atbCast = false;
    this._castColor = null;
    }
    this.maxAtb = this.actionAtb();
    if (this.isAtbFrozen()) {
    this.ftb = this.maxAtb;
    } else {
    this.atb = this.maxAtb;
    }
    };

    Note that:


    1. If a battler becomes casting actions, Game_Battler.prototype._atbCast will be set as true.


    2. If a battler becomes not casting actions, Game_Battler.prototype._atbCast will be set as false.


    It means the sole purpose of Game_Battler.prototype._atbCast is to detect changes from casting actions to not casting actions and vice versa.


    BattleManager.updateAtbInput will setup battlers having BattleManager.isInputReady returning true:

    BattleManager.isInputReady = function(member) {
    return member !== this._subject && member !== this.actor() && this.isAtbReady(member);
    };

    So it'll return true if the battler isn't the Action Exeuction Subject, the currently inputting actor, and has full Battler ATB Clock, due to BattleManager.isAtbReady:

    BattleManager.isAtbReady = function(member) {
    return member.isAtbReady() && !this._actionBattlers.contains(member);
    };

    As Game_Battler.prototype.isAtbReady is this:

    Game_Battler.prototype.isAtbReady = function() {
    return this.isAppeared() && this.canMove() && this.atb === 0;
    };

    Combining all the above:


    1. Right after a battler has finished inputting actions, Game_Battler.prototype.prepareCastAction will be called, and Game_Battler.prototype._actions will be cloned and then cleared for actions needing casting.


    2. That battler will then be pushed at the bottom of BattleManager._actionBattlers.


    3. BattleManager.processTurn will setup that battler as the Action Execution Subject via BattleManager.getNextSubject at the next Battle Frame Update.


    4. If that battler doesn't have actions needing casting, all the actions will be executed, due to Game_Battler.prototype._actions not being empty.


    5. Otherwise that battler will become not the Action Execution Subject immediately, and Game_Battler.prototype.onAllActionsEnd, which calls Game_Battler.prototype.onAtbAllActionsEnd and eventually Game_Battler.prototype.clearAtb, will be called.


    6. The Battler ATB Clock will be set to the Maximum ATB Value(empty), which is adjusted by the casting actions via Game_Battler.prototype.actionAtb.


    7. When the Battler ATB Clock becomes full again, BattleManager.isInputReady, which is called by BattleManager.updateAtbInput, will return true due to Game_Battler.prototype.atb returning 0 and the battler already removed from BattleManager._actionBattlers back when BattleManager.processTurn was called.


    8. BattleManager.startInput, which calls BattleManager.startAtbInput and eventually BattleManager.startActionInput, will lead to BattleManager.setupAction being called again.


    9. The battler will then be pushed into BattleManager._actionBattlers again, and Game_Battler.prototype.setupCastingAction will be called again as well.


    10. This time Game_Battler.prototype.executeCastAction will be called instead of calling Game_Battler.prototype.prepareCastAction due to Game_Battler.prototype.isCasting returning true, causing Game_Battler.prototype._actions to turn back to the originally inputted action list(and the currently casting ones), meaning that that battler will become the real Action Execution Subject that can actually execute actions when BattleManager.processTurn will be called after at least 2 Battle Frame Updates(due to 1 of them needed to change the phase to the Action Execution Phase by calling BattleManager.startAtbInput).




    Starting ATB Value

    Game_Battler.prototype.onBattleStart is extended to setup the Starting ATB Value for each battler:



    Spoiler






    VictorEngine.ActiveTimeBattle.onBattleStart = Game_Battler.prototype.onBattleStart;
    Game_Battler.prototype.onBattleStart = function() {
    VictorEngine.ActiveTimeBattle.onBattleStart.call(this);
    this._isAtbFrozen = false;
    this.clearCasting();
    this.clearAtb();
    this.startingAtb();
    };

    Note that it initializes the action casting variables and the Battler ATB Clock as well.


    Setting up the Starting ATB Value is done by calling Game_Battler.prototype.startingAtb:

    Game_Battler.prototype.startingAtb = function() {
    this._atbActionSpeed = 0;
    var battleAdvantage = Imported['VE - Battle Advantage'];
    if (battleAdvantage && this.isBackAttack()) {
    this.atb = this.maxAtb;
    } else if (battleAdvantage && this.isSurrounded()) {
    this.atb = this.maxAtb;
    } else if (battleAdvantage && this.isSneakAttack()) {
    this.atb = 0;
    } else if (battleAdvantage && this.isPincerAttack()) {
    this.atb = 0;
    } else if (!battleAdvantage && BattleManager.isSurprise()) {
    this.atb = this.isActor() ? this.maxAtb : 0;
    } else if (!battleAdvantage && BattleManager.isPreemptive()) {
    this.atb = this.isActor() ? 0 : this.maxAtb;
    } else {
    var rate = VictorEngine.Parameters.ActiveTimeBattle.InitialATBRate * Math.random();
    this.atb -= this.maxAtb * rate / 100;
    }
    };




    Inputting Actions For Actors

    BattleManager.selectPreviousCommand is rewritten to be this:



    Spoiler






    BattleManager.selectPreviousCommand = function() {
    this.selectPreviousAtbCommand();
    };

    Where BattleManager.selectPreviousAtbCommand is this:

    BattleManager.selectPreviousAtbCommand = function() {
    if (!this.actor().selectPreviousCommand()) {
    if (this.atbShowPartyCommand()) {
    this._partyCommandIsOpen = true;
    } else if (this.actor() && this.atbReadyActors().length > 1) {
    this.actor().atb = 1;
    this.clearActor();
    this.updateAtbInput();
    }
    }
    };

    Note that:


    1. BattleManager.atbShowPartyCommand is this:

    BattleManager.atbShowPartyCommand = function() {
    return VictorEngine.Parameters.ActiveTimeBattle.ShowPartyCommand;
    };

    2. BattleManager._partyCommandIsOpen is set as true to setup the party command window at the next frame via Scene_Battle.prototype.changeInputWindow due to BattleManager.isInputting returning true.


    3. BattleManager.atbReadyActors is this:

    BattleManager.atbReadyActors = function() {
    return $gameParty.aliveMembers().filter(function(member) {
    return member.isAtbReady() && member.canInput();
    })
    };

    4. BattleManager.updateAtbInput is called here to setup the actor command window for another inputable actor at the exact same Battle Frame Update.


    Scene_Battle.prototype.commandFight is rewritten to close the party command window, and setup an actor command window for the currently inputable actor, if there's any:

    Scene_Battle.prototype.commandFight = function() {
    BattleManager.closePartyCommand();
    this.changeInputWindow();
    };



    Recall that BattleManager.selectNextAtbCommand will also call BattleManager.clearActor.


    It means that Action Times + is completely ignored by inputable actors in this ATB System plugin. It's because:


    1. When BattleManager.clear is called, so does the actor command window of the currently inputting actor.


    2. The entire process mentioned in Setting Up Action Execution Subject will be immediately triggered.


    3. During that process, that actor won't be inputable, due to that actor being in the BattleManager._actionBattlers at the next Battle Frame Update and that actor having nonzero Game_Battler.prototype.atb value for subsequent ATB Frame Updates..


    4.When that actor has zero Game_Battler.prototype.atb value, that actor will still be not inputable, due to Game_Battler.prototype.isCasting returning true.


    5. The entire process will normally end right after that actor has executed all inputted actions.


    Note that Action Times + still works on enemies and actors with autobattle/confusion.




    Actor Escapes

    BattleManager.startAction is extended to handle the initiation of individual actor escapes:



    Spoiler






    VictorEngine.ActiveTimeBattle.startActionBattleManager = BattleManager.startAction;
    BattleManager.startAction = function() {
    var subject = this._subject;
    var action = subject.currentAction();
    if (action.isEscape()) {
    this._phase = 'action';
    this._action = action;
    this._targets = [];
    this.refreshStatus();
    this._logWindow.startEscape(subject, action);
    } else {
    VictorEngine.ActiveTimeBattle.startActionBattleManager.call(this);
    }
    };

    Game_Action.prototype.isValid is extended to make individual escape actions always valid:

    VictorEngine.ActiveTimeBattle.isValid = Game_Action.prototype.isValid;
    Game_Action.prototype.isValid = function() {
    return this.isEscape() || VictorEngine.ActiveTimeBattle.isValid.call(this);
    };

    Game_Action.prototype.item is extended to return the escape action item for individual escape actions:

    VictorEngine.ActiveTimeBattle.item = Game_Action.prototype.item;
    Game_Action.prototype.item = function() {
    if (this.isEscape()) {
    return this._escapeAction;
    } else {
    return VictorEngine.ActiveTimeBattle.item.call(this);
    }
    };

    Window_BattleLog.prototype.endAction is extended to perform individual escape action pose/sequence:

    VictorEngine.ActiveTimeBattle.endAction = Window_BattleLog.prototype.endAction;
    Window_BattleLog.prototype.endAction = function(subject) {
    VictorEngine.ActiveTimeBattle.endAction.call(this, subject);
    if (subject.isEscapeCommand()) {
    this.push('performEscape', subject);
    subject.endEscapeCommand();
    }
    }



    On the other hand, BattleManager.displayEscapeFailureMessage is extended to handle failed party escape attempts:

    VictorEngine.ActiveTimeBattle.displayEscapeFailureMessage = BattleManager.displayEscapeFailureMessage;
    BattleManager.displayEscapeFailureMessage = function() {
    VictorEngine.ActiveTimeBattle.displayEscapeFailureMessage.call(this);
    this.clearAtbActions();
    };

    By calling BattleManager.clearAtbActions as well:

    BattleManager.clearAtbActions = function() {
    var members = $gameParty.members();
    for (var i = 0; i < members.length; i++) {
    var member = members;
    member.clearAtb();
    member.clearActions();
    member.clearCasting();
    var index = this._actionBattlers.indexOf(member);
    if (index >= 0) {
    this._actionBattlers.splice(index, 1);
    }
    }
    };






    Finally, by checking the extended Scene_Battle.prototype.commandEscape:

    Scene_Battle.prototype.commandEscape = function() {
    BattleManager.closePartyCommand();
    BattleManager.inputtingAction().setEscape();
    this.selectNextCommand();
    };

    Party command escape attempts are implemented as an individual escape action with its subject being the one triggering the party escape attempt.




    Status Window ATB Bars

    Window_BattleStatus.prototype.drawItem is extended to be this:



    Spoiler






    VictorEngine.ActiveTimeBattle.drawItem = Window_BattleStatus.prototype.drawItem;
    Window_BattleStatus.prototype.drawItem = function(index) {
    VictorEngine.ActiveTimeBattle.drawItem.call(this, index);
    var actor = $gameParty.battleMembers()[index];
    var rect = this.itemRectForText(index);
    this.drawActorAtb(actor, rect.x, rect.y, index);
    };

    Where Window_BattleStatus.prototype.drawActorAtb is this:

    Window_BattleStatus.prototype.drawActorAtb = function(actor, x, y, index) {
    if (VictorEngine.ActiveTimeBattle.gauge.status) {
    this._atbGauges = this._atbGauges || [];
    if (!this._atbGauges[index]) {
    this.createAtbGauge(actor, x, y, index);
    }
    this.redrawAtbGauge(index);
    }
    };

    And Window_BattleStatus.prototype.createAtbGauge is this:

    Window_BattleStatus.prototype.createAtbGauge = function(actor, x, y, index) {
    var position = VictorEngine.ActiveTimeBattle.gauge.statusPosition;
    x += Number(position[0]) || 0;
    y += Number(position[1]) || 0;
    var width = Number(position[2]) || 184;
    var gauge = new Window_AtbGauge(x, y, width, actor);
    this._atbGauges[index] = gauge;
    this._windowSpriteContainer.addChild(gauge);
    };

    Note that Window_AtbGauge.prototype.initialize is this:

    Window_AtbGauge.prototype.initialize = function(x, y, width, battler) {
    var width = width + this.standardPadding() * 2;
    var height = this.lineHeight() + this.standardPadding() * 2;
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    this.battler = battler;
    this.opacity = 0;
    };

    On the other hand, Window_BattleStatus.prototype.update is extended to be this:

    VictorEngine.ActiveTimeBattle.updateWindowBattleStatus = Window_BattleStatus.prototype.update;
    Window_BattleStatus.prototype.update = function() {
    VictorEngine.ActiveTimeBattle.updateWindowBattleStatus.call(this);
    this.updateAtbGauges();
    }

    Where Window_BattleStatus.prototype.updateAtbGauges is this:

    Window_BattleStatus.prototype.updateAtbGauges = function() {
    var gauges = this._atbGauges || [];
    for (var i = 0; i < gauges.length; i++) {
    var gauge = gauges;
    if (gauge) {
    if ($gameParty.members().contains(gauge.battler)) {
    this.redrawAtbGauge(i);
    } else {
    this._windowSpriteContainer.removeChild(gauge);
    this._atbGauges.splice(i, 1);
    i--;
    }
    }
    }
    }




    Window_BattleStatus.prototype.redrawAtbGauge calls Window_AtbGauge.prototype.redrawAtbGauge:

    Window_BattleStatus.prototype.redrawAtbGauge = function(index) {
    var gauge = this._atbGauges[index];
    if (gauge) {
    gauge.redrawAtbGauge();
    }
    }

    Which is this:

    Window_AtbGauge.prototype.redrawAtbGauge = function() {
    var rate = this.battler.atbRate();
    var width = this.contents.width;
    var color = this.atbGaugeColor();
    this.contents.clear();
    this.drawGauge(0, 0, width, rate, color[0] || '#009900', color[1] || '#00FF00');
    this.changeTextColor(this.systemColor());
    this.drawText(VictorEngine.ActiveTimeBattle.gauge.name, 0, 0, width);
    }

    Note that Window_AtbGauge.prototype.atbGaugeColor is this:

    Window_AtbGauge.prototype.atbGaugeColor = function() {
    var gauge = VictorEngine.ActiveTimeBattle.gauge;
    var state = this.battler.stateAtbColor();
    if (state) {
    var color = state.atbColor;
    } else if (this.battler.isAtbCast()) {
    var color = this.battler.castColor() || gauge.cast;
    } else {
    var color = gauge.wait;
    }
    return this.battler.atbFull() ? color.full : color.fill;
    }


    While the status window won't be redrawn per ATB Frame Update, each ATB bars are indeed redrawn per Battle Frame Update.




    Cast Cancel/ATB Delay

    Game_Action.prototype.applyItemUserEffect is extended to apply cast cancel and ATB delay effects as well:



    Spoiler






    VictorEngine.ActiveTimeBattle.applyItemUserEffect = Game_Action.prototype.applyItemUserEffect;
    Game_Action.prototype.applyItemUserEffect = function(target) {
    VictorEngine.ActiveTimeBattle.applyItemUserEffect.call(this);
    this.applyCastCancel(target);
    this.applyAtbDelay(target);
    };

    By calling Game_Action.prototype.applyCastCancel and Game_Action.prototype.applyAtbDelay:

    Game_Action.prototype.applyCastCancel = function(target) {
    var subject = this.subject();
    var objects = [this.item()].concat(subject.traitObjects());
    objects.forEach(function(object) {
    var cancel = object.castCancel;
    if (cancel && !object.noCastCancel && cancel * Math.random() < target.cancelResist()) {
    target.clearCasting();
    target.clearAtb();
    }
    });
    };

    Game_Action.prototype.applyAtbDelay = function(target) {
    var subject = this.subject();
    var objects = [this.item()].concat(subject.traitObjects());
    objects.forEach(function(object) {
    var delay = object.atbDelay;
    if (delay && delay.rate && delay.rate * Math.random() < target.delayResist()) {
    target.atb += target.maxAtb * delay.effect;
    }
    });
    };

    Note that Game_Battler.prototype.cancelResist and Game_Battler.prototype.delayResist are these:

    Game_Battler.prototype.cancelResist = function() {
    return this.traitObjects().reduce(function(r, object) {
    return r * (Math.max(1 - object.cancelResist, 0) || 0);
    }, 1);
    };

    Game_Battler.prototype.delayResist = function() {
    return this.traitObjects().reduce(function(r, object) {
    return r * (Math.max(1 - object.cancelResist, 0) || 0);
    }, 1);
    };




    Regen/Buffs Update Mode

    First, recall the regen/buffs update mode configuration documentations:



    Spoiler






    * - Regen Update Mode and Buffs Update Mode
    * Those plugin parameters decides when regeneration effects and buffs will be
    * updated.
    * If set as 'action', they will be updated after a battler finish an action,
    * only for that battler.
    * If set as 'turn', they will be updated at the turn end (based on the plugin
    * parameter 'Turn Update Mode') for all battlers.
    * States updates are decided on the state itself (see bellow for details)



    BattleManager.displayAtbStateMessages, which is called by BattleManager.startActionInput, BattleManager.updateAtbInput and Game_Battler.prototype.updateTimedStates, is this:

    BattleManager.displayAtbStateMessages = function(battler) {
    this._logWindow.displayAutoAffectedStatus(battler);
    this._logWindow.displayCurrentState(battler);
    this._logWindow.displayRegeneration(battler);
    };

    Game_Battler.prototype.regenerateAll, which is also called by Game_Battler.prototype.onAtbAllActionsEnd, is extended to be this:

    VictorEngine.ActiveTimeBattle.regenerateAll = Game_Battler.prototype.regenerateAll;
    Game_Battler.prototype.regenerateAll = function() {
    if (BattleManager.regenerationUpdate()) {
    VictorEngine.ActiveTimeBattle.regenerateAll.call(this);
    }
    };

    Where BattleManager.regenerationUpdate is this:

    BattleManager.regenerationUpdate = function() {
    return this.regenerationAction() || this.regenerationTurn();
    };

    And BattleManager.regenerationAction and BattleManager.regenerationTurn are these:

    BattleManager.regenerationAction = function() {
    var parameters = VictorEngine.Parameters.ActiveTimeBattle;
    return !this.isTurnEnd() && parameters.RegenUpdateMode.toLowerCase() === 'action';
    };

    BattleManager.regenerationTurn = function() {
    var parameters = VictorEngine.Parameters.ActiveTimeBattle;
    return this.isTurnEnd() && parameters.RegenUpdateMode.toLowerCase() === 'turn';
    };



    Similarly, Game_BattlerBase.prototype.updateBuffTurns is extended to be this:

    VictorEngine.ActiveTimeBattle.updateBuffTurns = Game_BattlerBase.prototype.updateBuffTurns;
    Game_BattlerBase.prototype.updateBuffTurns = function() {
    if (BattleManager.buffsUpdate()) {
    VictorEngine.ActiveTimeBattle.updateBuffTurns.call(this);
    }
    };

    Where BattleManager.buffsUpdate is this:

    BattleManager.buffsUpdate = function() {
    return this.buffsAction() || this.buffsTurn();
    };

    And BattleManager.buffsAction and BattleManager.buffsTurn are these:

    BattleManager.buffsAction = function() {
    var parameters = VictorEngine.Parameters.ActiveTimeBattle;
    return !this.isTurnEnd() && parameters.BuffsUpdateMode.toLowerCase() === 'action';
    };

    BattleManager.buffsTurn = function() {
    var parameters = VictorEngine.Parameters.ActiveTimeBattle;
    return this.isTurnEnd() && parameters.BuffsUpdateMode.toLowerCase() === 'turn';
    };




    Battler Sprite ATB Bars

    Sprite _Actor.prototype.update and Sprite_Enemy.prototype.update are exteneded to draw the battler sprite ATB bars:



    Spoiler






    //=============================================================================
    // Sprite_Actor
    //=============================================================================

    VictorEngine.ActiveTimeBattle.updateSpriteActor = Sprite_Actor.prototype.update;
    Sprite_Actor.prototype.update = function() {
    VictorEngine.ActiveTimeBattle.updateSpriteActor.call(this)
    var width = this._mainSprite ? this._mainSprite._frame.width : this.width;
    if (width && $gameParty.inBattle() && this._actor.isSpriteVisible() &&
    this._atbSpriteName !== this._battlerName) {
    this._atbSpriteName = this._battlerName;
    var show = VictorEngine.ActiveTimeBattle.gauge.actor;
    var position = VictorEngine.ActiveTimeBattle.gauge.actorPosition;
    this.drawSpriteAtb(this._actor, position, width, show);
    }
    };

    //=============================================================================
    // Sprite_Enemy
    //=============================================================================

    VictorEngine.ActiveTimeBattle.updateSpriteEnemy = Sprite_Enemy.prototype.update;
    Sprite_Enemy.prototype.update = function() {
    VictorEngine.ActiveTimeBattle.updateSpriteEnemy.call(this)
    var width = this._mainSprite ? this._mainSprite._frame.width : this.width;
    if (width && $gameParty.inBattle() && this._enemy.isSpriteVisible() &&
    this._atbSpriteName !== this._battlerName && !this._battler.enemy().hideAtbGauge) {
    this._atbSpriteName = this._battlerName;
    var show = VictorEngine.ActiveTimeBattle.gauge.enemy;
    var position = VictorEngine.ActiveTimeBattle.gauge.enemyPosition;
    this.drawSpriteAtb(this._enemy, position, width, show);
    }
    };

    By calling Sprite_Battler.prototype.drawSpriteAtb:

    Sprite_Battler.prototype.drawSpriteAtb = function(battler, position, width, show) {
    if (show) {
    if (!this._atbGauge || this._atbBattler !== battler) {
    this._atbBattler = battler;
    this.removeChild(this._atbGauge);
    this.createAtbGauge(battler, position, width);
    }
    this.redrawAtbGauge();
    }
    };

    Which calls Sprite_Battler.prototype.createAtbGauge:

    Sprite_Battler.prototype.createAtbGauge = function(battler, position, width) {
    width = Number(position[2]) || width;
    var gauge = new Window_AtbGauge(0, 0, width, battler);
    var x = Number(position[0]) || 0;
    var y = Number(position[1]) || 0;
    gauge.x = this._homeX - gauge.width / 2 + (battler.isFacingRight() ? -x : x);
    gauge.y = this._homeY - gauge.height / 2 + y;
    this._atbGauge = gauge;
    this.parent.addChild(gauge);
    };

    Note that a window instead of a sprite's used to draw the battler sprite ATB bar.


    On the other hand, Sprite_Battler.prototype.updateMain is extended to be this:

    VictorEngine.ActiveTimeBattle.updateMain = Sprite_Battler.prototype.updateMain;
    Sprite_Battler.prototype.updateMain = function() {
    VictorEngine.ActiveTimeBattle.updateMain.call(this);
    this.redrawAtbGauge();
    };

    Where Sprite_Battler.prototype.redrawAtbGauge is this:

    Sprite_Battler.prototype.redrawAtbGauge = function() {
    if (this._atbGauge) {
    this._atbGauge.redrawAtbGauge();
    this._atbGauge.visible = this.isAtbVisible();
    }
    }

    Note that the ATB bars will be redrawn even when it's invisible.


    Sprite_Battler.prototype.isAtbVisible is this:

    Sprite_Battler.prototype.isAtbVisible = function() {
    return (this.opacity || this._effectType === 'blink') && !BattleManager.isHideAtb() &&
    this._effectType !== 'collapse' && this._effectType !== 'bossCollapse';
    }

    Where BattleManager.isHideAtb is this:

    BattleManager.isHideAtb = function() {
    return !this._phase || this._phase === 'init' || this._phase === 'start' ||
    this.isAborting() || this.isBattleEnd();
    };



    Window_AtbGauge.prototype.initialize is this:

    Window_AtbGauge.prototype.initialize = function(x, y, width, battler) {
    var width = width + this.standardPadding() * 2;
    var height = this.lineHeight() + this.standardPadding() * 2;
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    this.battler = battler;
    this.opacity = 0;
    };

    Whereas Window_AtbGauge.prototype.redrawAtbGauge is this:

    Window_AtbGauge.prototype.redrawAtbGauge = function() {
    var rate = this.battler.atbRate();
    var width = this.contents.width;
    var color = this.atbGaugeColor();
    this.contents.clear();
    this.drawGauge(0, 0, width, rate, color[0] || '#009900', color[1] || '#00FF00');
    this.changeTextColor(this.systemColor());
    this.drawText(VictorEngine.ActiveTimeBattle.gauge.name, 0, 0, width);
    }

    And Window_AtbGauge.prototype.atbGaugeColor is this:

    Window_AtbGauge.prototype.atbGaugeColor = function() {
    var gauge = VictorEngine.ActiveTimeBattle.gauge;
    var state = this.battler.stateAtbColor();
    if (state) {
    var color = state.atbColor;
    } else if (this.battler.isAtbCast()) {
    var color = this.battler.castColor() || gauge.cast;
    } else {
    var color = gauge.wait;
    }
    return this.battler.atbFull() ? color.full : color.fill;
    }




    Battler ATB Ordering

    Spriteset_Battle.prototype.createLowerLayer is extended to be this:



    Spoiler






    VictorEngine.ActiveTimeBattle.createLowerLayer = Spriteset_Battle.prototype.createLowerLayer;
    Spriteset_Battle.prototype.createLowerLayer = function() {
    VictorEngine.ActiveTimeBattle.createLowerLayer.call(this);
    this.createAtbBar();
    };

    Where Spriteset_Battle.prototype.createAtbBar is this:

    Spriteset_Battle.prototype.createAtbBar = function() {
    if (VictorEngine.ActiveTimeBattle.bar.show) {
    this._spriteAtbBar = new Sprite_AtbBar();
    this._battleField.addChild(this._spriteAtbBar);
    }
    };

    And Sprite_AtbBar.prototype.initialize is this:

    Sprite_AtbBar.prototype.initialize = function() {
    Sprite_Base.prototype.initialize.call(this);
    this._battlersIcons = [];
    this.anchor.x = 0;
    this.anchor.y = 0.5;
    this.z = 10;
    };

    Note that:


    1. This is a single ATB Bar displaying all battler's Battler ATB Clocks altogether.


    2. The Battler Sprite ATB bars are updated by VE - Basic Module, via Sprite_AtbBar.prototype.update:

    Sprite_AtbBar.prototype.update = function() {
    Sprite_Base.prototype.update.call(this);
    this.updateBarBitmap();
    this.updateBattlerSicons();
    this.sortIconSprites();
    this.visible = !BattleManager.isHideAtb();
    };

    Sprite_AtbBar.prototype.updateBarBitmap is this:

    Sprite_AtbBar.prototype.updateBarBitmap = function() {
    if (!this.bitmap) {
    this.bitmap = ImageManager.loadSystem(this.display().name);
    this.bitmap.addLoadListener(this.updateBarFrame.bind(this));
    }
    };

    And Sprite_AtbBar.prototype.display is this:

    Sprite_AtbBar.prototype.display = function() {
    return VictorEngine.ActiveTimeBattle.bar;
    };

    Whereas Sprite_AtbBar.prototype.updateBarFrame is this:

    Sprite_AtbBar.prototype.updateBarFrame = function() {
    var x = this.display().position.x
    var y = this.display().position.y
    var width = this.bitmap.width;
    var height = this.bitmap.height;
    this.setFrame(0, 0, width, height);
    this.x = x;
    this.y = y;
    };

    And Sprite_AtbBar.prototype.updateBattlerSicons is this:

    Sprite_AtbBar.prototype.updateBattlerSicons = function() {
    var allMembers = BattleManager.allBattleMembers().reverse();
    var iconBattlers = this.iconBattlers();
    for (var i = 0; i < allMembers.length; i++) {
    var member = allMembers;
    if (!iconBattlers.contains(member)) {
    this.addIconBattler(member);
    }
    }
    for (var i = 0; i < iconBattlers.length; i++) {
    var member = iconBattlers;
    if (!allMembers.contains(member)) {
    this.removeIconBattler(member);
    }
    }
    };




    So it's to ensure all battlers are shown on that single ATB bar, and all battlers shown on that bar are indeed in battle, as shown by Sprite_AtbBar.prototype.addIconBattler and Sprite_AtbBar.prototype.removeIconBattler and Sprite_AtbBar.prototype.iconBattlers:

    Sprite_AtbBar.prototype.addIconBattler = function(battler) {
    var sprite = new Sprite_AtbIcon(battler);
    this._battlersIcons.push(sprite);
    this.addChild(sprite);
    };

    Sprite_AtbBar.prototype.removeIconBattler = function(battler) {
    var index = -1;
    for (var i = 0; i < this._battlersIcons.length; i++) {
    var icon = this._battlersIcons;
    if (icon.battler() === battler) {
    index = i;
    }
    }
    if (index >= 0) {
    this.removeChild(this._battlersIcons[index]);
    this._battlersIcons.splice(index, 1);
    }
    };

    Sprite_AtbBar.prototype.iconBattlers = function() {
    return this._battlersIcons.map(function(icon) {
    return icon.battler();
    });
    };




    Finally, Sprite_AtbBar.prototype.sortIconSprites is this:

    Sprite_AtbBar.prototype.sortIconSprites = function() {
    if (this._sortChildrenFrame !== Graphics.frameCount) {
    this.children.sort(this.compareIconSprites.bind(this));
    this._sortChildrenFrame = Graphics.frameCount;
    }
    };

    And Sprite_AtbBar.prototype.compareIconSprites is this:

    Sprite_AtbBar.prototype.compareIconSprites = function(a, b) {
    if ((a.z || 0) !== (b.z || 0)) {
    return (a.z || 0) - (b.z || 0);
    } else {
    return a.spriteId - b.spriteId;
    }
    };



    Sprite_AtbIcon is too advanced to be covered here, but basically it's to handle an icon representing each battler on that single ATB bar.




    Battle Frame Update Execution Simulation

    The below's a simplified flowchart illustrating a Battle Frame Update execution:



    Spoiler



    [​IMG]

    Note that only features essential to this ATB system plugin's battle flow's shown, and the flowchart isn't 100% accurate nor precise even in that sense.


    The below will explain the keys of the above flowchart, thus skipping some details that are trivial or understood already:


    1. When the Battle Frame Update runs, the plugin will first check if players are inputting commands.


    2. If that's the case and the ATB update mode is neither full active nor semi active, everything other than the currently executing action will stop until players finished inputting commands.


    3. Otherwise, the original Battle Frame Update, which handles setting up the new Action Execution Subject and executing actions for the current one, will be called.


    4. Right after the Action Execution Subject has executed all actions, that battler's Battler ATB Clock will be reset, and in some cases, begin to be frozen.


    5. Then, if any of the following's met:


    - ATB update mode is full active


    - ATB update mode is semi wait and there's no currently inputable actors


    - There's no Action Execution Subject and ATB update mode is semi active


    - There's no Action Execution Subject nor currently inputable actors


    The current Battle Frame Update will trigger, otherwise it won't.


    6. If the current Battle Frame Update continues, the Battle Turn Clock will be updated first, which will be updated per ATB Frame update for turn count mode being time, or right after a battler has executed all actions otherwise.


    7. Then the ATB Frame Update will be triggered, performing the following:


    - Setting up the actor command window for the 1st newly inputable actor if there's any such actor and no currently inputable actor


    - Makes actions and setups casting actions for all newly inputable battlers


    - Marks all battlers having completely casted all acions to become able to execute them all


    - Unfreeze all battlers previously having a frozen Battler ATB Clock with the freezing period just becoming expired


    - Updates each battler's Battler ATB Clock


    8. After that, ATB bars will be updated, ableit with none of them being updated by the ATB Frame Update but by the Battle Frame Update instead.


    9. When players input actions, they can either input 1 action for each inputable actor or trigger a party escape attempt.


    10. A success party escape attempt will basically work the same as those in the default RMMV battle system, while a failed one will reset all party members' Battler ATB Clock.


    11. As the currently inputting actor will become not inputable(with the actor command window closed) right after finishing inputting 1 action, Action Times + won't work for such actors.


    12. Right after a battler has finished inputting all actions(automatically or by players), that battler will either become eligible to execute them immediately, or have to cast all those actions before finishing casting them, in which that battler will become eligible to execute them.


    [/spoiler]


    The rest are nonessential ATB system features(i.e.: The ATB system can still function without any of them) and are too massive(and too advanced for some of them) to be covered here. After all, that plugin's written by Victor Sant but not me, so it's better to ask him/her if you can't thoroughly comprehend these nonessential ATB system features yourselves :p










    Summary

    VE - Active Time Battle



    Spoiler



    1. When the Battle Frame Update runs, the plugin will first check if players are inputting commands.


    2. If that's the case and the ATB update mode is neither full active nor semi active, everything other than the currently executing action will stop until players finished inputting commands.


    3. Otherwise, the original Battle Frame Update, which handles setting up the new Action Execution Subject and executing actions for the current one, will be called.


    4. Right after the Action Execution Subject has executed all actions, that battler's Battler ATB Clock will be reset, and in some cases, begin to be frozen.


    5. Then, if any of the following's met:


    - ATB update mode is full active


    - ATB update mode is semi wait and there's no currently inputable actors


    - There's no Action Execution Subject and ATB update mode is semi active


    - There's no Action Execution Subject nor currently inputable actors


    The current Battle Frame Update will trigger, otherwise it won't.


    6. If the current Battle Frame Update continues, the Battle Turn Clock will be updated first, which will be updated per ATB Frame update for turn count mode being time, or right after a battler has executed all actions otherwise.


    7. Then the ATB Frame Update will be triggered, performing the following:


    - Setting up the actor command window for the 1st newly inputable actor if there's any such actor and no currently inputable actor


    - Makes actions and setups casting actions for all newly inputable battlers


    - Marks all battlers having completely casted all acions to become able to execute them all


    - Unfreeze all battlers previously having a frozen Battler ATB Clock with the freezing period just becoming expired


    - Updates each battler's Battler ATB Clock


    8. After that, ATB bars will be updated, ableit with none of them being updated by the ATB Frame Update but by the Battle Frame Update instead.


    9. When players input actions, they can either input 1 action for each inputable actor or trigger a party escape attempt.


    10. A success party escape attempt will basically work the same as those in the default RMMV battle system, while a failed one will reset all party members' Battler ATB Clock.


    11. As the currently inputting actor will become not inputable(with the actor command window closed) right after finishing inputting 1 action, Action Times + won't work for such actors.


    12. Right after a battler has finished inputting all actions(automatically or by players), that battler will either become eligible to execute them immediately, or have to cast all those actions before finishing casting them, in which that battler will become eligible to execute them.
     
    Last edited by a moderator: Jul 2, 2016
    #10
    Victor Sant likes this.

Share This Page