diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeChecker.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeChecker.java index 4f845d1ab..9f7e5369e 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeChecker.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeChecker.java @@ -100,7 +100,16 @@ public final class TypeChecker { return true; } } - + + // Same-name match: handles ScriptTypeInfo, non-Java types, and same-class Java types. + // Must run before isAssignableFrom so type arg checking isn't skipped for same-class pairs. + if (expected.getFullName().equals(actual.getFullName())) { + if (expected.isParameterized() && actual.isParameterized()) { + return areTypeArgumentsCompatible(expected, actual); + } + return true; + } + // Object type compatibility (check inheritance) if (expected.getJavaClass() != null && actual.getJavaClass() != null) { Class expectedClass = expected.getJavaClass(); @@ -156,6 +165,45 @@ public final class TypeChecker { return false; } + private static boolean areTypeArgumentsCompatible(TypeInfo expected, TypeInfo actual) { + java.util.List expectedArgs = expected.getAppliedTypeArgs(); + java.util.List actualArgs = actual.getAppliedTypeArgs(); + + // Actual has no type args — it's a raw type being assigned to a parameterized type. + // In Java this is always valid (unchecked warning, not an error). + if (actualArgs.isEmpty()) { + return true; + } + + // If all of actual's type args are unresolved type parameters (e.g., Box + // from diamond "new Box<>()"), treat as raw/inferred — always compatible. + boolean allActualAreTypeParams = true; + for (TypeInfo actualArg : actualArgs) { + if (!actualArg.isTypeParameter()) { + allActualAreTypeParams = false; + break; + } + } + if (allActualAreTypeParams) { + return true; + } + + for (int i = 0; i < expectedArgs.size(); i++) { + TypeInfo expectedArg = expectedArgs.get(i); + TypeInfo actualArg = actualArgs.get(i); + + if (expectedArg.isTypeParameter() || actualArg.isTypeParameter()) { + continue; + } + + if (!isTypeCompatible(expectedArg, actualArg) && !isTypeCompatible(actualArg, expectedArg)) { + return false; + } + } + + return true; + } + /** * Check if the type is a numeric type (including wrappers). */ diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java index 8f310eb12..2e1fa9aa5 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java @@ -1287,6 +1287,9 @@ public class TypeInfo { return false; } + if(!appliedTypeArgs.equals(other.appliedTypeArgs)) + return false; + if (!Objects.equals(elementType, other.elementType)) return false;