Subversion Repositories plumbers

Rev

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

Rev Author Line No. Line
2 daniel-mar 1
unit Unit1;
2
 
25 daniel-mar 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
 
8 daniel-mar 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
10 daniel-mar 23
// TODO: Give controls better names
11 daniel-mar 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
8 daniel-mar 29
// Idea: unused liste auch bei decision page anzeigen
11 daniel-mar 30
// Idea: Function to re-order stuff?
2 daniel-mar 31
 
32
interface
33
 
34
uses
35
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
10 daniel-mar 36
  Dialogs, StdCtrls, Spin, Grids, GameBinStruct, ComCtrls, ExtCtrls, MPlayer,
37
  Menus;
2 daniel-mar 38
 
39
const
18 daniel-mar 40
  CUR_VER = '2020-06-08';
2 daniel-mar 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;
6 daniel-mar 138
    Panel1: TPanel;
139
    Panel2: TPanel;
8 daniel-mar 140
    Panel3: TPanel;
141
    Image3: TImage;
10 daniel-mar 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;
11 daniel-mar 158
    CheckBox1: TCheckBox;
159
    TabSheet8: TTabSheet;
160
    Button20: TButton;
2 daniel-mar 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);
8 daniel-mar 206
    procedure ListBox3Click(Sender: TObject);
10 daniel-mar 207
    procedure Button19Click(Sender: TObject);
208
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
11 daniel-mar 209
    procedure Button20Click(Sender: TObject);
210
    procedure CheckBox1Click(Sender: TObject);
2 daniel-mar 211
  private
212
    Game: TGameBinFile;
10 daniel-mar 213
    GameBak: TGameBinFile;
2 daniel-mar 214
    PlayStart: Cardinal;
215
    MediaplayerOpened: boolean;
216
    StopPlayRequest: boolean;
217
    FirstTickCount: Cardinal;
8 daniel-mar 218
    procedure ShowSceneBmp;
2 daniel-mar 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;
10 daniel-mar 235
    procedure RecalcStats;
2 daniel-mar 236
    procedure HandlePossibleEmptySceneList;
237
    procedure DisableEnableSceneControls(enable: boolean);
238
    procedure DisableEnablePictureControls(enable: boolean);
239
    procedure DisableEnableFileControls(enable: boolean);
6 daniel-mar 240
    class procedure AspectRatio(image: TImage; panel: TControl);
2 daniel-mar 241
  end;
242
 
243
var
244
  Form1: TForm1;
245
 
246
implementation
247
 
248
{$R *.dfm}
249
 
250
uses
251
  Math, MMSystem, ShellAPI;
252
 
10 daniel-mar 253
const
254
  DEFAULT_DURATION = 0;
255
  DEFAULT_FILENAME = 'DUMMY.BMP';
256
 
2 daniel-mar 257
function GetSceneIDFromName(sceneName: string): Integer;
12 daniel-mar 258
resourcestring
259
  S_SCENENAME_INVALID = 'Scene name invalid';
2 daniel-mar 260
var
261
  sSceneID: string;
262
begin
12 daniel-mar 263
  if Copy(sceneName, 1, 2) <> 'SC' then raise Exception.Create(S_SCENENAME_INVALID);
2 daniel-mar 264
  sSceneID := Copy(sceneName, 3, 2);
12 daniel-mar 265
  if not TryStrToInt(sSceneID, result) then raise Exception.Create(S_SCENENAME_INVALID);
2 daniel-mar 266
end;
267
 
10 daniel-mar 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
 
2 daniel-mar 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
 
12 daniel-mar 355
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
356
resourcestring
357
  S_COORDS = 'Coords: [%d, %d]';
2 daniel-mar 358
begin
12 daniel-mar 359
  label10.Caption := Format(S_COORDS, [Round(Image1.Picture.Width*(X/Image1.Width)),
360
                                       Round(Image1.Picture.Height*(Y/Image1.Height))]);
