Alias is not Super!

Help spread the word!Share on FacebookShare on TumblrTweet about this on TwitterShare on Google+Share on LinkedInShare on RedditFlattr the author

There is a bug in several of my scripts regarding aliasing and class inheritance, and this may be a very subtle issue and result in hours of debugging before you find it.

The problem occurred when a scripter was writing some scripts on top of existing custom scripts. In fact it occurred not once, but twice! While trying to alias the remove_state method in Game_Battler and adding a print statement to it, the print statement was never executed, but the state was still removed.

Thinking about it, there is nothing obvious about why this might be happening. It just seems like none of your changes are being applied during the game!

Example

We start with some example snippets in real code.
The remove_state method is defined as follows by default

class Game_Battler < Game_BattlerBase
  def remove_state(state_id)
    if state?(state_id)
      revive if state_id == death_state_id
      erase_state(state_id)
      refresh
      @result.removed_states.push(state_id).uniq!
    end
  end
end

And in one of my own scripts, I aliased the method in Game_Actor like this

class Game_Actor < Game_Battler
  alias :ft_passive_states_remove_state :remove_state
  def remove_state(state_id)    
    return if features_value_set(:passive_state).include?(state_id)
    ft_passive_states_remove_state(state_id)
  end
end

What I expect to happen

When I wrote the code, I believed that I am simply adding a conditional check for actor objects, and then it will call whatever previous logic was defined for remove_state.

What actually happens

Actually that is exactly what happened.

So what’s the problem?

Consider what happens if I want to alias the remove_state method again, adding a simple print statement:

class Game_Battler < Game_BattlerBase
  alias :th_print_test_remove_state :remove_state
  def remove_state(state_id)
    p "this works!"
    th_print_test_remove_state(state_id)
  end
end

Place this script below the custom actor script. When a state is removed from an actor, the print statement doesn’t occur! In fact, it’s as if the alias didn’t even work.

What happened?

alias is not super. It doesn’t automatically call methods defined in superclasses. For some reason I had thought I could use alias this way to avoid overwriting logic defined by other scripts in superclasses, but evidently it backfired and resulted in hard-to-find bugs.

Conclusion

This tutorial basically showed something that you should be careful when choosing to alias methods that are defined in methods defined in multiple classes within a hierarchy.

At the time of writing, I have not found any nice way to address the issue of adding logic to a child class while not overwriting additional logic in parent classes. If you know of any solutions I would encourage you to share it in the comments!

Spreading the word

If you liked the post and feel that others could benefit from it, consider tweeting it or sharing it on whichever social media channels that you use. You can also follow@HimeWorks to get the latest updates or suggest topics that you would like to read about.

 

Help spread the word!Share on FacebookShare on TumblrTweet about this on TwitterShare on Google+Share on LinkedInShare on RedditFlattr the author

You may also like...

Leave a Reply

Your email address will not be published.

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax