Subversion Repositories plumbers

Rev

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

  1. unit Unit1;
  2.  
  3. // TODO: the "folder open" icons look like you can CHOOSE a file, not open it!
  4. //       - change the icon to something else
  5. //       - add open-dialogs for choosing the bmp and wav files
  6. // TODO: Give controls better names
  7. // TODO: When loading the file, check if dependencies are broken and output a warning
  8. // Idea: About the hotspot / action markings:
  9. //       - anti moiree?
  10. //       - allow user to draw a rectangle with drag'n'drop instead of left and right mouse button.
  11. //         also, automatically determine the left/top and right/bottom border, so the user can draw the rectange in every direction
  12. // Idea: unused liste auch bei decision page anzeigen
  13. // Idea: Function to re-order stuff?
  14.  
  15. interface
  16.  
  17. uses
  18.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  19.   Dialogs, StdCtrls, Spin, Grids, GameBinStruct, ComCtrls, ExtCtrls, MPlayer,
  20.   Menus;
  21.  
  22. const
  23.   CUR_VER = '2020-06-08';
  24.  
  25. type
  26.   TForm1 = class(TForm)
  27.     ListBox1: TListBox;
  28.     Button1: TButton;
  29.     Button2: TButton;
  30.     Button3: TButton;
  31.     Button4: TButton;
  32.     Button5: TButton;
  33.     Button10: TButton;
  34.     PageControl2: TPageControl;
  35.     TabSheet4: TTabSheet;
  36.     Label1: TLabel;
  37.     Label2: TLabel;
  38.     SpinEdit1: TSpinEdit;
  39.     Edit1: TEdit;
  40.     Button6: TButton;
  41.     Button7: TButton;
  42.     Button8: TButton;
  43.     Button9: TButton;
  44.     ListBox2: TListBox;
  45.     TabSheet5: TTabSheet;
  46.     Label3: TLabel;
  47.     Image1: TImage;
  48.     Label10: TLabel;
  49.     Label11: TLabel;
  50.     Edit2: TEdit;
  51.     PageControl1: TPageControl;
  52.     TabSheet1: TTabSheet;
  53.     Label5: TLabel;
  54.     Label4: TLabel;
  55.     Label6: TLabel;
  56.     SpinEdit6: TSpinEdit;
  57.     SpinEdit2: TSpinEdit;
  58.     SpinEdit3: TSpinEdit;
  59.     SpinEdit4: TSpinEdit;
  60.     SpinEdit5: TSpinEdit;
  61.     ComboBox1: TComboBox;
  62.     TabSheet2: TTabSheet;
  63.     Label13: TLabel;
  64.     Label14: TLabel;
  65.     Label15: TLabel;
  66.     ComboBox2: TComboBox;
  67.     SpinEdit17: TSpinEdit;
  68.     SpinEdit18: TSpinEdit;
  69.     SpinEdit19: TSpinEdit;
  70.     SpinEdit20: TSpinEdit;
  71.     SpinEdit21: TSpinEdit;
  72.     TabSheet3: TTabSheet;
  73.     Label7: TLabel;
  74.     Label8: TLabel;
  75.     Label9: TLabel;
  76.     ComboBox3: TComboBox;
  77.     SpinEdit7: TSpinEdit;
  78.     SpinEdit8: TSpinEdit;
  79.     SpinEdit9: TSpinEdit;
  80.     SpinEdit10: TSpinEdit;
  81.     SpinEdit11: TSpinEdit;
  82.     SpinEdit12: TSpinEdit;
  83.     Image2: TImage;
  84.     Edit3: TEdit;
  85.     SpinEdit13: TSpinEdit;
  86.     Label12: TLabel;
  87.     Label16: TLabel;
  88.     Button13: TButton;
  89.     Button14: TButton;
  90.     MediaPlayer1: TMediaPlayer;
  91.     Timer1: TTimer;
  92.     GroupBox2: TGroupBox;
  93.     Button15: TButton;
  94.     Button17: TButton;
  95.     Button16: TButton;
  96.     Label18: TLabel;
  97.     Label17: TLabel;
  98.     ListBox3: TListBox;
  99.     Label19: TLabel;
  100.     Label24: TLabel;
  101.     Label25: TLabel;
  102.     Label26: TLabel;
  103.     Label27: TLabel;
  104.     Label28: TLabel;
  105.     Label29: TLabel;
  106.     Button11: TButton;
  107.     Button12: TButton;
  108.     Button18: TButton;
  109.     MainMenu1: TMainMenu;
  110.     File1: TMenuItem;
  111.     Close1: TMenuItem;
  112.     Newfile1: TMenuItem;
  113.     Savetogamebin1: TMenuItem;
  114.     Saveandtest1: TMenuItem;
  115.     N1: TMenuItem;
  116.     N2: TMenuItem;
  117.     Help1: TMenuItem;
  118.     About1: TMenuItem;
  119.     PopupMenu1: TPopupMenu;
  120.     Addtoscene1: TMenuItem;
  121.     Panel1: TPanel;
  122.     Panel2: TPanel;
  123.     Panel3: TPanel;
  124.     Image3: TImage;
  125.     Button19: TButton;
  126.     PageControl3: TPageControl;
  127.     TabSheet6: TTabSheet;
  128.     TabSheet7: TTabSheet;
  129.     Label20: TLabel;
  130.     Label21: TLabel;
  131.     Label22: TLabel;
  132.     Label23: TLabel;
  133.     Label34: TLabel;
  134.     Label35: TLabel;
  135.     Label32: TLabel;
  136.     Label33: TLabel;
  137.     Label30: TLabel;
  138.     Label31: TLabel;
  139.     Label36: TLabel;
  140.     Label37: TLabel;
  141.     CheckBox1: TCheckBox;
  142.     TabSheet8: TTabSheet;
  143.     Button20: TButton;
  144.     procedure ListBox1Click(Sender: TObject);
  145.     procedure Edit1Change(Sender: TObject);
  146.     procedure Edit2Change(Sender: TObject);
  147.     procedure SpinEdit1Change(Sender: TObject);
  148.     procedure Button3Click(Sender: TObject);
  149.     procedure FormShow(Sender: TObject);
  150.     procedure SpinEdit12Change(Sender: TObject);
  151.     procedure ActionTargetChange(Sender: TObject);
  152.     procedure ActionSpinEditsChange(Sender: TObject);
  153.     procedure Button1Click(Sender: TObject);
  154.     procedure Button10Click(Sender: TObject);
  155.     procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  156.       Y: Integer);
  157.     procedure ListBox2Click(Sender: TObject);
  158.     procedure SpinEdit13Change(Sender: TObject);
  159.     procedure Edit3Change(Sender: TObject);
  160.     procedure FormCreate(Sender: TObject);
  161.     procedure Button13Click(Sender: TObject);
  162.     procedure Button14Click(Sender: TObject);
  163.     procedure Button2Click(Sender: TObject);
  164.     procedure Timer1Timer(Sender: TObject);
  165.     procedure Button17Click(Sender: TObject);
  166.     procedure Button15Click(Sender: TObject);
  167.     procedure Button16Click(Sender: TObject);
  168.     procedure ListBox1DblClick(Sender: TObject);
  169.     procedure ListBox2DblClick(Sender: TObject);
  170.     procedure Button4Click(Sender: TObject);
  171.     procedure Button5Click(Sender: TObject);
  172.     procedure Button6Click(Sender: TObject);
  173.     procedure Button7Click(Sender: TObject);
  174.     procedure Button8Click(Sender: TObject);
  175.     procedure Button9Click(Sender: TObject);
  176.     procedure Image1MouseLeave(Sender: TObject);
  177.     procedure ListBox3DblClick(Sender: TObject);
  178.     procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
  179.       Shift: TShiftState; X, Y: Integer);
  180.     procedure Button11Click(Sender: TObject);
  181.     procedure Button12Click(Sender: TObject);
  182.     procedure Button18Click(Sender: TObject);
  183.     procedure Image1MouseEnter(Sender: TObject);
  184.     procedure About1Click(Sender: TObject);
  185.     procedure Close1Click(Sender: TObject);
  186.     procedure Addtoscene1Click(Sender: TObject);
  187.     procedure ListBox3MouseDown(Sender: TObject; Button: TMouseButton;
  188.       Shift: TShiftState; X, Y: Integer);
  189.     procedure ListBox3Click(Sender: TObject);
  190.     procedure Button19Click(Sender: TObject);
  191.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  192.     procedure Button20Click(Sender: TObject);
  193.     procedure CheckBox1Click(Sender: TObject);
  194.   private
  195.     Game: TGameBinFile;
  196.     GameBak: TGameBinFile;
  197.     PlayStart: Cardinal;
  198.     MediaplayerOpened: boolean;
  199.     StopPlayRequest: boolean;
  200.     FirstTickCount: Cardinal;
  201.     procedure ShowSceneBmp;
  202.     function CurScene: PSceneDef;
  203.     procedure LoadScene;
  204.     procedure Load;
  205.     procedure Save;
  206.     procedure ReloadActionSceneLists;
  207.     function GetGreatestSceneID: integer;
  208.     function SearchSceneIDGapFromTop: integer;
  209.     procedure RedrawDecisionBitmap;
  210.     function CurPicture: PPictureDef;
  211.     function CurPictureTimepos: Cardinal;
  212.     procedure New;
  213.     procedure NewScene;
  214.     procedure SetupGUIAfterLoad;
  215.     procedure RecalcSlideshowLength;
  216.     procedure RecalcSoundtrackLength;
  217.     procedure RecalcUnusedFiles;
  218.     procedure RecalcStats;
  219.     procedure HandlePossibleEmptySceneList;
  220.     procedure DisableEnableSceneControls(enable: boolean);
  221.     procedure DisableEnablePictureControls(enable: boolean);
  222.     procedure DisableEnableFileControls(enable: boolean);
  223.     class procedure AspectRatio(image: TImage; panel: TControl);
  224.   end;
  225.  
  226. var
  227.   Form1: TForm1;
  228.  
  229. implementation
  230.  
  231. {$R *.dfm}
  232.  
  233. uses
  234.   Math, MMSystem, ShellAPI;
  235.  
  236. const
  237.   DEFAULT_DURATION = 0;
  238.   DEFAULT_FILENAME = 'DUMMY.BMP';
  239.  
  240. function GetSceneIDFromName(sceneName: string): Integer;
  241. resourcestring
  242.   S_SCENENAME_INVALID = 'Scene name invalid';
  243. var
  244.   sSceneID: string;
  245. begin
  246.   if Copy(sceneName, 1, 2) <> 'SC' then raise Exception.Create(S_SCENENAME_INVALID);
  247.   sSceneID := Copy(sceneName, 3, 2);
  248.   if not TryStrToInt(sSceneID, result) then raise Exception.Create(S_SCENENAME_INVALID);
  249. end;
  250.  
  251. procedure SelectFilenameBase(Edit: TEdit);
  252. begin
  253.   if Edit.CanFocus then
  254.   begin
  255.     Edit.SetFocus;
  256.     Edit.SelStart := 0;
  257.     Edit.SelLength := Pos('.', Edit.Text)-1;
  258.   end;
  259. end;
  260.  
  261. function _DecisecondToTime(ds: integer): string;
  262. begin
  263.   result := FormatDatetime('hh:nn:ss', ds / 10 / (24*60*60))+','+IntToStr(ds mod 10);
  264. end;
  265.  
  266. { TForm1 }
  267.  
  268. function TForm1.GetGreatestSceneID: integer;
  269. var
  270.   i: integer;
  271. begin
  272.   result := -1;
  273.   for i := 0 to ListBox1.Items.Count - 1 do
  274.   begin
  275.     result := Max(result, GetSceneIDFromName(ListBox1.Items.Strings[i]));
  276.   end;
  277. end;
  278.  
  279. procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  280.   Shift: TShiftState; X, Y: Integer);
  281. var
  282.   sE1, sE2, sE3, sE4: TSpinEdit;
  283.  
  284.   procedure _Go;
  285.   begin
  286.     if Button = mbLeft then
  287.     begin
  288.       sE1.Value := Round(Image1.Picture.Width*(X/Image1.Width));
  289.       sE2.Value := Round(Image1.Picture.Height*(Y/Image1.Height));
  290.     end
  291.     else if Button = mbRight then
  292.     begin
  293.       sE3.Value := Round(Image1.Picture.Width*(X/Image1.Width));
  294.       sE4.Value := Round(Image1.Picture.Height*(Y/Image1.Height));
  295.     end;
  296.   end;
  297.  
  298. begin
  299.   if PageControl1.ActivePage = TabSheet1 then
  300.   begin
  301.     sE1 := SpinEdit3;
  302.     sE2 := SpinEdit2;
  303.     sE3 := SpinEdit4;
  304.     sE4 := SpinEdit5;
  305.     _Go;
  306.   end
  307.   else if PageControl1.ActivePage = TabSheet2 then
  308.   begin
  309.     sE1 := SpinEdit17;
  310.     sE2 := SpinEdit18;
  311.     sE3 := SpinEdit19;
  312.     sE4 := SpinEdit20;
  313.     _Go;
  314.   end
  315.   else if PageControl1.ActivePage = TabSheet3 then
  316.   begin
  317.     sE1 := SpinEdit7;
  318.     sE2 := SpinEdit8;
  319.     sE3 := SpinEdit10;
  320.     sE4 := SpinEdit9;
  321.     _Go;
  322.   end;
  323. end;
  324.  
  325. procedure TForm1.Image1MouseEnter(Sender: TObject);
  326. begin
  327.   Label28.Visible := true;
  328.   Label29.Visible := true;
  329. end;
  330.  
  331. procedure TForm1.Image1MouseLeave(Sender: TObject);
  332. begin
  333.   Label10.Caption := '';
  334.   Label28.Visible := false;
  335.   Label29.Visible := false;
  336. end;
  337.  
  338. procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  339. resourcestring
  340.   S_COORDS = 'Coords: [%d, %d]';
  341. begin
  342.   label10.Caption := Format(S_COORDS, [Round(Image1.Picture.Width*(X/Image1.Width)),
  343.                                        Round(Image1.Picture.Height*(Y/Image1.Height))]);
  344. end;
  345.  
  346. function TForm1.SearchSceneIDGapFromTop: integer;
  347. var
  348.   i: integer;
  349. begin
  350.   result := -1;
  351.   for i := 99 downto 0 do
  352.   begin
  353.     if ListBox1.Items.IndexOf(Format('SC%.2d', [i])) = -1 then
  354.     begin
  355.       result := i;
  356.       exit;
  357.     end;
  358.   end;
  359. end;
  360.  
  361. procedure TForm1.Button10Click(Sender: TObject);
  362. resourcestring
  363.   S_NOTFOUND = 'GAME.BIN saved; ShowTime32.exe not found.';
  364. begin
  365.   Save;
  366.   if not FileExists('ShowTime32.exe') then raise Exception.Create(S_NOTFOUND);
  367.   ShellExecute(Application.Handle, 'open', 'ShowTime32.exe', '', '', SW_NORMAL);
  368. end;
  369.  
  370. procedure TForm1.Button11Click(Sender: TObject);
  371. var
  372.   fileName: string;
  373. begin
  374.   fileName := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + CurScene^.szDecisionBmp;
  375.   if not FileExists(fileName) then exit;
  376.   ShellExecute(Application.Handle, 'open', PChar(fileName), '', '', SW_NORMAL);
  377. end;
  378.  
  379. procedure TForm1.Button12Click(Sender: TObject);
  380. var
  381.   fileName: string;
  382. begin
  383.   fileName := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + CurPicture^.szBitmapFile;
  384.   if not FileExists(fileName) then exit;
  385.   ShellExecute(Application.Handle, 'open', Pchar(fileName), '', '', SW_NORMAL);
  386. end;
  387.  
  388. procedure TForm1.Button13Click(Sender: TObject);
  389. var
  390.   fileName: string;
  391. begin
  392.   fileName := CurScene^.szSceneFolder;
  393.   if not DirectoryExists(fileName) then MkDir(fileName);
  394.   ShellExecute(Application.Handle, 'open', 'explorer.exe', PChar(fileName), '', SW_NORMAL);
  395. end;
  396.  
  397. procedure TForm1.HandlePossibleEmptySceneList;
  398. begin
  399.   if Game.numScenes = 0 then
  400.   begin
  401.     PageControl2.ActivePage := nil;
  402.     PageControl2.Pages[0].TabVisible := false;
  403.     PageControl2.Pages[1].TabVisible := false;
  404.   end
  405.   else
  406.   begin
  407.     if PageControl2.ActivePage = nil then PageControl2.ActivePageIndex := 0;
  408.     PageControl2.Pages[0].TabVisible := true;
  409.     PageControl2.Pages[1].TabVisible := true;
  410.   end;
  411. end;
  412.  
  413. procedure TForm1.Button14Click(Sender: TObject);
  414. resourcestring
  415.   S_DISCARD = 'Are you sure you want to discard all changes and create a new game file?';
  416. begin
  417.   if MessageDlg(S_DISCARD, mtConfirmation, mbYesNoCancel, 0) = mrYes then
  418.   begin
  419.     New;
  420.   end;
  421. end;
  422.  
  423. procedure TForm1.DisableEnableSceneControls(enable: boolean);
  424. begin
  425.   ListBox1.Enabled := enable;
  426.   Button1.Enabled := enable;
  427.   Button2.Enabled := enable;
  428.   Button4.Enabled := enable;
  429.   Button5.Enabled := enable;
  430. end;
  431.  
  432. procedure TForm1.DisableEnablePictureControls(enable: boolean);
  433. begin
  434.   Button8.Enabled := enable;
  435.   Button9.Enabled := enable;
  436.   Button6.Enabled := enable;
  437.   Button7.Enabled := enable;
  438. end;
  439.  
  440. procedure TForm1.DisableEnableFileControls(enable: boolean);
  441. begin
  442.   Button3.Enabled := enable;
  443.   Button10.Enabled := enable;
  444.   Button14.Enabled := enable;
  445. end;
  446.  
  447. var
  448.   AutomaticNextDia: boolean;
  449.  
  450. procedure TForm1.Button15Click(Sender: TObject);
  451. var
  452.   decisionBMP: string;
  453.   PictureLastFrame: Cardinal;
  454.   i: integer;
  455.   DecisionPicSet: boolean;
  456. begin
  457.   if PlayStart <> 0 then
  458.   begin
  459.     if MediaplayerOpened and not AutomaticNextDia then
  460.     begin
  461.       if MediaPlayer1.Mode = mpPlaying then MediaPlayer1.Stop;
  462.       Mediaplayer1.TimeFormat := tfMilliseconds;
  463.       if CurPictureTimepos*100 < Cardinal(MediaPlayer1.Length) then
  464.       begin
  465.         MediaPlayer1.Position := CurPictureTimepos * 100;
  466.         MediaPlayer1.Play;
  467.       end;
  468.     end;
  469.     AutomaticNextDia := false;
  470.     FirstTickCount := GetTickCount;
  471.     PlayStart := GetTickCount - CurPictureTimepos * 100;
  472.   end
  473.   else
  474.   begin
  475.     TabSheet5.TabVisible := false;
  476.     DisableEnablePictureControls(false);
  477.     DisableEnableSceneControls(false);
  478.     DisableEnableFileControls(false);
  479.     Edit1.Enabled := false;
  480.     Edit3.Enabled := false;
  481.     SpinEdit1.Enabled := false;
  482.     SpinEdit13.Enabled := false;
  483.     Label18.Font.Color := clGreen;
  484.     try
  485.       Timer1.Enabled := false;
  486.  
  487.       RecalcSoundtrackLength; // optional
  488.  
  489.       Mediaplayer1.FileName := Format('%s\%s', [CurScene^.szSceneFolder, CurScene^.szDialogWav]);
  490.       if FileExists(Mediaplayer1.FileName) then
  491.       begin
  492.         Mediaplayer1.TimeFormat := tfMilliseconds;
  493.         Mediaplayer1.Open;
  494.         MediaplayerOpened := true;
  495.         if CurPictureTimepos*100 < Cardinal(MediaPlayer1.Length) then
  496.         begin
  497.           MediaPlayer1.Position := CurPictureTimepos * 100;
  498.           MediaPlayer1.Play;
  499.         end;
  500.       end
  501.       else
  502.       begin
  503.         MediaplayerOpened := false;
  504.         MediaPlayer1.FileName := '';
  505.       end;
  506.       PlayStart := GetTickCount - CurPictureTimepos * 100;
  507.       Timer1.Enabled := true;
  508.  
  509.       {$REGION 'Calculate PictureLastFrame'}
  510.       PictureLastFrame := 0;
  511.       for i := 0 to CurScene^.numPics-1 do
  512.       begin
  513.         Inc(PictureLastFrame, Game.pictures[CurScene^.pictureIndex + i].duration);
  514.       end;
  515.       PictureLastFrame := PictureLastFrame * 100;
  516.       {$ENDREGION}
  517.  
  518.       DecisionPicSet := false;
  519.       while not Application.Terminated and
  520.             not StopPlayRequest and
  521.             (
  522.                   (MediaPlayer1.Mode = mpPlaying) or
  523.                   (GetTickCount - PlayStart <= PictureLastFrame)
  524.             ) do
  525.       begin
  526.         if GetTickCount - PlayStart <= PictureLastFrame then
  527.         begin
  528.           DecisionPicSet := false;
  529.           FirstTickCount := GetTickCount;
  530.           while not Application.Terminated and ((GetTickCount - FirstTickCount) < CurPicture^.duration * 100) and not StopPlayRequest do
  531.           begin
  532.             Application.ProcessMessages;
  533.             Sleep(0);
  534.           end;
  535.  
  536.           if not Application.Terminated and (ListBox2.ItemIndex < ListBox2.Count-1) and not StopPlayRequest then
  537.           begin
  538.             ListBox2.ItemIndex := ListBox2.ItemIndex + 1;
  539.             AutomaticNextDia := true;
  540.             ListBox2Click(ListBox2);
  541.           end;
  542.         end
  543.         else
  544.         begin
  545.           if not DecisionPicSet then
  546.           begin
  547.             decisionBMP := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + CurScene^.szDecisionBmp;
  548.             if (CurScene^.szDecisionBmp <> '') and FileExists(decisionBMP) then
  549.             begin
  550.               Image2.Picture.LoadFromFile(decisionBMP);
  551.               AspectRatio(Image2, Panel2);
  552.             end
  553.             else
  554.             begin
  555.               Image2.Picture := nil;
  556.             end;
  557.             DecisionPicSet := true;
  558.           end;
  559.           Application.ProcessMessages;
  560.           Sleep(0);
  561.         end;
  562.       end;
  563.     finally
  564.       PlayStart := 0;
  565.       if MediaPlayer1.Mode = mpPlaying then MediaPlayer1.Stop;
  566.       if MediaplayerOpened then MediaPlayer1.Close;
  567.       StopPlayRequest := false;
  568.       Label18.Font.Color := clWindowText;
  569.       Timer1.Enabled := false;
  570.       DisableEnableFileControls(true);
  571.       DisableEnableSceneControls(true);
  572.       DisableEnablePictureControls(true);
  573.       TabSheet5.TabVisible := true;
  574.       Edit1.Enabled := true;
  575.       SpinEdit1.Enabled := true;
  576.       Edit3.Enabled := true;
  577.       SpinEdit13.Enabled := true;
  578.       Label18.Caption := _DecisecondToTime(CurPictureTimepos);
  579.       ListBox2Click(ListBox2);
  580.     end;
  581.   end;
  582. end;
  583.  
  584. procedure TForm1.ShowSceneBmp;
  585. var
  586.   Filename: string;
  587. begin
  588.   Filename := string(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + CurPicture^.szBitmapFile);
  589.   if FileExists(Filename) then
  590.   begin
  591.     Image2.Picture.LoadFromFile(Filename);
  592.     AspectRatio(Image2, Panel2);
  593.   end
  594.   else
  595.   begin
  596.     Image2.Picture := nil;
  597.   end;
  598. end;
  599.  
  600. procedure TForm1.Button16Click(Sender: TObject);
  601. begin
  602.   ListBox2.ItemIndex := 0; // go to the beginning
  603.   ListBox2Click(ListBox2); // load picture data
  604.   Button15Click(Button15); // play
  605. end;
  606.  
  607. procedure TForm1.Button17Click(Sender: TObject);
  608. begin
  609.   if PlayStart <> 0 then
  610.   begin
  611.     Timer1.Enabled := false;
  612.     if MediaplayerOpened then MediaPlayer1.Stop;
  613.     StopPlayRequest := true;
  614.   end;
  615. end;
  616.  
  617. procedure TForm1.Button18Click(Sender: TObject);
  618. var
  619.   fileName: string;
  620. begin
  621.   fileName := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + CurScene^.szDialogWav;
  622.   if not FileExists(fileName) then exit;
  623.   ShellExecute(Application.Handle, 'open', PChar(fileName), '', '', SW_NORMAL);
  624. end;
  625.  
  626. procedure TForm1.Button19Click(Sender: TObject);
  627. resourcestring
  628.   S_DISCARD = 'Are you sure you want to discard all changes?';
  629. begin
  630.   if MessageDlg(S_DISCARD, mtConfirmation, mbYesNoCancel, 0) = mrYes then
  631.   begin
  632.     CopyMemory(@Game, @GameBak, SizeOf(Game));
  633.     SetupGUIAfterLoad;
  634.   end;
  635. end;
  636.  
  637. procedure TForm1.NewScene;
  638. resourcestring
  639.   S_SCENE_FULL = 'No more space for another scene';
  640. var
  641.   sceneID: integer;
  642.   newScene: PSceneDef;
  643. begin
  644.   if ListBox1.Items.Count = 100 then
  645.   begin
  646.     raise Exception.Create(S_SCENE_FULL);
  647.   end;
  648.  
  649.   sceneID := GetGreatestSceneID+1;
  650.   if sceneID >= Length(Game.scenes) then sceneID := SearchSceneIDGapFromTop;
  651.  
  652.   newScene := Game.AddSceneAtEnd(sceneID);
  653.   newScene.actions[0].nextSceneID := SCENEID_ENDGAME;
  654.   newScene.pictureIndex := Game.numPics;
  655.   ListBox1.Items.Add(newScene.szSceneFolder);
  656.   ReloadActionSceneLists;
  657.   HandlePossibleEmptySceneList;
  658.   ListBox1.ItemIndex := ListBox1.Items.Count - 1;
  659.   ListBox1Click(ListBox1);
  660. end;
  661.  
  662. procedure TForm1.Button1Click(Sender: TObject);
  663. begin
  664.   NewScene;
  665. end;
  666.  
  667. procedure TForm1.Button20Click(Sender: TObject);
  668. begin
  669.   Game.Defrag;
  670.   RecalcStats;
  671. end;
  672.  
  673. procedure TForm1.Button2Click(Sender: TObject);
  674. resourcestring
  675.   S_CONFLICT = 'Can''t delete this scene. There are following dependencies:'+#13#10#13#10+'%s';
  676.   S_SCENEID = 'Scene %s, action #%d';
  677.   // S_ONLYONE = 'Can''t delete the scene if there is only one.';
  678.   S_REALLY_DEL = 'Do you really want to delete scene %s?';
  679.   S_NEW_INTRO = 'Attention: This will make %s to your new intro sequence. Is that OK?';
  680. var
  681.   iScene, iAction, sceneID: integer;
  682.   conflicts: TStringList;
  683.   sWarning: string;
  684.   bakItemindex: integer;
  685. begin
  686.   if ListBox1.Count = 0 then exit;
  687.  
  688.   (*
  689.   if ListBox1.Count = 1 then
  690.   begin
  691.     raise Exception.Create(S_ONLYONE);
  692.   end;
  693.   *)
  694.  
  695.   conflicts := TStringList.Create;
  696.   try
  697.     sceneID := GetSceneIDFromName(CurScene^.szSceneFolder);
  698.     for iScene := 0 to Game.numScenes-1 do
  699.     begin
  700.       for iAction := 0 to Game.scenes[iScene].numActions-1 do
  701.       begin
  702.         if Game.scenes[iScene].actions[iAction].nextSceneID = sceneID then
  703.         begin
  704.           conflicts.Add(Format(S_SCENEID, [Game.scenes[iScene].szSceneFolder, iAction+1]));
  705.         end;
  706.       end;
  707.     end;
  708.     if conflicts.Count > 0 then
  709.     begin
  710.       raise Exception.CreateFmt(S_CONFLICT, [conflicts.Text]);
  711.     end;
  712.   finally
  713.     FreeAndNil(conflicts);
  714.   end;
  715.  
  716.   sWarning := Format(S_REALLY_DEL, [CurScene^.szSceneFolder]);
  717.   if (ListBox1.ItemIndex = 0) and (ListBox1.Count >= 2) then
  718.     sWarning := Format(S_NEW_INTRO, [ListBox1.Items[ListBox1.ItemIndex+1]]);
  719.   if (MessageDlg(sWarning, mtConfirmation, mbYesNoCancel, 0) <> mrYes) then
  720.   begin
  721.     Exit;
  722.   end;
  723.  
  724.   Game.DeleteScene(ListBox1.ItemIndex);
  725.  
  726.   bakItemindex := ListBox1.ItemIndex;
  727.   ListBox1.Items.Delete(bakItemindex);
  728.   ReloadActionSceneLists;
  729.   HandlePossibleEmptySceneList;
  730.   if ListBox1.Count > 0 then
  731.   begin
  732.     if bakItemindex = ListBox1.Count then Dec(bakItemindex);
  733.     Listbox1.ItemIndex := bakItemindex;
  734.     ListBox1Click(ListBox1);
  735.   end;
  736. end;
  737.  
  738. procedure TForm1.Button3Click(Sender: TObject);
  739. begin
  740.   Save;
  741. end;
  742.  
  743. procedure TForm1.Button4Click(Sender: TObject);
  744. begin
  745.   if ListBox1.ItemIndex <= 0 then exit;
  746.   Game.SwapScene(ListBox1.ItemIndex, ListBox1.ItemIndex-1);
  747.   ListBox1.Items.Exchange(ListBox1.ItemIndex, ListBox1.ItemIndex-1);
  748.   ReloadActionSceneLists;
  749. end;
  750.  
  751. procedure TForm1.Button5Click(Sender: TObject);
  752. begin
  753.   if ListBox1.ItemIndex = ListBox1.Count-1 then exit;
  754.   Game.SwapScene(ListBox1.ItemIndex, ListBox1.ItemIndex+1);
  755.   ListBox1.Items.Exchange(ListBox1.ItemIndex, ListBox1.ItemIndex+1);
  756.   ReloadActionSceneLists;
  757. end;
  758.  
  759. procedure TForm1.Button6Click(Sender: TObject);
  760. var
  761.   pic: PPictureDef;
  762. begin
  763.   if ListBox2.Items.Count > 0 then
  764.     pic := Game.AddPictureBetween(CurScene^.pictureIndex + Max(ListBox2.ItemIndex+1,0), false)
  765.   else
  766.     pic := Game.AddPictureBetween(CurScene^.pictureIndex + Max(ListBox2.ItemIndex,0), true);
  767.   pic.duration     := DEFAULT_DURATION;
  768.   pic.szBitmapFile := DEFAULT_FILENAME;
  769.   if ListBox2.ItemIndex = -1 then
  770.   begin
  771.     ListBox2.Items.Insert(ListBox2.Count, Format('(%d) %s', [DEFAULT_DURATION, DEFAULT_FILENAME]));
  772.     ListBox2.ItemIndex := ListBox2.Count - 1;
  773.   end
  774.   else
  775.   begin
  776.     ListBox2.Items.Insert(ListBox2.ItemIndex+1, Format('(%d) %s', [DEFAULT_DURATION, DEFAULT_FILENAME]));
  777.     ListBox2.ItemIndex := ListBox2.ItemIndex + 1;
  778.   end;
  779.   ListBox2Click(Listbox2);
  780.   SelectFilenameBase(Edit3);
  781.   RecalcStats;
  782. end;
  783.  
  784. procedure TForm1.Button7Click(Sender: TObject);
  785. var
  786.   bakIdx: integer;
  787. begin
  788.   if Listbox2.Count > 0 then
  789.   begin
  790.     bakIdx := ListBox2.ItemIndex;
  791.     Game.DeletePicture(CurScene^.pictureIndex + bakIdx);
  792.     ListBox2.Items.Delete(bakIdx);
  793.     if ListBox2.Count > 0 then
  794.     begin
  795.       if bakIdx = ListBox2.Count then Dec(bakIdx);
  796.       ListBox2.ItemIndex := bakIdx;
  797.       ListBox2Click(Listbox2);
  798.     end;
  799.     RecalcUnusedFiles;
  800.   end;
  801.   RecalcStats;
  802. end;
  803.  
  804. procedure TForm1.Button8Click(Sender: TObject);
  805. begin
  806.   if ListBox2.ItemIndex <= 0 then exit;
  807.   Game.SwapPicture(CurScene^.pictureIndex+ListBox2.ItemIndex, CurScene^.pictureIndex+ListBox2.ItemIndex-1);
  808.   ListBox2.Items.Exchange(ListBox2.ItemIndex, ListBox2.ItemIndex-1);
  809. end;
  810.  
  811. procedure TForm1.Button9Click(Sender: TObject);
  812. begin
  813.   if ListBox2.ItemIndex = ListBox2.Count-1 then exit;
  814.   Game.SwapPicture(CurScene^.pictureIndex+ListBox2.ItemIndex, CurScene^.pictureIndex+ListBox2.ItemIndex+1);
  815.   ListBox2.Items.Exchange(ListBox2.ItemIndex, ListBox2.ItemIndex+1);
  816. end;
  817.  
  818. procedure TForm1.Save;
  819. var
  820.   fs: TFileStream;
  821. begin
  822.   CopyMemory(@GameBak, @Game, SizeOf(Game));
  823.  
  824.   fs := TFileStream.Create('GAME.BIN', fmOpenWrite or fmCreate);
  825.   try
  826.     fs.WriteBuffer(Game, SizeOf(Game));
  827.   finally
  828.     FreeAndNil(fs);
  829.   end;
  830. end;
  831.  
  832. procedure TForm1.ActionTargetChange(Sender: TObject);
  833. resourcestring
  834.   S_ASSERT_SENDER = 'Unexpected sender for ActionTargetChange()';
  835. var
  836.   actionIdx: integer;
  837.   itemIdx: Integer;
  838. begin
  839.        if Sender = ComboBox1 then actionIdx := 0
  840.   else if Sender = ComboBox2 then actionIdx := 1
  841.   else if Sender = ComboBox3 then actionIdx := 2
  842.   else raise Exception.Create(S_ASSERT_SENDER);
  843.  
  844.   itemIdx := TComboBox(Sender).ItemIndex;
  845.   if itemIdx = 0 then
  846.     CurScene^.actions[actionIdx].nextSceneID := SCENEID_ENDGAME
  847.   else if itemIdx = 1 then
  848.     CurScene^.actions[actionIdx].nextSceneID := SCENEID_PREVDECISION
  849.   else
  850.     CurScene^.actions[actionIdx].nextSceneID := GetSceneIDFromName(ListBox1.Items[itemIdx - 2]);
  851. end;
  852.  
  853. procedure TForm1.Addtoscene1Click(Sender: TObject);
  854. var
  855.   fil: string;
  856. begin
  857.   if ListBox3.ItemIndex = -1 then exit;
  858.   fil := ListBox3.Items[ListBox3.ItemIndex];
  859.   Button6.Click;
  860.   Edit3.Text := fil;
  861. end;
  862.  
  863. procedure TForm1.Edit1Change(Sender: TObject);
  864. begin
  865.   _WriteStringToFilename(@CurScene^.szDialogWav, Edit1.Text);
  866.   RecalcSoundtrackLength;
  867.   RecalcUnusedFiles;
  868.  
  869.   Label24.Visible := not FileExists(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + Edit1.Text);
  870.   Label25.Visible := not FileExists(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + 'E' + Edit1.Text);
  871.  
  872.   Button18.Visible := CurScene^.szDialogWav <> '';
  873. end;
  874.  
  875. procedure TForm1.Edit2Change(Sender: TObject);
  876. begin
  877.   _WriteStringToFilename(@CurScene^.szDecisionBmp, Edit2.Text);
  878.   RecalcUnusedFiles;
  879.   RedrawDecisionBitmap;
  880.   Label27.Visible := not FileExists(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + Edit2.Text) and not (Edit2.Text = '');
  881.   Button11.Visible := CurScene^.szDecisionBmp <> '';
  882. end;
  883.  
  884. procedure TForm1.Edit3Change(Sender: TObject);
  885. begin
  886.   _WriteStringToFilename(@CurPicture^.szBitmapFile, Edit3.Text);
  887.   ListBox2.Items[ListBox2.ItemIndex] := Format('(%d) %s', [CurPicture^.duration, CurPicture^.szBitmapFile]);
  888.   RecalcUnusedFiles;
  889.   ShowSceneBmp;
  890.   Label26.Visible := not FileExists(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + Edit3.Text);
  891.   Button12.Visible := CurPicture^.szBitmapFile <> '';
  892. end;
  893.  
  894. procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  895. resourcestring
  896.   S_SAVE_CHANGES = 'Do you want to save the changes?';
  897. begin
  898.   if not CompareMem(@Game, @GameBak, SizeOf(Game)) then
  899.   begin
  900.     case MessageDlg(S_SAVE_CHANGES, mtConfirmation, mbYesNoCancel, 0) of
  901.       mrYes:
  902.       begin
  903.         Save;
  904.         CanClose := true;
  905.       end;
  906.       mrNo:
  907.       begin
  908.         CanClose := true;
  909.       end;
  910.       mrCancel:
  911.       begin
  912.         CanClose := false;
  913.       end;
  914.     end;
  915.   end;
  916. end;
  917.  
  918. procedure TForm1.FormCreate(Sender: TObject);
  919. begin
  920.   PageControl1.ActivePageIndex := 0;
  921.   PageControl2.ActivePageIndex := 0;
  922.   PageControl3.ActivePageIndex := 0;
  923.   Label10.Caption := '';
  924.   Label21.Caption := '';
  925.   Label23.Caption := '';
  926. end;
  927.  
  928. procedure TForm1.FormShow(Sender: TObject);
  929. begin
  930.   if FileExists('GAME.BIN') then
  931.     Load
  932.   else
  933.     New;
  934. end;
  935.  
  936. function TForm1.CurScene: PSceneDef;
  937. resourcestring
  938.   S_NO_SCENE_AVAILABLE = 'No scenes available. Please create one.';
  939.   S_NO_SCENE_SELECTED = 'No scene selected. Please select one.';
  940. begin
  941.   if ListBox1.Count = 0 then raise Exception.Create(S_NO_SCENE_AVAILABLE);
  942.   if ListBox1.ItemIndex = -1 then raise Exception.Create(S_NO_SCENE_SELECTED);
  943.   result := @Game.scenes[ListBox1.ItemIndex];
  944. end;
  945.  
  946. procedure TForm1.ListBox1Click(Sender: TObject);
  947. begin
  948.   LoadScene;
  949. end;
  950.  
  951. procedure TForm1.ListBox1DblClick(Sender: TObject);
  952. begin
  953.   Button16Click(Button16);
  954. end;
  955.  
  956. procedure TForm1.CheckBox1Click(Sender: TObject);
  957. begin
  958.   RedrawDecisionBitmap;
  959. end;
  960.  
  961. procedure TForm1.Close1Click(Sender: TObject);
  962. begin
  963.   Close;
  964. end;
  965.  
  966. function TForm1.CurPicture: PPictureDef;
  967. resourcestring
  968.   S_NO_PICTURE_AVAILABLE = 'No pictures available. Please create one.';
  969.   S_NO_PICTURE_SELECTED = 'No picture selected. Please select one.';
  970. begin
  971.   if ListBox2.Count = 0 then raise Exception.Create(S_NO_PICTURE_AVAILABLE);
  972.   if ListBox2.ItemIndex = -1 then raise Exception.Create(S_NO_PICTURE_SELECTED);
  973.   result := @Game.pictures[CurScene^.pictureIndex + ListBox2.ItemIndex]
  974. end;
  975.  
  976. function TForm1.CurPictureTimepos: Cardinal;
  977. var
  978.   i: integer;
  979. begin
  980.   result := 0;
  981.   for i := curScene^.pictureIndex to curScene^.pictureIndex + ListBox2.ItemIndex - 1 do
  982.   begin
  983.     Inc(result, Game.pictures[i].duration);
  984.   end;
  985. end;
  986.  
  987. class procedure TForm1.AspectRatio(image: TImage; panel: TControl);
  988. var
  989.   wi, hi, ws, hs: integer;
  990.   ri, rs: double;
  991. begin
  992.   wi := image.Picture.Width;
  993.   hi := image.Picture.Height;
  994.   ri := wi / hi;
  995.  
  996.   ws := panel.Width;
  997.   hs := panel.Height;
  998.   rs := ws / hs;
  999.  
  1000.   if rs > ri then
  1001.   begin
  1002.     image.Width  := Round(wi * hs/hi);
  1003.     image.Height := hs;
  1004.   end
  1005.   else
  1006.   begin
  1007.     image.Width  := ws;
  1008.     image.Height := Round(hi * ws/wi);
  1009.   end;
  1010.  
  1011.   image.Left := Round(panel.Width  / 2 - image.Width  / 2);
  1012.   image.Top  := Round(panel.Height / 2 - image.Height / 2);
  1013. end;
  1014.  
  1015. procedure TForm1.ListBox2Click(Sender: TObject);
  1016. var
  1017.   bakEvent: TNotifyEvent;
  1018. begin
  1019.   if CurScene^.numPics = 0 then
  1020.   begin
  1021.     SpinEdit13.Value := 0;
  1022.  
  1023.     bakEvent := Edit3.OnChange;
  1024.     try
  1025.       Edit3.OnChange := nil;
  1026.       Edit3.Text := '';
  1027.     finally
  1028.       Edit3.OnChange := bakEvent;
  1029.     end;
  1030.  
  1031.     Label18.Caption := '';
  1032.     Image2.Picture := nil;
  1033.     exit;
  1034.   end;
  1035.  
  1036.   SpinEdit13.Value := CurPicture^.duration;
  1037.   Edit3.Text := string(CurPicture^.szBitmapFile);
  1038.  
  1039.   ShowSceneBmp;
  1040.  
  1041.   Label18.Caption := _DecisecondToTime(CurPictureTimepos);
  1042.  
  1043.   if PlayStart <> 0 then
  1044.   begin
  1045.     Button15Click(Button15);
  1046.   end;
  1047.  
  1048.   RecalcStats;
  1049. end;
  1050.  
  1051. procedure TForm1.ListBox2DblClick(Sender: TObject);
  1052. begin
  1053.   Button15Click(Button15);
  1054. end;
  1055.  
  1056. procedure TForm1.ListBox3Click(Sender: TObject);
  1057. var
  1058.   fileName: string;
  1059. begin
  1060.   if ListBox3.ItemIndex = -1 then exit;
  1061.   fileName := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + ListBox3.Items[ListBox3.ItemIndex];
  1062.  
  1063.   if not FileExists(FileName) then
  1064.   begin
  1065.     Image3.Visible := false;
  1066.     exit;
  1067.   end;
  1068.  
  1069.   try
  1070.     Image3.Picture.LoadFromFile(Filename);
  1071.     AspectRatio(Image3, Panel3);
  1072.     Image3.Visible := true;
  1073.   except
  1074.     Image3.Visible := false;
  1075.     // It could also be a sound file, so we do not show errors
  1076.   end;
  1077. end;
  1078.  
  1079. procedure TForm1.ListBox3DblClick(Sender: TObject);
  1080. var
  1081.   fileName: string;
  1082. begin
  1083.   if ListBox3.ItemIndex = -1 then exit;
  1084.   fileName := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + ListBox3.Items[ListBox3.ItemIndex];
  1085.   if not FileExists(FileName) then exit;
  1086.   ShellExecute(Application.Handle, 'open', PChar(fileName), '', '', SW_NORMAL);
  1087. end;
  1088.  
  1089. procedure TForm1.ListBox3MouseDown(Sender: TObject; Button: TMouseButton;
  1090.   Shift: TShiftState; X, Y: Integer);
  1091. var
  1092.   APoint: TPoint;
  1093.   Index: integer;
  1094. begin
  1095.   if Button = mbRight then
  1096.   begin
  1097.     APoint.X := X;
  1098.     APoint.Y := Y;
  1099.     Index := TListbox(Sender).ItemAtPos(APoint, True);
  1100.     if Index > -1 then TListbox(Sender).Selected[Index] := True;
  1101.   end;
  1102. end;
  1103.  
  1104. procedure TForm1.SetupGUIAfterLoad;
  1105. var
  1106.   i: integer;
  1107. begin
  1108.   ListBox1.Clear;
  1109.   for i := 0 to Game.numScenes - 1 do
  1110.   begin
  1111.     ListBox1.Items.Add(string(Game.scenes[i].szSceneFolder));
  1112.   end;
  1113.   (*
  1114.   if ListBox1.Items.Count = 0 then
  1115.   begin
  1116.     NewScene;
  1117.   end;
  1118.   *)
  1119.  
  1120.   ReloadActionSceneLists;
  1121.   HandlePossibleEmptySceneList;
  1122.  
  1123.   if ListBox1.Count > 0 then
  1124.   begin
  1125.     ListBox1.ItemIndex := 0;
  1126.     ListBox1Click(Listbox1);
  1127.   end;
  1128. end;
  1129.  
  1130. procedure TForm1.New;
  1131. begin
  1132.   PageControl1.ActivePageIndex := 0;
  1133.   PageControl2.ActivePageIndex := 0;
  1134.   PageControl3.ActivePageIndex := 0;
  1135.   Label10.Caption := '';
  1136.   Label21.Caption := '';
  1137.   Label23.Caption := '';
  1138.  
  1139.   ZeroMemory(@Game, SizeOf(Game));
  1140.   ZeroMemory(@GameBak, SizeOf(Game));
  1141.  
  1142.   SetupGUIAfterLoad;
  1143. end;
  1144.  
  1145. procedure TForm1.Load;
  1146. resourcestring
  1147.   S_CORRECTED_PICTURE_COUNT = 'Picture count was wrong. Value was corrected.';
  1148. var
  1149.   fs: TFileStream;
  1150. begin
  1151.   fs := TFileStream.Create('GAME.BIN', fmOpenRead);
  1152.   try
  1153.     fs.ReadBuffer(Game, SizeOf(Game));
  1154.   finally
  1155.     FreeAndNil(fs);
  1156.   end;
  1157.  
  1158.   CopyMemory(@GameBak, @Game, SizeOf(Game));
  1159.  
  1160.   if Game.RealPictureCount <> Game.numPics then
  1161.   begin
  1162.     MessageDlg(S_CORRECTED_PICTURE_COUNT, mtInformation, [mbOk], 0);
  1163.     Game.numPics := Game.RealPictureCount;
  1164.   end;
  1165.  
  1166.   SetupGUIAfterLoad;
  1167. end;
  1168.  
  1169. procedure TForm1.RecalcSlideshowLength;
  1170. var
  1171.   i: integer;
  1172.   Sum: integer;
  1173. begin
  1174.   Sum := 0;
  1175.   for i := CurScene^.pictureIndex to CurScene^.pictureIndex + CurScene^.numPics - 1 do
  1176.   begin
  1177.     Inc(Sum, Game.pictures[i].duration);
  1178.   end;
  1179.   Label21.Caption := _DecisecondToTime(Sum);
  1180. end;
  1181.  
  1182. procedure TForm1.RecalcUnusedFiles;
  1183. var
  1184.   i, idx: integer;
  1185.   SR: TSearchRec;
  1186.   ext: string;
  1187.   sl: TStringList;
  1188.   s: string;
  1189. begin
  1190.   sl := TStringList.Create;
  1191.   try
  1192.     if FindFirst(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + '*.*', faArchive, SR) = 0 then
  1193.     begin
  1194.       repeat
  1195.         ext := UpperCase(ExtractFileExt(SR.Name));
  1196.         if ((ext <> '.PK' {Adobe Audition}) and (ext <> '.DB' {Windows Thumbnail})) then
  1197.         begin
  1198.           sl.Add(SR.Name); //Fill the list
  1199.         end;
  1200.       until FindNext(SR) <> 0;
  1201.       FindClose(SR);
  1202.     end;
  1203.  
  1204.     // Remove the entries which are present
  1205.     for i := CurScene^.pictureIndex to CurScene^.pictureIndex+CurScene^.numPics-1 do
  1206.     begin
  1207.       idx := sl.IndexOf(Game.pictures[i].szBitmapFile);
  1208.       if idx <> -1 then sl.Delete(idx);
  1209.     end;
  1210.  
  1211.     idx := sl.IndexOf(Edit1.Text);
  1212.     if idx <> -1 then sl.Delete(idx);
  1213.     idx := sl.IndexOf('E'+Edit1.Text);
  1214.     if idx <> -1 then sl.Delete(idx);
  1215.  
  1216.     idx := sl.IndexOf(Edit2.Text);
  1217.     if idx <> -1 then sl.Delete(idx);
  1218.  
  1219.     idx := sl.IndexOf(Edit3.Text);
  1220.     if idx <> -1 then sl.Delete(idx);
  1221.  
  1222.     if sl.Text <> ListBox3.Items.Text then
  1223.     begin
  1224.       i := ListBox3.ItemIndex;
  1225.       if i >= 0 then
  1226.         s := ListBox3.Items.Strings[i]
  1227.       else
  1228.         s := '';
  1229.       ListBox3.Items.Assign(sl);
  1230.       i := ListBox3.Items.IndexOf(s);
  1231.       if i >= 0 then
  1232.         ListBox3.ItemIndex := i
  1233.       else
  1234.         Image3.Visible := false;
  1235.     end;
  1236.   finally
  1237.     sl.Free;
  1238.   end;
  1239. end;
  1240.  
  1241. procedure TForm1.RecalcSoundtrackLength;
  1242. var
  1243.   FileName: string;
  1244. resourcestring
  1245.   S_NA = 'n/a';
  1246. begin
  1247.   if MediaPlayer1.Mode = mpPlaying then exit;
  1248.  
  1249.   FileName := IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + CurScene^.szDialogWav;
  1250.   if not FileExists(FileName) then
  1251.   begin
  1252.     Label23.Caption := S_NA;
  1253.     exit;
  1254.   end;
  1255.   MediaPlayer1.FileName := FileName;
  1256.   MediaPlayer1.TimeFormat := tfMilliseconds;
  1257.   MediaPlayer1.Open;
  1258.   Label23.Caption := _DecisecondToTime(MediaPlayer1.Length div 100);
  1259.   MediaPlayer1.Close;
  1260. end;
  1261.  
  1262. procedure TForm1.LoadScene;
  1263.  
  1264.   procedure _SelectActionComboBox(ComboBox: TComboBox; sceneID: SmallInt);
  1265.   var
  1266.     idx: Integer;
  1267.   begin
  1268.     if sceneID = SCENEID_ENDGAME then
  1269.       ComboBox.ItemIndex := 0
  1270.     else if sceneID = SCENEID_PREVDECISION then
  1271.       ComboBox.ItemIndex := 1
  1272.     else
  1273.     begin
  1274.       idx := Game.FindSceneIndex(sceneID);
  1275.       if idx = -1 then
  1276.         ComboBox.ItemIndex := -1
  1277.       else
  1278.         ComboBox.ItemIndex := 2 + idx;
  1279.     end;
  1280.   end;
  1281.  
  1282.   procedure _PreparePictureList;
  1283.   var
  1284.     i: integer;
  1285.     //pic: PPictureDef;
  1286.   begin
  1287.     ListBox2.Clear;
  1288.  
  1289.     if CurScene^.numPics = 0 then
  1290.     begin
  1291.       (*
  1292.       pic := Game.AddPictureBetween(CurScene^.pictureIndex + Max(ListBox2.ItemIndex,0));
  1293.       pic.duration := DEFAULT_DURATION;
  1294.       pic.szBitmapFile := DEFAULT_FILENAME;
  1295.       *)
  1296.     end;
  1297.  
  1298.     for i := CurScene^.pictureIndex to CurScene^.pictureIndex + CurScene^.numPics - 1 do
  1299.     begin
  1300.       Listbox2.Items.Add(Format('(%d) %s', [Game.pictures[i].duration, Game.pictures[i].szBitmapFile]));
  1301.     end;
  1302.   end;
  1303.  
  1304. begin
  1305.   SpinEdit1.Value := GetSceneIDFromName(CurScene^.szSceneFolder);
  1306.   Edit1.Text := string(CurScene^.szDialogWav);
  1307.   Edit2.Text := string(CurScene^.szDecisionBmp);
  1308.  
  1309.   _PreparePictureList;
  1310.   ListBox2.ItemIndex := 0;
  1311.   ListBox2Click(Listbox2);
  1312.  
  1313.   _SelectActionComboBox(ComboBox1, CurScene^.actions[0].nextSceneID);
  1314.   _SelectActionComboBox(ComboBox2, CurScene^.actions[1].nextSceneID);
  1315.   _SelectActionComboBox(ComboBox3, CurScene^.actions[2].nextSceneID);
  1316.  
  1317.   SpinEdit12.Value := CurScene^.numActions;
  1318.   SpinEdit12Change(SpinEdit12);
  1319.   PageControl1.ActivePageIndex := 0;
  1320.  
  1321.   SpinEdit3.Value  := CurScene^.actions[0].cHotspotTopLeft.X;
  1322.   SpinEdit17.Value := CurScene^.actions[1].cHotspotTopLeft.X;
  1323.   SpinEdit7.Value  := CurScene^.actions[2].cHotspotTopLeft.X;
  1324.   SpinEdit2.Value  := CurScene^.actions[0].cHotspotTopLeft.Y;
  1325.   SpinEdit18.Value := CurScene^.actions[1].cHotspotTopLeft.Y;
  1326.   SpinEdit8.Value  := CurScene^.actions[2].cHotspotTopLeft.Y;
  1327.   SpinEdit4.Value  := CurScene^.actions[0].cHotspotBottomRight.X;
  1328.   SpinEdit19.Value := CurScene^.actions[1].cHotspotBottomRight.X;
  1329.   SpinEdit10.Value := CurScene^.actions[2].cHotspotBottomRight.X;
  1330.   SpinEdit5.Value  := CurScene^.actions[0].cHotspotBottomRight.Y;
  1331.   SpinEdit20.Value := CurScene^.actions[1].cHotspotBottomRight.Y;
  1332.   SpinEdit9.Value  := CurScene^.actions[2].cHotspotBottomRight.Y;
  1333.   SpinEdit6.Value  := CurScene^.actions[0].scoreDelta;
  1334.   SpinEdit21.Value := CurScene^.actions[1].scoreDelta;
  1335.   SpinEdit11.Value := CurScene^.actions[2].scoreDelta;
  1336.  
  1337.   RedrawDecisionBitmap;
  1338.  
  1339.   RecalcSlideshowLength;
  1340.   RecalcSoundtrackLength;
  1341.  
  1342.   RecalcUnusedFiles;
  1343.  
  1344.   RecalcStats;
  1345. end;
  1346.  
  1347. procedure TForm1.RecalcStats;
  1348. begin
  1349.   Label35.Caption := IntToStr(ListBox1.ItemIndex) + ' / ' + IntToStr(Game.numScenes);
  1350.  
  1351.   if ListBox2.ItemIndex >= 0 then
  1352.     Label31.Caption := IntToStr(CurScene^.pictureIndex) + ' + ' + IntToStr(ListBox2.ItemIndex) + ' = ' + IntToStr(CurScene^.pictureIndex+ListBox2.ItemIndex)
  1353.   else
  1354.     Label31.Caption := IntToStr(CurScene^.pictureIndex);
  1355.  
  1356.   Label33.Caption := IntToStr(CurScene^.numPics) + ' / ' + IntToStr(Game.numPics);
  1357.  
  1358.   Label37.Caption := IntToStr(CurScene^.numActions) + ' / ' + IntToStr(Game.RealActionCount);
  1359. end;
  1360.  
  1361. procedure TForm1.ReloadActionSceneLists;
  1362.  
  1363.   procedure _ReloadActionSceneLists(ComboBox: TComboBox);
  1364.   resourcestring
  1365.     S_TERMINATE = 'Terminate program';
  1366.     S_PREV_DEC = 'Previous decision';
  1367.   var
  1368.     prevSelection: string;
  1369.     i: Integer;
  1370.   begin
  1371.     prevSelection := ComboBox.Text;
  1372.     try
  1373.       ComboBox.Items.Clear;
  1374.       ComboBox.Items.Add(S_TERMINATE);
  1375.       ComboBox.Items.Add(S_PREV_DEC);
  1376.       ComboBox.Items.AddStrings(ListBox1.Items);
  1377.     finally
  1378.       ComboBox.ItemIndex := 0;
  1379.       for i := ComboBox.Items.Count - 1 downto 0 do
  1380.       begin
  1381.         if ComboBox.Items.Strings[i] = prevSelection then
  1382.         begin
  1383.           ComboBox.ItemIndex := i;
  1384.         end;
  1385.       end;
  1386.     end;
  1387.   end;
  1388.  
  1389. begin
  1390.   _ReloadActionSceneLists(ComboBox1);
  1391.   _ReloadActionSceneLists(ComboBox2);
  1392.   _ReloadActionSceneLists(ComboBox3);
  1393. end;
  1394.  
  1395. procedure TForm1.SpinEdit12Change(Sender: TObject);
  1396. begin
  1397.   TabSheet3.TabVisible := SpinEdit12.Value >= 3;
  1398.   if PageControl1.ActivePage = TabSheet3 then PageControl1.ActivePage := TabSheet2;
  1399.  
  1400.   TabSheet2.TabVisible := SpinEdit12.Value >= 2;
  1401.   if PageControl1.ActivePage = TabSheet2 then PageControl1.ActivePage := TabSheet1;
  1402.  
  1403.   // TabSheet1.TabVisible := SpinEdit12.Value >= 1;
  1404.  
  1405.   CurScene^.numActions := SpinEdit12.Value;
  1406.  
  1407.   // QUE: zero data of actions which are inactive (numActions<action)?
  1408.  
  1409.   RecalcStats;
  1410.   RedrawDecisionBitmap;
  1411. end;
  1412.  
  1413. procedure TForm1.SpinEdit13Change(Sender: TObject);
  1414. begin
  1415.   if SpinEdit13.Text = '' then exit;
  1416.   CurPicture^.duration := SpinEdit13.Value;
  1417.   ListBox2.Items[ListBox2.ItemIndex] := Format('(%d) %s', [CurPicture^.duration, CurPicture^.szBitmapFile]);
  1418.   RecalcSlideshowLength;
  1419. end;
  1420.  
  1421. procedure TForm1.SpinEdit1Change(Sender: TObject);
  1422.  
  1423.   function _CountNumScenes(str: string): integer;
  1424.   var
  1425.     i: integer;
  1426.   begin
  1427.     result := 0;
  1428.     for i := 0 to ListBox1.Count-1 do
  1429.     begin
  1430.       if ListBox1.Items[i] = str then Inc(result);
  1431.     end;
  1432.   end;
  1433.  
  1434. var
  1435.   sNew: string;
  1436. begin
  1437.   // TODO: warn if dependencies are broken
  1438.  
  1439.   if SpinEdit1.Text = '' then exit;
  1440.   CurScene; // verify that a scene exists and is selected
  1441.   sNew := Format('SC%.2d', [SpinEdit1.Value]);
  1442.   ListBox1.Items[ListBox1.ItemIndex] := sNew;
  1443.   Label17.Visible := _CountNumScenes(sNew) > 1;
  1444.   ReloadActionSceneLists;
  1445.   _WriteStringToFilename(@CurScene^.szSceneFolder, ListBox1.Items[ListBox1.ItemIndex]);
  1446. end;
  1447.  
  1448. procedure TForm1.Timer1Timer(Sender: TObject);
  1449. var
  1450.   ms: Cardinal;
  1451. begin
  1452.   ms := GetTickCount - PlayStart;
  1453.   Label18.Caption := FormatDatetime('hh:nn:ss', ms / 1000 / (24*60*60)) + ',' + IntToStr(ms mod 1000 div 100);
  1454. end;
  1455.  
  1456. procedure TForm1.About1Click(Sender: TObject);
  1457. begin
  1458.   ShowMessage('Plumbers Don''t Wear Ties - GAME.BIN editor (can be also used to create new games based on the "ShowTime" engine!)'+#13#10#13#10+'Written by Daniel Marschall (www.daniel-marschall.de)'+#13#10#13#10+'Published by ViaThinkSoft (www.viathinksoft.com)'+#13#10#13#10+'Current version: ' + CUR_VER);
  1459. end;
  1460.  
  1461. procedure TForm1.ActionSpinEditsChange(Sender: TObject);
  1462. begin
  1463.   if TSpinEdit(Sender).Text = '' then exit; // user is about to enter a negative value
  1464.   if TSpinEdit(Sender).Text = '-' then exit; // user is about to enter a negative value
  1465.  
  1466.        if Sender = SpinEdit3  then CurScene^.actions[0].cHotspotTopLeft.X := TSpinEdit(Sender).Value
  1467.   else if Sender = SpinEdit17 then CurScene^.actions[1].cHotspotTopLeft.X := TSpinEdit(Sender).Value
  1468.   else if Sender = SpinEdit7  then CurScene^.actions[2].cHotspotTopLeft.X := TSpinEdit(Sender).Value
  1469.   else if Sender = SpinEdit2  then CurScene^.actions[0].cHotspotTopLeft.Y := TSpinEdit(Sender).Value
  1470.   else if Sender = SpinEdit18 then CurScene^.actions[1].cHotspotTopLeft.Y := TSpinEdit(Sender).Value
  1471.   else if Sender = SpinEdit8  then CurScene^.actions[2].cHotspotTopLeft.Y := TSpinEdit(Sender).Value
  1472.   else if Sender = SpinEdit4  then CurScene^.actions[0].cHotspotBottomRight.X := TSpinEdit(Sender).Value
  1473.   else if Sender = SpinEdit19 then CurScene^.actions[1].cHotspotBottomRight.X := TSpinEdit(Sender).Value
  1474.   else if Sender = SpinEdit10 then CurScene^.actions[2].cHotspotBottomRight.X := TSpinEdit(Sender).Value
  1475.   else if Sender = SpinEdit5  then CurScene^.actions[0].cHotspotBottomRight.Y := TSpinEdit(Sender).Value
  1476.   else if Sender = SpinEdit20 then CurScene^.actions[1].cHotspotBottomRight.Y := TSpinEdit(Sender).Value
  1477.   else if Sender = SpinEdit9  then CurScene^.actions[2].cHotspotBottomRight.Y := TSpinEdit(Sender).Value
  1478.   else if Sender = SpinEdit6  then CurScene^.actions[0].scoreDelta := TSpinEdit(Sender).Value
  1479.   else if Sender = SpinEdit21 then CurScene^.actions[1].scoreDelta := TSpinEdit(Sender).Value
  1480.   else if Sender = SpinEdit11 then CurScene^.actions[2].scoreDelta := TSpinEdit(Sender).Value;
  1481.  
  1482.   RedrawDecisionBitmap;
  1483. end;
  1484.  
  1485. procedure TForm1.RedrawDecisionBitmap;
  1486. var
  1487.   Filename: string;
  1488. begin
  1489.   FileName := string(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + Edit2.Text);
  1490.  
  1491.   Image1.Visible := FileExists(FileName);
  1492.  
  1493.   if not FileExists(FileName) then exit;
  1494.  
  1495.   Image1.Picture.Bitmap.LoadFromFile(FileName);
  1496.   AspectRatio(Image1, Panel1);
  1497.  
  1498.   Image1.Picture.Bitmap.PixelFormat := pf24bit; // Extend the palette, so we have red, green and blue guaranteed.
  1499.  
  1500.   Image1.Canvas.Brush.Style := bsDiagCross;
  1501.  
  1502.   if (CheckBox1.Checked or (CurScene^.numActions >= 1)) and
  1503.      (SpinEdit3.Value < SpinEdit4.Value) and (SpinEdit2.Value < SpinEdit5.Value) then
  1504.   begin
  1505.     Image1.Canvas.Pen.Color := clRed;
  1506.     Image1.Canvas.Brush.Color := clRed;
  1507.     Image1.Canvas.Rectangle(SpinEdit3.Value,  SpinEdit2.Value,  SpinEdit4.Value,  SpinEdit5.Value);
  1508.   end;
  1509.  
  1510.   if (CheckBox1.Checked or (CurScene^.numActions >= 2)) and
  1511.      (SpinEdit17.Value < SpinEdit19.Value) and (SpinEdit18.Value < SpinEdit20.Value) then
  1512.   begin
  1513.     Image1.Canvas.Pen.Color := clLime;
  1514.     Image1.Canvas.Brush.Color := clLime;
  1515.     Image1.Canvas.Rectangle(SpinEdit17.Value, SpinEdit18.Value, SpinEdit19.Value, SpinEdit20.Value);
  1516.   end;
  1517.  
  1518.   if (CheckBox1.Checked or (CurScene^.numActions >= 3)) and
  1519.      (SpinEdit7.Value < SpinEdit10.Value) and (SpinEdit8.Value < SpinEdit9.Value) then
  1520.   begin
  1521.     Image1.Canvas.Pen.Color := clBlue;
  1522.     Image1.Canvas.Brush.Color := clBlue;
  1523.     Image1.Canvas.Rectangle(SpinEdit7.Value,  SpinEdit8.Value,  SpinEdit10.Value, SpinEdit9.Value);
  1524.   end;
  1525. end;
  1526.  
  1527. end.
  1528.