1. ## Advanced Lape Reflection Tutorial

Now that the Lape-Reflection include is basically finished and I have some extra time, I have decided to write the last part of the two tutorials I had planned on doing for OSR reflection. This will hopefully teach scripters how the include is laid out internally, and how to use it to your advantage to make your own reflection functions specifically for your needs.

This will be written in a way that assumes you already have a decent knowledge of scripting with Lape. I suggest you read my basic scripting tutorial if you haven’t already.

• Intro
• Words of Caution
• Smart functions
• THooks
• Hook Caching
• Widgets
• Settings array
• Actor record
• Item Record
• Definitions
• Closing

Intro

Since most of the people reading this I assume will not have a lot of java experience or direct knowledge with reflection, I would like to try to clear up a couple things that might help understanding the rest of the tutorial.

In java, there are two 'main' types of fields, static and non static. If the field is static, then outside of the class it was declared in, it can only be accessed with a created instance of that class. Here is a pretty quick and good explanation about the different types of fields.

Now, all a "Hook" is, is the name of a field in an OSR gamepack. This is an example of a class in Gamepack #71 of the "Item" class, that I shortened, and re-factored it some in order to better understand it.

java Code:
public final class Item extends Renderable{    int AMOUNT;    int ID;        protected final g() {        return getItemDefinition(this.ID * -1947959454, (byte)(-37)).k(this.AMOUNT * 19586299, -1951670365);    }}

So, this method, when called, will invoke the getItemDefinition method, taking the ID and Amount as parameters. If you notice, both of these fields are being multiplied by a integer. This is done by jagex as a obfuscation method, to make it harder to get the fields from the client.

This actual piece of code looks like:

java Code:
public final class o extends cg{    int j;    int c;        protected final cl g() {        return x.j(this.c * -1947959454, (byte)(-37)).k(this.j * 19586299, -1951670365);    }}

So, for this gamepack, the Hook for Item_Amount is 'j' with a multiplier of 19586299. The Hook for Item_ID is 'c' with a multiplier of -1947959454.

Words of Caution

While using reflection with smart, you must always make sure that at all times when you load a pointer into memory, whether it is using a Reflect.Smart.GetObject function or a TReflectWidget.Get function that you free it. Once you start making your own functions, there isn't any memory management done by the include. You can easily introduce memory leaks if you are not careful, which can become a big problem. One common occurrence of leaks is when using a loop. When you load anything into memory, make sure that before every continue/break that the pointer is freed, and again at the end of the function. Another way this can be done is with a try except finally loop.

SMART functions

The complete list of all SMART functions are as follows:

