+2 votes

I'm using Godot 3.4.2.

Supose I have a Script attached to a CanvasItem, Control or Node2D. This script overrides the 'hide' method with a custom implementation, like this:

func hide():
    print('overriden hide')
    .hide()

At the last line I'm calling the super class implementation, CanvasItem::hide. This works as expected.

However, suppose I wanted to call the superclass hide method in other place of the code, in the same script, using the 'call' or 'callv' methods, like this

func _ready():
    call('hide')

When I do this, it will always call the overriden method, which is expected. But there is not way to only calling the superclass method.

The problem is that, when you use a symbol name on code you can always call a super method, like .hide(), but there is no built-in way of calling a super method dynamically. This is a problem if you needed to do dynamic recursive method calls on super, like this:

func hide():
    call('hide')

I'm currently using a workaround of temporarily removing the script, wich successfully let me call only the superclass method, like this:

func hide():
    var this = get_script()
    self.set_script(null)
    call('hide')
    self.set_script(this)

But unfortunately this has side effects. When I call setscript, the _init method is called again in the same script instance (which is not a problem if the script does not implements _init()) and also all default property initializations are done again, in other words, if you initialized a class property like var a = 1 but changed that value later, when you call setscript the default value assignment will happen again and any value in that property will be lost. Even properties without initialization, like var b will just be initialized to null. This would force anyone to store and reload the properties between the script reset above, like this:

func hide():
    var this = get_script()
    var props = this.get_script_property_list()
    for p in props: set_meta(p.name, get(p.name))
    self.set_script(null)
    call('hide')
    self.set_script(this)
    for p in props: set(p.name, get_meta(p.name))

As always, one might be wondering why I want to do this instead of just calling '.hide()'. I'm making a proxy class that forwards method calls from one node to another. This proxy class implements methods with the same name of existing methods of other classes, like CanvasItem::hide or AnimationPlayer::play. This class extends Node, so it can be attached to any kind of node. The problem is that when this class is attached to a node that already have a method with that name, I want to call the method not in the proxied target node, but in the script's node. And I'm doing dynamically with 'callv()' because the generic method call logic is hidden inside another method, so that my proxied method implementation for, say, 'AnimationPlayer::play' only looks like this:

func play(p1=default, p2=default, p3=default, p4=default):
    return _generic_method_caller_('play', p1, p2, p3, p4)

The 'default' above is just a class property that holds a plain Reference object, used as a signal so that I can properly set the size of the arguments array on callv() to contain only real arguments, not default ones.

I could work around this, but it would be too cumbersome. In each function call I would have to do something like this:

func play(p1=default, p2=default, p3=default, p4=default):
    if not ClassDB.class_has_method(get_class(), 'play'):
        return _generic_method_caller_('play', p1, p2, p3, p4)
    if p4 != default:
        .play(p1, p2, p3, p4)
    elif p3 != default:
        .play(p1, p2, p3)
    elif p2 != default:
        .play(p1, p2)
    elif p1 != default:
        .play(p1)
    else:
        .play()

So I would like to avoid doing this and I'm currrently using the other workaround, do the dynamic call between setting the script to null and back to the instance.

Does anyone knows of any other solutions besides replacing the architeture?

Godot version 3.4.2
in Engine by (263 points)
edited by

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.