DoubleX RMVXA Enhanced YSA Battle System: Classical ATB Tutorial

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,501
Reaction score
565
First Language
Chinese
Primarily Uses
N/A
As the main theme of ECATB is to give you as much control and freedom as possible while still keeping it as user-friendly as possible, while the reality is that you can easily get lost in its gigantic user configurations, this tutorial is made.

Although fully utilizing ECATB needs decent scripting proficiency, it still tries to target as many of you as possible, so this tutorial is divided into various levels targeting users with various scripting proficiencies.

If you think you belong to level x, you're advised to read from Level 1 to Level x, although you might learn something extra if you read Level x + y as well.

Bear in mind that you can always freely ask me for help, so it's ok if you still can't figure out things yourselves even after reading this tutorial. I'll try to give you a working answers for your needs, and I'll even try to explain how things work if you want.

Level 1(No scripting proficiency)

I'm sorry to say that ECATB can only give you really limited control and freedom, but that doesn't mean you won't be able to use ECATB at all. I hope the below guidelines can help you at least a bit.

1. Check every last bit of the core feature

It's because ECATB won't function properly as a decent atb system without it. For example, without <ecatb rate: RX> notetags, all battlers will use the same base atb fill rate, effectively killing the essence of any atb system.

2. Learn from the instructions

While being able to follow the instructions are excellent, being able to learn from them is even better.

While learning something like this would be way too advanced for you:

# Mutliplies the atb fill rate by the battler's agi / all battlers' # average agi # all battlers is def_sum_battlers R1 = %Q(#{R0}base[1][:val] * agi * base[1][:size] / base[1][:sum][:agi])
Code:
    # Checks if the member is charging the party escape    ECC1 = -> member { member.ecatb_esc }
You can still learn some of the basics like:

    # Sets the action cost as x    AC1 = %Q(x)    # Sets the action cost as the value of variable with id x    AC2 = %Q($game_variables[x])
You should be able to learn that x in AC1 is a variable in mathematics and it needs to be substituted with valid values, and $game_variables[x] in AC2 is the value of variable with id x.

The same applies to these:

# Sets the atb bar colors as text color c and r, g, b, a rgba values C1 = %Q(Colour.text_colour(c)) # Sets the atb bar colors as r, g, b, a rgba values C2 = %Q(Color.new(r, g, b, a))
Code:
    # Sets the force action cost as the state of switch with id x    FAC3 = %Q($game_switches[x])
Code:
    # Sets the battler ready se as se with name "name", volume volume and pitch    # pitch    SERA1 = %Q(RPG::SE.new("file", volume, pitch))
You should be able to learn that Colour.text_colour( c ) is the text color c and Color.new(r, g, b, a) is the color with r, g, b, a rgba values; You should also be able to learn that $game_switches[x] in FAC3 is the state of switch with id x; You should be able to learn that RPG::SE.new("file", volume, pitch) is SE using file with name file, volume volume and pitch pitch as well.

3. Don't worry too much about things you can't understand yet

For example, it's completely ok if you don't know what information like the below means:

# RX are used at: # 1. Game_BattlerBase # - @ecatb_notes[:rate].each { |rate| eval(rate) } in ecatb_gain_rate
Code:
      # Its corresponding method's created under BattleManager
As you can't use them yet anyway. They're for those having at least some scripting proficiency, and you can still use the basics without even bothering them.

4. Don't worry too much about using script calls

Although being able to use script calls is a prerequisite of using ECATB, doing so actually needs little scripting proficiency and you don't have that yet. Although quite some features can only be done via script calls, you can still use many of its basics. If you want to use them anyway, you can either learning the scripting basics, or ask me for help.

5. Don't be afraid of testing the configuration and notetag values

While you should have at least a vague idea after reading the instructions, you'll have to actually test them to check their effects yourselves in order to really understand what they do. As long as you stick to only using what you know, their effects shouldn't confuse you too much. For example:

# Sets the battler sprite atb bar text x and y positions # Its corresponding method's created under Game_Battler # It must return an array of real numbers and should return an array of # integers # Example: To set the battler sprite atb bar x and y positions as the # values of variables with id x and y respectively, set this as # %Q(%Q([$game_variables[x], $game_variables[y]])) :ecatb_bar_text_xy => %Q(%Q([-1, -7])),
Even I, the ECATB author, will have to test several times in order to get a suitable value for me, so if you're unsure what the effects of those values are, just test them and you'll probably know better.


Level 2(Little scripting proficiency)

As you know the Ruby basics like basic syntax and concepts and have at least a bit of basic Ruby coding experience, you should be able to use most, if not all, script calls listed in the Script Call Info.

Also, as you shouldn't be new to reading and writing basic Ruby codes, you should be able to use them as configuration or notetag values.

ECATB can still only give you limited control and freedom, however, as you can't use advanced RGSS3 codes yet. Nevertheless, I hope that the below guidelines can help you at least a bit.

1. Thinking of the script call contexts

While some script calls like this has only 1 context:

# 1. $game_system.set_battle_system:)system) |# - Changes the battle system to those supported by |# Yanfly Engine Ace - Ace Battle Engine |# - It can only be used outside battles
Some like this has more:

#    7. ecatb_reset(boolean)                                                   |#       - Cancels the battler's charging actions                               |#       - The battler's atb and action points will be reset as well if boolean |#         is true                                                              |#       - It applies to all battlers belonging to Game_Battler                 |
It can be used in damage formulae like this:

act = b.ecatb_act_times; b.ecatb_reset(true); a.ecatb_act_times += act; damage_formula # Take all the action points from the target to the user
It can also be used in script calls like this:

$game_troop.members.each { |member| member.ecatb_reset } # Cancels all enemies' charging actions
Note the difference between the contexts of the above 2 examples.

2. Using string interpolations

While you should be able to use this technique already, I've to emphasize that it's extremely important for using ECATB effectively and efficiently, as almost all configuration and notetag values are stored in strings. For example, you should know what these mean:

    # Don't edit this unless you understand what it does and how it works    SV0 = %Q(@ecatb_val[:color] = )    # Sets the starting atb value as x%    SV1 = %Q(#{SV0}x)
Code:
    # Sets the action cost as x    AC1 = %Q(x)    # Sets the action cost as the value of variable with id x    AC2 = %Q($game_variables[x])    # Sets the action cost as AC1 + AC2    AC3 = %Q(#{AC1} + #{AC2})
While you're not expected to know what @ecatb_val[:color] is yet, you should at least know that %Q(#{SV0}x) is equal to %Q(@ecatb_val[:color] = x), and %Q(#{AC1} + #{AC2}) is equal to %Q(x + $game_variables[x]).

You may have noticed that the SVX and ACX are actually building blocks of notetag values, as they can be reused and become some parts of the whole by using string interpolations.

As they're constants which can't be changed once set, the more fundamental ones must be placed above the less fundamental ones. For example, SV0 must be placed above SV1 and AC1 and AC2 must be placed above AC3.

3. Using basic Ruby codes in configuration and notetag values

For example, you should know what simple Ruby codes like these mean:

# Sets the action cost as x AC1 = %Q($game_switches[x] ? $game_variables[y] : $game_variables[z])
Code:
      :ecatb_pool_val => %Q(%Q(        return 0 if vals.empty?        avg = 0        vals.each { |val| avg += val }        avg / vals.size      )),
AC1 returns the value of variable with id y and z if switch with id x is on and off respectively, while ecatb_pool_val returns the average of all values in vals.

Also, note that notetag values are RGSS3 codes to be evaluated upon usage by using eval, while almost all configuration values are strings of the method contents, and those methods are created on the fly by using eval. So writing notetag values are writing expressions(although they can be more than 1 lines), while writing most configuration values are writing method contents.

4. Validating the configuration and notetag values

