1 line
19 KiB
C
Executable File
1 line
19 KiB
C
Executable File
|
|
//============================================================================
|
|
//----------------------------------------------------------------------------
|
|
// Interface.c
|
|
//----------------------------------------------------------------------------
|
|
//============================================================================
|
|
|
|
// I put all interface related code in here. Interface would include eventÉ
|
|
// handling, menus, dialog boxes, etc. All the user interaction that takesÉ
|
|
// place before and after an actual game is in play.
|
|
|
|
#include "Externs.h"
|
|
#include <Sound.h>
|
|
|
|
|
|
#define kAppleMenuID 128
|
|
#define iAbout 1
|
|
#define kGameMenuID 129
|
|
#define iNewGame 1
|
|
#define iPauseGame 2
|
|
#define iEndGame 3
|
|
#define iQuit 5
|
|
#define kOptionsMenuID 130
|
|
#define iSettings 1
|
|
#define iHelp 2
|
|
#define iHighScores 3
|
|
#define kAboutPictID 132
|
|
|
|
|
|
void DoAppleMenu (short);
|
|
void DoGameMenu (short);
|
|
void DoOptionsMenu (short);
|
|
void UpdateMainWindow (void);
|
|
void HandleMouseEvent (EventRecord *);
|
|
void HandleKeyEvent (EventRecord *);
|
|
void HandleUpdateEvent (EventRecord *);
|
|
void HandleOSEvent (EventRecord *);
|
|
void HandleHighLevelEvent (EventRecord *);
|
|
void DoAbout (void);
|
|
void DoGameSettings (void);
|
|
|
|
|
|
Rect mainWindowRect;
|
|
WindowPtr mainWindow;
|
|
MenuHandle appleMenu, gameMenu, optionsMenu;
|
|
Boolean switchedOut, quitting, canPlay, openTheScores;
|
|
|
|
extern prefsInfo thePrefs;
|
|
extern Rect backSrcRect, workSrcRect;
|
|
extern CGrafPtr backSrcMap, workSrcMap;
|
|
extern Boolean pausing, playing, helpOpen, scoresOpen;
|
|
|
|
|
|
//============================================================== Functions
|
|
//-------------------------------------------------------------- MenusReflectMode
|
|
|
|
// Depending on whether a game is in progress (paused) or not, I wantÉ
|
|
// menu items grayed out in one case and not grayed out in the other.
|
|
// This function, when called, displays the menus correctly dependingÉ
|
|
// on the mode we're in (playing or not playing, pausing or not).
|
|
|
|
void MenusReflectMode (void)
|
|
{
|
|
if (playing) // If a game is in progressÉ
|
|
{
|
|
DisableItem(gameMenu, iNewGame); // Cannot begin another New Game.
|
|
EnableItem(gameMenu, iPauseGame); // Can Pause Game.
|
|
if (pausing) // If we are pausedÉ
|
|
SetItem(gameMenu, iPauseGame,
|
|
"\pResume Game"); // Rename item "Resume Game".
|
|
else // If we are not pausedÉ
|
|
SetItem(gameMenu, iPauseGame,
|
|
"\pPause Game"); // Rename item "Pause Game".
|
|
EnableItem(gameMenu, iEndGame); // Can End Game.
|
|
DisableItem(optionsMenu, 0); // Cannot change game settings.
|
|
}
|
|
else // Else, if Glypha is idleÉ
|
|
{
|
|
EnableItem(gameMenu, iNewGame); // Can begin a New Game.
|
|
DisableItem(gameMenu, iPauseGame); // Cannot Pause Game.
|
|
SetItem(gameMenu, iPauseGame,
|
|
"\pPause Game"); // Rename item "Pause Game".
|
|
DisableItem(gameMenu, iEndGame); // Cannot End Game.
|
|
EnableItem(optionsMenu, 0); // Can change game settings.
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoAppleMenu
|
|
|
|
// This function takes care of handling the Apple menu (Desk Assecories and theÉ
|
|
// About box).
|
|
|
|
void DoAppleMenu (short theItem)
|
|
{
|
|
Str255 daName;
|
|
GrafPtr wasPort;
|
|
short daNumber;
|
|
|
|
switch (theItem) // Depending on the item selectedÉ
|
|
{
|
|
case iAbout: // If the About item was selectedÉ
|
|
if ((scoresOpen) || (helpOpen)) // If high scores or help screens upÉ
|
|
{
|
|
CloseWall(); // hide them.
|
|
scoresOpen = FALSE; // High scores no longer open.
|
|
helpOpen = FALSE; // Help screen is no longer open.
|
|
// Uncheck help & high scores menu items.
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
CheckItem(optionsMenu, iHighScores, scoresOpen);
|
|
}
|
|
DoAbout(); // Bring up the About dialog.
|
|
break;
|
|
|
|
default: // If any other item was selected (DA)É
|
|
GetItem(appleMenu, theItem, daName); // Get the name of the item selected.
|
|
GetPort(&wasPort); // Remember our port.
|
|
daNumber = OpenDeskAcc(daName); // Launch the Desk Accesory.
|
|
SetPort((GrafPtr)wasPort); // When we return, restore port.
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoGameMenu
|
|
|
|
// This function handles a users interaction with the Game menu. QuittingÉ
|
|
// Glypha, starting a new game, resuming a paused game are handled here.
|
|
|
|
void DoGameMenu (short theItem)
|
|
{
|
|
switch (theItem) // Depending on menu item selectedÉ
|
|
{
|
|
case iNewGame: // If user selected New Game itemÉ
|
|
if ((scoresOpen) || (helpOpen)) // If high scores or help screen is up,É
|
|
{ // close them first.
|
|
CloseWall();
|
|
scoresOpen = FALSE;
|
|
helpOpen = FALSE;
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
CheckItem(optionsMenu, iHighScores, scoresOpen);
|
|
}
|
|
InitNewGame(); // Initialize variables for a new game.
|
|
MenusReflectMode(); // Properly gray out the right menu items.
|
|
break;
|
|
|
|
case iPauseGame: // If user selected Pause Game itemÉ
|
|
if (pausing) // If we are paused, resume playing.
|
|
{
|
|
pausing = FALSE; // Turn off pausing flag.
|
|
DumpBackToWorkMap(); // Restore off screen just in case.
|
|
} // Actually pausing a game (not resuming)É
|
|
break; // is not really handled here. It's handledÉ
|
|
// directly within the main game loop.
|
|
|
|
case iEndGame: // Ending a game in progress isn't reallyÉ
|
|
break; // handled here - this is a dummy item.
|
|
// Ending a game is handled within the mainÉ
|
|
// game loop by looking for the 'command'É
|
|
// and 'E' key explicitly.
|
|
|
|
case iQuit: // If user selected Quit itemÉ
|
|
quitting = TRUE; // Set quitting flag to TRUE.
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoOptionsMenu
|
|
|
|
// This function handles the Options menu. Options include game settings,É
|
|
// displaying the high scores, and bringing up the Help screen.
|
|
|
|
void DoOptionsMenu (short theItem)
|
|
{
|
|
switch (theItem) // Depending on which item the user selectedÉ
|
|
{
|
|
case iSettings: // If user selected Game Settings itemÉ
|
|
if ((scoresOpen) || (helpOpen)) // Close high scores or help screen.
|
|
{
|
|
CloseWall();
|
|
scoresOpen = FALSE;
|
|
helpOpen = FALSE;
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
CheckItem(optionsMenu, iHighScores, scoresOpen);
|
|
}
|
|
DoGameSettings(); // Bring up game settings dialog.
|
|
break;
|
|
|
|
case iHelp: // If user selected Help itemÉ
|
|
if (helpOpen) // If Help open, close it.
|
|
{
|
|
CloseWall();
|
|
helpOpen = FALSE;
|
|
}
|
|
else // Else, if Help is not open - open it.
|
|
{
|
|
if (scoresOpen) // If the High Scores are up though,É
|
|
{
|
|
CloseWall(); // Close them first.
|
|
scoresOpen = FALSE;
|
|
CheckItem(optionsMenu, iHighScores, scoresOpen);
|
|
}
|
|
OpenHelp(); // Now open the Help screen.
|
|
}
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
break;
|
|
|
|
case iHighScores: // If user selected High ScoresÉ
|
|
if (scoresOpen) // If the High Scores are up, close them.
|
|
{
|
|
CloseWall();
|
|
scoresOpen = FALSE;
|
|
}
|
|
else // If the High Scores are not upÉ
|
|
{
|
|
if (helpOpen) // First see if Help is open.
|
|
{
|
|
CloseWall(); // And close the Help screen.
|
|
helpOpen = FALSE;
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
}
|
|
OpenHighScores(); // Now open the High Scores.
|
|
}
|
|
CheckItem(optionsMenu, iHighScores, scoresOpen);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoMenuChoice
|
|
|
|
// This is the main menu-handling function. It examines which menu was selectedÉ
|
|
// by the user and passes on to the appropriate function, the item within thatÉ
|
|
// menu that was selected.
|
|
|
|
void DoMenuChoice (long menuChoice)
|
|
{
|
|
short theMenu, theItem;
|
|
|
|
if (menuChoice == 0) // A little error checking.
|
|
return;
|
|
|
|
theMenu = HiWord(menuChoice); // Extract which menu was selected.
|
|
theItem = LoWord(menuChoice); // Extract which item it was that was selected.
|
|
|
|
switch (theMenu) // Now, depending upon which menu was selectedÉ
|
|
{
|
|
case kAppleMenuID: // If the Apple menu selectedÉ
|
|
DoAppleMenu(theItem); // Call the function that handles the Apple menu.
|
|
break;
|
|
|
|
case kGameMenuID: // If the Game menu selectedÉ
|
|
DoGameMenu(theItem); // Call the function that handles the Game menu.
|
|
break;
|
|
|
|
case kOptionsMenuID: // If the Options menu selectedÉ
|
|
DoOptionsMenu(theItem); // Call the function that handles the Options menu.
|
|
break;
|
|
}
|
|
|
|
HiliteMenu(0); // "De-invert" menu.
|
|
}
|
|
|
|
//-------------------------------------------------------------- UpdateMainWindow
|
|
|
|
// This is a simple function that simply copies the contents from theÉ
|
|
// background offscreen pixmap to the main screen. It is primarilyÉ
|
|
// called in response to an update event, but could be called any timeÉ
|
|
// when I want to force the screen to be redrawn.
|
|
|
|
void UpdateMainWindow (void)
|
|
{
|
|
CopyBits(&((GrafPtr)backSrcMap)->portBits,
|
|
&(((GrafPtr)mainWindow)->portBits),
|
|
&mainWindowRect, &mainWindowRect,
|
|
srcCopy, 0L);
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleMouseEvent
|
|
|
|
// Mouse clicks come here. This is standard event-handling drivel. No different
|
|
// from any other standard Mac program (game or otherwise).
|
|
|
|
void HandleMouseEvent (EventRecord *theEvent)
|
|
{
|
|
WindowPtr whichWindow;
|
|
Point localPoint;
|
|
long menuChoice;
|
|
short thePart;
|
|
// Determine window and where in window.
|
|
thePart = FindWindow(theEvent->where, &whichWindow);
|
|
|
|
switch (thePart) // Depending on where mouse was clickedÉ
|
|
{
|
|
case inSysWindow: // In a Desk Accesory.
|
|
SystemClick(theEvent, whichWindow); // (Is this stuff obsolete yet?)
|
|
break;
|
|
|
|
case inMenuBar: // Selected a menu item.
|
|
menuChoice = MenuSelect(theEvent->where);
|
|
if (canPlay) // Call menu handling routine.
|
|
DoMenuChoice(menuChoice);
|
|
break;
|
|
|
|
case inDrag: // Like the lazy bastard I amÉ
|
|
case inGoAway: // I'll just ignore these.
|
|
case inGrow: // But, hey, the window isn'tÉ
|
|
case inZoomIn: // movable or growable!
|
|
case inZoomOut:
|
|
break;
|
|
|
|
case inContent: // Click in the window itself.
|
|
FlashObelisks(TRUE); // Do lightning animation.
|
|
LogNextTick(3); // Lightning will hit cursor location.
|
|
localPoint = theEvent->where;
|
|
GlobalToLocal(&localPoint);
|
|
GenerateLightning(localPoint.h, localPoint.v);
|
|
StrikeLightning();
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
PlayExternalSound(kLightningSound, kLightningPriority);
|
|
LogNextTick(3);
|
|
GenerateLightning(localPoint.h, localPoint.v);
|
|
StrikeLightning();
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
LogNextTick(3);
|
|
GenerateLightning(localPoint.h, localPoint.v);
|
|
StrikeLightning();
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
PlayExternalSound(kLightningSound, kLightningPriority);
|
|
LogNextTick(3);
|
|
GenerateLightning(localPoint.h, localPoint.v);
|
|
StrikeLightning();
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
FlashObelisks(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleKeyEvent
|
|
|
|
// More standard issue. This function handles any keystrokes when no game is
|
|
// in session. Command-key strokes handled here too.
|
|
|
|
void HandleKeyEvent (EventRecord *theEvent)
|
|
{
|
|
char theChar;
|
|
Boolean commandDown;
|
|
|
|
theChar = theEvent->message & charCodeMask; // Extract key hit.
|
|
commandDown = ((theEvent->modifiers & cmdKey) != 0); // See if command key down.
|
|
|
|
if (commandDown) // If command key down, call menuÉ
|
|
{ // handling routine.
|
|
if (canPlay)
|
|
DoMenuChoice(MenuKey(theChar));
|
|
}
|
|
else
|
|
{
|
|
if (helpOpen) // Handle special keys if the helpÉ
|
|
{ // screen is up.
|
|
if (theChar == kUpArrowKeyASCII) // Up arrow key scrolls help down.
|
|
{
|
|
if (theEvent->what == autoKey)
|
|
ScrollHelp(-3);
|
|
else
|
|
ScrollHelp(-1);
|
|
}
|
|
else if (theChar == kDownArrowKeyASCII) // Down arrow key scrolls help up.
|
|
{
|
|
if (theEvent->what == autoKey)
|
|
ScrollHelp(3);
|
|
else
|
|
ScrollHelp(1);
|
|
}
|
|
else if (theChar == kPageDownKeyASCII) // Handle page down for help screen.
|
|
{
|
|
ScrollHelp(199);
|
|
}
|
|
else if (theChar == kPageUpKeyASCII) // Handle page up for help.
|
|
{
|
|
ScrollHelp(-199);
|
|
}
|
|
else if ((theChar == kHelpKeyASCII) && (!playing))
|
|
{ // Hitting Help key closes helpÉ
|
|
CloseWall(); // (if it's already open).
|
|
helpOpen = FALSE;
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
}
|
|
}
|
|
else if ((theChar == kHelpKeyASCII) && (!playing))
|
|
{ // Else, if help not open and HelpÉ
|
|
if (scoresOpen) // key is hit, open Help.
|
|
{ // Close high scores if open.
|
|
CloseWall();
|
|
scoresOpen = FALSE;
|
|
CheckItem(optionsMenu, iHighScores, scoresOpen);
|
|
}
|
|
OpenHelp(); // Open help.
|
|
CheckItem(optionsMenu, iHelp, helpOpen);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleUpdateEvent
|
|
|
|
// This function handles update events. Standard event-handling stuff.
|
|
|
|
void HandleUpdateEvent (EventRecord *theEvent)
|
|
{
|
|
if ((WindowPtr)theEvent->message == mainWindow)
|
|
{
|
|
SetPort((GrafPtr)mainWindow); // Don't forget this line, BTW.
|
|
BeginUpdate((GrafPtr)mainWindow); // I did once and it took meÉ
|
|
UpdateMainWindow(); // ages to track down that bug.
|
|
EndUpdate((GrafPtr)mainWindow); // Well, it took me a week I think.
|
|
canPlay = TRUE;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleOSEvent
|
|
|
|
// Handle switchin in and out events. Standard event-handling stuff.
|
|
|
|
void HandleOSEvent (EventRecord *theEvent)
|
|
{
|
|
if (theEvent->message & 0x01000000) // If suspend or resume eventÉ
|
|
{
|
|
if (theEvent->message & 0x00000001) // Specifically, if resume eventÉ
|
|
switchedOut = FALSE; // I keep thinking I should do more here.
|
|
else // Or if suspend eventÉ
|
|
switchedOut = TRUE; // What am I forgetting?
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleHighLevelEvent
|
|
|
|
// Again, it's a fact I'm lazy. AppleEvents are fairly easy to implement butÉ
|
|
// a nightmare to try and explain. Filling out the below function is left asÉ
|
|
// and exercise to the reader.
|
|
|
|
void HandleHighLevelEvent (EventRecord *theEvent)
|
|
{
|
|
// theErr = AEProcessAppleEvent(theEvent);
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleEvent
|
|
|
|
// Standard event stuff. This is the culling function that calls all the aboveÉ
|
|
// functions. It looks for an event and if it detects one, it calls the appropriateÉ
|
|
// function above to handle it.
|
|
|
|
void HandleEvent (void)
|
|
{
|
|
EventRecord theEvent;
|
|
long sleep = 1L;
|
|
Boolean itHappened;
|
|
// See if an event is queued up.
|
|
itHappened = WaitNextEvent(everyEvent, &theEvent, sleep, 0L);
|
|
|
|
if (itHappened) // Ah, an event. I live for events!
|
|
{
|
|
switch (theEvent.what) // And what kind of event be ya'?
|
|
{
|
|
case mouseDown: // Aiy! Y' be a mouse click do ya'?
|
|
HandleMouseEvent(&theEvent);
|
|
break;
|
|
|
|
case keyDown: // Key down, key held down events.
|
|
case autoKey:
|
|
HandleKeyEvent(&theEvent);
|
|
break;
|
|
|
|
case updateEvt: // Something needs redrawing!
|
|
HandleUpdateEvent(&theEvent);
|
|
break;
|
|
|
|
case osEvt: // Switching in and out events.
|
|
HandleOSEvent(&theEvent);
|
|
break;
|
|
|
|
case kHighLevelEvent: // Hmmmm. A "what" event?
|
|
HandleHighLevelEvent(&theEvent);
|
|
break;
|
|
}
|
|
}
|
|
else if (openTheScores) // Check for "auto open" flag.
|
|
{ // If TRUE, set the flag back toÉ
|
|
openTheScores = FALSE; // FALSE and open the high scores.
|
|
OpenHighScores();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoAbout
|
|
|
|
// This handles the About dialog. It brings up the About box in aÉ
|
|
// simple centered window with no drag bar, close box or anything.
|
|
// Leaving the dialog is handled with a simple mouse click.
|
|
|
|
void DoAbout (void)
|
|
{
|
|
Rect aboutRect;
|
|
WindowPtr aboutWindow;
|
|
|
|
SetRect(&aboutRect, 0, 0, 325, 318); // Bring up centered window.
|
|
CenterRectInRect(&aboutRect, &qd.screenBits.bounds);
|
|
aboutWindow = GetNewCWindow(129, 0L, kPutInFront);
|
|
MoveWindow((GrafPtr)aboutWindow, aboutRect.left, aboutRect.top, TRUE);
|
|
ShowWindow((GrafPtr)aboutWindow);
|
|
SetPort((GrafPtr)aboutWindow);
|
|
LoadGraphic(kAboutPictID); // Draw About dialog graphic.
|
|
|
|
do // Make sure button not downÉ
|
|
{ // before proceeding.
|
|
}
|
|
while (Button()); // Proceed.
|
|
do // And now wait until the mouseÉ
|
|
{ // is pressed before closing theÉ
|
|
} // window (ABout dialog).
|
|
while (!Button());
|
|
|
|
FlushEvents(everyEvent, 0); // Flush the queue.
|
|
|
|
if (aboutWindow != 0L)
|
|
DisposeWindow(aboutWindow); // Close the About dialog.
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoGameSettings
|
|
|
|
// This one however is a good and proper dialog box. It handles the meagerÉ
|
|
// preference settings for Glypha. Nothing fancy here to report. Just aÉ
|
|
// straight-forward dialog calling routine.
|
|
|
|
void DoGameSettings (void)
|
|
{
|
|
#define kGameSettingsDialogID 133
|
|
DialogPtr theDial;
|
|
long newVolume;
|
|
short i, item;
|
|
Boolean leaving;
|
|
|
|
CenterDialog(kGameSettingsDialogID); // Center dialog, then call up.
|
|
theDial = GetNewDialog(kGameSettingsDialogID, 0L, kPutInFront);
|
|
SetPort((GrafPtr)theDial);
|
|
ShowWindow((GrafPtr)theDial); // Make visible (after centering).
|
|
DrawDefaultButton(theDial); // Draw border around Okay button.
|
|
FlushEvents(everyEvent, 0);
|
|
// Put in a default sound volume.
|
|
SetDialogNumToStr(theDial, 3, (long)thePrefs.wasVolume);
|
|
SelIText(theDial, 3, 0, 1024); // Select it.
|
|
|
|
leaving = FALSE;
|
|
|
|
while (!leaving)
|
|
{
|
|
ModalDialog(0L, &item); // Simple modal dialog filtering.
|
|
|
|
if (item == 1) // Did user hit the Okay button?
|
|
{ // Well see if volume entered is legal.
|
|
GetDialogNumFromStr(theDial, 3, &newVolume);
|
|
if ((newVolume >= 0) && (newVolume <= 7))
|
|
{ // If it is legal, we'll note it and quit.
|
|
thePrefs.wasVolume = (short)newVolume;
|
|
SetSoundVol((short)newVolume);
|
|
leaving = TRUE; // Bye.
|
|
}
|
|
else // Otherwise, the volume entered is wrong.
|
|
{ // So we'll Beep, enter the last legalÉ
|
|
SysBeep(1); // value and select the text again.
|
|
SetDialogNumToStr(theDial, 3, (long)thePrefs.wasVolume);
|
|
SelIText(theDial, 3, 0, 1024);
|
|
}
|
|
}
|
|
else if (item == 2) // Did the user hit the "Clear Scores"É
|
|
{ // button?
|
|
for (i = 0; i < 10; i++) // Walk through and zero scores.
|
|
{
|
|
PasStringCopy("\pNemo", thePrefs.highNames[i]);
|
|
thePrefs.highScores[i] = 0L;
|
|
thePrefs.highLevel[i] = 0;
|
|
openTheScores = TRUE; // Bring up scores when dialog quits.
|
|
}
|
|
DisableControl(theDial, 2); // Gray out Clear Scores button.
|
|
}
|
|
}
|
|
|
|
DisposDialog(theDial); // Clean up before going.
|
|
}
|
|
|