home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacFormat 1999 Spring
/
macformat-077.iso
/
Shareware Plus
/
Development
/
SpriteWorld 2.2
/
SpriteWorld files
/
Sources
/
Tiling.c
< prev
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1999-02-12
|
85.6 KB
|
2,918 lines
|
[
TEXT/CWIE
]
///--------------------------------------------------------------------------------------
// Tiling.c
//
// By Vern Jensen. SWLoadTile routines by Karl Bunker.
//
// Created: 11/15/95
//
// Description: Routines to use tiling with SpriteWorld
///--------------------------------------------------------------------------------------
#ifndef __QUICKDRAW__
#include <QuickDraw.h>
#endif
#ifndef __MEMORY__
#include <Memory.h>
#endif
#ifndef __SPRITEWORLD__
#include "SpriteWorld.h"
#endif
#ifndef __SPRITEFRAME__
#include "SpriteFrame.h"
#endif
#ifndef __SPRITEWORLDUTILS__
#include "SpriteWorldUtils.h"
#endif
#ifndef __TILING__
#include "Tiling.h"
#endif
#ifndef __BLITPIXIE__
#include "BlitPixie.h"
#endif
///--------------------------------------------------------------------------------------
// SWInitTiling
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWInitTiling(
SpriteWorldPtr spriteWorldP,
short tileHeight,
short tileWidth,
short maxNumTiles)
{
OSErr err = noErr;
Size arraySize;
short tileIndex;
SW_ASSERT(spriteWorldP != NULL);
if (spriteWorldP->tilingIsInitialized)
{
err = kTilingAlreadyInitialized;
}
if (err == noErr)
{
spriteWorldP->tilingIsOn = true;
spriteWorldP->tilingIsInitialized = true;
spriteWorldP->lastActiveTileLayer = 0;
spriteWorldP->maxNumTiles = maxNumTiles;
spriteWorldP->tileHeight = tileHeight;
spriteWorldP->tileWidth = tileWidth;
}
if (err == noErr)
{
// Create both tiling cache and changedTiles array of rects
err = SWInitTilingCache(spriteWorldP);
}
if (err == noErr)
{
// Allocate memory for tileFrameArray
arraySize = (Size)maxNumTiles * sizeof(FramePtr);
spriteWorldP->tileFrameArray = (FramePtr *)NewPtrClear(arraySize);
err = MemError();
}
if (err == noErr)
{
// Allocate memory for curTileImage array
arraySize = (Size)maxNumTiles * sizeof(short);
spriteWorldP->curTileImage = (short *)NewPtr(arraySize);
err = MemError();
if (err == noErr)
{
// Set up values in curTileImage array
for (tileIndex = 0; tileIndex < maxNumTiles; tileIndex++)
spriteWorldP->curTileImage[tileIndex] = tileIndex;
}
}
if (err == noErr)
{
// Allocate memory for tileLayerArray
arraySize = (Size)kNumTileLayers * sizeof(TileMapStructPtr);
spriteWorldP->tileLayerArray = (TileMapStructPtr *)NewPtrClear(arraySize);
err = MemError();
}
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWExitTiling
///--------------------------------------------------------------------------------------
SW_FUNC void SWExitTiling(
SpriteWorldPtr spriteWorldP)
{
short tileIndex;
SW_ASSERT(spriteWorldP != NULL);
// Was tiling ever initialized?
if (spriteWorldP->tilingIsInitialized)
{
tileIndex = spriteWorldP->maxNumTiles;
while (tileIndex--)
{
SWDisposeTile(spriteWorldP, tileIndex);
}
DisposePtr((Ptr)spriteWorldP->tileFrameArray);
DisposePtr((Ptr)spriteWorldP->curTileImage);
DisposePtr((Ptr)spriteWorldP->tileLayerArray);
DisposePtr((Ptr)spriteWorldP->changedTiles);
spriteWorldP->changedTiles = NULL;
DisposePtr((Ptr)spriteWorldP->tilingCache[0]); // Dispose the data
DisposePtr((Ptr)spriteWorldP->tilingCache); // Dispose the array of pointers
spriteWorldP->tilingCache = NULL;
spriteWorldP->tilingIsInitialized = false;
spriteWorldP->tilingIsOn = false;
}
}
///--------------------------------------------------------------------------------------
// SWInitTilingCache - an internal function; called by SWInitTiling and SWChangeTileSize.
// Creates both the tiling cache and the changedTiles array of rects.
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWInitTilingCache(
SpriteWorldPtr spriteWorldP)
{
short row, col, numTilingCacheCols, numTilingCacheRows, *tilingCacheData;
Size arraySize;
OSErr err = noErr;
// Dispose the old tilingCache if necessary
if (spriteWorldP->tilingCache != NULL)
{
DisposePtr((Ptr)spriteWorldP->tilingCache[0]); // Dispose the data
DisposePtr((Ptr)spriteWorldP->tilingCache); // Dispose the array of pointers
}
// Dispose the old changeTiles array of rects if necessary
if (spriteWorldP->changedTiles != NULL)
{
DisposePtr((Ptr)spriteWorldP->changedTiles);
}
numTilingCacheRows = spriteWorldP->backRect.bottom / spriteWorldP->tileHeight;
numTilingCacheCols = spriteWorldP->backRect.right / spriteWorldP->tileWidth;
spriteWorldP->numTilingCacheRows = numTilingCacheRows;
spriteWorldP->numTilingCacheCols = numTilingCacheCols;
// Allocate memory for changedTiles array of rects
spriteWorldP->changedTilesArraySize = (numTilingCacheRows+1) * (numTilingCacheCols+1);
arraySize = (Size)(numTilingCacheRows+1) * (numTilingCacheCols+1) * sizeof(Rect);
spriteWorldP->changedTiles = (Rect *)NewPtr(arraySize);
err = MemError();
if (err == noErr)
{
// Allocate the array of pointers for the tiling Cache
arraySize = (Size)numTilingCacheRows * sizeof(short*);
spriteWorldP->tilingCache = (short **)NewPtr(arraySize);
err = MemError();
if (err == noErr)
{
// Allocate memory for the actual data of the tiling Cache
arraySize = (Size)numTilingCacheRows * numTilingCacheCols * sizeof(short);
tilingCacheData = (short *)NewPtr(arraySize);
err = MemError();
// If there was an error, dispose what we already created earlier
if (err != noErr)
DisposePtr((Ptr)spriteWorldP->tilingCache);
}
if (err == noErr)
{
// Point each element of the tilingCache array to each row of the data
for (row = 0; row < numTilingCacheRows; row++)
spriteWorldP->tilingCache[row] = &tilingCacheData[(long)row * numTilingCacheCols];
// Set all elements to -1 (indicating that each tile needs to be drawn)
for (row = 0; row < numTilingCacheRows; row++)
for (col = 0; col < numTilingCacheCols; col++)
spriteWorldP->tilingCache[row][col] = -1;
}
}
if (err)
{
spriteWorldP->tilingCache = NULL;
spriteWorldP->changedTiles = NULL;
}
return err;
}
///--------------------------------------------------------------------------------------
// SWCreateTileMap
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWCreateTileMap(
TileMapStructPtr *tileMapStructPP,
short numTileMapRows,
short numTileMapCols)
{
TileMapStructPtr tileMapStructP;
Size arraySize;
OSErr err = noErr;
// Allocate memory for the TileMapStruct
tileMapStructP = (TileMapStructPtr)NewPtrClear(sizeof(TileMapStruct));
err = MemError();
if (err == noErr)
{
tileMapStructP->numRows = numTileMapRows;
tileMapStructP->numCols = numTileMapCols;
// Allocate the array of pointers that point to the data of the TileMap
arraySize = (Size)numTileMapRows * sizeof(short*);
tileMapStructP->arrayOfPointersH = NewHandle(arraySize);
err = MemError();
}
if (err == noErr)
{
// Allocate memory for the actual data of the TileMap
arraySize = ((Size)numTileMapRows * numTileMapCols + 2) * sizeof(short);
tileMapStructP->tileMapDataH = NewHandleClear(arraySize);
err = MemError();
}
if (err == noErr)
{
SWLockTileMap(tileMapStructP);
}
if (err != noErr)
{
// Dispose what we created
if (tileMapStructP != NULL)
{
DisposeHandle((Handle)tileMapStructP->arrayOfPointersH);
DisposeHandle((Handle)tileMapStructP->tileMapDataH);
DisposePtr((Ptr)tileMapStructP);
tileMapStructP = NULL;
}
}
// Return a pointer to the TileMapStruct in the tileMapStructPP variable
*tileMapStructPP = tileMapStructP;
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWDisposeTileMap
///--------------------------------------------------------------------------------------
SW_FUNC void SWDisposeTileMap(
TileMapStructPtr *tileMapStructPP)
{
TileMapStructPtr myTileMapStructP = *tileMapStructPP;
if (myTileMapStructP != NULL)
{
DisposeHandle((Handle)myTileMapStructP->arrayOfPointersH);
DisposeHandle((Handle)myTileMapStructP->tileMapDataH);
DisposePtr((Ptr)myTileMapStructP);
*tileMapStructPP = NULL;
}
}
///--------------------------------------------------------------------------------------
// SWLockTileMap
///--------------------------------------------------------------------------------------
SW_FUNC void SWLockTileMap(
TileMapStructPtr tileMapStructP)
{
short **arrayOfPointers;
short *tileMapDataP;
short row;
SW_ASSERT(tileMapStructP != NULL);
HLockHi(tileMapStructP->arrayOfPointersH);
HLockHi(tileMapStructP->tileMapDataH);
tileMapDataP = (short*)*tileMapStructP->tileMapDataH;
arrayOfPointers = (short**)*tileMapStructP->arrayOfPointersH;
// Point each element of the array of pointers to each row of the data
for (row = 0; row < tileMapStructP->numRows; row++)
{
arrayOfPointers[row] = &tileMapDataP[(long)row * tileMapStructP->numCols + 2];
}
tileMapStructP->tileMap = arrayOfPointers;
tileMapStructP->isLocked = true;
}
///--------------------------------------------------------------------------------------
// SWUnlockTileMap
///--------------------------------------------------------------------------------------
SW_FUNC void SWUnlockTileMap(
TileMapStructPtr tileMapStructP)
{
SW_ASSERT(tileMapStructP != NULL);
HUnlock(tileMapStructP->arrayOfPointersH);
HUnlock(tileMapStructP->tileMapDataH);
tileMapStructP->tileMap = NULL;
tileMapStructP->isLocked = false;
}
///--------------------------------------------------------------------------------------
// SWInstallTileMap
///--------------------------------------------------------------------------------------
SW_FUNC void SWInstallTileMap(
SpriteWorldPtr spriteWorldP,
TileMapStructPtr tileMapStructP,
short tileLayer)
{
short curLayer;
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(tileLayer < kNumTileLayers);
// Install the TileMap
spriteWorldP->tileLayerArray[tileLayer] = tileMapStructP;
spriteWorldP->lastActiveTileLayer = 0;
// Find the last active tile layer
for (curLayer = 0; curLayer < kNumTileLayers; curLayer++)
{
if (spriteWorldP->tileLayerArray[curLayer] != NULL)
spriteWorldP->lastActiveTileLayer = curLayer;
}
// Set the appropriate tileRectDrawProc
if (spriteWorldP->lastActiveTileLayer == 0)
{
spriteWorldP->tileRectDrawProc = SWDrawTilesInRect;
}
else
{
spriteWorldP->tileRectDrawProc = SWDrawTileLayersInRect;
}
}
///--------------------------------------------------------------------------------------
// SWLoadTileMap
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWLoadTileMap(
TileMapStructPtr *tileMapStructPP,
short resourceID)
{
TileMapStructPtr tileMapStructP;
short *tileMapData;
Size arraySize;
OSErr err = noErr;
// Allocate memory for the TileMapStruct
tileMapStructP = (TileMapStructPtr)NewPtrClear(sizeof(TileMapStruct));
err = MemError();
if (err == noErr)
{
// Load the resource
tileMapStructP->tileMapDataH = Get1Resource('TMAP', resourceID);
if (tileMapStructP->tileMapDataH == NULL)
err = ResError() ? ResError() : resNotFound;
}
if (err == noErr)
{
DetachResource((Handle)tileMapStructP->tileMapDataH);
tileMapData = (short*)*tileMapStructP->tileMapDataH;
tileMapStructP->numRows = tileMapData[0];
tileMapStructP->numCols = tileMapData[1];
// Allocate the array of pointers that point to the data of the TileMap
arraySize = (Size)tileMapStructP->numRows * sizeof(short*);
tileMapStructP->arrayOfPointersH = NewHandle(arraySize);
err = MemError();
}
if (err == noErr)
{
SWLockTileMap(tileMapStructP);
}
if (err != noErr)
{
// Dispose what we created
if (tileMapStructP != NULL)
{
DisposeHandle((Handle)tileMapStructP->arrayOfPointersH);
DisposeHandle((Handle)tileMapStructP->tileMapDataH);
DisposePtr((Ptr)tileMapStructP);
tileMapStructP = NULL;
}
}
// Return a pointer to the TileMapStruct in the tileMapStructPP variable
*tileMapStructPP = tileMapStructP;
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWSaveTileMap
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWSaveTileMap(
TileMapStructPtr tileMapStructP,
short destResID)
{
short tempResID;
short *tileMapData;
Handle tempTileMapH;
OSErr err = noErr;
// Make sure there is a tileMap to save
if (tileMapStructP == NULL)
err = kNullTileMapErr;
if (err == noErr)
{
// Save numRows & numCols in the first two elements of the tileMapData
tileMapData = (short*)*tileMapStructP->tileMapDataH;
tileMapData[0] = tileMapStructP->numRows;
tileMapData[1] = tileMapStructP->numCols;
// Add the TMAP resource, assigning it a temporary, unused ID
tempResID = Unique1ID('TMAP');
AddResource(tileMapStructP->tileMapDataH, 'TMAP', tempResID, "\p");
err = ResError();
// This call may fix a sporadic problem I've had occasionally that occurs when
// opening one level, then opening another one, making it larger, saving it, and
// re-opening it, all without quitting. Unfortunately, I couldn't reproduce it to
// test this fix.
if (err == noErr)
WriteResource(tileMapStructP->tileMapDataH);
}
if (err == noErr)
{
do // Remove any old TMAP resources with destResID
{
SetResLoad(false);
tempTileMapH = Get1Resource('TMAP', destResID);
SetResLoad(true);
if (tempTileMapH != NULL)
{
RemoveResource(tempTileMapH);
// This could happen if the resource is protected
err = ResError();
SW_ASSERT(ResError() == noErr);
}
else if (ResError() != resNotFound && ResError() != noErr)
{
// This could happen if Get1Resource didn't have a free master
// pointer and there isn't enough memory to allocate a new block
err = ResError();
}
} while (tempTileMapH != NULL);
// Change the resource ID to the proper one
SetResInfo(tileMapStructP->tileMapDataH, destResID, "\p");
// Update file, writing new resource and deleting old one in one step.
UpdateResFile( CurResFile() );
DetachResource(tileMapStructP->tileMapDataH);
}
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWResizeTileMap
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWResizeTileMap(
TileMapStructPtr oldTileMapStructP,
short numNewTileMapRows,
short numNewTileMapCols)
{
TileMapStruct tempTileMapStruct;
TileMapStructPtr newTileMapStructP;
short row, numRowsToCopy, numColsToCopy;
Size arraySize;
OSErr err = noErr;
SW_ASSERT(numNewTileMapRows > 0 && numNewTileMapCols > 0);
if (oldTileMapStructP == NULL)
err = kNullTileMapErr;
if (err == noErr)
{
// Don't do anything if the TileMap is already the requested size.
if (oldTileMapStructP->numRows == numNewTileMapRows &&
oldTileMapStructP->numCols == numNewTileMapCols)
{
return noErr;
}
// Create the new TileMap
err = SWCreateTileMap(&newTileMapStructP, numNewTileMapRows, numNewTileMapCols);
}
if (err == noErr)
{
if (oldTileMapStructP->isLocked == false)
SWLockTileMap(oldTileMapStructP);
numRowsToCopy = SW_MIN(oldTileMapStructP->numRows, numNewTileMapRows);
numColsToCopy = SW_MIN(oldTileMapStructP->numCols, numNewTileMapCols);
// Copy the data from the old TileMap to the new TileMap
arraySize = (Size)numColsToCopy * sizeof(short);
for (row = 0; row < numRowsToCopy; row++)
{
BlockMoveData(&oldTileMapStructP->tileMap[row][0],
&newTileMapStructP->tileMap[row][0], arraySize);
}
// Swap contents of the new and old TileMapStructs
tempTileMapStruct = *oldTileMapStructP;
*oldTileMapStructP = *newTileMapStructP;
*newTileMapStructP = tempTileMapStruct;
// Dispose the newTileMapStruct, which now contains the old TileMap
SWDisposeTileMap(&newTileMapStructP);
}
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWLoadTileFromCicnResource
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWLoadTileFromCicnResource(
SpriteWorldPtr spriteWorldP,
short tileID,
short cicnID,
MaskType maskType)
{
OSErr err;
GWorldPtr saveGWorld;
GDHandle saveGDH;
FramePtr newFrameP;
CIconHandle cIconH;
Rect frameRect;
Boolean maskIsSolid = false;
SW_ASSERT(spriteWorldP != NULL);
err = noErr;
newFrameP = NULL;
if ( !spriteWorldP->tilingIsInitialized )
{
err = kTilingNotInitialized;
}
else if ( tileID < 0 || tileID >= spriteWorldP->maxNumTiles)
{
err = kOutOfRangeErr;
}
if (maskType == kSolidMask)
{
maskIsSolid = true;
maskType = kNoMask;
}
if ( err == noErr )
{
err = SWCreateFrameFromCicnResource(spriteWorldP, &newFrameP, cicnID, maskType);
if ( err == noErr )
{
newFrameP->tileMaskIsSolid = maskIsSolid;
// "Fix" the size if the CICN is larger than the tiles are
if (newFrameP->frameRect.right > spriteWorldP->tileWidth)
newFrameP->frameRect.right = spriteWorldP->tileWidth;
if (newFrameP->frameRect.bottom > spriteWorldP->tileHeight)
newFrameP->frameRect.bottom = spriteWorldP->tileHeight;
cIconH = GetCIcon( cicnID );
if (cIconH != NULL)
{
GetGWorld(&saveGWorld, &saveGDH);
HLock((Handle)cIconH);
frameRect = ((**cIconH).iconPMap.bounds);
(**cIconH).iconPMap.baseAddr = *(**cIconH).iconData;
(**cIconH).iconBMap.baseAddr = (Ptr)&((**cIconH).iconMaskData ) +
((**cIconH).iconMask.rowBytes * (frameRect.bottom-frameRect.top));
(void)LockPixels( GetGWorldPixMap( newFrameP->framePort ) );
SetGWorld( newFrameP->framePort, NULL );
if ( spriteWorldP->pixelDepth > 1 )
{
CopyBits( (BitMap*)(&((**cIconH).iconPMap)),
(BitMap*)*GetGWorldPixMap( newFrameP->framePort ),
&frameRect, &newFrameP->frameRect, srcCopy, nil);
}
else
{
CopyBits( (BitMap*)(&((**cIconH).iconBMap)),
(BitMap*)*GetGWorldPixMap( newFrameP->framePort ),
&frameRect,
&newFrameP->frameRect, srcCopy, nil);
}
UnlockPixels( GetGWorldPixMap( newFrameP->framePort ) );
DisposeCIcon( cIconH );
SetGWorld( saveGWorld, saveGDH );
}
else
{
err = MemError();
}
}
if ( err == noErr )
{
// Are we replacing an old tile?
if (spriteWorldP->tileFrameArray[tileID] != NULL)
{
SWDisposeTile( spriteWorldP, tileID );
}
spriteWorldP->tileFrameArray[tileID] = newFrameP;
newFrameP->useCount++;
}
}
if ( err != noErr && newFrameP != NULL )
{
SWDisposeFrame(&newFrameP);
}
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWLoadTilesFromPictResource
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWLoadTilesFromPictResource(
SpriteWorldPtr spriteWorldP,
short startTileID,
short endTileID,
short pictResID,
short maskResID,
MaskType maskType,
short horizBorderWidth,
short vertBorderHeight)
{
OSErr err;
short tileIndex;
FramePtr newFrameP;
GWorldPtr tempPictGWorldP,
tempMaskGWorldP,
tempTempMaskGWorldP,
currentGWorld;
GDHandle currentGDH;
Rect currentTileRect;
short horizOffset,
vertOffset;
Boolean allTilesDone,
maskIsSolid = false;
SW_ASSERT(spriteWorldP != NULL);
err = noErr;
newFrameP = NULL;
tempPictGWorldP = NULL;
tempMaskGWorldP = NULL;
tempTempMaskGWorldP = NULL;
if ( !spriteWorldP->tilingIsInitialized )
{
err = kTilingNotInitialized;
}
else if ( startTileID < 0 || endTileID >= spriteWorldP->maxNumTiles)
{
err = kOutOfRangeErr;
}
if (maskType == kSolidMask)
{
maskIsSolid = true;
maskType = kNoMask;
}
if ( err == noErr )
{
err = SWCreateGWorldFromPictResource( spriteWorldP, &tempPictGWorldP, pictResID );
if ( err == noErr && maskType != kNoMask )
err = SWCreateGWorldFromPictResource( spriteWorldP, &tempMaskGWorldP, maskResID );
if (err == noErr)
{
if ( pictResID == maskResID && tempMaskGWorldP != NULL )
{
err = SWBlackenGWorld( tempMaskGWorldP );
}
}
if ( err == noErr )
{
err = SWCreateFrameFromGWorldAndRectStart( &tempTempMaskGWorldP,
spriteWorldP->tileWidth, spriteWorldP->tileHeight, maskType );
}
if ( err == noErr )
{
SetRect( ¤tTileRect, 0, 0,
spriteWorldP->tileWidth, spriteWorldP->tileHeight );
tileIndex = startTileID;
horizOffset = spriteWorldP->tileWidth + horizBorderWidth;
vertOffset = spriteWorldP->tileHeight + vertBorderHeight;
allTilesDone = false;
while( !allTilesDone && err == noErr )
{
err = SWCreateFrameFromGWorldAndRectPartial( &newFrameP, tempPictGWorldP,
tempMaskGWorldP, tempTempMaskGWorldP, ¤tTileRect, maskType);
if ( newFrameP != NULL && err == noErr )
{
newFrameP->tileMaskIsSolid = maskIsSolid;
// Are we replacing an old tile?
if (spriteWorldP->tileFrameArray[tileIndex] != NULL)
{
SWDisposeTile( spriteWorldP, tileIndex );
}
spriteWorldP->tileFrameArray[tileIndex] = newFrameP;
newFrameP->useCount++;
tileIndex++;
if (tileIndex > endTileID )
{
allTilesDone = true;
}
else
{
if (tileIndex >= spriteWorldP->maxNumTiles)
{
err = kOutOfRangeErr;
}
currentTileRect.left += horizOffset;
currentTileRect.right += horizOffset;
if ( currentTileRect.right > tempPictGWorldP->portRect.right )
{
currentTileRect.left = 0;
currentTileRect.right = spriteWorldP->tileWidth;
currentTileRect.top += vertOffset;
currentTileRect.bottom += vertOffset;
if ( currentTileRect.bottom > tempPictGWorldP->portRect.bottom )
{
err = kOutOfRangeErr;
}
}
}
}
}
if (err == noErr)
{
// make a pixel mask
if ((maskType & kPixelMask) != 0)
{
if (spriteWorldP->pixelDepth <= 8)
{
GetGWorld( ¤tGWorld, ¤tGDH );
(void)LockPixels( GetGWorldPixMap(tempMaskGWorldP) );
SetGWorld(tempMaskGWorldP, nil);
InvertRect(&tempMaskGWorldP->portRect);
SetGWorld( currentGWorld, currentGDH );
UnlockPixels( GetGWorldPixMap(tempMaskGWorldP) );
}
}
else if (tempMaskGWorldP != NULL)
{
// If no pixel Mask wanted, dispose
// of the GWorld we used to make region
DisposeGWorld( tempMaskGWorldP );
}
}
}
SWCreateFrameFromGWorldAndRectFinish( tempTempMaskGWorldP );
}
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWDisposeTile
///--------------------------------------------------------------------------------------
SW_FUNC void SWDisposeTile(
SpriteWorldPtr spriteWorldP,
short tileID)
{
short tileIndex;
Boolean gWorldStillInUse;
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(spriteWorldP->tilingIsInitialized);
// see if any other tile is using this tile's GWorld
gWorldStillInUse = false;
tileIndex = spriteWorldP->maxNumTiles;
while ( tileIndex-- )
{
if ( tileIndex != tileID )
{
if ( spriteWorldP->tileFrameArray[tileIndex] != NULL &&
((spriteWorldP->tileFrameArray[tileIndex])->framePort ==
(spriteWorldP->tileFrameArray[tileID])->framePort) )
{
gWorldStillInUse = true;
}
}
}
// set flag that tells SWDisposeFrame whether to dispose of GWorld
(spriteWorldP->tileFrameArray[tileID])->sharesGWorld = gWorldStillInUse;
(void)SWDisposeFrame( &spriteWorldP->tileFrameArray[tileID] );
spriteWorldP->tileFrameArray[tileID] = NULL;
}
///--------------------------------------------------------------------------------------
// SWLockTiles
///--------------------------------------------------------------------------------------
SW_FUNC void SWLockTiles(
SpriteWorldPtr spriteWorldP)
{
short tileIndex;
SW_ASSERT(spriteWorldP != NULL);
// Tiling might not be initialized if this was called from SWLockSpriteWorld
if (spriteWorldP->tilingIsInitialized)
{
for (tileIndex = 0; tileIndex < spriteWorldP->maxNumTiles; tileIndex++)
{
if (spriteWorldP->tileFrameArray[tileIndex] != NULL)
SWLockFrame(spriteWorldP->tileFrameArray[tileIndex]);
}
}
}
///--------------------------------------------------------------------------------------
// SWUnlockTiles
///--------------------------------------------------------------------------------------
SW_FUNC void SWUnlockTiles(
SpriteWorldPtr spriteWorldP)
{
short tileIndex;
SW_ASSERT(spriteWorldP != NULL);
// Tiling might not be initialized if this was called from SWLockSpriteWorld
if (spriteWorldP->tilingIsInitialized)
{
for (tileIndex = 0; tileIndex < spriteWorldP->maxNumTiles; tileIndex++)
{
if (spriteWorldP->tileFrameArray[tileIndex] != NULL)
SWUnlockFrame(spriteWorldP->tileFrameArray[tileIndex]);
}
}
}
///--------------------------------------------------------------------------------------
// SWCreateExtraBackFrame
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWCreateExtraBackFrame(
SpriteWorldPtr spriteWorldP,
Rect *frameRect)
{
OSErr err = noErr;
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(frameRect->right > frameRect->left && frameRect->bottom > frameRect->top);
if (spriteWorldP->extraBackFrameP != NULL)
{
SWDisposeFrame(&spriteWorldP->extraBackFrameP);
}
// Make sure rect starts at 0,0
OffsetRect(frameRect, -frameRect->left, -frameRect->top);
err = SWCreateFrame(spriteWorldP->mainSWGDH, &spriteWorldP->extraBackFrameP,
frameRect, spriteWorldP->pixelDepth);
return err;
}
///--------------------------------------------------------------------------------------
// SWDisposeExtraBackFrame
///--------------------------------------------------------------------------------------
SW_FUNC void SWDisposeExtraBackFrame(
SpriteWorldPtr spriteWorldP)
{
SW_ASSERT(spriteWorldP != NULL);
SWDisposeFrame(&spriteWorldP->extraBackFrameP);
spriteWorldP->extraBackFrameP = NULL;
}
///--------------------------------------------------------------------------------------
// SWSetPortToExtraBackFrame
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWSetPortToExtraBackFrame(
SpriteWorldPtr spriteWorldP)
{
OSErr err = noErr;
SW_ASSERT(spriteWorldP != NULL);
if ( spriteWorldP->extraBackFrameP == NULL )
err = kNilFrameErr;
else if ( spriteWorldP->extraBackFrameP->isFrameLocked == false)
err = kNotLockedErr;
if (err == noErr)
SetGWorld(spriteWorldP->extraBackFrameP->framePort, nil);
SWSetStickyIfError( err );
return err;
}
///--------------------------------------------------------------------------------------
// SWSetTilingOn
///--------------------------------------------------------------------------------------
SW_FUNC void SWSetTilingOn(
SpriteWorldPtr spriteWorldP,
Boolean tilingIsOn)
{
SW_ASSERT(spriteWorldP != NULL);
spriteWorldP->tilingIsOn = tilingIsOn;
}
///--------------------------------------------------------------------------------------
// SWChangeTileSize
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWChangeTileSize(
SpriteWorldPtr spriteWorldP,
short tileHeight,
short tileWidth)
{
OSErr err;
SW_ASSERT(spriteWorldP != NULL);
spriteWorldP->tileHeight = tileHeight;
spriteWorldP->tileWidth = tileWidth;
// Dispose and rebuild the tiling cache, based on the new tile width & height
err = SWInitTilingCache(spriteWorldP);
return err;
}
///--------------------------------------------------------------------------------------
// SWSetSpriteLayerUnderTileLayer
///--------------------------------------------------------------------------------------
SW_FUNC void SWSetSpriteLayerUnderTileLayer(
SpriteLayerPtr spriteLayerP,
short tileLayer)
{
SW_ASSERT(spriteLayerP != NULL);
spriteLayerP->tileLayer = tileLayer;
}
///--------------------------------------------------------------------------------------
// SWSetTileMaskDrawProc
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWSetTileMaskDrawProc(
SpriteWorldPtr spriteWorldP,
DrawProcPtr drawProc)
{
OSErr err = noErr;
SW_ASSERT(spriteWorldP != NULL);
if (drawProc == BlitPixie8BitPartialMaskDrawProc ||
drawProc == BP8BitInterlacedPartialMaskDrawProc)
{
err = kBadParameterErr;
}
else if (spriteWorldP->pixelDepth != 8)
{
if (drawProc == BlitPixie8BitMaskDrawProc || drawProc == BP8BitInterlacedMaskDrawProc)
{
err = kWrongDepthErr;
}
}
if ( err == noErr )
{
spriteWorldP->tileMaskDrawProc = drawProc;
}
SWSetStickyIfError( err );
return err;
}
///--------------------------------------------------------------------------------------
// SWSetPartialMaskDrawProc
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWSetPartialMaskDrawProc(
SpriteWorldPtr spriteWorldP,
DrawProcPtr drawProc)
{
OSErr err = noErr;
SW_ASSERT(spriteWorldP != NULL);
if (spriteWorldP->pixelDepth != 8)
{
if (drawProc == BlitPixie8BitPartialMaskDrawProc ||
drawProc == BP8BitInterlacedPartialMaskDrawProc)
{
err = kWrongDepthErr;
}
}
if ( err == noErr )
{
spriteWorldP->partialMaskDrawProc = drawProc;
}
SWSetStickyIfError( err );
return err;
}
///--------------------------------------------------------------------------------------
// SWSetTileChangeProc
///--------------------------------------------------------------------------------------
SW_FUNC void SWSetTileChangeProc(
SpriteWorldPtr spriteWorldP,
TileChangeProcPtr tileChangeProc)
{
SW_ASSERT(spriteWorldP != NULL);
spriteWorldP->tileChangeProc = tileChangeProc;
}
#pragma mark -
///--------------------------------------------------------------------------------------
// SWDrawTilesInBackground
///--------------------------------------------------------------------------------------
SW_FUNC OSErr SWDrawTilesInBackground(
SpriteWorldPtr spriteWorldP)
{
GWorldPtr holdGWorld;
GDHandle holdGDH;
OSErr err = noErr;
SW_ASSERT(spriteWorldP != NULL);
GetGWorld( &holdGWorld, &holdGDH );
if ( !spriteWorldP->tilingIsInitialized )
err = kTilingNotInitialized;
if (err == noErr)
{
(*spriteWorldP->tileRectDrawProc)(spriteWorldP, &spriteWorldP->visScrollRect, true);
}
SetGWorld( holdGWorld, holdGDH );
SWSetStickyIfError(err);
return err;
}
///--------------------------------------------------------------------------------------
// SWDrawTile - sets value in tileMap and draws tile if visible in visScrollRect.
// The main core of this function is very similar to the inner loop of
// SWTileDrawLayersInRect, except that this calls SWAddChangedRect, has code to set the
// tiling cache, and makes sure the tile is visible on screen before drawing it.
// Oh, and SWDrawTile *sets* the tileID in the tileMap, instead of getting it. :-)
///--------------------------------------------------------------------------------------
SW_FUNC void SWDrawTile(
SpriteWorldPtr spriteWorldP,
short dstTileLayer,
short tileRow,
short tileCol,
short tileID)
{
short row, col, offscreenTileRow, offscreenTileCol, tileLayer;
Rect* visScrollRectP = &spriteWorldP->visScrollRect;
Rect* backRectP = &spriteWorldP->backRect;
short tileHeight = spriteWorldP->tileHeight;
short tileWidth = spriteWorldP->tileWidth;
Rect srcRect, dstRect;
FramePtr tileFrameP;
Boolean tileClipped, tileHasMask;
// We must have a TileMap installed in the dstTileLayer to draw in it!
if (spriteWorldP->tileLayerArray[dstTileLayer] == NULL)
return;
// Check SpriteWorldRec
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(spriteWorldP->tilingIsInitialized);
SW_ASSERT(spriteWorldP->tileMaskDrawProc != NULL);
SW_ASSERT(spriteWorldP->offscreenDrawProc != NULL);
SW_ASSERT(spriteWorldP->backFrameP->isFrameLocked);
SW_ASSERT(spriteWorldP->workFrameP->isFrameLocked);
SW_ASSERT(spriteWorldP->tileLayerArray[dstTileLayer] != NULL);
SW_ASSERT(spriteWorldP->tileLayerArray[dstTileLayer]->isLocked);
// Check parameters
SW_ASSERT(dstTileLayer <= spriteWorldP->lastActiveTileLayer);
SW_ASSERT(tileRow < spriteWorldP->tileLayerArray[dstTileLayer]->numRows);
SW_ASSERT(tileCol < spriteWorldP->tileLayerArray[dstTileLayer]->numCols);
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
SetGWorld(spriteWorldP->backFrameP->framePort, nil);
// Note: we can not return if the tileID is what is already in the TileMap,
// since tile animation would then not work.
// Store the new tileID in the TileMap of the dstTileLayer
spriteWorldP->tileLayerArray[dstTileLayer]->tileMap[tileRow][tileCol] = tileID;
row = tileRow * tileHeight;
col = tileCol * tileWidth;
dstRect.bottom = row + tileHeight;
dstRect.right = col + tileWidth;
// Clip tile dstRect with visScrollRect //
tileClipped = false;
if (row < visScrollRectP->top)
{
dstRect.top = visScrollRectP->top;
tileClipped = true;
}
else
dstRect.top = row;
if (col < visScrollRectP->left)
{
dstRect.left = visScrollRectP->left;
tileClipped = true;
}
else
dstRect.left = col;
if (dstRect.bottom > visScrollRectP->bottom)
{
dstRect.bottom = visScrollRectP->bottom;
tileClipped = true;
}
if (dstRect.right > visScrollRectP->right)
{
dstRect.right = visScrollRectP->right;
tileClipped = true;
}
// Draw tile if visible on screen (in visScrollRect)
if (dstRect.left < dstRect.right && dstRect.top < dstRect.bottom)
{
// Save rect as having been changed
SWAddChangedRect(spriteWorldP, &dstRect);
// Now get the tileID of this row and col in tileLayer 0
if (spriteWorldP->tileLayerArray[0] == NULL)
tileID = -1;
else
tileID = spriteWorldP->tileLayerArray[0]->tileMap[tileRow][tileCol];
// Now we redraw all tiles in this location
if (tileID >= 0)
{
tileFrameP = spriteWorldP->tileFrameArray[spriteWorldP->curTileImage[tileID]];
SW_ASSERT(tileFrameP != NULL);
SW_ASSERT(tileFrameP->isFrameLocked);
// Determine whether the tile has a mask with background showing through
tileHasMask = (tileFrameP->maskPort != NULL || tileFrameP->maskRgn != NULL);
}
else
tileHasMask = false;
// Copy a piece from the extraBackFrameP only if there is no tile, or the
// tile has a mask with background showing through the unmasked part.
if ( (tileID == -1 || tileHasMask) && spriteWorldP->extraBackFrameP != NULL)
{
// There is no tile in this spot, or there is a masked tile and
// there is an extraBackFrameP, so copy a piece from extraBackFrameP
// Note: the function below wraps the dstRect for us, and leaves it
// that way when it returns, so we don't have to wrap it ourselves.
SWWrapRectFromExtraBackFrame(spriteWorldP, &dstRect);
}
else
{
// Make the tile's dest rect local to the offscreen area
dstRect.top -= spriteWorldP->vertScrollRectOffset;
dstRect.bottom -= spriteWorldP->vertScrollRectOffset;
dstRect.left -= spriteWorldP->horizScrollRectOffset;
dstRect.right -= spriteWorldP->horizScrollRectOffset;
// Wrap tile to top or bottom of offscreen area
if (dstRect.bottom > backRectP->bottom)
{
dstRect.top -= backRectP->bottom;
dstRect.bottom -= backRectP->bottom;
}
else if (dstRect.top < backRectP->top)
{
dstRect.top += backRectP->bottom;
dstRect.bottom += backRectP->bottom;
}
// Wrap tile to left or right side of offscreen area
if (dstRect.right > backRectP->right)
{
dstRect.left -= backRectP->right;
dstRect.right -= backRectP->right;
}
else if (dstRect.left < backRectP->left)
{
dstRect.left += backRectP->right;
dstRect.right += backRectP->right;
}
}
if (tileID >= 0)
{
srcRect = tileFrameP->frameRect;
// Clip new srcRect
if (row < visScrollRectP->top)
srcRect.top += visScrollRectP->top - row;
if (col < visScrollRectP->left)
srcRect.left += visScrollRectP->left - col;
if (row + tileHeight > visScrollRectP->bottom)
srcRect.bottom -= row + tileHeight - visScrollRectP->bottom;
if (col + tileWidth > visScrollRectP->right)
srcRect.right -= col + tileWidth - visScrollRectP->right;
if (tileHasMask && spriteWorldP->extraBackFrameP != NULL)
{
// The tile has a mask, with background showing through
(*spriteWorldP->tileMaskDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
else
{
// The entire tile should be drawn
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
}
// Draw tiles in higher layers
for (tileLayer = 1; tileLayer <= spriteWorldP->lastActiveTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
continue;
SW_ASSERT(tileRow < spriteWorldP->tileLayerArray[tileLayer]->numRows);
SW_ASSERT(tileCol < spriteWorldP->tileLayerArray[tileLayer]->numCols);
tileID = spriteWorldP->tileLayerArray[tileLayer]->tileMap[tileRow][tileCol];
if (tileID < 0)
continue;
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
tileFrameP = spriteWorldP->tileFrameArray[spriteWorldP->curTileImage[tileID]];
SW_ASSERT(tileFrameP != NULL);
SW_ASSERT(tileFrameP->isFrameLocked);
srcRect = tileFrameP->frameRect;
// Clip new srcRect
if (row < visScrollRectP->top)
srcRect.top += visScrollRectP->top - row;
if (col < visScrollRectP->left)
srcRect.left += visScrollRectP->left - col;
if (row + tileHeight > visScrollRectP->bottom)
srcRect.bottom -= row + tileHeight - visScrollRectP->bottom;
if (col + tileWidth > visScrollRectP->right)
srcRect.right -= col + tileWidth - visScrollRectP->right;
// Draw the tile
if (tileFrameP->maskPort == NULL && tileFrameP->maskRgn == NULL)
{
// Tile has no mask
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
else
{
// Tile has a mask
(*spriteWorldP->tileMaskDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
}
// Copy tiles from back area to work area
SetGWorld(spriteWorldP->workFrameP->framePort, nil);
(*spriteWorldP->offscreenDrawProc)(spriteWorldP->backFrameP,
spriteWorldP->workFrameP, &dstRect, &dstRect);
// Update tiling cache info only if only one tile layer is used
if (spriteWorldP->lastActiveTileLayer == 0)
{
offscreenTileRow = dstRect.top / spriteWorldP->tileHeight;
offscreenTileCol = dstRect.left / spriteWorldP->tileWidth;
// Set new tile value in tilingCache
if (tileClipped || tileID < 0)
{
spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] = -1;
}
else
{
spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] =
spriteWorldP->curTileImage[tileID];
}
}
}
}
///--------------------------------------------------------------------------------------
// SWDrawTilesInRect - draw only one layer of tiles in updateRect of backFrame. Note:
// The only reason we use this instead of SWDrawTilesLayersInRect is because with this,
// we can use the tiling cache.
///--------------------------------------------------------------------------------------
SW_FUNC void SWDrawTilesInRect(
SpriteWorldPtr spriteWorldP,
Rect* updateRectP,
Boolean optimizingOn)
{
short row, col, tileRow, tileCol, tileID;
short startRow, startCol, stopRow, stopCol;
short offscreenTileRow, offscreenTileCol;
Rect srcRect, dstRect;
FramePtr tileFrameP;
Boolean tileClipped, tileHasMask;
Rect* visScrollRectP = &spriteWorldP->visScrollRect;
Rect* backRectP = &spriteWorldP->backRect;
Rect updateRect = *updateRectP;
short tileWidth = spriteWorldP->tileWidth;
short tileHeight = spriteWorldP->tileHeight;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(spriteWorldP->tileMaskDrawProc != NULL);
SW_ASSERT(spriteWorldP->offscreenDrawProc != NULL);
SW_ASSERT(spriteWorldP->backFrameP->isFrameLocked);
SW_ASSERT(spriteWorldP->tileLayerArray[0] != NULL);
SW_ASSERT(spriteWorldP->tileLayerArray[0]->isLocked);
SetGWorld(spriteWorldP->backFrameP->framePort, nil);
// Convert pixel row and col into tile row and col
startRow = updateRect.top / tileHeight;
startCol = updateRect.left / tileWidth;
stopRow = (updateRect.bottom-1) / tileHeight;
stopCol = (updateRect.right-1) / tileWidth;
row = startRow * tileHeight;
for (tileRow = startRow; tileRow <= stopRow; tileRow++, row += tileHeight)
{
col = startCol * tileWidth;
for (tileCol = startCol; tileCol <= stopCol; tileCol++, col += tileWidth)
{
if (spriteWorldP->tileLayerArray[0] != NULL)
{
SW_ASSERT(tileRow < spriteWorldP->tileLayerArray[0]->numRows);
SW_ASSERT(tileCol < spriteWorldP->tileLayerArray[0]->numCols);
}
dstRect.bottom = row + tileHeight;
dstRect.right = col + tileWidth;
// Is tile completely visible on screen?
if (row >= visScrollRectP->top && col >= visScrollRectP->left &&
dstRect.bottom <= visScrollRectP->bottom &&
dstRect.right <= visScrollRectP->right)
{
tileClipped = false;
}
else
{
tileClipped = true;
}
// Clip tile dstRect with updateRect //
if (row < updateRect.top)
dstRect.top = updateRect.top;
else
dstRect.top = row;
if (col < updateRect.left)
dstRect.left = updateRect.left;
else
dstRect.left = col;
if (dstRect.bottom > updateRect.bottom)
dstRect.bottom = updateRect.bottom;
if (dstRect.right > updateRect.right)
dstRect.right = updateRect.right;
if (spriteWorldP->tileLayerArray[0] == NULL)
tileID = -1;
else
tileID = spriteWorldP->tileLayerArray[0]->tileMap[tileRow][tileCol];
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
if (tileID >= 0)
{
tileFrameP = spriteWorldP->tileFrameArray[spriteWorldP->curTileImage[tileID]];
SW_ASSERT(tileFrameP != NULL);
SW_ASSERT(tileFrameP->isFrameLocked);
// Determine whether the tile has a mask with background showing through
tileHasMask = (tileFrameP->maskPort != NULL || tileFrameP->maskRgn != NULL);
}
else
tileHasMask = false;
// Copy a piece from the extraBackFrameP only if there is no tile, or the
// tile has a mask with background showing through the unmasked part.
if ( (tileID == -1 || tileHasMask) && spriteWorldP->extraBackFrameP != NULL)
{
// There is no tile in this spot, or there is a masked tile and
// there is an extraBackFrameP, so copy a piece from extraBackFrameP
// Note: the function below wraps the dstRect for us, and leaves it
// that way when it returns, so we don't have to wrap it ourselves.
SWWrapRectFromExtraBackFrame(spriteWorldP, &dstRect);
}
else
{
// Make the tile's dest rect local to the offscreen area
dstRect.top -= spriteWorldP->vertScrollRectOffset;
dstRect.bottom -= spriteWorldP->vertScrollRectOffset;
dstRect.left -= spriteWorldP->horizScrollRectOffset;
dstRect.right -= spriteWorldP->horizScrollRectOffset;
// Wrap tile to top or bottom of offscreen area
if (dstRect.bottom > backRectP->bottom)
{
dstRect.top -= backRectP->bottom;
dstRect.bottom -= backRectP->bottom;
}
else if (dstRect.top < backRectP->top)
{
dstRect.top += backRectP->bottom;
dstRect.bottom += backRectP->bottom;
}
// Wrap tile to left or right side of offscreen area
if (dstRect.right > backRectP->right)
{
dstRect.left -= backRectP->right;
dstRect.right -= backRectP->right;
}
else if (dstRect.left < backRectP->left)
{
dstRect.left += backRectP->right;
dstRect.right += backRectP->right;
}
}
offscreenTileRow = dstRect.top / tileHeight;
offscreenTileCol = dstRect.left / tileWidth;
if (tileID >= 0)
{
srcRect = tileFrameP->frameRect;
// Clip new srcRect
if (row < updateRect.top)
srcRect.top += updateRect.top - row;
if (col < updateRect.left)
srcRect.left += updateRect.left - col;
if (row + tileHeight > updateRect.bottom)
srcRect.bottom -= row + tileHeight - updateRect.bottom;
if (col + tileWidth > updateRect.right)
srcRect.right -= col + tileWidth - updateRect.right;
if (tileHasMask && spriteWorldP->extraBackFrameP != NULL)
{
// The tile has a mask, with background showing through
(*spriteWorldP->tileMaskDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
// Since the background is showing here, set this cache spot to -1
spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] = -1;
}
else
{
// Save time by not drawing tile if already the same
if (spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] !=
spriteWorldP->curTileImage[tileID] || !optimizingOn)
{
// The entire tile should be drawn
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
// Set new tile value in tilingCache
if (tileClipped)
{
spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] = -1;
}
else
{
spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] =
spriteWorldP->curTileImage[tileID];
}
}
}
}
else
{
// Since no tile is here, this spot in the tiling Cache should be set to -1
spriteWorldP->tilingCache[offscreenTileRow][offscreenTileCol] = -1;
// Cause assertion failure if tileID is -1 and there is no extraBackFrameP
SW_ASSERT(spriteWorldP->extraBackFrameP != NULL);
}
}
}
}
///--------------------------------------------------------------------------------------
// SWDrawTileLayersInRect - draws all tile layers in updateRect of backFrame
///--------------------------------------------------------------------------------------
SW_FUNC void SWDrawTileLayersInRect(
SpriteWorldPtr spriteWorldP,
Rect* updateRectP,
Boolean optimizingOn)
{
#pragma unused(optimizingOn) // This is used in our sister function, SWDrawTilesInRect
short row, col, tileRow, tileCol, tileID;
short startRow, startCol, stopRow, stopCol;
short tileLayer, startPixelCol;
Rect srcRect, dstRect;
FramePtr tileFrameP;
Rect* visScrollRectP = &spriteWorldP->visScrollRect;
Rect* backRectP = &spriteWorldP->backRect;
Rect updateRect = *updateRectP;
short tileWidth = spriteWorldP->tileWidth;
short tileHeight = spriteWorldP->tileHeight;
Boolean tileHasMask;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(spriteWorldP->tileMaskDrawProc != NULL);
SW_ASSERT(spriteWorldP->offscreenDrawProc != NULL);
SW_ASSERT(spriteWorldP->backFrameP->isFrameLocked);
SetGWorld(spriteWorldP->backFrameP->framePort, nil);
// Convert pixel row and col into tile row and col
startRow = updateRect.top / tileHeight;
startCol = updateRect.left / tileWidth;
stopRow = (updateRect.bottom-1) / tileHeight;
stopCol = (updateRect.right-1) / tileWidth;
startPixelCol = startCol * tileWidth;
row = startRow * tileHeight;
for (tileRow = startRow; tileRow <= stopRow; tileRow++, row += tileHeight)
{
col = startPixelCol;
for (tileCol = startCol; tileCol <= stopCol; tileCol++, col += tileWidth)
{
if (spriteWorldP->tileLayerArray[0] != NULL)
{
SW_ASSERT(spriteWorldP->tileLayerArray[0]->isLocked);
SW_ASSERT(tileRow < spriteWorldP->tileLayerArray[0]->numRows);
SW_ASSERT(tileCol < spriteWorldP->tileLayerArray[0]->numCols);
}
dstRect.bottom = row + tileHeight;
dstRect.right = col + tileWidth;
// Clip tile dstRect with updateRect //
if (row < updateRect.top)
dstRect.top = updateRect.top;
else
dstRect.top = row;
if (col < updateRect.left)
dstRect.left = updateRect.left;
else
dstRect.left = col;
if (dstRect.bottom > updateRect.bottom)
dstRect.bottom = updateRect.bottom;
if (dstRect.right > updateRect.right)
dstRect.right = updateRect.right;
if (spriteWorldP->tileLayerArray[0] == NULL)
tileID = -1;
else
tileID = spriteWorldP->tileLayerArray[0]->tileMap[tileRow][tileCol];
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
if (tileID >= 0)
{
tileFrameP = spriteWorldP->tileFrameArray[spriteWorldP->curTileImage[tileID]];
SW_ASSERT(tileFrameP != NULL);
SW_ASSERT(tileFrameP->isFrameLocked);
// Determine whether the tile has a mask with background showing through
tileHasMask = (tileFrameP->maskPort != NULL || tileFrameP->maskRgn != NULL);
}
else
tileHasMask = false;
// Copy a piece from the extraBackFrameP only if there is no tile, or the
// tile has a mask with background showing through the unmasked part.
if ( (tileID == -1 || tileHasMask) && spriteWorldP->extraBackFrameP != NULL)
{
// There is no tile in this spot, or there is a masked tile and
// there is an extraBackFrameP, so copy a piece from extraBackFrameP
// Note: the function below wraps the dstRect for us, and leaves it
// that way when it returns, so we don't have to wrap it ourselves.
SWWrapRectFromExtraBackFrame(spriteWorldP, &dstRect);
}
else
{
// Make the tile's dest rect local to the offscreen area
dstRect.top -= spriteWorldP->vertScrollRectOffset;
dstRect.bottom -= spriteWorldP->vertScrollRectOffset;
dstRect.left -= spriteWorldP->horizScrollRectOffset;
dstRect.right -= spriteWorldP->horizScrollRectOffset;
// Wrap tile to top or bottom of offscreen area
if (dstRect.bottom > backRectP->bottom)
{
dstRect.top -= backRectP->bottom;
dstRect.bottom -= backRectP->bottom;
}
else if (dstRect.top < backRectP->top)
{
dstRect.top += backRectP->bottom;
dstRect.bottom += backRectP->bottom;
}
// Wrap tile to left or right side of offscreen area
if (dstRect.right > backRectP->right)
{
dstRect.left -= backRectP->right;
dstRect.right -= backRectP->right;
}
else if (dstRect.left < backRectP->left)
{
dstRect.left += backRectP->right;
dstRect.right += backRectP->right;
}
}
if (tileID >= 0)
{
srcRect = tileFrameP->frameRect;
// Clip new srcRect
if (row < updateRect.top)
srcRect.top += updateRect.top - row;
if (col < updateRect.left)
srcRect.left += updateRect.left - col;
if (row + tileHeight > updateRect.bottom)
srcRect.bottom -= row + tileHeight - updateRect.bottom;
if (col + tileWidth > updateRect.right)
srcRect.right -= col + tileWidth - updateRect.right;
if (tileHasMask && spriteWorldP->extraBackFrameP != NULL)
{
// The tile has a mask, with background showing through
(*spriteWorldP->tileMaskDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
else
{
// The entire tile should be drawn
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
}
// Draw tiles in higher layers
for (tileLayer = 1; tileLayer <= spriteWorldP->lastActiveTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
continue;
SW_ASSERT(spriteWorldP->tileLayerArray[tileLayer]->isLocked);
SW_ASSERT(tileRow < spriteWorldP->tileLayerArray[tileLayer]->numRows);
SW_ASSERT(tileCol < spriteWorldP->tileLayerArray[tileLayer]->numCols);
tileID = spriteWorldP->tileLayerArray[tileLayer]->tileMap[tileRow][tileCol];
if (tileID < 0)
continue;
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
tileFrameP = spriteWorldP->tileFrameArray[spriteWorldP->curTileImage[tileID]];
SW_ASSERT(tileFrameP != NULL);
SW_ASSERT(tileFrameP->isFrameLocked);
srcRect = tileFrameP->frameRect;
// Clip new srcRect
if (row < updateRect.top)
srcRect.top += updateRect.top - row;
if (col < updateRect.left)
srcRect.left += updateRect.left - col;
if (row + tileHeight > updateRect.bottom)
srcRect.bottom -= row + tileHeight - updateRect.bottom;
if (col + tileWidth > updateRect.right)
srcRect.right -= col + tileWidth - updateRect.right;
// Draw the tile
if (tileFrameP->maskPort == NULL && tileFrameP->maskRgn == NULL)
{
// Tile has no mask
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
else
{
// Tile has a mask
(*spriteWorldP->tileMaskDrawProc)(tileFrameP,
spriteWorldP->backFrameP, &srcRect, &dstRect);
}
}
}
}
}
///--------------------------------------------------------------------------------------
// SWDrawTilesAboveSprite - draw tiles over sprite using the tiles' masks.
// Assumes that updateRect fits within the bounds of the tileMap.
///--------------------------------------------------------------------------------------
SW_FUNC void SWDrawTilesAboveSprite(
SpriteWorldPtr spriteWorldP,
Rect* updateRectP,
short startLayer)
{
Rect* backRectP = &spriteWorldP->backRect;
Rect updateRect = *updateRectP;
short tileLayer;
short tileWidth = spriteWorldP->tileWidth;
short tileHeight = spriteWorldP->tileHeight;
short row, col, tileRow, tileCol, tileID;
short startRow, startCol, stopRow, stopCol;
short startPixelRow, startPixelCol;
Rect srcRect, dstRect;
FramePtr tileFrameP;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(startLayer >= 0);
// Convert pixel row and col into tile row and col
startRow = updateRect.top / tileHeight;
startCol = updateRect.left / tileWidth;
stopRow = (updateRect.bottom-1) / tileHeight;
stopCol = (updateRect.right-1) / tileWidth;
startPixelRow = startRow * tileHeight;
startPixelCol = startCol * tileWidth;
for (tileLayer = startLayer; tileLayer <= spriteWorldP->lastActiveTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
continue;
row = startPixelRow;
for (tileRow = startRow; tileRow <= stopRow; tileRow++, row += tileHeight)
{
col = startPixelCol;
for (tileCol = startCol; tileCol <= stopCol; tileCol++, col += tileWidth)
{
SW_ASSERT(tileRow < spriteWorldP->tileLayerArray[tileLayer]->numRows);
SW_ASSERT(tileCol < spriteWorldP->tileLayerArray[tileLayer]->numCols);
tileID = spriteWorldP->tileLayerArray[tileLayer]->tileMap[tileRow][tileCol];
if (tileID < 0)
continue;
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
tileFrameP = spriteWorldP->tileFrameArray[spriteWorldP->curTileImage[tileID]];
SW_ASSERT(tileFrameP != NULL);
// Skip tiles in the bottom layer that have no mask, and therefore
// aren't above the sprites, as long as no extraBackFrameP is installed.
if (tileLayer == 0 && spriteWorldP->extraBackFrameP == NULL &&
tileFrameP->maskPort == NULL && tileFrameP->maskRgn == NULL &&
tileFrameP->tileMaskIsSolid == false)
{
continue;
}
srcRect = tileFrameP->frameRect;
dstRect.bottom = row + tileHeight;
dstRect.right = col + tileWidth;
// Clip tile dstRect with updateRect //
if (row < updateRect.top)
{
dstRect.top = updateRect.top;
srcRect.top += updateRect.top - row;
}
else
dstRect.top = row;
if (col < updateRect.left)
{
dstRect.left = updateRect.left;
srcRect.left += updateRect.left - col;
}
else
dstRect.left = col;
if (dstRect.bottom > updateRect.bottom)
{
srcRect.bottom -= dstRect.bottom - updateRect.bottom;
dstRect.bottom = updateRect.bottom;
}
if (dstRect.right > updateRect.right)
{
srcRect.right -= dstRect.right - updateRect.right;
dstRect.right = updateRect.right;
}
// Make the tile's dest rect local to the offscreen area
dstRect.top -= spriteWorldP->vertScrollRectOffset;
dstRect.bottom -= spriteWorldP->vertScrollRectOffset;
dstRect.left -= spriteWorldP->horizScrollRectOffset;
dstRect.right -= spriteWorldP->horizScrollRectOffset;
// Wrap tile to top or bottom of offscreen area
if (dstRect.bottom > backRectP->bottom)
{
dstRect.top -= backRectP->bottom;
dstRect.bottom -= backRectP->bottom;
}
else if (dstRect.top < backRectP->top)
{
dstRect.top += backRectP->bottom;
dstRect.bottom += backRectP->bottom;
}
// Wrap tile to left or right side of offscreen area
if (dstRect.right > backRectP->right)
{
dstRect.left -= backRectP->right;
dstRect.right -= backRectP->right;
}
else if (dstRect.left < backRectP->left)
{
dstRect.left += backRectP->right;
dstRect.right += backRectP->right;
}
if (tileLayer == 0 && spriteWorldP->extraBackFrameP == NULL)
{
// The tile is in the bottom layer
if (tileFrameP->tileMaskIsSolid)
{
// Draw the tile without using a mask
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->workFrameP, &srcRect, &dstRect);
}
else
{
// Draw the masked part of the tile
(*spriteWorldP->partialMaskDrawProc)(tileFrameP,
spriteWorldP->workFrameP, &srcRect, &dstRect);
}
}
else
{
// The tile is in a higher layer, or is in the background layer and
// an extraBackFrameP is installed, and must be handled differently
if (tileFrameP->maskPort == NULL && tileFrameP->maskRgn == NULL)
{
// Tile has no mask
(*spriteWorldP->offscreenDrawProc)(tileFrameP,
spriteWorldP->workFrameP, &srcRect, &dstRect);
}
else
{
// Tile has a mask
(*spriteWorldP->tileMaskDrawProc)(tileFrameP,
spriteWorldP->workFrameP, &srcRect, &dstRect);
}
}
}
}
}
}
///--------------------------------------------------------------------------------------
// SWResetTilingCache
///--------------------------------------------------------------------------------------
SW_FUNC void SWResetTilingCache(
SpriteWorldPtr spriteWorldP)
{
short row, col;
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(spriteWorldP->tilingIsInitialized);
// Set all elements to -1 (indicating that each tile needs to be drawn)
for (row = 0; row < spriteWorldP->numTilingCacheRows; row++)
{
for (col = 0; col < spriteWorldP->numTilingCacheCols; col++)
{
spriteWorldP->tilingCache[row][col] = -1;
}
}
}
///--------------------------------------------------------------------------------------
// SWAddChangedRect - used by SWDrawTile
///--------------------------------------------------------------------------------------
SW_FUNC static void SWAddChangedRect(
SpriteWorldPtr spriteWorldP,
Rect *changedRectP)
{
short index;
Rect *changedTileP;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(changedRectP->top >= 0 && changedRectP->left >= 0 &&
changedRectP->right > changedRectP->left && changedRectP->bottom > changedRectP->top);
changedTileP = spriteWorldP->changedTiles;
for ( index = 0; index < spriteWorldP->numTilesChanged; index++, changedTileP++ )
{
// check for changedRectP entirely contained by changedTileP
if ( changedTileP->left <= changedRectP->left &&
changedTileP->top <= changedRectP->top &&
changedTileP->right >= changedRectP->right &&
changedTileP->bottom >= changedRectP->bottom )
{
return;
}
}
changedTileP = spriteWorldP->changedTiles;
for ( index = 0; index < spriteWorldP->numTilesChanged; index++, changedTileP++ )
{
// check for changedRectP horizontally adjacent to changedTileP
if ( changedTileP->top == changedRectP->top &&
changedTileP->bottom == changedRectP->bottom )
{
if ( changedRectP->left <= changedTileP->left && // changedRectP is to the left of changedTileP
changedRectP->right >= changedTileP->left || // or
changedRectP->left <= changedTileP->right && // changedRectP is to the right of changedTileP
changedRectP->right >= changedTileP->right )
{
changedTileP->left = SW_MIN( changedTileP->left, changedRectP->left );
changedTileP->right = SW_MAX( changedTileP->right, changedRectP->right );
return;
}
}
// check for changedRectP vertically adjacent to changedTileP
if ( changedTileP->left == changedRectP->left &&
changedTileP->right == changedRectP->right )
{
if ( changedRectP->top <= changedTileP->top && // changedRectP is above changedTileP
changedRectP->bottom >= changedTileP->top || // or
changedRectP->top <= changedTileP->bottom && // changedRectP is below changedTileP
changedRectP->bottom >= changedTileP->bottom )
{
changedTileP->top = SW_MIN( changedTileP->top, changedRectP->top );
changedTileP->bottom = SW_MAX( changedTileP->bottom, changedRectP->bottom );
return;
}
}
}
if (spriteWorldP->numTilesChanged < spriteWorldP->changedTilesArraySize)
{
spriteWorldP->changedTiles[spriteWorldP->numTilesChanged++] = *changedRectP;
}
else
{
// This shouldn't ever happen, but if it does, we'll trip our Assert routine.
SW_ASSERT(0);
SWFlagRectAsChanged(spriteWorldP, changedRectP);
}
}
///--------------------------------------------------------------------------------------
// SWChangeTileImage
///--------------------------------------------------------------------------------------
SW_FUNC void SWChangeTileImage(
SpriteWorldPtr spriteWorldP,
short tileID,
short newImage)
{
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(tileID < spriteWorldP->maxNumTiles);
SW_ASSERT(newImage < spriteWorldP->maxNumTiles);
// Set the current image
spriteWorldP->curTileImage[tileID] = newImage;
// Update the tile image on screen
SWUpdateTileOnScreen(spriteWorldP, tileID);
}
///--------------------------------------------------------------------------------------
// SWUpdateTileOnScreen - render new tile image in offscreen areas
///--------------------------------------------------------------------------------------
SW_FUNC void SWUpdateTileOnScreen(
SpriteWorldPtr spriteWorldP,
short tileID)
{
short tileRow, tileCol, tileLayer;
short startRow, startCol, stopRow, stopCol;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
// Convert pixel row and col into tile row and col
startRow = spriteWorldP->visScrollRect.top / spriteWorldP->tileHeight;
startCol = spriteWorldP->visScrollRect.left / spriteWorldP->tileWidth;
stopRow = (spriteWorldP->visScrollRect.bottom-1) / spriteWorldP->tileHeight;
stopCol = (spriteWorldP->visScrollRect.right-1) / spriteWorldP->tileWidth;
for (tileLayer = 0; tileLayer <= spriteWorldP->lastActiveTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
continue;
for (tileRow = startRow; tileRow <= stopRow; tileRow++)
{
for (tileCol = startCol; tileCol <= stopCol; tileCol++)
{
if (tileID == spriteWorldP->tileLayerArray[tileLayer]->tileMap[tileRow][tileCol])
SWDrawTile(spriteWorldP, tileLayer, tileRow, tileCol, tileID);
}
}
}
}
///--------------------------------------------------------------------------------------
// SWResetCurrentTileImages
///--------------------------------------------------------------------------------------
SW_FUNC void SWResetCurrentTileImages(
SpriteWorldPtr spriteWorldP)
{
short tileIndex;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
if (spriteWorldP->tilingIsInitialized)
{
for (tileIndex = 0; tileIndex < spriteWorldP->maxNumTiles; tileIndex++)
spriteWorldP->curTileImage[tileIndex] = tileIndex;
}
}
///--------------------------------------------------------------------------------------
// SWReturnTileUnderPixel
///--------------------------------------------------------------------------------------
SW_FUNC short SWReturnTileUnderPixel(
SpriteWorldPtr spriteWorldP,
short tileLayer,
short pixelCol,
short pixelRow)
{
short row, col;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(tileLayer < kNumTileLayers);
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
return -1; // No tile here, since there is no tileMap!
row = pixelRow / spriteWorldP->tileHeight;
col = pixelCol / spriteWorldP->tileWidth;
if (row < 0 || row >= spriteWorldP->tileLayerArray[tileLayer]->numRows ||
col < 0 || col >= spriteWorldP->tileLayerArray[tileLayer]->numCols )
{
return -1; // Pixel location is outside TileMap bounds!
}
else
{
return spriteWorldP->tileLayerArray[tileLayer]->tileMap[row][col];
}
}
///--------------------------------------------------------------------------------------
// SWCheckSpriteWithTiles
///--------------------------------------------------------------------------------------
SW_FUNC Boolean SWCheckSpriteWithTiles(
SpriteWorldPtr spriteWorldP,
SpritePtr srcSpriteP,
SWTileSearchType searchType,
Rect *insetRectP,
short startTileLayer,
short endTileLayer,
short firstTileID,
short lastTileID,
Boolean fixPosition)
{
short row, col, startRow, stopRow, startCol, stopCol;
TileMapPtr tileMap;
Rect oldFrameRect, destFrameRect;
Boolean foundTile = false;
Boolean rowLoopTest, colLoopTest;
short temp, tileID, tileLayer, rowIncrement, colIncrement;
SW_ASSERT(spriteWorldP != NULL && spriteWorldP->tilingIsInitialized);
SW_ASSERT(startTileLayer >= 0 && endTileLayer < kNumTileLayers);
SW_ASSERT(srcSpriteP != NULL);
oldFrameRect = srcSpriteP->oldFrameRect;
destFrameRect = srcSpriteP->destFrameRect;
if (insetRectP != NULL)
{
oldFrameRect.left += insetRectP->left;
oldFrameRect.top += insetRectP->top;
oldFrameRect.right -= insetRectP->right;
oldFrameRect.bottom -= insetRectP->bottom;
destFrameRect.left += insetRectP->left;
destFrameRect.top += insetRectP->top;
destFrameRect.right -= insetRectP->right;
destFrameRect.bottom -= insetRectP->bottom;
}
// We must do this so sprites hanging off the top or left side of the TileMap
// are still handled correctly. (The conversion from pixel to row won't work
// correctly for negative numbers, so we "fix" the problem here.)
if (oldFrameRect.top < 0)
oldFrameRect.top -= spriteWorldP->tileHeight;
if (oldFrameRect.bottom <= 0)
oldFrameRect.bottom -= spriteWorldP->tileHeight;
if (oldFrameRect.left <= 0)
oldFrameRect.left -= spriteWorldP->tileWidth;
if (oldFrameRect.right <= 0)
oldFrameRect.right -= spriteWorldP->tileWidth;
if (destFrameRect.left < 0)
destFrameRect.left -= spriteWorldP->tileWidth;
if (destFrameRect.right <= 0)
destFrameRect.right -= spriteWorldP->tileWidth;
if (destFrameRect.top < 0)
destFrameRect.top -= spriteWorldP->tileHeight;
if (destFrameRect.bottom <= 0)
destFrameRect.bottom -= spriteWorldP->tileHeight;
// startRow = the tile the oldFrameRect.side was about to run into.
// stopRow = the tile the destFrameRect.side is currently in.
// Function returns early if the sprite didn't move over
// a tile's bounds since last frame.
if (searchType == kSWTopSide)
{
startRow = (oldFrameRect.top / spriteWorldP->tileHeight);
stopRow = destFrameRect.top / spriteWorldP->tileHeight;
startCol = destFrameRect.left / spriteWorldP->tileWidth;
stopCol = (destFrameRect.right-1) / spriteWorldP->tileWidth;
if (fixPosition)
startRow--; // Check tile just above startRow
if (stopRow > startRow)
return false;
}
else if (searchType == kSWBottomSide)
{
startRow = ((oldFrameRect.bottom-1) / spriteWorldP->tileHeight);
stopRow = (destFrameRect.bottom-1) / spriteWorldP->tileHeight;
startCol = destFrameRect.left / spriteWorldP->tileWidth;
stopCol = (destFrameRect.right-1) / spriteWorldP->tileWidth;
if (fixPosition) // Check tile just below startRow
startRow++;
if (stopRow < startRow)
return false;
}
else if (searchType == kSWRightSide)
{
startCol = ((oldFrameRect.right-1) / spriteWorldP->tileWidth);
stopCol = (destFrameRect.right-1) / spriteWorldP->tileWidth;
startRow = destFrameRect.top / spriteWorldP->tileHeight;
stopRow = (destFrameRect.bottom-1) / spriteWorldP->tileHeight;
if (fixPosition) // Check tile just to the right of startCol
startCol++;
if (stopCol < startCol)
return false;
}
else if (searchType == kSWLeftSide)
{
startCol = oldFrameRect.left / spriteWorldP->tileWidth;
stopCol = destFrameRect.left / spriteWorldP->tileWidth;
startRow = destFrameRect.top / spriteWorldP->tileHeight;
stopRow = (destFrameRect.bottom-1) / spriteWorldP->tileHeight;
if (fixPosition) // Check tile just to the left of startCol
startCol--;
if (stopCol > startCol)
return false;
}
else // searchType == kSWEntireSprite
{
startRow = destFrameRect.top / spriteWorldP->tileHeight;
stopRow = (destFrameRect.bottom-1) / spriteWorldP->tileHeight;
startCol = destFrameRect.left / spriteWorldP->tileWidth;
stopCol = (destFrameRect.right-1) / spriteWorldP->tileWidth;
}
if (startRow <= stopRow)
rowIncrement = 1;
else
rowIncrement = -1;
if (startCol <= stopCol)
colIncrement = 1;
else
colIncrement = -1;
// Find the first tileLayer that's not NULL
for (tileLayer = startTileLayer; tileLayer <= endTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] != NULL)
break;
}
// Make sure things are within bounds (in case Sprite is hanging off edge of TileMap)
if (rowIncrement > 0)
{
if (stopRow < 0)
return false;
else if (stopRow >= spriteWorldP->tileLayerArray[tileLayer]->numRows)
stopRow = spriteWorldP->tileLayerArray[tileLayer]->numRows-1;
if (startRow < 0)
startRow = 0;
else if (startRow >= spriteWorldP->tileLayerArray[tileLayer]->numRows)
return false;
}
else // rowIncrement < 0
{
if (startRow < 0)
return false;
else if (startRow >= spriteWorldP->tileLayerArray[tileLayer]->numRows)
startRow = spriteWorldP->tileLayerArray[tileLayer]->numRows-1;
if (stopRow < 0)
stopRow = 0;
else if (stopRow >= spriteWorldP->tileLayerArray[tileLayer]->numRows)
return false;
}
if (colIncrement > 0)
{
if (stopCol < 0)
return false;
else if (stopCol >= spriteWorldP->tileLayerArray[tileLayer]->numCols)
stopCol = spriteWorldP->tileLayerArray[tileLayer]->numCols-1;
if (startCol < 0)
startCol = 0;
else if (startCol >= spriteWorldP->tileLayerArray[tileLayer]->numCols)
return false;
}
else // colIncrement < 0
{
if (startCol < 0)
return false;
else if (startCol >= spriteWorldP->tileLayerArray[tileLayer]->numCols)
startCol = spriteWorldP->tileLayerArray[tileLayer]->numCols-1;
if (stopCol < 0)
stopCol = 0;
else if (stopCol >= spriteWorldP->tileLayerArray[tileLayer]->numCols)
return false;
}
// Look for the tiles in each layer. We have two separate loops: one that scans
// through each row, then each col, and one that scans through each col, then each
// row. You must use the correct type depending on which direction the sprite is moving.
// (horizontally or vertically) for things to work correctly.
if (searchType == kSWTopSide || searchType == kSWBottomSide || searchType == kSWEntireSprite)
{
for (tileLayer = startTileLayer; tileLayer <= endTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
continue;
tileMap = spriteWorldP->tileLayerArray[tileLayer]->tileMap;
// Scan through all cols in a row before moving to next row
rowLoopTest = true;
for (row = startRow; rowLoopTest; row += rowIncrement)
{
rowLoopTest = row != stopRow;
colLoopTest = true;
for (col = startCol; colLoopTest; col += colIncrement)
{
colLoopTest = col != stopCol;
tileID = tileMap[row][col];
if (tileID >= firstTileID && tileID <= lastTileID)
{
foundTile = true;
goto exit;
}
}
}
}
}
else
{
for (tileLayer = startTileLayer; tileLayer <= endTileLayer; tileLayer++)
{
if (spriteWorldP->tileLayerArray[tileLayer] == NULL)
continue;
tileMap = spriteWorldP->tileLayerArray[tileLayer]->tileMap;
// Scan through all rows in a col before moving to next col
colLoopTest = true;
for (col = startCol; colLoopTest; col += colIncrement)
{
colLoopTest = col != stopCol;
rowLoopTest = true;
for (row = startRow; rowLoopTest; row += rowIncrement)
{
rowLoopTest = row != stopRow;
tileID = tileMap[row][col];
if (tileID >= firstTileID && tileID <= lastTileID)
{
foundTile = true;
goto exit;
}
}
}
}
}
exit:
if (foundTile && fixPosition)
{
if (searchType == kSWTopSide)
{
// (top of tile just below the tile the sprite is in) - (curSpriteTop)
temp = (row+1) * spriteWorldP->tileHeight - destFrameRect.top;
srcSpriteP->destFrameRect.top += temp;
srcSpriteP->destFrameRect.bottom += temp;
srcSpriteP->needsToBeDrawn = true;
}
else if (searchType == kSWBottomSide)
{
temp = destFrameRect.bottom - row * spriteWorldP->tileHeight;
srcSpriteP->destFrameRect.top -= temp;
srcSpriteP->destFrameRect.bottom -= temp;
srcSpriteP->needsToBeDrawn = true;
}
else if (searchType == kSWRightSide)
{
temp = destFrameRect.right - col * spriteWorldP->tileWidth;
srcSpriteP->destFrameRect.left -= temp;
srcSpriteP->destFrameRect.right -= temp;
srcSpriteP->needsToBeDrawn = true;
}
else if (searchType == kSWLeftSide)
{
temp = (col+1) * spriteWorldP->tileWidth - destFrameRect.left;
srcSpriteP->destFrameRect.left += temp;
srcSpriteP->destFrameRect.right += temp;
srcSpriteP->needsToBeDrawn = true;
}
}
return foundTile;
}
///--------------------------------------------------------------------------------------
// SWWrapRectToWorkArea - I think this is identical to SWEraseWrappedSprite, and is here
// simply so Scrolling.c isn't required during compiles.
///--------------------------------------------------------------------------------------
SW_FUNC void SWWrapRectToWorkArea(
SpriteWorldPtr spriteWorldP,
Rect* destRectP)
{
Rect destRect = *destRectP;
Rect tempDestRect;
FramePtr srcFrameP = spriteWorldP->backFrameP;
FramePtr dstFrameP = spriteWorldP->workFrameP;
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(spriteWorldP->backFrameP->isFrameLocked);
SW_ASSERT(spriteWorldP->workFrameP->isFrameLocked);
SetGWorld(spriteWorldP->workFrameP->framePort, nil);
// Make destRect local to the offscreen area
destRect.top -= spriteWorldP->vertScrollRectOffset;
destRect.bottom -= spriteWorldP->vertScrollRectOffset;
destRect.left -= spriteWorldP->horizScrollRectOffset;
destRect.right -= spriteWorldP->horizScrollRectOffset;
// Draw main image
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP, &destRect, &destRect);
// Wrap to top //
if (destRect.bottom > dstFrameP->frameRect.bottom)
{
tempDestRect.top = destRect.top - dstFrameP->frameRect.bottom;
tempDestRect.bottom = destRect.bottom - dstFrameP->frameRect.bottom;
tempDestRect.left = destRect.left;
tempDestRect.right = destRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
// Wrap to upper left or right corner //
if (destRect.right > dstFrameP->frameRect.right)
{
tempDestRect.left -= dstFrameP->frameRect.right;
tempDestRect.right -= dstFrameP->frameRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
}
else if (destRect.left < dstFrameP->frameRect.left)
{
tempDestRect.left += dstFrameP->frameRect.right;
tempDestRect.right += dstFrameP->frameRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
}
}
// Wrap to left or right side //
if (destRect.right > dstFrameP->frameRect.right)
{
tempDestRect.top = destRect.top;
tempDestRect.bottom = destRect.bottom;
tempDestRect.left = destRect.left - dstFrameP->frameRect.right;
tempDestRect.right = destRect.right - dstFrameP->frameRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
}
else if (destRect.left < dstFrameP->frameRect.left)
{
tempDestRect.top = destRect.top;
tempDestRect.bottom = destRect.bottom;
tempDestRect.left = destRect.left + dstFrameP->frameRect.right;
tempDestRect.right = destRect.right + dstFrameP->frameRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
}
// Wrap to bottom //
if (destRect.top < dstFrameP->frameRect.top)
{
tempDestRect.top = destRect.top + dstFrameP->frameRect.bottom;
tempDestRect.bottom = destRect.bottom + dstFrameP->frameRect.bottom;
tempDestRect.left = destRect.left;
tempDestRect.right = destRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
// Wrap to lower left or right corner //
if (destRect.right > dstFrameP->frameRect.right)
{
tempDestRect.left -= dstFrameP->frameRect.right;
tempDestRect.right -= dstFrameP->frameRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
}
else if (destRect.left < dstFrameP->frameRect.left)
{
tempDestRect.left += dstFrameP->frameRect.right;
tempDestRect.right += dstFrameP->frameRect.right;
(*spriteWorldP->offscreenDrawProc)(srcFrameP, dstFrameP,
&tempDestRect, &tempDestRect);
}
}
}
///--------------------------------------------------------------------------------------
// SWWrapRectFromExtraBackFrame - similar to SWWrapRectToScreen, just modified so the
// srcFramePtr is the extraBackFramePtr, the dstFrameP is the backFrameP, and the src
// and dst rects are what they should be. Note: the dstRect you pass to this function is
// the tile's rect *before* it's made local to the offscreen area and wrapped. It is also
// important that the actual dstRectP is modified, not a copy, since the callers expect this.
///--------------------------------------------------------------------------------------
SW_FUNC void SWWrapRectFromExtraBackFrame(
SpriteWorldPtr spriteWorldP,
Rect *dstRectP)
{
FramePtr srcFrameP = spriteWorldP->extraBackFrameP;
FramePtr dstFrameP = spriteWorldP->backFrameP;
Rect srcRect, tempSrcRect, tempDstRect;
short topClip=0, rightClip=0, bottomClip=0, leftClip=0;
short vertBackRectOffset, horizBackRectOffset;
SW_ASSERT(spriteWorldP != NULL);
SW_ASSERT(srcFrameP->isFrameLocked);
SW_ASSERT(dstFrameP->isFrameLocked);
// The code below is ripped from SWCalculateOffscreenScrollRect
// It is modified to do a similar function for the extraBackFrameP.
vertBackRectOffset = spriteWorldP->extraBackFrameP->frameRect.bottom *
(dstRectP->top / spriteWorldP->extraBackFrameP->frameRect.bottom);
horizBackRectOffset = spriteWorldP->extraBackFrameP->frameRect.right *
(dstRectP->left / spriteWorldP->extraBackFrameP->frameRect.right);
srcRect.top = dstRectP->top - vertBackRectOffset;
srcRect.bottom = dstRectP->bottom - vertBackRectOffset;
srcRect.left = dstRectP->left - horizBackRectOffset;
srcRect.right = dstRectP->right - horizBackRectOffset;
// We must do the dstRect calculation below *after* the code above!
// Make the tile's dest rect local to the offscreen area
dstRectP->top -= spriteWorldP->vertScrollRectOffset;
dstRectP->bottom -= spriteWorldP->vertScrollRectOffset;
dstRectP->left -= spriteWorldP->horizScrollRectOffset;
dstRectP->right -= spriteWorldP->horizScrollRectOffset;
// Wrap dstRect to top or bottom of offscreen area
if (dstRectP->bottom > dstFrameP->frameRect.bottom)
{
dstRectP->top -= dstFrameP->frameRect.bottom;
dstRectP->bottom -= dstFrameP->frameRect.bottom;
}
else if (dstRectP->top < dstFrameP->frameRect.top)
{
dstRectP->top += dstFrameP->frameRect.bottom;
dstRectP->bottom += dstFrameP->frameRect.bottom;
}
// Wrap dstRect to left or right side of offscreen area
if (dstRectP->right > dstFrameP->frameRect.right)
{
dstRectP->left -= dstFrameP->frameRect.right;
dstRectP->right -= dstFrameP->frameRect.right;
}
else if (dstRectP->left < dstFrameP->frameRect.left)
{
dstRectP->left += dstFrameP->frameRect.right;
dstRectP->right += dstFrameP->frameRect.right;
}
// Clip the source rect, and save what we clipped for wrapping later //
// clip off the top
if (srcRect.top < srcFrameP->frameRect.top)
{
topClip = srcFrameP->frameRect.top - srcRect.top;
srcRect.top += topClip;
}
// clip off the bottom
if (srcRect.bottom > srcFrameP->frameRect.bottom)
{
bottomClip = srcRect.bottom - srcFrameP->frameRect.bottom;
srcRect.bottom -= bottomClip;
}
// clip off the left
if (srcRect.left < srcFrameP->frameRect.left)
{
leftClip = srcFrameP->frameRect.left - srcRect.left;
srcRect.left += leftClip;
}
// clip off the right
if (srcRect.right > srcFrameP->frameRect.right)
{
rightClip = srcRect.right - srcFrameP->frameRect.right;
srcRect.right -= rightClip;
}
// Here we do the wrapping and drawing //
// Draw top section //
if (topClip)
{
// Calculate top piece //
// Wrap source rect to bottom side
tempSrcRect.right = srcRect.right; // Copy clipped source rect
tempSrcRect.left = srcRect.left;
tempSrcRect.bottom = srcFrameP->frameRect.bottom;
tempSrcRect.top = srcFrameP->frameRect.bottom - topClip;
// Position dest rect at top side
tempDstRect.top = dstRectP->top;
tempDstRect.bottom = dstRectP->top + topClip;
tempDstRect.left = dstRectP->left + leftClip;
tempDstRect.right = dstRectP->right - rightClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
if (leftClip) // Calculate top-left piece
{
// Wrap source rect to lower-right corner
tempSrcRect.bottom = srcFrameP->frameRect.bottom;
tempSrcRect.right = srcFrameP->frameRect.right;
tempSrcRect.top = srcFrameP->frameRect.bottom - topClip;
tempSrcRect.left = srcFrameP->frameRect.right - leftClip;
// Position dest rect at top-left corner
tempDstRect.left = dstRectP->left;
tempDstRect.top = dstRectP->top;
tempDstRect.right = dstRectP->left + leftClip;
tempDstRect.bottom = dstRectP->top + topClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
}
else if (rightClip) // Calculate top-right piece
{
// Wrap source rect to lower-left corner
tempSrcRect.bottom = srcFrameP->frameRect.bottom;
tempSrcRect.left = srcFrameP->frameRect.left;
tempSrcRect.right = srcFrameP->frameRect.left + rightClip;
tempSrcRect.top = srcFrameP->frameRect.bottom - topClip;
// Position dest rect at top-right corner
tempDstRect.top = dstRectP->top;
tempDstRect.right = dstRectP->right;
tempDstRect.bottom = dstRectP->top + topClip;
tempDstRect.left = dstRectP->right - rightClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
}
}
// Draw middle section //
// Calculate main middle piece (not wrapped)
tempDstRect.left = dstRectP->left + leftClip;
tempDstRect.top = dstRectP->top + topClip;
tempDstRect.right = dstRectP->right - rightClip;
tempDstRect.bottom = dstRectP->bottom - bottomClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &srcRect, &tempDstRect);
if (leftClip) // Draw left piece
{
// Wrap source rect to right side
tempSrcRect.top = srcRect.top; // Copy clipped source rect
tempSrcRect.bottom = srcRect.bottom;
tempSrcRect.right = srcFrameP->frameRect.right;
tempSrcRect.left = srcFrameP->frameRect.right - leftClip;
// Position dest rect at left side
tempDstRect.left = dstRectP->left;
tempDstRect.right = dstRectP->left + leftClip;
tempDstRect.top = dstRectP->top + topClip;
tempDstRect.bottom = dstRectP->bottom - bottomClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
}
else if (rightClip) // Draw right piece
{
// Wrap source rect to left side
tempSrcRect.top = srcRect.top; // Copy clipped source rect
tempSrcRect.bottom = srcRect.bottom;
tempSrcRect.left = srcFrameP->frameRect.left;
tempSrcRect.right = srcFrameP->frameRect.left + rightClip;
// Position dest rect at right side
tempDstRect.right = dstRectP->right;
tempDstRect.left = dstRectP->right - rightClip;
tempDstRect.top = dstRectP->top + topClip;
tempDstRect.bottom = dstRectP->bottom - bottomClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
}
// Draw bottom section //
if (bottomClip)
{
// Calculate bottom piece //
// Wrap source rect to top side
tempSrcRect.right = srcRect.right; // Copy clipped source rect
tempSrcRect.left = srcRect.left;
tempSrcRect.top = srcFrameP->frameRect.top;
tempSrcRect.bottom = srcFrameP->frameRect.top + bottomClip;
// Position dest rect at bottom side
tempDstRect.bottom = dstRectP->bottom;
tempDstRect.top = dstRectP->bottom - bottomClip;
tempDstRect.left = dstRectP->left + leftClip;
tempDstRect.right = dstRectP->right - rightClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
if (leftClip) // Draw bottom-left piece
{
// Wrap source rect to upper-right corner
tempSrcRect.top = srcFrameP->frameRect.top;
tempSrcRect.right = srcFrameP->frameRect.right;
tempSrcRect.bottom = srcFrameP->frameRect.top + bottomClip;
tempSrcRect.left = srcFrameP->frameRect.right - leftClip;
// Position dest rect at bottom-left corner
tempDstRect.bottom = dstRectP->bottom;
tempDstRect.left = dstRectP->left;
tempDstRect.top = dstRectP->bottom - bottomClip;
tempDstRect.right = dstRectP->left + leftClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
}
else if (rightClip) // Draw bottom-right piece
{
// Wrap source rect to upper-left corner
tempSrcRect.top = srcFrameP->frameRect.top;
tempSrcRect.left = srcFrameP->frameRect.left;
tempSrcRect.bottom = srcFrameP->frameRect.top + bottomClip;
tempSrcRect.right = srcFrameP->frameRect.left + rightClip;
// Position dest rect at bottom-right corner
tempDstRect.bottom = dstRectP->bottom;
tempDstRect.right = dstRectP->right;
tempDstRect.top = dstRectP->bottom - bottomClip;
tempDstRect.left = dstRectP->right - rightClip;
(*spriteWorldP->screenDrawProc)(srcFrameP, dstFrameP, &tempSrcRect, &tempDstRect);
}
}
}