- Joined
- Jan 2, 2014
- Messages
- 1,787
- Reaction score
- 939
- First Language
- Chinese
- Primarily Uses
- N/A
This post talks about more advanced concepts and techniques about inspecting objects. You're assumed to have at least:
- Some scripting proficiency(Inexperienced scripters having basic knowledge to the default RMVXA scripts and written at least several simple but complete scripts)
- Basic knowledge to object inspections
Object Inspection Tree
Tree Reduction Recursions
Tree Reduction Scripts
obj.trace_obj(klass) creates the Reduced Object Inspection Tree of obj containing the paths of all klass objects which is linked to obj.
This ensure no object will be queried more than once:
(@obj_trace ||= {})[klass] ? return : @obj_trace[klass] = {}This queries all instance variables linked to the currently queried object:
trace_instance_obj(klass)If the currently queried object is an Array/Hash/Range/Struct, these will query all its elements/values:
return trace_array_obj(klass) if is_a?(Array)return trace_hash_obj(klass) if is_a?(Hash)return trace_range_obj(klass) if is_a?(Range)trace_struct_obj(klass) if is_a?(Struct)Each linked instance variable/element/value of the currently queried object will call trace_all_obj(klass):
If it belongs to klass, @obj_trace[klass][iks] will store the "#{klass}".to_sym tag to notify it belongs to klass.
It'll then create its Reduced Object Inspection Tree and embed that tree to the currently queried object only if it's not empty.
If a Reduced Object Inspection Tree has no objects having the "#{klass}".to_sym tag, that tree will be empty.
For the base case, it won't be empty only if the queried object belongs to klass, as the Onject Inspection Tree is always finite(circular references are always omitted/stopped).
So that script builds the Reduced Object Inspection Tree of the currently queried objects by combining those of all its instance variable/elements/values if they're not empty, meaning tree recursion's used there.
For example:
@actions.trace_obj(Numeric)The below shows how @actions.obj_trace[Numeric] is built:
@actions.obj_trace[Numeric] = {}@forcing.obj_trace[Numeric] = {}@item.obj_trace[Numeric] = {}@class.obj_trace[Numeric] = {}@item.obj_trace[Numeric][
item_id] = [:Numeric]@item_id.obj_trace[Numeric] = {}@actions.obj_trace[Numeric][
item] = {
item_id=>:Numeric}@actions.obj_trace[Numeric][
target_index] = [:Numeric]@target_index.obj_trace[Numeric] = {}@actions.obj_trace[Numeric][
value] = [:Numeric]@value.obj_trace[Numeric] = {}@last_ecatb_confirm.obj_trace[Numeric] = {}@ecatb_confirm.obj_trace[Numeric] = {}This should show that that script uses depth first search to run the recursion.
The result will be this:
p(@actions.obj_trace[Numeric])
That's all for now. I hope this can help you grasp these more advanced stuffs. For those comprehending object inspections, feel free to correct me if there's anything wrong 
- Some scripting proficiency(Inexperienced scripters having basic knowledge to the default RMVXA scripts and written at least several simple but complete scripts)
- Basic knowledge to object inspections
Object Inspection Tree
Recall an example in basic knowledge to object inspections:
p(@item)
Both @class and @item_id directly links to @item. Such links can be displayed this way:
@item | --------- | |@class @item_idIf you know what a tree is, It's actually a tree with @item as the root and @both @class and @item as the leaves. The former is the parent of the latters and the latters are childrens of the former. The latters are siblings of each other and the height of that tree is 1. As it shows the inspection results of @item, It's the Object Inspection Tree of @item.
Recalling another example:
p(@actions)
The Object Inspection Tree of @actions is this:
@actions | --------------------------------------------------------------------- | | | | | | |@subject @forcing @item @target_index @value @last_ecatb_confirm @ecatb_confirm | | --------- | | @class @item_idAlthough @subject refers to an Game_Actor instance, @actions is actually linked to another Game_Actor instance that refers to the exact same object of what @subject refers to, so the @subject part of the @actions Object Inspection Tree is drawn this way to mean it refers to an object that is already linked by @subject.
Also, note that
@item | --------- | |@class @item_idIs both a subtree of the @actions Object Inspection Tree and the Object Inspection Tree of @item.
The indirect link between @class and @actions are shown as the path from @actions to @class:
@actions | ------------------------- | @item | ----- | @class@class is a children of @item, which is a children of @actions, so @class is a descendant of @actions and @actions is an ancestor of @class.
If the linkages of all Numeric to @actions are to be shown instead, the result will be this:
@actions | ------------------------- | | | @item @target_index @value | ----- | @item_idIt combines the path from @actions to all Numeric, resulting in a Reduced Object Inspection Tree containing all Numeric.
p(@item)
Code:
#<Game_BaseItem:0x897ae70 @class=RPG::Skill, @item_id=91>
@item | --------- | |@class @item_idIf you know what a tree is, It's actually a tree with @item as the root and @both @class and @item as the leaves. The former is the parent of the latters and the latters are childrens of the former. The latters are siblings of each other and the height of that tree is 1. As it shows the inspection results of @item, It's the Object Inspection Tree of @item.
Recalling another example:
p(@actions)
Code:
[#<Game_Action:0x897ae84 @subject=#<Game_Actor:0x9aa16b8 ...>, @forcing=false, @item=#<Game_BaseItem:0x897ae70 @class=RPG::Skill, @item_id=91>, @target_index=0, @value=0, @last_ecatb_confirm=true, @ecatb_confirm=true>]
@actions | --------------------------------------------------------------------- | | | | | | |@subject @forcing @item @target_index @value @last_ecatb_confirm @ecatb_confirm | | --------- | | @class @item_idAlthough @subject refers to an Game_Actor instance, @actions is actually linked to another Game_Actor instance that refers to the exact same object of what @subject refers to, so the @subject part of the @actions Object Inspection Tree is drawn this way to mean it refers to an object that is already linked by @subject.
Also, note that
@item | --------- | |@class @item_idIs both a subtree of the @actions Object Inspection Tree and the Object Inspection Tree of @item.
The indirect link between @class and @actions are shown as the path from @actions to @class:
@actions | ------------------------- | @item | ----- | @class@class is a children of @item, which is a children of @actions, so @class is a descendant of @actions and @actions is an ancestor of @class.
If the linkages of all Numeric to @actions are to be shown instead, the result will be this:
@actions | ------------------------- | | | @item @target_index @value | ----- | @item_idIt combines the path from @actions to all Numeric, resulting in a Reduced Object Inspection Tree containing all Numeric.
As the root of an object's inspection tree is that object itself, the reduction always starts from the root.
In general, some specified conditions will be used to determine which linked objects will remain in the Reduced Object Inspection Tree. That check will be used on all objects except the root in the Object Inspection Tree.
When using that check on one of them, the below questions will be asked:
Q1. Does this object refer to an object that's already linked with it?
If yes, do nothing, otherwise ask Q2 and then Q3.
Q2. Does this object meet those specified conditions?
If yes, a tag will be added to that object to notify that it'll remain in the Reduced Object Inspection Tree, otherwise do nothing.
Q3. Does this object contain other objects?
If yes, then Q1 will be asked on all those objects contained by that currently queried object, otherwise do nothing.
The reduction starts from asking Q3 on the root and ends with removing all objects not having that tag and not containing any object having that tag.
This algorithm is actually a tree recursion that uses DFS.
For example:
@actions | --------------------------------------------------------------------- | | | | | | |@subject @forcing @item @target_index @value @last_ecatb_confirm @ecatb_confirm | | --------- | | @class @item_idThe specified condition is if the object is a Numeric.
1. Ask Q3 on the root: Does @actions contain other objects?
- Yes(it contains @subject, @forcing, @item, @target_index, @value, @last_ecatb_confirm and @ecatb_confirm), so ask Q1on all those objects
2. Ask Q1 on @subject: Does @subject refer to an object that's already linked with it?
- Yes(it contains a Game_Acotr object that's already linked with it), so do nothing
3. Ask Q1 on @forcing: Does @forcing refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @forcing
4. Ask Q2 on @forcing: Is @forcing a Numeric?
- No(its value is false), so do nothing
5. Ask Q3 on @forcing: Does @forcing contain other objects?
- No, so do nothing
6. Ask Q1 on @item: Does @item refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @item
7. Ask Q2 on @item: Is @item a Numeric?
- No(it's a Game_BaseItem), so do nothing
8. Ask Q3 on @item: Does @item contain other objects?
- Yes(it contains @class and @item_id), so ask Q1on all those objects
9. Ask Q1 on @class: Does @class refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @class
10. Ask Q2 on @class: Is @class a Numeric?
- No(its value RPG::Skill), so do nothing
11. Ask Q3 on @class: Does @class contain other objects?
- No, so do nothing
12. Ask Q1 on @item_id: Does @item_id refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @item_id
13. Ask Q2 on @item_id: Is @item_id a Numeric?
- Yes(its value is 91), so add a tag to mark that it'll remain in the Reduced Object Inspection Tree
14. Ask Q3 on @item_id: Does @item_id contain other objects?
- No, so do nothing
Similar procedures go from the rest of the objects(@target_index, @value, @last_ecatb_confirm and @ecatb_confirm)
The final step is to remove all objects not having that tag, resulting in this Reduced Object Inspection Tree:
@actions | ------------------------- | | | @item @target_index @value | ----- | @item_id
In general, some specified conditions will be used to determine which linked objects will remain in the Reduced Object Inspection Tree. That check will be used on all objects except the root in the Object Inspection Tree.
When using that check on one of them, the below questions will be asked:
Q1. Does this object refer to an object that's already linked with it?
If yes, do nothing, otherwise ask Q2 and then Q3.
Q2. Does this object meet those specified conditions?
If yes, a tag will be added to that object to notify that it'll remain in the Reduced Object Inspection Tree, otherwise do nothing.
Q3. Does this object contain other objects?
If yes, then Q1 will be asked on all those objects contained by that currently queried object, otherwise do nothing.
The reduction starts from asking Q3 on the root and ends with removing all objects not having that tag and not containing any object having that tag.
This algorithm is actually a tree recursion that uses DFS.
For example:
@actions | --------------------------------------------------------------------- | | | | | | |@subject @forcing @item @target_index @value @last_ecatb_confirm @ecatb_confirm | | --------- | | @class @item_idThe specified condition is if the object is a Numeric.
1. Ask Q3 on the root: Does @actions contain other objects?
- Yes(it contains @subject, @forcing, @item, @target_index, @value, @last_ecatb_confirm and @ecatb_confirm), so ask Q1on all those objects
2. Ask Q1 on @subject: Does @subject refer to an object that's already linked with it?
- Yes(it contains a Game_Acotr object that's already linked with it), so do nothing
3. Ask Q1 on @forcing: Does @forcing refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @forcing
4. Ask Q2 on @forcing: Is @forcing a Numeric?
- No(its value is false), so do nothing
5. Ask Q3 on @forcing: Does @forcing contain other objects?
- No, so do nothing
6. Ask Q1 on @item: Does @item refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @item
7. Ask Q2 on @item: Is @item a Numeric?
- No(it's a Game_BaseItem), so do nothing
8. Ask Q3 on @item: Does @item contain other objects?
- Yes(it contains @class and @item_id), so ask Q1on all those objects
9. Ask Q1 on @class: Does @class refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @class
10. Ask Q2 on @class: Is @class a Numeric?
- No(its value RPG::Skill), so do nothing
11. Ask Q3 on @class: Does @class contain other objects?
- No, so do nothing
12. Ask Q1 on @item_id: Does @item_id refer to an object that's already linked with it?
- No, so ask Q2 and Q3 on @item_id
13. Ask Q2 on @item_id: Is @item_id a Numeric?
- Yes(its value is 91), so add a tag to mark that it'll remain in the Reduced Object Inspection Tree
14. Ask Q3 on @item_id: Does @item_id contain other objects?
- No, so do nothing
Similar procedures go from the rest of the objects(@target_index, @value, @last_ecatb_confirm and @ecatb_confirm)
The final step is to remove all objects not having that tag, resulting in this Reduced Object Inspection Tree:
@actions | ------------------------- | | | @item @target_index @value | ----- | @item_id
While simple Object Inspection Trees can be easily reduced manually, complicated ones often need custom scripts to be reduced. Recall the dreaded exercise 3 in basic knowledge to object inspection:
200 Scripts 30 Scripters Inspect Example 2
I used nearly 4 hours to come up with this:
200 Scripts 30 Scripters Reduced Inspect Example 2
Even though that's because I'm still an incredibly nub scripter, I still think almost no one will want to reduce that manually when there are custom scripts that will do just that.
The aforementioned tree reduction recursion algorithm comes into handy when writing such scripts. For example(DoubleX RMVXA Object Trace):
#----------------------------------------------------------------------------| # New method: trace_obj | # - Traces all objects belonging to klass linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_obj(klass) # Embeds the klass traces of all objects linking to this object (@obj_trace ||= {})[klass] ? return : @obj_trace[klass] = {} trace_instance_obj(klass) return trace_array_obj(klass) if is_a?(Array) return trace_hash_obj(klass) if is_a?(Hash) return trace_range_obj(klass) if is_a?(Range) trace_struct_obj(klass) if is_a?(Struct) # end # trace_obj #----------------------------------------------------------------------------| # New method: trace_instance_obj | # - Traces all instance variables belonging to klass linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_instance_obj(klass) # Embeds the klass traces of all instance variables linking to this object (instance_variables - OBJ_TRACE_IVAR).each { |ivar| trace_all_obj(klass, ivar, instance_variable_get(ivar)) } # end # trace_instance_obj #----------------------------------------------------------------------------| # New method: trace_array_obj | # - Traces all arrays linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_array_obj(klass) # Embeds the klass traces of all arrays linking to this object each_with_index { |val, index| trace_all_obj(klass, index, val) } # end # trace_array_obj #----------------------------------------------------------------------------| # New method: trace_hash_obj | # - Traces all hashes linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_hash_obj(klass) # Embeds the klass traces of all hashes linking to this object each { |key, val| trace_all_obj(klass, key, val) } # end # trace_hash_obj #----------------------------------------------------------------------------| # (v1.00b+)New method: trace_range_obj | # - Traces all ranges linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_range_obj(klass) # Embeds the klass traces of all ranges linking to this object index = -1 each { |val| trace_all_obj(klass, index += 1, val) } # end # trace_range_obj #----------------------------------------------------------------------------| # (v1.00b+)New method: trace_struct_obj | # - Traces all structs linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_struct_obj(klass) # Embeds the klass traces of all structs linking to this object each_pair { |key, val| trace_all_obj(klass, key, val) } # end # trace_struct_obj #----------------------------------------------------------------------------| # (v1.01a+)New method: trace_all_obj | # - Embeds all non empty klass traces linking to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced # iks: The index/key/symbol of the object trace # val: The object to be traced def trace_all_obj(klass, iks, val) # Marks the existence of a klass object or embeds a linked non empty trace @obj_trace[klass][iks] = ["#{klass}".to_sym] if val.is_a?(klass) val.trace_obj(klass) return if (trace = val.obj_trace[klass]).empty? (@obj_trace[klass][iks] ||= []) << trace # end # trace_all_obj
200 Scripts 30 Scripters Inspect Example 2
I used nearly 4 hours to come up with this:
200 Scripts 30 Scripters Reduced Inspect Example 2
Even though that's because I'm still an incredibly nub scripter, I still think almost no one will want to reduce that manually when there are custom scripts that will do just that.
The aforementioned tree reduction recursion algorithm comes into handy when writing such scripts. For example(DoubleX RMVXA Object Trace):
#----------------------------------------------------------------------------| # New method: trace_obj | # - Traces all objects belonging to klass linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_obj(klass) # Embeds the klass traces of all objects linking to this object (@obj_trace ||= {})[klass] ? return : @obj_trace[klass] = {} trace_instance_obj(klass) return trace_array_obj(klass) if is_a?(Array) return trace_hash_obj(klass) if is_a?(Hash) return trace_range_obj(klass) if is_a?(Range) trace_struct_obj(klass) if is_a?(Struct) # end # trace_obj #----------------------------------------------------------------------------| # New method: trace_instance_obj | # - Traces all instance variables belonging to klass linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_instance_obj(klass) # Embeds the klass traces of all instance variables linking to this object (instance_variables - OBJ_TRACE_IVAR).each { |ivar| trace_all_obj(klass, ivar, instance_variable_get(ivar)) } # end # trace_instance_obj #----------------------------------------------------------------------------| # New method: trace_array_obj | # - Traces all arrays linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_array_obj(klass) # Embeds the klass traces of all arrays linking to this object each_with_index { |val, index| trace_all_obj(klass, index, val) } # end # trace_array_obj #----------------------------------------------------------------------------| # New method: trace_hash_obj | # - Traces all hashes linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_hash_obj(klass) # Embeds the klass traces of all hashes linking to this object each { |key, val| trace_all_obj(klass, key, val) } # end # trace_hash_obj #----------------------------------------------------------------------------| # (v1.00b+)New method: trace_range_obj | # - Traces all ranges linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_range_obj(klass) # Embeds the klass traces of all ranges linking to this object index = -1 each { |val| trace_all_obj(klass, index += 1, val) } # end # trace_range_obj #----------------------------------------------------------------------------| # (v1.00b+)New method: trace_struct_obj | # - Traces all structs linked to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced def trace_struct_obj(klass) # Embeds the klass traces of all structs linking to this object each_pair { |key, val| trace_all_obj(klass, key, val) } # end # trace_struct_obj #----------------------------------------------------------------------------| # (v1.01a+)New method: trace_all_obj | # - Embeds all non empty klass traces linking to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced # iks: The index/key/symbol of the object trace # val: The object to be traced def trace_all_obj(klass, iks, val) # Marks the existence of a klass object or embeds a linked non empty trace @obj_trace[klass][iks] = ["#{klass}".to_sym] if val.is_a?(klass) val.trace_obj(klass) return if (trace = val.obj_trace[klass]).empty? (@obj_trace[klass][iks] ||= []) << trace # end # trace_all_obj
This ensure no object will be queried more than once:
(@obj_trace ||= {})[klass] ? return : @obj_trace[klass] = {}This queries all instance variables linked to the currently queried object:
trace_instance_obj(klass)If the currently queried object is an Array/Hash/Range/Struct, these will query all its elements/values:
return trace_array_obj(klass) if is_a?(Array)return trace_hash_obj(klass) if is_a?(Hash)return trace_range_obj(klass) if is_a?(Range)trace_struct_obj(klass) if is_a?(Struct)Each linked instance variable/element/value of the currently queried object will call trace_all_obj(klass):
#----------------------------------------------------------------------------| # (v1.01a+)New method: trace_all_obj | # - Embeds all non empty klass traces linking to this object | #----------------------------------------------------------------------------| # klass: The class of all objects to be traced # iks: The index/key/symbol of the object trace # val: The object to be traced def trace_all_obj(klass, iks, val) # Marks the existence of a klass object or embeds a linked non empty trace @obj_trace[klass][iks] = ["#{klass}".to_sym] if val.is_a?(klass) val.trace_obj(klass) return if (trace = val.obj_trace[klass]).empty? (@obj_trace[klass][iks] ||= []) << trace # end # trace_all_obj
It'll then create its Reduced Object Inspection Tree and embed that tree to the currently queried object only if it's not empty.
If a Reduced Object Inspection Tree has no objects having the "#{klass}".to_sym tag, that tree will be empty.
For the base case, it won't be empty only if the queried object belongs to klass, as the Onject Inspection Tree is always finite(circular references are always omitted/stopped).
So that script builds the Reduced Object Inspection Tree of the currently queried objects by combining those of all its instance variable/elements/values if they're not empty, meaning tree recursion's used there.
For example:
@actions.trace_obj(Numeric)The below shows how @actions.obj_trace[Numeric] is built:
@actions.obj_trace[Numeric] = {}@forcing.obj_trace[Numeric] = {}@item.obj_trace[Numeric] = {}@class.obj_trace[Numeric] = {}@item.obj_trace[Numeric][
The result will be this:
p(@actions.obj_trace[Numeric])
Code:
{:@item=>{:@item_id=>:Numeric}, :@target_index=>:Numeric, :@value=>:Numeric}
Last edited by a moderator:

