Subversion Repositories plumbers

Rev

Rev 18 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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