The RPG Maker Resource Kit

RMRK RPG Maker Creation => General Tutorials and Eventing => RPG Maker General => Tutorials Database => Topic started by: modern algebra on March 27, 2008, 02:36:32 AM

Title: Aliasing in RGSS/2
Post by: modern algebra on March 27, 2008, 02:36:32 AM
Aliasing

Why should we alias?

Always alias where it is possible. Most incompatibility issues can be avoided simply by aliasing a method rather then overwriting it. Why is this? This is because when reading scripts, the Interpreter will take the method that is the lowest in the script editor and read it, ignoring any instances of the method above that one.  This means that if you write a script which overwrites a method, and another script also overwrites that method, then the two scripts will be incompatible since the Interpreter only reads ONE of the rewritten methods. This might be confusing, so I will give an example:

Lets say we are making a script that allows the user to make skills that drain levels from the actor. Once the battle ends (or once the user drinks a potion or whatever), then we will want to be able to restore the lost levels and so, naturally, we need a way to record how much experience has been taken away, so that we know how much to give back to him when it is restored. Well, to do this, we probably want to take a look at the change_level or change_exp method, but for the sake of simplicity, we will look at just the change_level method:

Code: [Select]
  #--------------------------------------------------------------------------
  # * Change Level
  #     level : new level
  #     show  : Level up display flag
  #--------------------------------------------------------------------------
  def change_level(level, show)
    level = [[level, 99].min, 1].max
    change_exp(@exp_list[level], show)
  end


Now, what we need to do is simply record how much experience is being subtracted, and so what we would do (without aliasing) is record it into a variable @temporary_exp:

Code: [Select]
  #--------------------------------------------------------------------------
  # * Change Level
  #     level : new level
  #     show  : Level up display flag
  #     temp_change : Is it temporary or permanent?
  #--------------------------------------------------------------------------
  def change_level(level, show, temp_change = false)
    if temp_change
      # Lazy instantiation - it would probably be easier to properly initialize it in
      # the initialize method, but for the sake of the example this is better
      @temporary_exp = 0 if @temporary_exp == nil
      # @exp is current experience, so subtract from it the experience we are being reduced to.
      @temporary_exp += @exp - @exp_list[level]
    end
    level = [[level, 99].min, 1].max
    change_exp(@exp_list[level], show)
  end


So, basically, what we did was add the ability to specify whether or not the level of the hero is only temporarily affected, and then if it was we recorded the amount lost in our variable. Well, that's fine. We've done what we set out to do. But what if another script comes along that also overwrites that method. It doesn't matter what he/she does; if he/she overwrites change_level then our script and his/her script will be incompatible, because it can only read one of our scripts. However, if we aliased the script instead, that particular problem would not occur as long as our script is below his. So that is why we should alias, but how does aliasing affect that? Keep reading.


What does aliasing do?


Quote from: Help File
An aliased method carries over that method definition, retaining it even if the original method is redefined. This can be used when you want to change the actions of a given method, then use the result of the original method in the redefined method.

What that means is that you are essentially renaming the old method without knowing what the original method is (though in most cases you will expect it to do something specific), and thus we are able to call the method separate from our own method. Well, a side effect of that is that it also fixes the problem mentioned above by “saving” the method without the scripter needing to know what changes have been made to the default class. Thus, despite the fact that someone else has edited the class, aliasing will access that overwritten method, and thus save it. It is kind of like "saving" the old method under a new name. Thus, when we overwrite the old method name, we are not erasing that other method and we can just call it back. Hopefully you understand, but if not, I will go over it a little more as I go over how to alias a method.


How do you alias?

Essentially, you  use this code:

Code: [Select]
alias newmethod oldmethod
def oldmethod
  newmethod
end

Basically, what newmethod is there is actually the new name for the old method. Thus, newmethod is essentially calling the original method to act in our new method. The code above would then overwrite oldmethod, but the original method is still preserved through the aliasing and so when we call newmethod within our changed oldmethod, it will run the original method. Essentially, the code directly above does the exact same thing as the Original method with no changes whatsoever.

Take this code:

Code: [Select]
alias newmethod oldmethod
def oldmethod
  p ‘This prints before the old method is run’
  newmethod
  p ‘This prints after the old method is run’
end

It would print the first statement, then run the original method that is being aliased, then it would print the second statement. Essentially, where you place newmethod determines where the original method is run in your new method. While you can alias without actually calling the original method, if you overwrite the old method then you really ought to call the original method somewhere in your overwritten method. There can be reasons for aliasing without overwriting the old method, of course. For instance, if you are making a card game and you are using somebody's method that happens to take all the elements in an array and randomly rearranges them and it's called, say random_rearrange. Then it might make your script more intuitive to alias the method and call it shuffle. However, in most cases, you will be overwriting the old method and in that case you should call the old method somewhere in your new method


Example

Let’s close this tutorial by returning to the change_level example. Notice that in our code above the original method remains completely unmolested; it exists in its original form at the bottom of the method and looks like this:

Code: [Select]
    level = [[level, 99].min, 1].max
    change_exp(@exp_list[level], show)

This is a perfect place to alias, and this is what our aliased method now looks like:


Code: [Select]
  #--------------------------------------------------------------------------
  # * Change Level
  #     level : new level
  #     show  : Level up display flag
  #     temp_change : Is it temporary or permanent?
  #--------------------------------------------------------------------------
  alias modalg_tempchangestolvl_record_lvl_change change_level
  def change_level(level, show, temp_change = false)
    if temp_change
      # Lazy instantiation - it would probably be easier to properly initialize it in
      # the initialize method, but for the sake of the example this is better
      @temporary_exp = 0 if @temporary_exp == nil
      # @exp is current experience, so subtract from it the experience we are being reduced to.
      @temporary_exp += @exp - @exp_list[level]
    end
    modalg_tempchangestolvl_record_lvl_change (level, show)
  end


What is happening there is that it is essentially performing our purpose (recording the amount of experience changed), and then merely calling the original method. Thus, if the other scripter did change this method, it will run his modified method and so the method will also do what he/she wanted it to do, provided that his/her modification is either also aliased or located above our script in the Editor.


Dangers

To close this tutorial, there are a few dangers in using the method that you should be aware of. First, naming:

The more generic your name is, then the more likely it is that somebody else writing a different script will use the same name when he aliases another method in that class. If this happens, you will get a stack error and your script will be incompatible with the other scripter's. Thus, when naming, you should try to make your name as unique as possible, and there are a few strategies to achieve this goal.

(A) Put your name/screenname in the name. Notice that the name I gave my alias is prefaced by modalg.
(B) Put the name of the script that you are writing. The title I chose for this fake script was tempchangestolvl, referring to a title Like Temporary Changes to Level
(C) Be very specific as to the function that you add to the method. Notice that the name I used has  record_lvl_change in it.
(D) Shorten names arbitrarily. Notice modern algebra is shortened to modalg, Temporary is shortened to temp, Level is shortened to levl.

Those are the only strategies I used in the example, but if you feel your name is still too generic, you can also try:

(F) Add garbage to the method. Garbage just means that it has no relation to the method whatsoever, it is just random characters that are unlikely to appear in another scripter's naming. For instance: _lek34bow8
(E) Some scripters recommend including the name of the class to which the method belongs, which in this case would be Game_Actor. I've never seen the point of this, since any names that have a possibility of causing incompatibility would necessarily be in the same class. But I suppose it can't hurt especially if you employ strategy D. In this case (gm_actr, gam_acor, etc...). It certainly allows you to easily see what class the aliased method belongs to.

Another danger is adding unwanted recursion. If you don't know what Recursion is, please read Zeriab's excellent tutorial at this point: Letters to Satiel (http://rmrk.net/index.php/topic,6834.0.html)

Now that you know what it is, here is an example of it provided by Zeriab:

Code: [Select]
def oldmethod
  p 'I am the original old method'
end

alias newmethod oldmethod
def oldmethod
  p 'This prints before the old method is run'
  newmethod
  p 'This prints after the old method is run'
end

p 'First time'
oldmethod

alias newmethod oldmethod
def oldmethod
  p 'This prints before the old method is run'
  newmethod
  p 'This prints after the old method is run'
end

p 'Second time'
oldmethod

This would result in something like this happening:

Code: [Select]
# -> 'First time'
# -> 'This prints before the old method is run'
# -> 'I am the original old method'
# -> 'This prints after the old method is run'
# -> 'Second time'
# -> 'This prints before the old method is run'
# -> 'This prints before the old method is run'
# -> 'This prints before the old method is run'
# -> 'This prints before the old method is run'
# -> And so on infinitely?

As you see, the name confusion is causing recursion. This is because the second alias is aliasing the first aliased method, but because the identifier newmethod exists already, it is calling the first aliased method, and the first then proceeds to call itself over and over again everytime it hits newmethod. Thus, it is a good example of unwanted recursion and the importance of naming.

Alright, well hopefully that helped. If you didn't understand something, feel free to ask and I will do my best to clarify.
Title: Re: Aliasing in RGSS/2
Post by: ahref on April 12, 2008, 11:29:30 AM
excellent that explained it nicely:D