SRL Archive - Research & Development Lounge

Idea - Walking


Sir R. M8gic1an

2010-05-19 11:27:10

Just a quick idea for walking.. To throw it out there..

Open World Map.
Find Bitmap of Position to walk to.
Find Current Location
Identify all Walkable roads (autocolor roads, find roads, save as TPA)
Use a TPA algorithm to select a point every x pixels
Save BitmapArray of TPoints, with a small bitmap of every walk point (must be larger than road width or length)
Save a Bitmap of every step than can not be walked on the road. (Preset size, 4*4 ?)

Use the bitmaps to walk to wherever you want to go.
FreeBitmaps at Bitmap[t-2].
Bitmap[t-1] can be used to retrace a step if necessary.

= Profit.

What do you guys think?

~RM

marpis

2010-05-19 11:36:47

Sounds like mixter's SPAMS
http://villavu.com/forum/showthread.php?t=54803

Sir R. M8gic1an

2010-05-19 11:45:14

From: marpis
Sounds like mixter's SPAMS
http://villavu.com/forum/showthread.php?t=54803


Indeed. I just re-read the thread now.

A possible advantage might be that it creates the bitmap 'on the fly' ? Avoids the whole downloading of the map.

Also, I can't seem to find where his walker chooses how to walk. A feature I rather like from this idea is the fact that it'll walk on the roads. (As if it were WebWalk)

Other than that, this idea does not really seem that new to me anymore.

~RM

marpis

2010-05-19 11:52:10

From: Rasta Magician
Indeed. I just re-read the thread now.

A possible advantage might be that it creates the bitmap 'on the fly' ? Avoids the whole downloading of the map.

Also, I can't seem to find where his walker chooses how to walk. A feature I rather like from this idea is the fact that it'll walk on the roads. (As if it were WebWalk)

Other than that, this idea does not really seem that new to me anymore.

~RM


I think mixster has a plugin for the system. And it did walk on roads only, quite successfully.
The cool thing would be to find character's location on the worldmap without opening the map in game :rolleyes:

Sir R. M8gic1an

2010-05-19 11:56:59

From: marpis
I think mixster has a plugin for the system. And it did walk on roads only, quite successfully.
The cool thing would be to find character's location on the worldmap without opening the map in game :rolleyes:


Mixster's system might be efficient at that…

Just get a bitmap of the whole mm, and compare it to his world bitmap. I doubt there'd be any two exactly equal locations.

~RM

lordsaturn

2010-05-19 21:19:20

NO world map. period.

Treat the walkable paths as you would reflection hooks, they would only need to be updated every runescape update.

Edit: that's a lot of overhead..

marpis

2010-05-20 01:32:19

From: Rasta Magician
Mixster's system might be efficient at that…

Just get a bitmap of the whole mm, and compare it to his world bitmap. I doubt there'd be any two exactly equal locations.

~RM


I once tried to do this, but I failed. After that I got an idea how it could be done:
- Capture bmp of minimap
- Split it in 5x5 squares
- Record color info from the squares, like avereage H/S/L, what color it has most etc.
- Analyzing the worldmap: first do a TPA-search and TPAtoATPA with the most used color of the 1st square. Let's say there would be 60 squares. If you find an area on the worldmap that matches square one, analyze the 10x10 square next to it. If that matches too, check if you can find the next square too etc etc.
- if many squares were found in the worldmap matching in colors next to each other in the same order as in current MM, we have found our location on the worldmap with a 5-pixel accuracy! :)

lordsaturn

2010-05-20 23:27:15

My idea was more like… comparing the bitmap of the world map to your portion of the minimap, use a 49x49 kernel or whatever , abs(mmPixel-bmpPixel), and whichever kernel returned a value closest to 0 would be the best location match

edit: haven't thought about symbols much… and then there's the issue of efficiency

Avaphi

2010-05-20 23:30:42

I have another idea, in addition to his but didn't feel the need for another thread.

