Subversion Repositories plumbers

Rev

Rev 12 | Rev 17 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 12 Rev 16
1
unit Main;
1
unit Main;
2
 
2
 
3
// BUG: If you drag the window, the dia show will stop playing, but the sound continues! This makes everything out of sync.
3
// BUG: If you drag the window, the dia show will stop playing, but the sound continues! This makes everything out of sync.
4
// TODO: When the windows is only resized a little bit (A few pixels), the window should not centered
4
// TODO: When the windows is only resized a little bit (A few pixels), the window should not centered
5
//       ... Calc the width and height of ALL pictures, and then size the form to the biggest value?
5
//       ... Calc the width and height of ALL pictures, and then size the form to the biggest value?
6
//       ... or hard code the resolution in the INI file?
6
//       ... or hard code the resolution in the INI file?
7
// Idea: Ini Parameter if fullscreen is applied or not
7
// Idea: Ini Parameter if fullscreen is applied or not
8
// Idea: Savestates, speedup, pause, Use Space bar to go to the next decision point.
8
// Idea: Savestates, speedup, pause, Use Space bar to go to the next decision point.
9
 
9
 
10
// -----------------------------------------------------------------------------
10
// -----------------------------------------------------------------------------
11
 
11
 
12
// HOTSPOT_RELATIVE_ORIGIN is a new behavior which is not compatible with the original engine.
12
// HOTSPOT_RELATIVE_ORIGIN is a new behavior which is not compatible with the original engine.
13
// With HOTSPOT_RELATIVE_ORIGIN enabled, the coordinates will be relative to the picture
13
// With HOTSPOT_RELATIVE_ORIGIN enabled, the coordinates will be relative to the picture
14
// The original game has the origin at the top left corner of the screen.
14
// The original game has the origin at the top left corner of the screen.
15
// This is a problem because the game as well as the scene editor does not know the
15
// This is a problem because the game as well as the scene editor does not know the
16
// desired resolution, as it is automatically determined.
16
// desired resolution, as it is automatically determined.
17
// If we would hardcode the desired canvas (640x480) in <ExeName>.ini, then
17
// If we would hardcode the desired canvas (640x480) in <ExeName>.ini, then
18
// it would work, but then, the scene Editor can not know the desired resolution...
18
// it would work, but then, the scene Editor can not know the desired resolution...
19
{$DEFINE HOTSPOT_RELATIVE_ORIGIN}
19
{$DEFINE HOTSPOT_RELATIVE_ORIGIN}
20
 
20
 
21
interface
21
interface
22
 
22
 
23
uses
23
uses
24
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
24
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
25
  Dialogs, ExtCtrls, StdCtrls, Game;
25
  Dialogs, ExtCtrls, StdCtrls, Game, MPlayer;
26
 
26
 
27
type
27
type
28
  TMainForm = class(TForm)
28
  TMainForm = class(TForm)
29
    Image1: TImage;
29
    Image1: TImage;
30
    Panel1: TPanel;
30
    Panel1: TPanel;
31
    StartupTimer: TTimer;
31
    StartupTimer: TTimer;
-
 
32
    MediaPlayer1: TMediaPlayer;
-
 
33
    Panel2: TPanel;
32
    procedure FormCreate(Sender: TObject);
34
    procedure FormCreate(Sender: TObject);
33
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
35
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
34
    procedure StartupTimerTimer(Sender: TObject);
36
    procedure StartupTimerTimer(Sender: TObject);
