Gu3st...
2012-06-20 22:05:11
So I've messed around with a new kind of mouse moving procedures.
I tried to keep the code neat and tidy, but the code started being messy when I was 95% finished and I had to add WindMouse to the code to generate a new point, so I just didn't care about tidyness after that point..
The main idea, anyways, is to have an array of "TMouseData" which holds this information:
Fairly simple and pretty basic stuff, I know, but doing it like this makes it much easier to add stuff such as "Fitt's Law" and so on.
Here's the (messy) code I produced:
Bear in mind that I didn't use much time on this, so this is just a rough sketch of the idea, but I hope you get my point.. :p
This means that we can (in theory) click several places in one consecutive stroke with the mouse, or drag items "on the fly" (Starting position at mainscreen, start moving the mouse towards item1, start holding down the mouse at item1 and continue moving the mouse (this will be done without stopping moving the mouse at item1) towards item2, then release mouse)
I tried to keep the code neat and tidy, but the code started being messy when I was 95% finished and I had to add WindMouse to the code to generate a new point, so I just didn't care about tidyness after that point..
The main idea, anyways, is to have an array of "TMouseData" which holds this information:
- Point in space
- How long to wait before ilterating to the next point
- What buttons to hold down at this point
Fairly simple and pretty basic stuff, I know, but doing it like this makes it much easier to add stuff such as "Fitt's Law" and so on.
Here's the (messy) code I produced:
const
mouseRight = mouse_Right; // 0
mouseLeft = mouse_Left; // 1
mouseMiddle = mouse_Middle;// 2
mouseMove = 3; // 3
var
MouseSpeed : Integer;
type
TMouseData = record
P : TPoint;
Time : Integer; //0..2
ButtonDown : array [mouseRight..mouseMiddle] of Boolean;
end;
TMouseDataArray = array of TMouseData;
//I found these procedures to be kind of useless... Just kept them for historical reasons... or something..
{procedure setTime(var MDA : TMouseDataArray; const _mouseSpeed : Integer);
var
I : Integer;
begin
for I := 0 to High(MDA) do
MDA[I].Time := Round((Max(Random(Round(100/_mouseSpeed*3)), 5) * 1.0) * 1.5) + Random(Round(I div 5));
end;
procedure setButtonDown(var MDA : TMouseDataArray; const mouseIndexes : array [mouse_Right..mouse_Middle] of TIntegerArray);
var
J, I : Integer;
begin
for J := mouseRight to mouseMiddle do
for I := 0 to High(MDA) do
if InIntArray(mouseIndexes[J], I) then
MDA[I].ButtonDown[J] := True
else
MDA[I].ButtonDown[J] := False;
end;}
procedure setPath(var MDA : TMouseDataArray; const objectTPA : TPointArray; const _mouseAction, _mouseSpeed : Integer);
var
sP, mP : TPoint;
tmpTPA : TPointArray;
tmpBox : TBox;
tmpW, tmpH, I, J, H : Integer;
xs, ys, xe, ye, gravity, wind, minWait, maxWait, maxStep, targetArea: extended;
randSpeed: Extended;
//Taken from WindMouse
var
veloX, veloY, windX, windY, veloMag, dist, randomDist, lastDist, step: extended;
lastX, lastY: integer;
sqrt2, sqrt3, sqrt5: extended;
begin
GetMousePos(sP.X, sP.Y);
xs := sP.X;
ys := sP.Y;
tmpBox := GetTPABounds(tmpTPA);
tmpW := tmpBox.X2 - tmpBox.X1;
tmpH := tmpBox.Y2 - tmpBox.Y1;
tmpTPA := CopyTPA(objectTPA);
mP := MiddleTPA(tmpTPA);
SortTPAFrom(tmpTPA, sP);
mP := Point((tmpTPA[0].X + mP.X) div 2, (tmpTPA[0].Y + mP.Y) div 2);
xe := mP.X;
ye := mP.Y;
// Taken from MMouse
randSpeed:= (random(MouseSpeed) / 2.0 + MouseSpeed) / 10.0;
if randSpeed = 0.0 then randSpeed := 0.1;
gravity := 9.0;
wind := 3.0;
minWait := 10.0/randSpeed;
maxWait := 15.0/randSpeed;
maxStep := 10.0*randSpeed;
targetArea := 10.0*randSpeed;
//Taken from WindMouse
sqrt2 := sqrt(2);
sqrt3 := sqrt(3);
sqrt5 := sqrt(5);
while hypot(xs - xe, ys - ye) > 1 do
begin
//Taken from WindMouse
dist:= hypot(xs - xe, ys - ye);
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 * (xe - xs) / dist;
veloY:= veloY + gravity * (ye - ys) / dist;
if hypot(veloX, veloY) > maxStep then
begin
randomDist:= maxStep / 2.0 + random(round(maxStep) div 2);
veloMag:= sqrt(veloX * veloX + veloY * veloY);
veloX:= (veloX / veloMag) * randomDist;
veloY:= (veloY / veloMag) * randomDist;
end;
lastX:= Round(xs);
lastY:= Round(ys);
xs:= xs + veloX;
ys:= ys + veloY;
step:= hypot(xs - lastX, ys - lastY);
lastdist:= dist;
setLength(MDA, Length(MDA)+1);
I := High(MDA);
MDA[I].P := Point(Round(xs), Round(ys));
MDA[I].Time := Round((Max(Random(Round(100/_mouseSpeed*3)), 5) * 1.0) * 1.5) + Random(Round(I div 5));
end;
// This could be tidyier, but meh!!
// This part basically resets the ButtonDown attributes, then sets the ones at the end of the path to true
// So in other words; it will start clicking 2 steps before arriving at the final point.
H := High(MDA);
for I := 0 to H do
begin
for J := mouseRight to mouseMiddle do
MDA[I].ButtonDown[J] := False;
if InRange(mouseAction, mouseRight, mouseMiddle) then
if I > H-2 then // click 2 mouse steps before arriving
MDA[I].ButtonDown[mouseAction] := True;
end;
end;
// It shouldn't even function with Lape yet, but heey, why not add it in..?
{$IFDEF Lape}
procedure TMouseData.Perform();
{$ELSE}
procedure TMouseDataPerform(var MD : TMouseData);
{$ENDIF}
var
tmpI : Integer;
tmpB : Boolean;
begin
{$IFNDEF Lape}
with MD do
begin
{$ENDIF}
MoveMouse(P.X, P.Y);
for tmpI := 0 to High(ButtonDown) do
begin
IsMouseButtonDown(tmpI); // Free the buffer. (Is this still needed? I've always done it from SCAR 3.15b and upwards)
tmpB := IsMouseButtonDown(tmpI);
if ButtonDown[tmpI] then
begin
if not tmpB then // If ButtonDown says so, and it isn't down, MAKE IT SO.
HoldMouse(P.X, P.Y, tmpI);
end else
if tmpB then // Error! Mouse button down, when it shouldn't be; UN-DO :O :O
ReleaseMouse(P.X, P.Y, tmpI);
end;
Wait(Time);
{$IFNDEF Lape}
end;
{$ENDIF}
end;
procedure MouseTPA(const objectTPA : TPointArray; const _mouseAction, _mouseSpeed : Integer);
var
MDA : TMouseDataArray;
I : Integer;
begin
setPath(MDA, objectTPA, _mouseAction);
setTime(MDA, _mouseSpeed);
for I := 0 to High(MDA) do
{$IFDEF Lape}
MDA[I].Perform();
{$ELSE}
TMouseDataPerform(MDA[I]);
{$ENDIF}
if InRange(_mouseAction, mouseRight, mouseMiddle) then
ReleaseMouse(0, 0, _mouseAction); // Release the button in the end
end;
procedure MouseBox(const Box : TBox; const _mouseAction, _mouseSpeed : Integer);
begin
MouseTPA(TPAFromBox(Box), _mouseAction, _mouseSpeed);
end;
procedure Mouse(mousex, mousey, ranx, rany: Integer; button: integer);
begin
MouseTPA([Point(mousex+random(ranx), mousey+random(rany))], button, MouseSpeed);
end;
procedure MMouse(mousex, mousey, ranx, rany: Integer);
begin
MouseTPA([Point(mousex+random(ranx), mousey+random(rany))], mouseMove, MouseSpeed);
end;
begin
MouseSpeed := 13;
Mouse(0, 0, 0, 0, mouseLeft);
end.Bear in mind that I didn't use much time on this, so this is just a rough sketch of the idea, but I hope you get my point.. :p
This means that we can (in theory) click several places in one consecutive stroke with the mouse, or drag items "on the fly" (Starting position at mainscreen, start moving the mouse towards item1, start holding down the mouse at item1 and continue moving the mouse (this will be done without stopping moving the mouse at item1) towards item2, then release mouse)