When I am walking(legitly) I notice my self mousing over stuff as I walk by, random objects. Npc's, occasionally examining, etc. I wonder if you could make a function to recreate this process, because I asked a lot of my buddies and they do it too. What do you think?

NCDS

2010-05-20 23:59:09

From: Avaphi
I have another idea, in addition to his but didn't feel the need for another thread.

When I am walking(legitly) I notice my self mousing over stuff as I walk by, random objects. Npc's, occasionally examining, etc. I wonder if you could make a function to recreate this process, because I asked a lot of my buddies and they do it too. What do you think?


There are already function for that in AntiBan.scar.

Avaphi

2010-05-21 00:03:26

From: NCDS
There are already function for that in AntiBan.scar.


XD :duh:

Gu3st...

2010-05-21 13:50:19

Alright.. Here is my failed attempt to do marpis' 5x5 tile method:
{$i SRL/SRL.scar}
{$i SRL/SRL/misc/Debug.scar}

const
CaptureX = 551;
CaptureY = 9;

MATCHES = 25;
// How many squares must match for Result to be True?
// There is 683 squares that can be used.
// 899 Squares in total, where 216 can't be used at all (runescape gui).

var
BigBitmap : Integer;
// Bitmap of minimap
SmallBitmaps : array [0..683] of Integer;
// BigBitmap exploded into 5x5 bits

function CheckPositionWithWorldMap(WorldMapPath : String): TPoint;
var
WM : Integer;
begin
VM := LoadBitmap(WorldMapPath);
//......
end;

// NOTE: TEMPORALY PROCEDURE. JUST USED TO SEE IF THIS ACTUALLY WORKS
procedure LoadBadPoints(var BadPoints : TPointArray);
begin
SetArrayLength(BadPoints, 216);
BadPoints[0] := Point(0, 0);
BadPoints[1] := Point(1, 0);
BadPoints[2] := Point(2, 0);
BadPoints[3] := Point(3, 0);
BadPoints[4] := Point(4, 0);
BadPoints[5] := Point(5, 0);
BadPoints[6] := Point(6, 0);
BadPoints[7] := Point(7, 0);
BadPoints[8] := Point(8, 0);
BadPoints[9] := Point(9, 0);
BadPoints[10] := Point(10, 0);
BadPoints[11] := Point(11, 0);
BadPoints[12] := Point(0, 1);
BadPoints[13] := Point(1, 1);
BadPoints[14] := Point(2, 1);
BadPoints[15] := Point(3, 1);
BadPoints[16] := Point(4, 1);
BadPoints[17] := Point(5, 1);
BadPoints[18] := Point(6, 1);
BadPoints[19] := Point(7, 1);
BadPoints[20] := Point(8, 1);
BadPoints[21] := Point(0, 2);
BadPoints[22] := Point(1, 2);
BadPoints[23] := Point(2, 2);
BadPoints[24] := Point(3, 2);
BadPoints[25] := Point(4, 2);
BadPoints[26] := Point(5, 2);
BadPoints[27] := Point(6, 2);
BadPoints[28] := Point(0, 3);
BadPoints[29] := Point(1, 3);
BadPoints[30] := Point(2, 3);
BadPoints[31] := Point(3, 3);
BadPoints[32] := Point(4, 3);
BadPoints[33] := Point(5, 3);
BadPoints[34] := Point(0, 4);
BadPoints[35] := Point(1, 4);
BadPoints[36] := Point(2, 4);
BadPoints[37] := Point(3, 4);
BadPoints[38] := Point(4, 4);
BadPoints[39] := Point(0, 5);
BadPoints[40] := Point(1, 5);
BadPoints[41] := Point(2, 5);
BadPoints[42] := Point(3, 5);
BadPoints[43] := Point(0, 6);
BadPoints[44] := Point(1, 6);
BadPoints[45] := Point(2, 6);
BadPoints[46] := Point(0, 7);
BadPoints[47] := Point(1, 7);
BadPoints[48] := Point(0, 8);
BadPoints[49] := Point(1, 8);
BadPoints[50] := Point(0, 9);
BadPoints[51] := Point(0, 10);
BadPoints[52] := Point(0, 11);
BadPoints[53] := Point(0, 12);

