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

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,773
Reaction score
938
First Language
Chinese
Primarily Uses
N/A
Related topics


Basic knowledge 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 decent ATB system plugin and their addons, meaning this topic will focus on more advanced stuffs


Targeting Audiences


Those having decent battle related plugin development(experienced plugin developer having written dozens of decent battle related plugins with decent code qualities), and:


1. Solid understanding to the default RMMV battle flow implementations


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


3. Solid understanding on how the fundamental ATB system concepts work on the user level


Contents


I. ATB System Configuration Management


1. 3 Dimensions Of Control And Freedom


2. Notetag Management


3. Summary


II. ATB System Code Qualities


1. Invariants


2. Correctness


3. Robustness


4. Performance


5. Coupling


6. Cohesion


7. Flexibility


8. Summary


III. ATB System Addons


1. Bar Addon


2. Charge Addon


3. Cooldown Addon


4. Countdown Addon


5. Delay Addon


6. Order Addon


7. Addon Ordering Rules


8. Interaddon Dependencies


9. Summary


IV. Detailed DoubleX RMMV Minimalized ATB Reasoning


1. Invariants


2. Correctness


3. Robustness


4. Performance


5. Coupling


6. Cohesion


7. Flexibility


8. Summary


V. Detailed DoubleX RMMV Popularized ATB Core Reasoning


1. Invariants


2. Correctness


3. Robustness


4. Performance


5. Coupling


6. Cohesion


7. Flexibility


8. Summary


VI. Detailed Ellye's ATB(also includes CTB option) Reasoning


1. Invariants


2. Correctness


3. Robustness


4. Performance


5. Coupling


6. Cohesion


7. Flexibility


8. Summary


VII. Detailed YEP.24 - Battle System - Active Time Battle Reasoning


1. Invariants


2. Correctness


3. Robustness


4. Performance


5. Coupling


6. Cohesion


7. Flexibility


8. Summary


VIII. Summary


Before actually covering any of the above contents, I want to explain why this topic's structured this way:


1. Writing a decent ATB system plugin often demands its codebase to have decent code quality and its configurations to have decent control and freedom given to users


2. Writing a plugin often starts from designing the configuration regions so it's natural for this topic to explore that part first


3. Writing a codebase normally involves some planning in advance with code qualities taken into account so this topic should explore some code qualities afterwards


4. A decent ATB system plugin will either be extremely massive or have dozens of addon plugins needing the core plugin so this topic should cover some addons not already covered in Basic knowledge on writing ATB(Active Time Battle) system plugins


5. Studying some existing ATB system plugins can further consolidate the understanding on writing decent ATB plugins after grasping all the above more advanced stuffs


I'll start to talk about the contents in the upcoming replies in this topic. For now, let's cite some simplified ATB system flowcharts as both a preparation on what I'm going to cover and a revision on what you're assumed to know already:

ATB Flow Overview -



 













Simplified Ellye's ATB(also includes CTB option) Flowchart -

 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,773
Reaction score
938
First Language
Chinese
Primarily Uses
N/A
Now let's talk about I. ATB System Configuration Management.


Note that none of these are unique to ATB system plugins. Instead, they can be applied to most plugins. The point of this reply is to show how these global knowledge can be applied to ATB system plugins.


3 Dimensions Of Control And Freedom

Feature Set



 





The 1st dimension of control and freedom given to users is the feature set of the plugin. It directly defines the scope and indirectly defines the target audience of the plugin.


Normally, a feature set of a plugin shouldn't include those being 100% absolutely necessary for the very essence of that plugin core mechanics, as they must always be present for all plugins having the exact same nature.


For instance, the existence of the ATB Wait Condition itself shouldn't be a feature of any ATB system plugin(but its absence, if even possible at all, can be a feature lol), although the control and freedom over the ATB Wait Condition settings can be.


The feature set is of the highest level among all 3 dimensions of control and freedom, as it gives its users an overview of what this plugin's capable of as a whole.


As an example, DoubleX RMMV Minimalized ATB(MATB, same below) lets you do the following:



 





1. Set the maximum turn unit running the Battle Turn Clock


2. Set the actor name, icons, hp, mp, tp and atb bar x positions and widths in the status window



On the feature set level, MATB lets you set some configurations that can but shouldn't be hardcoded.


On a side note: Such a small feature set somehow indicates that MATB can be a minimum viable product.


Another example - the below is the feature set of DoubleX RMMV Popularized ATB Core(PATB Core, same below):

1. Lets you change the battle system in game but outside battles(between ATB and the default RMMV battle system)


2. Lets you set how the Battle Turn Clock runs


3. Lets you set the ATB Wait Condition


4. Lets you set how the Battler ATB Clock runs



And the below is that of DoubleX RMMV Popularized ATB(PATB, same below):

1. Lets you change the battle system in game but outside battles(among ATB, CTB and the default RMMV battle system)


2. Lets you set how the ATB Battle Clock runs


3. Lets you set the ATB Wait Condition


4. Lets you set how the Battler ATB Clock runs


5. Lets you set how the battler ATB bars are shown


6. Lets you set some skills/items to need charging


7. Lets you set some skills/items to need their users to cooldown right after using them


8. Lets you set whether and how the Battle Turn Clock are displayed


9. Lets you set some states to be tick-based instead of turn-based nor action-based


10. Lets you set some enemies and/or autobattle/confused actors delay for a while before inputting actions


11. Lets you set some common events to be run at some specified timing unique to ATB systems


12. Lets you set some keys to force run/stop the ATB Frame Update


13. Lets you set some SE to be played when some battlers meeting some conditions become Actable



On side note: I've also planned to write 1 more addon to let you set whether and how the battler ATB order are displayed :)


It's 100% crystal clear and obvious that, as long as the plugin's well-written, having a larger feature set will likely cover more user demands, leading to more users to be within the targeting audience.


It also usually means higher difficulty, however, as ensuring the entire feature set's working well as a whole becomes harder as it becomes larger(It's too advanced to be covered here).






Feature Configurations

The 2nd dimension of control and freedom given to users is the coverage of all configurations, notetags, plugin calls and commands of each part of the feature set. It defines how detailed of each part of the feature set is.


While different parts of the feature set can have different control and freedom given to users in this dimension, the overall 2nd dimension of control and freedom given to users can still be roughly measured.


Again, the mere existence of configurations that must exist for its part of the feature set shouldn't be a feature of the plugin, otherwise that part of the feature set for the plugin is effectively disabled.


For instance, if letting users set the ATB Wait Condition is a part of the feature set of an ATB system plugin, then a configuration must exist for users to actually set the ATB Wait Condition. On the other hand, letting users change that configuration value on the fly can be considered to be a feature.


As an example, the MATB has the below configuration coverage:



 





1. Set the maximum turn unit running the Battle Turn Clock


2. Set the actor name, icons, hp, mp, tp and atb bar x positions and widths in the status window



All these are configurations that can but shouldn't be hardcoded.


Another example is the configuration coverage of PATB Cooldown, which has the feature set of letting you set some skills/items to need their users to cooldown right after using them:

* @param cooldown_c1
* @desc Sets the 1st atb cooldown bar color as text color cooldown_c1
* cooldown_c1 must return a valid text color code
* cooldown_c1 should return the same value during the same battle to
* ensure proper atb cooldown bar color displays
* @default 19
*
* @param cooldown_c2
* @desc Sets the 2nd atb cooldown bar color as text color cooldown_c2
* cooldown_c2 must return a valid text color code
* cooldown_c2 should return the same value during the same battle to
* ensure proper atb cooldown bar color displays
* @default 26
*
* @param cooldown_bar_text
* @desc Sets the code of the cooldown bar description text as cooldown_bar_text
* It'll only be used if no <patb cooldown text: text> notetag's used
* Available cooldown_bar_text code:
* item - The skill/item name causing the cooldown will be the cooldown
* bar description text
* Setting cooldown_bar_text as an unavailable code means atb_bar_text
* will be the cooldown bar description text
* cooldown_bar_text should return the same code during the same battle to
* ensure proper cooldown bar text displays
* @default item
*
* @param post_cooldown_common_event_id
* @desc Sets the common event with id post_cooldown_common_event_id to be
* called right after a battler has finished cooling down
* post_cooldown_common_event_id must return a Number
* If post_cooldown_common_event_id doesn't return the id of an existing
* common event, no common event will be called with this timing
* @default 0
*
* @help
* Battlers using skills/items without the <patb cooldown: scale, code>
* notetag will be fully cooled down in 1 frame
* The default plugin file name is DoubleX RMMV Popularized ATB Cooldown v101a
* If you want to change that, you must edit the value of
* DoubleX_RMMV.PATB_Cooldown_File, which must be done via opening the plugin
* js file directly
*============================================================================
* ## Notetag Info
*----------------------------------------------------------------------------
* # Skill/Item Notetags:
* 1. <patb cooldown: scale, code>
* - Sets the cooldown rate to use the skill/item's invocation speed,
* which will be multiplied by scale
* - code can be either of the below:
* set - The cooldown value per frame will be the skill/item's
* invocation speed * scale, which should be nonnegative
* add - The cooldown value per frame will be the absolute value of
* the battler atb gain value per frame + the skill/item's
* invocation speed * scale
* multiply - If the skill/item's invocation speed * scale is
* positive, the cooldown value per frame will be the
* battler atb gain value per frame * the skill/item's
* invocation speed * scale
* If the skill/item's invocation speed * scale is
* negative, the cooldown value per frame will be the
* battler atb gain value per frame / (the skill/item's
* invocation speed * scale)
* If the skill/item's invocation speed * scale is 0, the
* battler using the skill/item will be fully cooled down
* in 1 frame
* 2. <patb cooldown colors: text color 1, text color 2>
* - Changes the atb cooldown bar color 1 and 2 to text color 1 and 2
* respectively when this notetag's used
* 3. <patb cooldown text: text>
* - Changes the atb cooldown bar description text as text when this
* notetag's used
*============================================================================
* ## Plugin Call Info
*----------------------------------------------------------------------------
* # Data Skill/Item manipulations
* 1. meta.patb_cooldown
* - Returns the skill/item invocation speed scale and cooldown rate
* code in the form of { scale: scale, code: code }
* 2. meta.patb_cooldown = { scale: scale, code: code }
* - Sets the skill/item invocation speed scale and cooldown rate code
* in the form of { scale: scale, code: code }
* - All meta.patb_cooldown changes can be saved if
* DoubleX RMMV Dynamic Data is used
* 3. meta.patb_cooldown_colors
* - Returns the text colors stored in
* <patb cooldown colors: text color 1, text color 2> in the form of
* [text color 1, text color 2]
* 4. meta.patb_cooldown_colors = [text color 1, text color 2]
* - Sets the text colors stored in
* <patb cooldown colors: text color 1, text color 2> as text color
* 1 and 2
* - All meta.patb_cooldown_colors changes can be saved if
* DoubleX RMMV Dynamic Data is used
* 5. meta.patb_cooldown_text
* - Returns the text stored in <patb cooldown text: text>
* 6. meta.patb_cooldown_text = text
* - Sets the text stored in <patb cooldown text: text> as text
* - All meta.patb_cooldown_text changes can be saved if
* DoubleX RMMV Dynamic Data is used
* # Battler manipulations
* 1. patb_val.cooldown
* - Returns the battler's cooldown value
* 2. patb_val.cooldown = val
* - Set the battler's cooldown value as val
* 3. patb_rate.cooldown
* - Returns the battler's cooldown rate
* 4. patb_rate.cooldown = rate
* - Set the battler's cooldown rate as rate
* - It'll be reevaluated if it can be changed without plugin calls
* 5. patb_val_change.cooldown = true
* - Notifies that the cooldown value's changed
* - It must be used right after the atb bar length changed





Again, as long as the plugin's well-written, the more detailed of a part of the feature set, the more powerful that part is. Just note that sometimes detailing a part of the feature set can affect some other parts of the feature set, as some parts of the feature set are inherently coupled(Its details will be covered in III. ATB System Addons).






Configuration Control And Freedom

The 3rd dimension of control and freedom given to users is the flexibility and power of each configuration, notetag, plugin call and command. It defines how adaptable of of each configuration, notetag, plugin call and command is.


While different configuration, notetag, plugin call and command can have different control and freedom given to users in this dimension, the overall 3rd dimension of control and freedom given to users can still be roughly measured.


Normally, this level of control and freedom can further be divided into 7 levels:



 





1. Hardcoded Values - Users must use the single predefined value and can't change it by any means


2. Hardcoded Choices - Users can only choose among all the predefined values


3. Editable Hardcoded Choices - Same as Hardcoded Choices, except that those choices can be changed on the fly


4. Simple Values - Users can only use simple values as those of configurations, notetags and arguments of plugin calls and commands


5. Editable Simple Values - Same as Simple Values, except that those values can be changed on the fly


6. Javascript Codes - Users can use javascript codes as values of configurations, notetags and arguments of plugin calls and commands


7. Editable Javascript Codes - Same as Javascript Codes, except that those codes can be changed on the fly



Unless you've at least advanced RMMV plugin development proficiency(veteran RMMV plugin developer having written at least 1 excellent advanced complex plugin with all code qualities being well balanced and prioritized, same below), you'll want to stay away from level 6, let alone level 7, for the overall level, as managing tons of such javascript codes can quickly end up with an utter mess for less capable plugin developers, especially in a large scale plugin.


On the other hand, the overall level being 1 to 3 are relatively a bit restrictive for any decent ATB system plugin, so you'll want to achieve at least level 4, and preferably level 5 for the overall level.


On a side note: Level 1 doesn't mean no control and freedom at all - The only control and freedom given to the user is to enable/disable the configurations, and/or use/not to use the notetag, plugin call and command :D


For example:

1. The below plugin call in MATB exhibits level 1 control and freedom given to users -



 









* 2. reset_matb()
* - Clears all battler's actions and resets the battler's atb value



It's because the only control and freedom users have over this plugin call is to use/not to use it.


2. The below plugin call in PATB Core exhibits level 2 control and freedom given to users -

* 12. 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



It's because its argument, type, must be either atb, charge or cooldown.


3. The below configuration in PATB Core exhibits level 3 control and freedom given to users -

* @param battle_system_code
* @desc Sets the code indicating if ATB 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



Its' because of this plugin call in the same plugin:

* 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



While the value can either be atb, ctb(with the CTB addon), or unavailable ones(any value other than atb and ctb), users can change the value set in that configuration using that plugin call.


4. The below plugin call in PATB Core exhibits level 4 control and freedom given to users -

* 4. patb_val.atb = val
* - Set the battler's atb value as val



It's because any Number between 0 and the Maximum ATB Value are valid simple values for this plugin call.


5. The below configuration in PATB Core exhibits level 5 control and freedom given to users -

* @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



Its' because of this plugin call in the same plugin:

* 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



Meaning that the simple value used by that configuration can be changed using that plugin call.


6. The below configuration in PATB Countdown is supposed to exhibit level 6 control and freedom given to users:

/* Sets something to happen when the turn of the countdown state state
* owned by the battler calling on_countdown_update decreases
* on_countdown_update will be bound to the battler upon use
*/
on_countdown_update: function(state) {
// Uses DoubleX RMMV State Triggers to trigger countdown update effects
//var GBB = DoubleX_RMMV.State_Triggers.Game_BattlerBase;
//GBB.execStateTriggers.call(this, state.id, "turn");
//

// Applies 10 hp damage to the state owner per countdown update
this._result.clear();
this._result.success = true;
this.gainHp(-10);
this.onDamage(10);
this.startDamagePopup();
if (this._hp <= 0) { this.performCollapse(); }
} // on_countdown_update



It's because its values are javascript codes.


7. Relatively more capable users can actually enjoy level 7 control and freedom from the above configuration by using this:

DoubleX_RMMV.PATB_Countdown.on_countdown_update = new Function("state", contents);



Where contents are set by those users.


On a side note: None of such changes will be saved, as that configuration isn't supposed to exhibit level 7 control and freedom give by users. So those relatively more capable users will have to restore their changes upon loading saved games themselves.


From my experiences and observations, any configuration/notetag/plugin call/command offering level 6 or 7 control and freedom to users can only be fully utilized by those having at least some plugin development proficiency. You should always keep the expected capabilities of your targeting audiences in mind when deciding what levels you want to reach(The trade between control and freedom given to users and user-friendliness are too advanced to be covered here).
















Notetag Management

Managing notetags involves 2 aspects - keeping them user-friendly enough on the user level, and keeping them under sufficient control on the implementation level.


Control And Freedom



 





As mentioned in Configuration Control And Freedom, each notetag can exhibit 1 of the 7 levels of control and freedom given to users.


In general, the higher the levels, the harder for the notetags to be managed. This applies on both the user-friendliness on the user level, and the difficulty to be controlled on the implementation level.


For instance, with the below plugin call, the below notetag in PATB Charge exhibits level 3 control and freedom given to users:



 








 










* 4. <patb charge prior item cost>
* - Sets the skill/item charging to take place before paying its cost


Code:
 *      8. meta.patb_charge_prior_item_cost = boolean                         
 *         - Sets whether the skill/item charging will take place before      
 *           paying its costs                                                 
 *         - All meta.patb_charge_prior_item_cost changes can be saved if     
 *           DoubleX RMMV Dynamic Data is used





For the user-friendliness on the user level, the setup just needs to do the following:


1. Lets users know the existence of this notetag


2. Lets users know what this notetag does


3. Lets users know where to place this notetag


4. Lets users know how to change the notetag values on the fly


5. Lets users know that such changes can be saved or not saved


It's because even users with no plugin development proficiency at all should still be able to use the notetag easily(as long as they're familiar with the RMMV basics), and those with just little RMMV plugin development proficiency(beginning javascript coder having written tiny amount of rudimentary javascript codes, same below) should be able to use the plugin call without too much effort.


For the control on the implementation level, the setup's easy, simple and small:

// data: The data to have its notetags read
DataManager.load_all_patb_charge_notes = DataManager.load_all_patb_notes;
DataManager.load_all_patb_notes = function() {
// Added
[$dataSkills, $dataItems].forEach(function(type) {
type.forEach(function(data) {
if (data) { this.load_patb_charge_notes(data); }
}, this);
}, this);
//
return this.load_all_patb_charge_notes();
}; // DataManager.load_all_patb_notes

// data: The data to have its notetags read
DataManager.load_patb_charge_notes = function(data) { // New
var charge = /< *patb +charge *: *(\d+) *, *(\w+) *>/i;
var color = /< *patb +charge +colors *: *(\d+) *, *(\d+) *>/i;
var text = /< *patb +charge +text *: *(\w+) *>/i;
var cost = /< *patb +charge +prior +item +cost *>/i, m = data.meta;
data.note.split(/[\r\n]+/).forEach(function(line) {
if (line.match(charge)) {
return m.patb_charge = { scale: +RegExp.$1, code: RegExp.$2 };
} else if (line.match(color)) {
return m.patb_charge_colors = [+RegExp.$1, +RegExp.$2];
}
if (line.match(text)) { return m.patb_charge_text = RegExp.$1; }
if (line.match(cost)) { return m.patb_charge_prior_item_cost = true; }
});
// Sets the default to be fully charged in 1 frame
m.patb_charge = m.patb_charge || { scale: 0, code: "multiply" };
//
}; // DataManager.load_patb_charge_notes


Code:
Game_Battler.prototype.patb_charge_prior_item_cost = function(item) {
// v1.01a+; New
    if (item && item.meta.patb_charge_prior_item_cost !== undefined) {
        return item.meta.patb_charge_prior_item_cost;
    }
    return $gameSystem.patb.charge_prior_item_cost;
}; // Game_Battler.prototype.patb_charge_prior_item_cost








Consider another example - the below notetag in DoubleX RMMV State Triggers, which exhibits level 7 control and freedom given to users with the below plugin call:

 










* # State Notetags:
* 1. <timing state trigger: STCX, STAX>
* - Sets a state to trigger STAX when timing and STCX are met
* - timing can be add, turn, remove or custom timings set by you
* - add means the state's just added
* - turn means the state's remaining turn's just reduced by 1
* - remove means the state's just removed
* - timing must only consist of alphanumeric characters
* - STCX can be set in State Trigger Condition Functions
* - STAX can be set in State Trigger Action Functions


Code:
 *      2. meta.stateTriggers[timing] = [[STCX, STAX], [STCX, STAX], ...]     
 *         - Adds a new timing with some STCX-STAX pairs or overwrites all the
 *           existing ones with those pairs if timing is an existing timing   
 *      3. meta.stateTriggers[timing][i] = [STCX, STAX]                       
 *         - Set the ith STCX-STAX pair as the new STCX-STAX pair
Code:
    /*------------------------------------------------------------------------
     *    State Trigger Condition Functions                                   
     *    - Setups STCX used by this plugin's notetags                        
     *------------------------------------------------------------------------*/
    /* STCX are used at:
     * 1. DoubleX_RMMV.State_Triggers.Game_BattlerBase
     *    - if (ST[trigger[0]].call(this)) { ST[trigger[1]].call(this); } in
     *      execStateTriggers
     * STCX are Javascript functions which will be bound to the battler upon use
     * STCX names can only use alphanumeric characters
     * state is the state using the STCX
     * The below STCX are examples added to help you set your STCX
     * You can freely use, rewrite and/or delete these examples
     */

    // Sets the state trigger condition as always true
    STC1: function(state) { return true; },

    // Sets the state trigger condition as needing switch with id x to be on
    STC2: function(state) { return $gameSwitches.value(x); },

    // Adds new STCX here
    

    /*------------------------------------------------------------------------
     *    State Trigger Action Values                                         
     *    - Setups STAX used by this plugin's notetags                        
     *------------------------------------------------------------------------*/
    /* STAX are used at:
     * 1. DoubleX_RMMV.State_Triggers.Game_BattlerBase
     *    - if (ST[trigger[0]].call(this)) { ST[trigger[1]].call(this); } in
     *      execStateTriggers
     * STAX are Javascript functions which will be bound to the battler upon use
     * STAX names can only use alphanumeric characters
     * state is the state using the STAX
     * The below STAX are examples added to help you set your STAX
     * You can freely use, rewrite and/or delete these examples
     */

    // Sets the state trigger action as what Special Effect Escape does
    STA1: function(state) { this.hide(); },

    // Sets the state trigger action as setting the battler's hp to full
    STA2: function(state) { this._hp = this.mhp; }

    // Adds new STAX here



To fully utilize those notetags, this plugin call will be needed as well:







* # Battler manipulations
* 1. execStateTriggers(stateId, timing)
* - Executes all state triggers with timing timing of state with id
* stateId



On a side note: This plugin call can further increase the control and freedom given to users, although that extra mile is most likely redundant:







* 2. DoubleX_RMMV.State_Triggers.prop = val
* - Sets the property prop under DoubleX_RMMV.State_Triggers as a
* function which will be bound to the battler upon use



Also, none of these changes will be saved, so this plugin call only has limited use :p





For the user-friendliness on the user level, the setup needs to do the following:


1. Lets users know the existence of this notetag


2. Lets users know what this notetag does


3. Lets users know where to place this notetag


4. Lets users know how to use timing, stax and stcx


5. Lets users know how to change the notetag values on the fly


6. Lets users know that such changes can be saved or not saved


While point 1, 2, 3 and 6 should be straightforward for users with just little RMMV plugin development proficiency, point 5 is likely incomprehensible for those not having some RMMV plugin development proficiency(inexperienced junior RMMV plugin developer having written several easy, simple and small RMMV plugins without nontrivial bugs, same below), and point 4 can hardly be achieved except for users having at least decent RMMV plugin development proficiency(experienced RMMV plugin developer having written dozens of decent RMMV plugins having decent code qualities, same below). That's why that plugin can only be utilized by those having at least decent RMMV plugin development proficiency.


Even for those with at least decent RMMV plugin development proficiency, they still need to know where and how those stcx and stax will be used, in order for them to write the functions with the correct context in mind. That's why the documentation has this part:

