SRL Archive - MSI Suggestions

Change to tile walking


Coh3n

2011-05-20 16:21:13

I was using one of the Runecrafters in the Public Scripts sections, and I came across this function:

function R_WebWalkPath(Points : TPointArray; WaitTime, RandWait : Integer) : Boolean;
var
I : Integer;
begin
Result:= False;
for I:= 0 to High(Points) do
begin
if not R_WebWalk(Points[I]) then
begin
WriteLn('Could not walk to point (' + IntToStr(Points[I].X) + ', ' + IntToStr(Points[I].Y) + ')');
Exit;
end;
Wait(WaitTime + Random(RandWait));
end;
Result:= True;
end;
I propose we replace our tile walking with something like this. There would be variations of course (i.e. we'd probably have our own version of Reflection's WebWalk). But what does this mean exactly?
  • Our walking will be much much much more human-like as this procedure simulates multiple clicking on the edge of the minimap, just as a human does.
  • Our script paths will only be one or two points (we'd probably call WebWalk in straight lines to get around walls/obstacles).
  • This will work with SPS easily. Will also work with Zyt3x's WebWalker (I think, I don't know THAT much about it).
  • It's much simpler which gets rid of the needless reflection only code and replaces it with some simple math.
  • It walks a 100% different path each time (i.e. it's not following the same tiles). It will be close (because we're going to and from the same location), but not the same.
All in all I think it's a great idea. I don't see any downsides. I also PM'd Rich (the one who wrote the above function), and he assured me that it's perfectly fine if we wanted to use that function.

Thoughts?

Gu3st...

2011-05-20 16:31:44

Yes. I quite like this proposal.

I can't think of any down-sides other than obstacles and the such, but that can easily be fixed by adding more points so it's not a big problem..

Coh3n

2011-05-20 18:06:48

From: Zyt3x
Yes. I quite like this proposal.

I can't think of any down-sides other than obstacles and the such, but that can easily be fixed by adding more points so it's not a big problem..

Yeah exactly. So for the Guild Miner for example, the path would just be:

Path := [ [TileAtBank, TileAtLadder], [TileBelowLadder, TileInMine] ];
And you would just call:

MSI_WebWalk(Path[i][j]);
Lol, guild isn't a very good example because the path is really short, but I'm sure you get the idea. Paths that are currently 20 points would be shortened to two endpoints and the points in between would be calculated.

NCDS

2011-05-20 21:39:26

I like the idea, although I don't think WebWalking is that human like, but that may just be my style is different than others. I'm not opposed to the idea at all though.

Coh3n

2011-05-20 21:42:34

Well technically it's not web walking. It's taking the endpoints and calculating a different path each time. I'm going to try and write a test script in the next couple days so you guys can see exactly what I mean. It's much more human like (in my opinion amyway).

NCDS

2011-05-20 21:45:44

From: Coh3n
Well technically it's not web walking. It's taking the endpoints and calculating a different path each time. I'm going to try and write a test script in the next couple days so you guys can see exactly what I mean. It's much more human like (in my opinion amyway).


Yeah I know it I only called it that because of the function name. :P


I just don't think it's that human like because it just clicks the same direction your trying to go, no matter what's in the way and no matter how far out of the way you go before reaching your destination.

For instance, clicking inside a fenced in area, making you walk around to enter the fenced in area, then clicking out of the fenced in area and walking back around it.

Coh3n

2011-05-20 21:49:34

Oh no, no, no. I agree, that isn't human-like. We'd be using the webwalk function, but it would walk in straight lines around the fence. I wish I wasn't on my phone, I could make a diagram for you. Take VEB –> VEM for example would walk from the bank, to outside east Varrock, THEN south rather than directly south-east from the bank, which is what you're talking about (I think).

Gu3st...

2011-05-21 05:09:34



Uploaded with ImageShack.us

Coh3n

2011-05-21 07:49:36

^ Exactly what I mean. :)

NCDS

2011-05-21 08:30:04

Yeah that's totally fine with me then, that's all I was worried about. :)

Boreas

2011-05-21 16:09:01

Whoever made the 'webwalk' that uses the blue line above needs to change the name, that is not webwalking. A web has nodes at corner points (the yellow blobs), with a clear line of sight between nodes.

The yellow should be called 'normal webwalking' and the blue should be called 'blind clicking towards a point'. Blind clicking towards a point can be used as a part of webwalking, to go between nodes in a web (which were placed with a clear line of sight between them to get around corners, and are chosen with an algorithm), but it is not webwalking by itself.

Gu3st...

2011-05-21 20:17:54

From: Boreas
Whoever made the 'webwalk' that uses the blue line above needs to change the name, that is not webwalking. A web has nodes at corner points (the yellow blobs), with a clear line of sight between nodes.

The yellow should be called 'normal webwalking' and the blue should be called 'blind clicking towards a point'. Blind clicking towards a point can be used as a part of webwalking, to go between nodes in a web (which were placed with a clear line of sight between them to get around corners, and are chosen with an algorithm), but it is not webwalking by itself.
I agree. Rename it to SplineWalk or something (as it makes a path using spline algorithms)

IPwnz

2011-05-21 22:06:12

From: Zyt3x
I agree. Rename it to SplineWalk or something (as it makes a path using spline algorithms)


Or NodeWalk because it's shorter. :P I like the idea, we just have to spend a little bit of time changing our current walking paths (right?) and we'll be set.

Coh3n

2011-05-22 02:19:28

From: IPwnz
Or NodeWalk because it's shorter. :P I like the idea, we just have to spend a little bit of time changing our current walking paths (right?) and we'll be set.

Not much, it's just replacing some points. Could be time consuming I guess, but it's definitely worth it.

Coh3n

2011-05-22 11:02:13

From: Boreas
Whoever made the 'webwalk' that uses the blue line above needs to change the name, that is not webwalking. A web has nodes at corner points (the yellow blobs), with a clear line of sight between nodes.

The yellow should be called 'normal webwalking' and the blue should be called 'blind clicking towards a point'. Blind clicking towards a point can be used as a part of webwalking, to go between nodes in a web (which were placed with a clear line of sight between them to get around corners, and are chosen with an algorithm), but it is not webwalking by itself.

It's what R_WebWalk does (the blue line). Thinking about it, it shouldn't be difficult to create an entire web walking system with nodes in certain areas. I mean, it's just a few tiles and some calculations. For obstacles you could just do something like:

function WebWalk(where: TTile; obstacle: TObstacle): boolean;
Where it would look for the obstacle while walking to the tile 'where'. Probably more complicated than that, but I think it would work.

Gu3st...

2011-05-22 11:19:08

type
TWebObstacle = record
placeholder:integer;
end;

TWebNode = record
Index : Integer;
Tile : TWebTile;
Obstacle : Integer; // if = -1 then no obstacle
ConnectsWith : TIntegerArray;
end;
TWebNodeArray = array of TWebNode;

Boreas

2011-05-22 15:43:17

From: Coh3n
It's what R_WebWalk does (the blue line). Thinking about it, it shouldn't be difficult to create an entire web walking system with nodes in certain areas. I mean, it's just a few tiles and some calculations. For obstacles you could just do something like:

function WebWalk(where: TTile; obstacle: TObstacle): boolean;
Where it would look for the obstacle while walking to the tile 'where'. Probably more complicated than that, but I think it would work.


Then R_WebWalk should be renamed to NodeWalk/SplineWalk.


From: Zyt3x
type
TWebObstacle = record
placeholder:integer;
end;

TWebNode = record
Index : Integer;
Tile : TWebTile;
Obstacle : Integer; // if = -1 then no obstacle
ConnectsWith : TIntegerArray;
end;
TWebNodeArray = array of TWebNode;


Agreed, obstacles should be a part of nodes.

Nodes can be 'dead-ends' with one 1 neighbor, destinations like banks, training spots, and teleport points.
Nodes can have have 2 neighbors, usually to get around a corner.
Nodes can have more than 2 neighbors, like crossroads (go from one skill to another)
Nodes can have 2 neighbors and an obstacle, including info needed to open the door etc

Then you need Dijkstra/AStar to go through the ConnectsWith to get an array of nodes, then NodeWalk/SplineWalk to each one.

Coh3n

2011-05-22 21:38:52

R_WebWalk will probably never be changed because people get extremely pissy if a function name or parameters change. I'm not sure what Dijkstra/AStar is.

Let's see if I'm understanding this correctly. Would it be something like this?


But then I'm not exactly sure how you would determine the "ConnectsWith" attribute. Unless it was something like:

ConnectsWith := [LOC_FW_MINE];
But I don't think that's what you guys mean.

E: I don't know if this is the right idea, but I came up with this small format. Let me know what you think.

program srlWebWalk;
{$DEFINE SMART}
{$i srl/srl.scar}
{$i reflection/reflection.simba}

const
SETTING_COUNT = 2;
SETTING_SET = 0;
SETTING_ENABLE_REST = 1;

type
TWebObstacle = record
objID: integer;
tiles: TPointArray; // may be able to do something like obj.getTiles();
colorInfo: TVariantArray; // to be used with smartColors();
end;

TWebNeighbour = record
name: string;
area: TBox; // tile box of the node's area
obstacle: TWebObstacle;
end;
TWebNeighbourArray = array of TWebNeighbour;

TWebNode = record
name: string;
area: TBox;
neighbours: TWebNeighbourArray;
end;
TWebNodeArray = array of TWebNode;

var
webNodes: TWebNodeArray;
webSettings: TBooleanArray;

procedure declareWebSettings();
begin
if (not webSettings[SETTING_SET]) then // if not set in script
begin
webSettings[SETTING_ENABLE_REST] := true; // will rest when low energy
end;
end;

procedure loadWebNodes();
begin
setLength(webNodes, 1);

with webNodes[0] do
begin
name := 'Falador West Bank';
area := intToBox();

setLength(neighbours, 2);
with neighbours[0] do
begin
name := 'Falador West Wall';
area := intToBox();
obstacle.objID := 1234;
obstacle.tiles := [];
obstacle.colorInfo := [];
end;

with neighbours[1] do
begin
name := 'Falador West Mine';
area := intToBox();
end;
end;
end;

function webWalk(where: TPoint): boolean;
begin
end;

begin
smart_Server := 152;
smart_Members := False;
smart_Signed := True;
smart_SuperDetail := False;

clearDebug();
setupSRL();
end.
The only thing I don't like is the gathering of tiles, but I'm not sure if that can ever be avoided.

Boreas

2011-05-23 00:03:58

