# Thread: Using records to create and find mainscreen objects

1. ## Using records to create and find mainscreen objects

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 pend;

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 codeend;

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:

1. Price
2. ClickType
3. DTM or Bitmap
4. TPA cluster distance
5. SPS map coordinates
6. Etc.

I hope you learnt something from this. Have fun with records!
Last edited by The Mayor; 05-23-2015 at 04:54 PM.

2. Great tutorial! Written very cleanly! Your tutorials are always great!

3. Makes sense for things you'd need to use in more than one way, nice tutorial

4. Nice! I remember seeing this when I dug through your gelatinous abomination script. This is perfect for a script I've been meaning to start.

5. I've never used records because I never understood them properly, now I do. Thanks for another great tutorial, Maymay.

On an unrelated note, how come there's no advanced object finding function in SRL-6? We have the basic mainScreen.findObject, so why not add a more accurate one that works in most situations?

6. SRL Member
Join Date
May 2012
Posts
491
Mentioned
23 Post(s)
Quoted
227 Post(s)
Should perhaps include T2DColorData?
I haven't seen it being used in many scripts, and combining 2 (or more) colors with low tolerances into a single TPA and then clustering them worked better then just clustering based on 1 color.
It allowed me to cluster based on shorter distances.

I sometimes even sort the clustered ATPA by size (color points), as that most often gives the closest TPA (more pixels/model onscreen).

Perhaps add an example with WaitTypeFunc near the end? That is also something I love using but barely see being used.

You could even talk a little about making includes with these records?
Would be usefull for some if they use multiple scripts with similar functions.

7. Thank you, will help me tidy up my script.

Edit: Took me a while to get my head around but I got there eventually.
Last edited by GetHyper; 06-17-2015 at 12:40 PM.

8. Originally Posted by StickToTheScript
Great tutorial! Written very cleanly! Your tutorials are always great!
Thanks

Originally Posted by BMWxi
Makes sense for things you'd need to use in more than one way, nice tutorial
Thanks

Originally Posted by evilcitrus
Nice! I remember seeing this when I dug through your gelatinous abomination script. This is perfect for a script I've been meaning to start.
Don't think anything like this was in GAO

Originally Posted by Incurable
I've never used records because I never understood them properly, now I do. Thanks for another great tutorial, Maymay.

On an unrelated note, how come there's no advanced object finding function in SRL-6? We have the basic mainScreen.findObject, so why not add a more accurate one that works in most situations?
mainScreen.findObject isn't really that basic and it will most probably work in most situations.

Originally Posted by Thomas
Should perhaps include T2DColorData?
I haven't seen it being used in many scripts, and combining 2 (or more) colors with low tolerances into a single TPA and then clustering them worked better then just clustering based on 1 color.
It allowed me to cluster based on shorter distances.

I sometimes even sort the clustered ATPA by size (color points), as that most often gives the closest TPA (more pixels/model onscreen).

Perhaps add an example with WaitTypeFunc near the end? That is also something I love using but barely see being used.

You could even talk a little about making includes with these records?
Would be usefull for some if they use multiple scripts with similar functions.
Those things are beyond the scope of this tutorial

Originally Posted by GetHyper
Thank you, will help me tidy up my script.

Edit: Took me a while to get my head around but I got there eventually.
Good to hear!

9. Originally Posted by The Mayor
Don't think anything like this was in GAO
Ahh you're right, it was your Runespan script. GAO used mayorObjectFinder()

10. Cheers for the tutorial, I'll be implementing such features into my mining script to cut down on the if... else statements and to just tidy it up in general. Thanks again!

11. Registered User
Join Date
May 2015
Posts
23
Mentioned
0 Post(s)
Quoted
5 Post(s)
Thanks for this tutorial, I will be using this soon in my next script(s)..

12. SRL Junior Member
Join Date
Jan 2013
Posts
145
Mentioned
0 Post(s)
Quoted
56 Post(s)
this is awesome.

How different is this from classes in python?