SRL Archive - Research & Development Lounge

Color Tile Walking Preparation


Boreas

2010-12-16 15:12:36

TLDR? Skip to bold.

This is a thread about how we can work together to prepare for several possible future color tile walking systems. By color tile walking system, I mean something that is based on a color version of GetMyPos, can accept paths (and webs made of nodes and paths) designed for reflection, and uses autocolor based simplification algorithms (write once (not counting tweaking) and apply everywhere). For this to work, regardless of what simplification algorithms are used, we need to prepare a special clean map before it can be simplified:

Step 1 is determining how the map will be stored. To do this we need the following data.
Q:How many pixels is a tile?
A:4x4=16 Thanks Quickmarch
Q:What are the dimensions in tiles of the world map?
It is 1863 by 1680 tiles.
Q:What is the diameter of the minimap (in pixels or tiles)?
A:37 tiles Thanks lordsaturn
Q:Where is MMCX,MMCY in relation to the current tile?
A:MMCX 627 MMCY 85. The center tile is MMCX1 626 MMCY1 83 MMCX2 629 MMCY2 86 Thanks lordsaturn for great pics
Q:What is the tile position of a known place?
A:Thanks again to lordsaturn. Tile 3043, 3349 (0,0 to the bottom left) by the falador fountain is pixels 3995,3275 to 3998,3278 on a map that is 7452, 6722 pixels (0,0 to the top left)

With this data I'm working a diagram here that will outline how to store the map.

Step 2 will be writing a function to take screenshots based on the specs in the diagram.
The screenshots will not contain symbols, so symbols approximate tile locations will need to be saved to a text file.

Q: Should we use something like this with explode in an INI or XML file?
A: INI with SymbolsChunk09,45=Bank-TileX,TileY-Water-TileX,TileY
Q: Do we have function that will detect all symbols within a given TBox, and return which symbols they are and where?
A: Never mind, see next one.

Is there a better way than recording symbol locations when processing screenshots?
Rather than doing that, would a few people like to volunteer to do the following? Use the PathMaker program to click on some bank symbols, and it spits out tile coords for each one. Have a script takes those tiles, figure out what chunk they are in (Tile X,Y is in chunk ((X-2044) div 25), ((Y-861) div 25)), stick those into the INI. Rinse and repeat for other symbols.

In the same file, we will need the tile locations of yellow NPC dots that do not walk around (bankers etc). Any volunteers for this?
More important than the symbols.

We also need someone to create an svn repo which will distribute this file, plus another INI file with a list of contributed map chunks, and the .simba file with the actual bitmap generating code.