Yea I guess changing names is a pain, I just don't want people getting confused and calling something that it isn't (blacklist vs blackcounts all over again).

Yea so every node has a number* (and/or name string and/or const for the number), and connectswith is an array of the numbers of the neighbors. Dijkstra/AStar takes a start node and end node, and figures out which node(s) connect them, by looking at the neighbors in connectswith. Some of this work may already be done (ReadySteadyGo?).

Also, yes, gathering the tiles for the nodes must be done. Though a good portion of them are banks/training spots you may already have, it's just the ones like the middle one in that picture. Again, some of this work may already be done (ReadySteadyGo?).

I think a combination of that node record and the one Zyt3x posted above would be good.
NodeNum: Integer; doesn't have to be the same as the index array
NodeName: string; descriptive name
Area: box, even if its just 1 tile
Center: if 1 tile, then that tile, if a box, then the middle (or topleft, or anywhere). **
Obstacle: some record that can be applied to doors/gates/ladders/shortcuts/etc, this can be added to over time
Neighbors: intarr values relate to the NodeNum of other nodes, and from that you can get the Area etc. TWebNeighbour would be too much maintenance, and repeating data in several places.

Consts like LOC_FW_MINE can hold the value of NodeNum if you want, rather than using NodeName.

*It may be better to have an integer in the node record, rather than using the index of the node array, just in case you want to chop up the array later.

**This is so that the algo can take into account the distance between neighbors, rather than just choosing the route with the fewest nodes (though the route with the fewest nodes is usually the shortest, so this is optional). Another option is having the Neighbor/Connectwith array being a TPA, where the X is the number of the neighbor node, and the Y is the distance to it. This requires more work (can be automated) when creating the nodes, but less computation for the algo during runtime.

Gu3st...

2011-05-23 01:58:15

I'd like a custom TWebTile type that stores both reflection tiles and SPS areas/tiles

Boreas

2011-05-23 08:45:36

Ah, I assumed SPS was made with the offsets and math to take reflection tiles. There should be a color version of GetMyPos IMO.

Coh3n

2011-05-23 11:09:22

From: Boreas
Yea so every node has a number* (and/or name string and/or const for the number), and connectswith is an array of the numbers of the neighbors. Dijkstra/AStar takes a start node and end node, and figures out which node(s) connect them, by looking at the neighbors in connectswith. Some of this work may already be done (ReadySteadyGo?).

I think constants would be best, I've always like that way. And I'm sure there's a lot done in ReadySteadyGo's web thing. I'd have to look at that a little.