2 daniel-mar 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);
12 daniel-mar 379
resourcestring
380
  S_NOTFOUND = 'GAME.BIN saved; ShowTime32.exe not found.';
2 daniel-mar 381
begin
382
  Save;
12 daniel-mar 383
  if not FileExists('ShowTime32.exe') then raise Exception.Create(S_NOTFOUND);
2 daniel-mar 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);
18 daniel-mar 431
resourcestring
432
  S_DISCARD = 'Are you sure you want to discard all changes and create a new game file?';
2 daniel-mar 433
begin
18 daniel-mar 434
  if MessageDlg(S_DISCARD, mtConfirmation, mbYesNoCancel, 0) = mrYes then
435
  begin
436
    New;
437
  end;
2 daniel-mar 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
 
6 daniel-mar 464
var
465
  AutomaticNextDia: boolean;
466
 
2 daniel-mar 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
6 daniel-mar 476
    if MediaplayerOpened and not AutomaticNextDia then
2 daniel-mar 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;
6 daniel-mar 486
    AutomaticNextDia := false;
2 daniel-mar 487
    FirstTickCount := GetTickCount;
488
    PlayStart := GetTickCount - CurPictureTimepos * 100;
489
  end
490
  else
491
  begin
8 daniel-mar 492
    TabSheet5.TabVisible := false;
2 daniel-mar 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;
6 daniel-mar 556
            AutomaticNextDia := true;
2 daniel-mar 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);
6 daniel-mar 568
              AspectRatio(Image2, Panel2);
2 daniel-mar 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);
8 daniel-mar 590
      TabSheet5.TabVisible := true;
2 daniel-mar 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
 
8 daniel-mar 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
 
2 daniel-mar 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
 
10 daniel-mar 643
procedure TForm1.Button19Click(Sender: TObject);
12 daniel-mar 644
resourcestring
645
  S_DISCARD = 'Are you sure you want to discard all changes?';
10 daniel-mar 646
begin
12 daniel-mar 647
  if MessageDlg(S_DISCARD, mtConfirmation, mbYesNoCancel, 0) = mrYes then
11 daniel-mar 648
  begin
649
    CopyMemory(@Game, @GameBak, SizeOf(Game));
650
    SetupGUIAfterLoad;
651
  end;
10 daniel-mar 652
end;
653
 
2 daniel-mar 654
procedure TForm1.NewScene;
12 daniel-mar 655
resourcestring
656
  S_SCENE_FULL = 'No more space for another scene';
2 daniel-mar 657
var
658
  sceneID: integer;
659
  newScene: PSceneDef;
660
begin
661
  if ListBox1.Items.Count = 100 then
662
  begin
12 daniel-mar 663
    raise Exception.Create(S_SCENE_FULL);
2 daniel-mar 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;
10 daniel-mar 671
  newScene.pictureIndex := Game.numPics;
2 daniel-mar 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
 
11 daniel-mar 684
procedure TForm1.Button20Click(Sender: TObject);
685
begin
686
  Game.Defrag;
687
  RecalcStats;
688
end;
689
 
2 daniel-mar 690
procedure TForm1.Button2Click(Sender: TObject);
12 daniel-mar 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?';
2 daniel-mar 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
12 daniel-mar 708
    raise Exception.Create(S_ONLYONE);
2 daniel-mar 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
12 daniel-mar 721
          conflicts.Add(Format(S_SCENEID, [Game.scenes[iScene].szSceneFolder, iAction+1]));
2 daniel-mar 722
        end;
723
      end;
724
    end;
725
    if conflicts.Count > 0 then
726
    begin
12 daniel-mar 727
      raise Exception.CreateFmt(S_CONFLICT, [conflicts.Text]);
2 daniel-mar 728
    end;
729
  finally
730
    FreeAndNil(conflicts);
731
  end;
732
 
12 daniel-mar 733
  sWarning := Format(S_REALLY_DEL, [CurScene^.szSceneFolder]);