Q: Can someone make a function that finds all symbol circles (which symbol is in them doesn't matter) in a given TBox and spits out a TPA of pixels that the symbol is covering?
A: Talked it over with my dad (programmer), I should have something soon.
Q: See previous question except for NPC/Drop/Player dots (maybe look for the shadow as it is the same for all 3).
A: Being handled on another thread

Step 2 so far:
  const 
CTWP = false; //Make true to help out CTWP

{*******************************************************************************
function CTWPGetMyPos: TTile;
By: BenLand100
Description: Returns the global tile position of your player.
*******************************************************************************}
function CTWPGetMyPos: TTile;
var
me: Integer;
begin
me := SmartGetFieldObject(0, hook_static_MyPlayer);
Result.x := SmartGetFieldInt(0, hook_static_BaseX) + SmartGetFieldInt(me, hook_animable_PixelX) / 512;
Result.y := SmartGetFieldInt(0, hook_static_BaseY) + SmartGetFieldInt(me, hook_animable_PixelY) / 512;
SmartFreeObject(me);

if CTWP then
CTWPMakeClean(Result);
end;

//LordSaturn
{function }procedure CTWPCollectDots(Bitmap: integer);//: TPointArray;
var
SearchColors: TIntegerArray;
OtherColors: T2DIntegerArray;
i, j, Hi, x, y, leftDev, rightDev, L, LTop: Integer;
ATPA: T2DPointArray;
TPA, FoundColorsTPA: TPointArray;
begin //red white yellow
//Freeze;
SearchColors := [217, 188, 14869218, 12961221, 56797, 52428];
SetLength(OtherColors, 3);
OtherColors[0] := [3553023, 789758, 2105598, 395004, 237, 206, 65536, 217, 188]; //red
OtherColors[1] := [16711422, 15527148, 13816530, 65536, 14869218, 12961221]; //white
OtherColors[2] := [1179390, 195836, 62965, 56797, 60909, 52428, 65536]; //yellow
LTop := 999;
SetLength(Result, LTop);
for i := 0 to 5 do
begin
if FindColors(TPA, SearchColors[i], 14, 14, 113, 113) then
FoundColorsTPA := CombineTPA(FoundColorsTPA, TPA); //if one color is covered, we will find the other
if (i mod 2 = 1) then
begin
ATPA := TPAtoATPAEx(FoundColorsTPA, 1, 0);
Hi := High(ATPA);
for j := 0 to Hi do
begin
//if (GetColor(ATPA[j][0].x, ATPA[j][0].y+1) = 65536) then //it is a dot
begin //begin finding the whole dot

leftDev := 0;
if (GetColor(ATPA[j][0].x-1, ATPA[j][0].y) = 65536) then //finding the left bound of the dot
leftDev := -1
else
if (GetColor(ATPA[j][0].x-2, ATPA[j][0].y) = 65536) then
leftDev := -2
else
if (GetColor(ATPA[j][0].x-1, ATPA[j][0].y) = 217) then
leftDev := -1;

rightDev := 0;
if (GetColor(ATPA[j][0].x+1, ATPA[j][0].y) = 65536) then //finding the right bound of the dot
rightDev := 1
else
if (GetColor(ATPA[j][0].x+2, ATPA[j][0].y) = 65536) then
rightDev := 2
else
if (GetColor(ATPA[j][0].x+1, ATPA[j][0].y) = 188) then
rightDev := 1;

for x := ATPA[j][0].x + leftDev to ATPA[j][0].x + rightDev do
for y := ATPA[j][0].y - 3 to ATPA[j][0].y + 1 do
begin
if ((x = ATPA[j][0].x + leftDev) and (y = ATPA[j][0].y - 3)) or //ignore 4 corners
((x = ATPA[j][0].x + leftDev) and (y = ATPA[j][0].y + 1)) or
((x = ATPA[j][0].x + rightDev) and (y = ATPA[j][0].y - 3)) or
((x = ATPA[j][0].x + rightDev) and (y = ATPA[j][0].y + 1)) then
Continue;
if InIntArray(OtherColors[i/2], GetColor(x, y)) then
begin
if (L = LTop) then
begin
LTop := LTop*2;
//SetLength(Result, LTop);
end;
FastSetPixel(x,y,clWhite,Bitmap); //Result[L] := Point(x, y);
Inc(L);
end;
end;
end;
end;
SetLength(FoundColorsTPA, 0);
end;
end;
//SetLength(Result, L);
//Unfreeze;
end;






procedure CTWPMakeClean(MyTile: TTile);
var
CAB: TBox; //CurrentAreaBox
Chunks, Corners, Corners2: TPointArray;
i, i2, counter, MainBitmap, ChunkBitmap: intger;
ChunkBitmaps: array of integer;
TmpPoint: TPoint;
ChunksTiles, FragmentsTiles, FragmentsPixels, FragmentsPos: TBoxArray;
begin
//Get tile bounds of CurrentAreaBox
CAB := IntToBox(MyTile.x - 12, MyTile.y + 12, MyTile.x + 12, MyTile.y - 12);//Make sure this does not normalize

//CurrentAreaBox is spread over 1, 2, or 4 chunks, so these arrays are set to 4
//In these arrays, [0] corresponds to the chunk that the top left corner of CAB is in,
//[1] is top right, [2] is bottom left, [3] is bottom right. This defined in Corners
Corners := [Point(CAB.x1, CAB.y1), Point(CAB.x2, CAB.y1), Point(CAB.x1, CAB.y2), Point(CAB.x2, CAB.y2)];
SetArrayLength(Chunks, 4);
SetArrayLength(ChunksTiles, 4);
SetArrayLength(FragmentsTiles, 4);
SetArrayLength(FragmentsPixels, 4);
SetArrayLength(FragmentsPos, 4);

//I'm using these conventions when working with tiles and pixels
//Pixels have 0,0 in the top left
//Tiles have 0,0 in the bottom left
//Chunks have 0,0 in the bottom left (the are 100x100 pixels, but they deliberately start and end on tiles so that no tile
//is spread over multiple chunks (25x25 tiles), therefore, they follow the tile convention)
//X1,Y1 is always top left, X2,Y2 is always bottom right
//For pixels, Y1 is lower than Y2. For tiles and chunks, Y2 is lower than Y1

for i:= 0 to 3) do
begin
//Find the Chunk that the corresponding corner of CAB is in
Chunks[i] := Point(((Corners[i].x-2044) div 25),((Corners[i].y-861) div 25));
//Find the tile bounds of that chunk
ChunksTiles[i] := IntToBox(2044+Chunks[i].x*25, 885+Chunks[i].y*25, 2068+Chunks[i].x*25, 861+Chunks[i].y*25);//No normalize
//These are the opposite corners of Corners
Corners2 := [Point(ChunksTiles[i].x2, ChunksTiles[i].y2), Point(ChunksTiles[i].x1, ChunksTiles[i].y2), Point(ChunksTiles[i].x2, ChunksTiles[i].y1), Point(ChunksTiles[i].x1, ChunksTiles[i].y1)]; //yes normalize
//Find the intersection of the corresponding Corner of CAB and the opposite corner of the Chunk
//that contains the Corner of CAB. Also make X1 lower than X2, and Y1 higher than Y2 (tiles start from the bottom left)
FragmentsTiles[i] := IntToBox(Min(Corners[i].x,Corners2[i].x), Max(Corners[i].y,Corners2[i].y), Max(Corners[i].x,Corners2[i].x), Min(Corners[i].y,Corners2[i].y);
//Find the pixel bounds (on MainBitmap) of thoses tiles
FragmentsPixels[i] := IntToBox(14+(FragmentsTiles[i].x1-(MyTile.x-12))*4, 14+((MyTile.y+12)-FragmentsTiles[i].y1)*4, 112-((MyTile.x+12)-FragmentsTiles[i].x2)*4, 112-(FragmentsTiles[i].y2-(MyTile.y-12))*4);
//Find the pixel box in Chunk that fragment will be copied to
FragmentsPos
end;

//Is this right?
MainBitmap := CreateBMP(128,128) //Create a bitmap 128x128
//Freeze; //Is this needed for CopyClientToBitmap?
//What is the command to do this?
CopyClientToBitmap(MainBitmap, 564, 21, 691, 148, 0, 0, 127, 127)
//Unfreeze;//Is this needed for CopyClientToBitmap?
//What is the command to do this?
SetTargetBitmap(MainBitmap);

//Do FindColorsTolerance etc on the MainBitmap

CTWPCollectDots(MainBitmap); //needs to set pixels on MainBitmap

White out the symbols //needs to set pixels on MainBitmap

//end of setting pixels on MainBitmap

SetLength(CopiedChunks,1);
CopiedChunks[0] := Point(-1,-1);
for i:= 0 to 3 do
Skip := false;
for i2 := 0 to counter do
begin
if (Chunks[i].x = CopiedChunks[i2].x) and (Chunks[i].x = CopiedChunks[i2].x) then
begin
Skip := true;
break;
end;
end;
if not Skip then
begin
SetArrayLength(CopiedChunks, counter+1);
CopiedChunks[Counter] := Chunks[i];
Inc(Counter);

//How to Load bitmap? How to make a number 2 digits by padding 0 on the left if needed? ie CleanChunk03-48.bmp
ChunkBitmap := CreateBitmapFromFile(CTWPath+'CleanChunk'+Pad(2,inttostr(Chunks[i].x))+'-'+Pad(2,inttostr(Chunks[i].y))'+'.bmp');
//If this file does not exist, create it, and fill with clWhite if it isn't already. (clWhite is RGB 255,255,255 right?)

//How to do this while skipping pixels in box FragmentPixels in MainBitmap that are clWhite?
Copy from FragmentsPixels[i].x1, FragmentsPixels[i].y1, FragmentsPixels[i].x2 FragmentsPixels[i].y2 on MainBitmap
to FragmentsPos[i].x1, FragmentsPos[i].y1, FragmentsPos[i].x2, FragmentsPos[i].y2 on ChunkBitmap

end;
end;

//restore the target
end;

Frement

2010-12-16 15:16:22

I would say a tile would be 3x3 on the minimap, as the middle white box in minimap is 3x3, and its your character, and it stands on one tile.

Quickmarch

2010-12-16 15:17:30

Officially it's 4x4 pixels per tile on the minimap.

Frement

2010-12-16 15:18:21

From: Quickmarch
Officially it's 4x4 pixels per tile on the minimap.


Then the middle box (Your player) is 1px off? NPC / Drop dots are 4x4.

Quickmarch

2010-12-16 15:21:25

It might be, but I haven't looked thoroughly at the dimensions/coordinates of the minimap.

lordsaturn

2010-12-16 16:01:33

Come on guys, this is stupid. Quit giving wrong/confusing information. Instead of making a post about nothing, do 5 seconds research come up with some god damn facts.

The local player dot in the center of the minimap is 3x3. Drop dots are roughly 4x4, more of a hexagonal shape but with only 4 sides.
Quote from runescape wiki:
The scale of the Minimap is equal to that of the official Java-based World Map at 100% zoom, or exactly 16 pixel-lengths^2 = 1 square. Alternatively, the conversion 4 pixel-lengths = 1 square-length may be used. When perfectly aligned along the horizontal and vertical axes, the Minimap displays a circular area with a radius of exactly 19 square-lengths.(Mems only)

Quickmarch

2010-12-16 16:08:56

From: lordsaturn
Come on guys, this is stupid. Quit giving wrong/confusing information. Instead of making a post about nothing, do 5 seconds research come up with some god damn facts.

The local player dot in the center of the minimap is 3x3. Drop dots are roughly 4x4, more of a hexagonal shape but with only 4 sides.
Quote from runescape wiki:


Sprites overlap… If you'd looked at the actual client the convertion from tile to screen uses a 4x multiplier.

lordsaturn

2010-12-16 16:15:53

From: Quickmarch
Sprites overlap… If you'd looked at the actual client the convertion from tile to screen uses a 4x multiplier.


What are you saying? I know that dot sprites overlap and I know how the tiletomm function converts.

Quickmarch

2010-12-16 16:18:01

You were saying I hadn't got my facts right, I was telling you otherwise.

Gu3st...

2010-12-16 16:22:42

From: Quickmarch
You were saying I hadn't got my facts right, I was telling you otherwise.
Actually, he was saying you were correct

Quickmarch

2010-12-16 16:29:32

From: Zyt3x
Actually, he was saying you were correct


Where?

Gu3st...

2010-12-16 16:45:25

From: Quickmarch
Where?
Read the quote he posted from the runescape wikia

Quickmarch

2010-12-16 16:50:00

From: lordsaturn
Come on guys, this is stupid. Quit giving wrong/confusing information. Instead of making a post about nothing, do 5 seconds research come up with some god damn facts.


I don't call that saying I was correct, regardless of his wikia quote.

Boreas

2010-12-16 16:51:00

So tiles are 4x4 pixels. On the tile the player is currently on, which pixel out of those 16 is MMCX,MMCY?

This should find the answer
var 
x, y :integer;
OldTile, New Tile: Tpoint;
begin
for x:= -5 to 5 do
begin
for y:= -5 to 5 do
begin
OldTile := GetMyPos;
Mouse(MMCX + x, MMCY + y, 0, 0, true);
Wait(1000 + random(1000));
NewTile := GetMyPos;
Writeln('X pixel: '+intostr(x)+' X tile: '+inttostr(NewTile.x-OldTile.x)+' Y pixel: '+intostr(Y)+' Y tile: '+inttostr(NewTile.Y-OldTile.Y));
end;
end;
end.

Feroc1ty

2010-12-16 19:13:46

I had the colored tile walking idea a very long time ago and actually made a post having an idea of an option of doing so, but it was completely ignored by the public… anyways here's the link hope you like what you see.

http://villavu.com/forum/showthread.php?t=60032

Boreas

2010-12-16 19:50:09

From: Feroc1ty
I had the colored tile walking idea a very long time ago and actually made a post having an idea of an option of doing so, but it was completely ignored by the public… anyways here's the link hope you like what you see.

http://villavu.com/forum/showthread.php?t=60032


That looks great for when you know what tile you are on. This thread is working towards a system that will tell you what tile you are on, like GetMyPos in reflection. They will complement each other quite well.

lordsaturn

2010-12-16 23:08:04

Here is proof of the 4x4 pixel to tile ratio. Taken from a zoomed in world map.

Boreas

2010-12-16 23:29:21

Awesome, thanks.

Is the 3x3 current tile marker in the bottom right corner of the tile like this?
XXXX
XYYY
XYYY
XYYY

Is the center of the 3x3 marker MMCX MMCY?

lordsaturn

2010-12-16 23:45:08

running this code:
procedure DebugTilesToMM;
var
x,y:integer;
pt:tpoint;
newtile,loc:ttile;
tpa:tpointarray;
begin
EnableSmartCanvas;
loc := GetMyPos;
for x := loc.x-50 to loc.x+50 do
for y := loc.y+50 downto loc.y-50 do
begin
newtile := tile(x,y);
if TileOnMM(newtile) then
begin
pt:=TileToMM(newtile);
tpa := CombineTPA(tpa, [pt]);
end;
end;
pt := TileToMM(loc);
writeln('tiletomm(myloc): '+tostr(pt));
writeln('mmcx, mmcy: 'tostr(point(mmcx, mmcy)));
SMART_DrawDotsEx(true, tpa, clRed);
SMART_DrawDotsEx(false, [point(mmcx, mmcy)], clBlue);
SMART_DrawDotsEx(false, [pt], clGreen);
end;
outputs this:
tiletomm(myloc): (626, 85)
mmcx, mmcy: (627, 85)
and this:


further observation:
then:

Boreas

2010-12-17 00:10:10

Perfect shot, thanks.

Anyone know the world map dimensions in tiles?

Boreas

2010-12-17 00:28:33

lordsaturn could you post a pic for this please? Or just let me know whether the box goes out of the minimap circle.
procedure DebugTilesToMM;
var
x,y:integer;
tpa:tpointarray;
begin
EnableSmartCanvas;

y:= 35;
for x := 578 to 877 do
tpa := CombineTPA(tpa, [point(x,y)]);
y:= 134;
for x := 578 to 877 do
tpa := CombineTPA(tpa, [point(x,y)]);
y:= 578;
for x := 35 to 134 do
tpa := CombineTPA(tpa, [point(x,y)]);
y:= 877;
for x := 34 to 134 do
tpa := CombineTPA(tpa, [point(x,y)]);
SMART_DrawDotsEx(true, tpa, clRed);
end;

lordsaturn

2010-12-17 00:43:57

on the minimap:
x-diameter in tiles:36
x-diameter in pixels:147
y-diameter in tiles:37
y-diameter in pixels:147
Please note that number of tiles visible can change depending on camera angle, as it did in this example. When minimap is facing directly north, the number of x-tiles visible is 37.







as you requested boreas, i think u might have messed up ur code though:

x-coord out of minimap at 681

Boreas

2010-12-17 00:49:39

Lol yes I did. Forgot to switch x and y for the verticals, this should do it.

procedure DebugTilesToMM;
var
x,y:integer;
tpa:tpointarray;
begin
EnableSmartCanvas;

y:= 35;
for x := 578 to 877 do
tpa := CombineTPA(tpa, [point(x,y)]);
y:= 134;
for x := 578 to 877 do
tpa := CombineTPA(tpa, [point(x,y)]);
x:= 578;
for y := 35 to 134 do
tpa := CombineTPA(tpa, [point(x,y)]);
x:= 877;
for y := 34 to 134 do
tpa := CombineTPA(tpa, [point(x,y)]);
SMART_DrawDotsEx(true, tpa, clRed);
end;

That should be a 25x25 tile box. Which according to pythag is a diagonal of 35.3553391, which should be well within the minimap.

lordsaturn

2010-12-17 01:09:51

Yeah, that's what I did when i fixed your code earlier and I was confused because the box went off the minimap. x2 should be 677.
procedure DebugTilesToMM;
var
box:tbox;
begin
EnableSmartCanvas;

box.x1 := 578;
box.y1 := 35;
box.x2 := 677;
box.y2 := 134;

SMART_DrawBoxEx(True, box, clRed);
end;



Boreas

2010-12-17 01:21:56

Ah right, thanks. I had a typo in my notes for the center tile x1y1x2y2 (626,83,829,86).

One last thing. What was the tile you were on by the falador fountain when you did tiletomm(myloc): (626, 85)?

lordsaturn

2010-12-17 01:25:19

the tile was (3043, 3349) :)

next step is cleaning up the map, right? symbols will needed to be removed in addition to the names of the different location across runescape, which will be quite a task.

Boreas

2010-12-17 01:28:05

Perfect, thank you so much.

It's not finished, but this what the numbers are going into https://docs2.google.com/drawings/edit?id=12LUsy_-BcHK0TfILCMRns-B2n3RbMNFJRPqKkafBOVM&hl=en

TomTuff

2010-12-17 01:45:43

will pay to play be covered by this system as well?

Boreas

2010-12-17 02:08:11

From: TomTuff
will pay to play be covered by this system as well?


Short answer: p2p will not be denied coverage.

Long answer:
I'm making the 0,0 of the special map roughly around the bottom left corner of the map in here
http://simbareflection.googlecode.com/svn/trunk/Tools/PathMaker/

Which pieces of the special map get automatically generated depends on the community. At first it will just be pieces of commonly autoed areas, and will then grow and grow based on what people submit. We can also make a script that does nothing but wander all over the world generating more pieces.

So basically if you want an area covered for a certain script, just run that script with reflection walking enabled and the upcoming map piece generating function enabled, and you will get a bunch of clean bitmaps. Then run a function to turn those clean bitmaps into simple bitmaps (that function has not been chosen or developed yet), and then the area is covered.

(This system will be compatible with reflection paths, and I don't know how reflection handles dungeons and upper floors. So that will either be another map if it's another level, or it's in the black areas of the map linked to above it will be the same kind of deal.)

Frement

2010-12-17 02:24:13

From: Boreas
Short answer: p2p will not be denied coverage.

Long answer:
I'm making the 0,0 of the special map roughly around the bottom left corner of the map in here
http://simbareflection.googlecode.com/svn/trunk/Tools/PathMaker/

Which pieces of the special map get automatically generated depends on the community. At first it will just be pieces of commonly autoed areas, and will then grow and grow based on what people submit. We can also make a script that does nothing but wander all over the world generating more pieces.

So basically if you want an area covered for a certain script, just run that script with reflection walking enabled and the upcoming map piece generating function enabled, and you will get a bunch of clean bitmaps. Then run a function to turn those clean bitmaps into simple bitmaps (that function has not been chosen or developed yet), and then the area is covered.

(This system will be compatible with reflection paths, and I don't know how reflection handles dungeons and upper floors. So that will either be another map if it's another level, or it's in the black areas of the map linked to above it will be the same kind of deal.)


0,0 top-left corner would make more sense in my opinion.

any_one

2010-12-17 02:45:21

From: Frement
0,0 top-left corner would make more sense in my opinion.


+1, since this is how the pixel coordinates usually are. Though, usual cartesian coordinates could be good too (if you do any sort of maths it won't be very strange at all :)). Just could get confusing.

Boreas

2010-12-17 02:45:47

From: Frement
0,0 top-left corner would make more sense in my opinion.


Tiles have 0,0 at the bottom left, and the map chunk names are related to tiles.
For example CleanChunk40-52.bmp would contain tiles 2044+40*25, 861+52*25 to 2044+40*25+24, 861+52*25+24.

Converting between tiles and pixels is done on the chunk level, and chunks are only 100x100 pixels. At that point we flip it like TileToMM does.

lordsaturn

2010-12-17 02:50:19

From: Boreas
Tiles have 0,0 at the bottom left, and the map chunk names are related to tiles.
For example CleanChunk40-52.bmp would contain tiles 2044+40*25, 861+52*25 to 2044+40*25+24, 861+52*25+24.

Converting between tiles and pixels is done on the chunk level, and chunks are only 100x100 pixels. At that point we flip it like TileToMM does.


Ok so I think the hardest part of the color walkers will be location identification. In order to identify the chunk the player was standing in we could use identifiers like symbols or tree count in each chunk?

Boreas

2010-12-17 03:03:24

From: lordsaturn
Ok so I think the hardest part of the color walkers will be location identification. In order to identify the chunk the player was standing in we could use identifiers like symbols or tree count in each chunk?


Exactly. All we need is a replacement for GetMyPos. TileToMM, WalkToTile, and WalkPath all can be made without reflection, they just need to know your current tile. Same deal with the relativity functions Feroc1ty posted.

Things like symbols and last known position narrow down which chunks to check. Tree and rock counts further narrow it down. Then we compare a simplified version of our current area to the simplified versions of those chunks. By simplified I mean it will pretty much be white, with green squares to represent trees, grey squares for rocks etc. The more stuff that can be consistently autocolored the better.

E: 3am, passing out now

Feroc1ty

2010-12-17 04:26:14

From: Boreas
Exactly. All we need is a replacement for GetMyPos. TileToMM, WalkToTile, and WalkPath all can be made without reflection, they just need to know your current tile. Same deal with the relativity functions Feroc1ty posted.

Things like symbols and last known position narrow down which chunks to check. Tree and rock counts further narrow it down. Then we compare a simplified version of our current area to the simplified versions of those chunks. By simplified I mean it will pretty much be white, with green squares to represent trees, grey squares for rocks etc. The more stuff that can be consistently autocolored the better.

E: 3am, passing out now


I really never understood why you would waste hours upon hours of time to simply get a script to recognize that you're at the varock bank… and than move you to wherever you need to be…

What I do is say… place my character wherever he needs to be, and than use my moving function to move the character using my function in an array of tiles, and to make the movement to tiles "random" I simply make the function pseudo-random ( make it use a random tile number, but remember the random and balance it on the next movement)


Not discouraging anyone working on this but it's like… we've had scripts start when your character is positioned correctly for ever now, and it's easy to simulate reflections movement… without the fact that the actual function would have no clue where the fuck it is, simply knows what it's doing.


(Never really needed to ever use a world walker, but if that's ever to change I probably wouldn't use the colored one and go with the reflection reliability and precision.)

Boreas

2010-12-17 09:42:29

Right, but this is also for fighting scripts, teleporting randoms, scripts that talk to NPCs that move. Basically anytime there is movement that you are not in control of, which would offset blind clicking.

Sir R. M8gic1an

2010-12-17 09:57:02

Cleaning the minimap shouldn't be too hard…


It's basically doing picture 1, and then whatever doesn't belong to the background is part of the 'clean' MM. The background can also be used to decide where you are.

I think my TAreas might actually be very useful for GetMyPos

TAreas: http://villavu.com/forum/showthread.php?t=58309

~RM

Boreas

2010-12-17 10:13:41

From: Sir R. Magician
Cleaning the minimap shouldn't be too hard…


It's basically doing picture 1, and then whatever doesn't belong to the background is part of the 'clean' MM. The background can also be used to decide where you are.

I think my TAreas might actually be very useful for GetMyPos

TAreas: http://villavu.com/forum/showthread.php?t=58309

~RM


Cleaning the minimap is actually just copying from the client to a bitmap, except for symbols and dots, and also putting in static NPCs like bankers. You will then be able to take the clean bitmaps and turn them into simplified bitmaps with picture 1 and TAreas (or whatever your simplification algorithm of choice is).

any_one

2010-12-17 10:28:31

How would you suggest doing picture 1 there? I tried a little test function and well… it works, but it takes a heck of a long time to process the whole minimap. Basically the function takes a bitmap and tolerance and minimum number of pixels and outputs something like array of TArea (currently only the TPA part). Automatically colouring the areas for debugging purposes is actually harder than I thought so I just made it do shades of gray. This takes around 1-2 mins of processing (haven't actually accurately timed it) and produces a picture like yours (but with solid grey areas) except it treats non-contiguous areas as different even if they are the same colour. I'd probably recode the whole thing (and it might even be faster?) if it didn't separate non-contiguous areas.

How did you get that picture originally anyway? Picking colours? I would really like to see an example of a function that does what you have there :)

Sir R. M8gic1an

2010-12-17 10:40:59

That picture was made in paint, but I'd probably do something among these lines:


TPA := TPAFromCircle(MMCenter, MMRadius);
Colors := GetColors(TPA);

SetLength(OldColors, 1);
SetLength(ColorCounts, 1);
ArrayLength := 1;

for i:= 0 to High(Colors) do
begin
ColorPos := InIntArrayPos(Colors[i]);

if ColorPos > 0 then
inc(ColourCounts[ColorPos])
else begin
SetLength(OldColors, ArrayLength + 1);
SetLength(ColorCounts, ArrayLength + 1);

OldColors[ArrayLength] := Colors[i];
ColorCounts[ArrayLenght] := 1;

inc(ArrayLength);
end;

BubbleSortArrays(OldColors, ColorCounts);

i := 0;

while (ColorsCounts[i] > 30) do
begin
FindColorsTPA(TPA, OldColors[i], MM, 10); //10 tolerance

TArea[h] := TPA;

Inc(i);
end;

end;

Somewhere among these lines :p

~RM

Boreas

2010-12-17 14:46:37

Updated first post with bold questions for step 2.

lordsaturn

2010-12-17 14:51:27

I think naturally we should use INI because simba has INI functions and does not have XML functions. Even though I personally enjoy XML a lot more and I am sure many others would concur.

Edit: I think this would be a good opportunity to rewrite all symbol finding functions. I know in the past several alternatives have been offered (e.g. nielsie) but none have flourished.

e2: it is not viable to record symbol locations because they are random. thus symbols location detection should be independent from symbols, and if it so happens to find a symbol then we can narrow down the number of chunks that could be a possible match.

because of the random symbol location, perhaps if we find a symbol in one chunk we should also attribute the symbol to the chunks around it? the other option would be to only attribute the symbol to the chunk in which it was found while data collecting, and when recalling chunks when you have found a symbol, also check the chunks adjacent to the chunk with the symbol for a match. following me?

i understand this logic could be subject to change because i do not know how dependent our location detection will be upon symbols. perhaps (hopefully) not at all.


e3: the only exception to the above is the dungeon arrow, which i think is consistent with it's location on the map. please someone correct me if i'm wrong.


e4: reread your first post. "approximate symbol tiles" could work.

e5: i think that grabbing the symbols through pathmaker, then storing the tiles is a good plan. Then when calling upon the chunks that have that symbol, also include chunks say within 30 tiles of the symbol that was found.

Boreas

2010-12-17 15:38:02

From: lordsaturn
I think naturally we should use INI because simba has INI functions and does not have XML functions. Even though I personally enjoy XML a lot more and I am sure many others would concur.

Edit: I think this would be a good opportunity to rewrite all symbol finding functions. I know in the past several alternatives have been offered (e.g. nielsie) but none have flourished.

e2: it is not viable to record symbol locations because they are random. thus symbols location detection should be independent from symbols, and if it so happens to find a symbol then we can narrow down the number of chunks that could be a possible match.

because of the random symbol location, perhaps if we find a symbol in one chunk we should also attribute the symbol to the chunks around it? the other option would be to only attribute the symbol to the chunk in which it was found while data collecting, and when recalling chunks when you have found a symbol, also check the chunks adjacent to the chunk with the symbol for a match. following me?

i understand this logic could be subject to change because i do not know how dependent our location detection will be upon symbols. perhaps (hopefully) not at all.


e3: the only exception to the above is the dungeon arrow, which i think is consistent with it's location on the map. please someone correct me if i'm wrong.


e4: reread your first post. "approximate symbol tiles" could work.


Gotcha, INI it is then.

Yea the randomness of symbols is why they will be stripped out for the clean chunks. However if their approximate locations are recorded in an INI, at least we will know which chunks to give priority. Let's say that symbols can vary by as much as 10 tiles, if a symbol is within 10 tiles from the edge of the 25x25 tile chunk, then the adjacent chunk can also be given priority.

E: Re your E5. Yea now that I think about it the less real time processing the better. Removing symbols and dots is enough as it is.

Heysus

2010-12-17 20:33:45

From: lordsaturn
Ok so I think the hardest part of the color walkers will be location identification. In order to identify the chunk the player was standing in we could use identifiers like symbols or tree count in each chunk?


What we should do is check the world map the first time and then spiral search from the last chunk we were in after that.

lordsaturn

2010-12-17 20:56:02

From: Heysus
What we should do is check the world map the first time and then spiral search from the last chunk we were in after that.


The point is not to have to check the world map. It is stupid and ugly and slow and I hate the idea.

Heysus

2010-12-17 21:04:04

From: lordsaturn
The point is not to have to check the world map. It is stupid and ugly and slow and I hate the idea.


I meant just the first time when the script starts but I guess it would be better if we could do it without checking the map.

Boreas

2010-12-17 22:42:14

From: Heysus
I meant just the first time when the script starts but I guess it would be better if we could do it without checking the map.


That actually could work very well when there isn't a LastKnownPosition, or when the available symbols/trees/rocks/etc on the minimap are not enough to get a location. The symbols on the in-game world map don't move. In fact, the map in the reflection SVN for the PathMaker is almost exactly the same (I saw one symbol in the port sarim/draynor area slightly off). So if we make a text file with symbol tiles from the PathMaker, and make a reverse symbol finder that IDs all symbols on the in-game world map viewed at 100%, we could get a tile location purely based on symbols, without even using simplification algorithms. Of course it would only be used when all else fails as it takes forever to load.

Edit: Actually it's even easier than that, I didn't know about the map in map button by the zoom level.
Ignore this, the map in map is a much better way.
program new;

//Make sure world map is at 100%
const //Fill in bounds for in-game world map.
WMX1 =
WMY1 =
WMX2 =
WMY2 =

//1600 pts out of 337,500

function WorldMapSymbols: array of TPointArray;
var
Grid: array of array of boolean;
i,i2,i3: integer;
Point, CenterPoint, NewPoint: Tpoint;
BlackTPA, SymbolTPA: TPointArray;
begin
//Make grid for world map
SetArrayLength(Grid, 750);
for i := 0 to 759 do
begin
SetArrayLength(Grid[i), 450);
end;

//Fill grid with black points
FindColors(BlackTPA, 65536, WMX1, WMY1, WMX2, WMY2);
for i := 0 to High(BlackTPA) do
begin
Grid[BlackTPA[i].x][BlackTPA[i].y] := true;
end;

for i := 0 to High(BlackTPA) do
begin
Point:=BlackTPA[i];
i2 := 0;
repeat
CenterPoint.x := Point.x+Round(7*Sin(Radians(i2)));
CenterPoint.y := Point.y+Round(7*Cos(Radians(i2)));

Score := 0;
i3 := 0;
repeat
TempPoint.x:= CenterPoint.x+Round(7*Sin(Radians(i3)));
TempPoint.y:= CenterPoint.y+Round(7*Cos(Radians(i3)));
if Grid[TempPoint.x][TempPoint.y] then
Score := Score + 1;
i3 := i3 + 9;
//In addition to score threshold below, if you are halfway
//around and have less than 10 hits, don't bother
if (i3 = 180) and (Score < 10) then
break;
until i3 = 360;
//Max score is 40, meaning CenterPoint is the center point
//of a symbol whos outline is not covered at all
//If you want, you can use a lower score threshold, but then
//check if you see the background color 12632256 at radius 6
//from CenterPoint probably every 10 degrees

//Using 20 for now
if Score > 20 then //CenterPoint is probably a symbol
SymbolTPA := CombineTPA(SymbolTPA,[CenterPoint]);

i2 := i2 + 9;
until i2=360;
end;

SetArrayLength(BlackTPA, 0);
SetArrayLength(Grid, 0);
//SymbolTPA now has the centerpoints of all the symbols
//
For i:= 0 to High(SymbolTPA) do
writeln(inttostr(SymbolTPA[i].x)+','+inttostr(SymbolTPA[i].y));

marpis

2010-12-19 04:15:37

Note that we can't do shit before we can make real plugins to Simba, as now it's practically impossible. So many errors there.

Also, my method doesn't need a "cleaned" map, the RS worldmap with 100% zoom works fine.

Gu3st...

2010-12-19 06:40:59

From: marpis
Note that we can't do shit before we can make real plugins to Simba, as now it's practically impossible. So many errors there.

Also, my method doesn't need a "cleaned" map, the RS worldmap with 100% zoom works fine.
All those 'errors' were tiny mistakes made by you and not Simba / lazarus ;)

Wizzup?

2010-12-19 06:51:45

From: Zyt3x
All those 'errors' were tiny mistakes made by you and not Simba / lazarus ;)


Except for the one final feature I suppose. Do note that Raymond already made several plugins for Simba (the antirandom ones) and SMART is a plugin as well.

marpis

2010-12-19 07:51:57

From: Zyt3x
All those 'errors' were tiny mistakes made by you and not Simba / lazarus ;)


From: Wizzup?
Except for the one final feature I suppose. Do note that Raymond already made several plugins for Simba (the antirandom ones) and SMART is a plugin as well.


I've tried practically everything and still I get the

[Error] (2:1): Unable to register function function Galileo_FindMapInMapExx(LargeMap, SmallMap: array of array of array of integer; var foundX, foundY: integer; SideLength: integer; tol: extended; MaxFails: integer): boolean; at line 1

because it looks like I really can't use 3D Integer arrays in an exported function. I am truly confused with all this.

Frement

2010-12-19 08:24:32

From: marpis
I've tried practically everything and still I get the

[Error] (2:1): Unable to register function function Galileo_FindMapInMapExx(LargeMap, SmallMap: array of array of array of integer; var foundX, foundY: integer; SideLength: integer; tol: extended; MaxFails: integer): boolean; at line 1

because it looks like I really can't use 3D Integer arrays in an exported function. I am truly confused with all this.


Maybe pass the array as a string and explode it inside your plugin?

Wizzup?

2010-12-19 08:32:43

No. That is not a solution. Like I said, it'll be investigated soon enough. ATM I don't have time.

Frement

2010-12-19 08:39:06

From: Wizzup?
No. That is not a solution. Like I said, it'll be investigated soon enough. ATM I don't have time.


Maybe not a solution, but a fix? And whats wrong with that? If marpis wants the plugin released, and that is a fix for the problem, the solution can be figured out when you or someone else gets time to fix it permanently?

marpis

2010-12-19 08:52:33

From: Frement
Maybe pass the array as a string and explode it inside your plugin?


With a 1D intarray that'd be very easy but I just don't want to see the trouble just to overcome a problem that shouldn't really be there.
I'll wait until Wizzup or someone else has time.

Boreas

2010-12-19 10:42:35

Speaking of Simba, what's the best way to go about this?

1) Copy a piece of the client to a bitmap
2) Do color searches and set pixels on the bitmap
3)Load a second bitmap from a file, creating that file if it doesn't exist
—When creating a bitmap, is there a default color or do we have to fill it in?
4) Copy a TBox of pixels from first bitmap to second bitmap, except the clWhite (or whatever default color is) pixels in first bitmap
5) Free second bitmap and repeat steps 3-4 with the same first bitmap, but different TBoxes of the first bitmap and different second bitmaps
6) Assuming step 2 required setting the target, reset the target back to SMART. Also free first bitmap.

//Is this right?
MainBitmap := CreateBMP(128,128) //Create a bitmap 128x128
//Freeze; //Is this needed for CopyClientToBitmap?
//What is the command to do this?
CopyClientToBitmap(MainBitmap, 564, 21, 691, 148, 0, 0, 127, 127)
//Unfreeze;//Is this needed for CopyClientToBitmap?
//What is the command to do this?
SetTargetBitmap(MainBitmap);

//Do FindColorsTolerance etc on the MainBitmap

CollectDotsCTW; //needs to set pixels on MainBitmap

White out the symbols //needs to set pixels on MainBitmap

//end of setting pixels on MainBitmap

SetLength(CopiedChunks,1);
CopiedChunks[0] := Point(-1,-1);
for i:= 0 to 3 do
Skip := false;
for i2 := 0 to counter do
begin
if (Chunks[i].x = CopiedChunks[i2].x) and (Chunks[i].x = CopiedChunks[i2].x) then
begin
Skip := true;
break;
end;
end;
if not Skip then
begin
SetArrayLength(CopiedChunks, counter+1);
CopiedChunks[Counter] := Chunks[i];
Inc(Counter);

//How to Load bitmap? How to make a number 2 digits by padding 0 on the left if needed? ie CleanChunk03-48.bmp
ChunkBitmap := CreateBitmapFromFile(CTWPath+'CleanChunk'+Pad(2,inttostr(Chunks[i].x))+'-'+Pad(2,inttostr(Chunks[i].y))'+'.bmp');
//If this file does not exist, create it, and fill with clWhite if it isn't already. (clWhite is RGB 255,255,255 right?)

//How to do this while skipping pixels in box FragmentPixels in MainBitmap that are clWhite?
Copy from FragmentsPixels[i].x1, FragmentsPixels[i].y1, FragmentsPixels[i].x2 FragmentsPixels[i].y2 on MainBitmap
to FragmentsPos[i].x1, FragmentsPos[i].y1, FragmentsPos[i].x2, FragmentsPos[i].y2 on ChunkBitmap

end;
end;

//restore the target
end;

Gu3st...

2010-12-19 16:43:21

From: Boreas
1) Copy a piece of the client to a bitmap
2) Do color searches and set pixels on the bitmap
3)Load a second bitmap from a file, creating that file if it doesn't exist
—When creating a bitmap, is there a default color or do we have to fill it in?
4) Copy a TBox of pixels from first bitmap to second bitmap, except the clWhite (or whatever default color is) pixels in first bitmap
5) Free second bitmap and repeat steps 3-4 with the same first bitmap, but different TBoxes of the first bitmap and different second bitmaps
6) Assuming step 2 required setting the target, reset the target back to SMART. Also free first bitmap.
CopyClientToBitmap(). Does not need Freeze and default color = 0

Boreas

2010-12-20 10:24:17

From: Zyt3x
CopyClientToBitmap(). Does not need Freeze and default color = 0


Thanks for that. Also thanks to Wizz and Ray for answering other questions.
_____________________________________

Step 2 alpha is pretty much done, just gotta make it compile with Simba and test symbol finding methods.
const 
CTWBOn = false; //Make true to help out CTWB
color_Black = 65536;
CTWBPath = AppPath + 'CTWB\';
var
CircleOffsets: TPointArray;

//LordSaturn
{function }procedure CTWBCollectDots(Bitmap: integer);//: TPointArray;
var
SearchColors: TIntegerArray;
OtherColors: T2DIntegerArray;
i, j, Hi, x, y, leftDev, rightDev, L, LTop: Integer;
ATPA: T2DPointArray;
TPA, FoundColorsTPA: TPointArray;
begin //red white yellow
//Freeze;
SearchColors := [217, 188, 14869218, 12961221, 56797, 52428];
SetLength(OtherColors, 3);
OtherColors[0] := [3553023, 789758, 2105598, 395004, 237, 206, 65536, 217, 188]; //red
OtherColors[1] := [16711422, 15527148, 13816530, color_Black, 14869218, 12961221]; //white
OtherColors[2] := [1179390, 195836, 62965, 56797, 60909, 52428, color_Black]; //yellow
LTop := 999;
SetLength(Result, LTop);
for i := 0 to 5 do
begin
if FindColors(TPA, SearchColors[i], 14, 14, 113, 113) then
FoundColorsTPA := CombineTPA(FoundColorsTPA, TPA); //if one color is covered, we will find the other
if (i mod 2 = 1) then
begin
ATPA := TPAtoATPAEx(FoundColorsTPA, 1, 0);
Hi := High(ATPA);
for j := 0 to Hi do
begin
//if (GetColor(ATPA[j][0].x, ATPA[j][0].y+1) = color_Black) then //it is a dot
begin //begin finding the whole dot
leftDev := 0;
if (GetColor(ATPA[j][0].x-1, ATPA[j][0].y) = color_Black) then //finding the left bound of the dot
leftDev := -1
else
if (GetColor(ATPA[j][0].x-2, ATPA[j][0].y) = color_Black) then
leftDev := -2
else
if (GetColor(ATPA[j][0].x-1, ATPA[j][0].y) = 217) then
leftDev := -1;
rightDev := 0;
if (GetColor(ATPA[j][0].x+1, ATPA[j][0].y) = color_Black) then //finding the right bound of the dot
rightDev := 1
else
if (GetColor(ATPA[j][0].x+2, ATPA[j][0].y) = color_Black) then
rightDev := 2
else
if (GetColor(ATPA[j][0].x+1, ATPA[j][0].y) = 188) then
rightDev := 1;

for x := ATPA[j][0].x + leftDev to ATPA[j][0].x + rightDev do
for y := ATPA[j][0].y - 3 to ATPA[j][0].y + 1 do
begin
if ((x = ATPA[j][0].x + leftDev) and (y = ATPA[j][0].y - 3)) or //ignore 4 corners
((x = ATPA[j][0].x + leftDev) and (y = ATPA[j][0].y + 1)) or
((x = ATPA[j][0].x + rightDev) and (y = ATPA[j][0].y - 3)) or
((x = ATPA[j][0].x + rightDev) and (y = ATPA[j][0].y + 1)) then
Continue;
if InIntArray(OtherColors[i/2], GetColor(x, y)) then
begin
if (L = LTop) then
begin
LTop := LTop*2;
//SetLength(Result, LTop);
end;
FastSetPixel(Bitmap, x, y, 0); //Result[L] := Point(x, y);
Inc(L);
end;
end;
end;
end;
SetLength(FoundColorsTPA, 0);
end;
end;
//SetLength(Result, L);
//Unfreeze;
end;

//Determines whether TPoint has at least Threshold color_BlackPoints in radius 7 around it
function CTWBIsSymbolCenter (ThePoint: TPoint, Threshold: integer): boolean;
var
i, Score: integer;
begin
result := false;
//if point is near the edge of the bitmap, doing a GetColor 7 away will result in out of range
//also, we only care about circle edge points 14 pixels in from the edges, so skip it
if (ThePoint.x > 6) and (ThePoint.x < 122) and (ThePoint.y > 6) and (ThePoint.y < 122) then
exit;
Score := 0;
for i := 0 to 39 do
begin
if GetColor(ThePoint.x + CircleOffsets[i].x, ThePoint.y + CircleOffsets[i].y) = color_Black then
begin
Inc(Score);
if Score > Threshold then
begin
result := true;
exit;
end;
end;
end;
end;


procedure CTWBMakeClean(MyTile: TTile);
var
CAB: TBox; //CurrentAreaBox
Chunks, Corners, Corners2, BlackPixels, CircleCenters: TPointArray;
i, i2, counter, MainBitmap, ChunkBitmap, NewTarget, OldTarget: intger;
CircleSliceOffsets, CircleSliceWidths: array of integer;
TmpPoint: TPoint;
TmpString: String;
Skip: Boolean;
ChunksTiles, FragmentsTiles, FragmentsPixels, FragmentsPos: TBoxArray;
begin
//Get tile bounds of CurrentAreaBox
CAB := IntToBox(MyTile.x - 12, MyTile.y + 12, MyTile.x + 12, MyTile.y - 12);//Make sure this does not normalize

//CurrentAreaBox is spread over 1, 2, or 4 chunks, so these arrays are set to 4
//In these arrays, [0] corresponds to the chunk that the top left corner of CAB is in,
//[1] is top right, [2] is bottom left, [3] is bottom right. This defined in Corners
Corners := [Point(CAB.x1, CAB.y1), Point(CAB.x2, CAB.y1), Point(CAB.x1, CAB.y2), Point(CAB.x2, CAB.y2)];
SetArrayLength(Chunks, 4);
SetArrayLength(ChunksTiles, 4);
SetArrayLength(FragmentsTiles, 4);
SetArrayLength(FragmentsPixels, 4);
SetArrayLength(FragmentsPos, 4);

//Contains offsets between the center and 40 outline points of circle with raidus 7 (ie a symbol)
CircleOffsets := [Point(0, 7), Point(1, 7), Point(2, 7), Point(3, 6), Point(4, 6), Point(5, 5), Point(6, 4),
Point(6, 3), Point(7, 2), Point(7, 1), Point(7, 0), Point(7, -1), Point(7, -2), Point(6, -3), Point(6, -4),
Point(5, -5), Point(4, -6), Point(3, -6), Point(2, -7), Point(1, -7), Point(0, -7), Point(-1, -7), Point(-2, -7),
Point(-3, -6), Point(-4, -6), Point(-5, -5), Point(-6, -4), Point(-6, -3), Point(-7, -2), Point(-7, -1),
Point(-7, 0), Point(-7, 1), Point(-7, 2), Point(-6, 3), Point(-6, 4), Point(-5, 5), Point(-4, 6), Point(-3, 6),
Point(-2, 7), Point(-1, 7)];

CircleSliceOffsets := [-2, -4, -5, -6, -6, -7, -7, -7];
CircleSliceWidths := [5, 9, 11, 13, 13, 15, 15, 15];

//Using these conventions when working with tiles and pixels
//Pixels have 0,0 in the top left
//Tiles have 0,0 in the bottom left
//Chunks have 0,0 in the bottom left (the are 100x100 pixels, but they deliberately start and end on tiles so that no tile
//is spread over multiple chunks (25x25 tiles), therefore, they follow the tile convention)
//X1,Y1 is always top left, X2,Y2 is always bottom right
//For pixels, Y1 is lower than Y2. For tiles and chunks, Y2 is lower than Y1

for i:= 0 to 3) do
begin
//Find the Chunk that the corresponding corner of CAB is in
Chunks[i] := Point(((Corners[i].x-2044) div 25),((Corners[i].y-861) div 25));
//Find the tile bounds of that chunk
ChunksTiles[i] := IntToBox(2044+Chunks[i].x*25, 885+Chunks[i].y*25, 2068+Chunks[i].x*25, 861+Chunks[i].y*25);//No normalize
//These are the opposite corners of Corners
Corners2 := [Point(ChunksTiles[i].x2, ChunksTiles[i].y2), Point(ChunksTiles[i].x1, ChunksTiles[i].y2), Point(ChunksTiles
[i].x2, ChunksTiles[i].y1), Point(ChunksTiles[i].x1, ChunksTiles[i].y1)]; //yes normalize
//Find the intersection of the corresponding Corner of CAB and the opposite corner of the Chunk
//that contains the Corner of CAB. Also make X1 lower than X2, and Y1 higher than Y2 (tiles start from the bottom left)
FragmentsTiles[i] := IntToBox(Min(Corners[i].x,Corners2[i].x), Max(Corners[i].y,Corners2[i].y), Max(Corners[i].x,Corners2
[i].x), Min(Corners[i].y,Corners2[i].y);
//Find the pixel bounds (on MainBitmap) of thoses tiles
FragmentsPixels[i] := IntToBox(14+(FragmentsTiles[i].x1-(MyTile.x-12))*4, 14+((MyTile.y+12)-FragmentsTiles[i].y1)*4, 112-
((MyTile.x+12)-FragmentsTiles[i].x2)*4, 112-(FragmentsTiles[i].y2-(MyTile.y-12))*4);
//Find the pixel box in Chunk that fragment will be copied to
FragmentsPos := IntToBox((FragmentsTiles[i].x1-ChunksTiles[i].x1)*4, (ChunkTiles[i].y1-FragmentTiles[i].y1)*4,
(FragmentsTiles[i].x1-ChunksTiles[i].x1)*4 + (FragmentPixels[i].x2 - FragmentPixels[i].x1),
(ChunkTiles[i].y1-FragmentTiles[i].y1)*4 + (FragmentPixels[i].y2 - FragmentPixels[i].y1));
end;

MainBitmap := BitmapFromClient(564, 21, 691, 148,);
OldTarget := GetImageTarget;
SetTargetBitmap(MainBitmap);
NewTarget := GetImageTarget;


//0 out the dots
CTWBCollectDots(MainBitmap);

//0 out the symbols
FindColors(BlackPixels, color_Black, 0, 0, 113, 113);

for i := 0 to High(BlackPixels) do
begin

//Start-Checking if this pixel is a part of a circle we already know about
Skip := false;
for i2 := 0 to High(CircleCenters) do
begin
counter:= (BlackPixels[i].x-CircleCenters[i2].x)^2+(BlackPixels[i].y-CircleCenters[i2].y)^2)
if (counter < 54) and (counter >44) then
begin
Skip := true;
break;
end;
end;
//End-Checking if this pixel is a part of a circle we already know about

