Rev 24 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | daniel-mar | 1 | unit Game; |
2 | |||
25 | daniel-mar | 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 | |||
2 | daniel-mar | 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; |
||
24 | daniel-mar | 42 | TWaitCallback = function(Game: TGame; AMilliseconds: Cardinal): boolean of object; |
2 | daniel-mar | 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; |
||
21 | daniel-mar | 56 | FCurDecisionScene: PSceneDef; |
57 | FPrevDecisionScene: PSceneDef; |
||
2 | daniel-mar | 58 | procedure TryExit; |
59 | procedure PrevDecisionScene; |
||
60 | protected |
||
61 | GameData: TGameBinFile; |
||
19 | daniel-mar | 62 | function Wait(AMilliseconds: integer): boolean; |
2 | daniel-mar | 63 | procedure PlayScene(scene: PSceneDef; goToDecision: boolean); |
23 | daniel-mar | 64 | function WavePrefix: string; |
2 | daniel-mar | 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 | |||
23 | daniel-mar | 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 | |||
2 | daniel-mar | 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 |
||
21 | daniel-mar | 123 | if Assigned(FPrevDecisionScene) then PlayScene(FPrevDecisionScene, true) |
2 | daniel-mar | 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 |
||
12 | daniel-mar | 137 | nextScene := GameData.FindScene(action^.nextSceneID); |
2 | daniel-mar | 138 | if Assigned(nextScene) then |
12 | daniel-mar | 139 | PlayScene(nextScene, action^.sceneSegment=SEGMENT_DECISION) |
2 | daniel-mar | 140 | (* |
141 | else |
||
142 | raise Exception.CreateFmt('Scene %d was not found in GAME.BIN', [action^.nextSceneID]); |
||
143 | *) |
||
144 | end; |
||
145 | end; |
||
146 | |||
19 | daniel-mar | 147 | function TGame.Wait(AMilliseconds: integer): boolean; |
2 | daniel-mar | 148 | begin |
149 | if Assigned(WaitCallback) then |
||
19 | daniel-mar | 150 | begin |
151 | result := WaitCallback(Self, AMilliseconds) |
||
152 | end |
||
2 | daniel-mar | 153 | else |
19 | daniel-mar | 154 | begin |
2 | daniel-mar | 155 | Sleep(AMilliseconds); |
19 | daniel-mar | 156 | result := false; // don't cancel |
157 | end; |
||
2 | daniel-mar | 158 | end; |
159 | |||
23 | daniel-mar | 160 | function TGame.WavePrefix: string; |
161 | begin |
||
162 | if Supports16BitWaveout then |
||
163 | result := '' |
||
164 | else |
||
165 | result := 'E'; |
||
166 | end; |
||
167 | |||
2 | daniel-mar | 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) + |
||
23 | daniel-mar | 182 | scene^.szSceneFolder + PathDelim + WavePrefix + scene^.szDialogWav); |
2 | daniel-mar | 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; |
||
19 | daniel-mar | 191 | if Wait(GameData.pictures[i].duration * 100) then |
192 | begin |
||
21 | daniel-mar | 193 | // Wait was cancelled by VK_RETURN |
19 | daniel-mar | 194 | AsyncSoundCallback(Self, ''); |
195 | break; |
||
196 | end; |
||
2 | daniel-mar | 197 | if Application.Terminated then Abort; |
198 | end; |
||
199 | end; |
||
200 | if scene^.szDecisionBmp <> '' then |
||
201 | begin |
||
21 | daniel-mar | 202 | FPrevDecisionScene := FCurDecisionScene; |
203 | FCurDecisionScene := scene; |
||
2 | daniel-mar | 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. |