From: Boreas
I think a combination of that node record and the one Zyt3x posted above would be good.
NodeNum: Integer; doesn't have to be the same as the index array
NodeName: string; descriptive name
Area: box, even if its just 1 tile
Center: if 1 tile, then that tile, if a box, then the middle (or topleft, or anywhere). **
Obstacle: some record that can be applied to doors/gates/ladders/shortcuts/etc, this can be added to over time
Neighbors: intarr values relate to the NodeNum of other nodes, and from that you can get the Area etc. TWebNeighbour would be too much maintenance, and repeating data in several places.
Why have Center? Could just use PointInBox(Area) or something similar. Also, there could be more than one obstacle per node (that's why I had TWebNeighbour), plus with the extra type, you would only have to search for the obstacle when it's needed, rather than at the node when some neighbours won't have an obstacle.

From: Boreas

Consts like LOC_FW_MINE can hold the value of NodeNum if you want, rather than using NodeName.

That was an MSI constant and I would rather have this a universal SRL tool so I think it's own constants would be the best idea.

From: Boreas

*It may be better to have an integer in the node record, rather than using the index of the node array, just in case you want to chop up the array later.
I had planned on writing in a way that you would never have to call a certain undex specifically, so just having a name should work, so the user can just call:

WebWalk('Varrock East Bank');
WebWalk(BANK_FALADOR_WEST);
WebWalk(Point());

From: Boreas

**This is so that the algo can take into account the distance between neighbors, rather than just choosing the route with the fewest nodes (though the route with the fewest nodes is usually the shortest, so this is optional). Another option is having the Neighbor/Connectwith array being a TPA, where the X is the number of the neighbor node, and the Y is the distance to it. This requires more work (can be automated) when creating the nodes, but less computation for the algo during runtime.

I don't think a few extra calculations will slow it down a noticeable amount. Ideally, I would like to just simply be able to add a node in the loadNodes procedure, and it would work..

Coh3n

2011-05-23 13:16:03

From: Boreas
Ah, I assumed SPS was made with the offsets and math to take reflection tiles. There should be a color version of GetMyPos IMO.

There's an SPS getMyPos, and there are functions that convert tiles to SPS coords. All in SPS.simba.

Coh3n

2011-08-01 01:05:14

Just a little update on this. I just got a thought before I head to bed. I think I'm going to incorporate this in the TBox_locations branch on my fork, so both the new systems can be tested together. Posting this more of a reminder to myself.

mormonman

2011-08-15 17:20:14

This seems like a bigger project than just MSI…if you wanted to do this the right way, it would take tons and tons of data collection. Plus you would *NEED* to use reflection. R_CanReach is required for a proper web.

Brandon

2011-08-15 17:46:55

*sigh* can someone explain the point of webwalking? I mean I don't really see the big deal :S we can just use tiles no? or walk path? what is the advantages of webwalking? Is it like RSBuddy's nanowalking? Where is walks anywhere just given a tile, does any obstacle, etc? I don't understand webwalking :S Humans teleport rather than walk long distances.. so I take it this is for short distances?

mormonman

2011-08-15 17:55:23

From: ggzz
*sigh* can someone explain the point of webwalking? I mean I don't really see the big deal :S we can just use tiles no? or walk path? what is the advantages of webwalking? Is it like RSBuddy's nanowalking? Where is walks anywhere just given a tile, does any obstacle, etc? I don't understand webwalking :S Humans teleport rather than walk long distances.. so I take it this is for short distances?


In my mind a "web" is a collection of tiles, obstacles, level requirements, items that can be used, etc. that a script can call on to get it to a tile the fastest. If you have an amulet of glory with charges, and you are in ardougne and want to get to soul wars it will detect the option to teleport, use it then walk to the soul wars entrance and interact with the portal and return true when you finish "teleporting" into the soul wars bank area.

**BANNED The Man

2011-08-15 18:09:41

From: mormonman
In my mind a "web" is a collection of tiles, obstacles, level requirements, items that can be used, etc. that a script can call on to get it to a tile the fastest. If you have an amulet of glory with charges, and you are in ardougne and want to get to soul wars it will detect the option to teleport, use it then walk to the soul wars entrance and interact with the portal and return true when you finish "teleporting" into the soul wars bank area.


This IS a Web.
Will check for teleports and if that teleport requires a quest etc.

<3 from Boom

Coh3n

2011-08-15 22:01:07

From: mormonman
This seems like a bigger project than just MSI…if you wanted to do this the right way, it would take tons and tons of data collection. Plus you would *NEED* to use reflection. R_CanReach is required for a proper web.
Oh my apologies, I forgot the webwalking discussion was also in this thread. I just plan on implementing what was outlined in the first post. Just allows long paths to be done in only a few tiles + that gives a different path every time.

mormonman

2011-08-15 22:03:40

From: Coh3n
Oh my apologies, I forgot the webwalking discussion was also in this thread. I just plan on implementing what was outlined in the first post. Just allows long paths to be done in only a few tiles + that gives a different path every time.


Ahh ok, that's pretty simple.

Coh3n

2011-08-15 22:05:40

From: mormonman
Ahh ok, that's pretty simple.
Yeah, and very effective. Funny thing you learn with experience - the simplest concepts often turn out to be the most effective.

E: Think I'm going to siggy that. :p

litoris

2011-08-16 03:49:06

This is from the air runner right? I always thought that the way that script makes you walk was great, lol.

Coh3n

2011-08-16 10:43:11

From: litoris
This is from the air runner right? I always thought that the way that script makes you walk was great, lol.
Yep! I was using that script and liked it also. Makes a lot more sense then the current way we're doing it.

Harry

2011-09-09 06:43:41

Walking in MSI is very slow and very botlike. Using points to walk, and even waiting quite a few seconds when it gets to that point before clicking again.

I suggest using a "waypoint" system, combined with R_WebWalk - at points where it's required you go there before turning. See my scripts for what I mean. It makes a nice dynamic path to follow, clicking IMO humanlike. I don't know a single human who clicks once to walk and doesn't click again until he reaches the flag.

PS: does MSI ignore spambots? I watched the script ignore this spambot over 3 times (I kept deleting it and when he ran to bank it'd ignore him again :p)

My main problem with running MSI is how slow it behaves. Takes a long time to do stuff / make a new decision. I understand the color finding stuff isn't as fast as reflection, but IMO it's much too slow for my tastes. Any way to speed it up more? Script feels like it's drunk. Other than that it works rather well and is accurate.

Coh3n

2011-09-09 13:28:32

For walking, see this thread. That's bee planned for a loooong time, I just haven't had the time to start it.

It's supposed to ignore spam-bots. That was a more recent update and I've never actually seen it in action so I have no idea what it's like.

What parts specifically are slow? There are a lot of issues with some new graphic areas that will be fixed as soon as this branch in merged, but that's a very large update and is taking a lot longer than expected to finish. Feel free to test it out.

And if by slow, you're talking about an extra 1-2 second wait here and there, that's probably just wait() calls. I agree on the walking, though, it is slow; although I'm not exactly sure why, it's supposed to click the next tile as soon as it shows on the minimap. IIRC, it wasn't always slow. I'm not sure what happened. :o

But yeah, I'd like to know specifically what you think is slow.

mormonman

2011-09-09 14:21:12

I have a better solution so you don't have to deal with R_WebWalk.

function WindPathEx(TPA: TPointArray): TPointArray;
var
ATPA: T2DPointArray;
Start: TPoint;
i: Integer;
begin
SetLength(ATPA, Length(TPA));
Start := R_GetMyPos;
for i := 0 to High(TPA) do
begin
ATPA[i] := R_WindPath(Start.x, Start.y, TPA[i].x, TPA[i].y, 4.0, 2.5, 0.0, 0.0, 4.5, 2.5);
Start := TPA[i];
end;
Result := MergeATPA(ATPA);
end;

Probably could be done quicker but I wrote it really fast for my blood pacter.

Edit: Also you can change the constants in R_WindPath…Plus I think that some kind of PathToPoint/PathToPoints should be added to srl or mml(would be the same as windpath but not reflection specific).

Coh3n

2011-09-09 18:15:01

From: mormonman
I have a better solution so you don't have to deal with R_WebWalk.

Probably could be done quicker but I wrote it really fast for my blood pacter.
What's wrong with R_WebWalk (R_WindWalk now)? Just wondering. My plan was actually do write up custom methods for doing this because it will have to work with any TPA (tiles and SPS points). Looking at R_WindPath, I think it would work fine for any TPA. It doesn't seem to be Reflection-specific. Could probably be named WindTPA and added to mml.

I like that function by the way, I'll probably end up using it (or some variation of). Would be nice if the Reflection documentation explained what each of the parameters were. The names aren't exactly clear.

From: mormonman

Edit: Also you can change the constants in R_WindPath…Plus I think that some kind of PathToPoint/PathToPoints should be added to srl or mml(would be the same as windpath but not reflection specific).
Could you elaborate on this a little?

I'll also probably take a look at Harry's script. He said he has a fast method of path walking so that may help a little.

mormonman

2011-09-09 20:00:08

From: Coh3n
Could you elaborate on this a little?


functions to be added to mml or srl:

function WindPathEx(Points: TPointArray; gravity, wind, maxStep, targetArea: extended): TPointArray;
var
veloX, veloY, windX, windY, veloMag, dist, randomDist, lastDist: extended;
lastX, lastY, i: integer;
sqrt2, sqrt3, sqrt5: extended;
begin
if (Length(Points) < 2) then
Exit;

sqrt2:= sqrt(2);
sqrt3:= sqrt(3);
sqrt5:= sqrt(5);
for i := 1 to High(Points) do
while hypot(Points[i-1].x - Points[i].x, Points[i-1].y - Points[i].y) > 1 do
begin
dist:= hypot(Points[i-1].x - Points[i].x, Points[i-1].y - Points[i].y);
wind:= minE(wind, dist);
if dist >= targetArea then
begin
windX:= windX / sqrt3 + (random(round(wind) * 2 + 1) - wind) / sqrt5;
windY:= windY / sqrt3 + (random(round(wind) * 2 + 1) - wind) / sqrt5;
end else
begin
windX:= windX / sqrt2;
windY:= windY / sqrt2;
if (maxStep < 3) then
begin
maxStep:= random(3) + 3.0;
end else
begin
maxStep:= maxStep / sqrt5;
end;
end;
veloX:= veloX + windX;
veloY:= veloY + windY;
veloX:= veloX + gravity * (Points[i].x - Points[i-1].x) / dist;
veloY:= veloY + gravity * (Points[i].y - Points[i-1].y) / dist;
if hypot(veloX, veloY) > maxStep then
begin
randomDist:= maxStep / 2.0 + random(round(maxStep) / 2);
veloMag:= sqrt(veloX * veloX + veloY * veloY);
veloX:= (veloX / veloMag) * randomDist;
veloY:= (veloY / veloMag) * randomDist;
end;
lastX:= Round(Points[i-1].x);
lastY:= Round(Points[i-1].y);
Points[i-1].x := Points[i-1].x + Round(veloX);
Points[i-1].y := Points[i-1].y + Round(veloY);
SetArrayLength(Result, GetArrayLength(Result) + 1);
Result[High(Result)] := Point(Round(Points[i-1].x), Round(Points[i-1].y));
lastdist:= dist;
end;
end;

function WindPath(Point1, Point2: TPoint; gravity, wind, maxStep, targetArea: extended): TPointArray;
begin
Result := WindPathEx([Point1, Point2], gravity, wind, maxStep, targetArea);
end;

Coh3n

2011-09-09 20:13:05

Looks good. I'd rather them be named WindTPAEx and WindTPA to go along with the other TPA methods. Mind giving an explanation of each parameter? Also, why "Wind" and not something like RandomPath?

mormonman

2011-09-09 21:11:34

Small bug… I think I'm going to scrap the whole wind/gravity spline idea.

Edit: I decided that I like the whole "odd" tile error I was getting. It's human-like.

NCDS

2011-09-09 21:48:26

Yes we ignore spam bots. If you want to turn it off for now, you can go into AntiBan.simba, then to MSI_AntiBan, then coment or remove MSI_IgnoreSpam. This will be added to the 'advanced features' part of the setup eventually.


Also, what type of walking are you using that's going slow? I'm running reflection right now and it seems to be alright. I agree with Coh3n though that it seems like it used to be faster.

Nava2

2011-09-10 03:26:12

Once again, Zeph and I already created this system… we wrote it in our ZNMM. It worked beautifully, and super fast. Just rip the code out and use it! Rather than splines though, we used a custom system I wrote that just randomized the point within radians away from the actual point and everytime it would always try to right its way back towards the point. This creates a quick and basic path to the point that is different any time.

The TBox solution is a good idea, though. Our system could easily be adapted, but I feel like it might be over kill for the mid-points, but final point makes sense for sure.

Coh3n

2011-09-10 04:00:58

Wouldn't your system and using R_WindWalk accomplish the same thing?

Nava2

2011-09-10 09:42:27

From: Coh3n
Wouldn't your system and using R_WindWalk accomplish the same thing?


Yes. but R_WindWalk is ugly and has little to no failsafes. The largest of these being that people usually do not heck if the player is around. Its essentially abused and used in all of the wrong places. C'est la vie.

Coh3n

2011-09-10 13:18:22

From: Nava2
Yes. but R_WindWalk is ugly and has little to no failsafes. The largest of these being that people usually do not heck if the player is around.
What do you mean? It calculates the path starting from where the player is? Feel free to post your code by the way. You've said so much about your system, yet have never posted code.

Nava2

2011-09-10 13:25:13

I keep forgetting that its not just lying around..

unction FindTileOnPath(var Index: integer; Path: TTileArray; P: TTile; MaxDist: Integer): Boolean;
var {Find the closest tile in a path within MaxDist}
I, H, DistC, DistS, T: integer;
begin
T := GetTimeRunning;
H := High(Path);
Index := -1;
if (H < 0) then Exit;

DistS := Distance(P.x, P.y, Path[0].x, Path[0].y); //Distance from Current Tile

for I := 0 to H do //Looks for the Closest Tile
begin
DistC := Distance(P.x, P.y, Path[I].x, Path[I].y);
if DistC <= DistS then
begin
DistS := DistC;
Index := I;
end;
end;

Result := DistS <= MaxDist;
if not Result then Index := -1;
WriteLn('FindTileOnPath: Index = ' + IntToStr(Index) + ' Time: ' + IntToStr(GetTimeRunning - T) + ' ms');
end;

function FurthestVisibleTile(tPath: TTileArray; P: TTile; sTile, eTile: Integer): Integer;
var {Returns the Index of the furthest tile in a path }
H: Integer; {between given indices that is still on the MM}
begin
SetLength(tPath, eTile + 1); //Removes tiles not in range
InvertPath(tPath);
SetLength(tPath, Length(tPath) - sTile);
InvertPath(tPath);

H := High(tPath);
for Result := H downto 0 do //Finds the furthest visible tile.
if (Sqrt(Sqr((tPath[Result].X - P.X) * 4.0) + Sqr((tPath[Result].Y - P.Y) * 4.0)) < 69.0) then
begin
IncEx(Result, sTile);
Exit;
end;
Result := -1;
end;

{
Creates a TPA between points, tDist intervals, Rand randonmness (Tiles)
}
function CreatePathBetween(fT, tT: TTile; tDist, Rand: Integer): TTileArray;
var
hyp, xR, yR: Extended;
l: Integer;
begin
if not LoggedIn then Exit;

{ Set Result Length and first point at initial tile. }
SetLength(Result, Ceil(Distance(fT.X, fT.y, tT.X, tT.Y) * 1.0 / tDist) + 1);
Result[0] := fT;

{ If no need for intermediate tiles then nvm. :) }
if (Distance(fT.X, fT.Y, tT.X, tT.Y) <= tDist) then
begin
Result[High(Result)] := tT;
Exit;
end;

{ Ratios for 'Similar Triangles' maths. }
hyp := Hypot(fT.X - tT.x, fT.Y - tT.Y);
xR := (tT.X - fT.X) / hyp;
yR := (tT.Y - fT.Y) / hyp;

{ Set points. }
for l := 1 to High(Result) - 1 do
begin
Result[l].X := Round(fT.X + l * tDist * xR) + RandomRange(-Rand, Rand);
Result[l].Y := Round(fT.Y + l * tDist * yR) + RandomRange(-Rand, Rand);
end;

{ Last point is tT. }
Result[High(Result)] := tT;
end;

{
Expands ctrlPoints into a path with tDist intervals and Randomness
}
function ExpandPath(ctrlTiles: TTileArray; tDist, Randomness: Integer): TTileArray;
var
i, h: Integer;
t: T2DPointArray;
begin
Result := [];
h := High(ctrlTiles);
SetLength(t, h);

for i := 1 to h do
t[i - 1] := CreatePathBetween(ctrlTiles[I - 1], ctrlTiles[I], tDist, Randomness);

Result := MergeATPA(t);
end;

{
Walks to a path.
}
function EnterPath(Path: TTileArray): Boolean;
var
I: Integer;
tmpPath: TTileArray;
begin
if (FindTileOnPath(I, Path, GetMyPos, 50)) then
begin
{ Create path between current tile and closest path tile }
tmpPath := CreatePathBetween(GetMyPos, Path[I], 3, 5);
Result := WalkPath(tmpPath);
if (Result) then Exit;
end;
WriteLn('Could not enter path!');
end;

{
Walk to a tile.
}
function WalkTile(T: TTile): Boolean;
begin
Result := EnterPath([T]);
end;

{
Walk to a path.
}
function Walk(Path: TTileArray; Destination: Integer): Boolean;
var
P, DTile: TTile;
H, D, tileInd, pInd, Attempts, time: Integer;
begin
time := GetTimeRunning;

H := High(Path);
if (H < 0) then Exit;

if (Destination = -1) then
D := H
else
D := Destination;

{ Expands path and finds new destination index }
DTile := Path[D];
Path := ExpandPath(Path, 1, 3);
H := High(Path);
FindTileOnPath(D, Path, DTile, 3);

{ Finds you on a path and attempts to walk to it if possible }
if (not(EnterPath(Path))) then
begin
p := GetMyPos;
WriteLn('E:: Lost en route. ' + IntToStr(p.x) + ', ' + IntToStr(p.y));;
LogOut;
Exit;
end;

FindTileOnPath(pInd, Path, GetMyPos, 5);
if (pInd > D) then
begin
{ Sorts out walking direction }
InvertPath(Path);
D := H - D;
pInd := H - pInd;
end;

P := GetMyPos;
{ Loops through the tiles and walks :) }
while (Distance(P.X, P.Y, Path[D].X, Path[D].Y) > 3) and (Attempts < 5) do
begin
tileInd := FurthestVisibleTile(Path, P, pInd, D);
{ If the furthest tile is found, Walk to it! }
if (tileInd > -1) then
begin
WriteFMT('Next Tile: %d (%d, %d)',
[tileInd, Path[tileInd].X, Path[tileInd].Y]);
if (WalkToTile(Path[tileInd],
0,
RandomRange(1, 4)*Integer(Random(20)<>0))) then
begin
Attempts := 0;
Wait(100 + Random(100));
P := GetMyPos;
FindTileOnPath(pInd, Path, P, 5);
end;
end;
Inc(Attempts);
end;

R_FFlag(0);
Result := (DistanceFrom(Path[D]) <= 5);
if (not(Result)) then
WriteLn('Walk failed');
end;

Some functions names have changed, but the algorithm still works as of a few months ago. I see no reason why it wouldn't. Basically, you just supply a series of control points to ExpandPath and it will fill in the rest of the points that are necessary and will make sure that they are within the flag distance before clicking (they are calculated to make sure). The expanding algorithm will also veer off and back repeatedly to make the path random.. I never did any drawings of how it worked. But it was legit flawless before we tried updating it and forgot about it.

Hell the points are probably all still valid in that script.. :P Fastest mining anyone *ever* did. But it took a lot of data-mining.

Anyway, the fill sauce is here: http://code.google.com/p/nzmm/source/browse/trunk/?r=9

Home

2011-09-10 13:28:14

From: Coh3n
What do you mean? It calculates the path starting from where the player is? Feel free to post your code by the way. You've said so much about your system, yet have never posted code.


function FindTileOnPath(var Index: integer; Path: TTileArray; P: TTile; MaxDist: Integer): Boolean;
var {Find the closest tile in a path within MaxDist}
I, H, DistC, DistS, T: integer;
begin
T := GetTimeRunning;
H := High(Path);
Index := -1;
if (H < 0) then Exit;