2 daniel-mar 734
  if (ListBox1.ItemIndex = 0) and (ListBox1.Count >= 2) then
12 daniel-mar 735
    sWarning := Format(S_NEW_INTRO, [ListBox1.Items[ListBox1.ItemIndex+1]]);
2 daniel-mar 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
10 daniel-mar 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;
2 daniel-mar 786
  if ListBox2.ItemIndex = -1 then
787
  begin
10 daniel-mar 788
    ListBox2.Items.Insert(ListBox2.Count, Format('(%d) %s', [DEFAULT_DURATION, DEFAULT_FILENAME]));
2 daniel-mar 789
    ListBox2.ItemIndex := ListBox2.Count - 1;
790
  end
791
  else
792
  begin
10 daniel-mar 793
    ListBox2.Items.Insert(ListBox2.ItemIndex+1, Format('(%d) %s', [DEFAULT_DURATION, DEFAULT_FILENAME]));
794
    ListBox2.ItemIndex := ListBox2.ItemIndex + 1;
2 daniel-mar 795
  end;
796
  ListBox2Click(Listbox2);
10 daniel-mar 797
  SelectFilenameBase(Edit3);
798
  RecalcStats;
2 daniel-mar 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;
10 daniel-mar 808
    Game.DeletePicture(CurScene^.pictureIndex + bakIdx);
2 daniel-mar 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;
8 daniel-mar 816
    RecalcUnusedFiles;
2 daniel-mar 817
  end;
10 daniel-mar 818
  RecalcStats;
2 daniel-mar 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
10 daniel-mar 839
  CopyMemory(@GameBak, @Game, SizeOf(Game));
840
 
2 daniel-mar 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);
12 daniel-mar 850
resourcestring
851
  S_ASSERT_SENDER = 'Unexpected sender for ActionTargetChange()';
2 daniel-mar 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
12 daniel-mar 859
  else raise Exception.Create(S_ASSERT_SENDER);
2 daniel-mar 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;
8 daniel-mar 896
  RedrawDecisionBitmap;
2 daniel-mar 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;
8 daniel-mar 906
  ShowSceneBmp;
2 daniel-mar 907
  Label26.Visible := not FileExists(IncludeTrailingPathDelimiter(CurScene^.szSceneFolder) + Edit3.Text);
908
  Button12.Visible := CurPicture^.szBitmapFile <> '';
909
end;
910
 
10 daniel-mar 911
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
12 daniel-mar 912
resourcestring
913
  S_SAVE_CHANGES = 'Do you want to save the changes?';
10 daniel-mar 914
begin
915
  if not CompareMem(@Game, @GameBak, SizeOf(Game)) then
916
  begin
12 daniel-mar 917
    case MessageDlg(S_SAVE_CHANGES, mtConfirmation, mbYesNoCancel, 0) of
11 daniel-mar 918
      mrYes:
10 daniel-mar 919
      begin
920
        Save;
921
        CanClose := true;
922
      end;
11 daniel-mar 923
      mrNo:
10 daniel-mar 924
      begin
925
        CanClose := true;
926
      end;
11 daniel-mar 927
      mrCancel:
10 daniel-mar 928
      begin
929
        CanClose := false;
930
      end;
931
    end;
932
  end;
933
end;
934
 
2 daniel-mar 935
procedure TForm1.FormCreate(Sender: TObject);
936
begin
937
  PageControl1.ActivePageIndex := 0;
938
  PageControl2.ActivePageIndex := 0;
10 daniel-mar 939
  PageControl3.ActivePageIndex := 0;
2 daniel-mar 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;
12 daniel-mar 954
resourcestring
955
  S_NO_SCENE_AVAILABLE = 'No scenes available. Please create one.';
956
  S_NO_SCENE_SELECTED = 'No scene selected. Please select one.';
2 daniel-mar 957
begin
12 daniel-mar 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);
2 daniel-mar 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
 
11 daniel-mar 973
procedure TForm1.CheckBox1Click(Sender: TObject);
974
begin
975
  RedrawDecisionBitmap;
