DoctorGibbs' Perl Toolkit for Interacting with the There World

Part 4. Program Your Avatar to Walk, Turn, Run

So far, we've sent keystrokes to the There window, but not timed keystrokes -- the difference being whether the exact times between key-down and key-up are significant. Now, let's send some timed keystrokes to the up, down, left and right arrow keys, causing our avatar to walk, turn, or run.

Be sure that you've followed the directions in Part 1 and invoked Win32::API, as well as having defined $key and $sleep. Now remembering hex keycodes is not nice, so put also some definitions near the top of your script

$arrowcodes{"L"} = hex("0x25");
$arrowcodes{"U"} = hex("0x26");
$arrowcodes{"R"} = hex("0x27");
$arrowcodes{"D"} = hex("0x28");
Here is the basic subroutine for pressing an arrow key for a specified time, then releasing it and (optionally) doing nothing for some additional time. All times are in milliseconds.
sub SendTimedArrow {
    my ($code,$time,$sleepfor) = @_;
    $key->Call($arrowcodes{$code}, 0x45, 0x01, 0);
    $sleep->Call($time);
    $key->Call($arrowcodes{$code}, 0x45, 0x03, 0);
    $sleep->Call($sleepfor);
}
We're going to assume that you've already set the window focus to the There window (see code example at the end of this part).

To step forward some number of steps, call this subroutine

sub StepAvatar {
    my $steps = shift;
    &SendTimedArrow("U",int(475*$steps),400);
}
Of course this is only an approximation, because one step does not always take exactly 475 milliseconds -- it depends on the terrain, and may even depend a bit on the previous state of your avatar. Play around with the constant if you are not getting the results you want.

A backwards step seems to take about 680 milliseconds, so we have

sub StepBackAvatar {
    my $steps = shift;
    &SendTimedArrow("D",int(680*$steps),400);
}
Turning left and right is even more of an approximation, since turns seem to have long "tails" to them in time. I find that 615 milliseconds approximates a 90 degree turn to the left or right.
sub TurnLeftAvatar {
    &SendTimedArrow("L",615,400);
}

sub TurnRightAvatar {
    &SendTimedArrow("R",615,400);
}
In the section on autopilots, you will learn how to make your avatar turn by exactly some amount (or to some exact heading) by sensing your heading and correcting within a feedback control loop.

Finally, running. Here we need a sequence of up-arrow key clicks, two 100 millisecond clicks to get into run mode, and then a long key-press for the number of steps. As before these are only approximations. (For some reason, I coded these with the elementary calls to Win32::API instead of using our SendTimedArrow() subroutine. I forget why!)

sub RunAvatar {
    my $steps = shift;
    $key->Call(0x26, 0x45, 0x01, 0);
    $sleep->Call(100);
    $key->Call(0x26, 0x45, 0x03, 0);
    $sleep->Call(100);
    $key->Call(0x26, 0x45, 0x01, 0);
    $sleep->Call(100);
    $key->Call(0x26, 0x45, 0x03, 0);
    $sleep->Call(100);
    $key->Call(0x26, 0x45, 0x01, 0);
    $sleep->Call(312*$steps);
    $key->Call(0x26, 0x45, 0x03, 0);
    $sleep->Call(500);
}
If you think you might have cruise control on, then you'll want to follow any call to RunAvatar (or any call to StepAvatar with a large number of steps) by a quick press of the down-arrow:
&SendTimedArrow("D",100,100);
Here is a code fragment, using the above subroutines, that steps your avatar forward 12 paces, turns left, runs 50 paces, stops for 3 seconds, steps back 5 paces, and turns right.
Win32::GuiTest::SetForegroundWindow(&GetThereWindow());
&StepAvatar(12);
&SendTimedArrow("D",100,100);
&TurnLeftAvatar();
&RunAvatar(50);
&SendTimedArrow("D",100,3000);
&StepBackAvatar(5);
&SendTimedArrow("D",100,100);
&TurnRightAvatar();
Next: Part 5