35
    procedure ControlClick(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
37
    procedure ControlClick(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
36
    procedure FormDestroy(Sender: TObject);
38
    procedure FormDestroy(Sender: TObject);
37
  private
39
  private
38
    FHotspots: array[0..2] of THotspot;
40
    FHotspots: array[0..2] of THotspot;
39
    FullscreenMode: boolean;
41
    FullscreenMode: boolean;
40
    procedure cbPictureShow(ASender: TGame; AFilename: string; AType: TPictureType);
42
    procedure cbPictureShow(ASender: TGame; AFilename: string; AType: TPictureType);
41
    procedure cbAsyncSound(ASender: TGame; AFilename: string);
43
    procedure cbAsyncSound(ASender: TGame; AFilename: string);
42
    procedure cbExit(ASender: TGame);
44
    procedure cbExit(ASender: TGame);
43
    procedure cbWait(ASender: TGame; AMilliseconds: integer);
45
    procedure cbWait(ASender: TGame; AMilliseconds: integer);
44
    procedure cbSetHotspot(ASender: TGame; AIndex: THotspotIndex; AHotspot: THotspot);
46
    procedure cbSetHotspot(ASender: TGame; AIndex: THotspotIndex; AHotspot: THotspot);
45
    procedure cbClearHotspots(ASender: TGame);
47
    procedure cbClearHotspots(ASender: TGame);
46
    procedure ClickEvent(X, Y: Integer);
48
    procedure ClickEvent(X, Y: Integer);
47
  public
49
  public
48
    game: TGame;
50
    game: TGame;
49
  end;
51
  end;
50
 
52
 
51
var
53
var
52
  MainForm: TMainForm;
54
  MainForm: TMainForm;
53
 
55
 
54
implementation
56
implementation
55
 
57
 
56
{$R *.dfm}
58
{$R *.dfm}
57
 
59
 
58
uses
60
uses
59
  MMSystem, IniFiles, Math;
61
  MMSystem, IniFiles, Math;
60
 
62
 
61
procedure Delay(const Milliseconds: DWord);
63
procedure Delay(const Milliseconds: DWord);
62
var
64
var
63
  FirstTickCount: DWord;
65
  FirstTickCount: DWord;
64
begin
66
begin
65
  FirstTickCount := GetTickCount; // TODO: Attention, GetTickCount can overflow
67
  FirstTickCount := GetTickCount; // TODO: Attention, GetTickCount can overflow
66
  while not Application.Terminated and ((GetTickCount - FirstTickCount) < Milliseconds) do
68
  while not Application.Terminated and ((GetTickCount - FirstTickCount) < Milliseconds) do
67
  begin
69
  begin
68
    Application.ProcessMessages;
70
    Application.ProcessMessages;
69
    Sleep(0);
71
    Sleep(0);
70
  end;
72
  end;
71
end;
73
end;
72
 
74
 
73
function AddThouSeps(const S: string): string;
75
function AddThouSeps(const S: string): string;
74
var
76
var
75
  LS, L2, I, N: Integer;
77
  LS, L2, I, N: Integer;
76
  Temp: string;
78
  Temp: string;
77
begin
79
begin
78
  // http://www.delphigroups.info/2/11/471892.html
80
  // http://www.delphigroups.info/2/11/471892.html
79
  result := S ;
81
  result := S ;
80
  LS := Length(S);
82
  LS := Length(S);
81
  N := 1 ;
83
  N := 1 ;
82
  if LS > 1 then
84
  if LS > 1 then
83
  begin
85
  begin
84
    if S [1] = '-' then  // check for negative value
86
    if S [1] = '-' then  // check for negative value
85
    begin
87
    begin
86
      N := 2;
88
      N := 2;
87
      LS := LS - 1;
89
      LS := LS - 1;
88
    end;
90
    end;
89
  end;
91
  end;
90
  if LS <= 3 then exit;
92
  if LS <= 3 then exit;
91
  L2 := (LS - 1) div 3;
93
  L2 := (LS - 1) div 3;
92
  Temp := '';
94
  Temp := '';
93
  for I := 1 to L2 do
95
  for I := 1 to L2 do
94
  begin
96
  begin
95
    Temp := {$IF not Declared(ThousandSeparator)}FormatSettings.{$IFEND}ThousandSeparator + Copy (S, LS - 3 * I + 1, 3) + Temp;
97
    Temp := {$IF not Declared(ThousandSeparator)}FormatSettings.{$IFEND}ThousandSeparator + Copy (S, LS - 3 * I + 1, 3) + Temp;
96
  end;
98
  end;
97
  Result := Copy (S, N, (LS - 1) mod 3 + 1) + Temp;
99
  Result := Copy (S, N, (LS - 1) mod 3 + 1) + Temp;
98
  if N > 1 then Result := '-' + Result;
100
  if N > 1 then Result := '-' + Result;
99
end;
101
end;
100
 
102
 
101
{ TMainForm }
103
{ TMainForm }
102
 
104
 
103
procedure TMainForm.cbPictureShow(ASender: TGame; AFilename: string; AType: TPictureType);
105
procedure TMainForm.cbPictureShow(ASender: TGame; AFilename: string; AType: TPictureType);
104
resourcestring
106
resourcestring
105
  S_YOUR_SCORE = 'Your score is: %s';
107
  S_YOUR_SCORE = 'Your score is: %s';
106
begin
108
begin
107
  if FileExists(AFilename) then
109
  if FileExists(AFilename) then
108
  begin
110
  begin
109
    Image1.Visible := false;
111
    Image1.Visible := false;
110
    try
112
    try
111
      Image1.Picture.LoadFromFile(AFilename);
113
      Image1.Picture.LoadFromFile(AFilename);
112
      Image1.Autosize := true;
114
      Image1.Autosize := true;
113
    finally
115
    finally
114
      // This speeds up the picture loading on very old computers
116
      // This speeds up the picture loading on very old computers
115
      Image1.Visible := true;
117
      Image1.Visible := true;
116
    end;
118
    end;
117
 
119
 
118
    // Make form bigger if necessary
120
    // Make form bigger if necessary
119
    if Image1.Width > ClientWidth then
121
    if Image1.Width > ClientWidth then
120
    begin
122
    begin
121
      ClientWidth := Min(Image1.Width, Screen.Width);
123
      ClientWidth := Min(Image1.Width, Screen.Width);
122
      if (ClientWidth >= Screen.Width) then FullscreenMode := true;
124
      if (ClientWidth >= Screen.Width) then FullscreenMode := true;
123
      Position := poScreenCenter;
125
      Position := poScreenCenter;
124
    end;
126
    end;
125
    if Image1.Height > ClientHeight then
127
    if Image1.Height > ClientHeight then
126
    begin
128
    begin
127
      ClientHeight := Min(Image1.Height, Screen.Height);
129
      ClientHeight := Min(Image1.Height, Screen.Height);
128
      if (ClientHeight >= Screen.Height) then FullscreenMode := true;
130
      if (ClientHeight >= Screen.Height) then FullscreenMode := true;
129
      Position := poScreenCenter;
131
      Position := poScreenCenter;
130
    end;
132
    end;
131
 
133
 
132
    // Center image
134
    // Center image
133
    Image1.Left := ClientWidth div 2 - Image1.Width div 2;
135
    Image1.Left := ClientWidth div 2 - Image1.Width div 2;
134
    Image1.Top := ClientHeight div 2 - Image1.Height div 2;
136
    Image1.Top := ClientHeight div 2 - Image1.Height div 2;
135
  end
137
  end
136
  else
138
  else
137
  begin
139
  begin
138
    Image1.Picture := nil;
140
    Image1.Picture := nil;
139
  end;
141
  end;
140
 
142
 
141
  if FullScreenMode then
143
  if FullScreenMode then
142
  begin
144
  begin
143
    BorderStyle := bsNone;
145
    BorderStyle := bsNone;
144
    FormStyle := fsStayOnTop;
146
    FormStyle := fsStayOnTop;
145
    Case AType of
147
    Case AType of
146
      ptDia: Screen.Cursor := -1;
148
      ptDia: Screen.Cursor := -1;
147
      ptDecision: Screen.Cursor := 0;
149
      ptDecision: Screen.Cursor := 0;
148
    End;
150
    End;
149
  end;
151
  end;
150
 
152
 
151
  Panel1.Caption := Format(S_YOUR_SCORE, [AddThouSeps(IntToStr(ASender.Score))]);
153
  Panel1.Caption := Format(S_YOUR_SCORE, [AddThouSeps(IntToStr(ASender.Score))]);
152
  Panel1.Left := 8;
154
  Panel1.Left := 8;
153
  Panel1.Top := Min(ClientHeight, Screen.Height) - Panel1.Height - 8;
155
  Panel1.Top := Min(ClientHeight, Screen.Height) - Panel1.Height - 8;
154
  Panel1.Visible := AType = ptDecision;
156
  Panel1.Visible := AType = ptDecision;
155
end;
157
end;
156
 
158
 
157
procedure TMainForm.cbAsyncSound(ASender: TGame; AFilename: string);
159
procedure TMainForm.cbAsyncSound(ASender: TGame; AFilename: string);
158
begin
160
begin
159
  PlaySound(nil, hinstance, 0);
161
  PlaySound(nil, hinstance, 0);
160
  if FileExists(AFilename) then
162
  if FileExists(AFilename) then
161
  begin
163
  begin
162
    PlaySound(PChar(AFilename), hinstance, SND_FILENAME or SND_ASYNC);
164
    PlaySound(PChar(AFilename), hinstance, SND_FILENAME or SND_ASYNC);
163
  end;
165
  end;
164
end;
166
end;
165
 
167
 
166
procedure TMainForm.cbSetHotspot(ASender: TGame; AIndex: THotspotIndex; AHotspot: THotspot);
168
procedure TMainForm.cbSetHotspot(ASender: TGame; AIndex: THotspotIndex; AHotspot: THotspot);
167
begin
169
begin
168
  FHotspots[AIndex] := AHotspot;
170
  FHotspots[AIndex] := AHotspot;
169
end;
171
end;
170
 
172
 
171
procedure TMainForm.cbClearHotspots(ASender: TGame);
173
procedure TMainForm.cbClearHotspots(ASender: TGame);
172
var
174
var
173
  i: Integer;
175
  i: Integer;
174
begin
176
begin
175
  for i := Low(FHotspots) to High(FHotspots) - 1 do
177
  for i := Low(FHotspots) to High(FHotspots) - 1 do
176
  begin
178
  begin
177
    FHotspots[i].lpAction := nil;
179
    FHotspots[i].lpAction := nil;
178
  end;
180
  end;
179
end;
181
end;
180
 
182
 
181
procedure TMainForm.cbExit(ASender: TGame);
183
procedure TMainForm.cbExit(ASender: TGame);
182
begin
184
begin
183
  Application.Terminate;
185
  Application.Terminate;
184
end;
186
end;
185
 
187
 
186
procedure TMainForm.cbWait(ASender: TGame; AMilliseconds: integer);
188
procedure TMainForm.cbWait(ASender: TGame; AMilliseconds: integer);
187
begin
189
begin
188
  //Cursor := crHourglass;
190
  //Cursor := crHourglass;
189
  try
191
  try
190
    Delay(AMilliseconds);
192
    Delay(AMilliseconds);
191
  finally
193
  finally
192
    //Cursor := crDefault;
194
    //Cursor := crDefault;
193
  end;
195
  end;
194
end;
196
end;
195
 
197
 
196
procedure TMainForm.FormCreate(Sender: TObject);
198
procedure TMainForm.FormCreate(Sender: TObject);
197
var
199
var
198
  ini: TMemIniFile;
200
  ini: TMemIniFile;
199
  iniFilename: string;
201
  iniFilename: string;
200
begin
202
begin
201
  iniFilename := ChangeFileExt(ExtractFileName(ParamStr(0)), '.ini');
203
  iniFilename := ChangeFileExt(ExtractFileName(ParamStr(0)), '.ini');
202
 
204
 
203
  DoubleBuffered := true;
205
  DoubleBuffered := true;
204
 
206
 
205
  if FileExists(iniFilename) then
207
  if FileExists(iniFilename) then
206
  begin
208
  begin
207
    ini := TMemIniFile.Create(iniFilename);
209
    ini := TMemIniFile.Create(iniFilename);
208
    try
210
    try
209
      Caption := ini.ReadString('Config', 'Title', '');
211
      Caption := ini.ReadString('Config', 'Title', '');
210
    finally
212
    finally
211
      FreeAndNil(ini);
213
      FreeAndNil(ini);
212
    end;
214
    end;
213
  end;
215
  end;
214
 
216
 
215
  try
217
  try
216
    Game := TGame.Create('.');
-
 
217
    Game.PictureShowCallback := cbPictureShow;
-
 
218
    Game.AsyncSoundCallback := cbAsyncSound;
-
 
219
    Game.ExitCallback := cbExit;
-
 
220
    Game.WaitCallback := cbWait;
-
 
221
    Game.SetHotspotCallback := cbSetHotspot;
-
 
222
    Game.ClearHotspotsCallback := cbClearHotspots;
-
 
223
    StartupTimer.Enabled := true;
218
    StartupTimer.Enabled := true;
224
  except
219
  except
225
    Application.Terminate;
220
    Application.Terminate;
226
  end;
221
  end;
227
end;
222
end;
228
 
223
 
229
procedure TMainForm.FormDestroy(Sender: TObject);
224
procedure TMainForm.FormDestroy(Sender: TObject);
230
begin
225
begin
231
  // Without this, some audio drivers could crash if you press ESC to end the game.
226
  // Without this, some audio drivers could crash if you press ESC to end the game.
232
  // (VPC 2007 with Win95; cpsman.dll crashes sometimes)
227
  // (VPC 2007 with Win95; cpsman.dll crashes sometimes)
233
  PlaySound(nil, hinstance, 0);
228
  PlaySound(nil, hinstance, 0);
234
end;
229
end;
235
 
230
 
236
procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
231
procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
237
  Shift: TShiftState);
232
  Shift: TShiftState);
