Sunday, 27 April 2014

Writing a StrPix-like program with Lazarus – 8

Rather than have the moveables’ object ID numbers displayed we will display their names.

We will store their names in a text file with each name on a separate line and where the line number matches the object ID if we consider the first line as line 0.

We could hard code the names into the program but reading them from a file means that new names can be added for new objects and the names changed if needed.

In the operating system navigate to our project’s folder. Here you will find our project files and source files as well as our program’s *.exe file which you can double click as normal to run the program.

ProjectFolder

In this folder create a new folder named “data”.

For my SoundEditor program I created a text file of all the moveable object names called NGLEObjects.txt and so will use that file.

Copy NGLEObjects.txt into the “data” folder just created. If you don’t have SoundEditor you can download a copy of NGLEObjects.txt from here.

datafolder

We will need to have the object names available before a *.wad file is opened so will need to read the names on program start. When a program is started, the form has to be created first so it is usual to have code you want executed before any user interaction written in the form’s Create method.

Go to Window>Form1 to display the form and click on the form to select it. Sometimes it is easier to select the form in the Object Inspector component list.

In the Object Inspector, click on the events tab and then click OnCreate.

Click on the 3 dot button that appears to create an empty TForm1.FormCreate method in unit1.

FormCreate

The easiest way to load a text file is using a TStringList. So our object names are available to all methods in unit1’s implementation section we will declare a global variable of the type TStringList.

implementation

uses TombRaiderWAD;

{$R *.lfm}

var
  streamWAD: TMemoryStream;
  WADCrawler: TWADCrawler;
  WADParser: TWADParser;
  moveableNames: TStringList; //NEW 

Click on the 3 dot button in the form’s OnCreate event to jump back to the FormCreate method.

Add the code as shown below.

procedure TForm1.FormCreate(Sender: TObject);
var
  s : string;
begin
  s := Application.ExeName; // gets path and filename of our program
  s := ExtractFilePath(s);  // extracts directory of our program
  s := s + 'data' + DirectorySeparator + 'NGLEObjects.txt'; // full path to text file
  if FileExists(s) then // execute next code only if the file exists
  begin
    moveableNames := TStringList.Create; // moveableNames must be created before use
    moveableNames.LoadFromFile(s); // load the text file into the stringlist
  end;
end;

Since we have used a create method to construct moveableNames we must free it sometime. We need the moveableNames variable available until the program ends so we will free it only when the program is closed.

Find the TForm1.FormClose method and add the code as shown below.

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  streamWAD.Free;
  moveableNames.Free; //NEW
end

Now we should not get a warning about unfreed memory blocks from Heaptrc.

Find the TForm1.MakeWadTree method. We need to change the code in the loop where the captions are created for the child nodes of Moveables.

Change the code as shown below.

procedure TForm1.MakeWadTree;
var
  s : string;
  root, n1 : TTreeNode;
  i : integer;
begin
  TreeView1.Items.Clear;

  s := ExtractFileName(FileOpen1.Dialog.FileName);
  root := TreeView1.Items.AddChild(nil, s);

  s := 'Size';
  n1 := TreeView1.Items.AddChild(root, s);

  s := Format('%.0n bytes',[streamWAD.Size*1.0]);
  TreeView1.Items.AddChild(n1, s);

  s := 'Version';
  n1 := TreeView1.Items.AddChild(root, s);

  s := Format('%d',[Wad.version]);
  TreeView1.Items.AddChild(n1, s);

  s := Format('Moveables (%d)',[Wad.numMovables]);
  n1 := TreeView1.Items.AddChild(root, s);

  // create a node for each moveable in the wad and caption with the slot number
  // each node will be a child of Moveables
  for i:=1 to Wad.numMovables do
  begin
    s := Format('Moveable %d',[Wad.movablesTableData[i-1].obj_ID]);
    if  (moveableNames.Count >0) // the moveable names were loaded
    and (Wad.movablesTableData[i-1].obj_ID > -1) // can't have negative index
    and (Wad.movablesTableData[i-1].obj_ID < (moveableNames.Count -1)) // index too high
    then
    begin
      s := moveableNames.Strings[Wad.movablesTableData[i-1].obj_ID]; // get the name from ID
    end;
    TreeView1.Items.AddChild(n1, s);
  end;

  s := Format('Statics (%d)',[Wad.numStatics]);
  n1 := TreeView1.Items.AddChild(root, s);

  for i := 1 to Wad.numStatics do
  begin
    s := Format('Static %d',[Wad.staticsTableData[i-1].obj_ID]);
    TreeView1.Items.AddChild(n1, s);
  end;

  root.Expand(False);

end;

Now if you build and run the program the slot names are displayed instead of the ID numbers. Note that in case the text file that contains the name is not loaded we still use the ID numbers as a backup.

MoveableNames

The text file NGLEObjects.txt has the names written in uppercase letters so the names are uppercase in our tree display.

I will show some FreePascal functions that you can use to change this if you wish.

If you wish to display the names as lowercase you can use the function LowerCase.

You can use the function AnsiProperCase if you want the first letter of each word capitalised. You will most likely need to add the unit strutils to a uses clause for the program to recognise the AnsiProperCase function since strutils is not one of the units automatically added to a uses clause when a new project is created.

implementation

uses TombRaiderWAD, strutils; 

If you hover the mouse pointer over a function name or other identifier and a hint doesn’t pop up with details about the function or identifier, then the program doesn’t recognise the function or identifier and you may need to add a unit to a uses clause.

To find which unit, select the name in the editor and click on Source>Show Unit/Identifier Dictionary. Type the name in the edit box if it is not already there. A list of unit names should appear so select the one you want.

If you want to add the unit to the interface section uses clause click Use Identifier button but if you want to add it to the implementation section uses clause check the option Add unit to implementation uses section and then click the Use Identifier button.

Rather than use LowerCase or AnsiProperCase each time though the loop in TForm1.MakeTreeWad use them once on the moveableNames stringlist in TForm1.FormCreate.

procedure TForm1.FormCreate(Sender: TObject);
var
  s : string;
begin
  s := Application.ExeName;
  s := ExtractFilePath(s);
  s := s + 'data' + DirectorySeparator + 'NGLEObjects.txt';
  if FileExists(s) then
  begin
    moveableNames := TStringList.Create;
    moveableNames.LoadFromFile(s);
    //moveableNames.Text := LowerCase(moveableNames.Text); // NEW
    moveableNames.Text := AnsiProperCase(moveableNames.Text, StdWordDelims); // NEW
  end;
end;

MoveableNamesProper

I will follow the same procedure to display the Static object names. I will get the names for the Static objects from the updated object.h file used by the latest WadMerger since that will have the extra static slots. NGLEStatics.txt

Next post we will use a TImage component to display the textures.



prev | next

No comments:

Post a Comment