Glypha3/Source/Utilities.c

1 line
17 KiB
C
Executable File

//============================================================================
//----------------------------------------------------------------------------
// Utilities.c
//----------------------------------------------------------------------------
//============================================================================
// These functions are sort of universal utility functions. They aren't specificÉ
// to Glypha per se. I use these (and others) in many, many games. Many of themÉ
// as well are useful for any app you might write for the Mac.
#include "Externs.h"
#define kActive 0
#define kInactive 255
GDHandle thisGDevice;
long tickNext;
//============================================================== Functions
//-------------------------------------------------------------- RandomInt
// Takes a short (range) and returns a random number from zero to range - 1.
short RandomInt (short range)
{
register long rawResult;
rawResult = Random();
if (rawResult < 0L)
rawResult *= -1L;
rawResult = (rawResult * (long)range) / 32768L;
return ((short)rawResult);
}
//-------------------------------------------------------------- RedAlert
// Generic error function. This is called when there is no hope of recovering
// from the error. A simple alert is brought up and the text passed in (theStr)
// is displayed. When the user clicks the Okay button, we quit to the Finder.
void RedAlert (StringPtr theStr)
{
#define kRedAlertID 128
short whoCares;
ParamText(theStr, "\p", "\p", "\p"); // Replace ^0 in alert with error mssg.
whoCares = Alert(kRedAlertID, 0L); // Bring up alert.
ExitToShell(); // Quit to Finder.
}
//-------------------------------------------------------------- FindOurDevice
// Get a handle to the MainDevice (monitor with the Menubar).
void FindOurDevice (void)
{
thisGDevice = GetMainDevice();
if (thisGDevice == 0L) // If a nil handle is returned...
RedAlert("\pCouldn't Find Our Device"); // call our universal error alert.
}
//-------------------------------------------------------------- LoadGraphic
// Handy function that loads a PICT graphic, get's its bounds and draws it.
// The port drawn to is assumed the current port. No scaling is done.
void LoadGraphic (short resID)
{
Rect bounds;
PicHandle thePicture;
thePicture = GetPicture(resID); // Load graphic from resource fork.
if (thePicture == 0L) // Check to see if nil (did it load?)
RedAlert("\pA Graphic Couldn't Be Loaded");
HLock((Handle)thePicture); // If we made it this far, lock handle.
bounds = (*thePicture)->picFrame; // Get a copy of the picture's bounds.
HUnlock((Handle)thePicture); // We can unlock the picture now.
OffsetRect(&bounds, -bounds.left, -bounds.top); // Offset bounds rect to (0, 0).
DrawPicture(thePicture, &bounds); // Draw picture to current port.
ReleaseResource((Handle)thePicture); // Dispose of picture from heap.
}
//-------------------------------------------------------------- CreateOffScreenPixMap
// Handles the creation of an offscreen pixmap. Depth is assumed to be that of theÉ
// current gDevice. If the allocation fails (low memory, etc.) we quit to Finder.
void CreateOffScreenPixMap (Rect *theRect, CGrafPtr *offScreen)
{
CTabHandle thisColorTable;
GDHandle oldDevice;
CGrafPtr newCGrafPtr;
Ptr theseBits;
long sizeOfOff, offRowBytes;
OSErr theErr;
short thisDepth;
oldDevice = GetGDevice();
SetGDevice(thisGDevice);
newCGrafPtr = 0L;
newCGrafPtr = (CGrafPtr)NewPtrClear(sizeof(CGrafPort));
if (newCGrafPtr != 0L)
{
OpenCPort(newCGrafPtr);
thisDepth = (**(*newCGrafPtr).portPixMap).pixelSize;
offRowBytes = ((((long)thisDepth *
(long)(theRect->right - theRect->left)) + 15L) >> 4L) << 1L;
sizeOfOff = (long)(theRect->bottom - theRect->top) * offRowBytes;
OffsetRect(theRect, -theRect->left, -theRect->top);
theseBits = NewPtr(sizeOfOff);
if (theseBits != 0L)
{
(**(*newCGrafPtr).portPixMap).baseAddr = theseBits;
(**(*newCGrafPtr).portPixMap).rowBytes = (short)offRowBytes + 0x8000;
(**(*newCGrafPtr).portPixMap).bounds = *theRect;
thisColorTable = (**(**thisGDevice).gdPMap).pmTable;
theErr = HandToHand((Handle *)&thisColorTable);
(**(*newCGrafPtr).portPixMap).pmTable = thisColorTable;
ClipRect(theRect);
RectRgn(newCGrafPtr->visRgn, theRect);
ForeColor(blackColor);
BackColor(whiteColor);
EraseRect(theRect);
}
else
{
CloseCPort(newCGrafPtr);
DisposePtr((Ptr)newCGrafPtr);
newCGrafPtr = 0L;
RedAlert("\pCouldn't Allocate Enough Memory");
}
}
else
RedAlert("\pCouldn't Allocate Enough Memory");
*offScreen = newCGrafPtr;
SetGDevice(oldDevice);
}
//-------------------------------------------------------------- CreateOffScreenBitMap
// Creates an offscreen bitmap. Depth is of course 1 (b & w). If this functionÉ
// fails to create the bitmap, we post an alert and quit to the Finder.
void CreateOffScreenBitMap (Rect *theRect, GrafPtr *offScreen)
{
GrafPtr theBWPort;
BitMap theBitMap;
long theRowBytes;
theBWPort = (GrafPtr)(NewPtr(sizeof(GrafPort)));
OpenPort(theBWPort);
theRowBytes = (long)((theRect->right - theRect->left + 15L) / 16L) * 2L;
theBitMap.rowBytes = (short)theRowBytes;
theBitMap.baseAddr = NewPtr((long)theBitMap.rowBytes *
(theRect->bottom - theRect->top));
if (theBitMap.baseAddr == 0L)
RedAlert("\pCouldn't Create Bitmaps");
theBitMap.bounds = *theRect;
if (MemError() != noErr)
RedAlert("\pCouldn't Create Bitmaps");
SetPortBits(&theBitMap);
ClipRect(theRect);
RectRgn(theBWPort->visRgn, theRect);
EraseRect(theRect);
*offScreen = theBWPort;
}
//-------------------------------------------------------------- ZeroRectCorner
// Offset rect to (0, 0). This means the upper left corner of the rect is
// moved to the origin - to (0, 0) - to the upperleft corner of the port.
void ZeroRectCorner (Rect *theRect)
{
theRect->right -= theRect->left; // Move right edge by amount of left.
theRect->bottom -= theRect->top; // Move bottom edge by amount of top.
theRect->left = 0; // Can now set left to zero.
theRect->top = 0; // Can set top edge to zero as well.
}
//-------------------------------------------------------------- FlashShort
// This is a simple debugging function that will display the short passed to itÉ
// in the upper left corner of the screen. It's a handy way to watch the valueÉ
// of a variable while the program is running.
void FlashShort (short theValue)
{
GrafPtr wasPort, tempPort;
Str255 tempStr;
Rect tempRect;
GetPort(&wasPort); // Remember old grafPort.
tempPort = (GrafPtr)NewPtrClear(sizeof(GrafPort));
OpenPort(tempPort); // Create a new empty port.
NumToString((long)theValue, tempStr); // Convert value passed in to a string.
MoveTo(20, 40); // Move the pen to the upperleft corner.
SetRect(&tempRect, 18, 20, 122, 42); // Create a rect up there as well.
EraseRect(&tempRect); // Erase the rect (to make a white hole).
DrawString(tempStr); // And draw our text into that hole.
ClosePort(tempPort); // Get rid of out temp port.
SetPort((GrafPtr)wasPort); // And set port back to the old one.
}
//-------------------------------------------------------------- LogNextTick
// Simple function to set a global (tickNext) to the current TickCount() plusÉ
// some offset. We'll then wait for TickCount() to exceed that global. We useÉ
// this function and the function below to regulate animation speeds (rememberÉ
// your game may be run on a slow Mac or a fast one - we need a way to keep theÉ
// motion consistent. I love when the comments are longer than the function.
// (Not really.)
void LogNextTick (long howMany)
{
tickNext = TickCount() + howMany; // Get machine's TickCount() and add to it.
}
//-------------------------------------------------------------- WaitForNextTick
// This is the companion function to the above function (LogNextTick()).
// We do nothing but loop until TickCount() catches up with (or passes) ourÉ
// global variable tickNext.
void WaitForNextTick (void)
{
do
{
}
while (TickCount() < tickNext); // Loop until TickCount() catches up.
}
//-------------------------------------------------------------- TrapExists
// A nice "test function" that test for the existence of some ToolBox trap.
// Returns TRUE if the function exists, FALSE if it doesn't.
Boolean TrapExists (short trapNumber)
{
#define kUnimpTrap 0x9F
// Test trap number against unimplemented trap number.
return ((NGetTrapAddress(trapNumber, ToolTrap) !=
NGetTrapAddress(kUnimpTrap, ToolTrap)));
}
//-------------------------------------------------------------- DoWeHaveGestalt
// This function specifically tests for the availablity of the Gestalt() function.
// It returns TRUE if Gestalt() exists, FALSE if it doesn't.
Boolean DoWeHaveGestalt (void)
{
#define kGestaltTrap 0xAD
// Call above function (TrapExists()) with the Gestalt() trap number.
return (TrapExists(kGestaltTrap));
}
//-------------------------------------------------------------- CenterAlert
// Handy function to center any alert within the main monitor.
void CenterAlert (short alertID)
{
AlertTHndl alertHandle;
Rect theScreen, alertRect;
short horiOff, vertOff;
Byte wasState;
theScreen = qd.screenBits.bounds; // Get main monitor's bounds.
theScreen.top += LMGetMBarHeight(); // Account for menubar height.
// Get handle to alert resource.
alertHandle = (AlertTHndl)GetResource('ALRT', alertID);
if (alertHandle != 0L) // Make sure we got it!
{ // Remember its "state" (locked, etc.)
wasState = HGetState((Handle)alertHandle);
HLock((Handle)alertHandle); // We'll lock it.
// Get a copy of it's bounds and zero.
alertRect = (**alertHandle).boundsRect;
OffsetRect(&alertRect, -alertRect.left, -alertRect.top);
// Calculate offsets for centering bounds.
horiOff = ((theScreen.right - theScreen.left) - alertRect.right) / 2;
vertOff = ((theScreen.bottom - theScreen.top) - alertRect.bottom) / 3;
// And offset the bounds copy.
OffsetRect(&alertRect, horiOff, vertOff + LMGetMBarHeight());
// Set alerts bounds to our centered rect.
(**alertHandle).boundsRect = alertRect;
HSetState((Handle)alertHandle, wasState);
}
}
//-------------------------------------------------------------- RectWide
// Handy function for returning the absolute width of a rectangle.
short RectWide (Rect *theRect)
{
return (theRect->right - theRect->left);
}
//-------------------------------------------------------------- RectTall
// Handy function for returning the absolute height of a rectangle.
short RectTall (Rect *theRect)
{
return (theRect->bottom - theRect->top);
}
//-------------------------------------------------------------- CenterRectInRect
// Nice utility function that takes two rectangles and centers the firstÉ
// rectangle within the second.
void CenterRectInRect (Rect *rectA, Rect *rectB)
{
short widthA, tallA;
widthA = RectWide(rectA); // Get width of 1st rect.
tallA = RectTall(rectA); // Get height of 1st rect.
// Do the math (center horizontally).
rectA->left = rectB->left + (RectWide(rectB) - widthA) / 2;
rectA->right = rectA->left + widthA;
// Do the math (center vertically).
rectA->top = rectB->top + (RectTall(rectB) - tallA) / 2;
rectA->bottom = rectA->top + tallA;
}
//-------------------------------------------------------------- PasStringCopy
// This is a nice function that helps to free you from dealing with C strings.
// It takes one Pascal-style string and copies it to a second.
void PasStringCopy (StringPtr p1, StringPtr p2)
{
register short stringLength;
stringLength = *p2++ = *p1++; // Get 1st string's length.
while (--stringLength >= 0) // Loop through each character in 1st string.
*p2++ = *p1++; // And copy to 2nd string.
}
//-------------------------------------------------------------- CenterDialog
// Like CenterAlert(), this function centers a Dialog on the main monitor.
void CenterDialog (short dialogID)
{
DialogTHndl dlogHandle;
Rect theScreen, dlogBounds;
short hPos, vPos;
Byte wasState;
theScreen = qd.screenBits.bounds; // Get main monitor's bounds.
theScreen.top += LMGetMBarHeight(); // Add menuBar's height.
// Load up dialog from resource.
dlogHandle = (DialogTHndl)GetResource('DLOG', dialogID);
if (dlogHandle != 0L) // If it loaded....!
{ // Remember handle state.
wasState = HGetState((Handle)dlogHandle);
HLock((Handle)dlogHandle); // We're going to lock it.
// Get a copy of the dialog's bounds.
dlogBounds = (**dlogHandle).boundsRect;
OffsetRect(&dlogBounds, -dlogBounds.left, -dlogBounds.top);
// Calculate how much to offset.
hPos = ((theScreen.right - theScreen.left) - dlogBounds.right) / 2;
vPos = ((theScreen.bottom - theScreen.top) - dlogBounds.bottom) / 3;
// Offset ourt copy of the bounds.
OffsetRect(&dlogBounds, hPos, vPos + LMGetMBarHeight());
// Set dlg's bounds to centered rect.
(**dlogHandle).boundsRect = dlogBounds;
HSetState((Handle)dlogHandle, wasState);// Restore handle's state.
}
}
//-------------------------------------------------------------- DrawDefaultButton
// A nice dialog function. This draws the bold default outline aroundÉ
// item #1 in the dialog passed in.
void DrawDefaultButton (DialogPtr theDialog)
{
Rect itemRect;
Handle itemHandle;
short itemType;
// Get at the item's bounds.
GetDItem(theDialog, 1, &itemType, &itemHandle, &itemRect);
InsetRect(&itemRect, -4, -4); // Inset (outset?) bounds by -4 pixels.
PenSize(3, 3); // Set the pen 3 pixels thick.
FrameRoundRect(&itemRect, 16, 16); // Draw the button outline.
PenNormal(); // And restore pen to 1 pixel thick.
}
//-------------------------------------------------------------- PasStringCopyNum
// Another function to keep you from using C strings. This one copies only aÉ
// certain number of characters from one Pascal-style string to a second.
void PasStringCopyNum (StringPtr p1, StringPtr p2, short charsToCopy)
{
short i;
if (charsToCopy > *p1) // If trying to copy more chars than there areÉ
charsToCopy = *p1; // Reduce the number of chars to copy to this size
*p2 = charsToCopy; // Set 2nd string's length to charsToCopy.
*p2++; // Point to first character in 2nd string.
*p1++; // Point to first character in 1st string.
for (i = 0; i < charsToCopy; i++)
*p2++ = *p1++; // Copy the specified number of chars over.
}
//-------------------------------------------------------------- GetDialogString
// Handy dialog function that returns a dialog item string. This will beÉ
// especially handy for getting the high score name the player enters.
void GetDialogString (DialogPtr theDialog, short item, StringPtr theString)
{
Rect itemRect;
Handle itemHandle;
short itemType;
// Get handle to dialog item.
GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
GetIText(itemHandle, theString); // Extract text from item handle.
}
//-------------------------------------------------------------- SetDialogString
// Like the above function, but this one sets a dialog items string to whateverÉ
// you pass in. We'll use this to set a default high score name.
void SetDialogString (DialogPtr theDialog, short item, StringPtr theString)
{
Rect itemRect;
Handle itemHandle;
short itemType;
// Get handle to dialog item.
GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
SetIText(itemHandle, theString); // Set the items text to theString.
}
//-------------------------------------------------------------- SetDialogNumToStr
// This one is like SetDialogString() above, but it takes a number (long)É
// instead of a string (the function will convert the long to a string for us).
void SetDialogNumToStr (DialogPtr theDialog, short item, long theNumber)
{
Str255 theString;
Rect itemRect;
Handle itemHandle;
short itemType;
NumToString(theNumber, theString); // Convert long to a string.
GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
SetIText(itemHandle, theString); // Set the item's text to this number/string.
}
//-------------------------------------------------------------- GetDialogNumFromStr
// This one is like GetDialogString() above, but returns a long (number)É
// instead of a string (it does this by converting the string to a long).
void GetDialogNumFromStr (DialogPtr theDialog, short item, long *theNumber)
{
Str255 theString;
Rect itemRect;
Handle itemHandle;
short itemType;
// Get a handle to the dialog item.
GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
GetIText(itemHandle, theString); // Get the item's text.
StringToNum(theString, theNumber); // Convert the text to a long.
}
//-------------------------------------------------------------- DisableControl
// Another dialog utility for "graying out" buttons or other controls in a dialog.
void DisableControl (DialogPtr theDialog, short whichItem)
{
Rect iRect;
Handle iHandle;
short iType;
// Get a handle to the dialog item.
GetDItem(theDialog, whichItem, &iType, &iHandle, &iRect);
// Set it's "hilite state" to "grayed out".
HiliteControl((ControlHandle)iHandle, kInactive);
}