godotengine / godot-proposals

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

Allow using a variable of type `Array[String]` as an argument to `@export_flags()` #3395

Open nabfrew opened 2 years ago

nabfrew commented 2 years ago

Describe the project you are working on

In my 2d game you build structures by attaching parts-nodes to attachment point-nodes. There is a set of part-types. Each part-node may function as more than one type, and each attachment point-node may accept more than one type.

I would like to define the part-types and the types that the attachment point nodes accept as exported bit flags. Provided they are the same, it would then be easy to check if there is a match, and if/how the parts should attach.

Describe the problem or limitation you are having in your project

I initially solved this using String arrays and a method to identify the intersection. This works fine, but the bit flag method is more elegant.

I can of course use the @export_bit_flags() method, and just copy and paste the strings. But this means having to maintain them to be the same.

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

I would like to be able to add an array of strings, that I can define in one place, as an argument to @export_flags().

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

var parts_types : Array[String] = ["wheel", "block", "leg"]
@export_flags(part_types) accepted_types = 6                  # 4 + 2, accepts block and leg-parts by default

I would like this in addition to the current implementation, where it only accepts multiple string arguments.

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

I think it will be used quite often.

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

I think it would be a pretty typical use of @export_flags, seems a waste for it to be implemented without having this functionality.

Calinou commented 2 years ago

I'm not sure if this is feasible, as GDScript expects annotation hint values to be constant. Here, there is no guarantee that your Array[String] value is constant, as it's declared with var instead of const.

If you switch the member variable declaration to use const instead, it might be feasible.

nabfrew commented 2 years ago

Hm, that's a shame, I hoped it might be an easy fix.

I didn't have my heart set on it being var, should I edit it in the main post?

Out of my depth now, but how about if under the hood it is copied to a constant? I can't think of any reason you would want it to change during runtime.

dalexeev commented 2 months ago

I don't think annotation overloads are a good idea, even though they are resolved at compile time so don't have the same problems as method overloads. Separate annotations also do not make sense, since they would clutter up @GDScript. Instead, we could add spread syntax:

const PART_TYPES = ["wheel", "block", "leg"]
@export_flags(...PART_TYPES) var accepted_types = 6

Also, it was previously possible to pass all hints via 1 annotation argument (like @export_flags("wheel,block,leg"), and also you could pass a constant expression), but we have limited the ability since other checks did not work correctly.

CrazyLegoMind commented 1 month ago

what about adding enums as @export_flags() argument?

i have this issue where in a class i use flags to check multiple properties of the object, but if then i want to have a flag field in another cl;ass with the same flag naming and amount... my only way is using the advanced export features available through property list

currently:

extends Resource
class_name Damage

enum Types {BASE=1,PIERCING=2,FIRE=4,SLUDGE=8,AETHER=16}

@export_flags("BASE", "PIERCING", "FIRE","SLUDGE","AETHER") var _types_mask: = 0b1

static func get_flags_export_hint() -> String:
    var hint_string := ""
    for key in Types:
        hint_string += "%s:%d" % [ key, Types[key]]
        hint_string += ","
    return hint_string
@tool
extends Area2D
class_name HurtBox

var damage_flags:int = 0b1

func _get_property_list():
    var properties = []
    properties.append({
        "name": "damage_flags",
        "type": TYPE_INT,
        "usage": PROPERTY_USAGE_DEFAULT,
        "hint": PROPERTY_HINT_FLAGS,
        "hint_string": Damage.get_flags_export_hint(),
    })
    return properties

what i would love to have:

extends Resource
class_name Damage

enum Types {BASE=1,PIERCING=2,FIRE=4,SLUDGE=8,AETHER=16}

@export_flags(Types ) var _types_mask: = 0b1
extends Area2D
class_name HurtBox

@export_flags(Damage.Types ) var damage_flags: = 0b1