Subversion Repositories plumbers

Rev

Rev 12 | Rev 17 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit Main;
  2.  
  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
  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.
  9.  
  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.  
  21. interface
  22.  
  23. uses
  24.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  25.   Dialogs, ExtCtrls, StdCtrls, Game, MPlayer;
  26.  
  27. type
  28.   TMainForm = class(TForm)
  29.     Image1: TImage;
  30.     Panel1: TPanel;
  31.     StartupTimer: TTimer;
  32.     MediaPlayer1: TMediaPlayer;
  33.     Panel2: TPanel;
  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);
  45.     procedure cbWait(ASender: TGame; AMilliseconds: integer);
  46.     procedure cbSetHotspot(ASender: TGame; AIndex: THotspotIndex; AHotspot: THotspot);
  47.     procedure cbClearHotspots(ASender: TGame);
  48.     procedure ClickEvent(X, Y: Integer);
  49.   public
  50.     game: TGame;
  51.   end;
  52.  
  53. var
  54.   MainForm: TMainForm;
  55.  
  56. implementation
  57.  
  58. {$R *.dfm}
  59.  
  60. uses
  61.   MMSystem, IniFiles, Math;
  62.  
  63. procedure Delay(const Milliseconds: DWord);
  64. var
  65.   FirstTickCount: DWord;
  66. begin
  67.   FirstTickCount := GetTickCount; // TODO: Attention, GetTickCount can overflow
  68.   while not Application.Terminated and ((GetTickCount - FirstTickCount) < Milliseconds) do
  69.   begin
  70.     Application.ProcessMessages;
  71.     Sleep(0);
  72.   end;
  73. end;
  74.  
  75. function AddThouSeps(const S: string): string;
  76. var
  77.   LS, L2, I, N: Integer;
  78.   Temp: string;
  79. begin
  80.   // http://www.delphigroups.info/2/11/471892.html
  81.   result := S ;
  82.   LS := Length(S);
  83.   N := 1 ;
  84.   if LS > 1 then
  85.   begin
  86.     if S [1] = '-' then  // check for negative value
  87.     begin
  88.       N := 2;
  89.       LS := LS - 1;
  90.     end;
  91.   end;
  92.   if LS <= 3 then exit;
  93.   L2 := (LS - 1) div 3;
  94.   Temp := '';
  95.   for I := 1 to L2 do
  96.   begin
  97.     Temp := {$IF not Declared(ThousandSeparator)}FormatSettings.{$IFEND}ThousandSeparator + Copy (S, LS - 3 * I + 1, 3) + Temp;
  98.   end;
  99.   Result := Copy (S, N, (LS - 1) mod 3 + 1) + Temp;
  100.   if N > 1 then Result := '-' + Result;
  101. end;
  102.  
  103. { TMainForm }
  104.  
  105. procedure TMainForm.cbPictureShow(ASender: TGame; AFilename: string; AType: TPictureType);
  106. resourcestring
  107.   S_YOUR_SCORE = 'Your score is: %s';
  108. begin
  109.   if FileExists(AFilename) then
  110.   begin
  111.     Image1.Visible := false;
  112.     try
  113.       Image1.Picture.LoadFromFile(AFilename);
  114.       Image1.Autosize := true;
  115.     finally
  116.       // This speeds up the picture loading on very old computers
  117.       Image1.Visible := true;
  118.     end;
  119.  
  120.     // Make form bigger if necessary
  121.     if Image1.Width > ClientWidth then
  122.     begin
  123.       ClientWidth := Min(Image1.Width, Screen.Width);
  124.       if (ClientWidth >= Screen.Width) then FullscreenMode := true;
  125.       Position := poScreenCenter;
  126.     end;
  127.     if Image1.Height > ClientHeight then
  128.     begin
  129.       ClientHeight := Min(Image1.Height, Screen.Height);
  130.       if (ClientHeight >= Screen.Height) then FullscreenMode := true;
  131.       Position := poScreenCenter;
  132.     end;
  133.  
  134.     // Center image
  135.     Image1.Left := ClientWidth div 2 - Image1.Width div 2;
  136.     Image1.Top := ClientHeight div 2 - Image1.Height div 2;
  137.   end
  138.   else
  139.   begin
  140.     Image1.Picture := nil;
  141.   end;
  142.  
  143.   if FullScreenMode then
  144.   begin
  145.     BorderStyle := bsNone;
  146.     FormStyle := fsStayOnTop;
  147.     Case AType of
  148.       ptDia: Screen.Cursor := -1;
  149.       ptDecision: Screen.Cursor := 0;
  150.     End;
  151.   end;
  152.  
  153.   Panel1.Caption := Format(S_YOUR_SCORE, [AddThouSeps(IntToStr(ASender.Score))]);
  154.   Panel1.Left := 8;
  155.   Panel1.Top := Min(ClientHeight, Screen.Height) - Panel1.Height - 8;
  156.   Panel1.Visible := AType = ptDecision;
  157. end;
  158.  
  159. procedure TMainForm.cbAsyncSound(ASender: TGame; AFilename: string);
  160. begin
  161.   PlaySound(nil, hinstance, 0);
  162.   if FileExists(AFilename) then
  163.   begin
  164.     PlaySound(PChar(AFilename), hinstance, SND_FILENAME or SND_ASYNC);
  165.   end;
  166. end;
  167.  
  168. procedure TMainForm.cbSetHotspot(ASender: TGame; AIndex: THotspotIndex; AHotspot: THotspot);
  169. begin
  170.   FHotspots[AIndex] := AHotspot;
  171. end;
  172.  
  173. procedure TMainForm.cbClearHotspots(ASender: TGame);
  174. var
  175.   i: Integer;
  176. begin
  177.   for i := Low(FHotspots) to High(FHotspots) - 1 do
  178.   begin
  179.     FHotspots[i].lpAction := nil;
  180.   end;
  181. end;
  182.  
  183. procedure TMainForm.cbExit(ASender: TGame);
  184. begin
  185.   Application.Terminate;
  186. end;
  187.  
  188. procedure TMainForm.cbWait(ASender: TGame; AMilliseconds: integer);
  189. begin
  190.   //Cursor := crHourglass;
  191.   try
  192.     Delay(AMilliseconds);
  193.   finally
  194.     //Cursor := crDefault;
  195.   end;
  196. end;
  197.  
  198. procedure TMainForm.FormCreate(Sender: TObject);
  199. var
  200.   ini: TMemIniFile;
  201.   iniFilename: string;
  202. begin
  203.   iniFilename := ChangeFileExt(ExtractFileName(ParamStr(0)), '.ini');
  204.  
  205.   DoubleBuffered := true;
  206.  
  207.   if FileExists(iniFilename) then
  208.   begin
  209.     ini := TMemIniFile.Create(iniFilename);
  210.     try
  211.       Caption := ini.ReadString('Config', 'Title', '');
  212.     finally
  213.       FreeAndNil(ini);
  214.     end;
  215.   end;
  216.  
  217.   try
  218.     StartupTimer.Enabled := true;
  219.   except
  220.     Application.Terminate;
  221.   end;
  222. end;
  223.  
  224. procedure TMainForm.FormDestroy(Sender: TObject);
  225. begin
  226.   // Without this, some audio drivers could crash if you press ESC to end the game.
  227.   // (VPC 2007 with Win95; cpsman.dll crashes sometimes)
  228.   PlaySound(nil, hinstance, 0);
  229. end;
  230.  
  231. procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
  232.   Shift: TShiftState);
  233. begin
  234.   if Key = VK_SPACE then
  235.   begin
  236.     if MediaPlayer1.Mode = mpPlaying then MediaPlayer1.Stop;
  237.   end;
  238.   if Key = VK_ESCAPE then Close;
  239. end;
  240.  
  241. procedure TMainForm.ClickEvent(X, Y: Integer);
  242. var
  243.   i: integer;
  244. begin
  245.   // If hotspots are overlaying, the lowest action will be chosen (same behavior as original game)
  246.   for i := Low(FHotspots) to High(FHotspots) do
  247.   begin
  248.     if Assigned(FHotspots[i].lpAction) and
  249.        (X >= FHotspots[i].cHotspotTopLeft.X) and
  250.        (Y >= FHotspots[i].cHotspotTopLeft.Y) and
  251.        (X <= FHotspots[i].cHotspotBottomRight.X) and
  252.        (Y <= FHotspots[i].cHotspotBottomRight.Y) then
  253.     begin
  254.       FHotspots[i].Game.PerformAction(FHotspots[i].lpAction);
  255.       Exit;
  256.     end;
  257.   end;
  258. end;
  259.  
  260. procedure TMainForm.ControlClick(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  261. begin
  262.   {$IFDEF HOTSPOT_RELATIVE_ORIGIN}
  263.   ClickEvent(X, Y);
  264.   {$ELSE}
  265.   ClickEvent(X+TControl(Sender).Left, Y+TControl(Sender).Top);
  266.   {$ENDIF}
  267. end;
  268.  
  269. procedure TMainForm.StartupTimerTimer(Sender: TObject);
  270. begin
  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;
  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;
  335. end;
  336.  
  337. end.
  338.