BadPoints[54] := Point(29-0, 0);
BadPoints[55] := Point(29-1, 0);
BadPoints[56] := Point(29-2, 0);
BadPoints[57] := Point(29-3, 0);
BadPoints[58] := Point(29-4, 0);
BadPoints[59] := Point(29-5, 0);
BadPoints[60] := Point(29-6, 0);
BadPoints[61] := Point(29-7, 0);
BadPoints[62] := Point(29-8, 0);
BadPoints[63] := Point(29-9, 0);
BadPoints[64] := Point(29-10, 0);
BadPoints[65] := Point(29-11, 0);
BadPoints[66] := Point(29-0, 1);
BadPoints[67] := Point(29-1, 1);
BadPoints[68] := Point(29-2, 1);
BadPoints[69] := Point(29-3, 1);
BadPoints[70] := Point(29-4, 1);
BadPoints[71] := Point(29-5, 1);
BadPoints[72] := Point(29-6, 1);
BadPoints[73] := Point(29-7, 1);
BadPoints[74] := Point(29-8, 1);
BadPoints[75] := Point(29-0, 2);
BadPoints[76] := Point(29-1, 2);
BadPoints[77] := Point(29-2, 2);
BadPoints[78] := Point(29-3, 2);
BadPoints[79] := Point(29-4, 2);
BadPoints[80] := Point(29-5, 2);
BadPoints[81] := Point(29-6, 2);
BadPoints[82] := Point(29-0, 3);
BadPoints[83] := Point(29-1, 3);
BadPoints[84] := Point(29-2, 3);
BadPoints[85] := Point(29-3, 3);
BadPoints[86] := Point(29-4, 3);
BadPoints[87] := Point(29-5, 3);
BadPoints[88] := Point(29-0, 4);
BadPoints[89] := Point(29-1, 4);
BadPoints[90] := Point(29-2, 4);
BadPoints[91] := Point(29-3, 4);
BadPoints[92] := Point(29-4, 4);
BadPoints[93] := Point(29-0, 5);
BadPoints[94] := Point(29-1, 5);
BadPoints[95] := Point(29-2, 5);
BadPoints[96] := Point(29-3, 5);
BadPoints[97] := Point(29-0, 6);
BadPoints[98] := Point(29-1, 6);
BadPoints[99] := Point(29-2, 6);
BadPoints[100] := Point(29-0, 7);
BadPoints[101] := Point(29-1, 7);
BadPoints[102] := Point(29-0, 8);
BadPoints[103] := Point(29-1, 8);
BadPoints[104] := Point(29-0, 9);
BadPoints[105] := Point(29-0, 10);
BadPoints[106] := Point(29-0, 11);
BadPoints[107] := Point(29-0, 12);