/* STCX are used at:
* 1. DoubleX_RMMV.State_Triggers.Game_BattlerBase
* - if (ST[trigger[0]].call(this)) { ST[trigger[1]].call(this); } in
* execStateTriggers
* STCX are Javascript functions which will be bound to the battler upon use





Fortunately, for the control on the implementation level, the setup can also be easy, simple and small in this case, if you know how this can be achieved:

DM.isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function() {
// Rewritten
return DM.isDatabaseLoaded.apply(this, arguments) && DM.loadAllNotes();
//
}; // DataManager.isDatabaseLoaded

DM.loadAllNotes = function() {
$dataStates.forEach(function(data) {
if (data) { DM.loadStateNotes(data); }
});
return true;
}; // DM.loadAllNotes

// data: The data to have its notetags read
DM.loadStateNotes = function(data) {
data.meta.stateTriggers = {};
var timing, triggers = data.meta.stateTriggers;
var regExp = /< *(\w+) +state +trigger *: *(\w+) *, *(\w+) *>/i;
data.note.split(/[\r\n]+/).forEach(function(line) {
if (!line.match(regExp)) { return; }
timing = RegExp.$1;
triggers[timing] = triggers[timing] || [];
triggers[timing].push([RegExp.$2, RegExp.$3]);
});
}; // DM.loadStateNotes


Code:
    /*------------------------------------------------------------------------
     *    Triggers each state action when each respective condition's met     
     *------------------------------------------------------------------------*/
    /* stateId: The id of the state triggering its actions
     * timing: The timing of the state triggering its actions
     */
    GBB.execStateTriggers = function(stateId, timing) {
        var state = $dataStates[stateId];
        var triggers = state.meta.stateTriggers[timing];
        if (!triggers) { return; }
        // Calls each STCX to see if its paired STAX should be called as well
        triggers.forEach(function(trigger) {
            if (!ST[trigger[0]].call(this, state)) { return; }
            ST[trigger[1]].call(this, state);
        }, this);
        //
    }; // GBB.execStateTriggers








The above examples demonstrates why you'll want to stay away from overall level of control and freedom given to users being 6(Javascript Codes) or 7(Editable Javascript Codes), unless you do feel/think that you're really just that capable :guffaw:


In short, the setup for notetags typically just needs to do the following:


1. Lets users know the existence of this notetag


2. Lets users know what this notetag does


3. Lets users know where to place this notetag


4. Lets users know how to set the notetag values


5. Lets users know what notetag values are valid


6. Lets users know how to change the notetag values on the fly


7. Lets users know that such changes can be saved or not saved


All these should be easy, simple and small for almost all users if those notetags only offer level 5(or below) control and freedom to users, but point 4, 5 and 6 can be hard for relatively less capable ones.







Notetag Types

This part is mainly about ensuring sufficient control over notetags on the implementation level, as it's little impact on the user level.


Using PATB as an example, the PATB Core has these 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



PATB Bar has these notetags:

* # Actor/Class/Weapon/Armor/Enemy/State Notetags:
* State notetags take the highest priority, followed by enemy, weapon,
* armor, class and actor
* The 1st notetag that's being read by the battler will be used
* 1. <patb bar show: code>
* - Sets the code determining when the battler ATB bars will be shown
* on the battler sprites as code
* Available codes are those of bar_show
* - Setting code as an unavailable code means the battler ATB bars
* will never be shown on the battler sprites
* 2. <patb bar w: w>
* - Sets the width of the ATB bars shown on the battler sprites as w
* 3. <patb bar h: h>
* - Sets the height of the ATB bars shown on the battler sprites as h
* 4. <patb bar x: x>
* - Sets the x offset of the ATB bars shown on the battler sprites as
* x
* 5. <patb bar y: y>
* - Sets the y offset of the ATB bars shown on the battler sprites as
* y
* 6. <patb bar opacity: opacity>
* - Sets the opacity of the ATB bars shown on the battler sprites as
* opacity
* 7. <patb bar text size: size>
* - Sets the size of the description text of the ATB bars shown on
* the battler sprites as size



PATB Charge has these notetags:

* # Skill/Item Notetags:
* 1. <patb charge: scale, code>
* - Sets the charge rate to use the skill/item's invocation speed,
* which will be multiplied by scale
* - code can be either of the below:
* set - The charge value per frame will be the skill/item's
* invocation speed * scale, which should be nonnegative
* add - The charge value per frame will be the absolute value of
* the battler atb gain value per frame + the skill/item's
* invocation speed * scale
* multiply - If the skill/item's invocation speed * scale is
* positive, the charge value per frame will be the
* battler atb gain value per frame * the skill/item's
* invocation speed * scale
* If the skill/item's invocation speed * scale is
* negative, the charge value per frame will be the
* battler atb gain value per frame / (the skill/item's
* invocation speed * scale)
* If the skill/item's invocation speed * scale is 0, the
* skill/item will be fully charged in 1 frame
* 2. <patb charge colors: text color 1, text color 2>
* - Changes the atb charge bar color 1 and 2 to text color 1 and 2
* respectively when this notetag's used
* 3. <patb charge text: text>
* - Changes the atb charge bar description text as text when this
* notetag's used
* 4. <patb charge prior item cost>
* - Sets the skill/item charging to take place before paying its cost



PATB Cooldown has these notetags:

* 1. <patb cooldown: scale, code>
* - Sets the cooldown rate to use the skill/item's invocation speed,
* which will be multiplied by scale
* - code can be either of the below:
* set - The cooldown value per frame will be the skill/item's
* invocation speed * scale, which should be nonnegative
* add - The cooldown value per frame will be the absolute value of
* the battler atb gain value per frame + the skill/item's
* invocation speed * scale
* multiply - If the skill/item's invocation speed * scale is
* positive, the cooldown value per frame will be the
* battler atb gain value per frame * the skill/item's
* invocation speed * scale
* If the skill/item's invocation speed * scale is
* negative, the cooldown value per frame will be the
* battler atb gain value per frame / (the skill/item's
* invocation speed * scale)
* If the skill/item's invocation speed * scale is 0, the
* battler using the skill/item will be fully cooled down
* in 1 frame
* 2. <patb cooldown colors: text color 1, text color 2>
* - Changes the atb cooldown bar color 1 and 2 to text color 1 and 2
* respectively when this notetag's used
* 3. <patb cooldown text: text>
* - Changes the atb cooldown bar description text as text when this
* notetag's used



PATB Countdown has this notetag:

* # State Notetags:
* 1. <patb countdown: sec>
* - Sets the state to have its turn updated every sec seconds
* - Every second in sec only consists of frames with atb updates



PATB Delay has this notetag:

* # Actor/Class/Weapon/Armor/Enemy/State Notetags:
* State notetags take the highest priority, followed by enemy, weapon,
* armor, class and actor
* 1. <operator patb delay: frame>
* - Assigns frame frames to the battler's delay via operator
* - The number of frames per second's hardcoded as 60 in RMMV default
* - 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



PATB Rate has this notetag:

* # 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



PATB Reset has this notetag:

* # Skill/Item Notetags:
* 1. <operator patb reset: val>
* - Assigns val to the battler's atb reset 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



PATB SE has this notetag:

* # 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



And PATB Start has this notetag:

* # 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





While there are dozens of notetags(23 uptil now to be precise) in the whole PATB system, actually they can be categorized into only few types(4 uptil now to be precise).


Type 1 - Actor/Class/Weapon/Armor/Enemy/State multiple <operator notetag: val>

<operator max patb val: val>


<operator patb delay: frame>


<operator patb rate: rate>


<operator patb start: val>



Type 2 - Actor/Class/Weapon/Armor/Enemy/State Notetags single <notetag: val1, val2, val3, ..., valn>

<patb colors: text color 1, text color 2>


<patb overlay colors: text color 1, text color 2>


<patb bar show: code>


<patb bar w: w>


<patb bar h: h>


<patb bar x: x>


<patb bar y: y>


<patb bar opacity: opacity>


<patb bar text size: size>


<patb can act se: file, vol, pitch, pan>



Type 3 - Skill/Item State single <notetag: val1, val2, val3, ..., valn>

<patb charge: scale, code>


<patb charge colors: text color 1, text color 2>


<patb charge text: text>


<patb charge prior item cost>


<patb cooldown: scale, code>


<patb cooldown colors: text color 1, text color 2>


<patb cooldown text: text>


<patb countdown: sec>



Type 4 - Skill/Item multiple <operator notetag: val>

<operator patb reset: val>





All type 1 notetags are stored like this(Using <operator max patb val: val> as an example):

data.note.split(/[\r\n]+/).forEach(function(line) {
if (line.match(max)) {
meta.max_patb_val.push([RegExp.$1, +RegExp.$2]);
}
});



All type 2 notetags are stored like this(Using <patb colors: text color 1, text color 2> as an example):

data.note.split(/[\r\n]+/).forEach(function(line) {
if (!meta.patb_colors && line.match(color)) {
meta.patb_colors = [+RegExp.$1, +RegExp.$2];
}
});



All type 3 notetags are stored like this(Using <patb charge: scale, code> as an example):

data.note.split(/[\r\n]+/).forEach(function(line) {
if (line.match(charge)) {
return m.patb_charge = { scale: +RegExp.$1, code: RegExp.$2 };
}
});



All type 4 notetags are stored like this(Using <operator patb reset: val> as an example):

data.note.split(/[\r\n]+/).forEach(function(line) {
if (line.match(reset)) { m.patb_reset.push([RegExp.$1, +RegExp.$2]); }
});





All stored type 1 notetag values are read upon use like this(Using <operator max patb val: val> as an example):

var max = $gameSystem.patb.max_atb_val;
this._max_patb_val = this.set_multi_patb_notes(max, "max_patb_val");


