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