BadPoints[108] := Point(0, 29-0);
BadPoints[109] := Point(1, 29-0);
BadPoints[110] := Point(2, 29-0);
BadPoints[111] := Point(3, 29-0);
BadPoints[112] := Point(4, 29-0);
BadPoints[113] := Point(5, 29-0);
BadPoints[114] := Point(6, 29-0);
BadPoints[115] := Point(7, 29-0);
BadPoints[116] := Point(8, 29-0);
BadPoints[117] := Point(9, 29-0);
BadPoints[118] := Point(10, 29-0);
BadPoints[119] := Point(11, 29-0);
BadPoints[120] := Point(0, 29-1);
BadPoints[121] := Point(1, 29-1);
BadPoints[122] := Point(2, 29-1);
BadPoints[123] := Point(3, 29-1);
BadPoints[124] := Point(4, 29-1);
BadPoints[125] := Point(5, 29-1);
BadPoints[126] := Point(6, 29-1);
BadPoints[127] := Point(7, 29-1);
BadPoints[128] := Point(8, 29-1);
BadPoints[129] := Point(0, 29-2);
BadPoints[130] := Point(1, 29-2);
BadPoints[131] := Point(2, 29-2);
BadPoints[132] := Point(3, 29-2);
BadPoints[133] := Point(4, 29-2);
BadPoints[134] := Point(5, 29-2);
BadPoints[135] := Point(6, 29-2);
BadPoints[136] := Point(0, 29-3);
BadPoints[137] := Point(1, 29-3);
BadPoints[138] := Point(2, 29-3);
BadPoints[139] := Point(3, 29-3);
BadPoints[140] := Point(4, 29-3);
BadPoints[141] := Point(5, 29-3);
BadPoints[142] := Point(0, 29-4);
BadPoints[143] := Point(1, 29-4);
BadPoints[144] := Point(2, 29-4);
BadPoints[145] := Point(3, 29-4);
BadPoints[146] := Point(4, 29-4);
BadPoints[147] := Point(0, 29-5);
BadPoints[148] := Point(1, 29-5);
BadPoints[149] := Point(2, 29-5);
BadPoints[150] := Point(3, 29-5);
BadPoints[151] := Point(0, 29-6);
BadPoints[152] := Point(1, 29-6);
BadPoints[153] := Point(2, 29-6);
BadPoints[154] := Point(0, 29-7);
BadPoints[155] := Point(1, 29-7);
BadPoints[156] := Point(0, 29-8);
BadPoints[157] := Point(1, 29-8);
BadPoints[158] := Point(0, 29-9);
BadPoints[159] := Point(0, 29-10);
BadPoints[160] := Point(0, 29-11);
BadPoints[161] := Point(0, 29-12);

BadPoints[162] := Point(29-0, 29-0);
BadPoints[163] := Point(29-1, 29-0);
BadPoints[164] := Point(29-2, 29-0);
BadPoints[165] := Point(29-3, 29-0);
BadPoints[166] := Point(29-4, 29-0);
BadPoints[167] := Point(29-5, 29-0);
BadPoints[168] := Point(29-6, 29-0);
BadPoints[169] := Point(29-7, 29-0);
BadPoints[170] := Point(29-8, 29-0);
BadPoints[171] := Point(29-9, 29-0);
BadPoints[172] := Point(29-10, 29-0);
BadPoints[173] := Point(29-11, 29-0);
BadPoints[174] := Point(29-0, 29-1);
BadPoints[175] := Point(29-1, 29-1);
BadPoints[176] := Point(29-2, 29-1);
BadPoints[177] := Point(29-3, 29-1);
BadPoints[178] := Point(29-4, 29-1);
BadPoints[179] := Point(29-5, 29-1);
BadPoints[180] := Point(29-6, 29-1);
BadPoints[181] := Point(29-7, 29-1);
BadPoints[182] := Point(29-8, 29-1);
BadPoints[183] := Point(29-0, 29-2);
BadPoints[184] := Point(29-1, 29-2);
BadPoints[185] := Point(29-2, 29-2);
BadPoints[186] := Point(29-3, 29-2);
BadPoints[187] := Point(29-4, 29-2);
BadPoints[188] := Point(29-5, 29-2);
BadPoints[189] := Point(29-6, 29-2);
BadPoints[190] := Point(29-0, 29-3);
BadPoints[191] := Point(29-1, 29-3);
BadPoints[192] := Point(29-2, 29-3);
BadPoints[193] := Point(29-3, 29-3);
BadPoints[194] := Point(29-4, 29-3);
BadPoints[195] := Point(29-5, 29-3);
BadPoints[196] := Point(29-0, 29-4);
BadPoints[197] := Point(29-1, 29-4);
BadPoints[198] := Point(29-2, 29-4);
BadPoints[199] := Point(29-3, 29-4);
BadPoints[200] := Point(29-4, 29-4);
BadPoints[201] := Point(29-0, 29-5);
BadPoints[202] := Point(29-1, 29-5);
BadPoints[203] := Point(29-2, 29-5);
BadPoints[204] := Point(29-3, 29-5);
BadPoints[205] := Point(29-0, 29-6);
BadPoints[206] := Point(29-1, 29-6);
BadPoints[207] := Point(29-2, 29-6);
BadPoints[208] := Point(29-0, 29-7);
BadPoints[209] := Point(29-1, 29-7);
BadPoints[210] := Point(29-0, 29-8);
BadPoints[211] := Point(29-1, 29-8);
BadPoints[212] := Point(29-0, 29-9);
BadPoints[213] := Point(29-0, 29-10);
BadPoints[214] := Point(29-0, 29-11);
BadPoints[215] := Point(29-0, 29-12);
end;

