home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-02-04 | 34.4 KB | 1,293 lines | [TEXT/PJMM] |
- {****************************************************}
- {}
- { CShGameDirector.p }
- {}
- { Director for the main, game playing window of the Showwit application. }
- {}
- {****************************************************}
-
-
- unit CShGameDirector;
-
- interface
-
- uses
- TCL, ShIntf;
-
- implementation
-
- uses
- Script;
-
- const
-
- { Dimensions. }
-
- kGameWindh = 504;
- kGameWindv = 290;
-
- kEdgeMarginh = 20;
- kEdgeMarginv = 10;
-
- kTableLenh = kGameWindh - 2 * kEdgeMarginh;
- kTableLenv = 18 * 6 + 2; { 6 rows, 18 pixels each, plus 2 pixels for the top and bottom. }
- kTablePosh = kEdgeMarginh;
- kTablePosv = 126;
-
- kStartLenh = 200;
- kStartLenv = kIconPixels;
- kStartPosh = (kGameWindh - kStartLenh) div 2;
- kStartPosv = kGameWindv - kStartLenv - kEdgeMarginv;
-
- kTargetPosh = kEdgeMarginh;
- kTargetPosv = kEdgeMarginv;
-
- kControlIconMargin = 24;
-
- kHelpPosh = kGameWindh - kIconPixels - kEdgeMarginh;
- kHelpPosv = kGameWindv - kIconPixels - kEdgeMarginv;
-
- kControlsLenh = kIconPixels * 2 + kControlIconMargin;
- kControlsLenv = kIconPixels;
- kControlsPosh = kHelpPosh - kControlsLenh - kControlIconMargin;
- kControlsPosv = kGameWindv - kControlsLenv - kEdgeMarginv;
-
- kRecordSpacing = 24;
-
- kNameLenh = 140;
- kNameLenv = 16;
- kNamePosh = 0;
- kNamePosv = 0;
-
- kMovesLenh = 140;
- kMovesLenv = 16;
- kMovesPosh = 0;
- kMovesPosv = kNameLenv + kRecordSpacing;
-
- kTimeLenv = 16;
- kTimeSpacing = 10;
-
- kHoursLenh = 20;
- kHoursLenv = kTimeLenv;
- kHoursPosh = 0;
- kHoursPosv = kMovesPosv + kMovesLenv + kRecordSpacing;
-
- kMinutesLenh = 20;
- kMinutesLenv = kTimeLenv;
- kMinutesPosh = kHoursLenh + kTimeSpacing;
- kMinutesPosv = kHoursPosv;
-
- kSecondsLenh = 20;
- kSecondsLenv = kTimeLenv;
- kSecondsPosh = kMinutesPosh + kSecondsLenh + kTimeSpacing;
- kSecondsPosv = kHoursPosv;
-
- kDataLenh = kNameLenh;
- kDataLenv = kNameLenv + kRecordSpacing + kMovesLenv + kRecordSpacing + kTimeLenv;
- kDataPosh = kGameWindh - kDataLenh - kEdgeMarginh;
- kDataPosv = kEdgeMarginv + 80;
-
- type
- BooleanPtr = ^Boolean;
- BooleanHandle = ^BooleanPtr;
-
- const
- kActivePlaySleepTime = 0;
- kLoopTime = 1036800000; { = 200 days in Ticks. Remember that maxLongInt = 2147483647. }
- { So we can have played up to 200 days before pausing, then 200 }
- { days afterwards, and can add the times without overflow. Silly, }
- { but the problems from overflow are avoided. }
-
- const
- kSTRGameNoTargetPICTs = 1;
- kSTRGameNoColorQDandNoBWTargetPICTs = 2;
- kSTRGameStartIndex = 3;
-
-
- {****************************************************}
- {}
- { IShGameDirector }
- {}
- { Construction of the game director object. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.IShGameDirector (aSupervisor: CShApp);
-
- var
- theTileCount: TileRange;
-
- { Obtain and act upon the preferences for animation and sound. }
- procedure InitAnimationAndSound;
-
- var
- theFlagHandle: BooleanHandle;
-
- begin { InitAnimationAndSound }
- theFlagHandle := nil;
-
- itsShApp.PreferencesFile.GetPref(kPrefAnimationOn, Handle(theFlagHandle));
- if theFlagHandle = nil then begin
- fAnimate := kAnimation;
- end { if }
- else begin
- fAnimate := theFlagHandle^^;
- end; { else }
-
- itsShApp.PreferencesFile.GetPref(kPrefSoundOn, Handle(theFlagHandle));
- if theFlagHandle = nil then begin
- fSound := kSound;
- end { if }
- else begin
- fSound := theFlagHandle^^;
- end; { else }
-
- if fSound then begin
- SATSoundOn;
- end { if }
- else begin
- SATSoundOff;
- end; { else }
-
- { Set the flag so that we know to play the welcoming sound. }
- fWelcomingSoundPlayed := FALSE;
- end; { InitAnimationAndSound }
-
- procedure GetPICTIDsForTargets;
-
- var
- theCounter, theLoadCount: Integer;
- theTargetBase: Integer;
- rHandle: Handle;
- rID: Integer;
- rType: ResType;
- rName: Str255;
-
- fi: FailInfo;
-
- procedure HandleFailure (error: Integer;
- message: LongInt);
-
- begin { HandleFailure }
- { Before exiting from the application, have to SetResLoad(TRUE), }
- { otherwise the Finder's code resource will not be loaded. }
- { See THINK Reference - SetResLoad for details. }
-
- SetResLoad(TRUE);
-
- rHandle := nil;
-
- ErrorAlert(error, message);
-
- { Debugging project - Compile exceptions with Debugging on }
- { otherwise the next call will crash. }
-
- ExitApplication;
- end; { HandleFailure }
-
- begin { GetPICTIDsForTargets }
- { When this routine commences, the main event loop has not commenced, }
- { so there is no main level failure handler. That's okay, because if }
- { something goes wrong here, we will want to just make things safe, }
- { put up an error alert and quit anyway. }
-
- { Set the safe conditions. }
-
- rHandle := nil;
-
- itsNumTargetPICTs := 0;
- itsTargetPICTIDs := nil;
-
- { Select the kind of pictures to look for. }
- if gSystem.hasColorQD then begin
- theTargetBase := PICTTargetColourBase;
- end { if }
- else begin
- theTargetBase := PICTTargetBWBase;
- end; { else }
-
- { Post our exception handler. }
-
- CatchFailures(fi, HandleFailure);
-
- { Because the preferences file is open, we have to use CountResources }
- { to get to the application's resource. }
-
- SetResLoad(FALSE); { Do not need resource, just information. }
-
- for theCounter := 1 to CountResources('PICT') do begin
- rHandle := GetIndResource('PICT', theCounter);
- GetResInfo(rHandle, rID, rType, rName);
- if rID >= theTargetBase then begin
- itsNumTargetPICTs := itsNumTargetPICTs + 1;
- end; { if }
-
- { Because the resource hasn't be loaded, we don't have to release it. }
- end; { for }
-
- if not (itsNumTargetPICTS > 0) then begin
- if gSystem.hasColorQD then begin
- Failure(kSTRGameNoTargetPICTs, SpecifyMsg(STRlistGameMessages, kSTRGameNoTargetPICTs));
- end { if }
- else begin
- Failure(kSTRGameNoColorQDandNoBWTargetPICTs, SpecifyMsg(STRlistGameMessages, kSTRGameNoColorQDandNoBWTargetPICTs));
- end; { else }
- end; { if }
-
- itsTargetPICTIDs := IDArrayH(NewHandleCanFail(SizeOf(Integer) * itsNumTargetPICTs));
- FailNIL(itsTargetPICTIDs);
-
- theLoadCount := 1;
- for theCounter := 1 to CountResources('PICT') do begin
- rHandle := GetIndResource('PICT', theCounter);
- GetResInfo(rHandle, rID, rType, rName);
- if rID >= theTargetBase then begin
- {$PUSH}
- {$R-}
- itsTargetPICTIDs^^[theLoadCount] := rID;
- {$POP}
- theLoadCount := theLoadCount + 1;
- end; { if }
-
- { Because the resource hasn't be loaded, we don't have to release it. }
- end; { for }
-
- SetResLoad(TRUE);
-
- { Panic's over. }
-
- Success;
- end; { GetPICTIDsForTargets }
-
- procedure BuildWindowAndMainPane;
-
- var
- theWindow: CWindow;
- theWindRect: Rect;
-
- theMenuBarHeight: Integer;
- theGDHandle: GDHandle;
-
- thePicture: CPicture;
-
- begin { BuildWindowAndMainPane }
- new(theWindow);
- theWindow.IWindow(WINDGame, FALSE, gDesktop, SELF);
- itsWindow := theWindow;
-
- { The window is initially hidden, set by the resource. }
-
- gDecorator.CenterWindow(theWindow);
-
- { Background picture. Start with the title screen. }
-
- new(thePicture);
- itsMainPicture := thePicture;
- itsMainPicture.IPicture(itsWindow, SELF, 0, 0, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY);
- itsMainPicture.FitToEnclFrame(kDoHorizontal, kDoVertical);
- end; { BuildWindowAndMainPane }
-
- procedure BuildTitleScreenPanes;
-
- var
- theButton: CButton;
- theScrollPane: CScrollPane;
- theLevelsTable: CShLevelsTable;
- theBorder: CPaneBorder;
-
- begin { BuildTitleScreenPanes }
- { Start button. We have to create this before the levels table, because when the table is installed in the }
- { levels scroll pane, this changes the selection, which calls the game director, and this sends a message }
- { to the (assumed to exist) start button. }
-
- new(theButton);
- itsStartButton := theButton;
- itsStartButton.INewButton(kStartLenh, kStartLenv, kStartPosh, kStartPosv, 'Start', TRUE, 0, itsWindow, SELF);
- itsStartButton.SetClickCmd(cmdStartGame);
-
- { Scroll pane for the table of levels. }
-
- new(theScrollPane);
- itsLevelsPane := theScrollPane;
- itsLevelsPane.IScrollPane(itsWindow, SELF, kTableLenh, kTableLenv, kTablePosh, kTablePosv, sizELASTIC, sizELASTIC, FALSE, TRUE, FALSE);
-
- { Array pane with the levels. }
-
- new(theLevelsTable);
- itsLevelsTable := theLevelsTable;
- itsLevelsTable.IShLevelsTable(itsLevelsPane, SELF, 0, 0, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY);
- itsLevelsPane.InstallPanorama(itsLevelsTable);
-
- { The levels table intercepts keystrokes first, for scrolling through the levels. }
-
- itsGopher := itsLevelsTable;
-
- { Border for the table. }
-
- new(theBorder);
- theBorder.IPaneBorder(kBorderLeft + kBorderTop + kBorderBottom);
- itsLevelsTable.SetBorder(theBorder);
- end; { BuildTitleScreenPanes }
-
- procedure BuildTiles;
-
- var
- theTileCount: TileRange;
- theTile: CShTile;
-
- begin { BuildTiles }
- for theTileCount := 1 to kMaxTiles do begin
- new(theTile);
- itsTiles[theTileCount] := theTile;
- itsTiles[theTileCount].IShTile(itsWindow, SELF, (theTileCount - 1) mod kMaxGridAcross * kTileSize + kTargetPosh, (theTileCount - 1) div kMaxGridAcross * kTileSize + kTargetPosv, sizFIXEDSTICKY, sizFIXEDSTICKY);
- itsTiles[theTileCount].SetGridPos(theTileCount);
- end; { for }
- end; { BuildTiles }
-
- procedure BuildGameControls;
-
- var
- thePane: CPane;
- theIcon: CIconPane;
-
- begin { BuildGameControls }
- { Controls for help, pausing/ resuming or aborting the game. }
-
- { Help icon is always visible. }
-
- new(theIcon);
- theIcon.IIconPane(itsWindow, SELF, kHelpPosh, kHelpPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnHelp, TRUE);
- theIcon.SetWantsClicks(TRUE);
- theIcon.SetClickCmd(cmdHelp);
-
- { Dummy pane to enclose - easier to get the coordinates right and to hide the controls. }
-
- new(thePane);
- itsPlayControls := thePane;
- itsPlayControls.IPane(itsWindow, SELF, kControlsLenh, kControlsLenv, kControlsPosh, kControlsPosv, sizFIXEDSTICKY, sizFIXEDSTICKY);
- itsPlayControls.SetWantsClicks(TRUE);
-
- new(theIcon);
- itsPauseIcon := theIcon;
- itsPauseIcon.IIconPane(itsPlayControls, SELF, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnPause, TRUE);
- itsPauseIcon.SetWantsClicks(TRUE);
- itsPauseIcon.SetClickCmd(cmdPauseGame);
-
- new(theIcon);
- itsResumeIcon := theIcon;
- itsResumeIcon.IIconPane(itsPlayControls, SELF, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnResume, TRUE);
- itsResumeIcon.SetWantsClicks(TRUE);
- itsResumeIcon.SetClickCmd(cmdResumeGame);
-
- new(theIcon);
- theIcon.IIconPane(itsPlayControls, SELF, kIconPixels + kControlIconMargin, 0, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnAbort, TRUE);
- theIcon.SetWantsClicks(TRUE);
- theIcon.SetClickCmd(cmdAbortGame);
- end; { BuildGameControls }
-
- procedure BuildGameDataPanes;
-
- var
- thePane: CPane;
- theText: CEditText;
-
- begin { BuildGameDataPanes }
-
- { Text boxes for the name, moves and time. To reduce flicker, have hours, minutes and seconds. }
-
- new(thePane);
- itsPlayData := thePane;
- itsPlayData.IPane(itsWindow, SELF, kDataLenh, kDataLenv, kDataPosh, kDataPosv, sizFIXEDSTICKY, sizFIXEDSTICKY);
-
- new(theText);
- itsNameText := theText;
- itsNameText.IEditText(itsPlayData, SELF, kNameLenh, kNameLenv, kNamePosh, kNamePosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kNameLenh);
- itsNameText.Specify(kNotEditable, kNotSelectable, kNotStylable);
- itsNameText.SetAlignCmd(cmdAlignLeft);
-
- new(theText);
- itsMovesText := theText;
- itsMovesText.IEditText(itsPlayData, SELF, kMovesLenh, kMovesLenv, kMovesPosh, kMovesPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kMovesLenh);
- itsMovesText.Specify(kNotEditable, kNotSelectable, kNotStylable);
- itsMovesText.SetAlignCmd(cmdAlignLeft);
-
- new(theText);
- itsHoursText := theText;
- itsHoursText.IEditText(itsPlayData, SELF, kHoursLenh, kHoursLenv, kHoursPosh, kHoursPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kHoursLenh);
- itsHoursText.Specify(kNotEditable, kNotSelectable, kNotStylable);
- itsHoursText.SetAlignCmd(cmdAlignRight);
-
- new(theText);
- itsMinutesText := theText;
- itsMinutesText.IEditText(itsPlayData, SELF, kMinutesLenh, kMinutesLenv, kMinutesPosh, kMinutesPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kMinutesLenh);
- itsMinutesText.Specify(kNotEditable, kNotSelectable, kNotStylable);
- itsMinutesText.SetAlignCmd(cmdAlignCenter);
-
- new(theText);
- itsSecondsText := theText;
- itsSecondsText.IEditText(itsPlayData, SELF, kSecondsLenh, kSecondsLenv, kSecondsPosh, kSecondsPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kSecondsLenh);
- itsSecondsText.Specify(kNotEditable, kNotSelectable, kNotStylable);
- itsSecondsText.SetAlignCmd(cmdAlignCenter);
- end; { BuildGameDataPanes }
-
- begin { IShGameDirector }
- itsShApp := aSupervisor;
-
- itsMainPicture := nil;
-
- itsLevelsPane := nil;
- itsLevelsTable := nil;
- itsStartButton := nil;
-
- itsNameText := nil;
- itsMovesText := nil;
- itsHoursText := nil;
- itsMinutesText := nil;
- itsSecondsText := nil;
-
- itsHoursStr := '';
- itsMinutesStr := '';
- itsSecondsStr := '';
-
- itsPlayControls := nil;
- itsPauseIcon := nil;
- itsResumeIcon := nil;
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount] := nil;
- end; { for }
-
- itsTargetPICTIDs := nil;
-
- IDirector(aSupervisor);
-
- fPlaying := kNotPlaying;
- fPaused := kNotPaused;
-
- itsStartLevel := kNoLevel;
-
- { At this point, the application and prefs file are well and truly established. }
-
- InitAnimationAndSound;
-
- { Because we are going to inspect the pictures ourselves, the preload bit }
- { on all targets should be FALSE. }
-
- GetPICTIDsForTargets;
-
- { Array indices are 1.. itsNumTargetPICTs. For something different, use zero.}
- itsLastPICTindex := 0;
-
- { Now build the window. }
-
- BuildWindowAndMainPane;
- BuildTitleScreenPanes;
- BuildTiles;
- BuildGameControls;
- BuildGameDataPanes;
- end; { IShGameDirector }
-
-
- {****************************************************}
- {}
- { Free }
- {}
- { Destruction of the game director object. We set our pointers to nil, for the }
- { objects to which they were pointing will be disposed of in inherited methods. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.Free;
-
- var
- theTileCount: TileRange;
-
- begin { Free }
- itsShApp := nil;
-
- { The window disposes of all the subviews when it is freed. }
- { For completeness, we set our pointers to nil. }
-
- itsMainPicture := nil;
-
- itsLevelsPane := nil;
- itsLevelsTable := nil;
- itsStartButton := nil;
-
- itsNameText := nil;
- itsMovesText := nil;
- itsHoursText := nil;
- itsMinutesText := nil;
- itsSecondsText := nil;
-
- itsPlayControls := nil;
- itsPauseIcon := nil;
- itsResumeIcon := nil;
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount] := nil;
- end; { for }
-
- ForgetHandle(itsTargetPICTIDs);
-
- inherited Free;
- end; { Free }
-
-
- {****************************************************}
- {}
- { DoCommand }
- {}
- { The game director is responsible for reacting to game controls. }
- { It also checks for the Help item being selected from the Help Menu. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.DoCommand (theCommand: longint);
-
- procedure DoConfigurePlayControls;
-
- begin { DoConfigurePlayControls }
- if fPlaying then begin
- itsPlayControls.Show;
- if fPaused then begin
- itsResumeIcon.Show;
- itsPauseIcon.Hide;
- end { if }
- else begin
- itsPauseIcon.Show;
- itsResumeIcon.Hide;
- end; { else }
- end { if }
- else begin
- itsPlayControls.Hide;
- end; { else }
- end; { DoConfigurePlayControls }
-
- procedure DoSpecifyRandomTarget (aRedraw: Boolean);
-
- var
- theIndex: Integer;
- theTargetID: Integer;
-
- theTileCount: TileRange;
-
- theLRect: LongRect;
- theQDRect: Rect;
-
- begin { DoSpecifyRandomTarget }
- SetCursor(gWatchCursor^^);
-
- theIndex := Abs(Random) mod itsNumTargetPICTs + 1;
- if theIndex = itsLastPICTindex then begin
- { This is pretty unlikely (and unlucky). Cycle to the next one along. }
- theIndex := (theIndex + 1) mod itsNumTargetPICTs + 1;
- end; { if }
-
- {$PUSH}
- {$R-}
- theTargetID := itsTargetPICTIDs^^[theIndex];
- {$POP}
- itsLastPICTindex := theIndex;
-
- { Do the blitting. This is the slow part. }
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount].SetTargetPICT(theTargetID);
- end; { for }
-
- { Now redraw. }
- if aRedraw then begin
- { We can call DrawAll directly, rather than using Refresh and the usual }
- { window Update method, because we don't have to worry about the }
- { tile having subviews. This is faster and reduces flicker. }
-
- for theTileCount := 1 to kMaxTiles do begin
- { Only redraw those tiles which are actually showing the picture. }
-
- if itsTiles[theTileCount].GetState = Showing then begin
- itsTiles[theTileCount].GetFrame(theLRect);
- itsTiles[theTileCount].FrameToWindR(theLRect, theQDRect);
- itsTiles[theTileCount].DrawAll(theQDRect);
- end; { if }
- end; { for }
- end; { if }
- end; { DoSpecifyRandomTarget }
-
- procedure DoSetTilesToStart (aRedraw: Boolean);
-
- var
- theStartConfig: ConfigType;
-
- theTileCount: TileRange;
-
- theLRect: LongRect;
- theQDRect: Rect;
-
- begin { DoSetTilesToStart }
- theStartConfig := CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel)).GetStart;
- for theTileCount := 1 to kMaxTiles do begin
- if theStartConfig[theTileCount] then begin
- itsTiles[theTileCount].SetState(Hiding);
- end { if }
- else begin
- itsTiles[theTileCount].SetState(Showing);
- end; { else }
- end; { for }
-
- { Now redraw. }
- if aRedraw then begin
- { We can call DrawAll directly, rather than using Refresh and the usual }
- { window Update method, because we don't have to worry about the }
- { tile having subviews. This is faster and reduces flicker. }
-
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount].GetFrame(theLRect);
- itsTiles[theTileCount].FrameToWindR(theLRect, theQDRect);
- itsTiles[theTileCount].DrawAll(theQDRect);
- end; { for }
- end; { if }
- end; { DoSetTilesToStart }
-
- procedure DoStartGame;
-
- var
- theTileCount: TileRange;
-
- begin { DoStartGame }
- fPlaying := kPlaying;
- fPaused := kNotPaused;
-
- { Play the start game sound. }
- SATSoundPlay(gSoundGameStarting, 9, TRUE);
- SATSoundEvents;
-
- itsLevelsPane.Hide;
- itsStartButton.Hide;
-
- if gSystem.hasColorQD then begin
- itsMainPicture.UsePICT(PICTGameColour);
- end { if }
- else begin
- itsMainPicture.UsePICT(PICTGameBW);
- end; { else }
- itsMainPicture.Refresh;
-
- { Specify, but don't draw yet. This will be done }
- { in the window's Update method. }
- DoSpecifyRandomTarget(FALSE);
-
- { Set the tiles, but don't draw yet. This will be done }
- { in the window's Update method. }
- DoSetTilesToStart(FALSE);
- for theTileCount := 1 to kMaxTiles do begin
- { Showing the tiles refreshes them also. }
- itsTiles[theTileCount].Show;
- end; { for }
-
- DoConfigurePlayControls;
- itsPlayData.Show;
-
- itsNameText.Show;
- itsNameText.SetTextString(CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel)).GetName);
-
- { Initialize the clock. }
-
- itsLastStartTime := TickCount;
- itsTimeBeforeLastStart := 0;
- itsTimeSinceLastStart := 0;
- itsTimeSinceLastStartOld := 0;
-
- itsHoursStr := '0';
- itsHoursText.SetTextString(itsHoursStr);
- itsHoursText.Show;
-
- itsMinutesStr := '00';
- itsMinutesText.SetTextString(itsMinutesStr);
- itsMinutesText.Show;
-
- itsSecondsStr := '00';
- itsSecondsText.SetTextString(itsSecondsStr);
- itsSecondsText.Show;
-
- { Initialize the number of moves made. }
-
- itsMovesText.Show;
- itsMoves := 0;
- itsMovesText.SetTextString('0');
-
- itsWindow.Update;
- end; { DoStartGame }
-
- procedure DoPauseGame;
-
- var
- theTileCount: TileRange;
-
- begin { DoPauseGame }
- itsTimeBeforeLastStart := (itsTimeBeforeLastStart - itsLastStartTime + TickCount) mod kLoopTime;
-
- fPaused := kPaused;
-
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount].SetWantsClicks(FALSE);
- end; { for }
-
- DoConfigurePlayControls;
-
- itsWindow.Update;
- end; { DoPauseGame }
-
- procedure DoResumeGame;
-
- var
- theTileCount: TileRange;
-
- begin { DoResumeGame }
- itsLastStartTime := TickCount;
- itsTimeSinceLastStart := 0;
- itsTimeSinceLastStartOld := 0;
-
- fPaused := kNotPaused;
-
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount].SetWantsClicks(TRUE);
- end; { for }
-
- DoConfigurePlayControls;
-
- itsWindow.Update;
- end; { DoResumeGame }
-
- procedure DoAbortGame;
-
- begin { DoAbortGame }
- fPlaying := kNotPlaying;
- fPaused := kNotPaused;
-
- { Play the game abort sound. }
- SATSoundPlay(gSoundGameAborted, 9, TRUE);
- SATSoundEvents;
-
- DisplayTitleScreen(FALSE);
- end; { DoAbortGame }
-
- procedure DoAboutAndHelp (aCommand: LongInt);
-
- begin { DoAboutAndHelp }
- if fPaused then begin
- inherited DoCommand(aCommand); { Pass onto application. }
- end { if }
- else begin
- DoPauseGame;
- inherited DoCommand(aCommand); { Pass onto application. }
- DoResumeGame;
- end; { else }
- end; { DoAboutAndHelp }
-
- procedure DoSetAnimationOrSound;
-
- var
- theFlagHandle: BooleanHandle;
-
- begin { DoSetAnimationOrSound }
- theFlagHandle := nil;
-
- SetCriticalOperation(TRUE);
- theFlagHandle := BooleanHandle(NewHandleCanFail(SizeOf(Boolean)));
- FailNIL(theFlagHandle);
- SetCriticalOperation(FALSE);
-
- if theCommand = cmdAnimationOn then begin
- fAnimate := not fAnimate;
- theFlagHandle^^ := fAnimate;
- itsShApp.PreferencesFile.SetPref(kPrefAnimationOn, Handle(theFlagHandle));
- end { if }
- else begin
- fSound := not fSound;
- theFlagHandle^^ := fSound;
- itsShApp.PreferencesFile.SetPref(kPrefSoundOn, Handle(theFlagHandle));
-
- if fSound then begin
- SATSoundOn;
- end { if }
- else begin
- SATSoundOff;
- end; { else }
- end; { else }
-
- ForgetHandle(theFlagHandle);
- end; { DoSetAnimationOrSound }
-
- begin { DoCommand }
- if HiWord(-theCommand) = kHMHelpMenuID then begin
- DoAboutAndHelp(cmdHelp);
- end { if }
- else begin
- case theCommand of
-
- cmdAbout, cmdHelp: begin
- DoAboutAndHelp(theCommand);
- end; { cmdAbout, cmdHelp }
-
- cmdStartGame: begin
- DoStartGame;
- end; { cmdStartGame }
-
- cmdPauseGame: begin
- DoPauseGame;
- end; { cmdPauseGame }
-
- cmdResumeGame: begin
- DoResumeGame;
- end; { cmdResumeGame }
-
- cmdAbortGame: begin
- DoAbortGame;
- end; { cmdAbortGame }
-
- cmdClickStart: begin
- itsStartButton.SimulateClick;
- end; { cmdClickStart }
-
- cmdAnimationOn, cmdSoundOn: begin
- DoSetAnimationOrSound;
- end; { cmdAnimationOn, cmdSoundOn }
-
- cmdChangeTargetPicture: begin
- DoSpecifyRandomTarget(TRUE);
- end; { cmdChangeTargetPicture }
-
- cmdResetTiles: begin
- DoSetTilesToStart(TRUE);
- end; { cmdResetTiles }
-
- otherwise begin
- inherited DoCommand(theCommand); { Invoke inherited method to handle other commands }
- end; { otherwise }
- end; { case}
- end; { else }
- end; { DoCommand }
-
-
- {****************************************************}
- {}
- { DoKeyDown }
- {}
- { Check for the help key being pressed. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.DoKeyDown (theChar: char;
- keyCode: Byte;
- macEvent: EventRecord);
-
- begin { DoKeyDown }
- if keyCode = KeyHelp then begin
- DoCommand(cmdHelp);
- end; { if }
- inherited DoKeyDown(theChar, keyCode, macEvent);
- end; { DoKeyDown }
-
-
- {****************************************************}
- {}
- { DoTileClicked }
- {}
- { Respond to a click on the given tile. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.DoTileClicked (aTileNum: TileRange);
-
- procedure FlipTiles;
-
- var
- theRule: TileRuleType;
-
- theTileCount: TileRange;
-
- ignoreTime: LongInt;
-
- begin { FlipTiles }
- theRule := CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel)).GetRules[aTileNum];
- with theRule do begin
- for theTileCount := 1 to theNumDep do begin
- itsTiles[theDep[theTileCount]].DoFlip(fAnimate);
- Dawdle(ignoreTime); { Give some time to update the clock. }
- end; { for }
- end; { with }
- end; { FlipTiles }
-
- function TargetIsShowing: Boolean;
-
- var
- theResult: Boolean;
-
- theTileCount: TileRange;
-
- begin { TargetIsShowing }
- theResult := TRUE;
-
- { Strictly, we shouldn't use a for loop, because it can terminate early. }
- { However, the difference isn't all that much - and I don't want to use Leave. }
-
- for theTileCount := 1 to kMaxTiles do begin
- theResult := theResult & (itsTiles[theTileCount].GetState = Showing);
- end; { for }
-
- TargetIsShowing := theResult;
- end; { TargetIsShowing }
-
- procedure UpdateMovesCounter;
-
- var
- theCountStr: Str255;
-
- begin { UpdateMovesCounter }
- itsMoves := itsMoves + 1;
- NumToString(itsMoves, theCountStr);
- itsMovesText.SetTextString(theCountStr);
-
- itsWindow.Update;
- end; { UpdateMovesCounter }
-
- procedure GameCompleted;
-
- const
- kEndGameDelay = 60;
-
- var
- theDelayStart: LongInt;
-
- theLevel: CShLevel;
-
- theTileCount: TileRange;
- theBestDirector: CShBestPlayDirector;
- theName: Str15;
-
- begin { GameCompleted }
- { Stop the clock. }
-
- itsTimeSinceLastStart := (TickCount - itsLastStartTime) mod kLoopTime;
-
- fPlaying := kNotPlaying;
- fPaused := kNotPaused;
-
- { Play the game complete sound. }
- SATSoundPlay(gSoundGameComplete, 9, TRUE);
- SATSoundEvents;
-
- { Update the clock. }
- itsWindow.Update;
-
- { Let the user have some time to admire their handiwork. }
- theDelayStart := TickCount;
- while TickCount < theDelayStart + kEndGameDelay do begin
- end; { while }
-
- theLevel := CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel));
-
- { Best criteria is the number of moves. This way, equal awards }
- { for having animation on or off. }
- if itsMoves < theLevel.GetMoves then begin
-
- { Get the player's name. }
-
- new(theBestDirector);
- theBestDirector.IShBestPlayDirector(SELF);
- theBestDirector.GetPlayerName(theName);
- ForgetObject(theBestDirector);
-
- { First update the level concerned. }
-
- theLevel.SetPlayer(theName);
- theLevel.SetMoves(itsMoves);
- theLevel.SetTime(itsTimeBeforeLastStart + itsTimeSinceLastStart); { < maxLongInt by construction. }
-
- { We send the best player information to the application, which }
- { decides whether to store it in the preference file (default levels) }
- { or in the active document. }
-
- itsShApp.StoreBestPlayer(itsStartLevel, theName, itsMoves, itsTimeBeforeLastStart + itsTimeSinceLastStart);
-
- end; { if }
- end; { GameCompleted }
-
- begin { DoTileClicked }
- FlipTiles;
- UpdateMovesCounter;
- if TargetIsShowing then begin
- GameCompleted;
- DisplayTitleScreen(TRUE);
- end; { if }
- end; { DoTileClicked }
-
-
- {****************************************************}
- {}
- { DisplayTitleScreen }
- {}
- { Display the title screen. Possible cases are on startup, after a level is }
- { completed, or a game is aborted. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.DisplayTitleScreen (aCompletedLevel: Boolean);
-
- var
- theTileCount: TileRange;
- theStartCell: Cell;
-
- begin { DisplayTitleScreen }
-
- if gSoundWelcome <> nil then begin
- { We play the welcoming sound once and only once. }
- { It will play asynchronously. }
- if not fWelcomingSoundPlayed then begin
- SATSoundPlay(gSoundWelcome, 9, FALSE);
- SATSoundEvents;
-
- fWelcomingSoundPlayed := TRUE;
- end { if }
- else begin
- { Having played this sound, we won't need it again. }
- { We dispose of it now, as it releases a sizable chunk of memory. }
-
- { Have to play it safe, and wait for all possible sounds to end }
- { before disposing of it. It is possible that our welcoming sound }
- { may still be playing. }
-
- while not SATSoundDone do begin
- SATSoundEvents;
- end; { while }
-
- SATDisposeSound(gSoundWelcome);
- gSoundWelcome := nil;
- end; { else }
- end; { if }
-
- itsPlayControls.Hide;
- itsPlayData.Hide;
-
- for theTileCount := 1 to kMaxTiles do begin
- itsTiles[theTileCount].Hide;
- end; { for }
-
- itsLevelsPane.Show;
- itsStartButton.Show;
-
- if gSystem.hasColorQD then begin
- itsMainPicture.UsePICT(PICTTitleColour);
- end { if }
- else begin
- itsMainPicture.UsePICT(PICTTitleBW);
- end; { else }
- itsMainPicture.Refresh;
-
- { If no level has been previously played, start at the first one, otherwise }
- { select the level after the one which has been successfully played. }
- { We do this last, so that the start button is updated. }
- { Recall that cell indices are one less than "real" indices. }
-
- if itsStartLevel = kNoLevel then begin
- SetCell(theStartCell, 0, kNoLevel);
- end { if }
- else if aCompletedLevel then begin
- SetCell(theStartCell, 0, itsStartLevel);
- end { else if }
- else begin
- SetCell(theStartCell, 0, itsStartLevel - 1);
- end; { else }
-
- itsLevelsTable.SelectCell(theStartCell, FALSE, FALSE);
- itsLevelsTable.ScrollToSelection;
-
- end; { DisplayTitleScreen }
-
-
- {****************************************************}
- {}
- { UpdateMenus }
- {}
- { The game director is responsible for all commands related to playing the }
- { game, and the user preferences for animation and sound. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.UpdateMenus;
-
- begin { UpdateMenus }
- inherited UpdateMenus;
-
- if fPlaying then begin
- gBartender.EnableCmd(cmdAbortGame);
-
- if fPaused then begin
- gBartender.EnableCmd(cmdResumeGame);
- end { if }
- else begin
- gBartender.EnableCmd(cmdPauseGame);
- end; { else }
-
- gBartender.EnableCmd(cmdChangeTargetPicture);
- gBartender.EnableCmd(cmdResetTiles);
- end { if }
- else begin
- gBartender.EnableCmd(cmdStartGame);
- end; { else }
-
- gBartender.CheckMarkCmd(cmdAnimationOn, fAnimate);
- gBartender.EnableCmd(cmdAnimationOn);
- gBartender.CheckMarkCmd(cmdSoundOn, fSound);
- gBartender.EnableCmd(cmdSoundOn);
- end; { UpdateMenus }
-
-
- {****************************************************}
- {}
- { ProviderChanged }
- {}
- { The levels table communicates with the game director through the provider }
- { changed mechanism. If the list of levels has changed its selection, it records }
- { the fact and informs the start button. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.ProviderChanged (aProvider: CCollaborator;
- reason: Longint;
- info: univ Ptr);
-
- var
- theRow: LevelsRange;
- theStr255: Str255;
-
- begin { ProviderChanged }
- if reason = tableSelectionChanged then begin
-
- GetIndString(theStr255, STRlistGameMessages, kSTRGameStartIndex);
-
- theRow := itsLevelsTable.RowSelected;
- if theRow = kNoLevel then begin
- itsStartButton.SetTitle(theStr255);
- itsStartButton.Deactivate;
- end { if }
- else if theRow <> itsStartLevel then begin
- itsStartButton.SetTitle(Concat(theStr255, ' “', CShLevel(CList(itsLevelsTable.GetArray).NthItem(theRow)).GetName, '”'));
- itsStartButton.Activate;
- end; { else if }
-
- itsStartLevel := theRow;
-
- end; { if }
-
- inherited ProviderChanged(aProvider, reason, info);
- end; { ProviderChanged }
-
-
- {****************************************************}
- {}
- { Dawdle }
- {}
- { If the game is progressing, update the clock. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.Dawdle (var maxSleep: longint);
-
- var
- hoursStr, minutesStr, secondsStr: Str2;
- theStr255: Str255;
-
- procedure GetTimeStrs (aDuration: LongInt;
- var aHoursStr, aMinutesStr, aSecondsStr: Str2);
-
- var
- theStr255: Str255;
-
- begin { GetTimeStrs }
- aDuration := aDuration div 60;
- NumToString(aDuration mod 60, theStr255);
-
- aSecondsStr := Copy(theStr255, 1, Length(theStr255));
- if Length(aSecondsStr) < 2 then begin
- aSecondsStr := Concat('0', aSecondsStr);
- end; { if }
-
- aDuration := aDuration div 60;
- NumToString(aDuration mod 60, theStr255);
- aMinutesStr := Copy(theStr255, 1, Length(theStr255));
- if Length(aMinutesStr) < 2 then begin
- aMinutesStr := Concat('0', aMinutesStr);
- end; { if }
-
- aDuration := aDuration div 60;
- NumToString(aDuration, theStr255);
- aHoursStr := Copy(theStr255, 1, Length(theStr255));
- end; { GetTimeStrs }
-
- begin { Dawdle }
- if fPlaying & not fPaused then begin
- itsTimeSinceLastStart := TickCount - itsLastStartTime;
- if itsTimeSinceLastStart - itsTimeSinceLastStartOld > 60 then begin
-
- GetTimeStrs(itsTimeBeforeLastStart + itsTimeSinceLastStart, hoursStr, minutesStr, secondsStr);
- theStr255 := '';
-
- if itsHoursStr <> hoursStr then begin
- itsHoursStr := hoursStr;
- theStr255 := Copy(itsHoursStr, 1, Length(itsHoursStr));
- itsHoursText.SetTextString(theStr255);
- end; { if }
-
- if itsMinutesStr <> minutesStr then begin
- itsMinutesStr := minutesStr;
- theStr255 := Copy(itsMinutesStr, 1, Length(itsMinutesStr));
- itsMinutesText.SetTextString(theStr255);
- end; { if }
-
- if itsSecondsStr <> secondsStr then begin
- itsSecondsStr := secondsStr;
- theStr255 := Copy(itsSecondsStr, 1, Length(itsSecondsStr));
- itsSecondsText.SetTextString(theStr255);
- end; { if }
-
- itsTimeSinceLastStartOld := itsTimeSinceLastStart;
- end; { if }
- maxSleep := kActivePlaySleepTime;
- end; { if }
- end; { Dawdle }
-
-
- {****************************************************}
- {}
- { SetLevels }
- {}
- { Installs the specified list of levels in the table. }
- {}
- {****************************************************}
-
- procedure CShGameDirector.SetLevels (aLevels: CList);
-
- begin { SetLevels }
- itsLevelsTable.SetArray(aLevels, TRUE);
-
- { If a new set of levels is in place, then it is effectively like starting out again. }
- { ie no level has been previously played. }
-
- itsStartLevel := kNoLevel;
- end; { SetLevels }
-
-
- {****************************************************}
- {}
- { IsPlaying }
- {}
- { Returns TRUE if playing, FALSE otherwise. }
- {}
- {****************************************************}
-
- function CShGameDirector.IsPlaying: Boolean;
-
- begin { IsPlaying }
- IsPlaying := fPlaying;
- end; { IsPlaying }
-
- end. { CShGameDirector }