//============================================================================ //---------------------------------------------------------------------------- // 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); }