procedure CaptureMinimap;
var
X, Y, I, L : Integer;
BadPoints : TPointArray;
P : TPoint;
begin
L := -1;
// NOTE: MUST FIND A BETTER WAY TO DEFINE WHICH POINTS ARE BADPOINTS!!!
LoadBadPoints(BadPoints);
// NOTE: MUST FIND A BETTER WAY TO DEFINE WHICH POINTS ARE BADPOINTS!!!
BigBitmap := BitmapFromClient(CaptureX, CaptureY, CaptureX + 150, CaptureY + 150);
// IT IS ALSO POSSIBLE TO MAKE IT BLACK AND WHITE ...
WriteLn('Captured minimap.');
for X := 0 to 29 do
for Y := 0 to 29 do
begin
if PointInTPA(Point(X, Y), BadPoints) then Continue;
Inc(L);
WriteLn(L);
SmallBitmaps[L] := CreateBitmap(5, 5);
FastDrawClear(SmallBitmaps[L], 0);
{//TEMPORALY METHOD OF 'EXPLODING' THE BIGBITMAP\\}
for P.X := 0 to 4 do
for P.Y := 0 to 4 do
FastSetPixel(SmallBitmaps[L], P.X, P.Y, FastGetPixel(BigBitmap, X*5+P.X, Y*5+P.Y));
{//TEMPORALY METHOD OF 'EXPLODING' THE BIGBITMAP\\}
DrawBitmapDebugImg(SmallBitmaps[L]);
end;
WriteLn('Exploded minimap to pieces.');
end;

begin
CaptureMinimap;
CheckPositionWithWorldMap(ScriptPath + 'worldmap.bmp');
end.
It's for Simba.
It successfully divides the Minimap into small pieces, but I have no idea how to find the small pieces on the worldmap

EDIT:
"BadPoints" are the part of the minimap box that has the runescape gui in it. Here is a example:
The red squares are the BadPoints.

Boreas

2010-05-21 16:43:43

If you don't want to define the bad points you could do 25x25 tiles (4x4 pixels I think)
(MMCX-(2+12*4), MMCY-(2+12*4), MMCX+(1+12*4), MMCY+(1+12*4)).

You could make a world map made up of minimap chunks (put into bigger chunks like in SPAM/S, and saved with tile coords). But when copying the minimap over to these blank bmps, only copy over what is recognized by a bunch of auto colors, and replace with a standard color for each auto color (and don't copy drop/npc/player dots). Such as symbols (replace with standardized icons), door color, wall color, water color, road color. More could be added to increase accuracy, like auto colors for light brown, brown, dark brown, light green, green, dark green, etc. Then when you apply the same filter to your current position minimap, you won't have so much noise (though there will still be pixel shifting).

Sir R. M8gic1an

2010-05-22 08:00:26

Detecting the symbols' outline is easy. It's a fixed color (or pretty much fixed, I use it in my GM)

~RM

marpis

2010-05-22 15:42:13

I'll continue this example script some day, now I'm too tired. So the idea again:
-Capture MM into 5x5 boxes, analyze max/min/avg HSL.
-Go through the whole worldmap bitmap searching every 5x5 area we have captured.
-Now we have an array of TMMSquare that are in our client minimap, and an array of the matching squares (MatchArray[0] contains every TMMSquare in the bitmap that matches Minimap[0], MatchArray[1] contains every TMMSquare in the bitmap that matches Minimap[1], so the type of MatchArray is "array of TMMSquare")
- Now the hard logic part, dunno exactly how this should be done the most efficient way: Find the spot in the worldmap bitmap that has most matches and that's our location in the worldmap.

const
MapWidth = 500;
MapHeight = 500;

type TMMSquare = record // 5x5
x1, y1, x2, y2: integer;
minH, maxH, minS, maxS, minL, maxL: Extended;
avgH, avgS, avgL: Extended;

type TMinimap: array [0..27] of array [0..27] TMMSquare;

procedure AnalyzeColorsBox(x1, y1, x2, y2, world: integer);
function WholeSquareInMinimap(Sq: TMMSquare): boolean;
function CaptureMinimap: TMinimap;
{ AnalyzeColorsBox analyzes the avg/max/min HSL values of a TPA taken from box x1, y1, x2, y2. If world>-1, it will take the colors from the worldmap bitmap inserted, else from the runescape client minimap }

function SquareToWorld(Sq: TMMSquare): array of TMMSquare;
var
Mapbmp, L: integer;
xx, yy: integer;
tmp_Sq: TMMSquare;
begin
Mapbmp := Loadbmp('map.path.here');
for xx := 0 to MapWidth do
for yy := 0 to MapHeight do
begin
tmp_Sq := AnalyzeColorsBox(xx, yy, xx+5, yy+5, Mapbmp);
if Abs(tmp_Sq.avgH - Sq.avgH) < 0.2 then
if Abs(tmp_Sq.avgS - Sq.avgS) < 0.2 then
...
begin
Inc(L);
SetLength(Result, L);
Result[L-1] := tmp_Sq; // We found a matching box
end;
end;
end;


function FindMe(out x, y: integer): boolean;
var
Map: TMinimap;
xx, yy: integer;
begin
// Capture all colors and calculate TMMSquare-info to a TMinimap variable
Map := CaptureMinimap;
//
end;

Sir R. M8gic1an

2010-05-22 15:59:44


//Colors: TIntegerArray that holds the colors of the 4*4 square

for i:= 0 to High(Colors) do
for h := 0 to High(Colors) do
if Colors[i] = Colors[h] then
Inc(TotalC[i]);

b := true;

while b do
begin
b := false;
for i:= 0 to High(TotalC) do
if TotalC[i] > Highest then
begin
HighColor := Colors[i];
b := true;
end;
end;

FindColorsTolerance(TPA, WorldMap, HighColor, Tol: 0);
TPAToATPAEx(TPA, 4, 4);
//make sure the TPA is split the right way to match the previous squares

for i:= 0 to High(ATPA) do
if not Length(ATPA) = Highest then
continue
else begin
//Match the rest of the colors
end;

How does that sound? :)

~RM

cohen

2010-05-22 22:18:13

Just do it via reflection, it's fairly easy.

Gu3st...

2010-05-22 22:35:10

From: cohen
Just do it via reflection, it's fairly easy.
The whole point is to not use reflection….

marpis

2010-05-23 02:41:23

From: Rasta Magician

//Colors: TIntegerArray that holds the colors of the 4*4 square

for i:= 0 to High(Colors) do
for h := 0 to High(Colors) do
if Colors[i] = Colors[h] then
Inc(TotalC[i]);

b := true;

while b do
begin
b := false;
for i:= 0 to High(TotalC) do
if TotalC[i] > Highest then
begin
HighColor := Colors[i];
b := true;
end;
end;

FindColorsTolerance(TPA, WorldMap, HighColor, Tol: 0);
TPAToATPAEx(TPA, 4, 4);
//make sure the TPA is split the right way to match the previous squares

for i:= 0 to High(ATPA) do
if not Length(ATPA) = Highest then
continue
else begin
//Match the rest of the colors
end;

How does that sound? :)

~RM


Would be better and faster but a bit more complicated. Although I don't know how slow my version would be when put in a plugin… I hope we'll see some results in this someday :)