976
end;
977
 
2 daniel-mar 978
procedure TForm1.Close1Click(Sender: TObject);
979
begin
980
  Close;
981
end;
982
 
983
function TForm1.CurPicture: PPictureDef;
12 daniel-mar 984
resourcestring
985
  S_NO_PICTURE_AVAILABLE = 'No pictures available. Please create one.';
986
  S_NO_PICTURE_SELECTED = 'No picture selected. Please select one.';
2 daniel-mar 987
begin
12 daniel-mar 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);
2 daniel-mar 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
 
6 daniel-mar 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
 
2 daniel-mar 1032
procedure TForm1.ListBox2Click(Sender: TObject);
10 daniel-mar 1033
var
1034
  bakEvent: TNotifyEvent;
2 daniel-mar 1035
begin
10 daniel-mar 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
 
2 daniel-mar 1053
  SpinEdit13.Value := CurPicture^.duration;
1054
  Edit3.Text := string(CurPicture^.szBitmapFile);
1055
 
8 daniel-mar 1056
  ShowSceneBmp;
2 daniel-mar 1057
 
1058
  Label18.Caption := _DecisecondToTime(CurPictureTimepos);
1059
 
1060
  if PlayStart <> 0 then
1061
  begin
1062
    Button15Click(Button15);
1063
  end;
10 daniel-mar 1064
 
1065
  RecalcStats;
2 daniel-mar 1066
end;
1067
 
1068
procedure TForm1.ListBox2DblClick(Sender: TObject);
1069
begin
1070
  Button15Click(Button15);
1071
end;
1072
 
8 daniel-mar 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];
14 daniel-mar 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;
8 daniel-mar 1094
end;
1095
 
2 daniel-mar 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
11 daniel-mar 1149
  PageControl1.ActivePageIndex := 0;
1150
  PageControl2.ActivePageIndex := 0;
1151
  PageControl3.ActivePageIndex := 0;
1152
  Label10.Caption := '';
1153
  Label21.Caption := '';
1154
  Label23.Caption := '';
1155
 
2 daniel-mar 1156
  ZeroMemory(@Game, SizeOf(Game));
10 daniel-mar 1157
  ZeroMemory(@GameBak, SizeOf(Game));
2 daniel-mar 1158
 
1159
  SetupGUIAfterLoad;
1160
end;
1161
 
1162
procedure TForm1.Load;
12 daniel-mar 1163
resourcestring
1164
  S_CORRECTED_PICTURE_COUNT = 'Picture count was wrong. Value was corrected.';
2 daniel-mar 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
 
11 daniel-mar 1175
  CopyMemory(@GameBak, @Game, SizeOf(Game));
1176
 
10 daniel-mar 1177
  if Game.RealPictureCount <> Game.numPics then
1178
  begin
12 daniel-mar 1179
    MessageDlg(S_CORRECTED_PICTURE_COUNT, mtInformation, [mbOk], 0);
10 daniel-mar 1180
    Game.numPics := Game.RealPictureCount;
1181
  end;
1182
 
2 daniel-mar 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;
6 daniel-mar 1203
  ext: string;
14 daniel-mar 1204
  sl: TStringList;
1205
  s: string;
2 daniel-mar 1206
begin
14 daniel-mar 1207
  sl := TStringList.Create;
6 daniel-mar 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
14 daniel-mar 1215
          sl.Add(SR.Name); //Fill the list
6 daniel-mar 1216
        end;
1217
      until FindNext(SR) <> 0;
1218
      FindClose(SR);
1219
    end;
2 daniel-mar 1220
 
6 daniel-mar 1221
    // Remove the entries which are present
1222
    for i := CurScene^.pictureIndex to CurScene^.pictureIndex+CurScene^.numPics-1 do
1223
    begin
14 daniel-mar 1224
      idx := sl.IndexOf(Game.pictures[i].szBitmapFile);
