I made this since a few people were talking about it. This little script can record all of your mouse clicks and save it to a file. This is meant for recording yourself play and will work with any window (such as SMART, the official client, or whatever). You can also load the data from the saved file and generate some averages too.
It is easiest to use it as an included file like so:
Simba Code:
program new;
{$DEFINE SMART}
{$i srl-6/srl.simba}
{$i mouserecorder.simba} // Throw it in your includes folder
begin
setupSRL();
if recorder.setTargetWindow() then // Sets the target to SMART by default
begin
recorder.setDebug(True); // To print each click as you make it
while not isKeyDown(VK_RETURN) do // Will log data until you press enter
recorder.recordMouse();
recorder.clicks.write(); // Debug all click parameters
recorder.clicks.averages(); // Print out average stats
recorder.clicks.saveToFile(); // Saves to a text file (ScriptPath + 'MouseData.txt' by default)
end;
end.
The output of this would look like the following:
Code:
{CLICKTYPE = 1, STARTX = 684, ENDX = 686, STARTY = 327, ENDY = 301, ISDRAG = True, STARTTIME = 166556375, DURATION = 78}
{CLICKTYPE = 1, STARTX = 609, ENDX = 512, STARTY = 296, ENDY = 333, ISDRAG = True, STARTTIME = 166556531, DURATION = 78}
{CLICKTYPE = 1, STARTX = 529, ENDX = 515, STARTY = 372, ENDY = 376, ISDRAG = True, STARTTIME = 166556687, DURATION = 63}
{CLICKTYPE = 1, STARTX = 431, ENDX = 431, STARTY = 423, ENDY = 423, ISDRAG = False, STARTTIME = 166556968, DURATION = 63}
{CLICKTYPE = 0, STARTX = 426, ENDX = 427, STARTY = 477, ENDY = 477, ISDRAG = True, STARTTIME = 166557218, DURATION = 63}
{CLICKTYPE = 0, STARTX = 407, ENDX = 407, STARTY = 356, ENDY = 356, ISDRAG = False, STARTTIME = 166558218, DURATION = 78}
{CLICKTYPE = 0, STARTX = 387, ENDX = 358, STARTY = 367, ENDY = 422, ISDRAG = True, STARTTIME = 166558390, DURATION = 78}
{CLICKTYPE = 0, STARTX = 349, ENDX = 352, STARTY = 522, ENDY = 526, ISDRAG = True, STARTTIME = 166558562, DURATION = 63}
---------------------------------------
- Mean left click duration: 75.45 -
- Mean right click duration: 80.11 -
- Mean total click duration: 76.81 -
- Drag clicks (% of total): 80.65 -
---------------------------------------
Here is an example of loading the clicks from a file, and displaying some averages:
Simba Code:
program new;
{$i mouserecorder.simba}
begin
recorder.clicks.loadFromFile(); // Loads from 'ScriptPath + MouseData.txt' by default)
recorder.clicks.write();
recorder.clicks.averages();
end.
I also threw in some painting stuff for generating a heatmap of the recorded clicks. Here is an example of using the official runescape client, and then drawing a heatmap on a new canvas after you're finished recording:
Simba Code:
program new;
{$i mouserecorder.simba}
begin
if recorder.setTargetWindow('RuneScape') then // Title of the official client
begin
while not isKeyDown(VK_RETURN) do // Will log data until you press enter
recorder.recordMouse();
recorder.canvas.init(); // Initialize the canvas grid
recorder.canvas.draw(recorder.clicks); // Create a heatmap of the clicks
end;
end.
An example of me mashing the mouse on the login screen would look something like this:
You can also change some of the drawing parameters, like so:
Simba Code:
recorder.canvas.init(2); // 2 means 2x2px grid (default is 4)
recorder.canvas.draw(recorder.clicks, MOUSE_RIGHT); // Only draw right clicks on the map
I am not maintaining this or improving it, so free free to do what you like with it. Here is the actual mouserecorder.simba file
Simba Code:
{$include_once srl-6/lib/utilities/math.simba}
type
TMapCell = record
count, cpm: SmallInt;
color: Integer;
box: TBox;
end;
TMapCanvas = array of TMapCell;
TMouseParam = record
clickType: TClickType;
startX, endX, startY, endY: Integer;
isDrag: Boolean;
startTime: Integer;
duration: SmallInt;
end;
TMouseParams = array of TMouseParam;
TMouseRecorder = record
clicks: TMouseParams;
canvas: TMapCanvas;
debug: Boolean;
end;
const
MOUSE_ALL = -2;
var
recorder: TMouseRecorder;
procedure TMouseRecorder.setDebug(Enabled: Boolean);
begin
Self.debug := Enabled;
end;
function TMouseRecorder.setTargetWindow(TargetWindow: String = 'SMARTv8'): Boolean;
var
processes: TSysProcArr;
i: Integer;
begin
processes := Client.GetIOManager().GetProcesses();
for i := 0 to High(processes) do
if pos(TargetWindow, processes[i].Title) then
begin
SetTarget(processes[i]);
WriteLn('TMouseParams: Target window ' + TargetWindow + ' is set.');
Exit(True);
end;
WriteLn('TMouseParams: Failed to find the target window: ' + TargetWindow);
Result := False;
end;
procedure TMouseRecorder.recordMouse();
begin
if System.IsMouseButtonDown(0) or System.IsMouseButtonDown(1) then
begin
SetLength(Self.clicks, Length(Self.clicks) + 1);
System.GetMousePos(Self.clicks[High(Self.clicks)].startX, Self.clicks[High(Self.clicks)].startY);
Self.clicks[High(Self.clicks)].startTime := GetSystemTime();
if System.IsMouseButtonDown(1) then
Self.clicks[High(Self.clicks)].clickType := MOUSE_LEFT
else
Self.clicks[High(Self.clicks)].clickType := MOUSE_RIGHT;
while System.IsMouseButtonDown(0) or System.IsMouseButtonDown(1) do
Sleep(1);
Self.clicks[High(Self.clicks)].duration := GetSystemTime() - Self.clicks[High(Self.clicks)].startTime;
System.GetMousePos(Self.clicks[High(Self.clicks)].endX, Self.clicks[High(Self.clicks)].endY);
if (Self.clicks[High(Self.clicks)].startX <> Self.clicks[High(Self.clicks)].endX) or
(Self.clicks[High(Self.clicks)].startY <> Self.clicks[High(Self.clicks)].endY) then
Self.clicks[High(Self.clicks)].isDrag := True;
if Self.clicks[High(Self.clicks)].duration = 0 then
SetLength(Self.clicks, High(Self.clicks))
else if Self.debug then
WriteLn(Self.clicks[High(Self.clicks)]);
end else
Sleep(3);
end;
procedure TMouseParams.saveToFile(Path: String = ScriptPath + 'mouseData.txt');
var
newfile, i: Integer;
begin
newfile := RewriteFile(Path, false);
for i := 0 to High(Self) do
WriteFileString(newfile, '[' + toStr(i) + ']' + toStr(recorder.clicks[i])+#13#10);
WriteFileString(newfile, '[');
WriteLn('TMouseParams: Saved ' + toStr(Length(Self)) + ' clicks to file: ' + path);
CloseFile(newfile);
end;
procedure TMouseParams.loadFromFile(Path: String = ScriptPath + 'mouseData.txt');
var
mouseData, i: Integer;
str: String;
TSA: TStringArray;
begin
mouseData := OpenFile(Path, false);
ReadFileString(mouseData, str, FileSize(mouseData));
TSA := MultiBetween(str, ']', '[');
SetLength(Self, Length(TSA));
for i := 0 to High(TSA) do
begin
Self[i].clickType := StrToInt(Between('CLICKTYPE = ', ',', TSA[i]));
Self[i].startX := StrToInt(Between('STARTX = ', ',', TSA[i]));
Self[i].endX := StrToInt(Between('ENDX = ', ',', TSA[i]));
Self[i].startY := StrToInt(Between('STARTY = ', ',', TSA[i]));
Self[i].endY := StrToInt(Between('ENDY = ', ',', TSA[i]));
Self[i].isDrag := StrToBool(Between('ISDRAG = ', ',', TSA[i]));
Self[i].startTime := StrToInt(Between('STARTTIME = ', ',', TSA[i]));
Self[i].duration := StrToInt(Between('DURATION = ', '}', TSA[i]));
end;
WriteLn('TMouseParams: Loaded ' + toStr(Length(TSA)) + ' clicks from file: ' + path);
CloseFile(mouseData);
end;
procedure TMouseParams.write();
var
i: Integer;
begin
for i := 0 to High(Self) do WriteLn(Self[i]);
end;
procedure TMouseParams.averages();
var
i: Integer;
avg, avgL, avgR, dragP: Extended;
cntL: Integer = 1;
cntR: Integer = 1;
begin
if Length(Self) < 1 then
begin
WriteLn('TMouseParams: Cannot generate avarages; No mouse data');
Exit;
end;
for i := 0 to High(Self) do
begin
if Self[i].isDrag then dragP += 1;
if Self[i].clickType = MOUSE_LEFT then
begin
cntL += 1;
avgL += Self[i].duration;
end;
if Self[i].clickType = MOUSE_RIGHT then
begin
cntR += 1;
avgR += Self[i].duration;
end;
end;
avg := avgL + avgR;
avg /= Length(Self);
avgL /= cntL;
avgR /= cntR;
dragP := (dragP / Length(Self)) * 100;
WriteLn('---------------------------------------');
WriteLn(PadR('- Mean left click duration: ', 32) + toStr(Round(avgL, 2)) + ' -');
WriteLn(PadR('- Mean right click duration: ', 32) + toStr(Round(avgR, 2)) + ' -');
WriteLn(PadR('- Mean total click duration: ', 32) + toStr(Round(avg, 2)) + ' -');
WriteLn(PadR('- Drag clicks (% of total): ', 32) + toStr(Round(dragP, 2)) + ' -');
WriteLn('---------------------------------------');
end;
function TMouseParams.getClickPoints(ClickType: Integer = MOUSE_ALL; DragOnly: Boolean = False): TPointArray;
var
i, j, width, height: Integer;
p: TPoint;
begin
GetClientDimensions(width, height);
for i := 0 to High(Self) do
if (Self[i].clickType = ClickType) or (ClickType = MOUSE_ALL) then
begin
p := Point(Self[i].startX, Self[i].startY);
if InRange(p.x, 0, width - 1) and InRange(p.y, 0, height - 1) then
Insert(p, Result, Length(Result));
end;
end;
procedure TMapCanvas.calcCellDensity(MouseData: TMouseParams; ClickType: Integer = MOUSE_ALL);
var
i, j, duration: Integer;
TPA: TpointArray;
begin
TPA := MouseData.getClickPoints(ClickType);
duration := MouseData[High(MouseData)].startTime - MouseData[Low(MouseData)].startTime;
for i := 0 to High(TPA) do
for j := 0 to High(Self) do
if PointInBox(TPA[i], Self[j].box) then
Self[j].count += 1;
for i := 0 to High(Self) do
Self[i].cpm := round(Self[i].count / (duration / 60000));
end;
procedure TMapCanvas.getCellColors();
var
i: Integer;
begin
if Length(Self) then
for i := 0 to High(Self) do
case Self[i].cpm of
1..3: Self[i].color := 14145535;
4..6: Self[i].color := 11579647;
7..9: Self[i].color := 9540095;
10..12: Self[i].color := 7303167;
13..15: Self[i].color := 5987327;
16..18: Self[i].color := 5197823;
19..21: Self[i].color := 4210943;
22..24: Self[i].color := 2960895;
25..27: Self[i].color := 1245437;
28..maxInt: Self[i].color := 255;
else
Self[i].color := 0;
end;
end;
procedure TMapCanvas.init(CellSize: Integer = 4);
var
i, width, height: Integer;
TBA: TBoxArray;
begin
WriteLn('TMapCanvas: Initializing the canvas with CellSize = ' + toStr(CellSize));
GetClientDimensions(width, height);
TBA := grid(round((width div CellSize)-1), round((height div CellSize)-1), CellSize, CellSize, CellSize, CellSize, Point(CellSize, CellSize));
SetLength(Self, Length(TBA));
for i := 0 to High(TBA) do Self[i].box := TBA[i];
end;
procedure TMapCanvas.draw(MouseData: TMouseParams; ClickType: Integer = MOUSE_ALL);
var
i, width, height: Integer;
bmp: Integer;
begin
WriteLn('TMapCanvas: Generating heat map');
if Length(MouseData) > 1 then
begin
GetClientDimensions(width, height);
bmp := CreateBitmap(width, height);
GetMufasaBitmap(bmp).CopyClientToBitmap(Client.GetIOManager(), False, 0, 0, width, height);
Self.calcCellDensity(MouseData, ClickType);
Self.getCellColors();
for i := 0 to High(Self) do
if Self[i].color <> 0 then
GetMufasaBitmap(bmp).Rectangle(Self[i].box, Self[i].color);
ClearDebugImg();
DisplayDebugImgWindow(width, height);
DrawBitmapDebugImg(GetMufasaBitmap(bmp).GetIndex());
FreeBitmap(bmp);
end else
WriteLn('TMapCanvas: Cannot generate map; less than 2 data points.');
end;