Subversion Repositories plumbers

Rev

Rev 24 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit Game;
  2.  
  3. (*
  4.  * Plumbers Don't Wear Ties - Inofficial Win32 Player
  5.  * Copyright 2017 - 2020 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  *)
  19.  
  20. interface
  21.  
  22. uses
  23.   SysUtils, Classes, Forms, GameBinStruct;
  24.  
  25. type
  26.   TPictureType = (ptDia, ptDecision);
  27.  
  28.   THotspotIndex = 0..2;
  29.  
  30.   TGame = class;
  31.   PHotspot = ^THotspot;
  32.   THotspot = record
  33.     game: TGame;
  34.     lpAction: PActionDef;
  35.     cHotspotTopLeft: TCoord;
  36.     cHotspotBottomRight: TCoord;
  37.   end;
  38.  
  39.   TShowPictureCallback = procedure(Game: TGame; AFilename: string; AType: TPictureType) of object;
  40.   TPlaySoundCallback = procedure(Game: TGame; AFilename: string) of object;
  41.   TSimpleCallback = procedure(Game: TGame) of object;
  42.   TWaitCallback = function(Game: TGame; AMilliseconds: Cardinal): boolean of object;
  43.   TSetHotspotCallback = procedure(Game: TGame; AIndex: THotspotIndex; AHotspot: THotspot) of object;
  44.   TClearHotspotsCallback = procedure(Game: TGame) of object;
  45.  
  46.   TGame = class(TObject)
  47.   private
  48.     FPictureShowCallback: TShowPictureCallback;
  49.     FAsyncSoundCallback: TPlaySoundCallback;
  50.     FExitCallback: TSimpleCallback;
  51.     FWaitCallback: TWaitCallback;
  52.     FSetHotspotCallback: TSetHotspotCallback;
  53.     FClearHotspotsCallback: TClearHotspotsCallback;
  54.     FDirectory: string;
  55.     FScore: integer;
  56.     FCurDecisionScene: PSceneDef;
  57.     FPrevDecisionScene: PSceneDef;
  58.     procedure TryExit;
  59.     procedure PrevDecisionScene;
  60.   protected
  61.     GameData: TGameBinFile;
  62.     function Wait(AMilliseconds: integer): boolean;
  63.     procedure PlayScene(scene: PSceneDef; goToDecision: boolean);
  64.     function WavePrefix: string;
  65.   public
  66.     procedure PerformAction(action: PActionDef);
  67.     property PictureShowCallback: TShowPictureCallback read FPictureShowCallback write FPictureShowCallback;
  68.     property AsyncSoundCallback: TPlaySoundCallback read FAsyncSoundCallback write FAsyncSoundCallback;
  69.     property ExitCallback: TSimpleCallback read FExitCallback write FExitCallback;
  70.     property WaitCallback: TWaitCallback read FWaitCallback write FWaitCallback;
  71.     property SetHotspotCallback: TSetHotspotCallback read FSetHotspotCallback write FSetHotspotCallback;
  72.     property ClearHotspotsCallback: TClearHotspotsCallback read FClearHotspotsCallback write FClearHotspotsCallback;
  73.     property Directory: string read FDirectory;
  74.     property Score: integer read FScore;
  75.     constructor Create(ADirectory: string);
  76.     procedure Run;
  77.   end;
  78.  
  79. implementation
  80.  
  81. uses
  82.   MMSystem, Windows;
  83.  
  84. function Supports16BitWaveout: boolean;
  85. var
  86.   caps: TWaveOutCaps;
  87. begin
  88.   ZeroMemory(@caps, sizeof(caps));
  89.   waveOutGetDevCaps(0, @caps, sizeof(caps));
  90.   result := caps.dwFormats and $CCCCCCCC <> 0; // Note: Original SHOWTIME.EXE only checked $0CCC
  91. end;
  92.  
  93. { TGame }
  94.  
  95. constructor TGame.Create(ADirectory: string);
  96. var
  97.   fs: TFileStream;
  98.   gameBinFilename: string;
  99. begin
  100.   FDirectory := ADirectory;
  101.  
  102.   gameBinFilename := IncludeTrailingPathDelimiter(ADirectory) + 'GAME.BIN';
  103.   if not FileExists(gameBinFilename) then
  104.   begin
  105.     raise Exception.Create('Cannot find GAME.BIN');
  106.   end;
  107.  
  108.   fs := TFileStream.Create(gameBinFilename, fmOpenRead);
  109.   try
  110.     fs.ReadBuffer(GameData, SizeOf(GameData));
  111.   finally
  112.     FreeAndNil(fs);
  113.   end;
  114. end;
  115.  
  116. procedure TGame.TryExit;
  117. begin
  118.   if Assigned(ExitCallback) then ExitCallback(Self);
  119. end;
  120.  
  121. procedure TGame.PrevDecisionScene;
  122. begin
  123.   if Assigned(FPrevDecisionScene) then PlayScene(FPrevDecisionScene, true)
  124. end;
  125.  
  126. procedure TGame.PerformAction(action: PActionDef);
  127. var
  128.   nextScene: PSceneDef;
  129. begin
  130.   Inc(FScore, action^.scoreDelta);
  131.   if action^.nextSceneID = SCENEID_PREVDECISION then
  132.     PrevDecisionScene
  133.   else if action^.nextSceneID = SCENEID_ENDGAME then
  134.     TryExit
  135.   else
  136.   begin
  137.     nextScene := GameData.FindScene(action^.nextSceneID);
  138.     if Assigned(nextScene) then
  139.       PlayScene(nextScene, action^.sceneSegment=SEGMENT_DECISION)
  140.     (*
  141.     else
  142.       raise Exception.CreateFmt('Scene %d was not found in GAME.BIN', [action^.nextSceneID]);
  143.     *)
  144.   end;
  145. end;
  146.  
  147. function TGame.Wait(AMilliseconds: integer): boolean;
  148. begin
  149.   if Assigned(WaitCallback) then
  150.   begin
  151.     result := WaitCallback(Self, AMilliseconds)
  152.   end
  153.   else
  154.   begin
  155.     Sleep(AMilliseconds);
  156.     result := false; // don't cancel
  157.   end;
  158. end;
  159.  
  160. function TGame.WavePrefix: string;
  161. begin
  162.   if Supports16BitWaveout then
  163.     result := ''
  164.   else
  165.     result := 'E';
  166. end;
  167.  
  168. procedure TGame.PlayScene(scene: PSceneDef; goToDecision: boolean);
  169. var
  170.   i: integer;
  171.   hotspot: THotspot;
  172. begin
  173.   if Assigned(ClearHotspotsCallback) then
  174.   begin
  175.     ClearHotspotsCallback(Self);
  176.   end;
  177.   if not goToDecision then
  178.   begin
  179.     if Assigned(AsyncSoundCallback) then
  180.     begin
  181.       AsyncSoundCallback(Self, IncludeTrailingPathDelimiter(FDirectory) +
  182.         scene^.szSceneFolder + PathDelim + WavePrefix + scene^.szDialogWav);
  183.     end;
  184.     for i := scene^.pictureIndex to scene^.pictureIndex + scene^.numPics - 1 do
  185.     begin
  186.       if Assigned(PictureShowCallback) then
  187.       begin
  188.         PictureShowCallback(Self, IncludeTrailingPathDelimiter(FDirectory) +
  189.           scene^.szSceneFolder + PathDelim + GameData.pictures[i].szBitmapFile, ptDia);
  190.       end;
  191.       if Wait(GameData.pictures[i].duration * 100) then
  192.       begin
  193.         // Wait was cancelled by VK_RETURN
  194.         AsyncSoundCallback(Self, '');
  195.         break;
  196.       end;
  197.       if Application.Terminated then Abort;
  198.     end;
  199.   end;
  200.   if scene^.szDecisionBmp <> '' then
  201.   begin
  202.     FPrevDecisionScene := FCurDecisionScene;
  203.     FCurDecisionScene := scene;
  204.     if Assigned(PictureShowCallback) then
  205.     begin
  206.       PictureShowCallback(Self, IncludeTrailingPathDelimiter(FDirectory) +
  207.         scene^.szSceneFolder + PathDelim + scene^.szDecisionBmp, ptDecision);
  208.     end;
  209.     if Assigned(SetHotspotCallback) then
  210.     begin
  211.       for i := 0 to scene^.numActions - 1 do
  212.       begin
  213.         hotspot.Game := Self;
  214.         hotspot.lpAction := @scene^.actions[i];
  215.         hotspot.cHotspotTopLeft.X := scene^.actions[i].cHotspotTopLeft.X;
  216.         hotspot.cHotspotTopLeft.Y := scene^.actions[i].cHotspotTopLeft.Y;
  217.         hotspot.cHotspotBottomRight.X := scene^.actions[i].cHotspotBottomRight.X;
  218.         hotspot.cHotspotBottomRight.Y := scene^.actions[i].cHotspotBottomRight.Y;        
  219.         SetHotspotCallback(Self, i, hotspot);
  220.       end;
  221.     end;
  222.   end
  223.   else
  224.   begin
  225.     if scene^.numActions > 0 then PerformAction(@scene^.actions[0]);
  226.   end;
  227. end;
  228.  
  229. procedure TGame.Run;
  230. begin
  231.   if GameData.numScenes = 0 then exit;
  232.   PlayScene(@GameData.Scenes[0], false);
  233. end;
  234.  
  235. end.
  236.