mastaraymond

2010-05-23 05:19:49

For your system marpis, you should try to make certain colors don't 'count' in the comparing process. Stuff like symbols/dots really pollute the MM.. Also for your method, the MM must be aimed perfectly North, which is kinda stinky.

Feroc1ty

2010-10-05 01:44:47

Had this idea a year or so back but gave up as I quit this community/runescape, here is what I had…

type path = record
node: TPointArray;
index: integer;
connection: array of integer;
end;

var paths:array of path;
curindex: integer;

function CreatePath(node:TPointArray):integer;
begin
SetArrayLength(paths, curindex+1);
paths[curindex].node := node;
paths[curindex].index := curindex;
result := curindex;
curindex := curindex + 1;
end;

procedure LinkPath(i,y:integer);
begin
SetArrayLength(paths[i].connection, GetArrayLength(paths[i].connection)+1);
paths[i].connection[High(paths[i].connection)] := paths[y].index;
end;

program Walk;

{.include paths.scar}
{.include llist.scar}

// Basic variables
var charx,chary, destx, desty:integer;
arrived: boolean;

function TPt(x,y:integer):TPoint;
begin
result.x := x;
result.y := y;
end;

// Basic math, used for path finding
function AngleBetweenPoints(x1,y1,x2,y2:integer):extended;
begin
result := Arctan2(y2-y1,x2-x1) * 180 / Pi;
if result < 0 then
result := Abs(result) + 180;
end;