if not Skip then
begin
//Start- Hope the top 5 pixels of the symbol are uncovered
Score := 0;
if GetColor(BlackPixels[i].x+1, BlackPixels[i].y) = color_Black then
if GetColor(BlackPixels[i].x+2, BlackPixels[i].y) = color_Black then
if GetColor(BlackPixels[i].x+3, BlackPixels[i].y) = color_Black then
if GetColor(BlackPixels[i].x+4, BlackPixels[i].y) = color_Black then //pixel is top left of group horizontal 5
begin
TmpPoint := Point(BlackPixels[i].x+2, BlackPixels[i].y+7); //Center of possible circle;
if CTWBIsSymbolCenter(TmpPoint, 20) then //TmpPoint is probably the center of a symbol
begin
CircleCenters := CombineTPA(CircleCenters, [TmpPoint]);
Skip := true;
break;
end;
end;
//End- Hope the top 5 pixels of the symbol are uncovered
//Start- Bruteforce
if not Skip then
begin
for i2 := 0 to 39 do
begin
TmpPoint := (BlackPixels[i].x+CircleOffsets[i2].x,BlackPixels[i].y+CircleOffsets[i2].y);
if CTWBIsSymbolCenter(TmpPoint, 20) then
begin
CircleCenters := CombineTPA(CircleCenters, [TmpPoint]);
Skip := true;
break;
end;
end;
end;
//End- Bruteforce
end;
end;

//now 0 out the circles (and contents)
for i := 0 to High(CircleCenters) do
begin
for i2:= 0 to 7 do
for i3 := 0 to CircleSliceWidths[i2] do
begin
FastSetPixel(MainBitmap, 0, CircleCenter[i].x + CircleSliceOffsets[i2] + i3, CircleCenter[i].y-7+i2);
if i2 < 7 then
FastSetPixel(MainBitmap, 0, CircleCenter[i].x + CircleSliceOffsets[i2] + i3, CircleCenter[i].y+7-i2);
end;
end;
//end of setting pixels on MainBitmap

OldTrans := GetTransparentColor;
SetTransparentColor(MainBitmap, 0);

SetLength(CopiedChunks,1);
Counter := 0;
CopiedChunks[0] := Point(-1,-1);
for i:= 0 to 3 do
begin
Skip := false;
TmpString := 'CleanChunk'+ Padz(IntToStr(Chunks[i].x),2)+'-'+Padz(IntToStr(Chunks[i].y),2);
for i2 := 0 to counter do
begin
if ReadINI('CleanChunksDone', TmpString, CTWBPath + 'CTWBSVN\SVNCleanChunks.INI')='DONE' then
begin
Skip := true;
break;
end;
if ReadINI('CleanChunksDone', TmpString, CTWBPath + 'UserCleanChunks.INI')='DONE' then
begin
Skip := true;
break;
end;
if (Chunks[i].x = CopiedChunks[i2].x) and (Chunks[i].x = CopiedChunks[i2].x) then
begin
Skip := true;
break;
end;
end;
if not Skip then
begin
SetArrayLength(CopiedChunks, counter+1);
CopiedChunks[Counter] := Chunks[i];
Inc(Counter);
TmpString := CTWBPath + TmpString +'.bmp';
if FileExists(TmpString) then
ChunkBitmap := LoadBitmap(TmpString)
else
ChunkBitmap := CreateBitmap(100,100);
FragmentBitmap := BitmapFromClient(FragmentsPixels[i].x1, FragmentsPixels[i].y1,
FragmentsPixels[i].x2, FragmentsPixels[i].y2);
FastDrawTransparent(FragmentsPos[i].x1, FragmentsPos[i].y1, FragmentBitmap, ChunkBitmap);
FreeBitmap(FragmentBitmap);
SaveBitmap(ChunkBitmap, TmpString);
FreeBitmap(ChunkBitmap);
end;
end;
SetTransparentColor(MainBitmap, OldTrans);
SetImageTarget(OldTarget);
FreeTarget(NewTarget);
FreeBitmap(MainBitmap);
end;




{*******************************************************************************
function CTWBGetMyPos: TTile;
By: BenLand100
Description: Returns the global tile position of your player.
*******************************************************************************}
function CTWBGetMyPos: TTile;
var
me: Integer;
begin
me := SmartGetFieldObject(0, hook_static_MyPlayer);
Result.x := SmartGetFieldInt(0, hook_static_BaseX) + SmartGetFieldInt(me, hook_animable_PixelX) / 512;
Result.y := SmartGetFieldInt(0, hook_static_BaseY) + SmartGetFieldInt(me, hook_animable_PixelY) / 512;
SmartFreeObject(me);
if CTWBOn then
CTWBMakeClean(Result);
end;

Edit: alpha was a nightmare, beta is much better

program new;
{$Define SMART}
{$i srl\srl.scar}
{$i Reflection\reflection.simba}


const
CTWBOn = true; //Make true to help out CTWB
color_Black = 65536;
CTWBPath = AppPath + 'CTWB\';
//lordsaturn
function CollectDots: TPointArray;
var
SearchColors: T2DIntegerArray;
TPA, TotalTPA: TPointArray;
i, j: Integer;
begin
SetLength(SearchColors, 3);
SearchColors[0] := [3553023, 789758, 2105598, 395004, 237, 206, 65536, 217, 188]; //red
SearchColors[1] := [16711422, 15527148, 13816530, 14869218, 12961221]; //white
SearchColors[2] := [1179390, 195836, 62965, 56797, 60909, 52428]; //yellow
for i := 0 to 2 do
begin
for j := 0 to High(SearchColors[i]) do
if FindColors(TPA, SearchColors[i][j], MMX1, MMY1, MMX2, MMY2) then
TotalTPA := CombineTPA(TotalTPA, TPA);
end;

Result := TotalTPA;
end;

//lordsaturn
function CollectSymbols: TPointArray;
var
SearchTPA, InsideTPA, OutlineTPA: TPointArray;
ATPA, SymbolATPA, PossiblesATPA, SearchATPA: T2DPointArray;
i, j, k, Hi, Hi2, Hi3, lowX, highX, x, y, L, L2, highCount, counter: Integer;
mid, aPt: TPoint;
SearchBox: TBox;
BadColors: TIntegerArray;
begin
//tpa of symbol outline, length = 40, midpoint = 7,7, bounds 14x14
SearchTPA := [Point(5, 0), Point(6, 0), Point(7, 0), Point(8, 0), Point(9, 0),
{5} Point(3, 1), Point(4, 1), Point(10, 1), Point(11, 1),
{9} Point(2, 2), Point(12, 2),
{11} Point(1, 3), Point(13, 3),
{13} Point(1, 4), Point(13, 4),
{15} Point(0, 5), Point(14, 5),
{17} Point(0, 6), Point(14, 6),
{19} Point(0, 7), Point(14, 7),
{21} Point(0, 8), Point(14, 8),
{23} Point(0, 9), Point(14, 9),
{25} Point(1, 10), Point(13, 10),
{27} Point(1, 11), Point(13, 11),
{29} Point(2, 12), Point(12, 12),
{31} Point(3, 13), Point(4, 13), Point(10, 13), Point(11, 13),
{35} Point(5, 14), Point(6, 14), Point(7, 14), Point(8, 14), Point(9, 14)];
BadColors := [789758, 217, 188, 206, 16711422, 14869218, 12961221, 13816530, 195836, 56797, 52428, 52428];
FindColors(InsideTPA, 12632256, MMX1, MMY1, MMX2, MMY2);
ATPA := TPAtoATPAEx(InsideTPA, 15, 15);
InsideTPA := []; //run faster
Hi := High(ATPA);
Hi3 := 39; //High(SearchTPA);
SetLength(SymbolATPA, Hi+1);
for i := 0 to Hi do
begin
//find top left, bottom right
SearchBox := GetTPABounds(ATPA[i]);
//get symbol outline
FindColors(OutlineTPA, 65536, SearchBox.x1-1, SearchBox.y1-1, SearchBox.x2+1, SearchBox.y2+1);

//build midpoint possibilities
Hi2 := High(OutlineTPA);
L2 := 0;
SetLength(PossiblesATPA, Hi2+1);
for j := 0 to Hi2 do
begin
if not (InIntArray(BadColors, GetColor(OutlineTPA[j].x, OutlineTPA[j].y-1))) then
begin
SetLength(PossiblesATPA[L2], Hi3+1);
for k := 0 to Hi3 do
begin
PossiblesATPA[L2][k].x := SearchTPA[k].x + OutlineTPA[j].x - 7;
PossiblesATPA[L2][k].y := SearchTPA[k].y + OutlineTPA[j].y - 7;
end;
Inc(L2);
end;
end;
OutlineTPA := []; //run faster
SetLength(PossiblesATPA, L2);

//find common midpoint
Hi2 := L2-1; //because it changed
highCount := 0;
for k := 0 to Hi3 do
begin
aPt := PossiblesATPA[0][k];
counter := 0;
for j := 1 to Hi2 do
if PointInTPA(aPt, PossiblesATPA[j]) then
Inc(counter);
if counter > highCount then
begin
mid := aPt;
highCount := counter;
end;
end;
PossiblesATPA := []; //run faster