While ECATB can check the validity of most configuration values for you, notetag values won't be checked as I don't know how to check them effectively and efficiently. So until I find such means if there's any, you'll have to check the validity of the notetag values yourselves. If you stick to only using the basic Ruby codes in notetag values, you should be able to use the information like this to check their validity:

# It must return an array # Its 1st element must return a positive real number and should return a # natural number      # Its 2nd element must return either :tick or :act
Also, it'd be excellent if you check the validity of the configuration values yourselves as well, as the ECATB validity check isn't 100% fool proof.

5. Don't worry too much about learning how the default or custom RMVXA scripts work

While you know that information like these are to tell you where, when and how the configuration and notetag values are used(i.e., informing you the contexts behind those values):

# RX are used at: # 1. Game_BattlerBase # - @ecatb_notes[:rate].each { |rate| eval(rate) } in ecatb_gain_rate
Code:
      # Its corresponding method's created under BattleManager
You also know that you need to understand the mentioned parts to use the above information, and doing so is still likely beyond your current scripting proficiency. However, you can still use Ruby codes that work under any context by only using globally available elements like global variables.


Level 3(Some scripting proficiency)

As you should have a basic understanding of how the default RMVXA scripts work(at least the battle related parts) and should have some actual scripting experience(you've probably released at least few simple original scripts), you should be able to use all but the most advanced ECATB features.

Also, as you should be able to read others' custom scripts, you should be able to read the ECATB implementations locally(i.e., specific methods) and have at least a vague idea about what they do locally. You're not needed to understand them or read the ECATB implementations globally(i.e., as a whole).

Although you still don't have the full control and freedom ECATB gives yet, I hope that the below guidelines can help you at least a bit.

1. Read the contexts of the configuration and notetag values

While you're not needed to understand those contexts, you should at least have a vague idea about them. For example:

# RX are used at: # 1. Game_BattlerBase # - @ecatb_notes[:rate].each { |rate| eval(rate) } in ecatb_gain_rate
Code:
      # Its corresponding method's created under BattleManager
The former tells you that your RX must work in ecatb_gain_rate, and the latter tells you that your method contents must work under BattleManager. While you're not expected to fully utilize new methods and/or variables in ECATB yet, you should at least be able to use the ones in the default RMVXA scripts. You should also be able to tell if your configuration and notetag values are valid in the mentioned contexts as long as you know what those values are.

2. Use the default RMVXA script methods and variables

For example, you should know what these mean:

# Checks if the party command window is active WC5 = %Q(@party_command_window.active) # Checks if the actor target window is active WC6 = %Q(@actor_window.active) # Checks if the enemy target window is active WC7 = %Q(@enemy_window.active) # Checks if the skill window is active WC8 = %Q(@skill_window.active) # Checks if the item window is active WC9 = %Q(@item_window.active) # Checks if the actor command window is active WC10 = %Q(@actor_command_window.active)
Code:
      :ecatb_battler_count => %Q(%Q($game_party.alive_members.size +                                     $game_troop.alive_members.size)),
Code:
      # It must return an array of battlers
Code:
    # Sets the battler methods to use the unison method rules    # It must be a hash of strings of the battler method arguments    # Its keys must be the battler method name symbols    # Methods with name method_name will be aliased to method_name_ecatb    # Example:     UNISON_DEFS = {      # General Form:      # [:method_class, :super_class] => {      #   :method_name => "method_arguments"      # }      [:Game_BattlerBase] => {        :param => "param_id"                # Adds new methods here              }            # Adds new classes here          }
You should also know where, when and how they're used, although you're not expected to understand the contexts behind them yet. Also, as some default RMVXA script methods are aliased or rewritten, you should note their behavior change before using them, to ensure you know what you're doing. Both aliased and rewritten methods are clearly stated by their documentations that they're aliased or rewritten respectively.

3. Understand the relations between the configuration and notetag values

For example, you should know how the below notetag and configuration values are related:

#--------------------------------------------------------------------------| # Unison Method Rule Notetag Values | # - Setups UDRX used by <ecatb unison def rule: UDRX> notetags | #--------------------------------------------------------------------------| # UDRX are used at: # 1. Game_BattlerBase # - rule = eval(item.ecatb_unison_def_rule) in set_ecatb_unison_defs # UDRX are strings of RGSS3 codes # UDRX must return a symbol # You must implement the rule in ecatb_unison_rules # UDRX names can only use alphanumeric characters # The below UDRX are examples added to help you set your UDRX # You can freely use, rewrite and/or delete these examples # Adds new UDRX here # Sets the default unison method rule # It'll be used for def only if it's no UDRX to use # Example: To set the default unison mthod rule as returning the minimum, # set this as %Q:)min) ECATB_UNISON_DEF_RULE = %Q:)avg)
Code:
    # Sets the battler methods to use the unison method rules    # It must be a hash of strings of the battler method arguments    # Its keys must be the battler method name symbols    # Methods with name method_name will be aliased to method_name_ecatb    # Example:     UNISON_DEFS = {      # General Form:      # [:method_class, :super_class] => {      #   :method_name => "method_arguments"      # }      [:Game_BattlerBase] => {        :param => "param_id"                # Adds new methods here              }            # Adds new classes here          }
Code:
      # Implements the unison method rules      # The unison method of all unison battlers can be referneced by vals      # The unison method rule can be referenced by rule      # Its corresponding method's created under Game_Battler      # It must return a real number      # Example:       :ecatb_unison_rules => %Q(%Q(        if rule == :min          vals.min        elsif rule == :avg          avg = 0          vals.each { |val| avg += val }          avg / vals.size        elsif rule == :max          vals.max        else          0        end      ))
UDRX set the method rule used by the skills/items using those UDRX, UNISON_DEFS sets all methods under their classes that can use the unison method rules, and ecatb_unison_rules sets all unison method rules that can be used by UDRX and UNISON_DEFS. There are quite some other configuration and notetag values that are related, and some ECATB features can only be used via using those relations. Unfortunately, some relations are rather elusive and using some of the relations needs decent scripting proficiency, in which you don't have yet. But you can still ask me for using those relations you found.

4. Reading ECATB new methods and variables

While you're not expected to understand them yet, you should at least have a vague idea about them. The documentations should help you at least a bit:

# 2. Method documentation |# - The 1st part informs whether the method's rewritten, aliased or new |# - The 2nd part describes what the method does for new methods only |# - The 3rd part describes what the arguments of the method are |# - The 4th part describes how this method works for new methods only, |# and describes the parts added or rewritten for rewritten or aliased |# methods only |# Example: |# #----------------------------------------------------------------------------|# # Rewrite/Alias/New method: def_name |# # - What this method does |# #----------------------------------------------------------------------------|# # *args: What these arguments are |# def def_name(*args) |# # How this method works |# def_name_code |# # |# end # def_name |
For example:

  #----------------------------------------------------------------------------|  #  New method: exec_ecatb_act_cost                                           |  #  - Sets the action cost and atb reset value                                |  #----------------------------------------------------------------------------|  # forced: The action force flag  def exec_ecatb_act_cost(forced)    return unless current_action && current_action.item    force_act_cost = eval(current_action.item.ecatb_force_act_cost)    if !forced || force_act_cost      # Sets the action cost if charge prior action cost is true      if eval(current_action.item.ecatb_charge_prior_act_cost)        if @ecatb_unison_actors.size > 1          @ecatb_unison_actors.each(          &DoubleX_RMVXA::ECATB::BLOCK[:exec_ecatb_act_cost])        else          pay_ecatb_act_cost        end      end      #      # Sets the atb reset value      set_ecatb_reset_val      #    end    force_act_cost  end # exec_ecatb_act_cost
Code:
  attr_reader :ecatb_act_times # Stores the number of action points
You should at least know that exec_ecatb_act_cost sets the battler's action cost and atb reset value and @ecatb_act_times stores the number of action points of its battler, although you're not expected to understand how exec_ecatb_act_cost works yet. Now you should have a better idea of how to set FACX and CPACX(like what new methods and variables can be used), although fully comprehending their contexts are still likely beyond your scripting proficiency. Just bear in mind that you only need to focus on figuring out what a new method/variable does and how you can use it.

5. Using new methods set by you

You should already know that the configuration values are actually method contents, and the method name is actually the configuration name. You should also know that those methods are created on the fly. So you can actually use them in notetag values or even other configuration values. For example:

:pre_ecatb_update => %Q(%Q( BattleManager.ecatb_def_sum_battlers.each { |battler| [:color, :charge, :cooldown].each { |type| battler.ecatb_note_change[type] = true } } )),
The above allows all battlers' atb, charge and cooldown bar colors to be different per frame. Note that pre_ecatb_update uses the configuration value ecatb_def_sum_battlers and the script call ecatb_note_change[note] = true. The above must be used if you want the atb, charge and cooldown bar colors to be different per frame.

By thinking and experimenting how to mix new methods, script calls and notetag value expressions together, you might end up finding new relations between some of them.


Level 4(Decent scripting proficiency)

As you've quite a lot of scripting experience and have probably written at least dozens of really useful scripts, you should have little trouble understanding others' custom scripts locally(i.e. method level). Also, as you've a solid understanding of how the default RMVXA scripts work(at least the battle related parts), you should've little trouble understanding how the default RMVXA battle system works as well. So you should be able to almost fully use the control and freedom ECATB gives. You'll still probably gain from reading the below guidelines, however, as they should help you better understanding ECATB implementations locally more effectively and efficiently. Nevertheless, you're not expected to understand ECATB globally yet.

1. Using anonymous functions

Specifically, you should know how to use this:

anonymous_function = -> iterator { block }iterator_method(&anonymous_function)
Which is the same as this:

iterator_method { |iterator| block }
It's because it's an extremely important optimization technique used in ECATB. For example:

# Checks if the member is charging the party escape ECC1 = -> member { member.ecatb_esc }
Code:
    # Checks if the member has fully charged a skill/item    EEC1 = -> member { member.ecatb_val[:charge] >= 100.0 }
You should know what they do, how to use them and what their limitations are.

You'll find that the ECATB implementations uses anonymous functions heavily. For example, the constant BLOCK in the ECATB implementation part of DoubleX_RMVXA::ECATB is an array of anonymous functions.

2. Understanding new methods and variables

While you're not expected to understand ECATB as a whole yet, you should be able to understand any new method and variable in ECATB locally. For example, you should understand what the below does, how they work and how to use them:

# Mutliplies the atb fill rate by the battler's agi / all battlers' # average agi # all battlers is def_sum_battlers R1 = %Q(#{R0}base[1][:val] * agi * base[1][:size] / base[1][:sum][:agi])
Code:
  #----------------------------------------------------------------------------|  #  New method: ecatb_gain_rate                                               |  #  - Returns the atb gain value per frame                                    |  #----------------------------------------------------------------------------|  def ecatb_gain_rate    base = update_ecatb_base    # Reevaluates the atb rate only if any of its components' changed    if base[0] || ecatb_battler_change?(:rate)      set_ecatb_rate      @ecatb_notes[:rate].each { |rate| eval(rate) }      @ecatb_rate[:color] = base[1][:val]    end    #    @ecatb_rate[:color]  end # ecatb_gain_rate  #----------------------------------------------------------------------------|  #  New method: update_ecatb_base                                             |  #  - Checks if the atb rate needs to be reevaluated                          |  #----------------------------------------------------------------------------|  def update_ecatb_base    # Checks if the number of battlers, methods sums or the fill time's changed    change = false    types = {      size: BattleManager.ecatb_def_sum_battlers.size,      sum: BattleManager.ecatb_battlers_def_sums,      val: 100.0 / (BattleManager.ecatb_base_fill_t * Graphics.frame_rate)    }    types.each { |key, val|      next if @last_ecatb_base[key] == val      @last_ecatb_base[key] = val      change = true    }    [change, types]    #  end # update_ecatb_base
Code:
  #----------------------------------------------------------------------------|  #  New method: set_ecatb_rate                                                |  #  - Sets the current atb gain rate when its notetags change                 |  #----------------------------------------------------------------------------|  def set_ecatb_rate    # Updates the atb gain rate notetags using the current order    @ecatb_notes[:rate] = []    ecatb_rate_ord.each { |note|      next unless send(@ecatb_item[note][0])      set_ecatb_notes(send(@ecatb_item[note][1]), :rate, 1, true)    }    #  end # set_ecatb_rate
You should know that @ecatb_notes[:rate] stores all <ecatb rate: RX> notetags used by the battler, @ecatb_rate[:color] is the cached result of all those evaluations, etc. Upon further understanding, you should understand how @ecatb_notes[:rate] stores those notetags by understanding set_ecatb_notes, and that understand is crucial for fully utilizing those notetags. You only need to focus on comprehending what a new method/variable does, how they work locally and how you can use them.

3. Understanding how to use other scripts along with ECATB

When using others' custom scripts' features with ECATB's ones, you've to pay extra attention on possible compatibility issues raised by your configuration and notetag values.

For example, with DoubleX RMVXA State Triggers, you can replicate the CATB Clear and parts of the CATB Countdown feature. Specifically:

# Sets the state trigger action as clearing its owner's atb value and action points STA1 = "ecatb_reset(true)" # Sets the state trigger action as clearing its owner's charging actions only STA2 = "ecatb_reset" # Sets the state trigger action as resetting its owner's charge value to x STA3 = "@ecatb_val[:charge] = x" # Sets the state trigger action as resetting its owner's cooldown value to x STA4 = "@ecatb_val[:cooldown] = x" # Sets the state trigger action as resetting its owner's action points as x STA5 = "ecatb_act_times = x"
You should understand what these STAX do, how they work and how to use them.

Another example(with DoubleX RMVXA Intercept Magic):

    AC1 = %Q(states.any? { |state| state.intercept_tier > x } ? y : z)
AC1 returns y and z if the battler has and hasn't intercept states with tier above x respectively.

While you're not expected to know how other custom scripts interact with ECATB as a whole, you should be able to figure out how parts of other custom scripts interacts with parts of ECATB, and how you can use those interactions.

4. Using hidden script calls

Although the Script Calls Info already has a long script call list, there are actually some more ones that aren't mentioned. It's because they're not supposed to be used as script calls and doing so can easily cause chaos and/or crashes. But as you already have decent scripting proficiency, if you understand what they do, how they work locally and how to use them, you can still use them as long as utmost caution is exercised. For example:

attr_accessor :ecatb_refresh # Stores the refreshing calling flag
It's used to notify the windows(status window, status aid window, help window, actor window, cancel window, etc) that the battler's displayed information changed and needs to be redrawn. So calling battler.ecatb_refresh = true will redraw all those windows. But as you know, redrawing windows are expensive, redrawing them too frequently can cause significant average fps drop. So it should only be used when they need to be redrawn and those needs aren't covered by ECATB already. This can be the case when you use others' custom scripts that have those needs.

When using unmentioned script calls, you should always clearly know what you're doing and have clear reasons behind them, otherwise can will most likely end up wreaking havoc. But if you're lucky or smart, you might end up finding well-hidden ECATB capabilities which is even hidden to me, the ECATB author.

5. Don't be too insane on setting configuration and/or notetag values

While you've almost full control and freedom when using ECATB, ECATB itself can't give you unlimited control and freedom, so you should bear in mind that every configuration and notetag value has their limitations, and you should be able to find at least some of them. For example:

- While variable action costs are supported, random action costs aren't.

- Variable maximum atb/charge/cooldown values aren't supported, they're always 100.0.

- Changing battle systems during battles aren't supported.

There are more, but listing them all would be too exhaustive.

Also, always think of the performance impact when you set the configuration and notetag values. For example, the below value will extremely likely cause significant average fps drop:

# Sets something to happen right before updating the atb clock # Its corresponding method's created under Scene_Battle # Example: To support changing atb, charge and cooldown bar colors of all # battlers per frame, set this as # %Q(%Q(all_battle_members.each { |member| # member.ecatb_note_change[:color] = true # })) :pre_ecatb_update => %Q(%Q(all_battle_members.each { |battler| battler.ecatb_refresh = true })),
After all, when you're trying to fully utilize ECATB, you're actually coding quite some parts of it, so the proper coding practices should be exercised to prevent making the configuration and notetag values out of your control. One especially important programming aspect you should always bear in mind is code performance, as ECATB needs extremely performant codes to work well.


Level 5(Advanced Scripting Proficiency)

This part is dedicated for those going to edit ECATB, write compatibility fixes and/or addons to it, and/or tweaking it to suit specific projects' needs. Other users don't need to read this part, but they might still learn something useful for using ECATB if they do read this part.

As you've been a scripting veteran for quite some time and has written at least 1 advanced complex script like a custom battle system, you can probably write an excellent atb system yourselves. Such capability is extremely useful for fully comprehending how ECATB works. With the below guidelines, you should be able to deal with the entire ECATB system as a whole.

1. Understand the design patterns

ECATB tries to conform to the MVC(Model View Controller) pattern. Specifically:

- DataManager, RPG::Actor, RPG::Class, RPG::Enemy, RPG::EquipItem, RPG::State, RPG::UsableItem and Game_System are almost entirely Data

- BattleManager are mainly Model although it's some Control functions

- Game_Action, Game_BattlerBase(closer to Data), Game_Battler(closer to Control), Game_Actor, Game_Enemy, Game_Party and Game_Troop are almost entirely Model

- Sprite_Battler, ECATB_Bar, ECATB_Clock_Bar, Window_ItemList, Window_BattleLog, Window_ActorCommand, Window_BattleStatus, Window_BattleActor, Window_BattleEnemy, ECATB_Clock_Window, ECATB_Force_Window and ECATB_Pool_Window are almost entirely View

- Scene_Battle is mainly Control, although it's some Model functions

ECATB also tries to be as compact as possible. For example:

# Stores create_ecatb_defs, check_ecatb_def and reset_ecatb_def under all # classes and modules having methods created using the configurations CHECK_RESET_DEF = {} [:BATTLEMANAGER, :GAME_BATTLER, :GAME_ACTOR, :GAME_PARTY_TROOP, :WINDOW_BATTLESTATUS, :ECATB_CLOCK_WINDOW, :ECATB_CLOCK_BAR, :ECATB_FORCE_WINDOW, :ECATB_POOL_WINDOW, :SCENE_BATTLE].each { |key| CHECK_RESET_DEF[key] = %Q( def create_ecatb_defs #{key == :GAME_ACTOR || key == :GAME_ENEMY ? "super" : ""} DoubleX_RMVXA::ECATB::#{key.id2name}.each_key { |config| create_ecatb_def(config) } end def check_ecatb_def(config, check) percent = act = "" cur_clock = max_clock = turn_count = index = rule = 0 vals = [] val = eval(config + check[0]) reset_ecatb_def(config, check, "is invalid.") unless eval(check[1]) #{RESCUE_CONFIG_VAL ? %Q(rescue reset_ecatb_def(config, check, "could crash the game.")) : ""} end def reset_ecatb_def(config, check, type) #{SUPPRESS_RESET_CONFIG_VAL_TEXT ? "" : %Q(msgbox("The value of " + config + " is\n" + eval("$game_system." + config) + "\nwhich " + type))} eval("$game_system." + config + " = " + check[2]) create_ecatb_def(config.to_sym, false) #{SUPPRESS_RESET_CONFIG_VAL_TEXT ? "" : %Q(msgbox( "Its value is reset to its default:\n" + eval(check[2])))} end ) }
It's an abstract method creator and validator, which is somehow similar to an abstract factory.

Its concrete counterparts are implemented in classes registered in CHECK_RESET_DEF. For example:

#----------------------------------------------------------------------------| # New method: create_ecatb_def | # - Creates a and checks that new method using configuration config | #----------------------------------------------------------------------------| # config: The configuration used to create a new method # It must be a string containing the name of a configuration # validate: The indicator of whether the configuration will be checked # It should be either false or true def create_ecatb_def(config, validate = true) # Creates a new method with its contents being its configuration value method = config.id2name eval(%Q( def #{method}#{DoubleX_RMVXA::ECATB::BATTLEMANAGER[config][0]} #{eval("$game_system.#{method}")} end )) # # Checks if that new method can crash the game and returns a valid value return unless validate check_ecatb_def(method, DoubleX_RMVXA::ECATB::BATTLEMANAGER[config]) # end # create_ecatb_def # Checks the new method and resets it to its default if it's invalid module_eval(DoubleX_RMVXA::ECATB::CHECK_RESET_DEF[:BATTLEMANAGER])
It's the concrete counterparts used by BattleManager.

Another abstract method aliasing generator is this:

# Aliases methods that use unison method rules DoubleX_RMVXA::ECATB::UNISON_DEFS.each { |klass, defs| defs.each { |name, args| name = name.id2name eval(%Q(class #{klass[0].id2name}#{klass[1] ? " < #{klass[1].id2name}" : ""} alias #{name}_ecatb #{name} def #{name}(#{args}) @ecatb_unison_defs[:#{name}] || #{name}_ecatb(#{args}) endend )) } }
For the same reason, an abstract notetag value reader's used:

#----------------------------------------------------------------------------| # New method: set_ecatb_notes | # - Stores the value of all specified notetags preserving their orders | #----------------------------------------------------------------------------| # items: The data containing the notetags # note: The notetag type # size: The maximum size of notetag values to be stored # add: The truth value of if more than 1 notetag values can be used def set_ecatb_notes(items, note, size, add = false) # Reads all item's speficied notetag values until the maximum size's reached items.each { |item| size = item.ecatb_notes[note].size if add (0..(size - 1)).each { |index| next unless item.ecatb_notes[note][index] if add @ecatb_notes[note].push(item.ecatb_notes[note][index]) next end return if @ecatb_notes[note].size == size @ecatb_notes[note][index] ||= item.ecatb_notes[note][index] } } # end # set_ecatb_notes
You should have a clear understanding of how the above templates works when concrete information are given by its callers. You should also know how to edit and/or call them without breaking anything unnoticed by you.

2. Understand the core structures

All features must be registered here:

# Stores all types of configurations CONFIG = [CORE, BAR, CANCEL, CHARGE, CLOCK, COOLDOWN, FORCE, HOTKEY, POOL, SE, UNISON]
Their configurations are registered with their arguments, validation checks and default values in ECATB implementation part of DoubleX_RMVXA::ECATB. Most configurations need to be registered in a class to be used under that class, and all those classes need to be registered in the above CHECK_RESET_DEF. Unregistered ones won't have validation checks nor associated methods. For example:

# Stores all configurations having corresponding methods in ECATB_Clock_Bar # General form: :config => [ # %Q(arguments), # %Q(validation check), # %Q(default value) # ] ECATB_CLOCK_BAR = { :ecatb_clock_bar_colors => [ %Q(), %Q(val.is_a?(Array) && (val[0].is_a?(Color) || val[0].is_a?(Colour)) && (val[1].is_a?(Color) || val[1].is_a?(Colour))), %Q(%Q([Colour.text_colour(7), Colour.text_colour(8)])) ], :ecatb_clock_bar_wh => [ %Q(), %Q(val.is_a?(Array) && val[0].is_a?(Numeric) && val[0] >= 0 && val[1].is_a?(Numeric) && val[1] >= 0), %Q(%Q([112, 16])) ], :ecatb_clock_bar_xy => [ %Q(), %Q(val.is_a?(Array) && val[0].is_a?(Numeric) && val[1].is_a?(Numeric)), %Q(%Q([8, 8])) ] }
All configurations used under ECATB_Clock_Bar are registered in ECATB_CLOCK_BAR, which is registered in CHECK_RESET_DEF.

All configuration values are stored in Game_System and can be accessed by:

#----------------------------------------------------------------------------| # New public instance variables | #----------------------------------------------------------------------------| # Stores all configuration values DoubleX_RMVXA::ECATB::CONFIG.each { |configs| configs.each_key { |config| eval("attr_accessor :#{config.id2name}") } } #
When a save file's created, all the configuration values are saved in those accessors under Game_System, letting users change the configuration values on the fly.

3. Understand the basic principles

As an atb system, ECATB needs to be as performant as possible while still not sacrificing any other programming aspect too much, especially at hotspots(costly codes being run at least once per frame), otherwise the average fps will likely suffer. Some techniques such as lazy evaluations and memoizations are heavily used. For example:

#----------------------------------------------------------------------------| # New method: ecatb_gain_rate | # - Returns the atb gain value per frame | #----------------------------------------------------------------------------| def ecatb_gain_rate base = update_ecatb_base # Reevaluates the atb rate only if any of its components' changed if base[0] || ecatb_battler_change?:)rate) set_ecatb_rate @ecatb_notes[:rate].each { |rate| eval(rate) } @ecatb_rate[:color] = base[1][:val] end # @ecatb_rate[:color] end # ecatb_gain_rate
It's to prevent evaluating <ecatb rate: RX> notetags many times per frame, as eval is extremely expensive and a battler can have lots of such notetags. Therefore @ecatb_rate[:color] is used to cache the result and those notetags are only reevaluated when any result determining factor changes.

For this reason, methods like the below are used to check if reevaluations are needed:

#----------------------------------------------------------------------------| # New method: ecatb_battler_change? | # - Checks if any notetag changed | #----------------------------------------------------------------------------| # note: The notetag type to be checked def ecatb_battler_change?(note) change = false # Checks if any notetag's value's changed if @ecatb_note_change[note] @ecatb_note_change[note] = false change = true end # # Checks if any new notetag's added or old notetag's gone if @ecatb_battler_change[note] @ecatb_battler_change[note] = false change = true end # change end # ecatb_battler_change?
Again, performance isn't everything in ECATB implementations, and it indeed tries to have a working balance over all programming aspects, by properly prioritizing them at every part of it, as different parts have different priorities. Understanding those priorities is crucial for you to understand the design decisions behind ECATB.

4. Understand the relationship between features

Many features are related to each other, and editing any of those will likely lead to editing some others, so you should always keep an eye on those relationships before touching any of their methods. For example:

  #----------------------------------------------------------------------------|  #  New method: ecatb_unison_actor?                                           |  #  - Checks if the actor can use the unison skill/item                       |  #----------------------------------------------------------------------------|  # type: The unison skill/item data type  # item: The unison skill/item  # actors: The unison actor list  def ecatb_unison_actor?(type, item, actors)    # Checks if the general skill/item usable conditions are met    if type == :skill      return false unless skills.include?(item) || skill_conditions_met?(item)    else      return false unless item_conditions_met?(item)    end    #    # Checks if the actor's inputable and can pay the action cost    return false if auto_battle? || confusion?    battler = self    cost = eval(item.ecatb_act_cost)    cost *= actors.size if $game_party.ecatb_pool    return true if @ecatb_act_times >= cost    return false unless @ecatb_val[:charge] > 0    !eval(current_action.item.ecatb_charge_prior_act_cost)    #  end # ecatb_unison_actor?
ecatb_unison_actor? shows that the pool, charge and unison features are closely related. In fact, changing the implementations of those will likely change those of the others.

The focus on these issues should be the high level concepts and mechanics of the features addressed and how they should interact with each other. Without realizing these high level relations and thinking those features as a whole, you're also impossible to figure out a working solution.

5. Don't break the changing battle system outside battles feature

You should always bear in mind that ECATB lets you change among battle systems supported by Yanfly Engine Ace - Ace Battle Engine outside battles, so any ECATB specific feature should be disabled when the battle system isn't ECATB. BattleManager.btype?:)ecatb) is your best friend here. For example:

#----------------------------------------------------------------------------| # Alias method: create_all_windows | #----------------------------------------------------------------------------| alias create_all_windows_ecatb create_all_windows def create_all_windows create_all_windows_ecatb # Added to create ecatb windows for ecatb battle system create_ecatb_windows if BattleManager.btype?:)ecatb) # end # create_all_windows
ECATB specific windows should only be created when the battle system is ECATB.

You should also pay extra attention on possible compatibility issues with other battle system scripts supported by Yanfly Engine Ace - Ace Battle Engine. You should have a clear vision on what should happen when the battle system changes from one to another, in order to ensure those change will always function properly, meaning that you've to comprehend each of those battle systems as a whole as well.
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,501
Reaction score
565
First Language
Chinese
Primarily Uses
N/A
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,501
Reaction score
565
First Language
Chinese
Primarily Uses
N/A
Further reading(Mainly targeting those at Level 5 or above):

http://forums.rpgmakerweb.com/index.php?/topic/39139-deciding-the-degree-of-control-and-freedom-given-to-script-users/

Again, I want to emphasize that ECATB tries to give users level 5 control and freedom. I'd be glad if you try to conform to this when doing ECATB script support :D

For users belonging to level 4(decent scripting proficiency)or above(other users might still be able to follow though :) ):

