2121import org .jetbrains .annotations .NotNull ;
2222
2323import java .io .*;
24+ import java .math .BigDecimal ;
2425import java .nio .charset .StandardCharsets ;
2526import java .nio .file .Files ;
2627import java .nio .file .Path ;
@@ -556,6 +557,7 @@ public void exportToWurst(ObjMod<? extends ObjMod.Obj> dataStore,
556557 try (BufferedWriter out = Files .newBufferedWriter (outFile , StandardCharsets .UTF_8 )) {
557558 out .write ("package WurstExportedObjects_" + fileType .getExt () + "\n " );
558559 out .write ("import ObjEditingNatives\n " );
560+ out .write ("import ObjEditingCommons\n " );
559561 // Add the appropriate stdlib wrapper import for the file type so generated
560562 // code using e.g. AbilityDefinitionSlow can reference that class directly.
561563 switch (fileType ) {
@@ -675,6 +677,13 @@ private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileT
675677 return false ;
676678 }
677679
680+ for (ObjMod .Obj .Mod m : mods ) {
681+ StdlibObjectMappings .FieldMethodInfo info = fieldMethods .get (fieldKey (m , fileType ));
682+ if (info != null && !canUseWrapperForMod (m , info )) {
683+ return false ;
684+ }
685+ }
686+
678687 out .append ("@compiletime function create_" ).append (fileType .getExt ()).append ("_" ).append (newId )
679688 .append ("()\n " );
680689 out .append ("\t new " ).append (wrapperClass ).append ("(" ).append (constructorArgs ).append (")\n " );
@@ -686,7 +695,7 @@ private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileT
686695 if (info .hasLevel () && m instanceof ObjMod .Obj .ExtendedMod ) {
687696 out .append (String .valueOf (((ObjMod .Obj .ExtendedMod ) m ).getLevel ())).append (", " );
688697 }
689- out .append (formatModValue (m , info . isBoolField () )).append (")\n " );
698+ out .append (formatWrapperValue (m , info )).append (")\n " );
690699 } else {
691700 // No wrapper method for this field — emit as a commented raw call so the
692701 // user can see what needs to be handled and add it manually.
@@ -714,6 +723,36 @@ static String fieldKey(ObjMod.Obj.Mod m, ObjectFileType fileType) {
714723 return m .toString () + ":0" ;
715724 }
716725
726+ private static boolean canUseWrapperForMod (ObjMod .Obj .Mod m , StdlibObjectMappings .FieldMethodInfo info ) {
727+ if (!info .parameterType ().isEmpty () && !isPrimitiveParameter (info .parameterType ())
728+ && !isEnumParameter (info .parameterType ())) {
729+ return false ;
730+ }
731+ if (isEnumParameter (info .parameterType ())) {
732+ return enumConstantForObjectString (info .parameterType (), m .getVal ().toString ()) != null ;
733+ }
734+ return true ;
735+ }
736+
737+ private static boolean isEnumParameter (String parameterType ) {
738+ return ENUM_OBJECT_STRING_TO_CONSTANT .containsKey (parameterType );
739+ }
740+
741+ private static boolean isPrimitiveParameter (String parameterType ) {
742+ return switch (parameterType ) {
743+ case "int" , "string" , "real" , "bool" , "boolean" -> true ;
744+ default -> false ;
745+ };
746+ }
747+
748+ private static String formatWrapperValue (ObjMod .Obj .Mod m , StdlibObjectMappings .FieldMethodInfo info ) {
749+ String enumConstant = enumConstantForObjectString (info .parameterType (), m .getVal ().toString ());
750+ if (enumConstant != null ) {
751+ return enumConstant ;
752+ }
753+ return formatModValue (m , info .isBoolField ());
754+ }
755+
717756 /** Formats a mod value for use in generated Wurst source. */
718757 static String formatModValue (ObjMod .Obj .Mod m , boolean isBoolField ) {
719758 if (isBoolField ) {
@@ -722,9 +761,117 @@ static String formatModValue(ObjMod.Obj.Mod m, boolean isBoolField) {
722761 if (m .getValType () == ObjMod .ValType .STRING ) {
723762 return Utils .escapeString (m .getVal ().toString ());
724763 }
764+ if (m .getValType () == ObjMod .ValType .REAL || m .getValType () == ObjMod .ValType .UNREAL ) {
765+ return formatRealLiteral (m .getVal ().toString ());
766+ }
725767 return m .getVal ().toString ();
726768 }
727769
770+ private static String formatRealLiteral (String value ) {
771+ try {
772+ String plain = new BigDecimal (value ).toPlainString ();
773+ return plain .contains ("." ) ? plain : plain + ".0" ;
774+ } catch (NumberFormatException e ) {
775+ return value ;
776+ }
777+ }
778+
779+ private static @ Nullable String enumConstantForObjectString (String parameterType , String value ) {
780+ Map <String , String > values = ENUM_OBJECT_STRING_TO_CONSTANT .get (parameterType );
781+ if (values == null ) {
782+ return null ;
783+ }
784+ String constant = values .get (value );
785+ return constant == null ? null : parameterType + "." + constant ;
786+ }
787+
788+ private static Map <String , String > enumConstants (String ... valueConstantPairs ) {
789+ Map <String , String > result = new LinkedHashMap <>();
790+ for (int i = 0 ; i < valueConstantPairs .length ; i += 2 ) {
791+ result .put (valueConstantPairs [i ], valueConstantPairs [i + 1 ]);
792+ }
793+ return Collections .unmodifiableMap (result );
794+ }
795+
796+ private static final Map <String , Map <String , String >> ENUM_OBJECT_STRING_TO_CONSTANT = Map .of (
797+ "Race" , enumConstants (
798+ "commoner" , "Commoner" ,
799+ "creeps" , "Creeps" ,
800+ "critters" , "Critters" ,
801+ "demon" , "Demon" ,
802+ "human" , "Human" ,
803+ "naga" , "Naga" ,
804+ "nightelf" , "Nightelf" ,
805+ "orc" , "Orc" ,
806+ "other" , "Other" ,
807+ "undead" , "Undead" ,
808+ "unknown" , "Unknown"
809+ ),
810+ "MovementType" , enumConstants (
811+ "" , "None" ,
812+ "foot" , "Foot" ,
813+ "horse" , "Horse" ,
814+ "fly" , "Fly" ,
815+ "hover" , "Hover" ,
816+ "float" , "Float" ,
817+ "amph" , "Amphipic"
818+ ),
819+ "ArmorType" , enumConstants (
820+ "small" , "Small" ,
821+ "medium" , "Medium" ,
822+ "large" , "Large" ,
823+ "fort" , "Fortified" ,
824+ "normal" , "Normal" ,
825+ "hero" , "Hero" ,
826+ "divine" , "Divine" ,
827+ "none" , "Unarmored"
828+ ),
829+ "AttackType" , enumConstants (
830+ "unknown" , "Unknown" ,
831+ "normal" , "Normal" ,
832+ "pierce" , "Pierce" ,
833+ "siege" , "Siege" ,
834+ "spells" , "Spells" ,
835+ "chaos" , "Chaos" ,
836+ "magic" , "Magic" ,
837+ "hero" , "Hero"
838+ ),
839+ "WeaponType" , enumConstants (
840+ "_" , "None" ,
841+ "normal" , "Normal" ,
842+ "instant" , "Instant" ,
843+ "artillery" , "Artillery" ,
844+ "aline" , "ArtilleryLine" ,
845+ "missile" , "Missile" ,
846+ "msplash" , "MissileSplash" ,
847+ "mbounce" , "MissileBounce" ,
848+ "mline" , "MissileLine"
849+ ),
850+ "WeaponSound" , enumConstants (
851+ "Nothing" , "Nothing" ,
852+ "AxeMediumChop" , "AxeMediumChop" ,
853+ "MetalHeavyBash" , "MetalHeavyBash" ,
854+ "MetalHeavyChop" , "MetalHeavyChop" ,
855+ "MetalHeavySlice" , "MetalHeavySlice" ,
856+ "MetalLightChop" , "MetalLightChop" ,
857+ "MetalLightSlice" , "MetalLightSlice" ,
858+ "MetalMediumBash" , "MetalMediumBash" ,
859+ "MetalMediumChop" , "MetalMediumChop" ,
860+ "MetalMediumSlice" , "MetalMediumSlice" ,
861+ "RockHeavyBash" , "RockHeavyBash" ,
862+ "WoodHeavyBash" , "WoodHeavyBash" ,
863+ "WoodLightBash" , "WoodLightBash" ,
864+ "WoodMediumBash" , "WoodMediumBash"
865+ ),
866+ "ArmorSoundType" , enumConstants (
867+ "Ethereal" , "Ethereal" ,
868+ "Flesh" , "Flesh" ,
869+ "Wood" , "Wood" ,
870+ "Stone" , "Stone" ,
871+ "Metal" , "Metal"
872+ )
873+ );
874+
728875 /** Appends a single raw field-setter call (e.g. ..setLvlDataUnreal(...)) to {@code out}. */
729876 private static void appendRawMod (Appendable out , ObjMod .Obj .Mod m , ObjectFileType fileType ) throws IOException {
730877 if (m instanceof ObjMod .Obj .ExtendedMod ext ) {
0 commit comments