Code:
/* 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

/* 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
Code:
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



All stored type 2 notetag values are read upon use like this(Using <patb colors: text color 1, text color 2> as an example):

this._patb_colors[type] = this.set_patb_notes("p" + type + "_colors");


Code:
// 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[i] && d[i].meta[note]) { return d[i].meta[note]; }
        }
    }
    return null;
    //
}; // Game_Battler.prototype.set_patb_notes

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



All stored type 3 notetag values are read upon use like this(Using <patb charge: scale, code> as an example):

var item = this._actions[0].item(), charge = item.meta.patb_charge;



All stored type 4 notetag values are read upon use like this(Using <operator patb reset: val> as an example):

this._subject.set_patb_reset_val(this._action.item());


Code:
Game_Battler.prototype.set_patb_reset_val = function(item) {
    var val = this._patb_reset_val;
    item.meta.patb_reset.forEach(function(reset) {
        val = this.operate_patb_notes(val, reset[0], reset[1]);
    }, this);
    this._patb_reset_val = val;
}; // Game_Battler.prototype.set_patb_reset_val
Code:
/* 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





The aforementioned functions for reading stored notetag values are Abstract Notetag Readers. Each Abstract Notetag Reader can read all concrete notetags(by taking the concrete notetag name as an argument) belonging to the same abstract notetag type addressed by that reader.


In the case of the PATB system, notetags belonging to type 1, 2 and 4 have their respective Abstract Notetag Readers, while those belonging to type 3 are so easy, simple and small that no Abstract Notetag Readers are needed.


The main function of Abstract Notetag Readers is to centralize all notetag reading functions into the same component. It can improve and/or preserve the below code qualities that are especially important in ATB system plugins(their details will be covered in II. ATB System Code Qualities) if it's applied when they're called for(which is itself too advanced to be covered here):


1. Achieves functional cohesion, by dedicating only 1 component per notetag type to read all stored notetag values belonging to the same type


2. Lowers coupling, by ensuring no components other than those Abstract Notetag Readers will ever need to care how the notetag values are read


3. Boosts performance, by isolating all resource demanding(hotspot) notetag reading codes in the same component, allowing those codes to be optimized for memory and time performance without even worrying about any other components


4. Improves robustness, by reducing the difficulties of tracing the root cause of bugs(single responsibility principle), and ensuring no Abstract Notetag Reader will ever break any other components and vice versa(separation of concerns)


5. Preserves flexibility, by making new Abstract Noteag Readers for new notetag types that needs one and can't be handled by any existing ones(but the codebase should still be kept DRY).










Summary

There are 3 dimensions of freedom for a plugin:


1. Feature set(high level), which directly defines the scope and indirectly defines the target audience of the plugin


(The feature set is of the highest level among all 3 dimensions of control and freedom, as it gives its users an overview of what this plugin's capable of as a whole)


2. Configuration coverage, which defines how detailed of each part of the feature set is


(While different parts of the feature set can have different control and freedom given to users in this dimension, the overall 2nd dimension of control and freedom given to users can still be roughly measured)


3. Configuration/notetag/plugin call/command flexibility. which defines how adaptable of of each configuration, notetag, plugin call and command is


(While different configuration, notetag, plugin call and command can have different control and freedom given to users in this dimension, the overall 3rd dimension of control and freedom given to users can still be roughly measured)


This dimension of control and freedom can further be divided into the below 7 levels -


    Level1(Hardcoded Values) - Users must use the single predefined value and can't change it by any means


    Level2(Hardcoded Choices) - Users can only choose among all the predefined values


    Level3(Editable Hardcoded Choices) - Same as Hardcoded Choices, except that those choices can be changed on the fly


    Level4(Simple Values) - Users can only use simple values as those of configurations, notetags and arguments of plugin calls and commands


    Level5(Editable Simple Values) - Same as Simple Values, except that those values can be changed on the fly


    Level6(Javascript Codes) - Users can use javascript codes as values of configurations, notetags and arguments of plugin calls and commands


    Level 7(Editable Javascript Codes) - Same as Javascript Codes, except that those codes can be changed on the fly


From my experiences and observations, any configuration/notetag/plugin call/command offering level 6 or 7 control and freedom to users can only be fully utilized by those having at least some plugin development proficiency. You should always keep the expected capabilities of your targeting audiences in mind when deciding what levels you want to reach(The trade between control and freedom given to users and user-friendliness are too advanced to be covered here).


Managing notetags involves 2 aspects - keeping them user-friendly enough on the user level, and keeping them under sufficient control on the implementation level.


Both aspects involves the control and freedom given to users by the notetags, and the latter aspect can also be dealt with using notetag types.


Regarding the control and freedom given to users by the notetags:


- In general, the higher the levels, the harder for the notetags to be managed. This applies on both the user-friendliness on the user level, and the difficulty to be controlled on the implementation level.


- In short, the setup for notetags typically just needs to do the following:


  1. Lets users know the existence of this notetag


  2. Lets users know what this notetag does


  3. Lets users know where to place this notetag


  4. Lets users know how to set the notetag values


  5. Lets users know what notetag values are valid


  6. Lets users know how to change the notetag values on the fly


  7. Lets users know that such changes can be saved or not saved


  All these should be easy, simple and small for almost all users if those notetags only offer level 5(or below) control and freedom to users, but point 4, 5 and 6 can be hard for relatively less capable ones.


Regarding notetag types:


- By categorizing concrete notetags into abstract notetag types, one can use an Abstract Notetag Reader per abstract notetag type to centralize all notetag reading functions into the same component.


- Using Abstract Notetag Readers when they're called for can improve/preserve the below code qualities that are especially important in the ATB system plugin:


  1. Achieves functional cohesion, by dedicating only 1 component per notetag type to read all stored notetag values belonging to the same type


  2. Lowers coupling, by ensuring no components other than those Abstract Notetag Readers will ever need to care how the notetag values are read


  3. Boosts performance, by isolating all resource demanding(hotspot) notetag reading codes in the same component, allowing those codes to be optimized for memory and time performance without even worrying about any other components


  4. Improves robustness, by reducing the difficulties of tracing the root cause of bugs(single responsibility principle), and ensuring no Abstract Notetag Reader will ever break any other components and vice versa(separation of concerns)


  5. Preserves flexibility, by making new Abstract Noteag Readers for new notetag types that needs one and can't be handled by any existing ones(but the codebase should still be kept DRY).
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,773
Reaction score
938
First Language
Chinese
Primarily Uses
N/A
Now let's talk about II. ATB System Code Qualities.


Note that:


- Most of these are unique to ATB system plugins. Instead, they can be applied to most plugins. The point of this reply is to show how these global knowledge can be applied to ATB system plugins.


- None of these should be treated as dogmas without thinking about them all beforehand.


Invariants

Definition



 





From Wikipedia:


- An invariant is a condition that can be relied upon to be true during execution of a program, or during some portion of it. It is a logical assertion that is held to always be true during a certain phase of execution.


In other words, there are 2 types of invariants:


Global Invariants - Assumptions that are supposed to be always 100% held across the entire codebase under any possible circumstances


Contextual Invariants - Assumptions that are supposed to be always 100% held under some situations involving some parts of the codebase


For example, the below is a Global Invariant:


- The ATB Wait Condition must be run before running the ATB Frame Update at the exact same frame


Because:


1. It must be always 100% held, otherwise the ATB Frame Update might be run at a frame before running the ATB Wait Condition at the same frame, with the latter informing that the former shouldn't run at that frame.


2. It applies to the entire codebase, as everything unique to ATB systems(except the ATB Wait Condition itself of course) must be triggered by the ATB Frame Update.


3. It applies to any possible circumstances, as it's just outright impossible for the ATB Frame Update to be always run per frame(even when messages are showing, which can lead to nontrivial bugs), and the only thing stopping the ATB Frame Update from running per frame is ATB Wait Condition.


Whereas the below is a Contextual Invariant:


- The party escape attempt must be disabled when an action invoked by or targeting a party member's executing


Because:


1. It must always be 100% held, otherwise the action invoker or target might become a hidden battler, which can sometimes crash the game


2. It only applies when the ATB Wait Condition won't be met just because of some actors being inputable, as players won't be able to input anything, including the party escape attempt, when an action's executing in that case


3. It only applies to the party escape command parts of the codebase, as it's the only part that can trigger a party escape attempt, and normally, the rest of the codebase has nothing to do with party escapes



Interinvariant Dependency

A Broken Invariant is one that isn't indeed always 100% held for its designated scopes.


For example:


- A Broken Global Invariant is one that isn't indeed always 100% held across the entire codebase under any possible circumstances.


- A Broken Contextual Invariant is one that isn't indeed always 100% held under all the situations involving all those parts of the codebase that are supposed to have it being always 100% held.


Invariant A is said to be depending on invariant B if the former will become a Broken Invariant when the latter becomes one.


The codebase is said to have Interinvariant Dependency if it's any such invariants.


For example, the below Global Invariant:


- The ATB Frame Update can only be run at a frame if the ATB Wait Condition lets it be run at the exact same frame


Depends on te below Global Invariant(Which is already mentioned):


- The ATB Wait Condition must be run before running the ATB Frame Update at the exact same frame


Because:


- The former invariant can be rephrased as The ATB Frame Update must not be run at a frame if the ATB Wait Condition doesn't let it be run at the exact same frame


- The only way for the ATB Wait Condition to stop an ATB Frame Update from being run is to return a result notifying the codebase that the latter won't be run.


- The only way for the ATB Wait Condition to return a result is to be called.


- For that result to be able to notify the codebase that the latter won't be run, that result must be informed to the codebase before the latter's run.


- But if the latter invariant becomes a Broken Invariant, the ATB Wait Condition might be run after running the ATB Frame Update at the exact same frame.


- So the codebase might not be informed by the result returned by the ATB Wait Condition before the ATB Frame Update is being run.


- It means that the codebase might not be able to stop the ATB Frame Update from being run even when the ATB Wait Condition doesn't let it be run.


Therefore the former invariant will become a Broken Invariant if the latter invariant becomes one.


On a side note: The above example implies that every ATB system always has at least 1 Interinvariant Dependencies.







Correctness

Behavioral Requirements



 





Simply put, a correct codebase is one having no nontrivial bugs, so correctness of a codebase is a measure of the number of nontrivial bugs of the codebase, and the impacts of each of them to the codebase.


From Wikipedia:


- A software bug is an error, flaw, failure or fault in a computer program or system that causes it to produce an incorrect or unexpected result, or to behave in unintended ways.


To avoid circular definitions, the definition of a nontrivial bug can be simplified as:


A nontrivial bug in a codebase is an unexpected result and/or intended behavior that has nontrivial inconsistencies with the behavioral requirements of the codebase.


Therefore, to measure the correctness of a codebase, one must first know all the behavioral requirements of that codebase.


Usually, many, albeit hardly all, of the behavioral requirements of a codebase can already be found just by reading the user configuration and documentation parts of that codebase.


For example, the below are some behavioral requirements of DoubleX RMMV Minimalized ATB(MATB; same below):



 









* @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


Code:
 *    # Configuration manipulations                                           
 *      1. $gameSystem.matb.param                                             
 *         - Returns the value of param listed in the plugin manager          
 *      2. $gameSystem.matb.param = val                                       
 *         - Sets the value of param listed in the plugin manager as val      
 *         - All $gameSystem.matb.param changes will be saved                 
 *    # Battler manipulations                                               
 *      1. matb_val                                                           
 *         - Returns the battler's atb value                                  
 *      2. reset_matb()                                             
 *         - Clears all battler's actions and resets the battler's atb value






Bug Hunting

As mentioned, correctness is a measure of both the number of nontrivial bugs and the impacts of each of those bugs.


From my experience and observation as both a plugin developer and user, even 1 serious bug generally means a lot more than tons of barely nontrivial bugs combined.


Also, applying the Pareto principle, as stated by Wikipedia, to the correctness of the codebase:


- The Pareto principle (also known as the 80–20 rule, the law of the vital few, and the principle of factor sparsity)[1] states that, for many events, roughly 80% of the effects come from 20% of the causes.


For many plugins, roughly 80% of the compromise of the behavioral requirements come from 20% of the nontrivial bugs.


On the other hand, sometimes invariant A becoming a Broken Invariant is the sole reason for the existence of bug A, and invariant B becoming a Broken Invariant is the sole reason for the existence of bug B.


If invariant B becoming a Broken Invariant is also the sole reason for invariant A to become a Broken Invariant, then fixing bug B will automatically fix bug A as well, because:


- The only way to fix bug B is to fix invariant B(by making it not a Broken Invariant anymore; same below).


- This will lead to invariant A also being fixed.


- This means that bug A will be fixed too.


This can lead to an illusion that, if bug A is an extremely serious bug but bug B is just a barely nontrivial one, then fixing bug B first will actually be more productive than fixing bug A first, as the former will directly lead to the latter, while doing the latter first can demand a lot more costs.


But luck asides, the only way to know that fixing bug B will lead to fixing bug A is to investigate the root cause of bug A. So we'll still want to focus on the known major bugs first.


Of course, it doesn't mean that barely nontrivial bugs can be safely ignored, as they can still hurt the codebase, and sometimes, they can become technical debts that can eventually lead to technical bankruptcy.


Usually, the major bugs of an ATB system comes from its core functionalities, like:


- ATB Wait Condition


- ATB Frame Update


- Battle Turn Clock


- Battler ATB Clock


Invariants also play an important role in correctness, and the below definition will come into handy:


A Nontrivial Invariant is one that, once become a Broken Invariant, will lead to nontrivial bugs.


So it's useful to hunt for all Nontrivial Invariants of a codebase and check whether any of them are Broken Invariants in order to hunt for bugs and fix them.


On a side note: All the aforementioned invariants are Nontrivial Invariants.







Robustness

Definition



 





From Wikipedia:


- In computer science, robustness is the ability of a computer system to cope with errors during execution[1][2] and cope with erroneous input.


For users, usually the most important measure of such ability is the correctness of the codebase under such cases.


In most cases, the codebase can become somehow incorrect in this manner due to some Nontrivial Invariants becoming Broken Invariants.


To generalize all such cases, a codebase will have a certain average chance for at least 1 Nontrivial Invariants to become Broken Invariants.


Also, as some Nontrivial Invariants will lead to more serious bugs than others when becoming Broken invariants, the seriousness of such impacts also has to be measured.


This lead to the below simplified measure of robustness:


The robustness of a codebase can be roughly measured by the chance of its Nontrivial Invariants to become Broken Invariants and the seriousness of the manifested nontrivial bugs.


Using this simplified measurement:


1. A 100% robust codebase is one that is always 100% correct under any possible circumstances.


2. A 0% robust codebase is one that is never 100% correct under any possible circumstances.


3. A x% robust codebase, where 0 < x < 100, is one that is always 100% correct under some circumstances but not always 100% correct under some other circumstances.


From my experience and observation:


1. It's simply unrealistic, if even possible at all, to have a 100% robustness codebase.


2. A x% robustness codebase, where 0 < x < 100, is always 100% correct under its targeted situations but not always 100% correct when there are compatibility issues with some other codebases.


3. Almost no one can accurately measure that value of x, although we can still have a vague impression of how robust a codebase is.


So to increase robustness, you'll want to refactor the codebase so that it'll need less and less Nontrivial Invariants, especially those that are likely to become Broken Invariants, those having Interinvariant Dependencies, and/or will really wreck havoc when they do, while still preserving correctness, and preferably all the other important code qualities.



Fault Isolation

While it's impractical to have a codebase that will never have any fault under any possible circumstances, we still don't want a single fault to compromise the whole codebase when it comes.


This is what fault isolation is about - To ensure a fault within a component won't propagate to any other components.


To implement fault isolation, a codebase should be so modular that it's compartmentalized into independent but interrelated components so that no single faulty component can compromise the entire codebase.


Of course, in practice, some faults in some components are simply too severe to be separated from the rest of the codebase.


For instance, if the component implementing the ATB Wait Condition is so faulty that the ATB Wait Condition is always met, the ATB Frame Update will never run, thus compromising the whole ATB system, even when the codebase is so well-written that that faulty component does nothing other than implementing the ATB Wait Condition, no other component does the same job, and the only effect to the other component is whether the ATB Frame Update will be run.


These are some example faults in some example components that can compromise the whole ATB system:


Component implementing ATB Wait Condition


- The ATB Wait Condition is always met, leading to the ATB Frame Update never being run


- The ATB Wait Condition is always not met, leading to crashes when displaying messages


Component implementing ATB Frame Update


- No battler's Battler ATB Clock are ever updated, leading to no battler being able to do anything


Component implementing Battler ATB Clock


- The Battler ATB Clock is always reset to be completely empty, leading to no battler being able to do anything


Therefore, it's useful to mark all such possible faults in all such potential components, and keep a close eye to them, in order to prevent them from being real issues and fix them immediately in case they do.


On a side note: In general, the more InterInvariant Dependencies a codebase have, the less able it can perform fault isolation, as such codebases can have a chain of Broken Invariants with just 1 of them due to the single original fault.







Performance

Memory And Time



 





When talking about code performance, it's mainly about memory usage and time spent for executing some operations.


1 using tool to measure code performance is the big O notation.


Some ways to come up with less performant algorithms demonstrated some usages of the big O notation, and you're highly encouraged and recommended to read that



In RMMV, what most users care about is the time performance, which is directly illustrated by the average and minimum fps.


What we, as RMMV plugin developers, want to void, is writing plugins leading to constant severe lag and fps drop, even on machines barely meeting the RMMV system requirements.


On the other hand, the RMMV minimum requirements on RAM is 2GB, which is quite abundant for most RMMV plugin developers.


At least, from my experience and observation, an ATB system hardly demands so many RAM to impose nontrivial memory usage issues.


So I'll focus mainly on time consumption rather then memory usage when talking about code performance, although the latter won't be completely ignored.



FPS Meter

When optimizing for time performance, we've to measure it, and a meter showing the FPS and/or the number of ms spent on executing operations at a frame will come into handy.


Fortunately, RMMV already has a built in FPS meter, which can be shown by pressing F2, or some other keys doing the same thing(usually implemented by some plugins).


We also know it has 2 modes, with 1 showing the FPS, and 1 showing the number of ms spent on executing operations at a frame.


As RMMV is hardcoded to have 60 FPS(unless the load is too heavy to even achieve this), the conversion from the ms mode to the FPS mode is this:


Number of FPS = 1000 / Math.max(16.666...6, number of ms spent on executing operations at a frame)


On the other hand, when the number of FPS shown by the FPS mode is 60, you won't know the exact number of ms spent on executing operations at a frame, you just know that it must be smaller than 17.


So I personally find the ms mode more useful when measuring the plugin time performance, even although the FPS mode is what most users care more.



Hotspots

From Wikipedia:


- A hot spot in computer science is most usually defined as a region of a computer program where a high proportion of executed instructions occur or where most time is spent during the program's execution (not necessarily the same thing since some instructions are faster than others).


As few, if any at all, operations unique to ATB systems will demand such a large amount of resources that, even being executed sparsely(absolutely/relatively), will still have a significant impact on code performance, the below simplified definition can be applied to ATB systems instead:


A hotspot in ATB system plugins is a section of code that is run extremely frequently.


From my experience and observation, under this definition, a simplified criterion can be used to judge whether a section of code is a hotspot:


If a section of code is run at least nearly once per frame on average, then it's a hotspot; If a section of code is run no where close by once per frame on average, then it's not a hotspot.


For instance, the below sections of codes are most likely hotspots:


- ATB Wait Condition


- ATB Frame Update


- Battle Turn Clock


- Battler ATB Clock


Sometimes though, a section of code can run extremely frequently or sparsely on average, and both behaviors can be considered normal and/or common, depending on how the users use the ATB system plugins. This leads to the below definition:


A Potential Hotspot is a section of code that can become a hotspot or not a hotspot depending on normal and/or common use cases.


For instance, Game_Battler.prototype.set_patb_colors is marked as a Potential Hotspot(PATB Core v101a):



 











// 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



It's because it'll only be called when Game_Battler.prototype.are_patb_battler_changed returns true with the color notetag change notification flags raised(PATB Core v101a; check Change Notification Flags for this):

/*----------------------------------------------------------------------------
* 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



Game_Battler.prototype._patb_battler_change won't return true frequently, and Game_Battler.prototype._patb_note_change will only return true if users use this plugin call(PATB Core v101a):

* 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



So Game_Battler.prototype.set_patb_colors will only become a hotspot if the above plugin calls are used extremely frequently, meaning that Game_Battler.prototype.set_patb_colors is a Potential Hotspot.


The point of marking functions as hotspots and/or Potential Hotspots is to know which functions should optimize for code performance.


You may feel/think that all functions should be optimized, perhaps maximally, for code performance, as it's extremely important for the user experience, especially for ATB systems which demands exceptionally large amount of computing resources.


While it's true in most cases, it's also quite hard to optimize for code performance without sacrificing any other important code qualities, so there's a need to balance and prioritize them(which is too advanced to be covered here), as illustrated by some well-written plugins. So we'll want to know which sections of code really warrants performance optimizations, and usually most hotspots/Potential Hotspots do.






Reasons To Change

Consider Game_Battler.prototype.update_max_patb_val, which is a hotspot(PATB Core v101a):



 











/*----------------------------------------------------------------------------
* 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");
// Multiplies the difference by 10000 to work with very small max atb value
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



It checks if the Maximum ATB Value change notification flag's raised, and reevaluates the Maximum ATB Value if it does.


As DoubleX RMMV Popularized ATB Core(PATB Core; same below) lets users change the Maximum ATB Value per frame, you might think that the plugin should simply reevaluate it per frame to ensure 100% correctness.


However, this could lead to a noticeable, if not significant, constant lag and fps drop, especially if users uses dozens, or even hundreds, of <operator max patb val: val> notetags, as shown by Game_Battler.prototype.set_multi_patb_notes(PATB Core v101a):

/* 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



Where Game_Battler.prototype.patb_note_data is this(PATB Core v101a):

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



And Game_Battler.prototype.operate_patb_notes is this(PATB Core v101a):

/* 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





Instead, we should realize that every change of everything has some reasons behind it, and these reasons are called Reasons To Change.


In this example, there are 3 Reasons To Change:


1. The value of $gameSystem.patb.max_atb_val has changed


2. The effective <operator max patb val: val> notetag list has changed(changed some notetag orderings, added some new notetags, removed some existing notetags)


3. The value of some effective <operator max patb val: val> notetags has changed


As both 1st and 3rd one must be triggered by plugin calls, they can be grouped as 1 Reason To Change: The users have used relevant plugin calls.


On the other hand, the 2nd one is typically triggered by equip/state(with these notetags) add/removal and/or class change/enemy transform, although users can still use plugin calls to explicitly add new notetags to/remove existing notetags to some database items(actor/class/equip/state/enemy). So the 2nd Reason To Change has to be checked by the plugin implementation itself.


The 1st and 3rd Reasons To Change will be reflected by the Game_Battler.prototype._patb_note_change part of the Maximum ATB Value change notification flag, while the 2nd one will be reflected by Game_Battler.prototype._patb_battler_change part as well.


While Game_Battler.prototype._patb_note_change is directly raised by plugin calls meaning it's a deterministic indication that a Reason To Change is definitely triggered, Game_Battler.prototype._patb_battler_change is raised when Game_Battler.prototype.set_patb_refresh, which is called by Game_Battler.prototype.refresh(and some other functions as well), is called(PATB Core v101a):

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



When the 2nd Reason To Change is triggered, Game_Battler.prototype.set_patb_refresh will definitely be called.


On the other hand, just because Game_Battler.prototype.set_patb_refresh is called doesn't mean the 2nd Reason To Change is triggered.


So the use of Game_Battler.prototype.set_patb_refresh is this:


- When it's called, a Reason To Change might be triggered; When it's not called, a Reason To Change is definitely not triggered.


This leads to the below definitions:


A Deterministic Reasons To Change Detector is a component that can accurately tell whether all Reasons To Change it's monitoring have triggered or not triggered.


A Probabilistic Reasons To Change Detector is a component that can accurate tell that all Reasons To Changes it's monitoring have not triggered but can't accurately tell that they've triggered.


At the 1st glance a Deterministic Reasons To Change Detector seems to be more useful than a Probabilistic Reasons To Change Detector.


In some cases, however, the latter's more suitable, as sometimes just 1 of the latter can already replace several, and sometimes even dozens, of the former.


For example, Game_Battler.prototype.set_patb_refresh is a Probabilistic Reasons To Change Detector that can detect all Reasons To Change the effective <operator max patb val: val> notetag list.


If it's to be replaced by Deterministic Reasons To Change Detectors, the below ones will be needed:


1. Actor class change detector - Detecting whether the effective <operator max patb val: val> notetag list is indeed changed due to a class change, by checking the effective <operator max patb val: val> notetag list of the old class against that of the new class


2. Actor equip change detector - Detecting whether the effective <operator max patb val: val> notetag list is indeed changed due to an equip change, by checking the effective <operator max patb val: val> notetag list of the old equip against that of the new equip


3. Battler state change detector - Detecting whether the effective <operator max patb val: val> notetag list is indeed changed due to a state change, by checking the effective <operator max patb val: val> notetag list of the old state against that of the new state


4. Enemy transformation detector - Detecting whether the effective <operator max patb val: val> notetag list is indeed changed due to the enemy transformation, by checking the effective <operator max patb val: val> notetag list of the old form against that of the new form


Obviously, it'd lead to much more complicated and convoluted codes, and can even be less performant than the original Probabilistic Reasons To Change Detector, especially when those effect <operator max patb val: val> notetag lists are long.


On a side note: Without comparing the old and new effective <operator max patb val: val> notetag list, those Deterministic Reasons To Change Detectors will become Probabilistic Reasons To Change Detectors, which are only marginally more performant than the original one but will lead to much more codes.


If a single Deterministic Reasons To Change Detector is used on Game_Battler.prototype.set_patb_refresh, it will be checking the whole old effective <operator max patb val: val> notetag list against the whole new one, which is likely so unperformant that doing so will probably defeat the purpose of using change notification flags in the first place.


Therefore, in this case, I'll prefer to use a single Probabilistic Reasons To Change Detector to have a better balance between code performance and simplicity(balancing and prioritizing important code qualities are too advanced to be covered here).






Applying Nontrivial Invariants

Recalling the aforementioned Maximum ATB Value example, there are 3 Reasons To Change the Maximum ATB Value:


1. The value of $gameSystem.patb.max_atb_val has changed


2. The effective <operator max patb val: val> notetag list has changed(changed some notetag orderings, added some new notetags, removed some existing notetags)


3. The value of some effective <operator max patb val: val> notetags has changed


If one can't be absolutely sure that there won't be any other Reasons To Change, one won't be sure that the whole Maximum ATB Value change notification flag will never be not raised even when it's to be raised. If such cases do happen, the codebase will have nontrivial bugs.


It means that to use this performance optimization trick in this example, one has to treat the below as a Nontrivial Invariant:


- The aforementioned 3 Reasons To Change will always be the only Reasons To Change the Maximum ATB Value.


In general, any performance optimization using Reasons To Change will have to ensure that "Those Reasons To Change will always be the only Reasons To Change" is a Nontrivial Invariant.


But as mentioned in Robustness, adding Nontrivial Invariants will make a codebase less robust, unless those Nontrivial Invariants will indeed never become Broken Invariants under any possible circumstances, which is clearly nearly impossible just by considering all the possible compatibility issues(provided that one doesn't ignore them).


So in essence, such performance optimization tricks are actually trading robustness for performance. How to make such trades are too advanced to be covered here.


In practice, new Reasons To Change mainly come from a plugin interacting with some other plugins, and these new Reasons To Change are some of the causes of compatibility issues among these plugins. Therefore it's desirable to implement the performance optimizations in ways that it's extremely unlikely to have new Reasons To Change under almost all circumstances.


For instance, if, for some reasons, a battler changes class/equip/state/transforms without calling Game_Battler.prototype.refresh, due to the interaction of some other plugins, the optimization tricks used on Maximum ATB Value will lead to bugs. As this should never happen, or be at least extremely rare(at least with a decent plugin having decent code qualities; it's determined by experience and observation), this source for having new Reasons To Change should be unlikely enough to not to be a reason to stop using the optimization trick.



Change Notification Flags

The application of change notification flags on optimizing code performance is to inform hotspots/Potential Hotspots that some resource demanding operations has/might need to be executed. Those operations will only be executed when the corresponding change notification flags are raised.


So the relationship between Reasons To Change and change notification flags should now be 100% crystal clear and obvious to you:


A change notification flag will be raised when a Reasons To Change has indeed triggered or might be triggered.


The former cases are reported by Deterministic Reasons To Change Detectors and the latter cases are reported by Probabilistic Reasons To Change Detectors.


To sum up, the goal of change notification flags is to ensure all resource demanding operations executed by hotspots/Potential Hotspots will be called if and only if at least 1 corresponding Reasons To Change might be triggered.


Using the Notifications of this forum as an analogy:


- If this function didn't exist, members would have to periodically check for all topics they wish to follow in order to know whether new replies are added.


- With this function, members don't have to do so, as such events will be notified by this function, thus saving members tons of resources(effort, time, etc).


To ensure the Notifications of this forum to work correctly for each user:


- Each member has to have his/her/its own separate instance of the Notifications, so that no such instance will depend on any other such instance.


- If the Notifications of this forum doesn't work this way, but rather only having a single instance shared by all members, then after that instance is checked by a member for the 1st time, it'll be reset to be the state indicating there are no new replies, leading to subsequent members using that instance falsely thinking that there are no new replies.


Applying the above analogy to the change notification flags:


- Every Reason To Change is analogous to a topic in this forum, so every Reason To Change needs its own change notification flag, unless the members concerned only cares whether there are any new replies from any of their followed topics, in which just 1 change notification flag will suffice.


- Every component being affected by a Reason To Change is analogous a member following a topic in this forum, so every component being affected by a Reason To Change needs its own separate instance of the change notification flag raised by that Reason To Change.


Going back to DoubleX RMMV Popularized ATB(PATB; same below), almost all notetags needing change notification flags have the below 2 Reasons To Change:


1. The effective notetag list have changed


2. Relevant plugin calls are used


The 1st one is reflected by the change notification flag Game_Battler.prototype._patb_battler_change and the 2nd one is recflected by the change notification flag Game_Battler.prototype._patb_note_change.


Each notetag having these 2 Reasons To Change will have a separate change notification flag instance for each Reason To Change. For instance:


- The Maximum ATB Value change notification flag instances are Game_Battler.prototype._patb_battler_change.max_patb_val and Game_Battler.prototype._patb_noter_change.max_patb_val


- The ATB bar color change notification flag instances are Game_Battler.prototype._patb_battler_change.atb_color and Game_Battler.prototype._patb_noter_change.atb_color


- The ATB bar overlay color change notification flag instances are Game_Battler.prototype._patb_battler_change.atb_overlay_color and Game_Battler.prototype._patb_noter_change.atb_overlay_color


However, as those components only care whether any of the Reasons To Change might be triggered, those 2 change notification flags for each component can actually be combined. I didn't do this as I feel/think that the existing design is more readable :p







Coupling

From Wikipedia:


- In software engineering, coupling is the manner and degree of interdependence between software modules; a measure of how closely connected two routines or modules are;[1] the strength of the relationships between modules.


That site also lists the below types of coupling(its application to the higher levels, like the plugin level, is too advanced to be covered here):


Content Coupling



 





From Wikipedia:


- Content coupling (also known as Pathological coupling) occurs when one module modifies or relies on the internal workings of another module (e.g., accessing local data of another module). Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module.


For example, if the component implementing the ATB Frame Update also implements every last details of the Battler ATB Clock, then content coupling exists between ATB Frame Update and Battler ATB Clock, as the former directly determines the content of the latter.


The relevent codes can be something like this:



 












BattleManager.runAtbFrameUpdate = function() {
this.someUnrelatedCodes();
var allMovableMembers = $gameParty.movableMembers(), index, lastAtbVal;
allMovableMembers = allMovableMembers.concat($gameTroop.movableMembers());
// Directly implements every last detail of the Battler ATB Clock
allMovableMembers.forEach(function(mem) {
if (mem.battlerATBClock >= this.maxATBVal) { return; }
mem.battlerATBClock += mem.agi * this.maxATBVal / 60 / this.atbFillTime;
if (mem.battlerATBClock < this.maxATBVal) { return; }
mem.battlerATBClock = this.maxATBVal;
mem.makeActions();
}, this);
// Content Coupling
this.someOtherUnrelatedCodes();
}; // BattleManager.runAtbFrameUpdate



Note that the name of this function, BattleManager.runAtbFrameUpdate, doesn't indicate that it also implements the Battler ATB Clock.


Only those having a thorough comprehension of ATB systems can immediately grasp what BattleManager.atbFrameUpdate is really doing, and the Battler ATB Clock is actually implemented by the component implmenting the ATB Frame Update. For those not being that capable yet, changing the component implmenting the ATB Frame Update can unknowingly change the implementation of the Battler ATB Clock as well.


This type of coupling is generally considered to be 1 of the most destructive antipatterns as it manifests an extremely high and the highest coupling.






Common Coupling

From Wikipedia:


- Common coupling (also known as Global coupling) occurs when two modules share the same global data (e.g., a global variable). Changing the shared resource implies changing all the modules using it.


For example, if more than 1 components are using the same change notification flag instance(as mentioned in Change Notification Flags), then common coupling exists among all these components, as that single change notification flag instance is read from and written by all components using it.


This type of coupling is generally considered to be a severe antipattern as it manifests a lower coupling than content coupling but still quite a high coupling.



External Coupling

From Wikipedia:


- External coupling occurs when two modules share an externally imposed data format, communication protocol, or device interface. This is basically related to the communication to external tools and devices.


For example, if more than 1 notetags must always use the same particular Abstract Notetag Reader, then external coupling exists among all components using these notetags, as changing that Abstract Notetag Reader can change the final values of those notetags which is used by those components.


On a side note: In practice, there's no need to restirct a notetag to always use the same Abstract Notetag Reader. When that Abstract Notetag Reader is no longer appropriate for that notetag, a different/new Abstract Notetag Reader can be used by that notetag instead. If this lead to some Abstract Notetag Readers to be no longer used by more than 1 notetags, then simply throw away those Abstract Notetag Readers.


This type of coupling is generally considered to be a moderate antipattern as it manifests a lower coupling then common coupling but still slightly high coupling.



Control Coupling

From Wikipedia:


- Control coupling is one module controlling the flow of another, by passing it information on what to do (e.g., passing a what-to-do flag).


For example, control coupling exists among Game_Battler.prototype.reset_patb and its callers(PATB Core v100a):



 












/* 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.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



Because its callers are passing reset to it informing whether the Battler ATB Clock needs to be reset as well.


If the meaning of reset is reserved in Game_Battler.prototype.reset_patb, then the reset argument of all its caller will have to be reversed as well.


This type of coupling is generally considered to be a severe code smell as it manifests a medium coupling but a lower coupling then external coupling.






Stamp Coupling

From Wikipedia:


- Stamp coupling occurs when modules share a composite data structure and use only a part of it, possibly a different part (e.g., passing a whole record to a function that only needs one field of it). This may lead to changing the way a module reads a record because a field that the module does not need has been modified.


For example, stamp coupling exists among Window_BattleStatus.prototype.draw_actor_matb and its callers(MATB v100a):



 












/* 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



Because only the actor's Battler ATB Clock, but rather than the whole actor, is used.


If the variable storing the Battler ATB Clock changes, Window_BattleStatus.prototype.draw_actor_matb will also have to change.


This type of coupling is generally considered to be a moderate code smell as it manifests a slightly low coupling and a lower coupling then control coupling.






Data Coupling

From Wikipedia:


- Data coupling occurs when modules share data through, for example, parameters. Each datum is an elementary piece, and these are the only data shared (e.g., passing an integer to a function that computes a square root).


For example, data coupling exists among Game_Battler.prototype.are_patb_battler_changed and its callers(PATB v101a):



 












/*----------------------------------------------------------------------------
 *    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



Because note is just a String, which is an elementary piece of data in Javascript ES5.


Sometimes it's necessary to pass simple data as arguments. In this case, that's the only way for this function to know which change notification flag's queried without inducing higher coupling(and reset afterwards).


This type of coupling is generally considered to be acceptable and sometimes necessary, as it manifests quite a low coupling and a lower coupling then stamp coupling.






Message Coupling

From Wikipedia:


- This is the loosest type of coupling. It can be achieved by state decentralization (as in objects) and component communication is done via parameters or message passing (see Message passing).


For example, message coupling exists among BattleManager.start_matb_battle and its callers(MATB v100a):



 












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



Because its callers just need to know its function name(signature).


This type of coupling is generally considered to be ideal although not always possible as it manifests an extremely low coupling and the lowest coupling as long as coupling's inevitable.






No coupling

From Wikipedia:


- Modules do not communicate at all with one another.


For example, no coupling exists between BattleManager.start_matb_battle and BattleManager.can_update_matb(MATB v100a):



 












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

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



Because the former's only called before the battle's completely ready to start, while the latter's only called after the battle's started, and neither needs to know anything about the other.


This type of coupling is generally considered to be the best, but only when it's possible and indeed called for, as sometimes some components are inherently coupled(as illustrated in III. ATB System Addons).






Temporal coupling

Simply put, temporal coupling exists among some components when those components must be called under some particular timing/ordering rules.


For example, this already mentioned Nontrivial Invariant demonstrates that temporal coupling exists between ATB Wait Condition and ATB Frame Update:


- The ATB Wait Condition must be run before running the ATB Frame Update at the exact same frame


In practice, temporal coupling can be further divided into Explicit Temporal Coupling and Implicit Temporal Coupling:


- Explicit Temporal Coupling is a temportal coupling where those particular timing/ordering rules are extremely crystal clear and obvious.


- Implicit Temporal Coupling is a temporal coupling where those particular timing/ordering rules are extremely elusive.


Clearly, whether a temporal coupling's a Explicit Temporal Coupling or Implicit Temporal Coupling is relative to the codebase readers.


For instance, the above Nontrivial Invariant probably should exhibit an Explicit Temporal Coupling for those having at least a Basic knowledge on writing ATB(Active Time Battle) system plugins, but will probably exhibit an Implicit Temporal Coupling for some of the others.


However, that doesn't mean that categorization's 100% subjective. For instance, if an ATB system plugin codebase explicitly demands its readers to have the aforementioned capability then the above Nontrivial Invariant in that codebase can be objectively judging as exhibiting an Explicit Temporal Coupling, as it's just ATB system 101 for them.


On a side note: The above Nontrivial Invariant implies that some Explicit Temporal Coupling are inevitable for any ATB system plugin. However, we can still stirve for eliminating all Implicit Temporal Coupling for those meeting the capability requirements for codebase readers stated by the codebase.







Cohesion

From Wikipedia:


- In computer programming, cohesion refers to the degree to which the elements of a module belong together.[1] Thus, cohesion measures the strength of relationship between pieces of functionality within a given module.


That site also lists the below types of cohesion(its application to the higher levels, like the plugin level, is too advanced to be covered here):


Coincidental Cohesion



 





From Wikipedia:


- Coincidental cohesion is when parts of a module are grouped arbitrarily; the only relationship between the parts is that they have been grouped together (e.g. a “Utilities” class).


For example, if the component implementing the Battler ATB Clock also implements every last detail of the ATB bar colors, then this component will exhibit coincidental cohesion, as the only remote commonality they have is that they're both battler functionalities, and this commonality is so meaningless, pointless and useless that it doesn't even warrant a logical cohesion(check Logical Cohesion for details).


The relevent codes can be something like this:



 













Game_Battler.prototype.updateBattlerATBClock = function() {
var maxVal = BattleManager.maxATBVal;
if (this.battlerATBClock < maxVal) {
this.battlerATBClock += this.agi * maxVal / 60 / this.atbFillTime;
if (this.battlerATBClock >= maxVal) {
this.battlerATBClock = maxVal;
this.makeActions();
}
}
// Implements every last detail of the ATB bar colors here as well
this.atbBarColor = this.abstractNotetagReaderType1("atbBarColor");
// Coincidental Cohesion
}; // Game_Battler.prototype.updateBattlerATBClock



Note that the function name, Game_Battler.prototype.updateBattlerATBClock, doesn't indicate that it also updates the ATB bar color.


To accurately describe its functionalities, its name probably needs to be changed as something like:


- Game_Battler.prototype.updateBattlerATBClockAndATBBarColor


- Game_Battler.prototype.updateSomeBattlerData


- Game_Battler.prototype.updateAllBattlerData


On a side note: The above function also indicates that content coupling exists between the Battler ATB Clock and ATB bar color(check Content Coupling).


Other than the 100% crystal clear and obvious effect that even those having a thorough comprehension on writing ATB system plugins will likely be utterly confused by this function at the 1st glance, another immediately noticeable problem of this function is that, if the plugin currently/later supports hiding the battler ATB bars, then each of those battlers not showing their ATB bars will be doing a noticeable amount of redundant works per frame, due to the fact that those results won't be even used at all.


The Battler ATB Clock and ATB bar colors have to be implemented by 2 separate components in order to solve this problem, meaning that that function can no longer have coincidental cohesion.


This type of cohesion is generally considered to be 1 of the most destructive antipatterns as it manifests an extremely bad and the worst cohesion.






Logical Cohesion

From Wikipedia:


- Logical cohesion is when parts of a module are grouped because they are logically categorized to do the same thing even though they are different by nature (e.g. grouping all mouse and keyboard input handling routines).


For example, suppose an ATB system lets users set some skills/items to need to be charged(wait for a while with the battler not being able to do anything)before being executed, and some others to need their users to have to cooldown(wait for a while with the battler not being able to do anything)after using them but before refilling the atb bars(These 2 addons will be covered in III. ATB System Addons).


If the Battler ATB Clock for all Refilling ATB, Charging ATB, and Cooling Down ATB phases(These latter 2 phases will be covered in III. ATB System Addons) are implemented in a single component and the implementation of all those 3 phases are independent of each other, then that component will exhibit logical cohesion, as while they're all Battler ATB Clocks, their nature are significantly different from each other, and a battler can only be in exact 1 of the Refilling ATB, Charging ATB and Cooling Down ATB phase when updating the Battler ATB Clock, meaning that only exactly 1 of the 3 implementations will be used per Battler ATB Clock component call.


The relevent codes can be something like this:



 













Game_Battler.prototype.updateBattlerATBClock = function(type) {
// Implements all the 3 Battler ATB Clock types in the same function
var maxVal = BattleManager.maxATBVal;
var rate = this.agi * maxVal / 60 / this.atbFillTime;
if (type === "atb") {
if (this.battlerATBClock < maxVal) {
this.battlerATBClock += rate;
if (this.battlerATBClock >= maxVal) {
this.battlerATBClock = maxVal;
this.makeActions();
}
}
} else if (type === "charge") {
if (this.battlerChargeClock < maxVal) {
rate *= this.currentAction().item().meta.chargeRate;
this.battlerChargeClock += rate;
if (this.battlerChargeClock >= maxVal) {
this.battlerChargeClock = maxVal;
}
}
} else if (type === "cooldown") {
if (this.battlerCooldownClock < maxVal) {
rate *= this.currentAction().item().meta.cooldownRate;
this.battlerCooldownClock -= rate;
if (this.battlerCooldownClock <= 0) {
this.battlerCooldownClock = 0;
}
}
} else {
console.log("Invalid type " + type + "passed to Game_Battler.prototype.updateBattlerATBClock");
}
// Logical Cohesion
}; // Game_Battler.prototype.updateBattlerATBClock



On a side note: Control coupling also exists among the above function and its callers(check Control Coupling).


It should be 100% crystal clear and obvious to those experienced with compatibility issues that, if even just a small part of the implementation of only 1 of the 3 Battler ATB Clock type has to undergo a minor change, the whole function will have to be rewritten, which can eventually lead to compatibility nightmares.


To avoid this, the implementations of the 3 Battler ATB Clock types have to be separated into 3 independent components, so that editing the implementation of 1 component won't affect the compatibility of any other components, meaning that this function can no longer have logical cohesion.


This type of cohesion is generally considered to be a severe antipattern as it manifests a better cohesion than coincidental cohesion but still quite a bad cohesion.






Temporal Cohesion

From Wikipedia:


- Temporal cohesion is when parts of a module are grouped by when they are processed - the parts are processed at a particular time in program execution (e.g. a function which is called after catching an exception which closes open files, creates an error log, and notifies the user).


For example, suppose an ATB system plugin supports 2 ATB reset modes - the one just cancelling the charging skill/item, and the one resetting the ATB value to be completely empty as well.


If both cancelling the charging skill/item and resetting the ATB value to be completely empty are bundled into the same function, then that function will have temporal cohesion, as they're actually 2 different functionalities although they must be called at the same time for the latter ATB reset mode.


The relevent codes can be something like this:



 













Game_Battler.prototype.resetBattlerATBClock = function() {
// Implements both reset functionalities in the same function
this.battlerATBClock = 0;
this.clearActions();
var index = BattleManager.action_battlers.indexOf(this);
if (index >= 0) { BattleManager.action_battlers.splice(index, 1); }
if (BattleManager.actor() === this) { BattleManager.clearActor(); }
// Temporal Cohesion
}; // Game_Battler.prototype.resetBattlerATBClock



It should be 100% crystal clear and obvious that the 1st ATB reset mode can't use this function due to the presence of the 1st line, but could use it without that line. Such practices can eventually lead to a wet(write everything twice) codebase.


To dry up the codebase, either the 1st line should become a new function, or moved directly to the function triggering the 1st ATB reset mode, meaning that the function can no longer have temporal cohesion.


This type of cohesion is generally considered to be a moderate antipattern as it manifests a better cohesion than logical cohesion but still a slightly bad cohesion.






Procedural Cohesion

From Wikipedia:


Procedural cohesion is when parts of a module are grouped because they always follow a certain sequence of execution (e.g. a function which checks file permissions and then opens the file).


For example, the example function in Content Coupling also exhibits procedural cohesion, as the ATB Frame Update must (perhaps immediately)update the Battler ATB Clock.


An immediate problem is that, the component implementing the ATB Frame Update does/knows too much, which is a direct result of breaking encapsulation.


This can be solved by implementing the Battler ATB Clock in a separate component instead, meaning that the function can no longer have procedural cohesion.


This type of cohesion is generally considered to be a severe code smell as it manifests a better cohesion than temporal cohesion and a moderate cohesion.



Communicational Cohesion

From Wikipedia:


- Communicational cohesion is when parts of a module are grouped because they operate on the same data (e.g. a module which operates on the same record of information).


For example, any decent ATB system won't redraw the whole status window per ATB Frame Update just to ensure all the ATB bars are up to date. Instead, a new function just redrawing the ATB bars will be called per ATB Frame Update instead.


The function implementing the ATB bar redraws, with the aforementioned checkig, can be something like this:



 













Window_BattleStatus.prototype.refreshATBBars = function() { // New; Hotspot
var barText = this.getATBBarText();
var color0 = this.getATBColor0(), color1 = this.getATBColor1();
var actor, ox = this.getATBBarOx(), rect, x, y, w = this.getATBBarW();
for (var index = 0, max = this.maxItems(); index < max; index++) {
actor = $gameParty.battleMembers()[index];
rect = this.gaugeAreaRect(index);
x = rect.x;
y = rect.y;
this.drawGauge(x, y, w, actor.atbFillRatio(), color_0, color_1);
this.changeTextColor(this.systemColor());
this.drawText(barText, x, y, this.textWidth(barText));
}
}; // Window_BattleStatus.prototype.refreshATBBars



Note that only the codes inside the for loop are actually redrawing the actor ATB bars.


These codes can actually be reused by Window_BattleStatus.prototype.drawGaugeAreaWithTp and Window_BattleStatus.prototype.drawGaugeAreaWithoutTp, which is called when the whole status window's redrawn, which will also redraw the ATB bars, which are parts of the status window.


So to dry up the codebase, the aforementioned section of codes should be inside a separate function which is called by both Window_BattleStatus.prototype.refreshATBBars and functions redrawing the whole status window, meaning that Window_BattleStatus.prototype.refreshATBBars can no longer have communication cohesion.


This type of cohesion is generally considered to be a moderate code smell as it manifests a better cohesion than procedural cohesion and a slightly good cohesion.






Sequential Cohesion

From Wikipedia:


- Sequential cohesion is when parts of a module are grouped because the output from one part is the input to another part like an assembly line (e.g. a function which reads data from a file and processes the data).


For example, suppose an ATB system plugin lets users to set some skills/items to need to be charged before being executed, with the charge rate based on the Battler ATB Rate.


If both the Battler ATB Rate and charge rate are implemented in the same component, that component will exhibit sequential cohesion, as the evaluated Battler ATB Rate from the former implementation is immediately passed to the latter implementation to evaluate the charge rate.


The function implementing both functionalities can be something like this:



 













Game_Battler.prototype.setATBRates = function() {
// Both the Battler ATB Rate and charge rate are evaluated here
this.battlerATBRate = this.abstractNotetagReaderType2("battlerATBRate");
if (this.currentAction() && this.currentAction().item()) {
this.battlerChargeRate = this.battlerATBRate * this.currentAction().item().meta.chargeRate;
}
// Sequential Cohesion
} // Game_Battler.prototype.setATBRates



While it's okay in many cases, it can still be somehow undesirable if the plugin also lets users set whether the charge rate will be based on the Battler ATB Rate.


To solve this, this function will have to be broken down into 2 separate functions, with 1 implementing the Battler ATB Rate and the other implementing the charge rate, meaning that it can no longer have sequential cohesion.


This type of cohesion is generally considered to be acceptable as it manifests a better cohesion than communicational cohesion and quite a good cohesion.






Functional Cohesion

From Wikipedia:


- Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module (e.g. Lexical analysis of an XML string).


For example, BattleManager.start_matb_battle exhibits functional cohesion(MATB v100a):



 













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



It's because it just does a single well-defined task - Sets the Starting ATB Value for all battlers upon battle start.


This type of cohesion is generally considered to be ideal as it manifests a better cohesion than sequential cohesion and the best cohesion.










I wanted to write the rest of II. ATB System Code Qualities, but as it'd lead to this reply exceeding the 10000 word limit, and I've already spent 12 hours writing this in a single shot, I've to write the rest in an upcoming reply :guffaw:
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,773
Reaction score
938
First Language
Chinese
Primarily Uses
N/A
Now let's continue the rest of II. ATB System Code Qualities



Again, note that:


- Most of these are unique to ATB system plugins. Instead, they can be applied to most plugins. The point of this reply is to show how these global knowledge can be applied to ATB system plugins.


- None of these should be treated as dogmas without thinking about them all beforehand.


Flexibility

Simply put, flexibility is a measure of how adaptable a plugin's to various situations, including those far from its targeting scopes.


For users, it generally means the ability of a plugin's configurations to suit a wide range of their demands, including those very project-specific, and the compatibility of a plugin with some other plugins.


For a larger system consisting of many component, each of them warranting a separate plugin, flexibility also means the freedom of only picking the ones really needed(it's rather undesirable to have to use addon A just because addon B needs it and addon A isn't actually what's wanted), and the lack of plugin ordering restrictions.


For plugin developers, flexibility normally means how extensible(we don't want to have to rewrite a large portion of the codebase just to add 1 new feature) and scalable a codebase is(sometimes up to nearly 10000 LoC when the plugin becomes an advanced complex plugin).


Configuration control and freedom are already covered in I. ATB System Configuration Management, compatibility is out of the scope of this topic(as it's too case-specific and many ATB system compatibility issues aren't special ones needing any extraordinary care), and Addon Ordering Rules and Interaddon Dependencies will be covered in III. ATB System Addons. So now I'll focus on the extensibility and scalability of a codebase.


Extensibility



 





From Wikipedia:


- In software engineering, extensibility (not to be confused with forward compatibility) is a system design principle where the implementation takes future growth into consideration. It is a systemic measure of the ability to extend a system and the level of effort required to implement the extension.


An immediate application of extensibility to ATB system is writing new ATB system addons for ATB systems using the Core Addon Approach or writing new ATB system components for ATB systems using the Single Plugin Approach.


As a counterexample, back in RMVXA, when I was writing addons to the awesome Yami's CATB, I ended up having to rewrite a large portion of the color addon just to add the new cooldown addon.


Clearly, these 2 addons should be at least nearly totally independent from each other, so at most minor tweaks to the former is acceptable.


In this case, it means that I've screwed up the godsend ATB system into an utter mess, by making it less and less extensible, eventually to the point that I'm facing tons of weird bugs when I try to add the CTB addon to CATB lol


Fortunately, I've learned the lesson from my experience working with CATB and applied that lesson when writing PATB.


For instance, the implementation of the Abstract Notetag Readers is quite extensible(PATB v101a):



 








/* 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

/* 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



Using it can be as simple as this(PATB v101a):

this._max_patb_val = this.set_multi_patb_notes(max, "max_patb_val");



If the Maximum ATB Value notetags are changed to become no longer appropriate to use the aforementioned Abstract Notetag Reader, a new one can be used instead.


If a new notetag, say, Battler ATB Rate notetags are added into the play, and it can use the same Abstract Notetag Reader, then the new codes can be as simple as this(PATB Rate v100b):

this._patb_rate.atb = this.set_multi_patb_notes(rate, "patb_rate");



It's because each notetag just need to know which Abstract Notetag Reader matches its needs, and no Abstract Notetag Reader even needs to care about its callers.


As another example, the PATB CTB addon also demonstrates quite an extensible design in the ATB Frame Update implementation in the PATB Core(PATB CTB v100b):

/*----------------------------------------------------------------------------
* 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



It's because the entire ATB Frame Update implementation, BattleManager.update_patb, can be confidently wholly reused, due to be being a reusable component.


On the contrary, an inextensible design could need the CTB addon to rewrite a large portion of BattleManager.update_patb(For instance, the upcoming CATB CTB addon needs to rewrite the whole CATB ATB Frame Update).


From these examples, and perhaps our experience and observation, extensible designs are mainly based on designs with low coupling and high cohesion, as such designs make the whole system decomposable into Building Blocks, which are essentially independent, indivisible, necessary, reusable and unique units(Building Block itself is a topic that's too advanced to be covered here).


When adding new addons, configurations to existing addons, etc, it's desirable to reuse as many existing components as possible, and modify as few of them as possible.


Of course, it's even more desirable to preserve the extensibility after an extension, or the codebase and gradually deteriorate into less and less extensible one.


On a side note: The above also demonstrates that some code qualities are interrelated. Such interrelations are too advanced to be covered here.


P.S.: Configurations/Notetags with level 6(Javascript Codes) or 7(Editable Javascript Codes) control and freedom given to users, when implemented well, also exhibits high extensibility, as users are essentially extending the ATB system plugin just by writing/editing those javascript codes as configuration/notetag values. Nevertheless, you'll want to stay away from this unless you've advanced RMMV plugin development proficiency(veteran RMMV plugin developer having written at least 1 excellent advanced complex plugin with all code qualities being well balanced and prioritized; same below), as such extensions are done by users rather than you, albeit under your guidance, meaning that those extensions can quickly become an utter mess unless well managed.






Scalability

From Wikipedia:


- Scalability is the capability of a system, network, or process to handle a growing amount of work, or its potential to be enlarged in order to accommodate that growth.


The main application for scalability to ATB system plugins is increasing the configuration/notetag/plugin call/command level of control and freedom given to users. This involves both the user level and the implementation level.


On the user level, the main concern is whether the plugin's still user-friendly enough after increasing the configuration/notetag/plugin call/command level of control and freedom given to users. A plugin with an ideal level of scalability is one that's extremely user-friendly to even the most incapable users, even when its configurations/notetag/plugin call/commands offers level 7(Editable Javascript Codes) control and freedom to users. This is already mentioned in I. ATB System Configuration Management.


On the implementation level:


Let's use the Maximum ATB Value notetag as an example. Right now it exhibits level 5(Editable Simple Values) control and freedom given to users, with the aid of the below plugin call(PATB Core v101a):



 








* # 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


Code:
 *      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



Suppose later I want to increase its control and freedom given to users to level 7(Editable Javascript Codes).


The user configuration and documentation regions have to be changed into something like this:

* # Actor/Class/Weapon/Armor/Enemy/State Notetags:
* State notetags take the highest priority, followed by enemy, weapon,
* armor, class and actor
* 1. <max patb val: MATBX>
* - Sets the maximum atb value of a battler as MATBX, which can be
* set in Maximum ATB Notetag Values
* - The final max atb_value must be at least 10000 times larger than
* 2 ^ -52


Code:
 *      2. meta.max_patb_val = MATBX                                          
 *         - Sets the maximum atb value stored in                             
 *           <max patb val: MATBX> as the name of a function returning a      
 *           Number                                                           
 *         - 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
Code:
    /*------------------------------------------------------------------------
     *    Maximum ATB Functions                                               
     *    - Setups MATBX used by <max patb val: MATBX>                        
     *------------------------------------------------------------------------*/
    /* MATBX are used at:
     * 1. Game_Battler
     *    - val = patb[nvx].call(this, val, data); in set_multi_patb_nvx
     * MATBX are Javascript functions which will be bound to the battler upon
     * use
     * MATBX names can only use alphanumeric characters
     * val is the maximum atb value right before using MATBX
     * data is the data storing the MATBX
     * The below MATBX are examples added to help you set your MATBX
     * You can freely use, rewrite and/or delete these examples
     */

    // Sets the maximum atb value as x
    MATB1: function(val, data) { return x; },

    // Adds the maximum atb value by the value of variable with id x
    MATB2: function(val, data) { return val + $gameVariables.value(x); },

    // Adds new MATBX here



