Using records to create and find mainscreen objects
In this tutorial I'm going to show you how to create and find mainscreen objects using records. This might look complicated at first, but it can shorten your scripts and make them much easier to maintain. Would you like to be able to write a script like this:
Simba Code:
if Copper.Find() then
Copper.Drop()
else
Copper.WalkTo();
PART 1: Records
A record is an advanced data type which is used to group multiple variables (known as 'fields') together under the same umbrella. Each of these variables can be a different type, so a record can be very complex.
For example, a type that you are no doubt familiar with is the TPoint. This is actually a record that contains 2 fields:
Simba Code:
type TPoint = record
x, y: integer;
end;
Each TPoint has an x and a y coordinate. You can access each of the fields like so:
Simba Code:
procedure example();
var
p: TPoint;
begin
p.x := 10;
p.y := 20;
writeLn(p); // Will print [10, 20]
end;
The fields that make up a record will appear in the codehints (see X and Y at the bottom), which is helpful:
Because a TPoint is a type, you can create type methods. To try and explain this simply, the type becomes an object, on which the method is called (all of the procedures and functions in the above codehint are methods that act on a TPoint). Look at the following example because my explanation probably doesn't make sense:
Simba Code:
procedure TPoint.print();
begin
writeLn(Self.X); // Self refers to the TPoint which is passed to this procedure
writeLn(Self.Y);
end;
procedure example();
var
p: TPoint;
begin
p := point(10, 20);
p.print(); // Calling the above print method on p
end;
This picture might help you understand:
That is basically what records and type methods looks like, and how to access fields in a record.
PART 2: Creating a TMSObject record
What I'm going to do now is create a new type that will represent a MainScreen Object (something I want to find on the MainScreen, such as a NPC, a Tree, or a Rock). Each object will have certain information associated with it, such as a colour and mouseOverText. Each piece of information will represent a new 'field' (a new variable) in the record.
Simba Code:
type TMSObject = record
Name: String;
Colour: TColorData; // TColorData isn't used that much, but I'll explain below
OverText, ChooseOption: TStringArray;
end;
Above you can see I've created a new type which I called a 'TMSObject' ("Type MainScreen Object"). This type has 4 fields, a name, colour, overText and chooseOptions. Now I can create some variables of this new TMSObject type:
Simba Code:
var
Copper, Tin, Banker: TMSObject;
Now that I've created 3 TMSObjects, I need to assign some information to them. I could assign each field for each object one by one, like:
Simba Code:
procedure loadObjects();
begin
Copper.Name := 'Copper Rock';
Copper.OverText := ['opper'];
Copper.Colour := [4808543, 7, [2, [0.14, 0.15, 0.00]]]; // TColorData structure, see note below
Tin.Name := 'Tin Rock';
// Etc....
end;
Just a quick note: notice how I put all of the colour information (color, tol, hue, sat, cts) together? You might have guessed that the TColorData type is also a record, with fields for color, tolerance, CTS, etc. This is the TColorData type structure:
[Color, Tolerance, [CTS, [Hue, Saturation, Sensitivity]]]
This also means that I am using a 'record within a record', so to access the Copper object's tolerance I would write:
Simba Code:
writeLn(Copper.Colour.tolerance);
This is the easy way to assign information, but imagine if I had a large number of objects, each with a large number of fields; that would take quite a few lines to write. Instead of doing it that way, I'll create a procedure which will take the field information as parameters, before assigning that information to the object:
Simba Code:
procedure TMSObject.Init(_Name: String; _Colour: TColorData; _OverText, _ChooseOption: TStringArray);
begin
Self.Name := _Name; // The procedure parameters start with '_' to differentiate them from the record fields
Self.Colour := _Colour;
Self.OverText := _OverText;
Self.ChooseOption := _ChooseOption;
end;
Now in my loadObjects() procedure I can just pass all the information to the above .Init() method. Remember, I'm calling the .Init() method on the objects themselves:
Simba Code:
procedure loadObjects();
begin
Copper.Init('Copper Rock', [4808543, 7, [2, [0.14, 0.15, 0.00]]], ['opper'], ['ine Cop']);
Tin.Init('Tin Rock', [3808543, 7, [2, [0.13, 0.13, 0.00]]], ['in'], ['ine Tin']);
Banker.Init('Banker NPC', [6508543, 3, [2, [0.11, 0.86, 0.00]]], ['ank'], ['pen Bank']);
end;
Now that I've entered all the information, all I have to do is run loadObjects() when my script starts, to initialise them. The last thing I need to do is write some code to actually find them.
PART 3: Finding the TMSObject
I'm going to use this method to find the colours:
Simba Code:
function findColorsTolerance(var points: TPointArray; color: integer; searchBox: TBox; tol: Integer; settings: TColorSettings): Boolean;
If I wanted to search for the Copper, what I could do is this:
Simba Code:
procedure findCopper();
var
TPA: TPointArray;
begin
findColorsTolerance(TPA, Copper.Colour.color, mainScreen.getBounds(), Copper.Colour.tolerance, Copper.Colour.settings);
// ATPA + mouse code
end;
While that is somewhat effective and clean, I can take it a step further by creating a type method to find any TMSObject:
Simba Code:
function TMSObject.Find(): Boolean;
var
i: Integer;
TPA: TPointArray;
ATPA: T2DPointArray;
begin
writeLn('Searching for: ' + Self.Name); // Prints the TMSObject's name
if Self.Colour.gatherIn(TPA, mainScreen.getBounds()) then // A TColourData method. Puts matching colours into 'TPA'
begin
ATPA := TPA.cluster(15);
if (length(ATPA) < 1) then exit(false);
for i := 0 to high(ATPA) do
begin
mouse(ATPA[i].getBounds().getRandomPoint());
if isMouseOverText(Self.OverText) then // The TMSObject's OverText
begin
fastClick(MOUSE_RIGHT);
result := chooseOption.select(Self.ChooseOption); // The TMSObject's ChooseOptions
if result then break;
end;
end;
end;
writeLn(Self.Name + '.Find(): Result = ' + boolToStr(result));
end;
That is quite straight forward when you break it down. Now I can write code like:
Simba Code:
procedure example();
begin
if Copper.Find() then
writeLn('We just clicked on a copper rock!');
if Banker.Find() then
if bankScreen.isOpen(5000) then
writeLn('We found and opened the bank!');
end;
So that's all there is to it. You can add an endless number of fields to a record. I could have added things such as:
- Price
- ClickType
- DTM or Bitmap
- TPA cluster distance
- SPS map coordinates
- Etc.
I hope you learnt something from this. Have fun with records!