Glypha3/Source/Interface.c

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.
}