SRL Archive - SRL Member Goodies

[Snippet]FindFreeTargets: TPointArray; (Script included)


Flight

2012-01-26 08:44:57

I'd like to keep this in the Members section for now, atleast until I've developed some combat scripts with this.

First off, I worked really hard on this believe it or not, so if you use it in a script please do give credits… With that being said, thanks again Markus for helping me with that 'Range check error'. :duh:

What this function does is finds available (not in combat) NPCs on your screen (based on your input), and returns a TPA with each point containing the middle of each of those NPCs, sorted from the center of the screen.

Some examples of beta tests on Giant Rats behind Lumbridge castle: (Using CTS3, for what it's worth)



I explained out exactly how this function works, so maybe it would be good for some to learn from. The parameters are obvious, and if you're reading this post then you've already proven you have enough knowledge to understand what each parameter means, if not just ask questions here. For the record, I already know that it's a bit messy. :p


Function FindFreeTargets(Color, Tol, CTS, MinCount, Width, Height: Integer): TPointArray;
var
NPCBox,HPZone: TBox;
TopPoint,MiddleHPZone,
MiddleNPCBox: TPoint;
TPA,GreenPointsA,RedPointsA,
GreenPointsB,RedPointsB,
RTPA: TPointArray;
L,i,CTS_Old,R: Integer;
ATPA: T2DPointArray;
begin
CTS_Old := GetColorToleranceSpeed;
{Find NPC colors}
ColorToleranceSpeed(CTS);
FindColorsSpiralTolerance(MSCX, MSCY, TPA, Color, MSX1, MSY1, MSX2, MSY2, Tol);

if (Length(TPA) < 1) then
begin
ColorToleranceSpeed(CTS_Old);
Exit;
end;
{Split TPA's into boxes defined by 'Width' & 'Height'}
ATPA := SplitTPAEx(TPA, Width, Height); //In this instance, I prefer SplitTPA
L := High(ATPA);
SetArrayLength(ATPA, L+1);
{Loop through each box}
for i := 0 to L do
begin
{If the # of color points found inside the box is >= to 'MinCount'}
if (Length(ATPA[i]) >= MinCount) then
NPCBox := GetTPABounds(ATPA[i]);
{Inside each valid NPC box}
with NPCBox do
begin
{Get middle top point of the box}
TopPoint := Point(((NPCBox.X1+NPCBox.X2)/2), NPCBox.Y1);

if (TopPoint.X < MSX1) then
TopPoint.X := MSX1;
if (TopPoint.Y < MSY1) then
TopPoint.Y := MSY1;
{Using 'TopPoint', specify a static region in which a health bar would be present}
HPZone := IntToBox(TopPoint.X-30, TopPoint.Y-40, TopPoint.X+30, TopPoint.Y);

if (HPZone.X1 < MSX1) then
HPZone.X1 := MSX1;
if (HPZone.X2 > MSX2) then
HPZone.X2 := MSX2;
if (HPZone.Y1 < MSY1) then
HPZone.Y1 := MSY1;
if (HPZone.Y2 > MSY2) then
HPZone.Y1 := MSY2;
{These two aren't really needed}
MiddleHPZone := Point(((HPZone.x1+HPZone.x2)/2), ((HPZone.y1+HPZone.y2)/2));
MiddleNPCBox := Point(((NPCBox.x1+NPCBox.x2)/2), ((NPCBox.y1+NPCBox.y2)/2));

{Search within both the 'NPCBox' and the HPZone for both red & green colors}
ColorToleranceSpeed(3);
FindColorsSpiralTolerance(MiddleNPCBox.X, MiddleNPCBox.Y, GreenPointsA, 49242, NPCBox.X1, NPCBox.Y1, NPCBox.X2, NPCBox.Y2, 2);
FindColorsSpiralTolerance(MiddleNPCBox.X, MiddleNPCBox.Y, RedPointsA, 524459, NPCBox.X1, NPCBox.Y1, NPCBox.X2, NPCBox.Y2, 2);
FindColorsSpiralTolerance(MiddleHPZone.X, MiddleHPZone.Y, GreenPointsB, 49242, HPZone.X1, HPZone.Y1, HPZone.X2, HPZone.Y2, 2);
FindColorsSpiralTolerance(MiddleHPZone.X, MiddleHPZone.Y, RedPointsB, 524459, HPZone.X1, HPZone.Y1, HPZone.X2, HPZone.Y2, 2);
ColorToleranceSpeed(CTS_Old);

{If no red or green is found within the NPCBox and HPZone}
if ((Length(GreenPointsA) = 0) and (Length(RedPointsA) = 0)
and (Length(GreenPointsB) = 0) and (Length(RedPointsB) = 0)) then
begin
{Add the center of the NPCBox to 'RTPA'}
SetArrayLength(RTPA, (Length(RTPA)+1));
RTPA[R] := MiddleNPCBox;
Inc(R);
end;

end;

end;

{Sort the 'RTPA' from the center of the screen}
SortTPAFrom(RTPA, Point(MSCX, MSCY));
{RTPA now holds the center point for each NPCBox of 'avaiable' targets}
Result := RTPA;

end;

Here's a quick script I threw together to fight level 10 Minotaurs in Stronghold of Security:

program MinotaurMasher;

{$DEFINE SRL5}
{$DEFINE SMART}

{$i SRL/srl.simba}
{$i SRL/SRL/misc/paintsmart.simba}


Const
WeaponSpeed = 'Fast';
Training = 'Strength';
HPLevel = 80;

Var
KillCount,Exp,HPExp,
WeaponTimer: Integer;


Procedure DeclarePlayers;
begin
HowManyPlayers := 1;
NumberOfPlayers(HowManyPlayers);
CurrentPlayer := 0;
with Players[0] do
begin
Name := '';
Pass := '';
Level[SKILL_HITPOINTS] := HPLevel;
BoxRewards := ['XP','xp','lamp'];
LampSkill := 'Mining';
Active := True;
end;
end;

Procedure LoadVars;
begin

case Lowercase(WeaponSpeed) of
'fast': WeaponTimer := 800;
'normal': WeaponTimer := 1500;
'slow': WeaponTimer := 3000;
end;

end;

Procedure OnScreen;
var
XPH,XPH2,TTB,
KPH: Integer;
begin

XPH := Round((Exp * 3600) / (GetTimeRunning / 1000));
XPH2 := Round((HPExp * 3600) / (GetTimeRunning / 1000));
KPH := Round((KillCount * 3600) / (GetTimeRunning / 1000));
//TTB := (((w) + (y) + BreakRounds)-GetTimeRunning);

SMART_DrawTextMulti(True, True, ['Minotaur Masher 1.0 by Flight','',
''+MsToTime(GetTimeRunning, Time_Abbrev),'',
''+Capitalize(Training)+' XP: '+IntToStr(Exp)+' ('+IntToStr(XPH)+' / hour)','',
'Hitpoints XP: '+IntToStr(HPExp)+' ('+IntToStr(XPH2)+' / hour)','',
'Kills: '+IntToStr(KillCount)+' ('+IntToStr(KPH)+' / hour)'],
Point(10, 200), SmallCharsNS, 16777215);

//SMART_DrawTextMulti(False, True, ['Time till break: '+MsToTime(TTB,Time_Bare)], Point(570, 445), SmallCharsNS, 65280);
end;

Procedure HumanMMouse(eX, eY, ranX, ranY: Integer);
var
randSpeed: extended;
X,Y,X2,Y2,A,Dist,MP: integer;
begin
A := MouseSpeed;
GetMousePos(X, Y);
Dist := Distance(X, Y, eX, eY);
MP := Round(Dist/150);
if MP < 0 then
MP := 1;

randSpeed := (random(MouseSpeed) / 2.0 + MouseSpeed) / 10.0;
X2 := RandomRange(eX-(A*MP), eX+(A*MP));
Y2 := RandomRange(eY-(A*MP), eY+(A*MP));
WindMouse(X, Y, X2, Y2, 11, 8, 10.0 / randSpeed, 12.0 / randSpeed, 10.0 * randSpeed, 10.0 * randSpeed);
GetMousePos(X, Y);
MMouse(eX, eY, ranX, ranY);
MouseSpeed := A;
end;

Procedure HumanRandomMouse;
var
randSpeed: extended;
x, y, firstSpeed: integer;
begin
case(Random(10)) of
2..8:
begin
firstSpeed := mouseSpeed;

mouseSpeed := RandomRange(5,10);
randSpeed := (random(MouseSpeed) / 2.0 + MouseSpeed) / 10.0;
GetMousePos(x, y);
WindMouse(x, y, RandomRange(x - 75, x + 75), RandomRange(y - 75, y + 75),
30, 55, 10.0 / randSpeed, 12.0 / randSpeed, 10.0 * randSpeed, 10.0 * randSpeed);

mouseSpeed := firstSpeed;
end;
end;
end;

Function IsMoving_F: Boolean;
begin
Result := PixelShift(IntToBox(MMCX-30, MMCY-30, MMCX+30, MMCY+30), 200) > 200;
end;

Function Attacking: Boolean;
var
PBox: TBox;
begin
PBox := IntToBox(245, 135, 280, 185);
Result := (AveragePixelShift(PBox, 250, 500) > 500);
end;

Procedure GetShiftCount;
var
PBox: TBox;
begin
PBox := IntToBox(245, 135, 280, 185);
Writeln(IntToStr(AveragePixelShift(PBox, 250, 500)));
end;

Function DidAntiban: Boolean;
var
i: Integer;
begin
Result := False;
if not LoggedIn then Exit;

i := Random(450);
case i of
1..45:
begin
HumanRandomMouse;
Result := True;
end;
46:
begin
HoverSkill('Strength', False);
Result := True;
end;
47:
begin
HoverSkill('Attack', False);
Result := True;
end;
48:
begin
HoverSkill('Hitpoints', False);
Result := True;
end;
49..450: Exit;
end;

end;

Function Eat: Boolean;
var
i: Integer;
begin
Result := False;

if Not LoggedIn then Exit;
if InvEmpty then Exit;
if (HPPercent >= 60) then Exit;

for i := 0 to 28 do
begin
FindNormalRandoms;

if ExistsItem(i) then
begin
InvMouse(i, mouse_move);

if WaitUpText('Eat', 300) then
begin
ClickMouse2(True);
Wait(RandomRange(875, 990));
if (HPPercent >= 90) then
Exit;
end;

if (i = 28) then
Exit;

end;

end;
end;

Function FindFreeTargets(Color, Tol, CTS, MinCount, Width, Height: Integer): TPointArray;
var
NPCBox,HPZone: TBox;
TopPoint,MiddleHPZone,
MiddleNPCBox: TPoint;
TPA,GreenPointsA,RedPointsA,
GreenPointsB,RedPointsB,
RTPA: TPointArray;
L,i,CTS_Old,R: Integer;
ATPA: T2DPointArray;
begin
CTS_Old := GetColorToleranceSpeed;
{Find NPC colors}
ColorToleranceSpeed(CTS);
FindColorsSpiralTolerance(MSCX, MSCY, TPA, Color, MSX1, MSY1, MSX2, MSY2, Tol);

if (Length(TPA) < 1) then
begin
ColorToleranceSpeed(CTS_Old);
Exit;
end;
{Split TPA's into boxes defined by 'Width' & 'Height'}
ATPA := SplitTPAEx(TPA, Width, Height);
L := High(ATPA);
SetArrayLength(ATPA, L+1);
{Loop through each box}
for i := 0 to L do
begin
{If the # of color points found inside the box is >= to 'MinCount'}
if (Length(ATPA[i]) >= MinCount) then
NPCBox := GetTPABounds(ATPA[i]);
{Inside each valid NPC box}
with NPCBox do
begin
{Get middle top point of the box}
TopPoint := Point(((NPCBox.X1+NPCBox.X2)/2), NPCBox.Y1);

if (TopPoint.X < MSX1) then
TopPoint.X := MSX1;
if (TopPoint.Y < MSY1) then
TopPoint.Y := MSY1;
{Using 'TopPoint', specify a static region in which a health bar would be present}
HPZone := IntToBox(TopPoint.X-30, TopPoint.Y-40, TopPoint.X+30, TopPoint.Y);

if (HPZone.X1 < MSX1) then
HPZone.X1 := MSX1;
if (HPZone.X2 > MSX2) then
HPZone.X2 := MSX2;
if (HPZone.Y1 < MSY1) then
HPZone.Y1 := MSY1;
if (HPZone.Y2 > MSY2) then
HPZone.Y1 := MSY2;
{These two aren't really needed}
MiddleHPZone := Point(((HPZone.x1+HPZone.x2)/2), ((HPZone.y1+HPZone.y2)/2));
MiddleNPCBox := Point(((NPCBox.x1+NPCBox.x2)/2), ((NPCBox.y1+NPCBox.y2)/2));

{Search within both the 'NPCBox' and the HPZone for both red & green colors}
ColorToleranceSpeed(3);
FindColorsSpiralTolerance(MiddleNPCBox.X, MiddleNPCBox.Y, GreenPointsA, 49242, NPCBox.X1, NPCBox.Y1, NPCBox.X2, NPCBox.Y2, 2);
FindColorsSpiralTolerance(MiddleNPCBox.X, MiddleNPCBox.Y, RedPointsA, 524459, NPCBox.X1, NPCBox.Y1, NPCBox.X2, NPCBox.Y2, 2);
FindColorsSpiralTolerance(MiddleHPZone.X, MiddleHPZone.Y, GreenPointsB, 49242, HPZone.X1, HPZone.Y1, HPZone.X2, HPZone.Y2, 2);
FindColorsSpiralTolerance(MiddleHPZone.X, MiddleHPZone.Y, RedPointsB, 524459, HPZone.X1, HPZone.Y1, HPZone.X2, HPZone.Y2, 2);
ColorToleranceSpeed(CTS_Old);

{If no red or green is found within the NPCBox and HPZone}
if ((Length(GreenPointsA) = 0) and (Length(RedPointsA) = 0)
and (Length(GreenPointsB) = 0) and (Length(RedPointsB) = 0)) then
begin
{Add the center of the NPCBox to 'RTPA'}
SetArrayLength(RTPA, (Length(RTPA)+1));
RTPA[R] := MiddleNPCBox;
Inc(R);
end;

end;

end;

{Sort the 'RTPA' from the center of the screen}
SortTPAFrom(RTPA, Point(MSCX, MSCY));
{RTPA now holds the center point for each NPCBox of 'avaiable' targets}
Result := RTPA;

end;

procedure AttackMinotaur;
var
T: Integer;
GoOn: Boolean;
TPA: TPointArray;
label
Search;
begin
if not LoggedIn then Exit;
Eat;

Search:
GoOn := False;
MouseSpeed := RandomRange(14, 22);
TPA := FindFreeTargets(10594473, 3, 3, 10, 20, 20);

if (Length(TPA) > 0) then
begin
HumanMMouse(TPA[0].X, TPA[0].Y, 5, 5);
if WaitUpTextMulti(['ck M','inot', 'taur'], 300) then
begin
ClickMouse2(True);
if not DidRedClick then
goto Search;
end else
goto Search;

FFlag(0);
While IsMoving_F do
Wait(1);

MarkTime(T);
repeat
if not LoggedIn then Exit;
FindNormalRandoms;

if (TimeFromMark(T) > 3000) then
Exit;

if Attacking then
break;

if FindBlackChatMessage('omeone else is fighting') then
break;

until(false)

if FindBlackChatMessage('omeone else is fighting') then
goto Search;

GoOn := True;
MarkTime(T);
repeat
if not LoggedIn then Exit;
FindNormalRandoms;
Eat;

//if (Attacking or DidAntiBan) then
if Attacking then
MarkTime(T);

if (TimeFromMark(T) > WeaponTimer) then
break;
until(false)

if GoOn then
begin
GoOn := False;
Inc(KillCount);
IncEx(Exp, 40);
IncEx(HPExp, 14);
//Inc(KillCount_C);
OnScreen;
end;

end;
end;

begin
Smart_Server := 86;
Smart_Members := True;
Smart_Signed := True;
Smart_SuperDetail := False;

setupSRL();
DeclarePlayers;

LoadVars;
Smart_ClearCanvas;
Wait(RandomRange(500, 675));
OnScreen;

Repeat

//GetShiftCount;
if not LoggedIn then
begin
LoginPlayer;
ClickNorth(SRL_ANGLE_HIGH);
end;

FindNormalRandoms;
AttackMinotaur;

Until(False)

end.


Scaper

2012-01-26 09:08:15

Niiice definatly if i ever decide t bot again use this

mormonman

2012-01-26 13:18:21

With CTS2 you can find the hp bar with one set of color, tolerance, and h/s mods.

ColorToleranceSpeed(2);
SetColorspeed2Modifiers(5.33, 1.31);
FindColorsSpiralTolerance(MSCX, MSCY, TPA, 78384, B.X1, B.Y1, B.X2, B.Y2, 3)

z92

2012-01-26 16:19:22

Very interesting. Thank you for sharing it!

Bobzilla69

2012-01-26 16:25:34

Looks good, similar to what I do in ratinator to see if rats in fight and at actual location.

mormonman

2012-01-26 16:36:13

You should probably add Hmod and Smod parameters…so people can actually make use of CTS 2,3…

Edit: Or you could change the parameter to taking a TPA(the midpoints of npcs found by a custom user procedure), then it would be include-worthy :)

Edit2: Or a ATPA so you can still use the bounds to search in.

Edit3: This is what I use to check each monster for an hp bar:
function FindHPBarDist(w, h: Integer; var P: TPoint): Boolean;
var
TPA, PHPB: TPointArray;
ATPA: T2DPointArray;
HM, SM: Extended;
CTS, i, Dist: Integer;
tB, B: TBox;
TP: TPoint;
begin
Result := False;
if not LoggedIn then Exit;
PHPB := TPAFromBox(IntToBox(227, 121, 293, 149));
B := IntToBox(P.x - w, P.y - Round(h/2), P.x + w, P.y + h);
if (B.X1 < MSX1) then B.X1 := MSX1;
if (B.Y1 < MSY1) then B.Y1 := MSY1;
if (B.X2 > MSX2) then B.X2 := MSX2;
if (B.Y2 > MSY2) then B.Y2 := MSY2;
CTS := GetColorToleranceSpeed;
GetColorspeed2Modifiers(HM, SM);
ColorToleranceSpeed(2);
SetColorspeed2Modifiers(5.33, 1.31);
if FindColorsSpiralTolerance(P.x, P.y, TPA, 78384, MSX1, MSY1, MSX2, MSY2, 3) then
begin
TPA := ClearTPAFromTPA(TPA, PHPB);
ATPA := TPAtoATPAEx(TPA, 55, 7);
if not (Length(ATPA) > 0) then Exit;
SortATPAFromMidPoint(ATPA, P);
for i := 0 to High(ATPA) do
begin
tB := GetTPABounds(ATPA[i]);
Dist := Distance(tB.X1, tB.Y1, tB.X2, tB.Y2);
TP := MiddleTPA(ATPA[i]);
if (Dist > 54) and (PointInBox(TP, B)) then
begin
Result := True;
if Debug then
SMART_DrawBoxEx(True, tB, clGreen);
P := TP;
Exit;
end;
end;
end;
ColorToleranceSpeed(CTS);
SetColorspeed2Modifiers(HM, SM);
end;

Kyle Undefined

2012-01-26 16:36:32

Very nice Flight! :D

P1nky

2012-01-26 17:14:00

Lovely, I shall use this in a upcoming script :)
Of course, will credit you Flight :)

Flight

2012-01-26 22:02:07

From: mormonman
You should probably add Hmod and Smod parameters…so people can actually make use of CTS 2,3…


I was thinking about doing this as well but for the time being I decided against it simply because when testing I stuck with CTS3. You're right though, most people would use CTS1 or 2, so it would be wise to add in those two extra parameters.

From: mormonman

Edit: Or you could change the parameter to taking a TPA(the midpoints of npcs found by a custom user procedure), then it would be include-worthy :)


Now there's a thought… Although a majority of people, I'm assuming, wouldn't know how to make a function to search for colors, split those into an ATPA, and get the middle point in each TPA, and then add them to a new TPA. That suggestion would be good for really custom NPC-finding, but might not be so noob-friendly. :p

From: mormonman

Edit3: This is what I use to check each monster for an hp bar:
function FindHPBarDist(w, h: Integer; var P: TPoint): Boolean;
var
TPA, PHPB: TPointArray;
ATPA: T2DPointArray;
HM, SM: Extended;
CTS, i, Dist: Integer;
tB, B: TBox;
TP: TPoint;
begin
Result := False;
if not LoggedIn then Exit;
PHPB := TPAFromBox(IntToBox(227, 121, 293, 149));
B := IntToBox(P.x - w, P.y - Round(h/2), P.x + w, P.y + h);
if (B.X1 < MSX1) then B.X1 := MSX1;
if (B.Y1 < MSY1) then B.Y1 := MSY1;
if (B.X2 > MSX2) then B.X2 := MSX2;
if (B.Y2 > MSY2) then B.Y2 := MSY2;
CTS := GetColorToleranceSpeed;
GetColorspeed2Modifiers(HM, SM);
ColorToleranceSpeed(2);
SetColorspeed2Modifiers(5.33, 1.31);
if FindColorsSpiralTolerance(P.x, P.y, TPA, 78384, MSX1, MSY1, MSX2, MSY2, 3) then
begin
TPA := ClearTPAFromTPA(TPA, PHPB);
ATPA := TPAtoATPAEx(TPA, 55, 7);
if not (Length(ATPA) > 0) then Exit;
SortATPAFromMidPoint(ATPA, P);
for i := 0 to High(ATPA) do
begin
tB := GetTPABounds(ATPA[i]);
Dist := Distance(tB.X1, tB.Y1, tB.X2, tB.Y2);
TP := MiddleTPA(ATPA[i]);
if (Dist > 54) and (PointInBox(TP, B)) then
begin
Result := True;
if Debug then
SMART_DrawBoxEx(True, tB, clGreen);
P := TP;
Exit;
end;
end;
end;
ColorToleranceSpeed(CTS);
SetColorspeed2Modifiers(HM, SM);
end;


Very neat, I like how this works. I'm going to assume that 'P' is the middle point of each NPC you've previously located? So then you'd just run this function in your actual NPC locating function; probably starting with the closest to the center of the screen just search them all until you've found one that FindHPBarDist returns true?

mormonman

2012-01-26 22:57:35

From: Flight
Very neat, I like how this works. I'm going to assume that 'P' is the middle point of each NPC you've previously located? So then you'd just run this function in your actual NPC locating function; probably starting with the closest to the center of the screen just search them all until you've found one that FindHPBarDist returns true?


I sort thru my tpa's of each possible npc, removing the ones that are dead(hp bar empty), fighting(if its not a multi-zone), or not within certain npc-specific checks(length, height, width, etc). Then I mouse over them until I find the right uptext. It is extremely accurate :)

Usa

2012-01-27 18:56:26

This is great stuff, very useful snipplet.

Flight

2012-01-27 21:42:13

From: mormonman
I sort thru my tpa's of each possible npc, removing the ones that are dead(hp bar empty), fighting(if its not a multi-zone), or not within certain npc-specific checks(length, height, width, etc). Then I mouse over them until I find the right uptext. It is extremely accurate :)


Oh Mormonman, I also, just last night, made a quick function to look for HP bars very near with the player, and thanks to you I was able to correctly use ClearTPAFromTPA to exclude our player's HP bar. The function works WONDERFUL, and it will accurately tell if we're in combat with a nearby NPC. Also, in the instance another player/NPC is fighting near us then the script will resort to pixelshift to tell if we're still animating.

I applied this to that MinotaurMasher and it gained a speed increase of roughly 52% (from 16k / hour to 23k / hour). :) So wohoo! Combat scripts are gonna be a piece of cake now.

Bobzilla69

2012-01-28 03:18:52

you sound like your making great progress on this and making some wonderful functions, the thing i like the mist is we dont just have to do fighting with it, it should work with most gathering skills.

cants wait for some more lovely code :)

Jagex_Fagex

2012-01-28 11:56:30

This is ok to use in our own scripts? If we can, i will probably use this soon :)

'Toxin

2012-02-02 19:49:33

From: Jagex_Fagex
This is ok to use in our own scripts? If we can, i will probably use this soon :)


Of course it is okay, just always always always credit, and rep the man, he's done some great work with this.