In ECATB v0.01a or older, almost all notetag values are stored using this general form:

eval("DoubleX_RMVXA::ECATB::" + $x)While in ECATB v0.01b, almost all of them are stored using these general form:

eval("-> battler { battler.instance_exec { #{eval("DoubleX_RMVXA::ECATB::" + $x)} } }")
Code:
eval("-> battler, val = nil { battler.instance_exec { #{eval("DoubleX_RMVXA::ECATB::" + $x)} } }")
Code:
eval("-> battler, val { battler.instance_exec { #{eval("DoubleX_RMVXA::ECATB::" + $x)} } }")
After extensive testings, I decided to use lambda with instance exec instead of direct eval, as I think I've sufficient data to prove that doing so will likely at least slightly improve ECATB's efficiency.

In some really extreme test cases, the average fps in my machine(i7-3820 + 2 * 2GB DDR3 1333 + GTX 550Ti) boosted by roughly 7(from roughly 88 to roughly 95).

While the above change doesn't have any major impact on setting the notetag values, you need to know this change in order to effectively and efficiently understand how the ECATB v0.01b notetag values works. You also need to know what this does, how this works and how to use this:

eval("-> user { user.instance_exec { #{note_val} } }")The below shows the actual change on how the notetag values are stored:

ECATB v0.01a or older -

