What is the best way to handle variants of a base script

In my project I will have items with the same base behavior and attributes with different values (i.e durability is 20 for one variant and 40 for another)

They way I have it currently, I have an abstract class then a script for each variant which changes the attributes. This would mean I would end up with a bunch (at least 100) of ~10 line scripts.

Alternatively I can just have one script and use _init() function to set the attributes, then depending on the required variant, passing in different values.

I’m just wondering if there is a standard or recommended way to go about this?

From what I understand, I think you should use Resources to define the different attributes of each type of object.
https://docs.godotengine.org/en/latest/tutorials/scripting/resources.html

Looked into it, turns out I had no idea what you could do with resources! Exactly what I needed, thanks for the help :slight_smile:

As an example, I have an UpgradableEntity base class for anything I want to upgrade, this class handles the live state of the entity but also holds the “definition” resource. This resource is exported so I can define it in the inspector easily.

As for the “definition”, it looks something like this.

class_name UpgradableEntityDefinition
extends Resource

@export_group(“General”)
@export var display_name: String = “”
@export var description: String = “”
@export var currency_type: GameEnum.CURRENCY_TYPE = GameEnum.CURRENCY_TYPE.INGOT
@export var entity_type: GameEnum.ENTITY_TYPE = GameEnum.ENTITY_TYPE.BUILDING

@export_group(“Progression”)
@export_range(1, 8) var maximum_tier: int = 8
@export_range(1, 100) var levels_per_tier: int = 10
@export var level_progression: LevelProgressionDefinition

@export_group(“Purchase Cost”)
@export var base_purchase_cost: float = 100.0
@export var purchase_tier_multiplier: float = 3.0

@export_group(“Promotion Cost”)
@export var base_promotion_cost: float = 1000.0
@export var promotion_tier_multiplier: float = 3.0

func get_entity_type() → GameEnum.ENTITY_TYPE:
return entity_type

func get_display_name(_tier: int = 0) → String:
return display_name

func get_description(_tier: int = 0) → String:
return description

Then inside of the inspector, I can make adjustments. (Note: TowerDefinition extends UpgradableEntityDefinition)

This is just one of many ways to use the system. In some cases I also define these numbers in the definition and have the inspector be there in case I want to make one-off adjustments for whatever reason.

class_name TowerDefinition
extends UpgradableEntityDefinition

const NAME_BY_TYPE: Dictionary[GameEnum.TOWER_TYPE, String] = {
GameEnum.TOWER_TYPE.ARROW: “Arrow Tower”,
GameEnum.TOWER_TYPE.MAGE: “Mage Tower”,
}

const DESCRIPTION_BY_TYPE: Dictionary[GameEnum.TOWER_TYPE, String] = {
GameEnum.TOWER_TYPE.ARROW: “Fires fast attacking arrows that target enemies at a distance.”,
GameEnum.TOWER_TYPE.MAGE: “Casts magical orbs at the enemy deal small amounts of splash damage to nearby enemies.”,
}

const PURCHASE_COST_BY_TYPE: Dictionary[GameEnum.TOWER_TYPE, int] = {
GameEnum.TOWER_TYPE.ARROW: 100,
GameEnum.TOWER_TYPE.MAGE: 300,
}

@export var sub_entity_type: GameEnum.TOWER_TYPE = GameEnum.TOWER_TYPE.ARROW

func get_display_name(_tier: int = 0) → String:
return NAME_BY_TYPE.get(sub_entity_type)

func get_description(_tier: int = 0) → String:
return DESCRIPTION_BY_TYPE.get(sub_entity_type)

func get_sub_entity_type() → GameEnum.TOWER_TYPE:
return sub_entity_type

func get_purchase_cost(_tier: int = 1) → int:
return PURCHASE_COST_BY_TYPE.get(sub_entity_type, 100)