1225
      if idx <> -1 then sl.Delete(idx);
6 daniel-mar 1226
    end;
1227
 
14 daniel-mar 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);
2 daniel-mar 1232
 
14 daniel-mar 1233
    idx := sl.IndexOf(Edit2.Text);
1234
    if idx <> -1 then sl.Delete(idx);
2 daniel-mar 1235
 
14 daniel-mar 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;
6 daniel-mar 1253
  finally
14 daniel-mar 1254
    sl.Free;
6 daniel-mar 1255
  end;
2 daniel-mar 1256
end;
1257
 
1258
procedure TForm1.RecalcSoundtrackLength;
1259
var
1260
  FileName: string;
12 daniel-mar 1261
resourcestring
1262
  S_NA = 'n/a';
2 daniel-mar 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
12 daniel-mar 1269
    Label23.Caption := S_NA;
2 daniel-mar 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
12 daniel-mar 1291
      idx := Game.FindSceneIndex(sceneID);
2 daniel-mar 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;
10 daniel-mar 1302
    //pic: PPictureDef;
2 daniel-mar 1303
  begin
1304
    ListBox2.Clear;
1305
 
1306
    if CurScene^.numPics = 0 then
1307
    begin
10 daniel-mar 1308
      (*
2 daniel-mar 1309
      pic := Game.AddPictureBetween(CurScene^.pictureIndex + Max(ListBox2.ItemIndex,0));
10 daniel-mar 1310
      pic.duration := DEFAULT_DURATION;
1311
      pic.szBitmapFile := DEFAULT_FILENAME;
1312
      *)
2 daniel-mar 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;
10 daniel-mar 1360
 
1361
  RecalcStats;
2 daniel-mar 1362
end;
1363
 
10 daniel-mar 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
 
2 daniel-mar 1378
procedure TForm1.ReloadActionSceneLists;
1379
 
1380
  procedure _ReloadActionSceneLists(ComboBox: TComboBox);
12 daniel-mar 1381
  resourcestring
1382
    S_TERMINATE = 'Terminate program';
1383
    S_PREV_DEC = 'Previous decision';
2 daniel-mar 1384
  var
1385
    prevSelection: string;
1386
    i: Integer;
1387
  begin
1388
    prevSelection := ComboBox.Text;
1389
    try
1390
      ComboBox.Items.Clear;
12 daniel-mar 1391
      ComboBox.Items.Add(S_TERMINATE);
1392
      ComboBox.Items.Add(S_PREV_DEC);
2 daniel-mar 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)?
10 daniel-mar 1425
 
1426
  RecalcStats;
11 daniel-mar 1427
  RedrawDecisionBitmap;
2 daniel-mar 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
10 daniel-mar 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);
2 daniel-mar 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);
8 daniel-mar 1507
 
1508
  Image1.Visible := FileExists(FileName);
1509
 
6 daniel-mar 1510
  if not FileExists(FileName) then exit;
2 daniel-mar 1511
 
6 daniel-mar 1512
  Image1.Picture.Bitmap.LoadFromFile(FileName);
1513
  AspectRatio(Image1, Panel1);
1514
 
2 daniel-mar 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
 
11 daniel-mar 1519
  if (CheckBox1.Checked or (CurScene^.numActions >= 1)) and
1520
     (SpinEdit3.Value < SpinEdit4.Value) and (SpinEdit2.Value < SpinEdit5.Value) then
2 daniel-mar 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
 
11 daniel-mar 1527
  if (CheckBox1.Checked or (CurScene^.numActions >= 2)) and
1528
     (SpinEdit17.Value < SpinEdit19.Value) and (SpinEdit18.Value < SpinEdit20.Value) then
2 daniel-mar 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
 
11 daniel-mar 1535
  if (CheckBox1.Checked or (CurScene^.numActions >= 3)) and
1536
     (SpinEdit7.Value < SpinEdit10.Value) and (SpinEdit8.Value < SpinEdit9.Value) then
2 daniel-mar 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.