godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.08k stars 69 forks source link

Add anonymous classes to GDScript #10132

Open gunbuilderguy opened 1 week ago

gunbuilderguy commented 1 week ago

Describe the project you are working on

I'm currently porting a java game that heavily uses anonymous classes amongst other things. I've managed to cope with things like passing from java's generous enum behaviours by making them public static constants etc.

But anonymous classes is something i cannot easily cope with without, here's a simple dummy example:

if(usecase_1){
  CombatManager manager = new CombatManager("fast-ride"){
    @Override
    public float getAggressionWeighing(GameCharacter target, ArrayList<GameCharacter> Enemies){
      switch(target.getDefaultStance()){
        case STANCE_FRONT:
          ...
      }
    }
  }
} else if(usecase_2){
  CombatManager manager = new CombatManager("indoors"){
//12 other cases

Describe the problem or limitation you are having in your project

The limitation here comes to translating this to godot while keeping it reasonably readable, as the current ways to translate it is to:

This kind of limitation becomes especially painful when you make multiple constants that are based off anonymous classes, for example; a colour database that has functions for more specific behaviors:

  public static Colour RED = new Colour(false, BaseColour.RED, "red", "red").setLinkedLighter(RED_LIGHT);

  public static Colour COVERING_RAINBOW = new Colour(false, BaseColour.BLUE_LIGHT,
      "<span style='color:#E64C4C;'>r</span>"
      + "<span style='color:#E6854C;'>a</span>"
      + "<span style='color:#E6C74C;'>i</span>"
      + "<span style='color:#6EE64C;'>n</span>"
      + "<span style='color:#4CB2E6;'>b</span>"
      + "<span style='color:#AD4CE6;'>o</span>"
      + "<span style='color:#E64CA8;'>w</span>", 
      "rainbow") {
    @Override
    public List<String> getRainbowColours() {
      return Util.newArrayListOfValues(
        "#E64C4C",
        "#E6854C",
        "#E6C74C",
        "#6EE64C",
        "#4CB2E6",
        "#AD4CE6",
        "#E64CA8");
    }
  };

In the game's database, most colours don't use anonymous classes, but given there are about five hundred members, the translated version ends up with nearly a hundred script files acting as anonymous classes, and having them be inner classes would make the colour database much harder to read than it has any reason to.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Adding an anonymous class structure in GDscript would go a long way fixing that kind of translation woe, something similar to:

var watch : Item = Item.new("watch", args[...]):
  func advance(ms : float, owner: GameCharacter) -> void:
    self.customData["timerS"] = self.customData.get("timerS", 0) + ms*0.001

equivalent to

class watch extends Item:
  func advance(ms : float, owner: GameCharacter) -> void:
    self.customData["timerS"] = self.customData.get("timerS", 0) + ms*0.001

and

class_name watch
extends Item

func advance(ms : float, owner: GameCharacter) -> void:
  self.customData["timerS"] = self.customData.get("timerS", 0) + ms*0.001

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

The compiler, specifically in GDScriptCompiler::_prepare_compilation, handles inner classes, the code can be reused in the individual function compiling to prepare the compilation of those anonymous classes when processing those functions to treat them in a similar fashion.

Currently, i'm sifting through the code and i'm working towards making a PR that would allow anonymous classes, figuring out the token system and its handlers. If this issue is just not brought up enough (a few reddit posts or godot forum posts), i'll figure out in time a working PR to implement it in godot.

If this enhancement will not be used often, can it be worked around with a few lines of script?

As specified above, it CAN be worked around but it comes at the cost of readability, and in the case of the colour database mentioned bloats the script to about twice its line count on top of hurting its readability. And as an extra inconvenience, you will have your autocomplete flooded with dozens of classes that are meant to be anonymous.

Is there a reason why this should be core and not an add-on in the asset library?

I don't think this can simply be made in an add-on at that level of code.

gunbuilderguy commented 1 week ago

In short, the addition would consist of modifying GDScriptParser::parse_precedence or add to the tokenizer's get_rule's colon token an anonymous class parser infix, getting the node's class and making a class extending it.

The problem i can see arising would be that if the value is already parsed and creates a new instance of that existing class, that you would have to assign it as being that inheriting anonymous class, and my knowledge of how that works is too limited to know if that is feasible. I at the very least think this is possible in theory.