238
begin
233
begin
-
 
234
  if Key = VK_SPACE then
-
 
235
  begin
-
 
236
    if MediaPlayer1.Mode = mpPlaying then MediaPlayer1.Stop;
-
 
237
  end;
239
  if Key = VK_ESCAPE then Close;
238
  if Key = VK_ESCAPE then Close;
240
end;
239
end;
241
 
240
 
242
procedure TMainForm.ClickEvent(X, Y: Integer);
241
procedure TMainForm.ClickEvent(X, Y: Integer);
243
var
242
var
244
  i: integer;
243
  i: integer;
245
begin
244
begin
246
  // If hotspots are overlaying, the lowest action will be chosen (same behavior as original game)
245
  // If hotspots are overlaying, the lowest action will be chosen (same behavior as original game)
247
  for i := Low(FHotspots) to High(FHotspots) do
246
  for i := Low(FHotspots) to High(FHotspots) do
248
  begin
247
  begin
249
    if Assigned(FHotspots[i].lpAction) and
248
    if Assigned(FHotspots[i].lpAction) and
250
       (X >= FHotspots[i].cHotspotTopLeft.X) and
249
       (X >= FHotspots[i].cHotspotTopLeft.X) and
251
       (Y >= FHotspots[i].cHotspotTopLeft.Y) and