NOTE_MID = %Q( notes.each { |note| eval("@ecatb_notes[:" + note + "] = []") } @note.split(/[\r\n]+/).each { |line| case line when ecatb::REGEX[0] @ecatb_notes[$1.to_sym][$2.to_i] ||= eval("ecatb::" + $3) when ecatb::REGEX[1] @ecatb_notes[:rate].push(eval("ecatb::" + $1)) when ecatb::REGEX[2] @ecatb_notes[:se] = [eval("ecatb::" + $1)] when ecatb::REGEX[3] @ecatb_notes[("start_" + $1).to_sym].push(eval("ecatb::" + $2)) when ecatb::REGEX[6] @ecatb_notes[:order_icon] = [eval("ecatb::" + $1)] when ecatb::REGEX[7] @ecatb_notes[:order_opacity] = [eval("ecatb::" + $1)] when ecatb::REGEX[8] @ecatb_notes[:order_scale] = [eval("ecatb::" + $1)] when ecatb::REGEX[9] @ecatb_notes[:order_z] = [eval("ecatb::" + $1)] )
Code:
    COUNTDOWN_NOTE_MID = %Q(      when ecatb::REGEX[5]        @ecatb_countdown_interval ||= eval("ecatb::" + $1)    )
Code:
  #----------------------------------------------------------------------------|  #  New method: load_ecatb_notes                                              |  #  - Loads all skill and item notetags                                       |  #----------------------------------------------------------------------------|  def load_ecatb_notes    @ecatb_unison_actor_def = {}    @ecatb_unison_actor_def_rule = {}    # Checks each line against all notetags and stores their values    @note.split(/[\r\n]+/).each { |line|      DoubleX_RMVXA::ECATB::USABLEITEM_NOTES.each_with_index { |note, i|        next unless line =~ DoubleX_RMVXA::ECATB::REGEX[i + 10]        if i > 13          note += "[:#{$1}]"          val = $2        else          val = $1        end        eval("@#{note} ||= DoubleX_RMVXA::ECATB::#{val}")      }    }    DoubleX_RMVXA::ECATB::USABLEITEM_NOTES.each { |note|      eval("@#{note} ||= DoubleX_RMVXA::ECATB::#{note.upcase}")    }    #  end # load_ecatb_notes
ECATB v0.01b -

Code:
    NOTE_MID = %Q(    notes.each { |note| eval("@ecatb_notes[:" + note + "] = []") }    @note.split(/[\r\n]+/).each { |line|      case line      when ecatb::REGEX[0]        @ecatb_notes[$1.to_sym][$2.to_i] ||= eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $3) + " } }")      when ecatb::REGEX[1]        @ecatb_notes[:rate].push(eval("-> battler, val { battler.instance_exec{         " + eval("ecatb::" + $1) + " } }"))      when ecatb::REGEX[2]        @ecatb_notes[:se] = [eval("-> battler { battler.instance_exec{ " +         eval("ecatb::" + $1) + " } }")]      when ecatb::REGEX[3]        @ecatb_notes[("start_" + $1).to_sym].push(eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $2) + " } }"))      when ecatb::REGEX[6]        @ecatb_notes[:order_icon] = [eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $1) + " } }")]      when ecatb::REGEX[7]        @ecatb_notes[:order_opacity] = [eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $1) + " } }")]      when ecatb::REGEX[8]        @ecatb_notes[:order_scale] = [eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $1) + " } }")]      when ecatb::REGEX[9]        @ecatb_notes[:order_z] = [eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $1) + " } }")]    )