DistS := Distance(P.x, P.y, Path[0].x, Path[0].y); //Distance from Current Tile

for I := 0 to H do //Looks for the Closest Tile
begin
DistC := Distance(P.x, P.y, Path[I].x, Path[I].y);
if DistC <= DistS then
begin
DistS := DistC;
Index := I;
end;
end;

Result := DistS <= MaxDist;
if not Result then Index := -1;
WriteLn('FindTileOnPath: Index = ' + IntToStr(Index) + ' Time: ' + IntToStr(GetTimeRunning - T) + ' ms');
end;

function FurthestVisibleTile(tPath: TTileArray; P: TTile; sTile, eTile: Integer): Integer;
var {Returns the Index of the furthest tile in a path }
H: Integer; {between given indices that is still on the MM}
begin
SetLength(tPath, eTile + 1); //Removes tiles not in range
InvertPath(tPath);
SetLength(tPath, Length(tPath) - sTile);
InvertPath(tPath);

H := High(tPath);
for Result := H downto 0 do //Finds the furthest visible tile.
if (Sqrt(Sqr((tPath[Result].X - P.X) * 4.0) + Sqr((tPath[Result].Y - P.Y) * 4.0)) < 69.0) then
begin
IncEx(Result, sTile);
Exit;
end;
Result := -1;
end;