And the Maximum ATB Value notetag will need to change its notetag storage function, as well as a new Abstract Notetag Reader:

// data: The data to have its notetags read
DataManager.load_patb_data_notes = function(data) { // v0.00b+; New
var color = /< *patb +colors *: *(\d+) *, *(\d+) *>/i;
var overlay = /< *patb +overlay +colors *: *(\d+) *, *(\d+) *>/i;
var max = /< *max +patb +val *: *(\w+) *>/i;
var meta = data.meta;
meta.max_patb_val = [];
data.note.split(/[\r\n]+/).forEach(function(line) {
if (!meta.patb_colors && line.match(color)) {
meta.patb_colors = [+RegExp.$1, +RegExp.$2];
} else if (!meta.patb_overlay_colors && line.match(overlay)) {
meta.patb_overlay_colors = [+RegExp.$1, +RegExp.$2];
} else if (line.match(max)) {
meta.max_patb_val.push(RegExp.$1);
}
});
}; // DataManager.load_patb_data_notes


Code:
/* 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_nvx = function(val, note) {
// New; Potential Hotspot
    var patb = DoubleX_RMMV.PATB_CORE;
    this.patb_note_data().forEach(function(type) {
        type.forEach(function(data) {
            data.meta[note].forEach(function(nvx) {
                // Bound all patb[nvx] to Game_Battler.prototype upon game start
                val = patb[nvx](val, data);
                //
            }, this);
        }, this);
    }, this);
    return val;
}; // Game_Battler.prototype.set_multi_patb_nvx



While I don't feel/think such extension demonstrate an ideal level of scalability of the original codebase, I still fee/think it's acceptable, because:


1. Game_Battler.prototype.set_multi_patb_nvx is only a Potential Hotspot, so it can only have noticeable performance issues on users using plugin calls to explicitly change the Maximum ATB Value extremely frequently.


2. On the implementation side, only the Maximum ATB Value notetag storage function is changed and a new Abstract Notetag Reader's added. The rest of the codebase can remain completely intact.


3. The newly added Abstract Notetag Reader can later also be used by some other notetags having level 6(Javascript Codes) or 7(Editable Javascript Codes) control and freedom given to users and the same notetag type as the the Maximum ATB Value notetag.


Another application of scalability of ATB system plugins is the number of battlers and effective notetags per battler in battle.


For most ATB system plugins, a main challenge is to keep the codebase sufficiently performant on machines barely meeting the RMMV system requirements(like keeping the average FPS close to 60 and the frequency of lags low) with hundreds(and in rare extreme cases, thousands) of effective notetags being used per frame in battle. That's almost purely about the implementation level.


If you want to make an ATB system plugin supporting, say, more than 1 battlers executing actions at the exact same frame, the cocurrency complexity will also scale as the number of battlers increases, the number of targets and repeats the skills/items increases, the number of Unison Skill/Item increases, etc. On the user level, they may be uttered confused and feel/think that the action execution ordering are completely arbitrary; On the implementation level, the component detecting and resolving concurrency issues may become so complicated and convoluted that even your future selves won't know what's going on. Cases like these are too advanced to be covered here.


Achieving an ideal level of scalability often means making the codebase to be able to handle the most extreme cases having a remotely nontrivial chance to even happen just once.


Of course, that can mean investing a disproportionately huge amount of resources just to handle some really rare cases, and sometimes, also mean sacrificing codebase readability and simplicity.


Therefore, like many other code qualities, a codebase with a higher level of scalability doesn't always mean it's better, as it might sacrifice lots of a lot more code qualities for that codebase, meaning code quality balancing and prioritization applies to scalability as well. This topic is too advanced to be covered here.










Summary

Note that:


- Most of these are unique to ATB system plugins. Instead, they can be applied to most plugins. The point of this reply is to show how these global knowledge can be applied to ATB system plugins.


- None of these should be treated as dogmas without thinking about them all beforehand.


Invariants



 





There are 2 types of invariants:


Global Invariants - Assumptions that are supposed to be always 100% held across the entire codebase under any possible circumstances


Contextual Invariants - Assumptions that are supposed to be always 100% held under some situations involving some parts of the codebase


A Broken Invariant is one that isn't indeed always 100% held for its designated scopes.


For example:


- A Broken Global Invariant is one that isn't indeed always 100% held across the entire codebase under any possible circumstances.


- A Broken Contextual Invariant is one that isn't indeed always 100% held under all the situations involving all those parts of the codebase that are supposed to have it being always 100% held.


Invariant A is said to be depending on invariant B if the former will become a Broken Invariant when the latter becomes one.


The codebase is said to have Interinvariant Dependency if it's any such invariants.



Correctness

Simply put, a correct codebase is one having no nontrivial bugs, so correctness of a codebase is a measure of the number of nontrivial bugs of the codebase, and the impacts of each of them to the codebase.


A nontrivial bug in a codebase is an unexpected result and/or intended behavior that has nontrivial inconsistencies with the behavioral requirements of the codebase.


Therefore, to measure the correctness of a codebase, one must first know all the behavioral requirements of that codebase.


A Nontrivial Invariant is one that, once become a Broken Invariant, will lead to nontrivial bugs.


So it's useful to hunt for all Nontrivial Invariants of a codebase and check whether any of them are Broken Invariants in order to hunt for bugs and fix them.



Robustness

The robustness of a codebase can be roughly measured by the chance of its Nontrivial Invariants to become Broken Invariants and the seriousness of the manifested nontrivial bugs.


So to increase robustness, you'll want to refactor the codebase so that it'll need less and less Nontrivial Invariants, especially those that are likely to become Broken Invariants, those having Interinvariant Dependencies, and/or will really wreck havoc when they do, while still preserving correctness, and preferably all the other important code qualities.


The purpose of fault isolation is to ensure a fault within a component won't propagate to any other components.


To implement fault isolation, a codebase should be so modular that it's compartmentalized into independent but interrelated components so that no single faulty component can compromise the entire codebase.


Of course, in practice, some faults in some components are simply too severe to be separated from the rest of the codebase.


Therefore, it's useful to mark all such possible faults in all such potential components, and keep a close eye to them, in order to prevent them from being real issues and fix them immediately in case they do.


In general, the more InterInvariant Dependencies a codebase have, the less able it can perform fault isolation, as such codebases can have a chain of Broken Invariants with just 1 of them due to the single original fault.



Performance

In RMMV, what most users care about is the time performance, which is directly illustrated by the average and minimum fps.


What we, as RMMV plugin developers, want to void, is writing plugins leading to constant severe lag and fps drop, even on machines barely meeting the RMMV system requirements.


A hotspot in ATB system plugins is a section of code that is run extremely frequently.


If a section of code is run at least nearly once per frame on average, then it's a hotspot; If a section of code is run no where close by once per frame on average, then it's not a hotspot.


A Potential Hotspot is a section of code that can become a hotspot or not a hotspot depending on normal and/or common use cases.


The point of marking functions as hotspots and/or Potential Hotspots is to know which functions should optimize for code performance.


Every change of everything has some reasons behind it, and these reasons are called Reasons To Change.


A Deterministic Reasons To Change Detector is a component that can accurately tell whether all Reasons To Change it's monitoring have triggered or not triggered.


A Probabilistic Reasons To Change Detector is a component that can accurate tell that all Reasons To Changes it's monitoring have not triggered but can't accurately tell that they've triggered.


In general, any performance optimization using Reasons To Change will have to ensure that "Those Reasons To Change will always be the only Reasons To Change" is a Nontrivial Invariant.


But as mentioned in Robustness, adding Nontrivial Invariants will make a codebase less robust, unless those Nontrivial Invariants will indeed never become Broken Invariants under any possible circumstances, which is clearly nearly impossible just by considering all the possible compatibility issues(provided that one doesn't ignore them).


So in essence, such performance optimization tricks are actually trading robustness for performance.


In practice, new Reasons To Change mainly come from a plugin interacting with some other plugins, and these new Reasons To Change are some of the causes of compatibility issues among these plugins. Therefore it's desirable to implement the performance optimizations in ways that it's extremely unlikely to have new Reasons To Change under almost all circumstances.


The application of change notification flags on optimizing code performance is to inform hotspots/Potential Hotspots that some resource demanding operations has/might need to be executed. Those operations will only be executed when the corresponding change notification flags are raised.


In other words, the goal of change notification flags is to ensure all resource demanding operations executed by hotspots/Potential Hotspots will be called if and only if at least 1 corresponding Reasons To Change might be triggered.



Coupling

Content Coupling is generally considered to be 1 of the most destructive antipatterns as it manifests an extremely high and the highest coupling.


Common Coupling is generally considered to be a severe antipattern as it manifests a lower coupling than content coupling but still quite a high coupling.


External Coupling is generally considered to be a moderate antipattern as it manifests a lower coupling then common coupling but still slightly high coupling.


Control Coupling is generally considered to be a severe code smell as it manifests a medium coupling but a lower coupling then external coupling.


Stamp Coupling is generally considered to be a moderate code smell as it manifests a slightly low coupling and a lower coupling then control coupling.


Data Coupling is generally considered to be acceptable and sometimes necessary, as it manifests quite a low coupling and a lower coupling then stamp coupling.


Message Coupling is generally considered to be ideal although not always possible as it manifests an extremely low coupling and the lowest coupling as long as coupling's inevitable.


No coupling is generally considered to be the best, but only when it's possible and indeed called for, as sometimes some components are inherently coupled(as illustrated in III. ATB System Addons).


Temporal coupling exists among some components when those components must be called under some particular timing/ordering rules.


- Explicit Temporal Coupling is a temportal coupling where those particular timing/ordering rules are extremely crystal clear and obvious.


- Implicit Temporal Coupling is a temporal coupling where those particular timing/ordering rules are extremely elusive.


Clearly, whether a temporal coupling's a Explicit Temporal Coupling or Implicit Temporal Coupling is relative to the codebase readers.


Some Explicit Temporal Coupling are inevitable for any ATB system plugin. However, we can still stirve for eliminating all Implicit Temporal Coupling for those meeting the capability requirements for codebase readers stated by the codebase.



Cohesion

Coincidental Cohesion is generally considered to be 1 of the most destructive antipatterns as it manifests an extremely bad and the worst cohesion.


Logical Cohesion is generally considered to be a severe antipattern as it manifests a better cohesion than coincidental cohesion but still quite a bad cohesion.


Temporal cohesion is generally considered to be a moderate antipattern as it manifests a better cohesion than logical cohesion but still a slightly bad cohesion.


Procedural cohesion is generally considered to be a severe code smell as it manifests a better cohesion than temporal cohesion and a moderate cohesion.


Communicational cohesion is generally considered to be a moderate code smell as it manifests a better cohesion than procedural cohesion and a slightly good cohesion.


Sequential Cohesion is generally considered to be acceptable as it manifests a better cohesion than communicational cohesion and quite a good cohesion.


Functional Cohesion is generally considered to be ideal as it manifests a better cohesion than sequential cohesion and the best cohesion.



Flexibility

Simply put, flexibility is a measure of how adaptable a plugin's to various situations, including those far from its targeting scopes.


Extensible designs are mainly based on designs with low coupling and high cohesion, as such designs make the whole system decomposable into Building Blocks, which are essentially independent, indivisible, necessary, reusable and unique units(Building Block itself is a topic that's too advanced to be covered here).


When adding new addons, configurations to existing addons, etc, it's desirable to reuse as many existing components as possible, and modify as few of them as possible.


Of course, it's even more desirable to preserve the extensibility after an extension, or the codebase and gradually deteriorate into less and less extensible one.


Configurations/Notetags with level 6(Javascript Codes) or 7(Editable Javascript Codes) control and freedom given to users, when implemented well, also exhibits high extensibility, as users are essentially extending the ATB system plugin just by writing/editing those javascript codes as configuration/notetag values.


The main application for scalability to ATB system plugins is increasing the configuration/notetag/plugin call/command level of control and freedom given to users. This involves both the user level and the implementation level.


On the user level, the main concern is whether the plugin's still user-friendly enough after increasing the configuration/notetag/plugin call/command level of control and freedom given to users. A plugin with an ideal level of scalability is one that's extremely user-friendly to even the most incapable users, even when its configurations/notetag/plugin call/commands offers level 7(Editable Javascript Codes) control and freedom to users.


Another application of scalability of ATB system plugins is the number of battlers and effective notetags per battler in battle.


For most ATB system plugins, a main challenge is to keep the codebase sufficiently performant on machines barely meeting the RMMV system requirements(like keeping the average FPS close to 60 and the frequency of lags low) with hundreds(and in rare extreme cases, thousands) of effective notetags being used per frame in battle.
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,773
Reaction score
938
First Language
Chinese
Primarily Uses
N/A
Now let's talk about III. ATB System Addons.


I'll only briefly cover the code qualities of each addon. The purpose behind this is to use some easy, simple and small examples to demonstrate some ways to reason about code qualities in ATB systems. Therefore, you may want to check and verify the details yourselves.


Bar Addon

The bar addon's to let users set if and how the ATB bars will be displayed nearby battler sprites, as illustrated in the video:

You may want to check the plugin PATB Bar for details.


Invariants

The 2 most crystal clear and obvious Global Invariants, as well all the other addons, are these:


1. PATB Bar needs PATB Core in order to work. It's because PATB uses the Core Addon Approach.


2. PATB Bar must be completely disabled if PATB Core uses an unavailable battle system(any of those other than atb and ctb).


Another crystal clear and obvious Global Invariant, but unique to this addon, is this: The ATB bars won't be shown on any actor sprite if the battle system isn't sideview to begin with.


Basically, those are the only new Nontrivial Invariants of this addon, as almost all the rest are directly inherited from PATB Core.

Correctness

Evaluating correctness involves these 3 parts in practice:


1. The behavioral requirements


2. The actual behaviors


3. The inconsistencies between 1 and 2


The behavioral requirements are basically these:


1. The visibility of an ATB bar shown nearby each battler sprite must work correctly according to relevant configurations and the visibility of that battler sprite itself


2. The positional offsets of those ATB bars must work correctly according to relevant configurations


3. The dimensions, opacity and description text size of those ATB bars must work correctly according to relevant configurations


4. The ATB bar fill ratio must correctly show the corresponding battler ATB value relative to that battler's Maximum ATB Value


The actual behaviors corresponding to the 1st behavioral requirement's implemented in Window_Patb_Bar.prototype.updateVisible.


So the 1st behavioral requirement's completely consistent with its actual behaviors, because:


1. It disables the ATB bars for unavailable battle systems


2. It disables the ATB bars when the battle's not complete ready to start yet or has completely ended


3. It disables the ATB bars in case its battler's completely gone


4. It correctly implements the corresponding configurations


The actual behaviors corresponding to the 2nd behavioral requirement's implemented in Window_Patb_Bar.prototype.initialize and Window_Patb_Bar.prototype.updateNotes.


So the 2nd behavioral requirement's completely consistent with its actual behaviors, because:


1. Those positional offsets are correctly initialized


2. They will be automatically used to adjust the positions of those ATB Bars when their corresponding battler sprites change positions


3. When those positional offsets themselves change, the positions of those ATB Bars will also be correctly adjusted accordingly


The actual behaviors corresponding to the 3rd behavioral requirement's implemented in Window_Patb_Bar.prototype.initialize, Window_Patb_Bar.prototype.standardFontSize, Window_Patb_Bar.prototype.resetFontSettings, Window_Patb_Bar.prototype.updateNotes, Window_Patb_Bar.prototype.updateW and Window_Patb_Bar.prototype.updateH.


So the 3rd behavioral requirement's completely consistent with its actual behaviors, because:


1. Those dimensions are correctly initialized


2. When those dimensions themselves change, this._redraw, which is a change notification flag subscribed by the ATB bar redraw function, will be raised


3. The ATB bar opacity are correctly updated


4. The ATB bar description text size are correctly updated, and those text themselves will be redrawn right after their sizes' changed, due to this._redraw, which is the change notification flag subscribed by the ATB bar redraw function, being raised


The actual behaviors corresponding to the 4th behavioral requirement's implemented in Window_Patb_Bar.prototype.updateFillWidth


So the 4th behavioral requirement's completely consistent with its actual behaviors, because:


1. The ATB bar fill ratio's a direct manifestations of its corresponding battler ATB value fill rate


2. The ATB bar will be redrawn when its fill ratio changes, due to this._redraw, which is the change notification flag subscribed by the ATB bar redraw function, being raised


Therefore, PATB Bar works correctly.

Robustness

Evaluating robustness involves these 3 parts in practice:


1. How likely any Nontrivial Invariant will become a Broken Invariant


2. How severe the nontrivial bugs can be manifested when 1 happens


3. How easy a fault in this addon can propagate into any other plugin


The answer of 1 is simple and small: Basically the same as that of PATB Core, without considering any other Nontirivial Invariant in PATB Core.


The answer of 2 is that, the result can be very serious, because:


1. PATB Bar will simply crash without PATB Core


2. PATB Bar will display meaningless, pointless and useless ATB bars in unavailable battle systems


The answer of 3 is that, the fault in PATB Bar's almost completely isolated from any other plugin, because:


1. The only contact point of PATB Bar outside PATB is Sprite_Battler, which just creates an instance of Window_Patb_Bar for each of its corresponding battler sprite


2. Window_Patb_Bar will be effectively disabled for unavailable battle systems, meaning that PATB Bar can almost only affect other PATB plugins


3. Neither any other PATB addon nor PATB Core depend on PATB Bar, although PATB Bar does depend on PATB Core


Therefore, PATB Bar should only be more robust than PATB Core.

Performance

Evaluating time performance involves these 3 parts in practice:


1. Reasoning about what costly operations the codebase must do and how they can be optimized


2. Reasoning about what costly and/or unnecessary operations the codebase has done and how they're done


3. Actually benchmarking the number of ms spent on executing operations at a frame on machines barely meeting the RMMV minimum requirements


The answer of 1 is this:


1. Redrawing the ATB Bars with their corresponding description texts redrawn.


It's demonstrated in Window_Patb_Bar.prototype.redraw, which actually redraws the ATB Bars along with their corresponding description text(you can check the details yourselves in PATB Bar v1.00b).


On a side note: Actually the description text content change itself should also be a Reason To Change, but it doesn't need to be handled by PATB Bar at all as it's already handled by PATB Core.


2. They can be optimized by only redrawing those that might have at least 1 Reasons To Change triggered.


It's demonstrated in Window_Patb_Bar.prototype.needRedraw, which checks and resets this._redraw, which is the change notification flag subscribed by the ATB bar redraw function, being raised.


Checking the above sections, the Reasons To Change here are changing the description text size, changing the ATB Bar dimensions, the ATB Bar fill ratio and the corresponding battler him/her/itself.


Note that this._redraw would be a Deterministic Reasons To Change Detector if it wouldn't be raised by BattleManager.need_patb_refresh, which is itself a Probabilistic Reasons To Change Detector.


Window_Patb_Bar.prototype.updateBar shows that Window_Patb_Bar.prototype.redraw will only be called when this._redraw is raised.


The answer of 2 is this: PATB Bars does no other costly operations at all.


The answer of 3 is this: In my machine(WinXP + i7-3820 + 2 * 2GB DDR3-1333 + Asus GTX550Ti), each ATB Bar leads to 1ms added load per battler per frame on average.


Therefore, PATB Core should be barely performant enough for many machines, but still nowhere near ideal, as manifested when many other resource consuming plugins are used as well.

Coupling

Evaluating coupling involves these 4 parts in practice:


1. How much any other plugin must depend on this plugin


2. How much this plugin must depend on any other plugin


3. How much any other plugin actually depends on this plugin


4. How much this plugin actually depends on any other plugin


The answer of 1 is no dependency at all for any other plugin, as PATB Bar does nothing else other than displaying an ATB Bar for each battler sprite(with only 1 exception that the screen would have too many things to display if PATB Bar's used).


The answer of 2 is this: It must know the current ATB colors, description text contents, fill rate and type of the battler owning the ATB Bar.


The answer of 3 is the same as that of 1.


The answer of 4 is the same as that of 2, with the below added key points:


1. With PATB Charge and PATB Cooldown used as well, the ATB colors, description text contents, and fill rate can be the charge or cooldown ones as well, and the ATB type can be charge or cooldown as well.


2. However, it doesn't mean PATB Bar needs to depend on either of the former plugins, as they share the same interfaces as PATB Core, which are Game_Battler.prototype.patb_colors, Game_Battler.prototype.patb_type, Game_Battler.prototype.patb_bar_text and Game_Battler.prototype.patb_fill_rate(you can check these yourselves), when it comes to the aforementioned dependencies.


3. Actually, PATB Bars knows absolutely nothing about neither PATB Charge nor PATB Cooldown, even including their very existence. All PATB Bars needs to know are the aforementioned functions as interfaces, which exhibits data and message coupling.


4. Such a loose coupling is of no coincidence at all. Instead, it's due to my experience and observations in ATB system scripts and plugins, and my meticulous and thorough foreplanning even before writing a single character in PATB Core.


5. An ideal implementation of the Core Addon Approach is to make the core plugin the common interface shared by all addon plugins in order to loosen the coupling among them. This topic itself's too advanced to be covered here.


Therefore, PATB Bar almost exhibits the loosest coupling it can achieve.

Cohesion

Evaluating cohesion involves these 2 parts in practice:


1. How many functions this plugin needs to serve and how specific and well-defined they need to be


2. How many functions this plugin actually serve and how specific and well-defined they actually are


Evaluating cohesion in ATB systems involves this in practice as well:


3. How many functions this plugin can be implemented outside ATB systems


The answer of 1 is only 1 well-defined specific function: Displaying an ATB Bar nearby each battler sprite of its corresponding battler.


This function's sufficiently well-defined as it's crystal clear and obvious for almost anyone familiar with ATB systems, and is specific enough as everything can be encapsulated into 1 sufficiently easy, simple and small object per battler sprite that only displays the ATB fill ratio of that battler.


The answer of 2 is the same as that of 1, meaning that PATB Bar achieves functional cohesion.


The answer of 3 is most likely none, as ATB Bars are unique to ATB systems, while implementing a generic bar addon as a template to implement all bars(hp, mp, tp, ATB, etc) will most likely induce very tight coupling with the specific ATB system it's meant to work with, since it at least needs to know the ATB Frame Update, ATB Bar colors, description text contents and ATB fill ratio in order to display the ATB Bars correctly.

Flexibility

Evaluating flexibility involves these 4 parts in practice:


1. How massive the 3 dimensions of control and freedom this plugin can give to its users


2. How massive the 3 dimensions of control and freedom this plugin actually gives to its user


3. How much cost will be needed for this plugin to offer more massive 3 dimensions of control and freedom to its users


4. How capable this plugin can handle extreme scales(data sizes, data change frequency, etc)


The answer of 1, from my experience, knowledge and observations, is letting users to:


1. Sets when the ATB Bars will be shown


2. Sets the ATB Bar dimensions


3. Sets the ATB Bar positional offsets relative to its corresponding battler sprite


4. Sets the ATB Bar opacity


5. Set the ATB Bar colors


6. Set the ATB Bar text description contents


7. Set the ATB Bar text description size


All under level 7 configuration control and freedom, Editable Javascript Codes.


The answer of 2 is similar to that of 1, except that 5 and 6 are outsourced to PATB Core, PATB Charge and PATB Cooldown, and the configuration control and freedom is level 5, Editable Simple Values.


The answer of 3 is the same as that of PATB Core, as PATB Bar uses PATB Core functions to read configuration values.


The answer of 4 is that, in my machine(WinXP + i7-3820 + 2 * 2GB DDR3-1333 + Asus GTX550Ti), 12 battlers are nearly the limit of PATB Bar, as this scale already hits the 15ms combined load per frame on average, which is just 1ms away from the load limit in order to maintain the constant 60FPS.


So PATB Bar is nowhere close to be the most flexible plugin ever, but should still be flexible enough for most ordinary uses(which is to keep this plugin easy, simple and small enough to be sufficiently popularized).






Charge Addon

This addon lets users set some skills/items to need their users to wait for amount of time set by users after inputting them in order to execute them, as illustrated in the video:

You may want to check the plugin PATB Charge for details.


Invariants

It's similar to PATB Bar in that it also shares all Nontrivial Invariants in PATB Core, except that PATB Charge doesn't have any Nontrivial Invariant unique to itself.

Correctness

The behavioral requirements are basically these:


1. The ATB charge bar colors and description text contents must work correctly according to the relevant configurations


2. The skill/item cost can either be paid before and after its charging, depending on the relevant configurations


3. The skill/item cost charge rate must work correctly according to the relevant configurations


4. The Action Execution Subject must be the one having full ATB charge value and those having full ATB charge value must be eligible to be the Action Execution Subject.


5. The transitions between the ATB Charge Phase and some other phases for the Battler ATB Clock must work correctly right after finishing inputting/executing a skill/item.


The actual behaviors corresponding to the 1st behavioral requirement is implemented in Game_Battler.prototype.set_patb_colors and Game_Battler.prototype.patb_bar_text.


So the 1st behavioral requirement's completely consistent with its corresponding actual behaviors, as both of those functions are from PATB Core, which are themselves correct.


The actual behaviors corresponding to the 2nd behavioral requirement is implemented in Game_Action.prototype.confirm_patb_item, Game_Battler.prototype.useItem, Game_Battler.prototype.patb_charge_prior_item_cost and Game_Actor.prototype.confirm_patb_acts.


So the 2nd behavioral requirement's completely consistent with its corresponding actual behaviors, as:


1. Game_Battler.prototype.patb_charge_prior_item_cost correctly uses the relevant configurations


2. Both Game_Action.prototype.confirm_patb_item and Game_Actor.prototype.confirm_patb_acts will pay the skill/item cost if Game_Battler.prototype.patb_charge_prior_item_cost returns false


3. Game_Battler.prototype.useItem will pay the skill/item cost if Game_Battler.prototype.patb_charge_prior_item_cost returns true


The actual behaviors corresponding to the 3rd behavioral requirements is implemented in Game_Battler.prototype.update_patb_val, which calls Game_Battler.prototype.update_patb_charge, which calls Game_Battler.prototype.get_patb_charge_rate, with its change notification flag implemented in Game_Battler.prototype.can_patb_charge_rate_change, which will lead to the call of Game_Battler.prototype.set_patb_charge_rate if it's raised.


So the 3rd behavioral requirement's completely consistent with its corresponding actual behaviors, as:


1. Game_Battler.prototype.update_patb_val is implemented by PATB Core, which is itself correct.


2. Game_Battler.prototype.update_patb_charge uses the same implementation of the original Game_Battler.prototype.update_patb_val, which is correct due to 1.


3. Game_Battler.prototype.can_patb_charge_rate_change uses the same implementation as those change notification flags in PATB Core, which are themselves correct.


4. Game_Battler.prototype.set_patb_charge_rate correctly uses the relevant configurations.


5. Game_Battler.prototype.get_patb_charge_rate will always return the updated charge rate, which its correctness assured by 1 and 2.


The actual behaviors corresponding to the 4th behavioral requirements is implemented in Game_Battler.prototype.can_patb_act and Game_Battler.prototype.has_full_patb_charge.


So the 4th behavioral requirement's completely consistent with its corresponding actual behaviors, as:


1. The Action Execution Subject must be the one passing the Game_Battler.prototype.can_patb_act check, which is the PATB Core actual behavior.


2. All battlers passing the Game_Battler.prototype.can_patb_act will be eligible to be the Action Execution subject, which is the PATB Core actual behavior.


The actual behaviors corresponding to the 5th behavioral requirements is implemented in BattleManager.endAction, Game_Battler.prototype.can_patb_charge, Game_Battler.prototype.reset_patb_charge_val, Game_Battler.prototype.is_patb_charge, Game_Battler.prototype.set_patb_charge, Game_Battler.prototype.set_patb_charge_item and Game_Battler.prototype.mark_patb_charge_change.


So the 5th behavioral requirement's completely consistent with its corresponding actual behaviors, as:


1. BattleManager.endAction will call Game_Battler.prototype.set_patb_charge to setup the charging for the next action of the current Action Execution Subject.


2. Game_Battler.prototype.can_patb_charge will return true if and only if the ATB's fully filled, the ATB cooldown value's empty, and the battler already has inputted a skill/item.


3. Game_Battler.prototype.reset_patb_charge_val will clear the cached charging skill/item and empties the ATB charge value.


4. Game_Battler.prototype.is_patb_charge returns whether the ATB charge value's nonempty


5. Game_Battler.prototype.set_patb_charge_item will cache the currently inputted skill/item and empties the ATB charge value so the battler will be ready to charge the cached skill/item.


6. Game_Battler.prototype.mark_patb_charge_change will be used to inform the displays to be updated when the phase of the Battler ATB Clock changes from charging to something else and vice versa.


Therefore PATB Charge works correctly.

Robustness

Regarding how likely any Nontrivial Invariant will become a Broken Invariant, again, it's basically the same as that of PATB Core, without considering any other Nontirivial Invariant in PATB Core.


Regarding how severe the nontrivial bugs can be manifested when 1 happens, it's basically the same as PATB Bar.


Regarding how easy a fault in this addon can propagate into any other plugin, the fault in PATB Charge's almost completely isolated from any other plugin, because:


1. All entry points exposed by the PATB Charge are covered by PATB Core, meaning that any fault in PATB Charge can only damage PATB itself.


2. Neither PATB Core nor any PATB addon ever needs to know anything about PATB Charge, even including its very existence, although PATB Charge does depend on PATB Core.


Therefore PATB Charge should only be more robust than PATB Core.

Performance

Regarding what costly operations the codebase must do and how they can be optimized, basically there's none.


Regarding what costly and/or unnecessary operations the codebase has done and how they're done, there's actually none.


Due to the aforementioned answers, there's no real need to benchmark for PATB Charge.

Coupling

Regarding how much any other plugin must depend on this plugin, there's no dependency at all for any other plugin, as all PATB Charge does is to add a new phase to the Battler ATB Clock.


Regarding how much this plugin must depend on any other plugin, theoretically PATB Charge should only need to know the ATB Rate, which is what the ATB Charge Rate's based on, the ATB value, which is used to determine whether a battler can charge skills/items(using skills/items alone can lead to complicated codes when considering forced actions), and the ATB cooldown value, which is used to determine whether a battler's cooling down.


Regarding how much any other plugin actually depends on this plugin, it's the same as the 1st answer.


Regarding how much this plugin actually depends on any other plugin, it's the same as the 2nd answer, meaning that PATB Charge depends on PATB Core as well as PATB Cooldown.


Actually, if PATB would restrict PATB Cooldown to be always placed above PATB Charge, PATB Charge wouldn't depend on PATB Cooldown at all, as PATB Cooldown could intercept the charging itself in Game_Battler.prototype.update_patb_val.


To me, however, this added restriction will be much worse than slightly increasing the coupling of PATB Charge(note that right now PATB Cooldown depends nothing on PATB Charge), as the former move will directly affect users, while the latter one won't, and the latter will only increase the coupling from no coupling to message coupling(as PATB Charge need to know Game_Battler.prototype.is_patb_cooldown, which is a new function in PATB Cooldown).


Therefore PATB Charge exhibits very loose coupling, although not the loosest one possible due to balancing coupling with user-friendliness(which is too advanced to be covered here).

Cohesion

Regarding how many functions this plugin needs to serve and how specific and well-defined they need to be, there's only 1 well-defined specific function: Adding the charge phase to the Battler ATB Clock.


This function's well-defined enough as it's crystal clear and obvious for those familiar with ATB systems, and is specific enough as it's nothing more than an added waiting time between finishing inputting actions and before executing them.


Regarding how many functions this plugin actually serve and how specific and well-defined they actually are, it's the same as that of 1st one, meaning that PATB Bar achieves functional cohesion.


Regarding how many functions this plugins can be implemented outside ATB systems, it's none, as PATB Charge is entirely about Battler ATB Clock, which is itself completely unique to ATB systems.

Flexibility

Regarding how massive the 3 dimensions of control and freedom this plugin can give to its users, from my experience, knowledge and observation, it should be able to let users:


1. Set the ATB charge bar colors


2. Set the ATB charge bar description text contents, possibly the skills/items the battlers are charging


3. Sets whether the skill/item costs are paid before or after charging the skill/item


4. Sets whether and how some charging skills/items can be canceled before they're executed


5. Sets some extra events to happen when the battler becomes fully charged the skill/item


6. Sets the charge rate of the skills/items
All under level 7 configuration control and freedom, Editable Javascript Codes.


Regarding how massive the 3 dimensions of control and freedom this plugin actually give to its users, it's similar to the above list, except that 4 and 5 aren't included, and the configuration control and freedom's at level 5, Editable Simple Values.


Regarding how much cost will be needed for this plugin to offer more massive 3 dimensions of control and freedom to its users, it's basically the same as that of PATB Core, as PATB Bar uses PATB Core functions to read configuration values.


Regarding how capable this plugin can handle extreme scales(data sizes, data change frequency, etc), this plugin should be rather capable, as it doesn't have any costly operations to begin with.


Therefore PATB Charge is certainly not the most flexible plugin ever, but should still be flexible enough for most ordinary uses(which is to keep this plugin easy, simple and small enough to be sufficiently popularized).






Cooldown Addon

This addon lets users set some skills/items to need their users to wait for amount of time set by users after executing them before their ATB values can be refilled again, as illustrated in the video:

You may want to check the plugin PATB Cooldown for details.


Invariants

It's almost identical to PATB Charge.

Correctness

It's almost identical to PATB Charge, except that charge needs to be replaced by cooldown.

Robustness

It's almost identical to PATB Charge.

Performance

It's almost identical to PATB Charge.

Coupling

It's almost identical to PATB Charge, except that PATB Cooldown doesn't need to know anything about PATB Charge, even including its very existence.

Cohesion

It's almost identical to PATB Charge, except that charge needs to be replaced with cooldown.

Flexibility

It's almost identical to PATB Charge.






Countdown Addon

This addon lets users set some states to have their turns based on number of ATB Frame Updates passed(although the unit's restricted to number of seconds under ordinary use in order to preserve time performance) instead of the battle turn counter, as illustrated in the video:

You may want to check the plugin PATB Countdown for details.


Invariants

It's similar to PATB Bar in that it also shares all Nontrivial Invariants in PATB Core, except that PATB Countdown also has 1 new Nontrivial Invariant:


No other state type will share the same auto removal timing as that of countdown states.


While this Nontrivial Invariant will wreck havoc upon becoming a Broken Invariant due to PATB Countdown removing those other state types in Game_Battler.prototype.update_patb_state_countdown, how easy this Nontrivial Invariant will become a Broken Invariant depends on how likely the countdown state auto removal timing will be the same as any other custom ones.


In this case, unfortunately, due to my utter incompetence and inexperience, it's incredibly easy, as the auto removal timing of countdown states is hardcoded as 3.


Ideally, it should have been configurable by users in the first place. But at the very least, it should be replaced by something much more unique, like "patb_countdown" instead of 3.

Correctness

The behavioral requirements are basically these:


1. The state turns must be and only be updated according to relevant configurations for countdown states


2. The effects triggered for countdown state must be what's specified in relevant configurations


The actual behaviors corresponding to the 1st behavioral requirement's implemented in Game_Battler.prototype.update_patb_state_countdown.


So the 1st behavioral requirement's completely consistent with its corresponding actual behaviors, as the FPS is hardcoded to be 60 in RMMV.


The actual behaviors corresponding to the 2nd behavioral requirement's implemented in DoubleX_RMMV.PATB_Countdown.on_countdown_update.


So the 2nd behavioral requirement's completely consistent with its corresponding actual behaviors, as DoubleX_RMMV.PATB_Countdown.on_countdown_update is the configuration itself.


Therefore PATB Countdown works correctly.

Robustness

Regarding how likely any Nontrivial Invariant will become a Broken Invariant, again, it's basically the same as that of PATB Core, without considering any other Nontirivial Invariant in PATB Core.


An important exception is already mentioned in Invariants. In fact, that Nontrivial Invariant already becomes a Broken Invariant when working with Yanfly Engine Plugins - Buffs & States Core, leading to some new state types in that plugin also being falsely removed by PATB Countdown due to sharing the same auto removal timing, meaning that it's the root cause of the compatibility issues.


Regarding how severe the nontrivial bugs can be manifested when 1 happens, it's exactly what's just mentioned.


Regarding how easy a fault in this addon can propagate into any other plugin, the fault in PATB Countdown can propagate to all plugins having new state types sharing the same auto removal timing as that of countdown states, just like those aforementioned cases.


Therefore, PATB Countdown is undoubtedly not robust at all.

Performance

Regarding what costly operations the codebase must do and how they can be optimized, there's none in PATB Countdown itself.


Regarding what costly and/or unnecessary operations the codebase has done and how they're done, there's actually none in PATB Countdown itself, but it can lead to almost all costly operations in PATB Core being called much more frequently due to calling Game_Battler.prototype.set_patb_refresh whenever a countdown state's turn's updated in Game_Battler.prototype.update_patb_state_countdown.


Game_Battler.prototype.set_patb_refresh is itself a Probabilistic Reasons To Change Detector that basically raises all change notification flags in PATB Core, and thus most of those in many other PATB addons.


Calling Game_Battler.prototype.set_patb_refresh here is because basically almost anything can happen in DoubleX_RMMV.PATB_Countdown.on_countdown_update. Although I can instead ask users to call that themselves when they aren't sure whether any Reasons To Change will be triggered, it'd mean they'd have to know all those Reasons To Change, making this plugin extremely demanding to users.


So to ensure sufficient user-friendliness, I decided to make Game_Battler.prototype.set_patb_refresh a mandatory call.


Regarding benchmarking the number of ms spent on executing operations at a frame, there's no way to have a general benchmark, as it greatly depends on the contents of DoubleX_RMMV.PATB_Countdown.on_countdown_update, which is set by users.

Coupling

Regarding how much any other plugin must depend on this plugin, there's no dependency at all for any other plugin, as all PATB Countdown does is to add a new state type having the remaining turns based on the number of ATB Frame Update passed.


Regarding how much this plugin must depend on any other plugin, PATB Countdown only needs to know the function implementing the Battler ATB Clock, which is triggered by ATB Frame Update(actually PATB Countdown can directly extend the function implementing the ATB Frame Update, but as this plugin's solely battler business and has nothing to do with the battle flow, extending the function implementing the Battler ATB Clock should be a better choice overall).


Regarding how much any other plugin actually depends on this plugin, the answer's basically the same as the 1st one, except that no other plugin can use 3 as the custom auto removal timing.


Regarding how much this plugin actually depends on any other plugin, the answer's basically the same as the 2nd one, except that no other plugin can use 3 as the custom auto removal timing.


Therefore, without the aforementioned exception, PATB Countdown could exhibit the loosest coupling it can achieve. Unfortunately, now it has unnecessary potential subtle coupling eventually leading to real compatibility issues.

Cohesion

Regarding how many functions this plugin needs to serve and how specific and well-defined they need to be, there's only 1 well-defined specific function: Adding a new state type having the remaining turns based on the number of ATB Frame Update passed.


This function's well-defined enough as it's crystal clear and obvious for those familiar with ATB systems, and is specific enough as it's nothing more than an added a new state type having the remaining turns based on the number of ATB Frame Update passed.


Regarding how many functions this plugin actually serve and how specific and well-defined they actually are, it's the same as that of 1st one, meaning that PATB Countdown achieves functional cohesion.


Regarding how many functions this plugins can be implemented outside ATB systems, it's none, as countdown states will only be updated in ATB Frame Update, which is itself completely unique to ATB systems.


If it would be handled in a generic tick-based state plugin, that plugin would need to know the function implementing the Battler ATB Clock for each specific ATB system it's meant to work with. This can lead to very tight coupling among these plugins.

Flexibility

Regarding how massive the 3 dimensions of control and freedom this plugin can give to its users, from my experience, knowledge and observation, it should be able to let users:


1. Set how many ATB Frame Updates are needed to trigger 1 state turn update


2. Set what happens when the aforementioned state turn updates are triggered


Under level 7 configuration control and freedom, Editable Javascript Codes.


Regarding how massive the 3 dimensions of control and freedom this plugin actually give to its users, it's similar to the above, except that the configuration control and freedom for the 1st one's at level 6, Javascript Codes.


Regarding how much cost will be needed for this plugin to offer more massive 3 dimensions of control and freedom to its users, it's basically the same as that of PATB Core, as PATB Bar uses PATB Core functions to read configuration values.


Regarding how capable this plugin can handle extreme scales(data sizes, data change frequency, etc), this plugin will likely suffer from serious time performance issues quickly, especially when DoubleX_RMMV.PATB_Countdown.on_countdown_update triggers many really costly operations.


Therefore PATB Charge is certainly not really that flexible, but should still be flexible enough for most ordinary uses(which is to keep this plugin easy, simple and small enough to be sufficiently popularized).


On a side note: I actually consider this plugin to be too hard to be used for many users already, as fully utilizing DoubleX_RMMV.PATB_Countdown.on_countdown_update needs decent RMMV battle related plugin development proficiency(experienced plugin developer having written dozens of decent battle related plugins with decent code qualities).






Delay Addon

This addon lets users set some battlers not under players' control to need to wait for amount of time set by users before inputting actions. You may want to check the plugin PATB Delay for details.


On a side note: Technically, it's actually the opposite - The aforementioned wait takes place right after those battlers have finished inputting actions but right before charging them, which appears to be doing what this plgin's supposed to do, as the difference's almost completely undetectable by nearly all players, even when they know this difference already.


Invariants



Spoiler



It's almost identical to PATB Charge.

Correctness

It's almost identical to PATB Charge, except that charge needs to be replaced by delay.

Robustness

It's almost identical to PATB Charge.

Performance

It's almost identical to PATB Core.

Coupling

It's almost identical to PATB Cooldown.

Cohesion

It's almost identical to PATB Charge, except that charge needs to be replaced with delay.

Flexibility

It's almost identical to PATB Charge.






Order Addon

This addon lets users set whether and how a window displaying all battlers' ATB Bar type fill ratios on the same line, effectively showing their ATB value ordering, as illustrated in the video:

You may want to check the plugin PATB Order for details.


Invariants

It's similar to PATB Bar in that it also shares all Nontrivial Invariants in PATB Core, except that PATB Order also has 1 new Nontrivial Invariant: The battle can only have actors and enemies as battlers.


It's directly manifested by the layouts of the battler order window, as it's supposed to display only 2 sides opposing each other - 1 side above the ATB bars, another below the ATB bars.


When this Nontrivial Invariant becomes a Broken Invariant, no battler being neither actors nor enemies will be displayed on the battler order window, leading to incomplete or even misleading vital information being displayed.




Correctness

The behavioral requirements are basically these:


1. Whether the battler order window will be shown must be strictly following its relevant configurations


2. The battler order window positions and dimensions must work correctly according to their relevant configurations


3. The positional offsets and width of each of the ATB bars in the battler order window must work correctly according to their relevant configurations


4. The positional offsets and size of the ATB bar description texts in the battler order window must work correctly according to their relevant configurations


5. The opacities, starting positional offsets, size and source bitmap of the battler sprites in the battler order window must work correctly according to their relevant configurations


6. The x positions of the battler sprites in the battler order window must accurately reflect their corresponing battlers' ATB value fill ratios.


The actual behaviors corresponding to the 1st behavioral requirement's implemented in Window_Patb_Order.prototype.update.


So the 1st behavioral requirement's completely consistent with its corresponding actual behaviors, as whether the battler order window's visible's solely controlled by the relevant configuration.


The actual behaviors corresponding to the 2nd behavioral requirement's implemented in Window_Patb_Order.prototype.initialize.


However, the 2nd behavioral requiement's drastically inconsistent with its corresponding actual behaviors, as changing the values of the relevant configurations during battles won't change the battler order window positions and dimensions during battle, due to those values not used at all in any other functions, and Window_Patb_Order.prototype.initialize will only be called once per battle.


The actual behaviors corresponding to the 3rd behavioral requirement's implemented in Window_Patb_Order.prototype.drawBars.


However, the 3rd behavioral requiement's drastically inconsistent with its corresponding actual behaviors, as Window_Patb_Order.prototype.drawBars is only called by Window_Patb_Order.prototype.initialize, meaning that the same reasons for the 2nd behavioral requirements apply to here as well.


The actual behaviors corresponding to the 4th behavioral requirement's implemented in Window_Patb_Order.prototype.drawTexts.


However, the 4th behavioral requiement's drastically inconsistent with its corresponding actual behaviors, as Window_Patb_Order.prototype.drawTexts is only called by Window_Patb_Order.prototype.initialize, meaning that the same reasons for the 2nd behavioral requirements apply to here as well.


The actual behaviors corresponding to the 5th behavioral requirement's implemented in Sprite_Patb_Order_Battler_Icon.prototype.update, Sprite_Patb_Order_Battler_Icon.prototype.updateBitmapSource, Sprite_Patb_Order_Battler_Icon.prototype.setBitmapSourceIcon, Sprite_Patb_Order_Battler_Icon.prototype.updateBitmap, Sprite_Patb_Order_Actor_Icon.prototype.setBitmapSourceBattler and Sprite_Patb_Order_Enemy_Icon.prototype.setBitmapSourceBattler.


However, the 5th behavioral requirement's noticeably inconsistent with its corresponding actual behaviors, as both the size and the starting x offsets of battler sprites in the battler order window is only set once in Sprite_Patb_Order_Battler_Icon.prototype.initialize(with the y offsets set in the actor and enemy sprite prototypes), meaning that changing their corresponding configuration values during battles will actually mean nothing even when it's supposed to mean something.


The actual behaviors corresponding to the 6th behavioral requiement's implemented in Sprite_Patb_Order_Battler_Icon.prototype.updateXPos.


So the 6th behavioral requirement's completely consistent with its corresponding actual behaviors, as the x positions of the battlers sprites in the battler order window relative to its ATB bars is based on their starting x offsets plus the battler ATB value fill ratio multiplied by ATB bar width added by the x offset of the ATB bar having the same type of those battlers.


Therefore PATB Order doesn't work correctly on the 2nd, 3rd, 4th and 5th behavioral requirements, meaning that this plugin has nontrivial bugs.

Robustness

Regarding how likely any Nontrivial Invariant will become a Broken Invariant, again, it's basically the same as that of PATB Charge, except that it'll also come when working with plugins having battlers other than actors and enemies.


Unfortunately, as I don't have much observations on such plugins, I can't draw any concrete conclusion on this question.


Regarding how severe the nontrivial bugs can be manifested when 1 happens, it's already mentioned in the Invariants section.


Regarding how easy a fault in this addon can propagate into any other plugin, the fault in PATB Order's almost completely isolated from any other plugin, because:


1. All entry points exposed by the PATB Order are covered by PATB Core, meaning that any fault in PATB Order can only damage PATB itself.


2. Neither PATB Core nor any PATB addon ever needs to know anything about PATB Order, even including its very existence, although PATB Order does depend on PATB Core and PATB Cooldown(the cooldown type sginature, in this case, "cooldown").


Note that this applies even when no battlers that are neither actors nor enemies will be shown on the battler order window.


Therefore, until I've sufficient observations on plugins having battlers other than actors and enemies, I can't answer how robust PATB Order really is.

Performance

Regarding what costly operations the codebase must do and how they can be optimized, there are some:


1. Redrawing the ATB bars in the battler order window


2. Redrawing the ATB bar descirption texts in the battler order window


3. Redrawing the battler sprites in the battler order window


How they can be optimized remains the same - Minimizing the frequnency of triggering any Reasons to Change, and only executes those costly operations when at least 1 Reasons To Change might have triggered.


Regarding what costly and/or unnecessary operations the codebase has done and how they're done, only the 3rd one's actually done, due to PATB Order being severely incorrect.


Actually, it's composed by 2 costly operations - loading bitmaps via ImageManager and performing bitmap block transfers in Sprite_Patb_Order_Battler_Icon.prototype.updateBitmap.


It should be 100% crystal clear and obvious that they're some of the most costly operations in the entire default RMMV codebase.


So, like any other costly operations, the battler sprites should only be redrawn when they're visible and at least 1 of their Reasons To Change might have triggered, including:


1. The battler sprite icon index has changed


2. The battler sprite size has changed(not implemented in PATB Order yet)


3. The source bitmap width and height has changed(not implemented in PATB Order yet)


All these are demonstated in Sprite_Patb_Order_Battler_Icon.prototype.update and Sprite_Patb_Order_Battler_Icon.prototype.updateBitmapSource.


In this case, PATB Order is actually too performant - At the cost of being incorrect due to missing some Reasons To Change.


On a side note: Updating the battler icon index in Sprite_Patb_Order_Battler_Icon.prototype.update is certainly a mistake, as it can instead be updated in Sprite_Patb_Order_Battler_Icon.prototype.updateBitmapSource to achieve better time performance while sacrificing no other important code quality at all, as updating the battler sprite's meaningless, pointless and useless if it's invisible to begin with.


Regarding benchmarking the number of ms spent on executing operations at a frame, I failed to benchmark it, unfortunately, due to some unknown reasons making my RMMV tests suffering from insane lags even in the default RMMV battle system.

Coupling

Regarding how much any other plugin must depend on this plugin, there's no dependency at all for any other plugin, as all PATB Order does is to display all battler sprites on the same line indicating their ATB value fill ratios and thus their ATB orderings.


Regarding how much this plugin must depend on any other plugin, PATB Order only needs to know the ATB types and te current ATB value fill ratios.


Regarding how much any other plugin actually depends on this plugin, the answer's basically the same as the 1st one, except that plugins having battlers other than actors and enemies will have to solve aforementioned compatibility issues PATB Order.


Unfortunately, due to the limited space in the battle screen, changing the battler order window to display more than 2 sides is unlikely to be sufficiently feasible.


Regarding how much this plugin actually depends on any other plugin, the answer's basically the same as the 2nd one, except that it also needs to know the ATB cooldown type signature, which is "cooldown" in this case, in order to ensure continuous battler sprites movements in the battler order window.


On a side note: I already have a solution to remove this coupling, even though this would mean slightly tweaking PATB Core, PATB Charge and PATB Cooldown.


Therefore, with the aforementioned solution implemented, PATB Order could almost exhibit the loosest coupling it can reasonably achieve.

Cohesion

Regarding how many functions this plugin needs to serve and how specific and well-defined they need to be, there's only 1 well-defined specific function: Displaying all battler sprites on the same line indicating their ATB value fill ratios and thus their ATB orderings.


This function's well-defined enough as it's crystal clear and obvious for those familiar with ATB systems, and is specific enough as it's nothing more than an displaying all battler sprites on the same line indicating their ATB value fill ratios and thus their ATB orderings.


Regarding how many functions this plugin actually serve and how specific and well-defined they actually are, it's the same as that of 1st one, meaning that PATB Order achieves functional cohesion.


Regarding how many functions this plugins can be implemented outside ATB systems, it's none, as PATB Order needs to know the ATB types and te current ATB value fill ratios, which are all unique to ATB systems.

Flexibility

Regarding how massive the 3 dimensions of control and freedom this plugin can give to its users, from my experience, knowledge and observation, it should be able to let users:


1. Set whether the battler order window will be shown


2. Set the battler order window positions and dimensions


3. Set the positional offsets and width of each of the ATB bars in the battler order window


4. Set the positional offsets and size of the ATB bar description texts in the battler order window


5. Set the opacities, starting positional offsets, size and source bitmap of the battler sprites in the battler order window


6. Set the x positions of the battler sprites in the battler order window


Under level 7 configuration control and freedom, Editable Javascript Codes.


Regarding how massive the 3 dimensions of control and freedom this plugin actually give to its users, it's similar to the above, except that the configuration control and freedom's at level 5, Javascript Codes.


Regarding how much cost will be needed for this plugin to offer more massive 3 dimensions of control and freedom to its users, it's basically the same as that of PATB Core, as PATB Bar uses PATB Core functions to read configuration values.


There's an exception though - Making this plugin support battlers other than actors and enemies, as doing so would increase the space needed for the battler order window. Due to the limited size of the battle screen, it's probably not so feasible.


Regarding how capable this plugin can handle extreme scales(data sizes, data change frequency, etc), this plugin will likely suffer from serious time performance issues quickly, especially when many battler sprites have to be redrawn extremely frequently over a long time.


Therefore PATB Charge is certainly no where near flexible, but should still be flexible enough for most ordinary uses(which is to keep this plugin easy, simple and small enough to be sufficiently popularized).


Of course, when it comes to working with plugins having battlers other than actors and enemies, this plugin can only do little, due to the aforementioned battle screen size issue.









Addon Ordering Rules

Definition



Spoiler



Recall that temporal coupling exists among some components when those components must be called under some particular timing/ordering rules.


So loosely speaking, if an addon must be placed above/below another addon, then these addons can also be roughly regarded as exhibiting temporal coupling.


This leads to the below vague definition:


Addon ordering rules are the descriptions of temporal couplings among addons.


Also, recall these definitions:


- Explicit Temporal Coupling is a temportal coupling where those particular timing/ordering rules are extremely crystal clear and obvious.


- Implicit Temporal Coupling is a temporal coupling where those particular timing/ordering rules are extremely elusive.


This leads to the below defintions:


- Explicit Addon Ordering Rules are addon ordering rules that are well-informed to nearly all their targeting audiences.


- Implicit Addon Ordering Rules are addon ordering rules that are really elusive from most of their targeting audiences.


As mentioned, Implicit Temporal Coupling's almost always undesirable, as converting it into an Explicit Temporal Coupling's almost always easy, simple and small - Just clearly documents that temporal coupling.


The same applies to addon ordering rules, meaning that all addon ordering rules should be clearly documented.

Removal

Recall that PATB Charge needs to know whether PATB Cooldown's present in order to work correctly.


If I would add this addon ordering rule - PATB Cooldown must be placed above PATB Charge, I could remove the aforementioned coupling.


Now let's consider the opposite situation - PATB Cooldown must be placed above PATB Charge to begin with.


Suppose I remained that nub in this case, but I wanted to remove that addon ordering rule.


Then I'd realize that doing so will have to slightly tweak PATB Charge to work differently depending on whether PATB Cooldown exists.


Of course, in this case, I'll remove that addon ordering rule, as the added coupling is barely nontrivial, while preserving the addon ordering rule can lead to significant decrease of user-friendliness in some use cases.


However, the key of the above example is this: Removing addon ordering rules isn't always certainly better, as it sometimes has its tradeoffs that can be very case-specific instead. How to get the balance is too advanced to be covered here.






Interaddon Dependencies

Definition



Spoiler



Simply put, Interaddon Dependencies measures the coupling among addons.


Sometimes, addon A depends on addon B while addon B doesn't depend on addon A.


For example, both PATB Charge and PATB Order depends on PATB Cooldown but PATB Cooldown doesn't depend on either PATB Charge nor PATB Order.


In this case, there are 2 Interaddon Dependencies, each having degree of 1. The minimum, maximum and total degree of Interaddon Dependencies of PATB is thus 0(some PATB addons are completely decoupled from each other), 1 and 2 respectively. Mean, median, mode and range can also be used, but their usage should just be the same as the minimum, maximum and total.


On the other hand, back in working with CATB in RMVXA with my addons, I've experienced cases where addon A depends on addon B and C, addon B depends on addon A and C, and addon C depends on addon A and B.


In this case, there's just 1 Interaddon Dependeny among those addons, albeit having degree of 2 + 2 + 2 = 6(I recall that the maximum degree of Interaddon Dependencies of CATB with my addons is 12, which is just outright ridiculous).


Unfortunately, any rigorous defintions involving Interaddon Dependencies involves digraphs, which are too advanced to be covered here.

Removal

As mentioned, if the core plugin serves as the common interface used by all addon plugins, then many Interaddon Dependencies can be removed.


However, sometimes removing Interaddon Dependencies without excessively overenginnering the core plugin inevitably means adding addon ordering rules.


For instance, back in RMVXA, I've made te unison addon for CATB and the unison module for ECATB.


Simply put, they both serves to implement unison skills/items in specific ATB systems.


If you're familiar with both ATB systems and unison skills/items, you should be able to realize that, without excessively overenginnering the core plugin, the below Interaddon Dependencies can only be removed by adding addon ordering rules:


1. The unison addon must depend on the charge addon.


2. The unison addon must depend on the cooldown addon.


3. The charge addon must depend on the unison addon.


4. The cooldown addon must depend on the unison addon.


So it's assumed that no addon ordering rule exists among the charge, cooldown and unison addons.


Proof of 1 and 3:


Let's consider charging unison skills/items.


Recall that:


- Charging skills/items means the existence of waiting time between finishing inputting the skills/items and actually executing them.


- Unison skills/items are those used by more than 1 battlers simultaneously.


The combination of the above 2 definitions means that, unison skills/items must be charged by all the involved battlers simultaneously, meaning that their ATB charging values must be kept synchronized.


If there're neither Interaddon Dependencies nor addon ordering rules between charge and unison addons, neither can know the existence of the other, nor whether the other's placed above or below itself.


It means that the charge addon has no way to know whether the skills/items are unison ones.


Without excessively overenginnering the core plugin to the point that every skill/item's regarded as unison skills/items with the ordinary ones treated as unison skills/items involving just 1 battler(which is just outright ridiculous), charging ordinary skills/items and charging unison ones certainly needs noticeably different implementations.


So the charge addon can't correctly judge which implementation should be used, leading to it being incorrect.


Similarly, the unison addon has no way to know whether the skills/items need charging.


Without excessively overenginnering the core plugin to the point that every skill/item actually needs charging, with the ordinary ones always being fully charged in 1 ATB Frame Update(which is complete insanity in the core plugin but totally acceptable in the charge addon), unison skills/items demanding charging and not demanding charging certainly needs noticeably different implementations.


So the unison addon can't correctly judge which implementation should be used, leading to it being incorrect.


Proof of 2 and 4: Basically the same as that of 1 and 3, except that charge is replaced with cooldown.


In short, a trilemma exists among removing Interaddon Dependencies, removing addon ordering rules and avoding excessively overengineering the core plugin, when the unison addon can be used with the charge and/or the cooldown addon.






Summary

Normally, an addon plugin inherits at least some Nontrivial Invariants from the core plugin, as the former has to reuse some implementations of the latter.


Evaluating correctness involves these 3 parts in practice:


1. The behavioral requirements


2. The actual behaviors


3. The inconsistencies between 1 and 2


Evaluating robustness involves these 3 parts in practice:


1. How likely any Nontrivial Invariant will become a Broken Invariant


2. How severe the nontrivial bugs can be manifested when 1 happens


3. How easy a fault in this plugin can propagate into any other plugin


Evaluating time performance involves these 3 parts in practice:


1. Reasoning about what costly operations the codebase must do and how they can be optimized


2. Reasoning about what costly and/or unnecessary operations the codebase has done and how they're done


3. Actually benchmarking the number of ms spent on executing operations at a frame on machines barely meeting the RMMV minimum requirements


Minimizes the frequency of triggering any Reasons to Change, and only executes those costly operations when at least 1 Reasons To Change might have triggered, in order to optimize for time performance. Just bear in mind that going to far can lead to missing some Reasons To Change, causing nontrivial bugs to appear.


Evaluating coupling involves these 4 parts in practice:


1. How much any other plugin must depend on this plugin


2. How much this plugin must depend on any other plugin


3. How much any other plugin actually depends on this plugin


4. How much this plugin actually depends on any other plugin


If the core plugin can effectively serve as the common interface used by all the addon plugins, then those addon plugins can become very loosely coupled.


Evaluating cohesion involves these 2 parts in practice:


1. How many functions this plugin needs to serve and how specific and well-defined they need to be


2. How many functions this plugin actually serve and how specific and well-defined they actually are


Evaluating cohesion in ATB systems involves this in practice as well:


3. How many functions this plugin can be implemented outside ATB systems


Evaluating flexibility involves these 4 parts in practice:


1. How massive the 3 dimensions of control and freedom this plugin can give to its users


2. How massive the 3 dimensions of control and freedom this plugin actually gives to its user


3. How much cost will be needed for this plugin to offer more massive 3 dimensions of control and freedom to its users


4. How capable this plugin can handle extreme scales(data sizes, data change frequency, etc)


Addon ordering rules are the descriptions of temporal couplings among addons.


Explicit Addon Ordering Rules are addon ordering rules that are well-informed to nearly all their targeting audiences.


Implicit Addon Ordering Rules are addon ordering rules that are really elusive from most of their targeting audiences.


Removing addon ordering rules isn't always certainly better, as it sometimes has its tradeoffs that can be very case-specific instead.


Interaddon Dependencies measures the coupling among addons.


In some cases, trilemmas can exist among removing Interaddon Dependencies, removing addon ordering rules and avoding excessively overengineering the core plugin.
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,773
Reaction score
938
First Language
Chinese
Primarily Uses
N/A
Now let's talk about IV. Detailed DoubleX RMMV Minimalized ATB Reasoning


Because it's easy, simple and small, I can afford a really detailed reasoning on MATB, so be prepared on delving deep into rather technical stuffs.


Nevertheless, I'm not going to cover portions solely serving for bug fixes lol


Invariants

Only new Nontrivial Invariants will be covered here.


Battle Frame Update



Spoiler



As Scene_Battle.prototype.updateBattleProcess is rewritten to change the Battle Frame Update to run the ATB Frame Update when the ATB Wait Condition's not met:



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.phase === 'action') return BattleManager.updateAction();
if (BattleManager.can_update_matb() && !this.isAnyInputWindowActive()) {
this.update_matb();
}
//
}; // Scene_Battle.prototype.updateBattleProcess

It becomes clear that any plugin placing below MATB rewriting Scene_Battle.prototype.updateBattleProcess or not using this version when extending it will break MATB.




ATB Wait Condition

As Scene_Battle.prototype.isAnyInputWindowActive implements the ATB Wait Condition in MATB, it's hardcoded as being always met whenever players are inputting actions.


It means that, if Scene_Battle.prototype.isAnyInputWindowActive is rewritten or extended to be possible to return false even when players are inputting actions, the ATB Wait Condition will be broken.


As mentioned earlier, once the ATB Wait Condition becomes Absolutely Stricter, many previously non-issues will become real ones that must be handled. So this will lead to many other aspects of MATB being broken.
ATB Frame Update



Scene_Battle.prototype.update_matb and BattleManager.update_matb implements the ATB Wait Condition:



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

Code:
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

Basically, there's no new Nontrivial Invariants introduced by MATB when it comes to the ATB Frame Update itself.


Battler ATB Clock



Game_Battler.prototype.update_matb implements the Battler ATB Clock:



Spoiler






Game_Battler.prototype.update_matb = function() { // New; Hotspot
if (this._matb_val >= 100 || this.restriction() > 3) return;
this._matb_val_ch = this._matb_val !== (this._matb_val += this.agi / 100);
if (this._matb_val < 100) return;
this._matb_val = 100;
this.makeActions();
}; // Game_Battler.prototype.update_matb

There are 2 new Nontrivial Invariants introduced here:


1. The Maximum ATB Value is always hardcoded as a fixed value(100 in this case)


2. The ATB Rate is hardcoded as a fixed formula(agi / Maximum ATB Value per frame in this case)


Setting Up Action Execution Subject



BattleManager.process_matb_turn setups new Action Execution Subjects:



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

Basically, there's 1 already exist Nontrivial Invariant here - Only 1 battler can be the Action Execution Subject at the exact same frame.


It's because it also applies to all Scene_Battle based battle systems, except those written by real geniuses lol


Battle Turn Clock



BattleManager.update_matb_turn and BattleManager.end_matb_turn implements the Battle Turn Clock:



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

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

There's 1 new Nontrivial Invariant and 1 already existing all the entire RMMV codebase:


New - The Battle Turn Clock must be based on a number of frames passed


Already existing - The number of frames per second must be a fixed value(60 in this case)


Setup Inputable Actors



Scene_Battle.prototype.update_matb is also responsible for finding newly inputable actors, while Scene_Battle.prototype.update_matb_actor_selection setups an actor command window for the chosen newly inputable actor:



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

Basically, there's no new Nontrivial Invariants introduced by MATB when it comes to setting up newly inputable actors itself.


Actor ATB Bars



Window_BattleStatus.prototype.refresh_matb_bars checks if any actor ATB bar needs to be redrawn, and Window_BattleStatus.prototype.draw_actor_matb redraws them:



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

/* 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 matb = $gameSystem.matb, w = matb.atb_bar_w;
var c0 = this.textColor(matb.atb_c1), c1 = this.textColor(matb.atb_c2);
this.drawGauge(x, y, w, actor.matb_val / 100, c0, c1);
this.changeTextColor(this.systemColor());
this.drawText('AP', x, y, this.textWidth('AP'));
}; // Window_BattleStatus.prototype.draw_actor_matb

There are basically 3 new Nontrivial Invariants:


1. The actor ATB bar must be drawn at the right hand side of the actor mp/tp bar on the status window


2. The actor ATB bar colors must be composed by text colors


3. The actor ATB bar description text must be hardcoded as a fixed value(AP in this case)


Starting ATB Value



BattleManager.startBattle, BattleManager.start_matb_battle, Game_Battler.prototype.set_start_matb_val and Game_BattlerBase.prototype.set_start_matb_val implements the starting ATB value:



Spoiler






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

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

Code:
// 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
Code:
// 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

So basically there are 3 Nontrivial Invariants here - 2 being new and 1 already existing:


New - The starting ATB value for all battlers are always hardcoded to various fixed values depending on the battle start type, and the extended functions must not be rewritten or ignored via extension by plugin placed below MATB


Already Existing - The battle start type only consists of normal, preemptive and surprise


Resetting ATB Value



Game_Battler.prototype.reset_matb implements the resetting ATB value:



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

Basically, there's 1 new Nontrivial Invariant here - The ATB value can only be reset to a fixed value which is hardcoded.


Action Confirmation Flag



Game_Action.prototype.setSkill, Game_Action.prototype.setItem, Game_Action.prototype.confirm_matb_item, Scene_Battle.prototype.commandGuard, Scene_Battle.prototype.onActorOk, Scene_Battle.prototype.onEnemyOk and Scene_Battle.prototype.confirm_matb_act implements the action confirmation flag:



Spoiler






Game_Action.prototype.setSkillMatb = Game_Action.prototype.setSkill;
Game_Action.prototype.setSkill = function(skillId) {
this.setSkillMatb(skillId);
this.confirm_matb_item(); // Added
}; // Game_Action.prototype.setSkill

Game_Action.prototype.setItemMatb = Game_Action.prototype.setItem;
Game_Action.prototype.setItem = function(itemId) {
this.setItemMatb(itemId);
this.confirm_matb_item(); // Added
}; // Game_Action.prototype.setItem

Game_Action.prototype.confirm_matb_item = function() { // New
this._matb_confirm = this._matb_confirm || !this.needsSelection();
}; // Game_Action.prototype.confirm_matb_item

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



There's 1 new Nontrivial Invariant introduced here: No function extended by MATB can be rewritten or ignored via further extending them by plugins placed below MATB.


On a side note: It turns out that the entire action confirmation flag is simply an accidental complexity, due to BattleManager._actionBattlers taking 2 responsibilities - marking inputable actors and battlers eligible for being the Action Execution Subject, instead of adhering to the single responsibility principle. This leads to a need to introduce a new flag to separate those 2 responsibilities. This can be easily remedied by delegating the 1st responsibility to a new array(like $gameParty._inputable_matb_actors), but it's beyond the scope of this reply.


Failed Party Escape Attempt



Game_Party.prototype.clearActions resets all actors' Battler ATB Clock:



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

Basically, there's 1 already existing Nontrivial Invariant: Game_Unit.prototype.clearActions must be called by failed party escape attempts, otherwise actors with autobattle/confusion will still execute those automatically inputted actions.






In short, MATB has quite a lot of invariants, mainly because many things are hardcoded.






Correctness

Recall that evaluating correctness involves 3 parts - Clarifying behavioral requirements, analyzing actual behaviors, and comparing them under the plugin's targeted contexts.


Battle Frame Update



Spoiler



The behavioral requirements here are easy, simple and small:


1. Asks the Action Execution Subject to execute actions


2. Runs the ATB Frame Update when the ATB Wait Condition isn't met


Which is the same as the actual behaviors, because BattleManager.updateAction in the default RMMV codebase is exactly executing actions for the Action Execution Subject, and its Action Execution Phase(BattleManager._phase === 'action') means that there's the Action Execution Subject executing actions.

ATB Wait Condition

The behavioral requirements here are easy, simple and small: It must always be met whenever players are inputting commands.


Which is the same as the actual behaviors, because the default RMMV Scene_Battle.prototype.isAnyInputWindowActive will always return true whenever any input window's active, and at least 1 input window must be active in order for players to input commands.
ATB Frame Update



The behavioral requirements here are easy, simple and small:


1. It must run the Battler ATB Clock first


2. It must setup new Action Execution Subject immediately afterwards, unless there's already one


3. It must then run the Battle Turn Clock


4. It must also setup newly inputable actors, unless the actor command window's already setup


5. It must update the actor ATB bar on the status window as well


Which is exactly what Scene_Battle.prototype.update_matb and BattleManager.update_matb are doing.
Battler ATB Clock



The behavioral requirements here are easy, simple and small:


1. Only movable battlers can have their Battler ATB Clock updated


2. Only battlers not having full ATB value can have their Battler ATB Clock updated, with its rate based on agi and Maximum ATB Value


3. Battlers becoming having full ATB value should make new action slots in order to input actions


Which is exactly what Game_Battler.prototype.update_matb is doing.
Setting Up Action Execution Subject



The behavioral requirements here are easy, simple and small:


1. Nothing should be done if there's Action Execution Subject already


2. Otherwise the 1st battler being eligible to be the Action Execution Subject should become the Action Execution Subject


Which is exactly what BattleManager.process_matb_turn is doing, because the default RMMV BattleManager.processTurn is exactly setting up a new Action Execution Subject.
Battle Turn Clock



The behavioral requirements here are easy, simple and small:


1. The Battle Turn Clock must be updated per ATB Frame Update


2. The battle turn counter must be updated after a number of seconds composed by ATB Frame Updates has passed in the same turn


Which is exactly what BattleManager.update_matb_turn and BattleManager.end_matb_turn are doing, because:


1. BattleManager.update_matb_turn is always called exactly once per ATB Frame Update


2. The number of frames per seconds is hardcoded as 60 in the default RMMV


Note that the Battle Turn Clock's still correct even when max_turn_unit changes during the same battle, although changing that extremely frequently can indeed be really confusing(and setting max_turn_unit as an extremely small value can actually lead to extremely severe performance issues).
Setup Inputable Actors



The behavioral requirements here are easy, simple and small:


1. Nothing should be done when players are already inputting commands


2. Otherwise the actor command window for the 1st newly inputable actor should be setup to let players input commands for that actor


Which is exactly the same as what Scene_Battle.prototype.update_matb and Scene_Battle.prototype.update_matb_actor_selection are doing.
Actor ATB Bars



The behavioral requirements here are easy, simple and small: The actor ATB bars should always accurately display the corresponding Battler ATB Clocks.


Which is exactly the same as what Window_BattleStatus.prototype.refresh_matb_bars and Window_BattleStatus.prototype.draw_actor_matb are doing, because there's no need to redraw the ATB bars when the Battler ATB Clocks themselves don't change.
Starting ATB Value



The behavioral requirements here are easy, simple and small:


1. The Battler ATB Clock must be empty for unmovable battlers, actors with battle start type other than preemptive, or enemies with battle start type other than surprise


2. The Battler ATB Clock must be full for other cases


3. Actions must be made right upon battle start for battlers having full Battler ATB Clocks


Which is exactly what functions implementing starting ATB value is doing.
Resetting ATB Value



The behavioral requirements here are easy, simple and small:


1. The Battler ATB Clock must be reset to be empty


2. All actions must be cleared


3. The battler must be removed from BattleManager._actionBattlers, which is used to mark inputable actors and battlers eligible to be the Action Execution Subject


Which is exactly what Game_Battler.prototype.reset_matb is doing.
Action Confirmation Flag



While it's completely unnecessary if MATB's refactored to use a much more elegant design, right now it's still needed.


Nevertheless, the behavioral requirements here are easy, simple and small: The action confirmation flag should be raised for actor actions if and only if those actions are completely inputted by players.


It's because its sole purpose in MATB is to differentiate between inputable actors and battler eligible to be the Action Execution Subject, in which they're all stored in the same BattleManager._actionBattlers, which is itself a violation to the single responsibility principle.


That's exactly what function related to action confirmation flag's doing.

Failed Party Escape Attempt

The behavioral requirements here are easy, simple and small: All party members must have their Battler ATB Clocks reset right after a failed party escape attempt.


Which is exactly the same as what Game_Party.prototype.clearActions is doing.



In short, MATB is basically correct.





Robustness

Recall that a simplified measure on robustness is how likely the Nontrivial Invariants will become Broken Invariants, how much havoc they can wreck when that does happen, and how far those damage can propagate.


Battle Frame Update



Spoiler



There's only 1 new Nontrivial Invariant - Any plugin placing below MATB rewriting Scene_Battle.prototype.updateBattleProcess or not using this version when extending it will break MATB.


From my experience, knowledge and observation, that's quite unlikely, because doing so will likely break lots of things at the first place, and plugins really doing that are likely battle systems themselves, meaning that they're not supposed to work with MATB at the very beginning.


Nevertheless, if this Nontrivial Invariant does become a Broken Invariant, the result can be really severe - The entire MATB can completely cease to function at the worse case, where the ATB Frame Update can become never ever called.

ATB Wait Condition

There are actually 2 new Nontrivial Invariants:


1. If Scene_Battle.prototype.isAnyInputWindowActive is rewritten or extended to be possible to return false even when players are inputting actions, the ATB Wait Condition will be broken.


2. Once the ATB Wait Condition becomes Absolutely Stricter, many previously non-issues will become real ones that must be handled. So this will lead to many other aspects of MATB being broken.


Note that the 2nd Nontrivial Invariant actually depends on the 1st one, meaning that there's Interinvariant Dependency.


From my experience, knowledge and observation, the 1st Nontrivial Invariant's quite unlikely to become a Broken Invariant, because doing so will likely break lots of things at the first place, and plugins really doing that are likely battle systems themselves, meaning that they're not supposed to work with MATB at the very beginning.


On the other hand, the 2nd Nontrivial Invariant heavily depends on the positioning of MATB. Basically, it should almost never ever become a Broken Invariant as long as it remains as a minimum viable product serving as a tool for RMMV ATB system plugin development tutorials.


Nevertheless, if that Nontrivial Invariant does become a Broken Invariant, the entire MATB will become extremely buggy, because it's taking quite a lot of shortcuts that are only correct for the Absolutely Loosest ATB Wait Condition(check the comparison of MATB and PATB Core for details).
ATB Frame Update



As there's no new Nontrivial Invariants introduced by MATB when it comes to the ATB Frame Update itself, there's no need to evaluate robustness here.
Battler ATB Clock



There are 2 new Nontrivial Invariants introduced here:


1. The Maximum ATB Value is always hardcoded as a fixed value(100 in this case)


2. The ATB Rate is hardcoded as a fixed formula(agi / Maximum ATB Value per frame in this case)


Because MATB's supposed to be a minimum viable product serving as a tool for RMMV ATB system plugin development tutorials, both of them should almost never ever become Broken Invariants.


Even if that does happen, no serious damage will take place, because replacing those hardcoded values with configurations' easy, simple and small, as long a those configurations are themselves easy, simple and small too. Also, all the other components will be safe.
Setting Up Action Execution Subject



As there's no new Nontrivial Invariants introduced by MATB when it comes to the Setting Up Action Execution Subject itself, there's no need to evaluate robustness here.


The only exception is when you feel/think so gifted in RMMV plugin development that you want to try to challenge this Nontrivial Invariant applied to all Scene_Battle based battle systems - Only 1 battler can be the Action Execution Subject at the exact same frame. This is far beyond the scope of this reply, and is far too advanced to be covered here as well.
Battle Turn Clock



There's 1 new Nontrivial Invariant - The Battle Turn Clock must be based on a number of frames passed.


Because MATB's supposed to be a minimum viable product serving as a tool for RMMV ATB system plugin development tutorials, it should almost never ever become a Broken Invariant.


But if it does, the damage it causes can be consiedrable, and quite some refactoring will have to be done. Fortunately, all the other components will be safe.


Basically, if the Battle Turn Clock can also be based on the number of actions executed in the same turn, BattleManager.endAction will have to be extended to call BattleManager.update_matb_turn, which needs to be modified to take the unit indicator as the argument. this._matb_turn_clock has to be modified to support both units, and the action counterpart of max_turn_unit has to be added to be configurations as well.
Setup Inputable Actors



As there's no new Nontrivial Invariants introduced by MATB when it comes to setting up newly inputable actors itself, there's no need to evaluate robustness here.
Actor ATB Bars



There are basically 3 new Nontrivial Invariants:


1. The actor ATB bar must be drawn at the right hand side of the actor mp/tp bar on the status window


2. The actor ATB bar colors must be composed by text colors


3. The actor ATB bar description text must be hardcoded as a fixed value(AP in this case)


Because MATB's supposed to be a minimum viable product serving as a tool for RMMV ATB system plugin development tutorials, all of them should almost never ever become Broken Invariants.


In case that does happen, unnegligible damage can be caused, although all the other components will be safe.


While fixing that for the 2nd and 3rd ones are as easy as modifying and adding configurations, fixing that for the 3rd one involves a lot more work.


It's because with all the actor names, state icons, hp, mp and tp bars(which can be either absent or present) being present, there are so much combinations that adding configurations to set the x positions for all of them can lead to a much longer configuration list and a much larger codebase.
Starting ATB Value



There are 2 new Nontrivial Invariants:


1. The starting ATB value for all battlers are always hardcoded to various fixed values depending on the battle start type


2. The extended functions must not be rewritten or ignored via extension by plugin placed below MATB


For the 1st one, because MATB's supposed to be a minimum viable product serving as a tool for RMMV ATB system plugin development tutorials, it should almost never ever become a Broken Invariant.


Even if it does happen, fixing that, which mainly involves adding new configurations,  won't involve too much work, albeit a bit structural refactoring to Game_BattlerBase.prototype.set_start_matb_val is inevitable.


From my experience, knowledge and observation, the 2nd Nontrivial Invariant's quite unlikely to become a Broken Invariant, because doing so will likely break lots of things at the first place, and plugins really doing that are likely battle systems themselves, meaning that they're not supposed to work with MATB at the very beginning.


But if it does happen, then the MATB can have some real troubles. It's because then the state of the Battler ATB Clocks in the previous battle will be carried over to the next battle, which is clearly undesirable unless it's explicitly stated as a feature. Nevertheless, all the other components will be safe.
Resetting ATB Value



There's 1 new Nontirvial Invariant: The ATB value can only be reset to a fixed value which is hardcoded.


Because MATB's supposed to be a minimum viable product serving as a tool for RMMV ATB system plugin development tutorials, it should almost never ever become a Broken Invariant.


Even if it does happen, just a bit of refactoring in Game_Battler.prototype.reset_matb(by making it taking the ATB reset value as the argument) and adding 1 configuration can fix the slight damage it causes. Also, all the other components will be safe.
Action Confirmation Flag



While it's accidental complexity, I'll still analyze its robustness.


There's 1 new Nontrivial Invariant - No function extended by MATB can be rewritten or ignored via further extending them by plugins placed below MATB.


From my experience, knowledge and observation, it's quite unlikely to become a Broken Invariant, because doing so will likely break lots of things at the first place, and plugins really doing that are likely battle systems themselves, meaning that they're not supposed to work with MATB at the very beginning.


Nevertheless, if that does happen, inputable actors can be screwed. Either they won't be regarded as battlers eligible as the Action Execution Subject but instead be regarded as inputable actors even when they've completely inputted actions, or the opposite can happen even when they're still inputting actions. What's worse, this can eventually lead to the entire MATB to effectively stop working, because the behaviors can be more and more chaotic, possibly to the point that no one can really know what's actually going on.

Failed Party Escape Attempt

As there's no new Nontrivial Invariants introduced by MATB when it comes to sparty escape attempt, there's no need to evaluate robustness here.



In short, MATB is quite robust on many aspects, but really fragile on some others. What really stands out is the ATB Wait Condition, which can basically break the entire MATB completely even when it just becomes slightly buggy for a short time.





Performance

Recall that evaluating performance is only meaningful when there are costly operations that might be run extremely frequently. It implies that all such operations should be identified first.


Also, evaluating time performance involves these 3 parts in practice:


1. Reasoning about what costly operations the codebase must do and how they can be optimized


2. Reasoning about what costly and/or unnecessary operations the codebase has done and how they're done


3. Actually benchmarking the number of ms spent on executing operations at a frame on machines barely meeting the RMMV minimum requirements


Battle Frame Update



Spoiler



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.

ATB Wait Condition

There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
ATB Frame Update



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Battler ATB Clock



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.


Actually, there's 1 exception - when max_turn_unit is set as an extremely small value, which will lead to extremely frequent BattleManagter.refreshStatus call, which is an extremely costly operation.


However, it's really so unlikely that such an extraordinary case can be safely ignored.
Setting Up Action Execution Subject



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Battle Turn Clock



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Setup Inputable Actors



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Actor ATB Bars



There should only be 1 costly operation that are run frequently - redrawing the actor ATB bars. If updating the whole status window can take place per frame, then the entire plugin's screwed in terms of performance, because it's one of the most costly operations in the entire RMMV battle codebase.


Basically, there are only 2 reasons to redraw an actor ATB bar:


1. The ATB fill ratio has changed(here it's the same as the Battler ATB Clock has changed, because the Maximum ATB Value's always a constant)


2. Any configuration other than max_turn_unit has changed


In this case, Game_Battler.prototype._matb_val_ch serves as the change notification flag of the Deterministic Reasons To Change Detector for the aforementioned Reasons To Change.


While it's 100% crystal clear and obvious for the 1st one, as shown in Game_Battler.prototype.update_matb, it's alos the case for the 2nd one.


It's because the 2nd one must be explicitly done by users, meaning that they can simply use the below plugin call to raise Game_Battler.prototype._matb_val_ch:



Spoiler






* 3. matb_val_ch = true
* - Marks that the battler's atb value has just changed
* It must be used when any configuration other than max_turn_unit
* changes during battles

While it undoubtedly violates the principle of least astonishemnt, it's still the easiest, simplest and smallest design I can come up with in MATB uptil now.


It means that MATB has done no unnecessary costly operations here.


Finally, regarding benchmark, it's basically 1ms extra load per actor ATB bar per frame on my machine(WinXP + i7-3820 + 2 * 2GB DDR3 + GTX550Ti).


Starting ATB Value



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Resetting ATB Value



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Action Confirmation Flag



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.
Failed Party Escape Attempt



There's no costly operations that are run frequently, so there's no real need to evaluate performance here.



In short, MATB is rather performant, even though it's not really ideal on machines barely meeting the RMMV system requirement.






Coupling

Recall that evaluating coupling involves these 4 parts in practice:


1. How much any other plugin must depend on this plugin


2. How much this plugin must depend on any other plugin


3. How much any other plugin actually depends on this plugin


4. How much this plugin actually depends on any other plugin


Battle Frame Update



Spoiler



You may want to check introducing component cohesion/coupling digraphs for details.

ATB Wait Condition

You may want to check introducing component cohesion/coupling digraphs for details.
ATB Frame Update



You may want to check introducing component cohesion/coupling digraphs for details.
Battler ATB Clock



You may want to check introducing component cohesion/coupling digraphs for details.
Setting Up Action Execution Subject



You may want to check introducing component cohesion/coupling digraphs for details.
Battle Turn Clock



You may want to check introducing component cohesion/coupling digraphs for details.
Setup Inputable Actors



You may want to check introducing component cohesion/coupling digraphs for details.
Actor ATB Bars



You may want to check introducing component cohesion/coupling digraphs for details.
Starting ATB Value



It should be certain that BattleManager.startBattle exhibits message coupling, as it just needs to know the function name of BattleManager.start_matb_battle.


On the other hand, BattleManager.start_matb_battle exhibits control coupling with Game_Battler.prototype.set_start_matb_val and Game_BattlerBase.prototype.set_start_matb_val, because the passed argument start will control the flow in Game_BattlerBase.prototype.set_start_matb_val.


Refactoring this to become message coupling is easy, simple and small by utilizing message passing, even though doing so will violate the spirit of being a minimum viable product:



Spoiler






// start: The battle start type
Game_BattlerBase.prototype.set_start_matb_val = function(start) { // New
if (!this.canMove()) return this._matb_val = 0;
this["set_start_matb_val_" + start]();
}; // Game_BattlerBase.prototype.set_start_matb_val

Game_BattlerBase.prototype.set_start_matb_val_preempt = function() { // New
// Ensure the plugin will still work for battlers other than actors/enemies
this.set_start_matb_val_norm();
//
}; // Game_BattlerBase.prototype.set_start_matb_val_preempt

Game_BattlerBase.prototype.set_start_matb_val_surprise = function() { // New
// Ensure the plugin will still work for battlers other than actors/enemies
this.set_start_matb_val_norm();
//
}; // Game_BattlerBase.prototype.set_start_matb_val_surprise

Game_BattlerBase.prototype.set_start_matb_val_norm = function() { // New
this._matb_val = 0;
}; // Game_BattlerBase.prototype.set_start_matb_val_norm

// 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

Game_Actor.prototype.set_start_matb_val_preemptive = function() { // New
this._matb_val = 100;
}; // Game_Actor.prototype.set_start_matb_val_preemptive

Game_Enemy.prototype.set_start_matb_val_surprise = function() { // New
this._matb_val = 100;
}; // Game_Enemy.prototype.set_start_matb_val_surprise

Similarly, BattleManager.start_matb_battle can be refactored from exhibiting stamp coupling(by knowing battlers via $gameParty) to data coupling:

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

// start: The battle start type
Game_Unit.prototype.set_start_matb_val = function(start) { // New
this.members().forEach(function(mem) { mem.set_start_matb_val(start); });
}; // Game_Unit.prototype.set_start_matb_val

Strictly speaking, it's still slightly violating the Law of Demeter due to BattleManager now knowing some function names of battlers, but I don't think there's any real benefit for going that far towards decoupling here.


On a higher level, starting ATB value has message coupling with Battler ATB Clock, and no coupling with any other components.


Resetting ATB Value



Game_Battler.prototype.reset_matb is exhibiting stamp coupling with BattleManager due to exposing BattleManager._actionBattlers.


This can be easily refactored to become data coupling:



Spoiler






BattleManager.remove_matb_action_battlers = function(battler) { // New
var index = BattleManager.action_battlers.indexOf(battler);
if (index >= 0) BattleManager.action_battlers.splice(index, 1);
if (BattleManager.actor() === battler) BattleManager.clearActor();
}; // BattleManager.remove_matb_action_battlers

Game_Battler.prototype.reset_matb = function() { // New
this._matb_val = 0, this._matb_val_ch = true;
this.clearActions();
BattleManager.remove_matb_action_battlers(this);
}; // Game_Battler.prototype.reset_matb

On a higher level, resetting ATB value has message coupling with Battler ATB Clock, and no coupling with any other components.


Action Confirmation Flag



All functions implementing the action confirmation flag are basically exhibitng message coupling or data coupling.


On a higher level, action confirmation flag has stamp coupling with setting up Action Execution Subject and setup inputable actors, and no coupling with any other components.


Fixing those stamp coupling requires a much more elegant design, which is beyond the scopr of this reply.
Failed Party Escape Attempt



Game_Party.prototype.clearActions is exhibiting message coupling.


On a higher level, failed party escape attempt has message coupling with resetting ATB value, but no coupling with any other components.



In short, MATB exhibits quite a low coupling, although not to the point of being ideal on all aspects. But fixing those areas falling short is easy, simple and small.






Cohesion

Recall that evaluating cohesion involves these 2 parts in practice:


1. How many functions this plugin needs to serve and how specific and well-defined they need to be


2. How many functions this plugin actually serve and how specific and well-defined they actually are


Evaluating cohesion in ATB systems involves this in practice as well:


3. How many functions this plugin can be implemented outside ATB systems


Battle Frame Update



Spoiler



You may want to check introducing component cohesion/coupling digraphs for details.

ATB Wait Condition

You may want to check introducing component cohesion/coupling digraphs for details.
ATB Frame Update



You may want to check introducing component cohesion/coupling digraphs for details.
Battler ATB Clock



You may want to check introducing component cohesion/coupling digraphs for details.
Setting Up Action Execution Subject



You may want to check introducing component cohesion/coupling digraphs for details.
Battle Turn Clock



You may want to check introducing component cohesion/coupling digraphs for details.
Setup Inputable Actors



You may want to check introducing component cohesion/coupling digraphs for details.

Actor ATB Bars

You may want to check introducing component cohesion/coupling digraphs for details.

Starting ATB Value

It's exhibiting functional cohesion, as all it does is to set the initial states for the Battler ATB Clock according to the battle start type.


It can't be oursourced because Battler ATB Clock is unique to ATB systems.
Resetting ATB Value



It's exhibiting functional cohesion, as all it does is to reset the Battler ATB Clock to be empty.


It can't be oursourced because Battler ATB Clock is unique to ATB systems.
Action Confirmation Flag



It's exhibiting functional cohesion, as all it does is to inform whether an action's completely inputted by inputable actors(it's completely ignored by all the other battlers).


It can be eliminated if a much more elegant design's used, which is beyond the scope of this reply.
Failed Party Escape Attempt



It's exhibiting functional cohesion, as all it does is to reset all party member's Battler ATB Clocks.


It can't be oursourced because Battler ATB Clock is unique to ATB systems.



In short, other than action confirmation flag which is an accidental complexity, MATB exhibits an ideally high cohesion.





Flexibility

Recall that evaluating flexibility involves these 4 parts in practice:


1. How massive the 3 dimensions of control and freedom this plugin can give to its users


2. How massive the 3 dimensions of control and freedom this plugin actually gives to its user


3. How much cost will be needed for this plugin to offer more massive 3 dimensions of control and freedom to its users


4. How capable this plugin can handle extreme scales(data sizes, data change frequency, etc)


Battle Frame Update



Spoiler



It's pretty much nothing to do with flexibility, as nearly all ATB system can only do the almost same thing as MATB when it comes to the Battle Frame Update, which is responsible for executing actions by the Action Execution Subject, and running ATB Frame Update when the ATB Wait Condition's not met.

ATB Wait Condition

It has no freedom nor control given to users at all as it's hardcoded to be always using the Absolutely Loosest ATB Wait Condition, and right now it can't due to taking so many shortcuts which only works on that ATB Wait Condition.


Asking MATB to offer Absolutely Stricter ATB Wait Conditions means many, if not all, of those shortcuts, will have to be replaced by designs working on those ATB Wait Conditions, meaning that a major rewrite will have to take place.
ATB Frame Update



There's not much flexibility in ATB Frame Update to begin with, as its main role's to define what stuffs unique to ATB systems has to be run. Usually the flexibilities will be on those unique stuffs' shoulders.
Battler ATB Clock



It has no control nor freedom given to users at all, because:


1. The Maximum ATB Value's hardcoded to be a fixed number


2. The ATB rate's hardcoded to be a fixed formula


Asking MATB to offer configurations for these can be either easy, simple and small, or complicated, convoluted and costly, depending on the level of control and freedom offered by users.


Basically, level 5(Editable Simple Values) or below should be the former case, whereas level 6(Javascript Codes) or above should be the latter case.
Setting Up Action Execution Subject



There's not much flexibility in Setting Up Action Execution Subject to begin with, as its main role's to setup the 1st battler eligible to be the Action Execution Subject to become one.


The only exception is when you feel/think so gifted in RMMV plugin development that you want to try to challenge this Nontrivial Invariant applied to all Scene_Battle based battle systems - Only 1 battler can be the Action Execution Subject at the exact same frame. This is far beyond the scope of this reply, and is far too advanced to be covered here as well.
Battle Turn Clock



It has limited control and freedom given to users, because max_turn_unit is on level 5(Editable Simple Values), while the Battle Turn Clock is always based on the number of ATB Frame Updates in the same turn.


Depending on the level of control and freedom given to users and the diversities of units of the Battle Turn Clock, offering more control and freedom can be either easy, simple and small, or complicated, convoluted and costly. For instance, just making a counterpart based on the number of executed actions in the same turn is likely the former case.
Setup Inputable Actors



While the main role of setup inputable actors is to setup an actor command window for the 1st newly inputable actors if no command window's already setup, it can also let users setup the actor command window for the next/prior inputable actor, and/or even inputable actors with the specified party member index via hotkeys.


Of course, these new features are totally lacking from MATB, and adding them likely means lots of extra work, plus a high risk of making it much less easy, simple nor small, which is the very spirit of MATB.
Actor ATB Bars



The flexibility's mainly about the visual options of actor's ATB bars.


MATB offers very limited options here, because it only gives level 5(Editable Simple Values) control and freedom for the ATB bar x and y position, maximum width, and colors.


On the other hand, truly flexible ATB system plugin can also offer at least the following:


1. Whether the actor ATB bar's horizontal or vertical(or even more advanced options)


2. Whether the actor ATB bar's placed on the status window, actor sprites, or other positions of the battle screen


3. Whether all actor's Battler ATB Clocks are shown on the same party ATB bar.


4. Whether the actor ATB bars will be shown


Adding any, let alone all, of the above means a major rewrite of MATB, probably making it not a minimum viable product anymore.
Starting ATB Value



While MATB has handled the starting ATB value for all the battle start types - normal, preemptive and surprise, all those starting ATB values are always hardcoded to be a fixed value.


Fortunately, offering more control and freedom is easy, simple and small, unless it's reached level 6(Javascript Codes) or above, because it's just about replacing those hardcoded values with new configurations.
Resetting ATB Value



Right now MATB offers no flexibility here at all.


On the other hand, really flexibile ATB system can at least offer the following:


1. Whether just the actions will be cleared or the ATB value will be reset as well


2. The ATB reset value should be configurable by users


Fortunately, offering them is easy, simple and small, unless it's reached level 6(Javascript Codes) or above, because it's just about replacing those hardcoded values with new configurations.
Action Confirmation Flag



It's pretty much nothing to do with flexibility, as it's an accidental complexity serving to distinguish between inputable actors and battler eligible to be the Action Execution Subject in the same BattleManager._actionBattlers.
Failed Party Escape Attempt



As it's basically about maintaining penalities in failed party escape attempt to prevent abuse, flexibility's typically not a main concern here, although it doesn't mean that no flexibility can be given here.



In short, MATB's a very inflexible plugin, due to an immense lack of 3 dimensions of control and freedom. It's because MATB's meant to be a minimum viable product serving as a tool in ATB system plugin tutorials, meaning that easy, simple and small should be its spirit.






Summary

Invariants



Spoiler



MATB has quite a lot of invariants, mainly because many things are hardcoded. For instance:


1. Any plugin placing below MATB rewriting or ignoring functions(by extending them) extended or rewritten by MATB will break it


2. Once the ATB Wait Condition becomes Absolutely Stricter, many previously non-issues will become real ones that must be handled. So this will lead to many other aspects of MATB being broken


3. The Maximum ATB Value is always hardcoded as a fixed value(100 in this case)


4. The ATB Rate is hardcoded as a fixed formula(agi / Maximum ATB Value per frame in this case)


5. The Battle Turn Clock must be based on a number of frames passed


6. The actor ATB bar must be drawn at the right hand side of the actor mp/tp bar on the status window


7. The actor ATB bar colors must be composed by text colors


8. The actor ATB bar description text must be hardcoded as a fixed value(AP in this case)


9. The starting ATB value for all battlers are always hardcoded to various fixed values depending on the battle start type


10. The ATB value can only be reset to a fixed value which is hardcoded.

Correctness

MATB is basically correct, as far as the below behavioral requirements are concerned:


1. Battle Frame Update -


- Asks the Action Execution Subject to execute actions


- Runs the ATB Frame Update when the ATB Wait Condition isn't met


2. ATB Wait Condition - It must always be met whenever players are inputting commands.


3. ATB Frame Update -


- It must run the Battler ATB Clock first


- It must setup new Action Execution Subject immediately afterwards, unless there's already one


- It must then run the Battle Turn Clock


- It must also setup newly inputable actors, unless the actor command window's already setup


- It must update the actor ATB bar on the status window as well


4. Battler ATB Clock -


- Only movable battlers can have their Battler ATB Clock updated


- Only battlers not having full ATB value can have their Battler ATB Clock updated, with its rate based on agi and Maximum ATB Value


- Battlers becoming having full ATB value should make new action slots in order to input actions


5. Setting Up Action Execution Subject -


- Nothing should be done if there's Action Execution Subject already


- Otherwise the 1st battler being eligible to be the Action Execution Subject should become the Action Execution Subject


6. Battle Turn Clock -


- The Battle Turn Clock must be updated per ATB Frame Update


- The battle turn counter must be updated after a number of seconds composed by ATB Frame Updates has passed in the same turn


7. Setup Inputable Actors -


- Nothing should be done when players are already inputting commands


- Otherwise the actor command window for the 1st newly inputable actor should be setup to let players input commands for that actor


8. Actor ATB Bars - The actor ATB bars should always accurately display the corresponding Battler ATB Clocks


9. Starting ATB Value - The starting ATB value for all battlers are always hardcoded to various fixed values depending on the battle start type


10. Resetting ATB Value -


- The Battler ATB Clock must be reset to be empty


- All actions must be cleared


- The battler must be removed from BattleManager._actionBattlers, which is used to mark inputable actors and battlers eligible to be the Action Execution Subject


11. Action Confirmation Flag - The action confirmation flag should be raised for actor actions if and only if those actions are completely inputted by players


12. Failed Party Escape Attempt - All party members must have their Battler ATB Clocks reset right after a failed party escape attempt
Robustness



MATB is quite robust on many aspects, but really fragile on some others. What really stands out is the ATB Wait Condition, which can basically break the entire MATB completely even when it just becomes slightly buggy for a short time.


Specifically, MATB is quite robust on these Nontrivial Invariants:


1. Any plugin placing below MATB rewriting or ignoring functions(by extending them) extended or rewritten by MATB will break it


2. The Maximum ATB Value is always hardcoded as a fixed value(100 in this case)


3. The ATB Rate is hardcoded as a fixed formula(agi / Maximum ATB Value per frame in this case)


4. The Battle Turn Clock must be based on a number of frames passed


5. The actor ATB bar colors must be composed by text colors


6. The actor ATB bar description text must be hardcoded as a fixed value(AP in this case)


7. The starting ATB value for all battlers are always hardcoded to various fixed values depending on the battle start type


8. The ATB value can only be reset to a fixed value which is hardcoded.


Whereas MATB is really fragile on these:


1. Once the ATB Wait Condition becomes Absolutely Stricter, many previously non-issues will become real ones that must be handled. So this will lead to many other aspects of MATB being broken


2. The actor ATB bar must be drawn at the right hand side of the actor mp/tp bar on the status window
Performance



MATB is rather performant, even though it's not really ideal on machines barely meeting the RMMV system requirement.


There should only be 1 costly operation that are run frequently - redrawing the actor ATB bars.


Basically, there are only 2 reasons to redraw an actor ATB bar:


1. The ATB fill ratio has changed(here it's the same as the Battler ATB Clock has changed, because the Maximum ATB Value's always a constant)


2. Any configuration other than max_turn_unit has changed


Finally, regarding benchmark, it's basically 1ms extra load per actor ATB bar per frame on my machine(WinXP + i7-3820 + 2 * 2GB DDR3 + GTX550Ti).
Coupling



MATB exhibits quite a low coupling, although not to the point of being ideal on all aspects. But fixing those areas falling short is easy, simple and small.


You may want to check introducing component cohesion/coupling digraphs for details.
Cohesion



Other than action confirmation flag which is an accidental complexity, MATB exhibits an ideally high cohesion.


You may want to check introducing component cohesion/coupling digraphs for details.
Flexibility



MATB's a very inflexible plugin, due to an immense lack of 3 dimensions of control and freedom. It's because MATB's meant to be a minimum viable product serving as a tool in ATB system plugin tutorials, meaning that easy, simple and small should be its spirit.
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Ami
What do you think about the Wood Bridge inside of Volcano?
Yet another gaming question, i'm currently playing Last of Us 2 and want to get another game to play next but can't decide between Ghost of Tsushima, Miles Morales & Jedi Fallen Order. After the latest mandalorian i AM on a star wars kick, but i also LOVED the last spiderman game that prequels Miles Morales. Ghost of Tsushima is the bottom of my list but i heard it was AWESOME. Just want a good story & fun gameplay.
I checked out Genshin Impact gameplay out of curiosity. I can't believe it's available as a mobile game too. Smooth animation, great music, and stellar sound effects (the ambient sounds are pretty detailed).

I'm flabbergasted to see games have becoming more and more advanced. Maybe also because I'm oblivious to current trend for a long time, it almost feel like I'm being sent into distant future. xD
xDRAGOONx wrote on WaywardMartian's profile.
Your artwork is amazing!!!
:ewat:
Stream will be live shortly with a session of the Interactive Text Adventure! Feel free to drop by!

Forum statistics

Threads
105,669
Messages
1,015,412
Members
137,344
Latest member
Zindasan0078
Top