RatKiller: A Making of
Over the years that I have been a part of this community, I have learned a great deal from the tutorial section. In fact, I owe this community a lot. I recently made the decision to quit my degree that I have been studying for 2 years and reapply for Computer Science. This community gave me the chance to hone my skills, and gave me confidence that I am suitable for such a course.
I am going to be making a tutorial as a proof of concept of my recently released MMToMS, to show it’s usefulness, but hopefully showing you a few hints and tips along the way.
This was originally intended to be a step by step tutorial, but I got carried away with the script. I personally feel it’s the details which make a script, so I’d like to explain my way of thinking in this ‘Making of’.
I’ve purposely decided to leave things that are generic out in order to focus on the more exciting parts.
OK, let’s jump in.
Simba Code:
type
MSObject = record
Col, Tol, TPADist, MinCount: Integer;
Hue, Sat: Extended;
Uptext: TStringArray;
MMDot: String;
end;
var
Rat, Bones: MSObject;
BonesDTM: Integer;
I like to define my variables before anything else. It sets the tone for the rest of the code, means less rewriting later, and it also helps to avoid repeating yourself.
As I had decided that I was going to make a Rat Fighting script that picks up and buries bones, a suitable way to define this data was immediately clear. All the script needs is two Main Screen objects, I knew I was going to use a typical TPA Objectfinding method with some added features. So I created a ‘MSObject’ record with common variables between the two objects and defined global variables ‘Rat’ and ‘Bones’ to have this Type.
I also needed a way to find Bones once they were in the inventory in order to bury them. I chose to use a DTM, since there was only one, but in a larger script would probably look into BlackLists.
I will explain ‘MSObject.MMDot’ further on, but take note that it’s defined here as it’s important.
Simba Code:
procedure LoadObjects;
begin
with Rat do
begin
Col := 4408390;
Tol := 10;
TPADist := 10;
Hue := 1.39;
Sat := 0.16;
UpText := ['ack Gian'];
MMDot := 'yellow';
MinCount := 50;
end;
with Bones do
begin
Col := 12501714;
Tol := 19;
TPADist := 5;
Hue := 0.23;
Sat := 0.96;
UpText := ['ones'];
MMDot := 'red';
MinCount := 40;
end;
BonesDTM := DTMFromString('mrAAAAHic42BgYGhlYmBoBuJaIG5kgvD7gbgOyp/EyMAwC4hnAPE0IO4B4qlQuhOIp/T2Ak1hwon/A0l+PJiRAIYBAMm8C/Y=');
end;
This procedure just declares the values of our variables. I used AutoColorAid to calculate appropriate CTS Speed Modifiers.
Now is probably a good time to give an overview of the method I used to find my objects. I will go into further detail after each block of code.
‘MMtoMSTile()’ is found in my include (3DProjection.simba) which is attached at the end of this tutorial. It calculates in 3D the Main Screen tile co-ordinates based on the compass angle. You can input a Mini Map co-ordinate and it will out put the co-ordinate of the tile in the Main Screen.
Another useful function, which is included in the include, is ‘GetMMDotsOnMS2’. This returns a TPointArray of all the tile co-ordinates of a specified Mini Map dot type, i.e. NPC = yellow, Ground Item = red.
We will use the information from these dots to significantly increase the accuracy and efficiency of our Object Finding. The real power of this is that you can use tiny minimum count checks, because by using the minimap, you know the object is there. This means that even if an object is covered by a tree, it will generally find it.
Simba Code:
function FindMSObjects(var Coords: TPointArray; Obj: MSObject): Boolean;
var
cts, Len, i, c: Integer;
TPA: TPointArray;
ATPA: TPointArrayArray;
begin
cts := GetColorToleranceSpeed;
ColorToleranceSpeed(2);
SetColorSpeed2Modifiers(Obj.Hue, Obj.Sat);
Result := False;
c:= 0;
if FindColorsTolerance(TPA, Obj.Col, MSX1, MSY1, MSX2, MSY2, Obj.Tol) then
begin
Result := True;
ATPA := SplitTPA(TPA, Obj.TPADist);
Len := Length(ATPA);
SetLength(Coords, Len);
for i := 0 to Len-1 do
begin
if Length(ATPA[i]) > Obj.MinCount then
begin
//SMART_DrawBoxEx(False, GetTPABounds(ATPA[i]), clAqua);
Coords[c] := MiddleTPA(ATPA[i]);
Inc(c);
end;
end;
end;
SetLength(Coords, c)
ColorToleranceSpeed(cts);
SetColorSpeed2Modifiers(0.2, 0.2);
end;
The above is pretty traditional TPA Object finding, it it returning a TPointArray with all the occurrences of an object. It uses the information provided by ACA earlier to do this.
Simba Code:
function FindMSObjectsByDot(var Coords: TPointArray; Obj: MSObject): Boolean;
var
BoxArr: TBoxArray;
Len, Len2, i, j, c: Integer;
TPA, TPA2: TPointArray;
begin
TPA := GetMMDotsOnMS2(Obj.MMDot, False);
Len := Length(TPA);
SetLength(BoxArr, Len);
for i := 0 to Len-1 do
BoxArr[i] := MakeBox(TPA[i].x-70, TPA[i].y-70, TPA[i].x+70, TPA[i].y+70);
if FindMSObjects(TPA2, Obj) then
begin
Len2 := Length(TPA2);
SetLength(Coords, Len2);
c := 0;
for i := 0 to Len2-1 do
for j := 0 to Len-1 do
if PointInBox(TPA2[i], BoxArr[j]) then
begin
Coords[c] := TPA2[i];
Inc(c);
Break;
end;
SetLength(Coords, c);
Result := (c > 0);
end;
end;
This is where it differs slightly, it is checking if the colour aproximated Object Points coincide with the 3D projected from Mini Map Co-ords. It does this by creating a box around each projected point and checking if any of the colour points are inside it.
Simba Code:
function InBadBox(p: TPoint): Boolean;
var
i, Len: Integer;
badboxarray: TBoxArray;
begin
Result := False;
BadBoxArray := FindAllHPBars(MSCX, MSCY, MSX1, MSY1, MSX2, MSY2);
Len := Length(badboxarray);
for i := 0 to Len-1 do
if PointInBox(p, MakeBox(BadBoxArray[i].X1, BadBoxArray[i].Y1, BadBoxArray[i].X2, BadBoxArray[i].Y2+60)) then
begin
Result := True;
Break;
end;
end;
function InRatFight: Boolean;
var
aps: Integer;
begin
aps := AveragePixelShift(MakeBox(MSCX-10, MSCY-25, MSCX+15, MSCY+15), 50, 2000);
Result := (aps > 100);
end;
function KillRat: Boolean;
var
TPA: TPointArray;
i, Len, x, y, a, b: Integer;
begin
Result := False;
if FindMSObjectsByDot(TPA, Rat) then
begin
Len := Length(TPA);
SortTPAFrom(TPA, Point(MMCX, MMCY));
for i := 0 to Len-1 do
begin
if inBadBox(TPA[i]) then
Continue;
MMouse(TPA[i].x, TPA[i].y, 3, 3);
if WaitUpTextMulti(Rat.UpText, 300) then
begin
GetMousePos(x, y);
Mouse(x, y, 0, 0, True);
if FindColorTolerance(a, b, 65278 {YellowWalkingCross}, x, y, x+1, y+1, 5) then
begin
WriteLn('MISSED');
Exit;
end;
if GetColor(x, y) = 65278 then
FFlag(2);
Wait(500);
repeat
Antiban;
until (not InRatFight) or (not srl_InFight)
Result := True;
Break;
end;
end;
end;
end;
There are a few details here that aren’t generally talked about in the tutorials I have seen.
‘KillRat’ is a procedure in which we need to use our previously made object finding routine, move the mouse to the object and test for the desired uptext. If this test is positive then we click to attack the rat. We then can perform various antiban routines whilst in the fight.
I realised that the Object Finder would sometimes find a rat that was already in combat, so I created the function InBadBox, which checks if the point is in a ‘Bad box’ which are generated from any HP bars that are on the screen.
I noticed that sometimes, due to the rats moving around pretty rapidly, it would miss. This isn’t much as of a problem, personally when I’m playing legit I sometimes miss too. What is a problem, is that it continues to wait until the player has stopped moving and enters the InFight loop. This look really unatural so I thought about how I could tell immediately whether it had missed the rat or not and avoid this unnecessary delay.
What I ended up doing was a simple check on the mouse co-ordinates for the cross that your cursor makes when clicking somewhere. If you click the object it is red, if you don’t it is yellow. Simple but effective. If the color is yellow, it Exits the procedure and the main loop immediately causes it to look for the next rat.
Another cool thing I used which I haven’t taken advantage of before is SRL’s animation procedures. It was incredibly easy to set up. I just made it do a few WriteLn’s telling me what the average pixel shift was while it was in a fight, and while it was not, giving me the information I needed to do a reliable check. I also used SRL’s ‘srl_InFight’ as a back up, which checks for the players health bar. This would handle anytime I was under a tree.
Simba Code:
procedure PickUpBones;
var
TPA: TPointArray;
i, Len, x, y, c: Integer;
begin
for c := 0 to Random(5) + 1 do
if FindMSObjectsByDot(TPA, Bones) then
begin
Len := Length(TPA);
SortTPAFrom(TPA, Point(MMCX, MMCY));
for i := 0 to Len-1 do
begin
MMouse(TPA[i].x, TPA[i].y, 3, 3);
if WaitUpTextMulti(['Take'], 100) then
begin
GetMousePos(x, y);
Mouse(x, y, 0, 0, False);
if WaitOptionMulti(Bones.UpText, 300) then
begin
FFlag(2);
Wait(500);
if i = Len-1 then
Exit;
Break;
end;
end;
end;
end;
end;
This works very similarly to KillRat, taking advantage of the minimap dots.
Simba Code:
function WeightedChoice(Weights: TIntegerArray): Integer;
var
i, Len, t, r: Integer;
Totals: TIntegerArray;
begin
Len := Length(Weights);
SetLength(Totals, Len);
t := 0
for i := 0 to Len-1 do
begin
IncEx(t, Weights[i]);
Totals[i] := t;
end;
r := Random(t);
for i := 0 to Len-1 do
if r < Totals[i] then
begin
Result := i;
Break;
end;
end;
procedure MainLoop;
begin
repeat
if KillRat then
begin
SMART_ClearCanvas;
if InvFull then
ClickAllItems('dtm', BonesDTM, '', 400+ random(200), []);
if WeightedChoice([3, 7]) = 0 then
PickUpBones;
end;
SMART_ClearCanvas;
until(not LoggedIn)
end;
Very standard main loop here. One thing I like is the use of ‘WeightedChoice’. It gives you complete control over the probability that something will happen. It’s use would probably be shown better in an anti ban routine. In this script I wanted to mimic myself, I don’t pick up bones after every kill. I tend to pick up quite randomly, but I still prefer the actual killing
Simba Code:
begin
MouseSpeed := 15;
Smart_Server := 1;
Smart_Members := False;
Smart_Signed := True;
Smart_SuperDetail := False;
SetupSRL;
SMART_ClearCanvas;
LoadObjects;
MainLoop;
end.
Last thing, MouseSpeed is something that you should think about. It completely depends on the task at hand and you should try to thing about what it would be if you were playing legit. For this script I chose a fast MouseSpeed because my targets are mobile.
I have attached the complete script and include to this post. Try it if you like, it is very fast and fun to watch. I may add Multiplayer and AntiBan at some point to release. I haven't decided yet.
And that is it, I hope you enjoyed reading this and you found something that might be useful. Please check out my MMtoMSTile functions as well!
The End.