{
Creates a TPA between points, tDist intervals, Rand randonmness (Tiles)
}
function CreatePathBetween(fT, tT: TTile; tDist, Rand: Integer): TTileArray;
var
hyp, xR, yR: Extended;
l: Integer;
begin
if not LoggedIn then Exit;

{ Set Result Length and first point at initial tile. }
SetLength(Result, Ceil(Distance(fT.X, fT.y, tT.X, tT.Y) * 1.0 / tDist) + 1);
Result[0] := fT;

{ If no need for intermediate tiles then nvm. :) }
if (Distance(fT.X, fT.Y, tT.X, tT.Y) <= tDist) then
begin
Result[High(Result)] := tT;
Exit;
end;

{ Ratios for 'Similar Triangles' maths. }
hyp := Hypot(fT.X - tT.x, fT.Y - tT.Y);
xR := (tT.X - fT.X) / hyp;
yR := (tT.Y - fT.Y) / hyp;

{ Set points. }
for l := 1 to High(Result) - 1 do
begin
Result[l].X := Round(fT.X + l * tDist * xR) + RandomRange(-Rand, Rand);
Result[l].Y := Round(fT.Y + l * tDist * yR) + RandomRange(-Rand, Rand);
end;

{ Last point is tT. }
Result[High(Result)] := tT;
end;

{
Expands ctrlPoints into a path with tDist intervals and Randomness
}
function ExpandPath(ctrlTiles: TTileArray; tDist, Randomness: Integer): TTileArray;
var
i, h: Integer;
t: T2DPointArray;
begin
Result := [];
h := High(ctrlTiles);
SetLength(t, h);

for i := 1 to h do
t[i - 1] := CreatePathBetween(ctrlTiles[I - 1], ctrlTiles[I], tDist, Randomness);

Result := MergeATPA(t);
end;