// Test function, dupes move to function from reflection
function MoveTo(p:TPoint):boolean;
begin
if (charx = p.x) and (chary = p.y) then
begin
result := false;
exit;
end;
charx := p.x;
chary := p.y;
writeln('Moved to :' + inttostr(p.x) + ',' + inttostr(p.y));
result := true;
end;

// Required to create and link paths, might make the paths into an ini
// file that will update itself automatically, but this is a test version
procedure Initialize();
begin
CreatePath(path1());
CreatePath(path2());
CreatePath(path3());
CreatePath(path4());
CreatePath(path5());
LinkPath(0,1);
LinkPath(0,2);
LinkPath(0,4);
LinkPath(1,2);
LinkPath(1,3);
LinkPath(1,4);
LinkPath(2,3);
LinkPath(3,4);
end;

// Loops through all of the TPA's and checks which path has the closest average point
function FindClosestPath():integer;
var i:integer;
length: extended;
begin
length := 5000;
for i := 0 to high(paths) do
begin
if length > Distance(charx, chary, paths[i].avg.x, paths[i].avg.y) then
begin
length := Distance(charx, chary, paths[i].avg.x, paths[i].avg.y);
result := i;
end;
end;
end;

// Like previous function, but this one finds the closest point in an array
function FindClosestPointInTPA(arr:TPointArray):integer;
var i,y:integer;
z:extended;
begin
z := 5000;
for i := 0 to High(arr) do
begin
if z > Distance(charx, chary, arr[i].x, arr[i].y) then
begin
y := i;
z := Distance(charx, chary, arr[i].x, arr[i].y);
end;
end;
result := y;
end;

// Will move through the TPA, checking if it's at the destination yet, or if it finished going through the TPA
procedure AutoMoveThroughTPA(start,inc:integer; node:TPointArray);
var i:integer;
begin
i := start - inc;
repeat
i := i + inc;
if MoveTo(node[i]) = true then
wait(100);
if (node[i].x = destx) and (node[i].y = desty) then
begin
arrived := true;
exit;
end;
until (((i = 0) and (i <> start)) or ((i = high(node)) and (high(node) <> start)))
end;

