@@ -207,6 +207,14 @@ String GDScriptFunction::_get_callable_call_error(const String &p_where, const C
207207 }
208208}
209209
210+ bool GDScriptFunction::_is_class_using_trait (Script *p_class_script, const StringName &trait_type) {
211+ GDScript *gdscript = Object::cast_to<GDScript>(p_class_script);
212+ if (gdscript && gdscript->traits_fqtn .has (trait_type)) {
213+ return true ;
214+ }
215+ return false ;
216+ }
217+
210218void (*type_init_function_table[])(Variant *) = {
211219 nullptr , // NIL (shouldn't be called).
212220 &VariantInitializer<bool >::init, // BOOL.
@@ -258,6 +266,7 @@ void (*type_init_function_table[])(Variant *) = {
258266 &&OPCODE_TYPE_TEST_ARRAY, \
259267 &&OPCODE_TYPE_TEST_DICTIONARY, \
260268 &&OPCODE_TYPE_TEST_NATIVE, \
269+ &&OPCODE_TYPE_TEST_TRAIT, \
261270 &&OPCODE_TYPE_TEST_SCRIPT, \
262271 &&OPCODE_SET_KEYED, \
263272 &&OPCODE_SET_KEYED_VALIDATED, \
@@ -284,6 +293,7 @@ void (*type_init_function_table[])(Variant *) = {
284293 &&OPCODE_ASSIGN_TYPED_SCRIPT, \
285294 &&OPCODE_CAST_TO_BUILTIN, \
286295 &&OPCODE_CAST_TO_NATIVE, \
296+ &&OPCODE_CAST_TO_TRAIT, \
287297 &&OPCODE_CAST_TO_SCRIPT, \
288298 &&OPCODE_CONSTRUCT, \
289299 &&OPCODE_CONSTRUCT_VALIDATED, \
@@ -938,6 +948,34 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
938948 }
939949 DISPATCH_OPCODE;
940950
951+ OPCODE (OPCODE_TYPE_TEST_TRAIT) {
952+ CHECK_SPACE (4 );
953+
954+ GET_VARIANT_PTR (dst, 0 );
955+ GET_VARIANT_PTR (value, 1 );
956+
957+ int trait_type_idx = _code_ptr[ip + 3 ];
958+ GD_ERR_BREAK (trait_type_idx < 0 || trait_type_idx >= _global_names_count);
959+ const StringName trait_type = _global_names_ptr[trait_type_idx];
960+
961+ bool was_freed = false ;
962+ Object *object = value->get_validated_object_with_check (was_freed);
963+ if (was_freed) {
964+ err_text = " Left operand of 'is' is a previously freed instance." ;
965+ OPCODE_BREAK;
966+ }
967+
968+ bool result = false ;
969+ if (object && object->get_script_instance ()) {
970+ Script *script_ptr = object->get_script_instance ()->get_script ().ptr ();
971+ result = _is_class_using_trait (script_ptr, trait_type);
972+ }
973+
974+ *dst = result;
975+ ip += 4 ;
976+ }
977+ DISPATCH_OPCODE;
978+
941979 OPCODE (OPCODE_TYPE_TEST_SCRIPT) {
942980 CHECK_SPACE (4 );
943981
@@ -1671,6 +1709,47 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
16711709 }
16721710 DISPATCH_OPCODE;
16731711
1712+ OPCODE (OPCODE_CAST_TO_TRAIT) {
1713+ CHECK_SPACE (4 );
1714+ GET_VARIANT_PTR (src, 0 );
1715+ GET_VARIANT_PTR (dst, 1 );
1716+ GET_VARIANT_PTR (to_type, 2 );
1717+
1718+ int trait_type_idx = _code_ptr[ip + 3 ];
1719+ GD_ERR_BREAK (trait_type_idx < 0 || trait_type_idx >= _global_names_count);
1720+ const StringName trait_type = _global_names_ptr[trait_type_idx];
1721+
1722+ #ifdef DEBUG_ENABLED
1723+ if (src->operator Object *() && !src->get_validated_object ()) {
1724+ err_text = " Trying to cast a freed object." ;
1725+ OPCODE_BREAK;
1726+ }
1727+ if (src->get_type () != Variant::OBJECT && src->get_type () != Variant::NIL) {
1728+ err_text = " Trying to assign a non-object value to a variable of trait '" + String (trait_type).replace (" ::" , " ." ) + " '." ;
1729+ OPCODE_BREAK;
1730+ }
1731+ #endif
1732+ bool valid = false ;
1733+
1734+ if (src->get_type () != Variant::NIL && src->operator Object *() != nullptr ) {
1735+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance ();
1736+
1737+ if (scr_inst) {
1738+ Script *src_type = src->operator Object *()->get_script_instance ()->get_script ().ptr ();
1739+ valid = _is_class_using_trait (src_type, trait_type);
1740+ }
1741+ }
1742+
1743+ if (valid) {
1744+ *dst = *src; // Valid cast, copy the source object
1745+ } else {
1746+ *dst = Variant (); // invalid cast, assign NULL
1747+ }
1748+
1749+ ip += 4 ;
1750+ }
1751+ DISPATCH_OPCODE;
1752+
16741753 OPCODE (OPCODE_CAST_TO_SCRIPT) {
16751754 CHECK_SPACE (4 );
16761755 GET_VARIANT_PTR (src, 0 );
0 commit comments