Results 1 to 10 of 10

Thread: [SRL] Finding Objects Accurately With RSWalker

  1. #1
    Join Date
    Feb 2012
    Location
    Canada
    Posts
    1,155
    Mentioned
    25 Post(s)
    Quoted
    430 Post(s)

    Default [SRL] Finding Objects Accurately With RSWalker

    Finding Objects Accurately With RSWalker
    Verified Working On: February 12, 2019

    Table of Contents:
    Part 1: Introduction
    Part 2: Getting The Coordinates
    Part 3: Using Colours
    Part 4: Putting It Together
    Part 5: Final Thoughts




    Part 1: Introduction

    What We Will Learn


    In this tutorial, you will learn a method of finding objects on the main screen using your minimap and RSWalker. This object finding technique uses both minimap coordinates and colour finding to allow very accurate object finding. The initial work was done by @slacky and he has documented his information here.

    If you find that you are lost, stuck, or confused about anything at any point in this tutorial, please read through the section again to make sure you have not skipped over anything important. Now, let's get started...



    Part 2: Getting The Coordinates

    How It Works


    Before we get going, we need to understand the general process of what is happening. Something to note is that the entirety of the Runescape world is made up into tiles (Yes, the tiles you see while in-game). For every object in the game, it can be pinpointed down to one or sometimes more tiles. For example, take a generic bank booth. The one showed below is located on a single tile and uses the entirety of the tile. Our goal is to locate only this bank booth using this object finding technique and to filter out the others.



    To do this, we need to find a method of getting the coordinates of the booth.



    The Script


    The coordinates of the bank booth can be gathered using the following collection of code:
    Simba Code:
    program new;
    {$define SMART}
    {$I SRL/OSR.simba}
    {$I RSWalker/Walker.simba}

    const
      WINAPI_CC  = {$IFDEF CPU386}' stdcall'{$ELSE}' win64'{$ENDIF};
      ffi_winapi = {$IFDEF CPU386}ffi_stdcall{$ELSE}ffi_win64{$ENDIF};

      // START FILLING IN HERE

      LOGIN_NAME   = '';           // Account Login Email/Username
      LOGIN_PASS   = '';           // Account Password
      RS_WORLD     = -1;           // Preferred world, -1 = random world
      IS_MEMBER    = False;        // Are you a member? True or False
      MAP = 'world[3_6].png';      // Map that you are using

      // END HERE

    type
      TMSObject = record
        WorldLoc: TPointArray;
        Color: TCTS2Color;
        MinCount: Int32;
        SplitDist: Int32;
      end;

      _EnumWindowsProc = function(wnd:DWORD; Param:Pointer): LongBool;
      TEnumWindowsProc = native(_EnumWindowsProc, ffi_winapi);

    var
      RSW: TRSWalker;
      startupHandle: PtrInt;
      client2: TClient;

    function GetForegroundWindow(): PtrUInt; external 'GetForegroundWindow@user32.dll' + WINAPI_CC;
    function GetWindowThreadProcessId(wnd: PtrUInt; out dwProcessId: DWORD): DWORD; external 'GetWindowThreadProcessId@user32.dll' + WINAPI_CC;
    function EnumChildWindows(hWndParent: DWORD; func: TEnumWindowsProc; Param: Pointer): LongBool; external 'EnumChildWindows@user32.dll' + WINAPI_CC;

    procedure declarePlayers();
    begin
      with Players.New()^ do
      begin
        LoginName  := LOGIN_NAME;
        Password   := LOGIN_PASS;
        IsActive   := True;
        IsMember   := IS_MEMBER;
        World      := RS_WORLD;
      end;
      Players.SetCurrent(0);
    end;

    function WorldToMSTile(Me, ObjLoc: TPoint; Height:Double=0; Offx,Offy:Double=0): TRectangle;
    var
      Angle: Double;
    begin
      ObjLoc := Point(MM2MS.MMCX, MM2MS.MMCY) + (ObjLoc - Me);
      Angle  := Minimap.GetCompassAngle(False);
      ObjLoc := ObjLoc.Rotate(Angle, Point(MM2MS.MMCX, MM2MS.MMCY));
      Result := Minimap.VecToMSRect(Vec3(ObjLoc.x - offx, ObjLoc.y - offy, Height), Angle);
    end;

    function HasFocus(PID: PtrUInt): Boolean;
    var tmp: DWORD;
    begin
      GetWindowThreadProcessId(GetForegroundWindow(), tmp);
      Result := tmp = PID;
    end;

    function GetRSAppletWnd(PID: DWORD): DWORD;
      function GetLastChild(Handle: DWORD; Param: Pointer): LongBool; static;
      begin
        DWORD(Param^) := handle;
        Result := True;
      end;
    var
      p: TSysProc;
      client: DWORD;
    begin
      for p in GetProcesses() do
        if p.Pid = PID then
          Break;
      EnumChildWindows(p.Handle, @GetLastChild, @Result);
    end;

    function SlowMSToMM(MS: TPoint): TPoint;
    var
      x,y: Int32;
      best,test: TPoint;
    begin
      for x:=MM2MS.MMCX-52 to MM2MS.MMCX+52 do
        for y:=MM2MS.MMCY-52 to MM2MS.MMCY+52 do
        begin
          test := Minimap.PointToMs([x,y],0);
          if Distance(test, MS) < Distance(best, MS) then
          begin
            best := test;
            Result := Point(x,y);
          end;
        end;
    end;

    procedure WaitReleaseKey();
    begin
      while client2.GetIOManager.IsMouseButtonDown(0) do Wait(10);
    end;

    function GetClick(out p: TPoint): Boolean;
    begin
      if not HasFocus(smart.PID) then Exit();
      if client2.GetIOManager.IsMouseButtonDown(0) then
      begin
        client2.GetIOManager.GetMousePos(p.x,p.y);
        Result := p.InBox(GetClientBounds);
        WaitReleaseKey();
      end;
    end;

    function SetupLocations(): TPointArray;
    var
      p, me, worldPt: TPoint;
      values: TPointArray;
      i: Integer;
      rect: TRectangle;
    begin
      while True do
      begin
        me := RSW.GetMyPos();
        if GetClick(p) then
        begin
          worldPt := (SlowMSToMM(p) - Point(MM2MS.MMCX, MM2MS.MMCY)) + me;
          values.append(worldPt);
          writeln(worldPt);
        end;

        if (length(values) > -1) then
        begin
          smart.Image.Clear();
          for i := 0 to high(values) do
          begin
            rect := WorldToMSTile(me, values[i]);
            if Mainscreen.GetBounds.Contains(rect.Bounds) then
            begin
              smart.Image.DrawTPA(rect.ToTPA.Connect, $FFFF);
            end;
            Wait(250);
          end;
        end;
      end;
      smart.Image.Clear();
    end;

    begin
      startupHandle := GetNativeWindow;
      smart.EnableDrawing := True;
      srl.Setup([]);
      RSW.Init(MAP, -1);
      declarePlayers();

      client2.Init(PluginPath);
      if GetRSAppletWnd(SMART.PID) = 0 then
        client2.GetIOManager.SetTarget2(startupHandle)
      else
        client2.GetIOManager.SetTarget2(GetRSAppletWnd(SMART.PID));

      if (not SRL.isLoggedIn) then
      begin
         Players.LoginCurrent();
         Mainscreen.SetAngle(True);
      end;

      writeln(startupHandle);
      writeln('WARNING: Moving the screen around and then selecting a point can be inaccurate. Be careful when using.');
      SetupLocations();

      RSW.Free();
      client2.Free();
    end.

    Since this code is fairly complex and a lot is going on, let me do my best to explain what is happening in a very basic manner. When this program is initially run, it will load up a S.M.A.R.T. window. Once Runescape has finished loading, it will attempt to log the player in using the specified details in the highlighted section. Once the player is logged in, you can click anywhere on the screen and a yellow bounding box will appear. The unique ability of this snippet of code is that you're able to click on the screen and select the tile you want while S.M.A.R.T. is still enabled (when you see on the bottom bar of S.M.A.R.T.) and your input is not recognized by the Runescape client.



    Simple, right?!



    The Map


    Let's set this up so that we can get the location of our bank booth. To do this properly, we will need to fill our the constants section of the script.

    Simba Code:
    // START FILLING IN HERE

      LOGIN_NAME   = '';           // Account Login Email/Username
      LOGIN_PASS   = '';           // Account Password
      RS_WORLD     = -1;           // Preferred world, -1 = random world
      IS_MEMBER    = False;        // Are you a member? True or False
      MAP = 'world[3_6].png';      // Map that you are using

      // END HERE

    Obviously, you will want to enter the login information, but the most important part of this section is the MAP. You want to make sure you load the correct part of the map so that the points that we are gathering will be accurate to our location. There are a large variety of maps that you can choose from that come bundled with RSWalker. To find them, navigate to C:\Simba\Includes\RSWalker\maps.

    It is useful to note that depending on where you are in the world and what you are specifically attempting to do, it is recommended that you use the smallest map possible. This is because attempting to locate a specific point on a large map takes a larger amount of time as opposed to a smaller map. Normally, the best case is to use the already sliced up world chunks located in the world folder.

    For our case, we are wanting to use the Varrock East bank. This happens to be located on the [3_6] map in the world folder and looks like this:


    Once you have your map selected and your login information filled out, run the program and let your account log in. Once you are logged in, you can now select the object that you wish to locate. In this case, we want to select that bank booth.




    The Coordinates


    Once you have selected your object and you see the yellow box around it on the screen, we want to look at the output that has been displayed in the Simba console. You should see the following line:
    Code:
    {X = 214, Y = 469}
    This is the X and Y position of the object that you have chosen in relation to the map you have loaded.

    Note that if you change your map to something like the world map instead of the [3_6] slice, the coordinate will not point to the same object as the coordinates are generated based on the size of the map loaded.

    Once we have the coordinates of our object, we can continue on and use colour finding to make sure that we select the proper item in the tile (especially useful if the item does not cover a complete tile).



    Part 3: Using Colours

    Getting The Colours With ACA


    The reason we still want to use colour in addition to the position is that we want to make sure we are as accurate as possible in locating the object we want to click. For example, if we wanted to open the bank using the bankers and we just use the tile location, we would have to click inaccurately inside of that tile. This is not something we want to do, especially if it is an object/NPC that is smaller than the tile size.

    If you haven't already seen this part of my other tutorial, please look through it and understand what we will be doing in order to gather the required colour.

    Using the ACA tool, we want to gather the colours of the bank booth. This is what I've come up with:


    Don't be alarmed with all of the other objects highlighted in red. This is because we will only be checking the bounds of the yellow square for this colour. This means that we will only find the booth located inside of the yellow square since it has this colour inside.

    With this information, you can now plug it into the following line of code:
    Simba Code:
    CTS2(COLOUR, TOL, HUE, SAT);

    After plugging them in, we should get:
    Simba Code:
    CTS2(2316393, 8, 0.06, 1.72);

    Now that we have our colours, let's get clicking!



    Part 4: Putting It Together

    The Code


    Before jumping into it, if you still haven't visited this thread, please do so and read it. I will not be explaininig it the following as much since most of the important information is located there.

    Below is the following program we will be using:
    Simba Code:
    program new;
    {$H-}
    {$define SMART}
    {$I SRL/OSR.simba}
    {$I RSWalker/Walker.simba}

    type
      TMSObject = record
        WorldLoc: TPointArray;  //loctions on the world map
        Color: TCTS2Color;      //must have color
        MinCount: Int32;        //size of TPA
        SplitDist: Int32;       //distance between pixels
      end;

    const
      // START FILLING IN HERE

      LOGIN_NAME   = '';           // Account Login Email/Username
      LOGIN_PASS   = '';           // Account Password
      RS_WORLD     = -1;           // Preferred world, -1 = random world
      IS_MEMBER    = False;        // Are you a member? True or False
      MAP = 'world[3_6].png';      // Map that you are using

      // END HERE

    var
      RSW: TRSWalker;

    procedure declarePlayers();
    begin
      with Players.New()^ do
      begin
        LoginName  := LOGIN_NAME;
        Password   := LOGIN_PASS;
        IsActive   := True;
        IsMember   := IS_MEMBER;
        World      := RS_WORLD;
      end;
      Players.SetCurrent(0);
    end;

    function TMSObject.Find(DoSort: Boolean=True; Expand:Int32=0): TRectArray;
    var
      loc, me: TPoint;
      rect: TRectangle;
      locations, TPA: TPointArray;
      ATPA: T2DPointArray;
    begin
      me := RSW.GetMyPos();
      locations := Copy(Self.WorldLoc);
      if DoSort then Locations.Sorted(me);

      for loc in Locations do
      begin
        rect := RSW.GetTileMSEx(me, loc, 1).Expand(Expand);
        if MainScreen.GetBounds.Contains(rect.Bounds) then
        begin
          if (srl.FindColors(TPA, Color, rect.Bounds) < Self.MinCount) then
            Continue;

          if (Self.SplitDist > 0) then
          begin
            TPA  := rect.Filter(TPA);
            ATPA := TPA.Cluster(Self.SplitDist);
            ATPA.SortByMiddle(rect.Mean);
            Result += ATPA[0].MinAreaRect;
          end else
            Result += rect.Expand(-Expand);
        end;
      end;
    end;

    procedure Test();
    var
      i: Int32;
      obj: TMSObject;
      rectangles: array of TRectangle;
    begin
      while True do
      begin
        //**************************************************************************
        obj := [[[XXX, YYY], [XXX, YYY]], CTS2(CCCCCC, TTT, H.HH, S.SS), UUU, ZZZ];
        //**************************************************************************
        rectangles := obj.Find(True, 3);
        smart.Image.Clear();
        for i:=0 to High(rectangles) do
          smart.Image.DrawTPA(rectangles[i].ToTPA.Connect, $00FFFF);
      end;
    end;


    begin
      smart.EnableDrawing := True;
      srl.Setup([]);
      RSW.Init(MAP, -1);
      declarePlayers();

      if (not SRL.isLoggedIn) then   //If not logged in then..
      begin
         Players.LoginCurrent();     //Log player in
         Mainscreen.setAngle(True);  //Sets the camera angle to the highest point
      end;

      test();

      RSW.Free();
    end.

    If you look through the code, you will notice a section where I have highlighted with comments that looks like this:
    Simba Code:
    //**************************************************************************
    obj := [[[XXX, YYY], [XXX, YYY]], CTS2(CCCCCC, TTT, H.HH, S.SS), UUU, ZZZ];
    //**************************************************************************

    This is where we create the object that we want to find on our main screen. Let me explain what each entry does.
    • '[XXX, YYY]': This is the location for your X and Y coordinates of the tile you are wanting to search for. You will notice that there are two slots in this array. This is useful in the case you want to locate another bank booth that might be either next to, or a little farther over from the first one.
    • 'CTS2(CCCCCC, TTT, H.HH, S.SS)': This is the colour that we determined earlier using ACA.
    • 'UUU': This is the minimum number of points the object must have of the given colour. If the number of points in the TPA falls below this, the object is ignored.
    • 'ZZZ': This is the maximum distance that is allowed between two pixels before the TPA ends.


    To plug this in with our information we gathered, we should get something like this:
    Simba Code:
    //**************************************************************************
    obj := [[[162, 652]], CTS2(2316393, 8, 0.06, 1.72), 200, 8];
    //**************************************************************************



    The Final Product


    Now your code should look something like this:
    Simba Code:
    program new;
    {$H-}
    {$define SMART}
    {$I SRL/OSR.simba}
    {$I RSWalker/Walker.simba}

    type
      TMSObject = record
        WorldLoc: TPointArray;  //loctions on the world map
        Color: TCTS2Color;      //must have color
        MinCount: Int32;        //size of TPA
        SplitDist: Int32;       //distance between pixels
      end;

    const
      // START FILLING IN HERE

      LOGIN_NAME   = '';           // Account Login Email/Username
      LOGIN_PASS   = '';           // Account Password
      RS_WORLD     = -1;           // Preferred world, -1 = random world
      IS_MEMBER    = False;        // Are you a member? True or False
      MAP = 'world[3_6].png';      // Map that you are using

      // END HERE

    var
      RSW: TRSWalker;

    procedure declarePlayers();
    begin
      with Players.New()^ do
      begin
        LoginName  := LOGIN_NAME;
        Password   := LOGIN_PASS;
        IsActive   := True;
        IsMember   := IS_MEMBER;
        World      := RS_WORLD;
      end;
      Players.SetCurrent(0);
    end;

    function TMSObject.Find(DoSort: Boolean=True; Expand:Int32=0): TRectArray;
    var
      loc, me: TPoint;
      rect: TRectangle;
      locations, TPA: TPointArray;
      ATPA: T2DPointArray;
    begin
      me := RSW.GetMyPos();
      locations := Copy(Self.WorldLoc);
      if DoSort then Locations.Sorted(me);

      for loc in Locations do
      begin
        rect := RSW.GetTileMSEx(me, loc, 1).Expand(Expand);
        if MainScreen.GetBounds.Contains(rect.Bounds) then
        begin
          if (srl.FindColors(TPA, Color, rect.Bounds) < Self.MinCount) then
            Continue;

          if (Self.SplitDist > 0) then
          begin
            TPA  := rect.Filter(TPA);
            ATPA := TPA.Cluster(Self.SplitDist);
            ATPA.SortByMiddle(rect.Mean);
            Result += ATPA[0].MinAreaRect;
          end else
            Result += rect.Expand(-Expand);
        end;
      end;
    end;

    procedure Test();
    var
      i: Int32;
      obj: TMSObject;
      rectangles: array of TRectangle;
    begin
      while True do
      begin
        //**************************************************************************
        obj := [[[214, 469]], CTS2(2316393, 8, 0.06, 1.72), 200, 8];
        //**************************************************************************
        rectangles := obj.Find(True, 3);
        smart.Image.Clear();
        for i:=0 to High(rectangles) do
          smart.Image.DrawTPA(rectangles[i].ToTPA.Connect, $00FFFF);
      end;
    end;


    begin
      smart.EnableDrawing := True;
      srl.Setup([]);
      RSW.Init(MAP, -1);
      declarePlayers();

      if (not SRL.isLoggedIn) then   //If not logged in then..
      begin
         Players.LoginCurrent();     //Log player in
         Mainscreen.setAngle(True);  //Sets the camera angle to the highest point
      end;

      test();

      RSW.Free();
    end.

    Now lets give it a run! We should have our character log in and our object appear on the screen like so:



    Part 5: Final Thoughts

    Conclusion


    Now that you have figured out a method of locating static objects using both the minimap location and the colour of the object, feel free to experiment with it in your scripts!

    If you happen to have any questions, feel free to ask them below. If there are any problems, errors, or anything that should be fixed, please let me know.
    Last edited by StickToTheScript; 02-12-2019 at 10:27 PM. Reason: Updated

  2. #2
    Join Date
    Sep 2012
    Location
    Netherlands
    Posts
    2,729
    Mentioned
    192 Post(s)
    Quoted
    1461 Post(s)

    Default

    very nice tut

  3. #3
    Join Date
    Sep 2010
    Posts
    5,754
    Mentioned
    136 Post(s)
    Quoted
    2736 Post(s)

    Default

    Nice, I fiddled with a similar concept with SPS but is wasn't nearly as accurate as RSwalker

  4. #4
    Join Date
    Feb 2012
    Posts
    173
    Mentioned
    2 Post(s)
    Quoted
    82 Post(s)

    Default

    Thank you for posting this tutorial

  5. #5
    Join Date
    Mar 2013
    Location
    Argentina
    Posts
    758
    Mentioned
    27 Post(s)
    Quoted
    365 Post(s)

    Default

    Great Tut
    Formerly known as Undorak7

  6. #6
    Join Date
    Jul 2018
    Posts
    22
    Mentioned
    0 Post(s)
    Quoted
    8 Post(s)

    Default

    Can it only be used with SMART?

  7. #7
    Join Date
    Feb 2012
    Location
    Canada
    Posts
    1,155
    Mentioned
    25 Post(s)
    Quoted
    430 Post(s)

    Default

    Quote Originally Posted by felparers View Post
    Can it only be used with SMART?
    Nope. It can work with any RS client. Only the drawing needs to be done in SMART. If you ignore the drawing, then you should be fine.

  8. #8
    Join Date
    Apr 2015
    Posts
    2
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

    Hey guys, This looks amazing and I would love to use it, I am trying to use the script for coordinates but I am getting an "Access Violation", I have tried running as admin with no luck

    "Access violation" at line 209, column 41 in file "C:\Simba 1.3 32\Includes\RSWalker\Walker.simba"
    Code:
    xcorr   := WorldSample.MatchTemplate(mmsample, Self.TMFormula);
    I am using Simba 1.3-rc1 32bit with RSWalker 1.3.0

    Fixed:

    Had to remove the -1 from

    Code:
    RSW.Init(MAP, -1);
    Last edited by EmberZ; 06-27-2019 at 03:50 PM.

  9. #9
    Join Date
    Aug 2019
    Posts
    29
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

    Thanks for another very helpful guide.

  10. #10
    Join Date
    Feb 2012
    Location
    Florida
    Posts
    185
    Mentioned
    0 Post(s)
    Quoted
    3 Post(s)

    Default

    EDIT: Yeah after testing this over n over. It seems a medium zoom level is optimal for this to work.
    Zoomed all the way out. This will not produce accurate tile locations over distance.
    This is good for providing reliable search locations.
    Heresy grows from Idleness. Its time to wake up

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •