I managed to finish this up tonight, and I can honestly say I'm proud at how well it's worked so far. I've automatically generated paths from VE bank to the mine and VE bank to the trees, and so far, they work perfectly.
If anyone can think of a cool name to call this utility, I will love you forever.
This is a scripting utility that automatically generates color walking points based off of reflection tiles. It uses the type that was created from
. The user simply loads a reflection path (with both endpoints included) and calls the proper procedures.
All forms of feedback is appreciated. If you have any issues, don't be afraid to ask.
If you're unsure about something, read the comments, I did comment quite a bit.
If you'd like to test the two paths I've generated, download the attached text file to your includes folder and run the commented ColorWalks in the mainloop.
Simba Code:
program AutoGenColorWalking;
{$i srl/srl/misc/smart.scar}
{$i srl/srl.scar}
{$i reflection/reflection.simba}
const
MOUSE_TOL_X = 0;
MOUSE_TOL_Y = 0;
PATH_RECORD_WALKPOINT = IncludePath + 'WalkPoints.txt';
PATH_NAME = 'VE Trees to Bank ';
type
TWalkPoint = record
Name : string;
Color : Integer;
Tolerance : Integer;
SortFrom : TPoint;
RWalkParams : TIntegerArray;
end;
TWalkPointArray = array of TWalkPoint;
var
gbl_WalkPoints: array of TWalkPoint;
gbl_TilePath: TPointArray;
procedure DeclareTilePath();
begin
{
// VE Bank to Mine
gbl_TilePath := [
Point(3254, 3421), Point(3265, 3429),
Point(3279, 3429), Point(3288, 3418),
Point(3292, 3406), Point(3292, 3393),
Point(3294, 3381), Point(3288, 3368)
];
}
// VE Bank to Trees
gbl_TilePath := [
Point(3253, 3421), Point(3265, 3428),
Point(3275, 3429), Point(3280, 3439),
Point(3276, 3450)
];
end;
// Converts a string to an integer array; for example, StrToIntArr([1, 2, 3, 4]) will result [1, 2, 3, 4]
function StrToIntArr(Str: string): TIntegerArray;
var
i: Integer;
s: TStringArray;
begin
if (Str = '') then
Exit;
s := Explode(', ', Str);
for i := 0 to High(s) do
try
SetLength(Result, Length(Result) + 1);
Result[High(Result)] := StrToInt(s[i]);
except
end;
end;
function IntArrToStr(Int: TIntegerArray): string;
var
i: Integer;
begin
if (Length(Int) <= 0) then
begin
Result := '';
Exit;
end;
for i := 0 to High(Int) do
if (i = High(Int)) then
Result := Result + IntToStr(Int[i])
else
Result := Result + IntToStr(Int[i]) + ', ';
end;
procedure ClearFile(FilePath: string);
var
f: Integer;
begin
try
f := RewriteFile(FilePath, False);
CloseFile(f);
except
end;
end;
// Loads all the walk points from the WalkPoints.txt file
procedure LoadAllWalkPoints();
var
i: Integer;
begin
if (not FileExists(PATH_RECORD_WALKPOINT)) then
Exit;
SetLength(gbl_WalkPoints, StrToInt(ReadINI('POINT_COUNT', 'PointCount', PATH_RECORD_WALKPOINT)));
for i := 0 to High(gbl_WalkPoints) do
with gbl_WalkPoints[i] do
begin
Name := ReadINI(IntToStr(i), 'Name', PATH_RECORD_WALKPOINT);
Color := StrToInt(ReadINI(IntToStr(i), 'Color', PATH_RECORD_WALKPOINT));
Tolerance := StrToInt(ReadINI(IntToStr(i), 'Tolerance', PATH_RECORD_WALKPOINT));
SortFrom.x := StrToInt(ReadINI(IntToStr(i), 'SortFrom.x', PATH_RECORD_WALKPOINT));
SortFrom.y := StrToInt(ReadINI(IntToStr(i), 'SortFrom.y', PATH_RECORD_WALKPOINT));
RWalkParams := StrToIntArr(ReadINI(IntToStr(i), 'RWalkParams', PATH_RECORD_WALKPOINT));
end;
end;
// Saves all the points to a file
procedure SaveAllWalkPoints();
var
i: Integer;
begin
ClearFile(PATH_RECORD_WALKPOINT);
WriteINI('POINT_COUNT', 'PointCount', IntToStr(Length(gbl_WalkPoints)), PATH_RECORD_WALKPOINT);
for i := 0 to High(gbl_WalkPoints) do
with gbl_WalkPoints[i] do
begin
WriteINI(IntToStr(i), 'Name', Name, PATH_RECORD_WALKPOINT);
WriteINI(IntToStr(i), 'Color', IntToStr(Color), PATH_RECORD_WALKPOINT);
WriteINI(IntToStr(i), 'Tolerance', IntToStr(Tolerance), PATH_RECORD_WALKPOINT);
WriteINI(IntToStr(i), 'SortFrom.x', IntToStr(SortFrom.x), PATH_RECORD_WALKPOINT);
WriteINI(IntToStr(i), 'SortFrom.y', IntToStr(SortFrom.y), PATH_RECORD_WALKPOINT);
WriteINI(IntToStr(i), 'RWalkParams', IntArrToStr(RWalkParams), PATH_RECORD_WALKPOINT);
end;
end;
// Works like DeleteValueInIntArr, except for a TWalkPointArray
// Used to delete the first point in a reflection path as that's where your player is
procedure RemoveIndex(var Arr: TWalkPointArray; Index: Integer);
var
arrLen, i: Integer;
begin
arrLen := High(Arr);
for i := Index to (arrLen - 1) do
Swap(Arr[i], Arr[i + 1]);
SetLength(Arr, arrLen);
end;
// Simple wait for the flag and yes it's supposed to wait until it's completely gone
// it ensures the walk point data is colleted accurately
procedure WaitFlag();
begin
while (WaitFunc(@CharacterMoving, 50, 1500)) do
begin
Wait(100 + Random(50));
if (not FlagPresent) then
Break;
end;
Wait(1000 + Random(500));
end;
// Returns the quadrant the point 'p' is in on the minimap
function GetQuadrant(p: TPoint): Integer;
var
i : Integer;
quadrants: array[1..4] of TBox;
begin
// Initiate the 4 differen quadrants
quadrants[1] := IntToBox(MMCX, MMY1, MMX2, MMCY); // Top Right
quadrants[2] := IntToBox(MMX1, MMY1, MMCX, MMCY); // Top Left
quadrants[3] := IntToBox(MMX1, MMCY, MMCX, MMY2); // Bottom Left
quadrants[4] := IntToBox(MMCX, MMCY, MMX2, MMY2); // Bottom Right
// Get the quadrant the point 'p' is in; set it as 'quad'
for i := 1 to 4 do
if (PointInBox(p, quadrants[i])) then
begin
Result := i;
Break;
end;
end;
// Gets the radial for any point 'p' on the minimap using Cosine Law
function GetRadial(p: TPoint): Integer;
var
sides: array[1..3] of Integer;
radCos: Extended;
begin
sides[1] := 30;
sides[2] := Distance(MMCX, MMCY, p.x, p.y);
sides[3] := Distance(MMCX, MMCY - sides[1], p.x, p.y);
radCos := (Sqr(sides[1]) + Sqr(sides[2]) - Sqr(sides[3])) / (2 * sides[1] * sides[2]);
Result := Round(Degrees(ArcCos(radCos)));
case GetQuadrant(p) of
2, 3: Result := 360 - Result;
1, 4: Result := Result;
end;
Writeln('Radial: ' + IntToStr(Result) + ' degrees');
end;
// Creates the actual TWalkPoint basied on the tile 'Tile'
function GenerateWalkPoint(Tile: TPoint): TWalkPoint;
var
mm_Point: TPoint;
mm_Radial: Integer;
begin
mm_Point := TileToMM(Tile);
if (not rs_OnMinimap(mm_Point.x, mm_Point.y)) then
begin
Writeln('GenerateWalkPoint: Point not on minimap');
Exit;
end;
mm_Radial := GetRadial(Point(mm_Point.x, mm_Point.y));
with Result do
begin
Name := PATH_NAME;
Color := GetColor(mm_Point.x, mm_Point.y);
Tolerance := 20;
SortFrom := Point(mm_Point.x, mm_Point.y);
RWalkParams := [
Color, // Color
mm_Radial - 10, // Start Radial
mm_Radial + 10, // End Radial
Distance(MMCX, MMCY, mm_Point.x, mm_Point.y) + 10, // Radius
RandomRange(-2, 2), // Mod X
RandomRange(-2, 2), // Mod Y
10 // Tolerance
];
// If the start radial is less than 0
if (RWalkParams[1] < 0) then
RWalkParams[1] := 360 + RWalkParams[1];
// If the end radial is greater than 360
if (RWalkParams[2] > 360) then
RWalkParams[2] := RWalkParams[2] - 360;
end;
Writeln('GenerateWalkPoint: Point Generated!');
end;
// Generates a TWalkPointArray based on the reflection path (Tiles)
function GeneratePath(Tiles: TPointArray): TWalkPointArray;
var
i: Integer;
mm_Point: TPoint;
begin
SetLength(Result, Length(Tiles));
for i := 1 to High(Tiles) do
begin
Result[i] := GenerateWalkPoint(Tiles[i]);
Result[i].Name := Result[i].Name + IntToStr(i);
mm_Point := TileToMM(Tiles[i]);
Mouse(mm_Point.x, mm_Point.y, MOUSE_TOL_X, MOUSE_TOL_Y, True);
WaitFlag();
end;
RemoveIndex(Result, 0);
end;
// Prints the path to the debug box
procedure PrintPath(WalkPoints: TWalkPointArray);
var
i: Integer;
begin
Writeln('case Index of');
for i := 0 to High(WalkPoints) do
with WalkPoints[i] do
begin
Writeln(' ' + IntToStr(i) + ':');
Writeln(' with gbl_WalkPoints[Index] do');
Writeln(' begin');
Writeln(' Name := ' + Name + ''''';');
Writeln(' Color := ' + IntToStr(Color) + ';');
Writeln(' Tolerance := ' + IntToStr(Tolerance) + ';');
Writeln(' SortFrom := Point(' + IntToStr(SortFrom.x) + ', ' + IntToStr(SortFrom.y) + ');');
Writeln(' RWalkParams := ' + ToStr(RWalkParams) + ';');
Writeln(' end;');
Writeln('');
end;
Writeln('end;');
end;
// Camaro's TPA function
function GetWalkCoords(WalkPoint: TWalkPoint): TPoint;
var
i, x, y: Integer;
TPA: TPointArray;
begin
Result := Point(-1, -1);
with WalkPoint do
begin
FindColorsSpiralTolerance(MMCX, MMCY, TPA, Color, MMX1, MMY1, MMX2, MMY2, Tolerance);
SortTPAFrom(TPA, SortFrom);
for i := 0 to High(TPA) do
begin
x := TPA[i].x;
y := TPA[i].y;
if (not rs_OnMinimap(x, y)) then
Continue;
Result := Point(x, y);
Break;
end;
end;
end;
// Walks from StartIndex to EndIndex
function ColorWalk(StartIndex, EndIndex: Integer): Boolean;
var
i: Integer;
mm_Point: TPoint;
begin
for i := StartIndex to EndIndex do
begin
mm_Point := GetWalkCoords(gbl_WalkPoints[i]);
if ((mm_Point.x <> -1) and (mm_Point.y <> -1)) then
begin
Mouse(mm_Point.x, mm_Point.y, 3, 3, True);
WaitFlag;
Result := (i = EndIndex);
end else begin
Writeln('Failed to walk to point: ' + IntToStr(i));
if (RadialWalkTolerance(gbl_WalkPoints[i].RWalkParams[0],
gbl_WalkPoints[i].RWalkParams[1],
gbl_WalkPoints[i].RWalkParams[2],
gbl_WalkPoints[i].RWalkParams[3],
gbl_WalkPoints[i].RWalkParams[4],
gbl_WalkPoints[i].RWalkParams[5],
gbl_WalkPoints[i].RWalkParams[6])) then
Result := (i = EndIndex);
end;
end;
end;
// Reverses the TPA so the same reflection path can be used to generate both color paths
// Taken straight from MSI
function MirrorTPA(TPA: TPointArray): TPointArray;
var
i, h: integer;
begin
h := 0;
SetLength(Result, Length(TPA));
for i:= High(TPA) DownTo 0 do
begin
Result[h] := TPA[i];
Inc(h);
end;
end;
// Adds 'Points' to the global array gbl_WalkPoints
procedure AddToGlobal(Points: TWalkPointArray);
var
i: Integer;
begin
for i := 0 to High(Points) do
begin
SetLength(gbl_WalkPoints, Length(gbl_WalkPoints) + 1);
gbl_WalkPoints[High(gbl_WalkPoints)] := Points[i];
end;
end;
// Generates and prints the path/reverse path of the relfection tiles
procedure CompletePath();
begin
AddToGlobal(GeneratePath(gbl_TilePath));
AddToGlobal(GeneratePath(MirrorTPA(gbl_TilePath)));
PrintPath(gbl_WalkPoints);
SaveAllWalkPoints();
end;
begin
Smart_Server := 152;
Smart_Members := False;
Smart_Signed := True;
Smart_SuperDetail := False;
ActivateClient;
SetupSRL;
SetupReflection;
ClearDebug;
DeclareTilePath();
LoadAllWalkPoints();
CompletePath();
//ColorWalk(0, 3); // VE Bank to Trees
//ColorWalk(4, 7); // VE Trees to Bank
end.