250
       (Y >= FHotspots[i].cHotspotTopLeft.Y) and
252
       (X <= FHotspots[i].cHotspotBottomRight.X) and
251
       (X <= FHotspots[i].cHotspotBottomRight.X) and
253
       (Y <= FHotspots[i].cHotspotBottomRight.Y) then
252
       (Y <= FHotspots[i].cHotspotBottomRight.Y) then
254
    begin
253
    begin
255
      FHotspots[i].Game.PerformAction(FHotspots[i].lpAction);
254
      FHotspots[i].Game.PerformAction(FHotspots[i].lpAction);
256
      Exit;
255
      Exit;
257
    end;
256
    end;
258
  end;
257
  end;
259
end;
258
end;
260
 
259
 
261
procedure TMainForm.ControlClick(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
260
procedure TMainForm.ControlClick(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
262
begin
261
begin
263
  {$IFDEF HOTSPOT_RELATIVE_ORIGIN}
262
  {$IFDEF HOTSPOT_RELATIVE_ORIGIN}
264
  ClickEvent(X, Y);
263
  ClickEvent(X, Y);
265
  {$ELSE}
264
  {$ELSE}
266
  ClickEvent(X+TControl(Sender).Left, Y+TControl(Sender).Top);
265
  ClickEvent(X+TControl(Sender).Left, Y+TControl(Sender).Top);
267
  {$ENDIF}
266
  {$ENDIF}
268
end;
267
end;
269
 
268
 
270
procedure TMainForm.StartupTimerTimer(Sender: TObject);
269
procedure TMainForm.StartupTimerTimer(Sender: TObject);
271
begin
270
begin
272
  StartupTimer.Enabled := false;
271
  StartupTimer.Enabled := false;
-
 
272
 
-
 
273
  if FileExists('INTRO.AVI') then
-
 
274
  begin
-
 
275
    MediaPlayer1.FileName := 'INTRO.AVI';
-
 
276
    MediaPlayer1.Open;
-
 
277
 
-
 
278
    Panel2.Visible := true;
-
 
279
    Panel2.Top := 0;
-
 
280
    Panel2.Left := 0;
-
 
281
    Panel2.Width  := MediaPlayer1.DisplayRect.Right;
-
 
282
    Panel2.Height := MediaPlayer1.DisplayRect.Bottom;
-
 
283
 
-
 
284
    ClientWidth := Panel2.Width;
-
 
285
    if (ClientWidth >= Screen.Width) then FullscreenMode := true;
-
 
286
    ClientHeight := Panel2.Height;
-
 
287
    if (ClientHeight >= Screen.Height) then FullscreenMode := true;
-
 
288
    Position := poScreenCenter;
-
 
289
 
-
 
290
    if FullScreenMode then
-
 
291
    begin
-
 
292
      BorderStyle := bsNone;
-
 
293
      FormStyle := fsStayOnTop;
-
 
294
      Screen.Cursor := -1;
-
 
295
    end;
-
 
296
 
-
 
297
    // For some reason, "Position := poScreenCenter" causes the video handle to break!
-
 
298
    // we need to close+open it again!
-
 
299
    MediaPlayer1.Close;
-
 
300
    MediaPlayer1.Open;
-
 
301
 
-
 
302
    MediaPlayer1.Play;
-
 
303
    while MediaPlayer1.Mode <> mpStopped do
-
 
304
    begin
-
 
305
      Sleep(100);
-
 
306
      Application.ProcessMessages;
-
 
307
      if Application.Terminated then break;
-
 
308
    end;
-
 
309
 
-
 
310
    MediaPlayer1.Close;
-
 
311
    Panel2.Visible := false;
-
 
312
    Screen.Cursor := 0;
-
 
313
  end;
-
 
314
 
-
 
315
  try
-
 
316
    Game := TGame.Create('.');
-
 
317
    try
-
 
318
      Game.PictureShowCallback := cbPictureShow;
-
 
319
      Game.AsyncSoundCallback := cbAsyncSound;
-
 
320
      Game.ExitCallback := cbExit;
-
 
321
      Game.WaitCallback := cbWait;
-
 
322
      Game.SetHotspotCallback := cbSetHotspot;
-
 
323
      Game.ClearHotspotsCallback := cbClearHotspots;
273
  Game.Run;
324
      Game.Run;
-
 
325
    finally
-
 
326
      FreeAndNil(Game);
-
 
327
    end;
-
 
328
  except
-
 
329
    on E: Exception do
-
 
330
    begin
-
 
331
      MessageDlg(E.Message, mtError, [mbOK], 0);
-
 
332
      Close;
-
 
333
    end;
-
 
334
  end;
274
end;
335
end;
275
 
336
 
276
end.
337
end.
277
 
338