//filler up
SetLength(SymbolATPA[i], 177);
if L = 0 then
SearchATPA := TPAtoATPAEx(SearchTPA, 15, 0); //go faster
L := 0;
Hi2 := 14; //High(SearchATPA);
for j := 0 to Hi2 do
begin
lowX := mid.x + SearchATPA[j][0].x - 7;
highX := mid.x + SearchATPA[j][High(SearchATPA[j])].x - 7;
y := mid.y + SearchATPA[j][0].y - 7;
for x := lowX to highX do
begin
SymbolATPA[i][L] := Point(x, y);
Inc(L);
end;
end;

end;
Result := MergeATPA(SymbolATPA);
end;

//lordsaturn
function StuffOnMM: TPointArray;
var
timer: Integer;
begin
timer := GetSystemTime;
Result := CombineTPA(CollectDots, CollectSymbols);
//Writeln('StuffOnMM took '+tostr(GetSystemTime-timer)+' msec.');
end;

procedure CTWBMakeClean(MyTile: TPoint);
var
CAB: TBox; //CurrentAreaBox
Chunks, Corners, Corners2, CopiedChunks, DotsTPA: TPointArray;
t,i, i2, counter, MainBitmap, ChunkBitmap, FragmentBitmap, NewTarget, OldTarget: integer;
TmpPoint: TPoint;
TmpString: String;
Skip: Boolean;
ChunksTiles, FragmentsTiles, FragmentsPixels, FragmentsPos: TBoxArray;
var
SearchColors: T2DIntegerArray;
TPA, TotalTPA: TPointArray;
il, jl: Integer;
begin
t := getsystemtime;
//Get tile bounds of CurrentAreaBox
CAB := IntToBox(MyTile.x - 12, MyTile.y + 12, MyTile.x + 12, MyTile.y - 12);//Make sure this does not normalize

//CurrentAreaBox is spread over 1, 2, or 4 chunks, so these arrays are set to 4
//In these arrays, [0] corresponds to the chunk that the top left corner of CAB is in,
//[1] is top right, [2] is bottom left, [3] is bottom right. This defined in Corners
Corners := [Point(CAB.x1, CAB.y1), Point(CAB.x2, CAB.y1), Point(CAB.x1, CAB.y2), Point(CAB.x2, CAB.y2)];
SetArrayLength(Chunks, 4);
SetArrayLength(ChunksTiles, 4);
SetArrayLength(FragmentsTiles, 4);
SetArrayLength(FragmentsPixels, 4);
SetArrayLength(FragmentsPos, 4);

//Using these conventions when working with tiles and pixels
//Pixels have 0,0 in the top left
//Tiles have 0,0 in the bottom left
//Chunks have 0,0 in the bottom left (the are 100x100 pixels, but they deliberately start and end on tiles so that no tile
//is spread over multiple chunks (25x25 tiles), therefore, they follow the tile convention)
//X1,Y1 is always top left, X2,Y2 is always bottom right
//For pixels, Y1 is lower than Y2. For tiles and chunks, Y2 is lower than Y1

for i:= 0 to 3 do
begin
//Find the Chunk that the corresponding corner of CAB is in
Chunks[i] := Point(((Corners[i].x-2044) div 25),((Corners[i].y-861) div 25));
//Find the tile bounds of that chunk
ChunksTiles[i] := IntToBox(2044+Chunks[i].x*25, 885+Chunks[i].y*25, 2068+Chunks[i].x*25, 861+Chunks[i].y*25);//No normalize
//These are the opposite corners of Corners
Corners2 := [Point(ChunksTiles[i].x2, ChunksTiles[i].y2), Point(ChunksTiles[i].x1, ChunksTiles[i].y2), Point(ChunksTiles
[i].x2, ChunksTiles[i].y1), Point(ChunksTiles[i].x1, ChunksTiles[i].y1)]; //yes normalize
//Find the intersection of the corresponding Corner of CAB and the opposite corner of the Chunk
//that contains the Corner of CAB. Also make X1 lower than X2, and Y1 higher than Y2 (tiles start from the bottom left)
FragmentsTiles[i] := IntToBox(Min(Corners[i].x,Corners2[i].x), Max(Corners[i].y,Corners2[i].y), Max(Corners[i].x,Corners2
[i].x), Min(Corners[i].y, Corners2[i].y));
//Find the pixel bounds (on MainBitmap) of thoses tiles
FragmentsPixels[i] := IntToBox(14+(FragmentsTiles[i].x1-(MyTile.x-12))*4, 14+((MyTile.y+12)-FragmentsTiles[i].y1)*4, 112-
((MyTile.x+12)-FragmentsTiles[i].x2)*4, 112-(FragmentsTiles[i].y2-(MyTile.y-12))*4);
//Find the pixel box in Chunk that fragment will be copied to
FragmentsPos[i] := IntToBox((FragmentsTiles[i].x1-ChunksTiles[i].x1)*4, (ChunksTiles[i].y1-FragmentsTiles[i].y1)*4,
(FragmentsTiles[i].x1-ChunksTiles[i].x1)*4 + (FragmentsPixels[i].x2 - FragmentsPixels[i].x1),
(ChunksTiles[i].y1-FragmentsTiles[i].y1)*4 + (FragmentsPixels[i].y2 - FragmentsPixels[i].y1));
end;

DotsTPA :=StuffOnMM;

MainBitmap := BitmapFromClient(564, 21, 691, 148);
OldTarget := GetImageTarget;
SetTargetBitmap(MainBitmap);
NewTarget := GetImageTarget;

for i := 0 to High(DotsTPA) do
begin
il:=DotsTPA[i].x-564;
jl:=DotsTPA[i].y-21;
if (il > -1) and (il < 128) and (jl > -1) and (jl < 128) then
FastSetPixel(MainBitmap, il, jl, 0);
end;

for il:= mmcx-1 to mmcx+1 do
for jl:= mmcy-1 to mmcy +1 do
FastSetPixel(MainBitmap, il-564, jl-21, 0);

SetLength(CopiedChunks,1);
Counter := 0;
CopiedChunks[0] := Point(-1,-1);
for i:= 0 to 3 do
begin
Skip := false;
TmpString := 'CleanChunk'+ Padz(IntToStr(Chunks[i].x),2)+'-'+Padz(IntToStr(Chunks[i].y),2);
for i2 := 0 to High(CopiedChunks) do
begin
if ReadINI('CleanChunksDone', TmpString, CTWBPath + 'CTWBSVN\SVNCleanChunks.INI')='DONE' then
begin
Skip := true;
break;
end;
if ReadINI('CleanChunksDone', TmpString, CTWBPath + 'UserCleanChunks.INI')='DONE' then
begin
Skip := true;
break;
end;
if (Chunks[i].x = CopiedChunks[i2].x) and (Chunks[i].y = CopiedChunks[i2].y) then
begin
Skip := true;
break;
end;
end;
if not Skip then
begin
SetArrayLength(CopiedChunks, counter+1);
CopiedChunks[Counter] := Chunks[i];
Inc(Counter);
TmpString := CTWBPath + TmpString +'.bmp';
if FileExists(TmpString) then
ChunkBitmap := LoadBitmap(TmpString)
else
ChunkBitmap := CreateBitmap(100,100);
FragmentBitmap := BitmapFromClient(FragmentsPixels[i].x1, FragmentsPixels[i].y1,
FragmentsPixels[i].x2, FragmentsPixels[i].y2);
SetTransparentColor(FragmentBitmap, 0);
FastDrawTransparent(FragmentsPos[i].x1, FragmentsPos[i].y1, FragmentBitmap, ChunkBitmap);
FreeBitmap(FragmentBitmap);
SaveBitmap(ChunkBitmap, TmpString);

FreeBitmap(ChunkBitmap);
end;
end;

SetImageTarget(OldTarget);
FreeTarget(NewTarget);
FreeBitmap(MainBitmap);
end;




{*******************************************************************************
function CTWBGetMyPos: TTile;
By: BenLand100
Description: Returns the global tile position of your player.
*******************************************************************************}
function CTWBGetMyPos: TPoint;
var
me: Integer;
begin
me := SmartGetFieldObject(0, hook_static_MyPlayer);
Result.x := SmartGetFieldInt(0, hook_static_BaseX) + SmartGetFieldInt(me, hook_animable_PixelX) / 512;
Result.y := SmartGetFieldInt(0, hook_static_BaseY) + SmartGetFieldInt(me, hook_animable_PixelY) / 512;
SmartFreeObject(me);
if CTWBOn then
if R_MakeCompass('n') then
if not R_FlagExists then
CTWBMakeClean(Result);
end;



var mypoint: tpoint;

begin
Smart_Server := 152;
Smart_Members := false;
Smart_signed := true;
Smart_SuperDetail := false;

SetUpSRL;
SetUpReflection;
HowManyPlayers := 1;
CurrentPlayer := 0;
NumberOfPlayers(HowmanyPlayers);
with Players[0] do
begin
Name := '';
Pass := '';
Active := true;
end;
LoginPlayer;

mypoint:= CTWBGetMyPos;

end.