Subversion Repositories plumbers

Rev

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