- Joined
- Jan 2, 2014
- Messages
- 1,787
- Reaction score
- 939
- First Language
- Chinese
- Primarily Uses
- N/A
I'm going to talk about atb system script notetag management now, but before that, I want to inform you that I've added invariants into the mix in the previous reply(#20 which is about time performance optimizations). From now on, I'll use more powerful atb system scripts as examples, so be prepared to read more complicated codes lol
Degrees of control and freedom given to users
Notetag Value Priority And Stackability
In this case, the item's notetag takes precedence over the battlers' ones, which takes precedence over the classes', equips' and finally states' ones. The notetag values are stacked via additions.
On the other hand, the color notetags of DoubleX RMVXA Color Addon to YSA Battle System: Classical ATB can't be stacked:
In this case, the notetag value from the state with the lowest priority will be used.
Caching And Reading Notetag Values
On the other hand, the values of the use_atb notetag of Akea Active Time Battle are read right before being used:
If the notetag values can be used frequently(like always used in some atb system hotspots), you'll want to cache them upon game start for the sake of time performance. While such caching adds memory usage thorough the entire game executions, the more frequently the notetag values are used, the more time performance outweighs the memory usage, and vice versa.
Nevertheless, use_atb notetag of Akea Active Time Battle doesn't need to be cached for this alone, as its values are only used when an action's executed, which shouldn't be frequent.
Optimizing Notetag Time Performance
Although I personally treat Yami as a superbly awesome scripting deity, the above method's undoubtedly pure madness in terms of time performance. The only reason I can think of why scripting giants like Yami will write such a complete mess is that it'll be more user-friendly.
While including skills there seems to be simply an understandably silly mistake(even though it's the greatest contributor to severe average fps drops when the battlers have lots of skills), all the others are much, much more serious code smells:
1. The notetag values are read right before being used instead of cached upon game start, even when they're normally used several(or up to dozens of) times per frame
2. The whole design doesn't try to cache which notetags(and hence their values) will be used as the above method always recheck which notetags will be used whenever it's called
There are some others, but these 2 are the biggest time performance killers, so I'll focus on them, especially the 2nd point as the 1st one are almost merely technical affairs(although advanced scripting proficiency might be needed there).
As mentioned in the last reply(#20 which is about time performance optimizations), invariants can be used to boost time performance. In this case, the basic invariant to be found is: When will value and/or value_percent change?
Right now the attempted answer's "per method call", which is the loosest and also the most meaningless and useless invariant possible.
It appears to be the strictest invariant possible, as the notetag values are RGSS3 codes that can return different values per frame(e.g.: rand is added into the mix). However, that's indeed just dead wrong, even though it seems to be 100% correct.
The real answer's this: When the effective notetag list changes(stacking order change, adding new effective notetag, removing notetag that becomes ineffective) or any effective notetag value changes without the former changes(e.g.: rand is added into the mix).
That seems to be the same as the above apparent answer, but in fact it isn't, as we can continue to ask: When will the effective notetag list change? When will any effective notetag value change without the former change?
The answer to the 1st question's easier and more straightforward: When the battler itself/battler class/battler equips/battler skill lists/battler states changes(adding/removing data or changing the existing ordering).
The answer to the 2nd question's harder and trickier: When the rest of the battler's internal states(program states rather than RPG::State) change, or the users added something else(like rand) into the mix.
That still looks like effectively the same as "per method call" as scripters have absolutely no control of what users might add into the mix, but again it's wrong although it's natural to come up with that.
If users add anything that can change the notetag values in ways scripters can't control anyway, those scripts can let those users to use script calls to explicitly inform them that those notetag values can change at specific moments, and assume that it's those users' responsibility to use those script calls properly. While it'll slightly harm those that use insane notetag values, it'll drastically benefit those who're not that crazy.
Implementing those answers can be done via battler change notification flags at all existing battler internal state changing methods(e.g.: refresh under Game_BattlerBase), and notetag change notification flags that are raised by users explicitly(e.g.: via script calls). The change notification flags can be analogous to this forum's user notifications, as they both need to be raised if there are changes, and reset if those notifications are already read.
While any entire answer to this case might need advanced scripting proficiency to be thoroughly comprehended(but if you still insist, you can try to thoroughly comprehend DoubleX RMVXA Enhanced YSA Battle System: Classical ATB which gives Level 5 control and freedom to users and needs advanced scripting proficiency to be thoroughly comprehended), the answer would be significantly simpler if those notetags only give Level 3 control and freedom to users instead(that's 1 solid reason why you'll want to stay away from Level 4 and even Level 5 unless you've advanced scripting proficiency). For instance, the CATB rate addon can be rewritten into something like this(I don't think there's any way to implement the answer without major refactoring):
@lcatbra_val is the cached value, @lcatbra_battler_change is the battler change notification flag, and @lcatbra_note_change is the notetag change notification flags that are raised by users explicitly.
In this case, there are 3 reasons to change @lcatbra_val:
1. The battler's agi changes
2. The effective notetag list changes(stacking order change, adding new effective notetag, removing notetag that becomes ineffective)
3. Any effective notetag value changes without the 2nd changes(e.g.: rand is added into the mix)
The 1st and 2nd reasons should be caught by refresh and clear_states(adding erase_state and add_new_state into the mix are just to play safe in case add_state or remove_state is bypassed).
The 3rd reason should be caught by users by properly using the battler script call lcatbra_note_change = true. It's assumed to be their responsibility.
Note that "there should be no other reason to change @lcatbra_val" is an invariant used here.
Nevertheless, the original implementation does have at least 1 merit - being more user-friendly by minimizing the users' responsibilities. But since it's for advanced users in the first place, they should be able to use that added notification flag properly(whether they'll be willing to do so is another story though).
The average benchmark of these cases in my machine(WinXP 32 bit + i7-3820 without overclock + ASUS GTX550 Ti without overclock + 2 * 2GB DDR3-1333 without overclock) are as follows(Both the Graphics.frame_rate and the monitor refresh rate are set as 120, 4 actors and 8 enemies are included, the average number of skills for each battler's roughly 10, and only the time interval where no battler can act are tested):
1. The original CATB Rate Addon(Without removing the skills portion)
- About 20 graphics redraws per frame(average)
- About 20 graphics updates per frame(average)
2. The original CATB Rate Addon(With the skills portion removed)
- About 60 graphics redraws per frame(average)
- About 60 graphics updates per frame(average)
3. The edited CATB Rate Addon(With Level 4 to Level 5 control and freedom given to users and the skills portion removed)
- About 120 graphics redraws per frame(average)
- About 120 graphics updates per frame(average)
I don't want to be harsh nor rude, but the original design's utterly unforgivable in terms of time performance.
P.S.: Those who do want their atb system scripts' notetags to give Level 4 to Level 5 control and freedom to their users will want to learn this trick:
data.note = eval("-> battler { battler.instance_exec { #{code} } }")It's almost as flexible as direct eval while it's still much faster than direct eval(up to something like 8 times faster in my machine).
This trick will be heavily used when this topic reaches advanced scripting proficiency, so you may want to practice it now if you really want to aim that high(like trying to make the most powerful atb system script ever).
P.S.S.: More convoluted and complicated notetag management(architectures, generics, etc) will be needed if an atb system script's an advanced complex one. It'll be covered when this topic reaches advanced scripting proficiency.
Degrees of control and freedom given to users
I think that it's the 1st thing to consider: How much control and freedom your notetags will give to your users? To me, there are basically 5 such levels:
Level 1 - Everything's hardcoded, meaning notetags don't exist at all
Level 2 - The notetag values are always constant literals, meaning they can't be changed once they're set
Level 3 - The notetag values are that of the switches/variables with the specified ids, meaning those values can be changed by changing those of the associated switches/variables
Level 4 - The notetag values are always constant RGSS3 codes, meaning those codes themselves can't be changed once they're set(but what they deliver can change depending on how those codes are written)
Level 5 - The notetag values are RGSS3 codes that can always be changed on the fly
You'll generally want to stay away from Level 4 and even Level 5 unless you really, really feel/think that you're already just that skilled. So I'll stick to Level 3 until this topic reaches the advanced scripting proficiency level XD
Level 1 - Everything's hardcoded, meaning notetags don't exist at all
Level 2 - The notetag values are always constant literals, meaning they can't be changed once they're set
Level 3 - The notetag values are that of the switches/variables with the specified ids, meaning those values can be changed by changing those of the associated switches/variables
Level 4 - The notetag values are always constant RGSS3 codes, meaning those codes themselves can't be changed once they're set(but what they deliver can change depending on how those codes are written)
Level 5 - The notetag values are RGSS3 codes that can always be changed on the fly
You'll generally want to stay away from Level 4 and even Level 5 unless you really, really feel/think that you're already just that skilled. So I'll stick to Level 3 until this topic reaches the advanced scripting proficiency level XD
In atb system scripts, there are normally 2 types of notetag values: Those that can be stacked, and those that can't. For example:
- The atb gain rate modifier can be defined to be stacked by specifying how they're stacked(like additions, multiplications, etc) and the orders they'll be stacked(e.g.: applying those from the states first before applying those from the battlers themselves)
- The atb bar color modifiers basically can't be stacked, so such notetag with the highest priority will be the effective one(e.g.: those from states always have the highest precedence)
For example, the cast cancel notetag of Victor Engine - Active Time Battle can be stacked:
#-------------------------------------------------------------------------- # * New method: setup_cast_cancel #-------------------------------------------------------------------------- def setup_cast_cancel(user, item) rate = item.cast_cancel rate += user.cast_cancel if item.physical? rate *= cast_protection execute_cast_cancel if rand < rate && cast_action? end
get_all_notes collects all notes from the battlers, then their classes and equips, and finally states.
- The atb gain rate modifier can be defined to be stacked by specifying how they're stacked(like additions, multiplications, etc) and the orders they'll be stacked(e.g.: applying those from the states first before applying those from the battlers themselves)
- The atb bar color modifiers basically can't be stacked, so such notetag with the highest priority will be the effective one(e.g.: those from states always have the highest precedence)
For example, the cast cancel notetag of Victor Engine - Active Time Battle can be stacked:
#-------------------------------------------------------------------------- # * New method: setup_cast_cancel #-------------------------------------------------------------------------- def setup_cast_cancel(user, item) rate = item.cast_cancel rate += user.cast_cancel if item.physical? rate *= cast_protection execute_cast_cancel if rand < rate && cast_action? end
Code:
#-------------------------------------------------------------------------- # * New method: cast_cancel #-------------------------------------------------------------------------- def cast_cancel regexp = /<CAST CANCEL: (\d+)%?>/i get_all_notes.scan(regexp).inject(0.0) {|r| r += ($1.to_f / 100) } end
On the other hand, the color notetags of DoubleX RMVXA Color Addon to YSA Battle System: Classical ATB can't be stacked:
#----------------------------------------------------------------------------| # New method: custom_catb_state_colors | #----------------------------------------------------------------------------| def custom_catb_state_colors return if @state_temp_catb && @state_temp_catb == states @state_temp_catb = states @custom_catb_state_color = false @state_temp_catb.each { |state| next if !state.catb_color && !state.catb_rgba if state.catb_color @custom_catb_state_color1 = state.catb_color1 @custom_catb_state_color2 = state.catb_color2 else @custom_catb_state_color1 = state.catb_rgba1 @custom_catb_state_color2 = state.catb_rgba2 end @custom_catb_state_color = true } end # custom_catb_state_colors
Caching And Reading Notetag Values
Typically, there are 2 ways to extract notetag values: Using rpg data instance variables to cache them upon game start, and reading those notetag right before using the values.
For example, the values of the skill/item charge rate notetags of YSA Battle System: Classical ATB are cached using a new RPG::UsableItem instance variable upon game start:
class RPG::UsableItem < RPG::BaseItem #-------------------------------------------------------------------------- # public instance variables #-------------------------------------------------------------------------- attr_accessor :charge_rate attr_accessor :charge_on #-------------------------------------------------------------------------- # common cache: load_notetags_catb #-------------------------------------------------------------------------- def load_notetags_catb @charge_rate = 100 @charge_on = false #--- self.note.split(/[\r\n]+/).each { |line| case line #--- when YSA::REGEXP::USABLEITEM::CHARGE_RATE @charge_on = true @charge_rate = $1.to_i #--- end } # self.note.split #--- @charge_rate = 100 if @charge_rate <= 0 endend # RPG::UsableItem
For example, the values of the skill/item charge rate notetags of YSA Battle System: Classical ATB are cached using a new RPG::UsableItem instance variable upon game start:
class RPG::UsableItem < RPG::BaseItem #-------------------------------------------------------------------------- # public instance variables #-------------------------------------------------------------------------- attr_accessor :charge_rate attr_accessor :charge_on #-------------------------------------------------------------------------- # common cache: load_notetags_catb #-------------------------------------------------------------------------- def load_notetags_catb @charge_rate = 100 @charge_on = false #--- self.note.split(/[\r\n]+/).each { |line| case line #--- when YSA::REGEXP::USABLEITEM::CHARGE_RATE @charge_on = true @charge_rate = $1.to_i #--- end } # self.note.split #--- @charge_rate = 100 if @charge_rate <= 0 endend # RPG::UsableItem
class Game_Battler < Game_BattlerBasealias :akea_atb_use_Item :use_item #-------------------------------------------------------------------------- # ? Usando habilidade/item # item : habilidade/item #-------------------------------------------------------------------------- def use_item(item) note = /<use_atb *(\d+)?>/i item.note =~ note ? @atb -= $1.to_i : @atb = 0 akea_atb_use_Item(item) endend
Nevertheless, use_atb notetag of Akea Active Time Battle doesn't need to be cached for this alone, as its values are only used when an action's executed, which shouldn't be frequent.
Optimizing Notetag Time Performance
Apart from preferring caching notetag values over reading them right before using them, which is a relatively minor time performance boost, you'll want to avoid the below crystal clear and obvious pitfall if you do value time performance(YSA Battle Add-on: Lunatic CATB Rate):
def lunatic_catb_rate_formula formulas = [] formulas = self.actor.catb_rate + self.class.catb_rate if self.actor? formulas = self.enemy.catb_rate if self.enemy? value = real_gain_catb value_percent = 100 if self.actor? if self.equips self.equips.each { |a| formulas += a.catb_rate if a } end if self.skills self.skills.each { |a| formulas += a.catb_rate if a } end end self.states.each { |state| formulas += state.catb_rate } for formula in formulas case formula.upcase #---------------------------------------------------------------------- # ATB Rate Formula No.1: BOOST PERCENT # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Boost ATB rate by x percents. # # Formula notetag: # <custom catb rate: boost percent x%> #---------------------------------------------------------------------- when /BOOST PERCENT[ ](\d+)([%?])/i value_percent += $1.to_i #---------------------------------------------------------------------- # ATB Rate Formula No.2: REDUCE PERCENT # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Reduce ATB rate by x percents. # # Formula notetag: # <custom catb rate: reduce percent x%> #---------------------------------------------------------------------- when /REDUCE PERCENT[ ](\d+)([%?])/i value_percent = [value_percent - $1.to_i, 1].max #---------------------------------------------------------------------- # ATB Rate Formula No.3: SET PERCENT # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Set ATB rate to x percents. # # Formula notetag: # <custom catb rate: set percent x%> #---------------------------------------------------------------------- when /SET PERCENT[ ](\d+)([%?])/i value_percent = [$1.to_i, 1].max #---------------------------------------------------------------------- # ATB Rate Formula No.4: SET VALUE # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Set base ATB filled speed to x. # # Formula notetag: # <custom catb rate: set value x> #---------------------------------------------------------------------- when /SET VALUE[ ](\d+)/i value = [$1.to_i, 1].max #---------------------------------------------------------------------- # ATB Rate Formula No.5: ADD VALUE # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Increase base ATB filled speed by x. # # Formula notetag: # <custom catb rate: add value x> #---------------------------------------------------------------------- when /ADD VALUE[ ](\d+)/i value += $1.to_i #---------------------------------------------------------------------- # ATB Rate Formula No.6: DEC VALUE # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Decrease base ATB filled speed by x. # # Formula notetag: # <custom catb rate: dec value x> #---------------------------------------------------------------------- when /DEC VALUE[ ](\d+)/i value = [value - $1.to_i, 1].max #---------------------------------------------------------------------- # ATB Starter Formula Default: COMMON RATE #---------------------------------------------------------------------- when /COMMON RATE/i # Do nothing #---------------------------------------------------------------------- # Stop editting past this point. #---------------------------------------------------------------------- end # End case end # End for #---------------------------------------------------------------------- # Return value. #---------------------------------------------------------------------- return value * value_percent / 100 end # End defIt'll be run whenever the battler's atb or charge value can be changed, which can happen on any atb frame update.
def lunatic_catb_rate_formula formulas = [] formulas = self.actor.catb_rate + self.class.catb_rate if self.actor? formulas = self.enemy.catb_rate if self.enemy? value = real_gain_catb value_percent = 100 if self.actor? if self.equips self.equips.each { |a| formulas += a.catb_rate if a } end if self.skills self.skills.each { |a| formulas += a.catb_rate if a } end end self.states.each { |state| formulas += state.catb_rate } for formula in formulas case formula.upcase #---------------------------------------------------------------------- # ATB Rate Formula No.1: BOOST PERCENT # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Boost ATB rate by x percents. # # Formula notetag: # <custom catb rate: boost percent x%> #---------------------------------------------------------------------- when /BOOST PERCENT[ ](\d+)([%?])/i value_percent += $1.to_i #---------------------------------------------------------------------- # ATB Rate Formula No.2: REDUCE PERCENT # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Reduce ATB rate by x percents. # # Formula notetag: # <custom catb rate: reduce percent x%> #---------------------------------------------------------------------- when /REDUCE PERCENT[ ](\d+)([%?])/i value_percent = [value_percent - $1.to_i, 1].max #---------------------------------------------------------------------- # ATB Rate Formula No.3: SET PERCENT # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Set ATB rate to x percents. # # Formula notetag: # <custom catb rate: set percent x%> #---------------------------------------------------------------------- when /SET PERCENT[ ](\d+)([%?])/i value_percent = [$1.to_i, 1].max #---------------------------------------------------------------------- # ATB Rate Formula No.4: SET VALUE # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Set base ATB filled speed to x. # # Formula notetag: # <custom catb rate: set value x> #---------------------------------------------------------------------- when /SET VALUE[ ](\d+)/i value = [$1.to_i, 1].max #---------------------------------------------------------------------- # ATB Rate Formula No.5: ADD VALUE # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Increase base ATB filled speed by x. # # Formula notetag: # <custom catb rate: add value x> #---------------------------------------------------------------------- when /ADD VALUE[ ](\d+)/i value += $1.to_i #---------------------------------------------------------------------- # ATB Rate Formula No.6: DEC VALUE # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Decrease base ATB filled speed by x. # # Formula notetag: # <custom catb rate: dec value x> #---------------------------------------------------------------------- when /DEC VALUE[ ](\d+)/i value = [value - $1.to_i, 1].max #---------------------------------------------------------------------- # ATB Starter Formula Default: COMMON RATE #---------------------------------------------------------------------- when /COMMON RATE/i # Do nothing #---------------------------------------------------------------------- # Stop editting past this point. #---------------------------------------------------------------------- end # End case end # End for #---------------------------------------------------------------------- # Return value. #---------------------------------------------------------------------- return value * value_percent / 100 end # End defIt'll be run whenever the battler's atb or charge value can be changed, which can happen on any atb frame update.
While including skills there seems to be simply an understandably silly mistake(even though it's the greatest contributor to severe average fps drops when the battlers have lots of skills), all the others are much, much more serious code smells:
1. The notetag values are read right before being used instead of cached upon game start, even when they're normally used several(or up to dozens of) times per frame
2. The whole design doesn't try to cache which notetags(and hence their values) will be used as the above method always recheck which notetags will be used whenever it's called
There are some others, but these 2 are the biggest time performance killers, so I'll focus on them, especially the 2nd point as the 1st one are almost merely technical affairs(although advanced scripting proficiency might be needed there).
As mentioned in the last reply(#20 which is about time performance optimizations), invariants can be used to boost time performance. In this case, the basic invariant to be found is: When will value and/or value_percent change?
Right now the attempted answer's "per method call", which is the loosest and also the most meaningless and useless invariant possible.
It appears to be the strictest invariant possible, as the notetag values are RGSS3 codes that can return different values per frame(e.g.: rand is added into the mix). However, that's indeed just dead wrong, even though it seems to be 100% correct.
The real answer's this: When the effective notetag list changes(stacking order change, adding new effective notetag, removing notetag that becomes ineffective) or any effective notetag value changes without the former changes(e.g.: rand is added into the mix).
That seems to be the same as the above apparent answer, but in fact it isn't, as we can continue to ask: When will the effective notetag list change? When will any effective notetag value change without the former change?
The answer to the 1st question's easier and more straightforward: When the battler itself/battler class/battler equips/battler skill lists/battler states changes(adding/removing data or changing the existing ordering).
The answer to the 2nd question's harder and trickier: When the rest of the battler's internal states(program states rather than RPG::State) change, or the users added something else(like rand) into the mix.
That still looks like effectively the same as "per method call" as scripters have absolutely no control of what users might add into the mix, but again it's wrong although it's natural to come up with that.
If users add anything that can change the notetag values in ways scripters can't control anyway, those scripts can let those users to use script calls to explicitly inform them that those notetag values can change at specific moments, and assume that it's those users' responsibility to use those script calls properly. While it'll slightly harm those that use insane notetag values, it'll drastically benefit those who're not that crazy.
Implementing those answers can be done via battler change notification flags at all existing battler internal state changing methods(e.g.: refresh under Game_BattlerBase), and notetag change notification flags that are raised by users explicitly(e.g.: via script calls). The change notification flags can be analogous to this forum's user notifications, as they both need to be raised if there are changes, and reset if those notifications are already read.
While any entire answer to this case might need advanced scripting proficiency to be thoroughly comprehended(but if you still insist, you can try to thoroughly comprehend DoubleX RMVXA Enhanced YSA Battle System: Classical ATB which gives Level 5 control and freedom to users and needs advanced scripting proficiency to be thoroughly comprehended), the answer would be significantly simpler if those notetags only give Level 3 control and freedom to users instead(that's 1 solid reason why you'll want to stay away from Level 4 and even Level 5 unless you've advanced scripting proficiency). For instance, the CATB rate addon can be rewritten into something like this(I don't think there's any way to implement the answer without major refactoring):
# Lunatic CATB Rate Formulas calculates actors/enemies ATB rate.# Use the following notetag to assign the formulas to be used.# NOTE: You can use this with Actor, Enemy, Equipment, Class or State.# NOTE2: MAX_CATB_VALUE = 100000.0# Notetag: <custom catb rate: operator, val, id_flag># operator is the operator with the current value at the left hand side and# val as the right hand side# val is:# - the actual value which is a float or integer if id_flag is val# - the id of the variable whose value's to be used if id_flag is var# Battler script call: lcatbra_note_change = true# Notifies that at least 1 effective notetag value changed# This includes the changes of the variable with id FILL_TIME_VARIABLEclass << DataManager # Edit alias load_database_lcatbra load_database def load_database(*argv, &argb) load_database_lcatbra(*argv, &argb) load_notetags_lcatbra # Added end # load_database def load_notetags_lcatbra # New [$data_actors, $data_classes, $data_enemies, $data_weapons, $data_armors, $data_states].each { |data| data.each { |obj| obj.load_notetags_lcatbra if obj } } end # load_notetags_lcatbraend # DataManagerclass RPG::BaseItem # Edit #----------------------------------------------------------------------------| # New public instance variable | #----------------------------------------------------------------------------| attr_accessor :catb_rate # The complete catb rate notetag list def load_notetags_lcatbra # New @catb_rate = { operator: [], val: [] } @note.split(/[\r\n]+/).each { |line| case line when /<(?:CUSTOM_CATB_RATE|custom catb rate):\s*.+,\s*.+,\s*\w+>/i @catb_rate[
perator] << $1.to_sym next @catb_rate[:val] << $2.to_f if $3.to_sym == :val @catb_rate[:val] << -> { $game_variables[$2.to_i] } end } end # load_notetags_lcatbraend # RPG::BaseItemclass Game_BattlerBase # Edit alias erase_state_lcatbra erase_state def erase_state(state_id, &argb) erase_state_lcatbra(state_id, &argb) @lcatbra_battler_change = true # Added to notify possible notetag change end # erase_state alias refresh_lcatbra refresh def refresh(*argv, &argb) refresh_lcatbra(*argv, &argb) @lcatbra_battler_change = true # Added to notify possible notetag change end # refreshend # Game_BattlerBaseclass Game_Battler < Game_BattlerBase # Edit #----------------------------------------------------------------------------| # New public instance variable | #----------------------------------------------------------------------------| attr_writer :lcatbra_note_change # The notetag value change notification flag alias clear_states_lcatbra clear_states def clear_states(*argv, &argb) clear_states_lcatbra(*argv, &argb) @lcatbra_battler_change = true # Added to notify possible notetag change end # clear_states alias add_new_state_lcatbra add_new_state def add_new_state(state_id, &argb) add_new_state(state_id, &argb) @lcatbra_battler_change = true # Added to notify possible notetag change end # add_new_state alias make_first_catb_value_lcatbra make_first_catb_value def make_first_catb_value(pre = 0, &argb) make_first_catb_value_lcatbra(pre, &argb) @lcatbra_battler_change = true # Added to run lunatic_catb_rate_formula end # make_first_catb_value MAX_CATB_VALUE = 100000.0 def lunatic_catb_rate_formula # New # Reevaluates the cached value only if the notetag value list may be changed return @lcatbra_val unless @lcatbra_battler_change || @lcatbra_note_change @lcatbra_battler_change = @lcatbra_note_change = false if actor? notes = actor.catb_rate + self.class.catb_rate equips.each { |equip| notes += equip.catb_rate if equip } if equips elsif enemy? notes = enemy.catb_rate else notes = [] end states.each { |state| notes += state.catb_rate } @lcatbra_val = real_gain_catb notes.each { |note| val = note[:val].respond_to?
call) ? note[:val].call : note[:val] @lcatbra_val.send(note[
perator], cal) } @lcatbra_val # end # lunatic_catb_rate_formulaend # Game_Battler
In this case, there are 3 reasons to change @lcatbra_val:
1. The battler's agi changes
2. The effective notetag list changes(stacking order change, adding new effective notetag, removing notetag that becomes ineffective)
3. Any effective notetag value changes without the 2nd changes(e.g.: rand is added into the mix)
The 1st and 2nd reasons should be caught by refresh and clear_states(adding erase_state and add_new_state into the mix are just to play safe in case add_state or remove_state is bypassed).
The 3rd reason should be caught by users by properly using the battler script call lcatbra_note_change = true. It's assumed to be their responsibility.
Note that "there should be no other reason to change @lcatbra_val" is an invariant used here.
Nevertheless, the original implementation does have at least 1 merit - being more user-friendly by minimizing the users' responsibilities. But since it's for advanced users in the first place, they should be able to use that added notification flag properly(whether they'll be willing to do so is another story though).
The average benchmark of these cases in my machine(WinXP 32 bit + i7-3820 without overclock + ASUS GTX550 Ti without overclock + 2 * 2GB DDR3-1333 without overclock) are as follows(Both the Graphics.frame_rate and the monitor refresh rate are set as 120, 4 actors and 8 enemies are included, the average number of skills for each battler's roughly 10, and only the time interval where no battler can act are tested):
1. The original CATB Rate Addon(Without removing the skills portion)
- About 20 graphics redraws per frame(average)
- About 20 graphics updates per frame(average)
2. The original CATB Rate Addon(With the skills portion removed)
- About 60 graphics redraws per frame(average)
- About 60 graphics updates per frame(average)
3. The edited CATB Rate Addon(With Level 4 to Level 5 control and freedom given to users and the skills portion removed)
- About 120 graphics redraws per frame(average)
- About 120 graphics updates per frame(average)
I don't want to be harsh nor rude, but the original design's utterly unforgivable in terms of time performance.
P.S.: Those who do want their atb system scripts' notetags to give Level 4 to Level 5 control and freedom to their users will want to learn this trick:
data.note = eval("-> battler { battler.instance_exec { #{code} } }")It's almost as flexible as direct eval while it's still much faster than direct eval(up to something like 8 times faster in my machine).
This trick will be heavily used when this topic reaches advanced scripting proficiency, so you may want to practice it now if you really want to aim that high(like trying to make the most powerful atb system script ever).
P.S.S.: More convoluted and complicated notetag management(architectures, generics, etc) will be needed if an atb system script's an advanced complex one. It'll be covered when this topic reaches advanced scripting proficiency.
Last edited by a moderator:
