/* #=======================# #== AS3 Sprite Parser ==# #== Made by zhade ==# #=======================# */ package parser.ragnarok { import flash.utils.ByteArray; import flash.utils.Timer; import flash.utils.*; import flash.display.*; import flash.errors.*; import flash.events.*; import flash.geom.*; import flash.net.*; public class SpriteParser { private var SPRBA:ByteArray = new ByteArray(); // The .spr file from which we gonna parse the information private var ACTBA:ByteArray = new ByteArray(); // .act, same as above private var spriteData:Array = new Array(); // This will only store the .spr version, ident and number of frames (PAL & RGB) private var actData:Array = new Array(); // In this array will store almost the entire act data, including each actions, its frames and the subframes information private var frameData:Array = new Array(); // This saves the sprites width, height, data length and offset of the images in the .spr file private var paletteData:Array = new Array(); // Pretty much self-explanory right? Structure is paletteData[index(hex)] = RR:GG:BB private var soundData:Array = new Array(); // Contains the path to sound files in grf, saved as strings private var bitmapArray:Array = new Array(); // Contains the from .spr to Flash Bitmap converted images private var dimArray:Array = new Array(); private var animCache:Array = new Array(); private var childArray:Array = new Array(); private var curAct:int; // Which action are we currently viewing? Needed for the UI private var curFrame:int; // Which frame, also needed for UI private var frameIdx:int; // An index used for a loop private var xDimMin:*, xDimMax:*, yDimMin:*, yDimMax:* = null; public var mcFrame:MovieClip = new MovieClip(); // The movieclip that will hold the frames patterns public var mcCapture:MovieClip = new MovieClip(); private var fileName:String; // Name of the input string without extension private var SPRLoader:URLLoader = new URLLoader(); // Load .spr file private var ACTLoader:URLLoader = new URLLoader(); // Load .act file private var runTimer:Timer = new Timer( 0 ); // Every 2/1000 sec a frame is converted from .spr to Bitmap. Is needed to prevent the application freeze private var playTimer:Timer = new Timer( 1000 ); // Timer for the playback, needed for UI. ( Value is changed later in code ) private var useCallback:Boolean = false; // Is set to true if user sets a function for callback private var callback:Function; // the function the user has set private var startPoint:Point = new Point( 0, 0 ); // default position, where the sprite is being placed private var useFixedFrames:Boolean = false; // This determines whether the frameLoading process is loading all Frames or just specifics. private var fixedFrames:Array = new Array(); private var hasParent:Boolean = false; private var hasChilds:Boolean = false; private var parentSpr:SpriteParser = null; public var isEquipment:Boolean = false; private var finished:Boolean = false; // After the parsing process this is set to true private var partialFinish:int; private var FrameTimer:Number; // Constructor of the class, either input a string for spr or do it later with the parse() function public function SpriteParser( SPRFilename:String = null ):void { if( SPRFilename != null) parse( SPRFilename ); } // End function // o=====================================================o // o=====================================================o // o== ==o // o== Sprite Parsing ==o // o== ==o // o=====================================================o // o=====================================================o // Load the file, and call the actual parsing function. If it has been parsed before, show error public function parse( SPRFilename:String, useFixed:Boolean=false ):void { useFixedFrames = useFixed; try { if( finished ) throw new Error( "Attempting to parse file twice. If you want to parse another sprite please create a new instance." ); fileName = SPRFilename; if( SPRFilename.lastIndexOf( "." ) != -1 ) fileName = SPRFilename.slice( 0, SPRFilename.lastIndexOf( "." ) ); SPRLoader.dataFormat = URLLoaderDataFormat.BINARY; var SPRRequest:URLRequest = new URLRequest( fileName + ".spr" ); SPRLoader.addEventListener( Event.COMPLETE, sprLoaded ); SPRLoader.load( SPRRequest ); SPRRequest = null; } catch( err:Error ) { trace( err ); } } // End function // Parse the sprite file. There isn't much I can say, without explaining the .spr file format. // Read the arrays string indexes, it should give you an idea what its reading at that point. private function sprLoaded( ev:Event ):void { trace( "Starting Sprite Parsing (SPR)" ); var TIME:Number=getTimer(); SPRLoader.removeEventListener( Event.COMPLETE, sprLoaded ); SPRBA = SPRLoader.data; SPRBA.endian = Endian.LITTLE_ENDIAN; spriteData['ident'] = SPRBA.readUTFBytes( 2 ); spriteData['version'] = SPRBA.readUnsignedShort().toString( 16 ); spriteData['num_pal'] = SPRBA.readUnsignedShort(); spriteData['num_rgba'] = SPRBA.readUnsignedShort(); var i:int; for( i = 0; i < spriteData['num_pal']; i++ ) { frameData[i] = { width: SPRBA.readUnsignedShort(), height: SPRBA.readUnsignedShort(), data_len: SPRBA.readUnsignedShort(), type: 0, offset: SPRBA.position }; SPRBA.position += frameData[i]['data_len']; //trace( i + ": " + frameData[i]['width'] + " / " + frameData[i]['height'] ); } // End for-loop for( i = 0; i < spriteData['num_rgba']; i++ ) { var idx = spriteData['num_pal'] + i; var Width:uint = SPRBA.readUnsignedShort(); var Height:uint = SPRBA.readUnsignedShort(); frameData[idx] = { width: Width, height: Height, data_len: Width * Height * 4, type: 1, offset: SPRBA.position }; SPRBA.position += frameData[idx]['data_len']; //trace( idx + ": " + frameData[idx]['width'] + " / " + frameData[idx]['height'] ); } // End for-loop // After the frames are done, we are almost at the end of the file. // The only thing thats left is the color palette. (Note that even if its a sprite with only RGBA // images, it will still have a palette, or so I hope =P // The palette is, fortunately, always 1024 bytes long. // There are 256 colors, saved in RRGGBBAA structure. 256 * 4 = 1024. spriteData['PAL'] = SPRBA.position; var a:int; var b:int; var SColor:String; var IdxColor; for( var p = 0; p < 256; p++ ) { paletteData[p.toString( 16 )] = decToCol( SPRBA.readUnsignedByte() << 16 | SPRBA.readUnsignedByte() << 8 | SPRBA.readUnsignedByte() ); SPRBA.position += 0x01; } // End for-loop /* while( SPRBA.position < SPRBA.length && paletteData.length <= 256 ) { IdxColor = SPRBA.readUnsignedByte().toString( 16 ); if( b < 256 ) { if( IdxColor.length < 2 ) IdxColor = "0" + IdxColor; SColor = SColor + IdxColor + ":"; } // End if a++; if( a >= 4 ) { paletteData[b.toString( 16 )] = SColor.slice( 0, SColor.length - 1 ); SColor = ""; b++; a = 0; } // End if } // End while-loop */ trace( "Time: " + (getTimer() - TIME) ); parseAct(); } // End function private function loadFrames():void { trace( "Starting Frame Conversion" ); FrameTimer=getTimer(); runTimer.addEventListener( TimerEvent.TIMER, frameLoading ); runTimer.start(); } // End function private function frameLoading( ev:TimerEvent ):void { if(useFixedFrames) frameIdx = fixedFrames[0]; if( frameIdx < ( spriteData['num_pal'] + spriteData['num_rgba'] ) ) { if( !bitmapArray[frameIdx] ) { var SPRBitmapData:BitmapData = new BitmapData( frameData[frameIdx]['width'], frameData[frameIdx]['height'], true); var SPRBitmap:Bitmap = new Bitmap( SPRBitmapData ); SPRBA.position = frameData[frameIdx]['offset']; // Is PAL image (Data Structure: [Palette Index, [Length:Optional]]) // The length is only defined for the first index in the palette, meaning 00 if( frameData[frameIdx]['type'] == 0 ) { var bgx = 0; var bgy = 0; var tmpColor; var fColor; var tmpLength; var trans = "ff"; var cidx = 0; var pixels:Vector. = new Vector.(frameData[frameIdx]['width']*frameData[frameIdx]['height'], true); //for( var i = 0; i < frameData[frameIdx]['data_len']; i++ ) { while( SPRBA.position < ( frameData[frameIdx]['offset'] + frameData[frameIdx]['data_len'] ) ) { tmpColor = SPRBA.readUnsignedByte().toString( 16 ); if( tmpColor == 00 ) { trans = "00"; tmpLength = SPRBA.readUnsignedByte(); } else { tmpLength = 1; trans = "ff"; } // End if for( var c = 0; c < tmpLength; c++ ) { // fColor = paletteData[tmpColor].split( ":" ); // fColor = int( "0x" + trans + fColor[0] + fColor[1] + fColor[2] ); fColor = "0x" + trans + paletteData[tmpColor]; //--SPRBitmapData.setPixel32( bgx, bgy, fColor ); pixels[cidx] = fColor; cidx++; bgx++; if( bgx >= frameData[frameIdx]['width'] ) { bgx = 0; bgy++; } // End if } // End for-loop } // End while-loop SPRBitmapData.setVector( SPRBitmapData.rect, pixels ); // Is RGBA image (Data Structure: [AABBGGRR], It also goes from top to bottom unlike the PAL image.) } else if( frameData[frameIdx]['type'] == 1 ) { var bgx2 = 0; var bgy2 = frameData[frameIdx]['height'] - 1; //var rColor:Array = new Array(); var rColor; while( SPRBA.position < ( frameData[frameIdx]['offset'] + frameData[frameIdx]['data_len'] ) ) { rColor = "0x" + toRGB( SPRBA.readUnsignedByte() << 24 | SPRBA.readUnsignedByte() | SPRBA.readUnsignedByte() << 8 | SPRBA.readUnsignedByte() << 16 ); /* rColor = [ SPRBA.readUnsignedByte().toString( 16 ), SPRBA.readUnsignedByte().toString( 16 ), SPRBA.readUnsignedByte().toString( 16 ), SPRBA.readUnsignedByte().toString( 16 ) ]; if( rColor[0].length < 2 ) rColor[0] = "0" + rColor[0]; if( rColor[1].length < 2 ) rColor[1] = "0" + rColor[1]; if( rColor[2].length < 2 ) rColor[2] = "0" + rColor[2]; if( rColor[3].length < 2 ) rColor[3] = "0" + rColor[3]; SPRBitmapData.setPixel32( bgx2, bgy2, int( "0x" + rColor[0] + rColor[3] + rColor[2] + rColor[1] ) ); */ SPRBitmapData.setPixel32( bgx2, bgy2, rColor ); bgx2++; if( bgx2 >= frameData[frameIdx]['width'] ) { bgx2 = 0; bgy2--; } // End if } // End while-loop } // End if bitmapArray[frameIdx] = SPRBitmap; } // End if } else { if(!useFixedFrames) { runTimer.stop(); runTimer.removeEventListener( TimerEvent.TIMER, frameLoading ); // parseAct(); parseComplete(); } } // End if if(!useFixedFrames) frameIdx++; else { fixedFrames.splice(0,0); // Remove first entry that has been loaded fixedFrames = reOrderArray(fixedFrames); // Makes all entries move one down, so that the first entry is set again frameIdx = fixedFrames[0]; if(fixedFrames.length <= 0) { runTimer.stop(); runTimer.removeEventListener( TimerEvent.TIMER, frameLoading ); // parseAct(); parseComplete(); } } // End if } // End function // o=====================================================o // o=====================================================o // o== ==o // o== Act Parsing ==o // o== ==o // o=====================================================o // o=====================================================o private function parseAct():void { try { ACTLoader.dataFormat = URLLoaderDataFormat.BINARY; var ACTRequest:URLRequest = new URLRequest( fileName + ".act" ); ACTLoader.addEventListener( Event.COMPLETE, actLoaded ); ACTLoader.load( ACTRequest ); ACTRequest = null; } catch( err:Error ) { trace( err ); } } // End function private function actLoaded( ev:Event ):void { trace( "Starting Sprite Parsing (ACT)" ); var TIME:Number=getTimer(); ACTLoader.removeEventListener( Event.COMPLETE, actLoaded ); ACTBA = ACTLoader.data; ACTBA.endian = Endian.LITTLE_ENDIAN; var anim:int; var nf:int; var pat:int; actData['ident'] = ACTBA.readUTFBytes( 2 ); actData['version'] = ACTBA.readUnsignedShort().toString( 16 ); actData['num_frames'] = ACTBA.readUnsignedShort(); actData['num_actions'] = Math.floor( actData['num_frames'] / 8 ); ACTBA.position += 0xA; // Those are useless/unused/unknown bytes that we skip // Loop through the actions for( anim = 0; anim < actData['num_frames']; anim++ ) { actData[anim] = new Array(); actData[anim]['num_frames'] = ACTBA.readInt(); // Loop through the actions frames for( nf = 0; nf < actData[anim]['num_frames']; nf++ ) { ACTBA.position += 0x20; // Skipped bytes again actData[anim][nf] = new Array(); actData[anim][nf]['num_subframes'] = ACTBA.readInt(); // Loop through the frames patterns for( pat = 0; pat < actData[anim][nf]['num_subframes']; pat++ ) { actData[anim][nf][pat] = new Array(); actData[anim][nf][pat] = { xOffset: ACTBA.readInt(), yOffset: ACTBA.readInt(), sprNo: ACTBA.readInt(), mirror: ACTBA.readInt(), red: ACTBA.readUnsignedByte(), green: ACTBA.readUnsignedByte(), blue: ACTBA.readUnsignedByte(), alpha: ACTBA.readUnsignedByte(), offset: ACTBA.position }; // There are differences depending on the version if( actData['version'] >= 200 && actData['version'] <= 203 ) actData[anim][nf][pat]['xyScale'] = ACTBA.readFloat(); if( actData['version'] >= 204 ) { actData[anim][nf][pat]['xScale'] = ACTBA.readFloat(); actData[anim][nf][pat]['yScale'] = ACTBA.readFloat(); } // End if actData[anim][nf][pat]['rotation'] = ACTBA.readInt(); actData[anim][nf][pat]['sprType'] = ACTBA.readInt(); if( actData['version'] >= 205 ) { actData[anim][nf][pat]['sprWidth'] = ACTBA.readInt(); actData[anim][nf][pat]['sprHeight'] = ACTBA.readInt(); } // End if } // End for-loop // After the patterns follows the sounds, those are pointers to the sounddata at the end of the actfile. actData[anim][nf]['soundNo'] = ACTBA.readInt(); var extrainfo = ACTBA.readInt(); // if this is actually 1 in an act, it will give us the info for reference (eg. head/body/headgear) if( extrainfo == 1 ) { ACTBA.position += 0x04; actData[anim][nf]['refX'] = ACTBA.readInt(); actData[anim][nf]['refY'] = ACTBA.readInt(); ACTBA.position += 0x04; } // End if } // End for-loop } // End for-loop // At the end of the act file we find the sound and interval list // the sounds are frames specific soundData['num_sounds'] = ACTBA.readInt(); if( soundData['num_sounds'] > 0 ) { for( var sn = 0; sn < soundData['num_sounds']; sn++ ) { soundData[sn] = ACTBA.readUTFBytes(40); } // End for-loop } // End if // the intervals are actions specific for( var intv = 0; intv < actData['num_frames']; intv++ ) { actData[intv]['interval'] = ACTBA.readFloat(); } // End if trace( "Time: " + (getTimer() - TIME) ); // parseComplete(); if(useFixedFrames) displayFirstFrame(); else loadFrames(); } // End function // Clean up to reduce memory usage? private function parseComplete():void { SPRLoader.close(); ACTLoader.close(); SPRLoader = null; ACTLoader = null; SPRBA = null; ACTBA = null; runTimer = null; mcCapture.graphics.beginFill(0xFFFFFF,0.15); mcCapture.graphics.drawRect( -1, -1, mcCapture.width+1, mcCapture.height+1 ); mcCapture.graphics.endFill(); trace( "Time: " + (getTimer() - FrameTimer) ); finished = true; if( hasParent ) parentSpr.partFinish(); else partFinish(); /*if( useFixedFrames ) displayAction(0, 0); if( useCallback ) callback(this);*/ } // End function // o=====================================================o // o=====================================================o // o== ==o // o== Functions ==o // o== ==o // o=====================================================o // o=====================================================o // Eh ya, put our info from the actData together and display it on stage I guess... // We loop through each pattern of the specific frame and do some magic to it and then display it. public function displayAction( action:int = 0, frame:int = 0 ):void { if( hasChilds ) { for( var i = 0; i < childArray.length; i++ ) { childArray[i].displayAction( action, frame ); } // End for-loop } try { if( !finished ) throw new Error( "Cannot display action when sprite is not fully parsed" ); if( action != curAct ) { xDimMin = null; xDimMax = null; yDimMin = null; yDimMax = null; } // End if curAct = action; curFrame = frame; var numPattern:int = actData[action][frame]['num_subframes']; while ( mcFrame.numChildren > 0 ) { delete( mcFrame.getChildAt( 0 ) ); mcFrame.removeChildAt( 0 ); } // End while-loop if( !animCache[curAct] ) animCache[curAct] = new Array(); for( var pat = 0; pat < numPattern; pat++ ) { var patData = actData[action][frame][pat]; if( patData['sprNo'] >= 0 ) { var frameNumPadding = ( patData['sprType'] == 1 ) ? spriteData['num_pal'] : 0; var tempBitmapData:BitmapData = bitmapArray[patData['sprNo'] + frameNumPadding].bitmapData.clone(); var tempBitmap:Bitmap = new Bitmap( tempBitmapData ); var patLayer:MovieClip = new MovieClip(); patLayer.ID = pat; patLayer.addChild( tempBitmap ); mcFrame.addChild( patLayer ); var Alpha:Number = patData['alpha'] / 255; var Red:Number = patData['red'] / 255; var Green:Number = patData['green'] / 255; var Blue:Number = patData['blue'] / 255; var cTrans:ColorTransform = patLayer.transform.colorTransform; cTrans.alphaMultiplier = Alpha; cTrans.redMultiplier = Red; cTrans.greenMultiplier = Green; cTrans.blueMultiplier = Blue; patLayer.transform.colorTransform = cTrans; var Mirror = 1; if( patData['mirror'] == 1 ) Mirror = -1; var xScale = 1; var yScale = 1; if( actData['version'] >= 200 && actData['version'] <= 203 ) { xScale = patData['xyScale']; yScale = patData['xyScale']; } else { xScale = patData['xScale']; yScale = patData['yScale']; } // End if patLayer.scaleX *= xScale * Mirror; patLayer.scaleY *= yScale; if( hasParent ) { var newOffsetX = null; var newOffsetY = null; // if( !isEquipment ) { var refOffset:Array = parentSpr.getRefOffset( curAct, curFrame ); newOffsetX = patData['xOffset'] + refOffset[0] - actData[curAct][curFrame]['refX']; newOffsetY = patData['yOffset'] + refOffset[1] - actData[curAct][curFrame]['refY']; // } else { // newOffsetX = patData['xOffset'] - actData[curAct][curFrame]['refX']; // newOffsetY = patData['yOffset'] - actData[curAct][curFrame]['refY']; // } // End if patLayer.x = Math.floor( ( startPoint.x - patLayer.width / 2 * Mirror ) ) + Math.floor( newOffsetX ); patLayer.y = Math.floor( ( startPoint.y - patLayer.height / 2 ) ) + Math.floor( newOffsetY ); } else { patLayer.x = Math.floor( ( startPoint.x - patLayer.width / 2 * Mirror ) ) + Math.floor( patData['xOffset'] ); patLayer.y = Math.floor( ( startPoint.y - patLayer.height / 2 ) ) + Math.floor( patData['yOffset'] ); } // End if if( Math.round( patData['rotation'] ) != 0 ) { var ptRotationPoint:Point = new Point( Math.floor( patLayer.x + patLayer.width / 2 * Mirror ), Math.floor( patLayer.y + patLayer.height / 2 ) ); rotateAroundCenter( patLayer, patData['rotation'], ptRotationPoint); } // End if } else { mcFrame.addChild( new MovieClip() ); // useless } // End if } // End for-loop if( count( animCache[curAct] ) < actData[curAct]['num_frames'] ) { if( mcFrame.numChildren > 0 ) { if( xDimMin == null ) xDimMin = mcFrame.getBounds( mcFrame ).x; if( xDimMax == null ) xDimMax = mcFrame.getBounds( mcFrame ).x + mcFrame.width; if( yDimMin == null ) yDimMin = mcFrame.getBounds( mcFrame ).y; if( yDimMax == null ) yDimMax = mcFrame.getBounds( mcFrame ).y + mcFrame.height; if( xDimMin > mcFrame.getBounds( mcFrame ).x ) xDimMin = mcFrame.getBounds( mcFrame ).x; if( xDimMax < mcFrame.getBounds( mcFrame ).x + mcFrame.width ) xDimMax = mcFrame.getBounds( mcFrame ).x + mcFrame.width; if( yDimMin > mcFrame.getBounds( mcFrame ).y ) yDimMin = mcFrame.getBounds( mcFrame ).y; if( yDimMax < mcFrame.getBounds( mcFrame ).y + mcFrame.height ) yDimMax = mcFrame.getBounds( mcFrame ).y + mcFrame.height; } // End if animCache[curAct][curFrame] = 1; } else { if( !dimArray[curAct] ) dimArray[curAct] = new Matrix( xDimMin, yDimMin, ( xDimMax - xDimMin ), ( yDimMax - yDimMin ) ); mcCapture.width = dimArray[curAct].c; mcCapture.height = dimArray[curAct].d; mcCapture.x = dimArray[curAct].a + mcCapture.width; mcCapture.y = dimArray[curAct].b + mcCapture.height; } // End if // var tmpMatrix:Matrix = new Matrix(); // tmpMatrix.translate( -mcFrame.getBounds(mcFrame).x, -mcFrame.getBounds(mcFrame).y ); // var cacheBitmapData:BitmapData = new BitmapData( mcFrame.width, mcFrame.height, true, 0x00000000 ); // var cacheBitmap:Bitmap = new Bitmap( cacheBitmapData ); // cacheBitmapData.draw( mcFrame, tmpMatrix ); // animCache[curAct][curFrame] = cacheBitmap; // } // End if } catch( err:Error ) { trace( err ); } } // End function private function rotateAroundCenter( ob:*, angleDegrees:Number, ptRotationPoint:Point ):void { var m:Matrix = ob.transform.matrix; m.tx -= ptRotationPoint.x; m.ty -= ptRotationPoint.y; m.rotate( angleDegrees * ( Math.PI / 180 ) ); m.tx += ptRotationPoint.x; m.ty += ptRotationPoint.y; ob.transform.matrix = m; } private function startAnimation( ev:TimerEvent ):void { curFrame++; if( curFrame >= actData[curAct]['num_frames'] ) curFrame = 0; gotoFrame( curFrame ); if( hasChilds ) { for( var i = 0; i < childArray.length; i++ ) { childArray[i].gotoFrame( curFrame ); } // End for-loop } // End if } // End function private function decToCol( dec:uint ):String { var red, green, blue; blue = ( dec & 255 ).toString( 16 ); green = ( ( dec & 65280 ) / 256 ).toString( 16 ); red = ( ( dec & 16711680) / 65536 ).toString( 16 ); red = ( red.length < 2 ) ? "0" + red : red; green = ( green.length < 2 ) ? "0" + green : green; blue = ( blue.length < 2 ) ? "0" + blue : blue; return ( red + green + blue ); } // End function private function toRGB( dec:uint ):String { return dec.toString( 16 ); } // End function private function count( arr:Array ):int { var count:int = 0; for( var i = 0; i < arr.length; i++ ) { if( arr[i] != null ) count++; } return count; } // End function private function getRefOffset( action:int, frame:int ):Array { return new Array( actData[action][frame]['refX'], actData[action][frame]['refY'] ); } // End function private function setChild( sprite:SpriteParser ):void { hasChilds = true; childArray.push( sprite ); } // End function private function reOrderArray( arr:Array ):Array { var newArr:Array = new Array(); for( var i=0; i= ( count( childArray ) + 1 ) ) { mcFrame.visible = true; if( useFixedFrames ) displayAction(0, 0); if( useCallback ) callback(this); } // End if } else { mcFrame.visible = true; if( useFixedFrames ) displayAction(0, 0); if( useCallback ) callback(this); } // End if } // End function // o=====================================================o // o=====================================================o // o== ==o // o== Public Functions ==o // o== ==o // o=====================================================o // o=====================================================o public function isLoaded( func:Function ):void { useCallback = true; callback = func; } // End function public function setPosition( xPos:int, yPos:int ):void { startPoint.x = xPos; startPoint.y = yPos; if( mcFrame.numChildren > 0 && finished ) { for( var pat = 0; pat < mcFrame.numChildren; pat++ ) { var patLayer = mcFrame.getChildAt( pat ); var patData = actData[curAct][curFrame][patLayer.ID]; var Mirror = 1; if( patData['mirror'] == 1 ) Mirror = -1; patLayer.x = Math.floor( ( startPoint.x - patLayer.width / 2 * Mirror ) ) + Math.floor( patData['xOffset'] ); patLayer.y = Math.floor( ( startPoint.y - patLayer.height / 2 ) ) + Math.floor( patData['yOffset'] ); } // End for-loop } // End if } // End function public function setFrame( mc:MovieClip ):void { mcFrame = mc; } // End function public function gotoFrame( frame:int = 0 ):void { if( frame >= 0 && frame < actData[curAct]['num_frames'] ) { displayAction( curAct, frame ); } // End if } // End function public function nextFrame():void { if( ( curFrame + 1 ) < actData[curAct]['num_frames'] ) displayAction( curAct, ( curFrame + 1 ) ); else displayAction( curAct, 0 ); } // End function public function prevFrame():void { if( ( curFrame - 1 ) >= 0 ) displayAction( curAct, ( curFrame - 1 ) ); else displayAction( curAct, ( actData[curAct]['num_frames'] - 1 ) ); } // End function public function playStopAnimation():void { if( !hasParent ) { if( !playTimer.running ) { playTimer.stop(); playTimer.delay = 22 * actData[curAct]['interval']; playTimer.addEventListener( TimerEvent.TIMER, startAnimation ); playTimer.start(); } else { playTimer.stop(); } // End if } // End if } // End function public function nextAct():void { if( ( curAct + 1 ) < actData['num_frames'] ) displayAction( ( curAct + 1 ), 0 ); else displayAction( 0, 0 ); } // End function public function prevAct():void { if( ( curAct - 1 ) >= 0 ) displayAction( ( curAct - 1 ), 0 ); else displayAction( ( actData['num_frames'] - 1 ), 0 ) } // End function public function generatePalette():Bitmap { try { if( !finished ) throw new Error("Cannot generate palette when sprite is not fully parsed"); var paletteBitmapData:BitmapData = new BitmapData( 256, 256, false, 0xffffff ); var paletteBitmap:Bitmap = new Bitmap( paletteBitmapData ); var pColor = 0; var X=0; var Y=0; for( var i = 0; i < 256; i++ ) { pColor = "0x" + paletteData[i.toString( 16 )]; paletteBitmapData.fillRect( new Rectangle( X * 16, Y * 16, 16, 16 ), pColor ); X++; if( X >= 16 ) { X = 0; Y++; } } } catch( err:Error ) { trace( err ); } return paletteBitmap; } // End function public function setParent( sprite:SpriteParser ):void { hasParent = true; parentSpr = sprite; sprite.setChild( this ); } // End function public function getCurFrame():int { return curFrame; } // End function public function getFrameCount():int { return actData[curAct]['num_frames']; } // End function public function getCurAct():int { return curAct; } // End function public function getActCount():int { return actData['num_frames']; } // End function } // End class } // End package