Loading subclass objects from script files

Godot Version

v4.2.2.stable.official [15073afe3]

Question

I have a var of type Script exported to the editor so that I can drag in gd scripts from my resources.
The script files all extend a ControlSource class itself extending RefCounted.
I can load the script and see the object in the debugger - the object is untyped and includes the fields from the superclass, but if I try to cast it as the Superclass, it fails and returns a Null.

Using print commands to debug it further, I noticed that the loaded script object claims to have the signals within its object:

Signal List: [{ "name": "move", "args": [{ "name": "direction", "class_name": &"", "type": 5, "hint": 0, "hint_string": "", "usage": 0 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "actions", "args": [{ "name": "inputs", "class_name": &"", "type": 28, "hint": 31, "hint_string": "ControlSource.ACTION", "usage": 0 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }]

Signal Move exists: true

However, no signals connect and instead, this error appears in the log:

E 0:00:01:0531   character_node.gd:18 @ _ready(): In Object of type 'GDScript': Attempt to connect nonexistent signal 'move' to callable 'CharacterBody2D(character_node.gd)::_update_direction'.
  <C++ Error>    Condition "!signal_is_valid" is true. Returning: ERR_INVALID_PARAMETER
  <C++ Source>   core/object/object.cpp:1344 @ connect()
  <Stack Trace>  character_node.gd:18 @ _ready()

character_node.gd:

extends CharacterBody2D
@export var speed = 200
@export var friction = 0.01
@export var acceleration = 0.1
@export var _control_script:Script
#var control_inputs:ControlSource
var direction:Vector2

#var control_class

func  _ready():
	direction = Vector2(0,0)
#    control_inputs = _control_script.new() as ControlSource
#    control_inputs.connect("move", _update_direction)

	print("Signal List: ", _control_script.get_script_signal_list())
	print("Signal Move exists: ", _control_script.has_script_signal("move"))
	_control_script.connect("move", _update_direction)
		
func _update_direction(event_direction:Vector2):
	direction = event_direction

func _physics_process(delta):
	if (velocity.x == 0  and velocity.y == 0):
		velocity = velocity.lerp(Vector2.ZERO, friction)
	else:
		velocity = velocity.lerp(direction.normalized() * speed * delta, acceleration * delta)
		move_and_slide()

ControlSource.gd:

class_name ControlSource
extends Script

enum ACTION { PUNCH, KICK, GRAPPLE }

signal move(direction:Vector2)
signal actions(inputs:Array[ACTION])

Player_One_Input.gd:

class_name PlayerOneInput
extends ControlSource

var _old_dir:Vector2

var _old_actions:Array[ControlSource.ACTION]

var _dir:Vector2 = Vector2(0,0)
var _actions:Array[ControlSource.ACTION] = []

func _physics_process(_delta) -> void:
	if Input.is_action_pressed("move_left"):
		_dir.x = -1
	elif Input.is_action_pressed("move_right"):
		_dir.x = 1
	else:
		_dir.x = 0
	if Input.is_action_pressed("move_up"):
		_dir.y = -1
	elif Input.is_action_pressed("move_down"):
		_dir.y = 1
	else:
		_dir.y = 0
	if Input.is_action_pressed("punch"):
		if ControlSource.ACTION.PUNCH not in _actions:
			_actions.append(ControlSource.ACTION.PUNCH)
	else:
		if ControlSource.ACTION.PUNCH in _actions:
			_actions.remove_at(_actions.find(ControlSource.ACTION.PUNCH))
	if _dir != _old_dir:
		move.emit(_dir)
		print("Updated _dir: ", _dir)
		_old_dir = _dir
		
	if _actions != _old_actions:
		actions.emit(_actions)
		print("Updated _actions: ", _actions)
		_old_actions = _actions

Can someone point me in the direction of how to handle polymorphism in Godot?
I was hoping to keep the character controllers and inputs/enemyAI as abstract components that I can plug in at instantiation or in the editor.

Many thanks in advance.

I have a var of type Script exported to the editor so that I can drag in gd scripts from my resources.

as i understand you need this working in Editor right?

then i guess issue might be lack of @tool (might be wrong here tho because you use debugger)

Thanks for responding so quickly, and not quite.

I’m trying to make scripts that inherit the ControlSource super class that I can drop onto different character objects in the editor to assign different input mappings and AI implementations to different characters.

(characters being a saved scene composed of various common bits of functionality)

I was trying to bake in the composition pattern to the character handling, whether it’s a player character, NPC or enemy.

Maybe its related to this topic then?

To check you might access ControlSource by loading it, not directly.