Subversion Repositories plumbers

Rev

Rev 22 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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