// Will check if the path is accesable, because it might be only connected in the opposite side of a path
function Pathable(path:integer):boolean;
begin
result := false;
if Distance(charx, chary, paths[path].first.x, paths[path].first.y) < Distance(charx, chary, paths[path].last.x, paths[path].last.y)then
begin
if (charx = paths[path].first.x) and (chary = paths[path].first.y) then
result := true
end;
if Distance(charx, chary, paths[path].first.x, paths[path].first.y) > Distance(charx, chary, paths[path].last.x, paths[path].last.y)then
begin
if (charx = paths[path].last.x) and (chary = paths[path].last.y) then
result := true
end;
end;

// Since we don't know how the character will go through the path (increasing or decreasing) we call this function
procedure TakePath(path:integer);
begin
if Distance(charx, chary, paths[path].first.x, paths[path].first.y) < Distance(charx, chary, paths[path].last.x, paths[path].last.y) then
begin
AutoMoveThroughTPA(0, 1, paths[path].node)
end else
begin
AutoMoveThroughTPA(high(paths[path].node), -1, paths[path].node)
end;
end;

// Function that will find the farthest end of a path, takes path index
function FarthestEndX(path:integer):integer;
begin
if Distance(charx, chary, paths[path].first.x, paths[path].first.y) > Distance(charx, chary, paths[path].last.x, paths[path].last.y) then
result := paths[path].first.x
else
result := paths[path].last.x;
end;

// Function that will find the farthest end of a path, takes path index
function FarthestEndY(path:integer):integer;
begin
if Distance(charx, chary, paths[path].first.x, paths[path].first.y) > Distance(charx, chary, paths[path].last.x, paths[path].last.y) then
result := paths[path].first.y
else
result := paths[path].last.y;
end;

// Checks how far away are the angles
function DegreeDiff(A, B:extended):extended;
begin
result:= Abs(A-B);
if result>180.0 then
result:= 180.0-(result-180.0);
end;

// Dick and balls of the script, it gets the shit done
function move(x,y:integer):boolean;
var p, n, curpath, lastpath:integer;
c, cd: extended;
node: TPointArray;
begin
c := 360;
arrived := false;
destx := x;
desty := y;
lastpath := -1;
curpath := FindClosestPath;
node := paths[curpath].node;
p := FindClosestPointinTPA(paths[curpath].node);
cd := AngleBetweenPoints(charx, chary, x, y);
if (p <> 0) and (p <> high(paths[curpath].node)) then
begin
if DegreeDiff(cd,AngleBetweenPoints(charx, chary, node[p+1].x,node[p+1].y)) < DegreeDiff(cd,AngleBetweenPoints(charx, chary, node[p-1].x, node[p-1].y)) then
begin
AutoMoveThroughTPA(p+1, 1, node);
end else
begin
AutoMoveThroughTPA(p-1, -1, node);
end;
end;

while (arrived = false) do
begin
cd := AngleBetweenPoints(charx, chary, x, y);
for p := 0 to high(paths[curpath].connection) do
begin
if (c > DegreeDiff(cd, AngleBetweenPoints(FarthestEndX(paths[paths[curpath].connection[p]].index), FarthestEndY(paths[paths[curpath].connection[p]].index), x, y))) and (p <> lastpath) and (Pathable(paths[paths[curpath].connection[p]].index) = true) then
begin
c := AngleBetweenPoints(FarthestEndX(paths[paths[curpath].connection[p]].index), FarthestEndY(paths[paths[curpath].connection[p]].index), x, y);
n := p;
end;
end;
c := 360;
TakePath(paths[paths[curpath].connection[n]].index);
lastpath := curpath;
curpath := n;
end;
end;

begin
Initialize;
ClearDebug;
MoveTo(TPt(5,0));
Move(0,7);
end.


completely forgot about it didn't even look at it just copied and pasted what I had from ages ago lol


edit: I guess I had a different idea than urs, mine was to make many paths into an ini file, not any certain order or anything and link them used linked lists, than simply get characters location using reflection, find the closest path to the character, and get him moving until he hits the path, after that the script starts to move toward destination… i think i only got half way thru making it tho lol