@@ -57,6 +57,8 @@ string importFolder = CheckValidity();
5757string packDir = Path . Combine ( ExePath , "Packager" ) ;
5858Directory . CreateDirectory ( packDir ) ;
5959
60+ bool noMasksForBasicRectangles = Data . IsVersionAtLeast ( 2022 , 9 ) ; // TODO: figure out the exact version, but this is pretty close
61+
6062try
6163{
6264 string sourcePath = importFolder ;
7173 int lastTextPage = Data . EmbeddedTextures . Count - 1 ;
7274 int lastTextPageItem = Data . TexturePageItems . Count - 1 ;
7375
76+ bool bboxMasks = Data . IsVersionAtLeast ( 2024 , 6 ) ;
77+ Dictionary < UndertaleSprite , Node > maskNodes = new ( ) ;
78+
7479 // Import everything into UTMT
7580 string prefix = outName . Replace ( Path . GetExtension ( outName ) , "" ) ;
7681 int atlasCount = 0 ;
178183 UndertaleString spriteUTString = Data . Strings . MakeString ( spriteName ) ;
179184 UndertaleSprite newSprite = new UndertaleSprite ( ) ;
180185 newSprite . Name = spriteUTString ;
181- newSprite . Width = ( uint ) n . Bounds . Width ;
182- newSprite . Height = ( uint ) n . Bounds . Height ;
186+ newSprite . Width = ( uint ) n . Texture . BoundingWidth ;
187+ newSprite . Height = ( uint ) n . Texture . BoundingHeight ;
183188 newSprite . MarginLeft = n . Texture . TargetX ;
184189 newSprite . MarginRight = n . Texture . TargetX + n . Bounds . Width - 1 ;
185190 newSprite . MarginTop = n . Texture . TargetY ;
@@ -232,31 +237,15 @@ try
232237 for ( int i = 0 ; i < frame ; i ++ )
233238 newSprite . Textures . Add ( null ) ;
234239 }
235- newSprite . CollisionMasks . Add ( newSprite . NewMaskEntry ( ) ) ;
236240
237- int width = ( ( n . Bounds . Width + 7 ) / 8 ) * 8 ;
238- BitArray maskingBitArray = new BitArray ( width * n . Bounds . Height ) ;
239- for ( int y = 0 ; y < n . Bounds . Height ; y ++ )
240- {
241- for ( int x = 0 ; x < n . Bounds . Width ; x ++ )
242- {
243- IMagickColor < byte > pixelColor = atlasPixels . GetPixel ( x + n . Bounds . X , y + n . Bounds . Y ) . ToColor ( ) ;
244- maskingBitArray [ y * width + x ] = ( pixelColor . A > 0 ) ;
245- }
246- }
247- BitArray tempBitArray = new BitArray ( width * n . Bounds . Height ) ;
248- for ( int i = 0 ; i < maskingBitArray . Length ; i += 8 )
241+ // Only generate collision masks for sprites that need them (in newer GameMaker versions)
242+ if ( ! noMasksForBasicRectangles ||
243+ newSprite . SepMasks is not ( UndertaleSprite . SepMaskType . AxisAlignedRect or UndertaleSprite . SepMaskType . RotatedRect ) )
249244 {
250- for ( int j = 0 ; j < 8 ; j ++ )
251- {
252- tempBitArray [ j + i ] = maskingBitArray [ - ( j - 7 ) + i ] ;
253- }
245+ // Generate mask later (when the current atlas is about to be unloaded)
246+ maskNodes . Add ( newSprite , n ) ;
254247 }
255- int numBytes = maskingBitArray . Length / 8 ;
256- byte [ ] bytes = new byte [ numBytes ] ;
257- tempBitArray . CopyTo ( bytes , 0 ) ;
258- for ( int i = 0 ; i < bytes . Length ; i ++ )
259- newSprite . CollisionMasks [ 0 ] . Data [ i ] = bytes [ i ] ;
248+
260249 newSprite . Textures . Add ( texentry ) ;
261250 Data . Sprites . Add ( newSprite ) ;
262251 continue ;
@@ -314,17 +303,90 @@ try
314303 break ;
315304 }
316305
317- int MarginLeft = n . Texture . TargetX ;
318- int MarginRight = n . Texture . TargetX + n . Bounds . Width - 1 ;
319- int MarginTop = n . Texture . TargetY ;
320- int MarginBottom = n . Texture . TargetY + n . Bounds . Height - 1 ;
321- if ( MarginLeft < sprite . MarginLeft ) sprite . MarginLeft = MarginLeft ;
322- if ( MarginTop < sprite . MarginTop ) sprite . MarginTop = MarginTop ;
323- if ( MarginRight > sprite . MarginRight ) sprite . MarginRight = MarginRight ;
324- if ( MarginBottom > sprite . MarginBottom ) sprite . MarginBottom = MarginBottom ;
306+ // Grow bounding box depending on how much is trimmed
307+ bool grewBoundingBox = false ;
308+ int marginLeft = n . Texture . TargetX ;
309+ int marginRight = n . Texture . TargetX + n . Bounds . Width - 1 ;
310+ int marginTop = n . Texture . TargetY ;
311+ int marginBottom = n . Texture . TargetY + n . Bounds . Height - 1 ;
312+ if ( marginLeft < sprite . MarginLeft )
313+ {
314+ sprite . MarginLeft = marginLeft ;
315+ grewBoundingBox = true ;
316+ }
317+ if ( marginTop < sprite . MarginTop )
318+ {
319+ sprite . MarginTop = marginTop ;
320+ grewBoundingBox = true ;
321+ }
322+ if ( marginRight > sprite . MarginRight )
323+ {
324+ sprite . MarginRight = marginRight ;
325+ grewBoundingBox = true ;
326+ }
327+ if ( marginBottom > sprite . MarginBottom )
328+ {
329+ sprite . MarginBottom = marginBottom ;
330+ grewBoundingBox = true ;
331+ }
332+
333+ // Only generate collision masks for sprites that need them (in newer GameMaker versions)
334+ if ( ! noMasksForBasicRectangles ||
335+ sprite . SepMasks is not ( UndertaleSprite . SepMaskType . AxisAlignedRect or UndertaleSprite . SepMaskType . RotatedRect ) ||
336+ sprite . CollisionMasks . Count > 0 )
337+ {
338+ if ( ( bboxMasks && grewBoundingBox ) || ( sprite . SepMasks is UndertaleSprite . SepMaskType . Precise && sprite . CollisionMasks . Count == 0 ) )
339+ {
340+ // Use this node for the sprite's collision mask if the bounding box grew (or if no collision mask exists for a precise sprite)
341+ maskNodes [ sprite ] = n ;
342+ }
343+ }
344+ }
345+ }
346+ }
347+
348+ // Update masks for when bounding box masks are enabled
349+ foreach ( ( UndertaleSprite maskSpr , Node maskNode ) in maskNodes )
350+ {
351+ // Generate collision mask using either bounding box or sprite dimensions
352+ maskSpr . CollisionMasks . Clear ( ) ;
353+ maskSpr . CollisionMasks . Add ( maskSpr . NewMaskEntry ( Data ) ) ;
354+ ( int maskWidth , int maskHeight ) = maskSpr . CalculateMaskDimensions ( Data ) ;
355+ int maskStride = ( ( maskWidth + 7 ) / 8 ) * 8 ;
356+
357+ BitArray maskingBitArray = new BitArray ( maskStride * maskHeight ) ;
358+ for ( int y = 0 ; y < maskHeight && y < maskNode . Bounds . Height ; y ++ )
359+ {
360+ for ( int x = 0 ; x < maskWidth && x < maskNode . Bounds . Width ; x ++ )
361+ {
362+ IMagickColor < byte > pixelColor = atlasPixels . GetPixel ( x + maskNode . Bounds . X , y + maskNode . Bounds . Y ) . ToColor ( ) ;
363+ if ( bboxMasks )
364+ {
365+ maskingBitArray [ ( y * maskStride ) + x ] = ( pixelColor . A > 0 ) ;
366+ }
367+ else
368+ {
369+ maskingBitArray [ ( ( y + maskNode . Texture . TargetY ) * maskStride ) + x + maskNode . Texture . TargetX ] = ( pixelColor . A > 0 ) ;
370+ }
325371 }
326372 }
373+ BitArray tempBitArray = new BitArray ( maskingBitArray . Length ) ;
374+ for ( int i = 0 ; i < maskingBitArray . Length ; i += 8 )
375+ {
376+ for ( int j = 0 ; j < 8 ; j ++ )
377+ {
378+ tempBitArray [ j + i ] = maskingBitArray [ - ( j - 7 ) + i ] ;
379+ }
380+ }
381+
382+ int numBytes = maskingBitArray . Length / 8 ;
383+ byte [ ] bytes = new byte [ numBytes ] ;
384+ tempBitArray . CopyTo ( bytes , 0 ) ;
385+ for ( int i = 0 ; i < bytes . Length ; i ++ )
386+ maskSpr . CollisionMasks [ 0 ] . Data [ i ] = bytes [ i ] ;
327387 }
388+ maskNodes . Clear ( ) ;
389+
328390 // Increment atlas
329391 atlasCount ++ ;
330392 }
@@ -572,7 +634,7 @@ public class Packer
572634 throw new ScriptException ( fi . FullName + " has 0 frames. Script has been stopped." ) ;
573635 }
574636
575- if ( ! isSprite && frames > 0 )
637+ if ( ! isSprite && frames > 1 )
576638 {
577639 throw new ScriptException ( fi . FullName + " is not a sprite, but has more than 1 frame. Script has been stopped." ) ;
578640 }
@@ -913,14 +975,8 @@ Pressing ""No"" will cause the program to ignore these images.");*/
913975 try
914976 {
915977 spriteName = stripped . Substring ( 0 , lastUnderscore ) ;
916- // check if the frame number is a valid string or not
917-
918-
919-
920-
921-
922-
923978
979+ // Check if the frame number is a valid string or not
924980 Int32 . Parse ( stripped . Substring ( lastUnderscore + 1 ) ) ;
925981 }
926982 catch
@@ -939,11 +995,10 @@ Pressing ""No"" will cause the program to ignore these images.");
939995 {
940996 continue ;
941997 }
942- // throw new ScriptException("Getting the sprite name of " + FileNameWithExtension + " failed.");
943998 }
944999
1000+ // If the sprite doesn't have an underscore, don't bother trying to parse it since it'll be single-frame anyways
9451001 int frame = 0 ;
946- // if the sprite doesn't have an underscore, don't bother trying to parse it since it'll be single-frame anyways
9471002 if ( spriteName != stripped )
9481003 {
9491004 Int32 validFrameNumber = 0 ;
0 commit comments