{
Walks to a path.
}
function EnterPath(Path: TTileArray): Boolean;
var
I: Integer;
tmpPath: TTileArray;
begin
if (FindTileOnPath(I, Path, GetMyPos, 50)) then
begin
{ Create path between current tile and closest path tile }
tmpPath := CreatePathBetween(GetMyPos, Path[I], 3, 5);
Result := WalkPath(tmpPath);
if (Result) then Exit;
end;
WriteLn('Could not enter path!');
end;

{
Walk to a tile.
}
function WalkTile(T: TTile): Boolean;
begin
Result := EnterPath([T]);
end;

{
Walk to a path.
}
function Walk(Path: TTileArray; Destination: Integer): Boolean;
var
P, DTile: TTile;
H, D, tileInd, pInd, Attempts, time: Integer;
begin
time := GetTimeRunning;

H := High(Path);
if (H < 0) then Exit;

if (Destination = -1) then
D := H
else
D := Destination;

{ Expands path and finds new destination index }
DTile := Path[D];
Path := ExpandPath(Path, 1, 3);
H := High(Path);
FindTileOnPath(D, Path, DTile, 3);

{ Finds you on a path and attempts to walk to it if possible }
if (not(EnterPath(Path))) then
begin
p := GetMyPos;
WriteLn('E:: Lost en route. ' + IntToStr(p.x) + ', ' + IntToStr(p.y));;
LogOut;
Exit;
end;

FindTileOnPath(pInd, Path, GetMyPos, 5);
if (pInd > D) then
begin
{ Sorts out walking direction }
InvertPath(Path);
D := H - D;
pInd := H - pInd;
end;

P := GetMyPos;
{ Loops through the tiles and walks :) }
while (Distance(P.X, P.Y, Path[D].X, Path[D].Y) > 3) and (Attempts < 5) do
begin
tileInd := FurthestVisibleTile(Path, P, pInd, D);
{ If the furthest tile is found, Walk to it! }
if (tileInd > -1) then
begin
WriteFMT('Next Tile: %d (%d, %d)',
[tileInd, Path[tileInd].X, Path[tileInd].Y]);
if (WalkToTile(Path[tileInd],
0,
RandomRange(1, 4)*Integer(Random(20)<>0))) then
begin
Attempts := 0;
Wait(100 + Random(100));
P := GetMyPos;
FindTileOnPath(pInd, Path, P, 5);
end;
end;
Inc(Attempts);
end;

R_FFlag(0);
Result := (DistanceFrom(Path[D]) <= 5);
if (not(Result)) then
WriteLn('Walk failed');
end;


~Home

Nava2

2011-09-10 13:32:41

I posted the wrong version, edited.

Coh3n

2011-09-10 13:50:09

Thanks, but no offense, I'll probably end up using a custom version of R_WindWalk simply because it essentially accomplishes the exact same thing you're doing in 1/10 the amount of code, and I don't have the time to figure out exactly how your code works. I've heard some good things about R_WindWalk, but maybe you could elaborate a little more on why you think it's so bad? It doesn't matter if it's ugly, it works, and it's in Reflection not MSI, so I couldn't care less. Any failsafes it lacks can be added in a custom version (needs to be custom to work with SPS anyway).

It just seems like your system is pretty complicated to accomplish something simple. I won't even be able to work on this for some time, so if you want to do it your way, be my guest. :p

From: mormonman
Small bug… I think I'm going to scrap the whole wind/gravity spline idea.

Edit: I decided that I like the whole "odd" tile error I was getting. It's human-like.
Suggest those functions in the Simba forum?

Harry

2011-09-10 13:55:06

So what's wrong with R_WebWalk using a waypoint system (to prevent it from clashing into stuff on the path)? I don't see any issues with it other than the prepackages version has no failsafes.

Nava2

2011-09-10 14:17:42

As I stated before, the issue is in WindWalk.

But, my code will also recover from any point on the path and still walk it perfectly – even if the player is not on a ctrl point. You could use Mormon's spline algorithm to expand the path just fine, and Zeph and I's version uses similar triangles math which will do the same thing just faster (not that it matters here).

Harry

2011-09-10 14:31:23

From: Nava2
As I stated before, the issue is in WindWalk.

But, my code will also recover from any point on the path and still walk it perfectly – even if the player is not on a ctrl point. You could use Mormon's spline algorithm to expand the path just fine, and Zeph and I's version uses similar triangles math which will do the same thing just faster (not that it matters here).

You mean how WindWalk won't click the point if it's too close to your current location? I failsafed that in my WebWalk and it works great. But cool.

Coh3n

2011-09-10 14:31:31

From: Nava2
As I stated before, the issue is in WindWalk.
You still haven't said what the issue is.

From: Nava2

But, my code will also recover from any point on the path and still walk it perfectly – even if the player is not on a ctrl point.
So will WindWalk.

From: Nava2
You could use Mormon's spline algorithm to expand the path just fine, and Zeph and I's version uses similar triangles math which will do the same thing just faster (not that it matters here).
Mormon's algorithm is R_WindPath, which is called in R_WindWalk.

All I've gotten out of this argument is that both methods do exactly the same thing and have the same capabilities, just done in different ways.

Coh3n

2011-10-05 22:51:13

So I've been working on this the last couple days. I decided to implement Nava's system and it's turned out to fit nicely into MSI. I just need to test it with SPS then recreate all the script paths (which may take some time).