Code:
    COUNTDOWN_NOTE_MID = %Q(      when ecatb::REGEX[5]        @ecatb_countdown_interval ||= eval("-> battler {         battler.instance_exec{ " + eval("ecatb::" + $1) + " } }")    )
Code:
  #----------------------------------------------------------------------------|  #  New method: load_ecatb_notes                                              |  #  - Loads all skill and item notetags                                       |  #----------------------------------------------------------------------------|  def load_ecatb_notes    @ecatb_unison_actor_def = {}    @ecatb_unison_actor_def_rule = {}    # Checks each line against all notetags and stores their values    @note.split(/[\r\n]+/).each { |line|      DoubleX_RMVXA::ECATB::USABLEITEM_NOTES.each_with_index { |note, i|        next unless line =~ DoubleX_RMVXA::ECATB::REGEX[i + 10]        if i > 13          note += "[:#{$1}]"          val = $2        else          val = $1        end        eval("@#{note} ||= -> battler, val = nil { battler.instance_exec {         #{eval("DoubleX_RMVXA::ECATB::#{val}")} } }")      }    }    DoubleX_RMVXA::ECATB::USABLEITEM_NOTES.each { |note|      eval("@#{note} ||= -> battler, val = nil { battler.instance_exec {       #{eval("DoubleX_RMVXA::ECATB::#{note.upcase}")} } }")    }    #  end # load_ecatb_notes
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,501
Reaction score
565
First Language
Chinese
Primarily Uses
N/A
Let's use some case studies to demonstrate some possible workflows of using and/or doing support stuffs to ECATB:

Level 1(No Scripting Proficiency)

Suppose I want to use a start value notetag on a state to set its owner's starting atb value as the value of variable with id 1, 2 and 3 for start type being normal, preemptive and surprise respectively. The below might be my workflow:

1. I look at the Notetag Info for info about the start value notetag:

#    9. <ecatb start val: start, SVX>                                          |#       Sets the atb value at the start of the battle as SVX                   |#       start can be either 0, 1 or 2, meaning the notetag will be used in     |#       normal, preemptive or surprise starts respectively                     |#       SVX can be set in Start Value Notetag Values                           |
2. I look at the existing SVX for help. In this case SV2 is what I want:

# Sets the atb value as that of variable with id x * 100%SV2 = %Q(#{SV0}$game_variables[x])
3. Now I know my desired SVXs are these:

SVX1 = %Q(#{SV0}$game_variables[1])SVX2 = %Q(#{SV0}$game_variables[2])SVX3 = %Q(#{SV0}$game_variables[3])
4. I rename that SVX to something helping me to remember what it does:

SVSetVar1 = %Q(#{SV0}$game_variables[1])SVSetVar2 = %Q(#{SV0}$game_variables[2])SVSetVar3 = %Q(#{SV0}$game_variables[3])
5. I add the below notetag into that state's notebox:

<ecatb start val: 0, SVSetVar1><ecatb start val: 1, SVSetVar2><ecatb start val: 2, SVSetVar3>

Level 2(Little Scripting Proficieny)

Suppose I want to make a skill's damage formula to be something like this:

dmg = target's current atb * damage formula / target's atb rate; clear target's atb; dmg
The below might be my workflow:

1. I look at the Script Call Info for info about reading a battler's current atb and atb rate, and clearing a battler's atb:

# 7. ecatb_reset(reset) |# - Cancels the battler's charging actions |# - The battler's atb and action points will be reset as well if reset |# is true |# 8. ecatb_rate[type] |# Returns the atb gain rate with type type |# - type can be either :color, :charge or :cooldown, meaning normal, |# charge and cooldown atb types respectively |# 9. ecatb_val[type] |# Returns the atb value with type type |# - type can be either :color, :charge or :cooldown, meaning normal, |# charge and cooldown atb types respectively |
2. Now I know they're ecatb_val[:color], ecatb_rate[:color] and ecatb_reset(true):

dmg = b.ecatb_val[:color] * dmg_formula / b.ecatb_rate[:color]; b.ecatb_reset(true); dmg

Level 3(Some Scripting Proficieny)

Suppose I want to make all battler's atb bar color to be fully red when the atb's empty, and fully green when the atb's full. The color transition from red to blue when the atb fills from empty to full should be linear. The below might be my workflow:

1. I look at the script implementation and find the instance variable storing its battler instance's atb value, and I found this:

attr_reader :ecatb_val # Stores the atb value
2. Now I know I need to check @ecatb_val, and I found that the below is what I want:

@ecatb_val[:color]
3. I setup :ecatb_bar_colors to be the below:

      # Sets the atb bar colors      # It'll be used only if no corresponding color notetags are used      # Its corresponding method's created under Game_Battler      # It must return an array of colors      # Example: To set the atb bar colors as text color c and r, g, b, a rgba      #          values, set this as      #          %Q(%Q([Colour.text_colour(c), Color.new(r, g, b, a)]))      :ecatb_bar_colors =>      %Q(%Q([Color.new(255 - @ecatb_val[:color] * 255 / 100,            @ecatb_val[:color] * 255 / 100, 0, 255),            Color.new(255 - @ecatb_val[:color] * 255 / 100,            @ecatb_val[:color] * 255 / 100, 0, 255)])),
4. I look at the Script Call Info and realized that the below script call needs to be used as well:

# 6. ecatb_note_change[note] = true |# - Notifies at least a value of a notetag with type note of a data used |# by the battler has changed |# - note can be :charge, :color, :cooldown, :start, :rate, :se, meaning |# charge rate, bar color, cooldown rate, start value, atb gain rate and|# battler ready se notetags respectively |# - This script call can also be used to change the charge, cooldown, atb|# gain rate and bar colors per frame when it's called per frame |
I use :pre_ecatb_update to call ecatb_note_change[:color] = true per frame:

# Sets something to happen right before updating the atb clock # Its corresponding method's created under Scene_Battle # Example: To support changing atb, charge and cooldown bar colors of all # battlers per frame, set this as # %Q(%Q(all_battle_members.each { |member| # member.ecatb_note_change[:color] = true # })) :pre_ecatb_update => %Q(%Q( all_battle_members.each { |member| member.ecatb_note_change[:color] = true } )),

Level 4(Decent Scripting Proficieny)

Suppose I want to use ECATB with DoubleX RMVXA State Triggers to cause some state triggers to have timing being when the global atb clock(with type tick) is divisible by Graphics.frame_rate. The below might be my workflow:

1. I look at the script implementation and find the instance variable storing the global atb clock(with type tick), and I found this:

# @ecatb_clock: Stores the global atb clock
2. Now I know I need to check @ecatb_clock, and I found that the below is what I want:

@ecatb_clock[:tick]
3. I also want to ensure the global atb clock type is tick, so I'll have to use the below check:

ecatb_turn_type == :tick
4. I check the Script Call Info in DoubleX RMVXA State Triggers and found this:

# * Battler manipulations |# 1. exec_state_triggers(state_id, timing) |# - Executes all state triggers with timing timing of state with id |# state_id |
5. I need to use a custom timing for those state triggers:

<second state trigger: STCX, STAX>
6. Combining all the above, I know what I need to write in :pre_ecatb_update:

# Sets something to happen right before updating the atb clock # Its corresponding method's created under Scene_Battle # Example: To support changing atb, charge and cooldown bar colors of all # battlers per frame, set this as # %Q(%Q(all_battle_members.each { |member| # member.ecatb_note_change[:color] = true # })) :pre_ecatb_update => %Q(%Q( return unless ecatb_turn_type == :tick return unless @ecatb_clock[:tick] % Graphics.frame_rate == 0 all_battle_members.each { |member| next unless member.alive? [state_id_1, state_id_2, state_id_3, ..., state_id_n.each] { |state_id| member.exec_state_triggers(state_id, :second) } } )),

Level 5(Advanced Scripting Proficieny)

Suppose I want to implement the Action Input Speed feature(I actually implemented it). The below might be my workflow(and indeed is):

1. I want to let users use Actors/Classes/Weapons/Armors/Enemies/States notetags to set the action input speed for enemies/actors with autobattle or confusion:

# 8. <ecatb speed reduce: SRX> |# Sets the number of frames an enemy or actor with autobattle and/or |# confusion needs when inputting actions as SRX, which can be set in |# Speed Reduce Notetag Values |
Code:
    #--------------------------------------------------------------------------|    #  Speed Reduce Notetag Values                                             |    #  - Setups SRX used by <ecatb speed reduce: SRX> notetags                 |    #--------------------------------------------------------------------------|    # SRX are used at:    # 1. Game_Battler    #    - @ecatb_notes[:speed].each { |note| note.call(self) } in    #      set_ecatb_speed_reduce    # SRX are strings of RGSS3 codes    # SRX must return a non-negative integer    # The naming of SRX can only use alphanumeric characters    # The below SRX are examples to help you set your CDRX    # You can freely use, rewrite and/or delete these examples    # Don't edit this unless you understand what it does and how it works    SR0 = %Q(@last_ecatb_speed_reduce = )    # Sets the action input speed reduction as x frames    SR1 = %Q(#{SR0}x)    # Sets the action input speed reduction as the number of frames being the    # value of variable with id x    SR2 = %Q(#{SR0}$game_variables[x])    # Adds the action input speed reduction by x frames    SR3 = %Q(@last_ecatb_speed_reduce += x)    # Adds new SRX here        #--------------------------------------------------------------------------|    #  Speed Configurations                                                    |    #  - Setups configurations of the speed features                           |    #  - The value of each configuration is the string of the content of its   |    #    corresponding method                                                  |    #--------------------------------------------------------------------------|    SPEED = {      # Sets the action input speed reduction for enemies and/or actors with      # autobattle and/or confusion      # Its corresponding method's created under Game_Battler      # It must return an array with its 1st element being a non-negative      # integer      # Example: To set the action input speed reduction for enemies and/or      #          actors with autobattle and/or confusion as the value of      #          variable with id x, set this as      #          %Q(%Q(@last_ecatb_speed_reduce = $game_variables[x]))      :ecatb_speed_reduce => %Q(%Q(@last_ecatb_speed_reduce = 0)),      # Sets the priority of the ecatb speed reduce notetags to be used      # Its corresponding method's created under Game_Battler      # It must return an array with elements being either :states, :enemies,      # :armors, :weapons, :classes or :actors      # Example: To set states to have a higher priority than that of actors,      #          set this as "[:states, :actors]"      :ecatb_speed_reduce_ord => %Q(%Q([:states, :enemies, :armors,                                         :weapons, :classes, :actors]))    }
2. I need to register the <ecatb speed reduce: SRX> notetag in REGEX and NOTE_MID:

/<ecatb speed reduce:\s*(\w+)\s*>/,
Code:
      when ecatb::REGEX[7]        @ecatb_notes[:speed] << eval("-> battler, val { battler.instance_exec{        " + eval("ecatb::" + $1) + " } }")
3. I need to register the speed configurations in CONFIG and GAME_BATTLER:

CONFIG = [CORE, BAR, CANCEL, CHARGE, CLOCK, COOLDOWN, FORCE, HOTKEY, ORDER, POOL, SE, SPEED, UNISON]
Code:
      :ecatb_speed_reduce => [        %Q(),        %Q(val.is_a?(Numeric) && val >= 0),        %Q(%Q(0))      ],      :ecatb_speed_reduce_ord => [        %Q(),        %Q(val.is_a?(Array) &&            val.all?(&DoubleX_RMVXA::ECATB::BLOCK[:check_ord])),        %Q(%Q([:states, :enemies, :armors, :weapons, :classes, :actors]))      ],
4. I need to use all the above to code the actual implementations:

Code:
  #----------------------------------------------------------------------------|  #  New method: set_ecatb_speed_reduce                                        |  #  - Sets the current battler action input speed reduction in frames         |  #----------------------------------------------------------------------------|  def set_ecatb_speed_reduce    # Updates the battler speed reduce notetags using the current order    @ecatb_notes[:speed] = []    ecatb_speed_reduce_ord.each { |note|      next unless send(@ecatb_item[note][0])      set_ecatb_notes(send(@ecatb_item[note][1]), :speed, 1, true)    }    #  end # set_ecatb_speed_reduce
Code:
  #----------------------------------------------------------------------------|  #  New method: ecatb_charge_update?                                          |  #  - Checks if charging should take place                                    |  #----------------------------------------------------------------------------|  def ecatb_charge_update?    # Checks if there's no cooldown, escaping or confirmed inputs    return false if @ecatb_val[:cooldown] > 0    return true if @ecatb_esc    return false unless @ecatb_val[:color] >= 100.0 && current_action    return false unless @ecatb_speed_reduce <= 0    return enemy? || current_action.ecatb_confirm if current_action.item    @ecatb_val[:charge] = 0.0    false    #  end # ecatb_charge_update?
Code:
  #----------------------------------------------------------------------------|  #  New method: ecatb_updates                                                 |  #  - Performs the battler's atb clock frame updates                          |  #----------------------------------------------------------------------------|  def ecatb_updates    # Updates the atb, charge, speed, cooldown and countdown statuses    return if !@ecatb_esc && dead?    @ecatb_act_times_change = @ecatb_val_change = false    if @ecatb_val[:color] < 100.0 && @ecatb_val[:cooldown] <= 0.0 && movable?      @ecatb_val[:color] += ecatb_gain_rate      @ecatb_val_change = @last_ecatb_val[:color] != @ecatb_val[:color]      @ecatb_val[:color] = 100.0 if @ecatb_val[:color] > 100.0    end    make_ecatb_act if @ecatb_val[:color] >= 100.0 && @ecatb_val[:cooldown] <= 0    ecatb_charge_update if @ecatb_val[:charge] < 100.0 && ecatb_charge_update?    ecatb_speed_reduce_update if @ecatb_speed_reduce > 0    ecatb_cooldown_update if @ecatb_val[:cooldown] > 0.0 && movable?    ecatb_countdown_update unless @ecatb_countdown_clock.empty?    #  end # ecatb_updates
Code:
  #----------------------------------------------------------------------------|  #  New method: ecatb_speed_reduce_update                                     |  #  - Updates the atb action input speed reduction by frames                  |  #----------------------------------------------------------------------------|  def ecatb_speed_reduce_update    #    return set_ecatb_speed_reduce if ecatb_battler_change?(:speed)    @ecatb_speed_reduce -= 1    @ecatb_speed_reduce = 0 if @ecatb_speed_reduce < 0    #  end # ecatb_speed_reduce_update  #----------------------------------------------------------------------------|  #  New method: set_ecatb_speed_reduce                                        |  #  - Sets the atb action input speed reduction by frames                     |  #----------------------------------------------------------------------------|  def set_ecatb_speed_reduce    last_speed_reduce = @last_ecatb_speed_reduce    ecatb_speed_reduce    super    @ecatb_notes[:speed].each { |note| note.call(self) }    @ecatb_speed_reduce += @last_ecatb_speed_reduce - last_speed_reduce    @ecatb_speed_reduce = 0 if @ecatb_speed_reduce < 0  end # set_ecatb_speed_reduce
Code:
  #----------------------------------------------------------------------------|  #  Alias method: make_auto_battle_actions                                    |  #----------------------------------------------------------------------------|  alias make_auto_battle_actions_ecatb make_auto_battle_actions  def make_auto_battle_actions    make_auto_battle_actions_ecatb    # Added to confirm inputs made with the auto battle status    confirm_ecatb_item    set_ecatb_speed_reduce    #  end # make_auto_battle_actions  #----------------------------------------------------------------------------|  #  Alias method: make_confusion_actions                                      |  #----------------------------------------------------------------------------|  alias make_confusion_actions_ecatb make_confusion_actions  def make_confusion_actions    make_confusion_actions_ecatb    # Added to confirm inputs made with the confusion status    confirm_ecatb_item    set_ecatb_speed_reduce    #  end # make_confusion_actions
Code:
  #----------------------------------------------------------------------------|  #  Alias method: make_actions                                                |  #----------------------------------------------------------------------------|  alias make_actions_ecatb make_actions  def make_actions    make_actions_ecatb    # Added to pay the action and item costs now if charge prior costs are true    return unless BattleManager.btype?(:ecatb)    return unless current_action && current_action.item    confirm_ecatb_item    set_ecatb_speed_reduce    #  end # make_actions
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,501
Reaction score
565
First Language
Chinese
Primarily Uses
N/A
For users belonging to level 5(advanced scripting proficiency)or above(other users might still be able to follow though :) ):

1. Lambdas with Marshal

Due to the aforementioned(#3) change and how notetags in ECATB works, battlers will store tons of lambdas.

As sometimes Marshal will be used on battlers(such as update_help under Window_EquipItem in the default RMVXA scripts) and lambdas can't be serialized, errors will occur if we do nothing to that.

As ECATB doesn't use any external tool, the only way I know to solve the problem is to clear and restore all battler lambdas right before and after using Marshal.

clear_ecatb_lambdas, clear_ecatb_block(Game_Actor only), reset_ecatb_lambdas and init_ecatb_block are methods used for just that:

#----------------------------------------------------------------------------| # (v0.01a+)New method: clear_ecatb_lambdas | # - Clears all stored atb order battler notetag values | #----------------------------------------------------------------------------| def clear_ecatb_lambdas # [:charge, :color, :cooldown, :order_icon, :order_opacity, :order_scale, :order_z, :rate, :se, :speed].each { |type| @ecatb_notes[type] = [] @ecatb_note_change[type] = true } $game_temp.ecatb_unison_actors[self] = @ecatb_unison_actors @ecatb_unison_actors = [] $game_temp.last_ecatb_item[self] = @last_ecatb_item @last_ecatb_item = nil # end # clear_ecatb_lambdas #----------------------------------------------------------------------------| # (v0.01a+)New method: reset_ecatb_lambdas | # - Resets all stored lambdas used in battles | #----------------------------------------------------------------------------| def reset_ecatb_lambdas # reset_ecatb_note_lambdas @ecatb_unison_actors = $game_temp.ecatb_unison_actors[self] $game_temp.ecatb_unison_actors.delete(self) @last_ecatb_item = $game_temp.last_ecatb_item[self] $game_temp.last_ecatb_item.delete(self) # end # reset_ecatb_lambdas
Code:
  #----------------------------------------------------------------------------|  #  New method: init_ecatb_block                                              |  #  - Initializes the iterator block                                          |  #----------------------------------------------------------------------------|  def init_ecatb_block    #    @ecatb_block = -> key {      next if @last_ecatb_val[key] == @ecatb_val[key]      @last_ecatb_val[key] = @ecatb_val[key]      @ecatb_val_change = true      break    }    #  end # init_ecatb_block  #----------------------------------------------------------------------------|  #  New method: clear_ecatb_block                                             |  #  - Clears the iterator block                                               |  #----------------------------------------------------------------------------|  def clear_ecatb_block    #    @ecatb_block = nil    #  end # clear_ecatb_block
2. Lambdas usage

You may have wondered why on_battle_end calls clear_ecatb_lambdas and clear_ecatb_block(Game_Actor only). It's because those lambdas are only ever used in battles, so they should be cleared outside battles to free up memory. If some new lambdas created by you need to be used outside battles as well, don't include them in clear_ecatb_lambdas. You need to use new methods to clear and reset them right before and after using Marshal.
 
Last edited by a moderator:

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Profile Posts

just spent FOREVER troubleshooting an issue with my keyboard... turns out the cord was just loose OTL
I count on someone one time, I let them promise something to me, and one week later it's complete silence as I'm completely let down. I hate everything.
Everytime I need to write dialogues I find myself writing new music pieces... what kind of trick is this?!
Me Being Dumb spending hrs trying to figure out Irenas Visual Novel busts plugin..lol. I'm probably missing something stupid and obvious xDD

let the journey begin >w>
X.X ok... I guess I'll just have to think about 48x48 a little differently. Its not "room for more work"..."its room for more detail!...yay"

Forum statistics

Threads
99,218
Messages
963,167
Members
130,807
Latest member
NotADev
Top