Simba Code:
function TReflectionSmart.GetArraySize(  Ref: Integer; Hook: THook; Dimension: Integer): Integer;begin    Result := SmartGetFieldArraySize(Reflect.Smart.Target, Ref, Hook.Path, Dimension);end;function TReflectionSmart.GetFieldObject(Ref: Integer; Hook: THook): Integer;begin  Result := SmartGetFieldObject(Reflect.Smart.Target, Ref, Hook.Path);end;procedure TReflectPointer.GetFieldObject(Ref: Integer; Hook: THook);begin  Self.Reference := SmartGetFieldObject(Reflect.Smart.Target, Ref, Hook.Path);end;procedure TReflectPointer.GetFieldObject(Hook: THook); overload;begin  Self.GetFieldObject(Self.Reference, Hook);end;function TReflectionSmart.GetFieldLongL(Ref: Integer; Hook: THook): Integer;begin    Result := SmartGetFieldLongL(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectPointer.GetFieldLongL(Hook: THook): Integer;begin  Result := SmartGetFieldLongL(Reflect.Smart.Target, Self.Reference, Hook.Path);end;function TReflectionSmart.GetFieldInt(Ref: Integer; Hook: THook): Integer;begin    Result := SmartGetFieldInt(Reflect.Smart.Target, Ref, Hook.Path) * Hook.Multi;end;function TReflectPointer.GetFieldInt(Hook: THook): Integer;begin  Result := SmartGetFieldInt(Reflect.Smart.Target, Self.Reference, Hook.Path)    * Hook.Multi;end;function TReflectionSmart.GetFieldShort(Ref: Integer; Hook: THook): Integer;begin    Result := SmartGetFieldShort(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectionSmart.GetFieldFloat(Ref: Integer; Hook: THook): Extended;begin    Result := SmartGetFieldFloat(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectionSmart.GetFieldDouble(Ref: Integer; Hook: THook): Extended;begin    Result := SmartGetFieldDouble(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectionSmart.GetFieldBoolean(Ref: Integer; Hook: THook): Boolean;begin  Result := SmartGetFieldBoolean(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectionSmart.GetFieldByte(Ref: Integer; Hook: THook): Integer;begin    Result := SmartGetFieldByte(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectionSmart.GetFieldArrayObject(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayObject(Reflect.Smart.Target, Ref, Hook.Path, Index);end;procedure TReflectPointer.GetFieldArrayObject(  Ref: Integer; Hook: THook; Index: Integer);begin    Self.Reference := SmartGetFieldArrayObject(Reflect.Smart.Target, Ref,    Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayInt(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayInt(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayFloat(  Ref: Integer; Hook: THook; Index: Integer): Extended;begin    Result := SmartGetFieldArrayFloat(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayDouble(  Ref: Integer; Hook: THook; Index: Integer): Extended;begin    Result := SmartGetFieldArrayDouble(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayBoolean(  Ref: Integer; Hook: THook; Index: Integer): Boolean;begin    Result := SmartGetFieldArrayBool(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayLongH(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayLongH(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayLongL(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayLongL(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayByte(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayByte(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayShort(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayShort(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArrayChar(  Ref: Integer; Hook: THook; Index: Integer): Integer;begin    Result := SmartGetFieldArrayChar(Reflect.Smart.Target, Ref, Hook.Path, Index);end;function TReflectionSmart.GetFieldArray2DObject(  Ref: Integer; Hook: THook; X, Y: Integer): Integer;begin    Result := SmartGetFieldArray2DObject(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;procedure TReflectPointer.GetFieldArray2DObject(  Ref: Integer; Hook: THook; X, Y: Integer);begin    Self.Reference := SmartGetFieldArray2DObject(    Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DInt(  Ref: Integer; Hook: THook; X, Y: Integer): Integer;begin    Result := SmartGetFieldArray2DInt(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DDouble(  Ref: Integer; Hook: THook; X, Y: Integer): Extended;begin    Result := SmartGetFieldArray2DDouble(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DFloat(  Ref: Integer; Hook: THook; X, Y: Integer): Extended;begin    Result := SmartGetFieldArray2DFloat(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DBoolean(  Ref: Integer; Hook: THook; X, Y: Integer): Boolean;begin    Result := SmartGetFieldArray2DBoolean(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DLongH(  Ref: Integer; Hook: THook; X, Y: Integer): Extended;begin    Result := SmartGetFieldArray2DLongH(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DLongL(  Ref: Integer; Hook: THook; X, Y: Integer): Extended;begin    Result := SmartGetFieldArray2DLongL(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DByte(  Ref: Integer; Hook: THook; X, Y: Integer): Integer;begin    Result := SmartGetFieldArray2DByte(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DChar(  Ref: Integer; Hook: THook; X, Y: Integer): Integer;begin    Result := SmartGetFieldArray2DChar(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray2DShort(  Ref: Integer; Hook: THook; X, Y: Integer): Integer;begin    Result := SmartGetFieldArray2DShort(Reflect.Smart.Target, Ref, Hook.Path, X, Y);end;function TReflectionSmart.GetFieldArray3DObject(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;begin    Result := SmartGetFieldArray3DObject(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;procedure TReflectPointer.GetFieldArray3DObject(  Ref: Integer; Hook: THook; X, Y, Z: Integer);begin    Self.Reference := SmartGetFieldArray3DObject(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DByte(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;begin    Result := SmartGetFieldArray3DByte(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DChar(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;begin    Result := SmartGetFieldArray3DChar(Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DShort(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;begin    Result := SmartGetFieldArray3DShort(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DInt(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Integer;begin    Result := SmartGetFieldArray3DInt(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DFloat(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;begin    Result := SmartGetFieldArray3DFloat(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DDouble(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;begin    Result := SmartGetFieldArray3DDouble(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DBoolean(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Boolean;begin    Result := SmartGetFieldArray3DBoolean(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DLongH(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;begin    Result := SmartGetFieldArray3DLongH(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;function TReflectionSmart.GetFieldArray3DLongL(  Ref: Integer; Hook: THook; X, Y, Z: Integer): Extended;begin    Result := SmartGetFieldArray3DLongL(    Reflect.Smart.Target, Ref, Hook.Path, X, Y, Z);end;procedure TReflectionSmart.FreeObject(Reference: Integer);begin  if not Reflect.Smart.IsNull(Reference) then    SmartFreeObject(Reflect.Smart.Target, Reference);end;procedure TReflectPointer.Free;begin  if not Reflect.Smart.IsNull(Self.Reference) then      SmartFreeObject(Reflect.Smart.Target, Self.Reference);end;function TReflectionSmart.IsPathValid(Ref: Integer; Hook: THook): Boolean;begin    Result := SmartIsPathValid(Reflect.Smart.Target, Ref, Hook.Path);end;function TReflectionSmart.AreEqual(RefOne, RefTwo: Integer): Boolean;begin    Result := SmartIsEqual(Reflect.Smart.Target, RefOne, RefTwo);end;function TReflectionSmart.IsNull(Ref: Integer): Boolean;begin    Result := SmartIsNull(Reflect.Smart.Target, Ref);end;function TReflectionSmart.StringFromString(  JavaString: Integer; Str: String): Integer;begin  Result := SmartStringFromString(Reflect.Smart.Target, JavaString, Str);end;function TReflectionSmart.StringFromBytes(Bytes: Integer; Str: String): Integer;begin  Result := SmartStringFromBytes(Reflect.Smart.Target, Bytes, Str);end;function TReflectionSmart.StringFromChars(Chars: Integer; Str: String): Integer;begin  Result := SmartStringFromChars(Reflect.Smart.Target, Chars, Str);end;function TReflectionSmart.GetFieldString(Ref: Integer; Hook: THook): String;var  StrInt: Integer;begin  StrInt := Reflect.Smart.GetFieldObject(Ref, Hook);  SetLength(Result, 512);  SetLength(Result, Reflect.Smart.StringFromString(StrInt, Result));  Result := Replace(Result, 'Â', '', [RfReplaceAll]);  Result := Replace(Result, #160, #32, [RfReplaceAll]);  Reflect.Smart.FreeObject(StrInt);end;function TReflectionSmart.GetFieldArrayString(  Ref: Integer; Hook: THook; Index: Integer): String;var  StrInt: Integer;begin  StrInt := Reflect.Smart.GetFieldArrayObject(Ref, Hook, Index);  SetLength(Result, 512);  SetLength(Result, Reflect.Smart.StringFromString(StrInt, Result));  Result := Replace(Result, 'Â', '', [RfReplaceAll]);  Result := Replace(Result, #160, #32, [RfReplaceAll]);  Reflect.Smart.FreeObject(StrInt);end;procedure _TReflectionSmartFreeAll;var  I: Integer;begin  for I := 0 to High(_ReflectionSmartList) do    if _ReflectionSmartList[I] <> nil then      PReflectionSmart(_ReflectionSmartList[I])^.Free;  SetLength(_ReflectionSmartList, 0);end;

As you can see, all of the GetObject functions are accessable through the type:
Simba Code:
type  TReflectPointer = record    Reference: Integer;  end;

While you can still access them through Reflect.Smart.GetObject();, for memory management, it is highly advised to use to former in all of your functions. A quick example of how to use one of the object functions to get our local players current animation is as follows:

Simba Code:
function OurAnimation: Integer;var  LocPlayer: TReflectPointer;begin  LocPlayer.GetFieldObject(0, Client_LocalPlayer);  Result := Reflect.Smart.GetFieldInt(LocPlayer.Reference, Actor_Animation);  LocPlayer.Free;end;

The first value in any of the SMART functions is the reference for the field. What this means, is in java, if a field is not static, it can only be accessed through an instance of the method that it is in. So the first part of the function was to load an instance of our local player into memory. Then, using that instance, we can grab any data we want from the instance, in this case, the animation. You may be wondering why the hook is "Actor_Animations" and not "LocalPlayer_Animation", which is a valid observation. I will clear this up in the "Actor" section of the tutorial.

Now, I mentioned that only non-static fields have to have an instance created. This means that any static fields can be accessed directly without creating an instance. So for any THook with the prefix "Client_" you leave the Reference of the Smart function as "0".

THooks

In reflection/Injection, the Hooks are the names of the classes/fields in the gamepack for the current revision. Since we can't invoke methods in smart, the only hooks that are of any use to us are the hooks for fields. If you aren't familiar with java, you can think of a field as a variable that can access and store different types of in-game data. Any field that is of the type integer or long, will almost always have a multiplier that must be multiplied by the field value in order to get the correct value.
It is for this reason that the fields are stored in a type THook.

Simba Code:
type THook = record  Path: string;  Multi: Integer;end;

So each THook holds the path name and the multiplier. Since only Integers and longs can have a multiplier, the multiplier for other types will just stay at 1. This layout of the THooks make it so that whenever an internal smart function is called, it will automatically multiply the field by the multiplier in order to get the correct value. This means you will never have to manually multiply any of the multipliers to get the actual value, it will be done internally for you.

Hook Caching

I was tempted to exclude this part from the tutorial, as it isn't anything that a scripter will use from the include, but for the sake of completeness and fully explaining the include, I will briefly go through it. The name "Hook Caching" isn't an accurate name for what we do in the include, but it’s the name I originally came up with and I never changed it.

For some functions and uses, instead of loading the reference into memory, using the data from it, and then immediately freeing it, we can actually store this reference in an array and access it later, or to free all the items in the array at once. The "HookCache" is a T2DArray that is accessed by using an enum in which to store, retrieve, and free the references when it is needed. The enum is:
Simba Code:
type  TCacheKey = (ckNull, ckRegion, ckSceneTile, ckObject, ckNpcNode, ckNpcDef,  ckNpcName, ckNpcOverHead, ckGroundArray, ckGroundList, ckLocalPlayer); var  HookCache: T2DIntegerArray;

For example, the array HookCache[ckNpcNode] will hold all the references regarding the Npc used throughout the include. This is used in GroundItems and Objects in this same manner. For both of them, we must iterate through the entire array of tiles in order to then iterate the linked list in GroundItems case, or grab the Object for Objects case. The entire loaded region is 104x104 tiles large, which is an enormous amount of tiles to go through! The include handles this as follows: Each time we go to a tile, we load the reference into a slot in the array, then next tile we go up a slot, until the entire plane is iterated. Then, at the end of the function, we free all references at the same time. This made object finding go from ~5 seconds for all tiles to around ~1 second. The Caching for Npc is similar, but there isn't a large increase in performance. LocalPlayer is used in a completely different way, and I will talk about that in the "Actor" section.

Widgets

This is the section that I assume most people will be interested in, and probably the most useful in your scripts. You can make incredibly advanced custom functions using widgets throughout your scripts. Widgets are kind of hard to easily explain, but basically they are just components that are on the Rs screen. You can almost think along the lines of, if it isn't an object, item, player, or npc, and you can do stuff with it/ get data from it, then it is probably a widget. They are mostly all rectangles, excluding sprites, and even then, their bounds are still rectangular.

With the intro out of the way, I think the easiest way to show how widgets are in game, is with the following picture:

The widget array is a 2D array containing widgets, which this include calls "Parent". These Parents may or may not have children. The children are widgets themselves, having all the same features that the parent has. A child can not have a child itself. So, the way this looks is:
Simba Code:
Client_GetWidgets[ContainerIndex][Parent].GetChildren[ChildIndex]

Now hopefully that makes sense, but if not I will try to clarify it. The main function that we use, which accesses a single widget in the array is:

Simba Code:
TReflectWidget.GetWidget(ContainerIndex, ParentIndex);

Using the picture above as a reference, if we do the following:
Simba Code:
var  TestWidget: TReflectWidget;begin  TestWidget.GetWidget(7, 1);  TestWidget.Free;end;

The Variable "TestWidget" corresponds to:

Since that widget does not have any children, that is as far as we can go along the array, there are no more widgets that can be accessed from there. Now that we have the widget, there are many functions that we can use on it. I will only talk about the ones that are useful and not only there in order to help other functions.
Simba Code:
TReflectWidget.GetText; //Returns the text of the widget, IF it has textTReflectWidget.GetItemID;//Returns ItemID, only used in bankTReflectWidget.GetItemStackSizes;//Returns Item StackSizes, only used in bankTReflectWidget.IsHidden;//Returns true if the widget is hidden on our screenTReflectWidget.GetBounds;//Returns a TBox of the widgets boundsTReflectWidget.Interact;//Will click the widget

For the following widget:

We would access it like so:
Simba Code:
var TestWidget, TestChild: TReflectWidget;begin  TestWidget.GetParent(5, 8);  TestChild.GetChild(TestWidget, 2);  TestWidget.Free;  TestChild.Free;end;

Something I would like to Point out is, since "TestWidget" and "TestChild" are both widgets, you can use the exact same functions above on both of them, even on "TestWidget", even though it has a child.

There are three more functions that can be used with widgets, that are needed when debugging widgets, and also for other various functions.
Simba Code:
TReflectWidget.Exists(ContainerIndex, ParentIndex: Integer);//Returns if there is a widget at the indicesTReflectWidget.IsValid(ContainerIndex, ParentIndex: Integer);//Returns if widget exists and is visible at indicesfunction TReflectWidget.HasChild;//Returns if a widget has a child

Please note that .Exists and .IsValid can only be used for finding a parent, and HasChild can only be used on a parent.

Here is an example of a quick debug that will print off all valid parents and child widgets, with their text, if any:

Simba Code:
var  ParentWidget, ChildWidget: TReflectWidget;  I, J, S, ChildWidth: Integer;begin  Reflect.Setup;  for I := 0 to 1000 do    for J := 0 to 1000 do    begin      if (not ParentWidget.Exists(I, J)) then        Continue;      ParentWidget.GetWidget(I, J);      if (not ParentWidget.HasChild) then      begin        Writeln('Parent => ' + ToStr(I) + ', ' + 'Child => ' + ToStR(J) + ' => ' + 'Text => ' + ParentWidget.GetText);        ParentWidget.Free;        Continue;      end;      ChildWidth := Reflect.Smart.GetArraySize(ParentWidget.Reference, Widget_Children, 0);      for S := 0 to ChildWidth do      begin        ChildWidget.GetChild(ParentWidget, S);        Writeln('Parent => ' + ToStr(I) + ', ' + 'Child => ' + ToStR(J) + ' => ' + ' ChildWidget => ' + ToStr(S) + ' => ' + 'Text => ' + ChildWidget.GetText);        ChildWidget.Free;      end;      ParentWidget.Free;    end;end.

I will post a second debug script tomorrow which will draw the bounds of all found widgets.

Settings Array

I mentioned the Settings array in the Basic tutorial, and since it is pretty easy to understand, I don't think I will need to go in to much detail on it. Basically, the settings array is a 1 dimensional integer array [I that has a length of 2000. These integer values can correspond to a lot of different data in the game. The individual integers are commonly refereed to as "varps" and the easiest way to describe them is that they hold in game variables, which change depending on their value. We have two function in the include that are used for them:
Simba Code:
Reflect.Internals.GetSettingArray;//Returns the entire Setting array, used for debuggingReflect.Internals.GetSetting(Setting: Integer);//Returns the value of the varp at "Setting"

The easiest way to find out what a varp does for a specific action, is for example for auto retaliate. If you turn it off, print out entire settings array, turn it on, print out the entire array again, and compare the two on DiffChecker, you will see that one value is different. Then you can use: Reflect.Internals(ThatIndex); to determine if autoretailiate is on or not. They will often correspond to Bits, which can be manipulated using bitshifts. I won't go into detail about this, since if you know how bitwise operators work then this will be easy for you. If you want an example, go to Prayer.simba and see how I determine which prayer is on.

Actor Record

The actor record is a very important feature in this include. In the OSR client Hierarchy, Npcs, Players, and our local player all extend from the actor class. What this means is, every field that actor has, Npcs, Players, and LocalPlayer also have. This is why, like I previously mentioned, we can use "Actor_" hooks on Client_LocalPlayer and we are able to get the fields from it. The way that the include handles these is quite efficient.

Simba Code:
type  TReflectActor = type(TReflectPointer);

So that means, that TReflectActor has one field, Reference: Integer; This makes the functions used in actor, extremely easy to be used with the other classes that extend from it.

Simba Code:
type  TReflectActor = type(TReflectPointer);function TReflectActor.GetSpokenText: string;begin  Result := Reflect.Smart.GetFieldString(Self.Reference, Actor_SpokenText);end;function TReflectActor.GetTile: TTile;begin  Result.X := Reflect.Internals.BaseX +    Reflect.Smart.GetFieldInt(Self.Reference, Actor_WorldX) div 128;  Result.Y:= Reflect.Internals.BaseY +    Reflect.Smart.GetFieldInt(Self.Reference, Actor_WorldY) div 128;end;function TReflectActor.GetQueue(Index: Integer = 0): TTile;begin  Result := Point(Reflect.Smart.GetFieldArrayInt(    Self.Reference, Actor_QueueX, Index) + Reflect.Internals.BaseX,    Reflect.Smart.GetFieldArrayInt(Self.Reference, Actor_QueueY, 0) +    Reflect.Internals.BaseY);end;function TReflectActor.GetAnimation: Integer;begin  Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_Animation);end;function TReflectActor.GetHealth: Integer;begin  Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_Health);end;function TReflectActor.GetMaxHealth: Integer;begin  Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_MaxHealth);end;function TReflectActor.GetInteractingIndex: Integer;begin  Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_InteractingIndex);end;function TReflectActor.GetCombatCycle: Integer;begin  Result := Reflect.Smart.GetFieldInt(Self.Reference, Actor_CombatCycle);end;function TReflectActor.IsMoving: Boolean;begin  Result := (Self.GetQueue.X <> Self.GetTile.X) or    (Self.GetQueue.Y <> Self.GetTile.Y);end;function TReflectActor.IsUnderAttack: Boolean;begin  Result := (Self.GetCombatCycle > Reflect.Internals.GetClientLoopCycle);end;

As you can see, the functions all use: Self.Reference as their object reference. That means, that no matter if we are writing a function for Npcs or Players or LocalPlayer, all we have to do is set the reference of that Record to the reference of it, and all the actor functions work for it.

For LocalPlayer.simba, what we do is:

Simba Code:
type  TReflectLocalPlayer = record(TReflectActor)  Username, Password, Pin: string;  Active: Boolean;end;procedure TReflectLocalPlayer.Create;begin  if HookCache[TCacheKey.ckLocalPlayer][0] = 0 then    Reflect.Mem.GetObject(ckNull, ckLocalPlayer, Client_LocalPlayer, 0, 0)  else  begin    Reflect.Mem.FreeObjects(ckLocalPlayer, False);    Reflect.Mem.GetObject(ckNull, ckLocalPlayer, Client_LocalPlayer, 0, 0)  end;  Self.Reference := HookCache[TCacheKey.ckLocalPlayer][0];end;

Now, in the .Create procedure, that sets the Reference of our LocalPlayer to our current instance. We store that reference in the hook cache, so it is not needed to create and free it each time that we use a function.
If you notice, there are a lot of functions in LocalPlayer.simba that are not using the actor class, and their reference in the SmartGet functions are 0. This is again because the field is static, and can be accessed without an instance created.

Definitions

In the OSR gamepack, Npcs, Objects, and Items all have a separate class in which is called the "Definition." So NpcDefinition is one for example. This definition class contains many useful fields which are not accessible in the main Class for Npc. Getting the additional fields in NpcDefinition isn't too hard, as you can just load the instance of it and grab the fields like the others. But for objects and Items, it is much more difficult for us. For java bots that can invoke methods, it is fairly easy to do, as you can feed a method within the definition class the ID and get the fields that way.

Since simba can't invoke methods, you must iterate a hashtable in order to get the fields from it. This is definitely do-able, but it isn't all that efficient, and you won't always be able to get the definitions for all ID's, since OSR only keeps a certain amount loaded at a time. The way that this include handles this problem, is by having a .txt list of all definitions for npcs, items, and objects, which is loaded into memory at the start of the script. This is accessed whenever a function regarding them is called, and is free'd upon termination. There is nearly no difference in speed, and every definition is loaded.

Closing

There was a lot covered in this tutorial, and I wrote it rather quickly, so if there are any sections that you need clarification on, please let me know and I will expand on it, or if you see any mistakes or typo's. I will also be editing this and making some parts easier to understand as time goes on.
Last edited by Kyle; 01-19-2016 at 12:12 AM.

2. SRL Junior Member
Join Date
May 2013
Posts
75
Mentioned
3 Post(s)
Quoted
48 Post(s)
Originally Posted by elfyyy
...
Could you give us a bit more detail on how is client THooks different from other ones?
Now I assume that if we look at i.e. Actor hooks and want to add a new one we just:
Simba Code:
{Actor = ab} Actor_Animation: THook =             ['be', 2003772957]; Actor_QueueX: THook =                ['by', 1]; Actor_NEWRANDOMHOOK: THook =         ['xx', 1];

Now, if we look at Client hooks, we see full paths:
Simba Code:
Client_LoopCycle: THook =            ['client.n', -663489053]; Client_Region: THook =                      ['ab.dc', 1]; Client_Plane: THook =                       ['aa.hk', 1605115673];
Why is it so?

Also, how is client.class differences in general, if any, compared to other classes?

Also, for example:
Simba Code:
Client_CurrentLevels: THook =        ['client.ho', 1];
I think this Hook only works for our player. Now, if I wanted to see all the cooking levels of fellow players around me, should I look for it in client.class or somewhere else? I am a bit confused now, because I found combat levels of ALL players in player.class, so why it is different now?

Thank you.

3. Originally Posted by MariusK
...
Anything that is listed under client, that has class.field is a static field. They don't neccassarily have to live within the client class, which is why not all of the hooks are client.foo. In an object orientatied language such as java, if a field is made static, you don't have to create an instance of the class in order to access it. This is why you can do:
Simba Code:
Reflect.Smart.GetFieldInt(0, Client.Foo);

vs

Simba Code:
var  Obj: TReflectPointer;begin  Obj.GetFieldObject(0, Client_LocalPlayer);  Reflect.Smart.GetFieldInt(Obj.Reference, Actor_Animation);end;

There are many fields, such as "Client_CurrentLevels" that you mentioned, which ONLY work for our local player, and not any other players. The only fields that work for LocalPlayer, Npcs, and Players are fields located in the Actor class. This is because the Npcs, LocalPlayer, and other Players all extend from Actor class(So they inherit the same fields).

Say for example, we have two classes in java, one is called main which contains our main method, and the other is called foo and contains two fields..

foo.java
JAVA Code:
public class foo {    int bar = 0;    static int meh = 0;}
main.java
JAVA Code:
public class main {    public static void main(String[] args) {        System.out.println(foo.meh); //Static, so we can directly access        foo newInstance = new foo(); //Create a instance of 'foo' called 'newInstance'        System.out.println(newInstance.bar); //Can now access 'bar' through 'newInstance'    }}
Last edited by Kyle; 04-01-2015 at 07:13 PM.

4. SRL Junior Member
Join Date
Apr 2013
Posts
680
Mentioned
13 Post(s)
Quoted
341 Post(s)
@Kyle; Unfortunately we missed another "text" that; as i seem to learn best by comparison and as i can't exactly find anything to compare here.

Code:
Parent => 219, Child => 0 =>  ChildWidget => 0 => Text => Select an Option
Parent => 219, Child => 0 =>  ChildWidget => 1 => Text => Pay servant 5000 coins
Parent => 219, Child => 0 =>  ChildWidget => 2 => Text => Don't
Parent => 219, Child => 0 =>  ChildWidget => 3 => Text => Fire servant
I have debugged the text; but what next?

5. Not sure if this is what you meant but if you want to find a widget then you could i.e. do it like this

Simba Code:
Function RBankOpen() : Boolean;Var  Widget : TReflectWidget;Begin  If Widget.IsValid(12, 4) Then    Result := True;  Widget.Free;End;

edit: The 12 represents what the Parent => points at, and the 4 is what the Child => points at (iirc). Not sure when there is a childwidget though.
Last edited by Joopi; 01-06-2016 at 11:36 AM.

6. SRL Junior Member
Join Date
Apr 2013
Posts
680
Mentioned
13 Post(s)
Quoted
341 Post(s)
Originally Posted by Joopi
Not sure if this is what you meant but if you want to find a widget then you could i.e. do it like this

Simba Code:
Function RBankOpen() : Boolean;Var  Widget : TReflectWidget;Begin  If Widget.IsValid(12, 4) Then    Result := True;  Widget.Free;End;

edit: The 12 represents what the Parent => points at, and the 4 is what the Child => points at (iirc). Not sure when there is a childwidget though.
Well i have been playing around with

Code:
Const
Widget_LevelUp_Container = 219;
LevelUp_Continue = 1;

function R_DidLevelUp(doClick: boolean = false): boolean;
var W : TReflectWidget;
begin
W.GetWidget(Widget_LevelUp_Container, LevelUp_Continue);
result := W.NotNull;
if result then begin
if doClick then
W.Interact;
W.Free;
end;
end;
Excuse the names; i edited it from ineedbots level up widget. I see he used .interact? obviously your function doesn't click? Thank you for what you have posted; it gives another avenue to compare;

I have gotten the widget (Boolean) working, it now return true or false. Which is great! but the (Reflect.Chat.GetChatIndices) return null for the text

**** edit ^

7. SRL Junior Member
Join Date
Apr 2013
Posts
680
Mentioned
13 Post(s)
Quoted
341 Post(s)
This is where i am up to; i just need to sort out the interact or click part.
Code:
Function sOption : Boolean;
Var
Widget : TReflectWidget;
Begin
If Widget.IsValid(219, 0) Then
Result := True;
if result then begin
// if doClick then
Widget.Interact;
Widget.Free;
End;
end
It doesn't matter what i do; i cannot seem to get the data i debugged to interact. I can get other widgets to work.

8. SRL Junior Member
Join Date
Mar 2012
Posts
107
Mentioned
2 Post(s)
Quoted
49 Post(s)
Originally Posted by AFools
@Kyle; Unfortunately we missed another "text" that; as i seem to learn best by comparison and as i can't exactly find anything to compare here.

Code:
Parent => 219, Child => 0 =>  ChildWidget => 0 => Text => Select an Option
Parent => 219, Child => 0 =>  ChildWidget => 1 => Text => Pay servant 5000 coins
Parent => 219, Child => 0 =>  ChildWidget => 2 => Text => Don't
Parent => 219, Child => 0 =>  ChildWidget => 3 => Text => Fire servant
I have debugged the text; but what next?
Simba Code:
function GetOption(s:String):TBox;varParentWidget, ChildWidget:TReflectWidget;i:integer;begin if not ParentWidget.Exists(219, 0) then  exit; ParentWidget.GetWidget(219, 0); for i:= 0 to ParentWidget.GetWidth do begin ChildWidget.GetChild(ParentWidget, i); if Pos(s, ChildWidget.GetText) then  Result := ChildWidget.GetBounds; else  continue;  end; end;

I have not tried this, but it should return the coords of the option that you need, in the form of a TBox.

9. SRL Junior Member
Join Date
Apr 2013
Posts
680
Mentioned
13 Post(s)
Quoted
341 Post(s)
Thank you i will give this a go and report back.

10. Mj
SRL Junior Member
Join Date
Jan 2014
Posts
51
Mentioned
5 Post(s)
Quoted
24 Post(s)
Thanks for the tutorial very helpful

11. Registered User
Join Date
May 2016
Posts
5
Mentioned
0 Post(s)
Quoted
0 Post(s)
This tutorial is awesome, I just started scripting with lape I have experience with Python, C#,Java, Perl. I'm looking for any further documentation related to the Reflection library I can't find much beyond the source code. I'm gonna check and see if I can find any working scripts to see how they're implementing certain things.

I'm also curious about working with widgets, as it is discussed in this thread do I have to run the widget debug script above to get the widget parent/child properties of the widget I wish to use?