[success] create AutoMove functionality

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
fcas3t
Posts: 43
Joined: Sun Nov 29, 2020 6:46 pm

[success] create AutoMove functionality

Post by fcas3t »

This week I was able to create AutoMove (a.k.a. AutoForward) functionality in Zscript.

It works similar to the AutoMove toggle key in Skyrim:

Press 'F' to automatically move forward until either
a) press 'F' again
or b) press a WASD key

Also, move at run speed when holding down the left Shift key.

I just hardcoded these keys in the code below, since I don't need a menu option.

I want to thank:
- m8f for the final piece. I wouldn't have movement without him!
- Nash for his movement mod. It provided the necessary Inventory structure.
- The GZ devs for this excellent game engine with powerful scripting capability.

Everyone has full freedom to do whatever they want with my code.
Last edited by fcas3t on Sat Dec 05, 2020 8:45 pm, edited 5 times in total.
fcas3t
Posts: 43
Joined: Sun Nov 29, 2020 6:46 pm

Re: How to create Automove (aka Autoforward) functionality?

Post by fcas3t »

edit: delete obsolete question and code
fcas3t
Posts: 43
Joined: Sun Nov 29, 2020 6:46 pm

Re: Need help creating Automove functionality

Post by fcas3t »

I've spent a number of hours this week trying to accomplish this goal. Unfortunately, after making some progress, I think I've hit a wall.

Even though a) the keyboard inputs are detected properly, b) my global-like 'thrust' var is being set properly, and c) the Tick override is applying it the way I thought would work, there just isn't any movement.

(As a last resort, I even tried moving Super.Tick() at the beginning of the function, but it made no difference.)

At the end of the code you'll see the 2 Printfs. The first one always reports 0 for the cmd.forwardmove, and the second always reports it being set properly with the h.thrust value. (And it's always the right intervals based on my button presses, though of course it's 2 printfs per tick, so the console fills fast. If you want to try it, I suggest pressing F twice in rapid succesion. Mix in presses of left Shift too for the running speed.)

These thrust values are what I found from Github web searches of the GZ repo. (For instance, see here, here and here.)

So my question now:
Is it even possible to induce player movement via Zscript?

If the answer is Yes, and you know how to do it, please respond here. I really would like this AutoMove feature; it would mean less W-keying for backtracking, looting after a fight, etc. Save some strain on my middle finger please :|

Otherwise, I guess it would mean having to modify the GZ source. That's a route I'd rather not go down.

Anyhow, here's my code. If you want to try it, just make the 2 text files and zip them up. (I'm sharing it this way because I benefitted from searchable code snippets on the forum in the process of getting as far as I did.)

mapinfo.txt

Code: Select all

GameInfo
{
    AddEventHandlers = "AutoForwardHandler"
}
zscript.zc

Code: Select all

version 4.2

class AutoForwardHandler : EventHandler
{
    int16 thrust;

    override void OnRegister()
    {
        thrust = 0;
    }

    override void WorldTick()
    {
        if ( level.time == 0 )
        {
            thrust = 0;
        }
    }

    override bool InputProcess( InputEvent e )
    {
        if ( e.Type == InputEvent.Type_KeyDown )
        {
            if ( e.KeyScan == 0x11 || e.KeyScan == 0x1F )
            {
                SendNetworkEvent( "AF_stop" );
            }
            else if ( e.KeyScan == 0x21 )
            {
                SendNetworkEvent( "AF_toggle" );
            }
            else if ( e.KeyScan == 0x2A )
            {
                SendNetworkEvent( "AF_shift_pressed" );
            }
        }
        else if ( e.Type == InputEvent.Type_KeyUp  &&  e.KeyScan == 0x2A )
        {
            SendNetworkEvent( "AF_shift_released" );
        }

        return false;
    }

    override void NetworkProcess( ConsoleEvent e )
    {
        if ( e.Name == "AF_stop" )
        {
            thrust = 0;
        }
        else if ( e.Name == "AF_toggle" )     // values from b_bot.h
        {
            thrust = thrust ? 0 : 0x1900;
        }
        else if ( e.Name == "AF_shift_pressed"  &&  thrust == 0x1900 )
        {
            thrust = 0x3200;
        }
        else if ( e.Name == "AF_shift_released"  &&  thrust == 0x3200 )
        {
            thrust = 0x1900;
        }
    }

    override void PlayerEntered( PlayerEvent e )
    {
        players[ e.PlayerNumber ].mo.A_GiveInventory( "DoAutoForward", 1 );
    }
}

class DoAutoForward : Inventory
{
    AutoForwardHandler h;

    override void Tick()
    {
        if ( owner && owner.player )
        {
            // since each level has its own handler
            h = AutoForwardHandler( EventHandler.Find("AutoForwardHandler") );

            if ( h.thrust )
            {
Console.Printf( "cmd.fm before = 0x%X", owner.player.cmd.forwardmove );

                owner.player.cmd.forwardmove = h.thrust;

Console.Printf( "h.thrust = 0x%X   cmd.fm = 0x%X", h.thrust, owner.player.cmd.forwardmove );
            }
        }

        Super.Tick();
    }
}
User avatar
m8f
 
 
Posts: 1456
Joined: Fri Dec 29, 2017 4:15 am
Preferred Pronouns: He/Him
Location: Siberia (UTC+7)

Re: Trying to create AutoMove functionality

Post by m8f »

Use PlayerPawn.ForwardThrust (double move, double angle).
User avatar
m8f
 
 
Posts: 1456
Joined: Fri Dec 29, 2017 4:15 am
Preferred Pronouns: He/Him
Location: Siberia (UTC+7)

Re: Trying to create AutoMove functionality

Post by m8f »

The simpler, though hacky solution, will be this keyconf:

Code: Select all

alias startrun "+forward; +forward; rebind stoprun"
alias stoprun  "openmenu mainmenu; closemenu; rebind startrun"

AddKeySection "Autorun" "Autorunkeys"
AddMenuKey    "Autorun" startrun
keyconf.txt
Just load this single file and assign the key in "Customize Controls" menu.
You do not have the required permissions to view the files attached to this post.
fcas3t
Posts: 43
Joined: Sun Nov 29, 2020 6:46 pm

Re: Trying to create AutoMove functionality

Post by fcas3t »

m8f wrote:Use PlayerPawn.ForwardThrust (double move, double angle).
THANK YOU!!! This was the missing piece. Now I have movement, and it works perfectly!

Here's the working code.

Note that you can change the AutoWalk and AutoRun speeds 8-)

After testing it out, I decided to stop AutoMove for any WASD key. It works more naturally this way (unlike Skyrim, which doesn't stop with the A or D keys for some reason).

mapinfo.txt

Code: Select all

GameInfo
{
    AddEventHandlers = "AutoForwardHandler"
}
zscript.zc

Code: Select all

version 4.2

class AutoForwardHandler : EventHandler
{
    double speed;

    const VEL_WALK = 1.0;

    const VEL_RUN  = 2.0;

    override void OnRegister()
    {
        speed = 0.0;
    }

    override void WorldTick()
    {
        if ( level.time == 0 )
        {
            speed = 0.0;
        }
    }

    override bool InputProcess( InputEvent e )
    {
        if ( e.Type == InputEvent.Type_KeyDown )
        {
            switch ( e.KeyScan )
            {
                case 0x11:  // WASD
                case 0x1E:
                case 0x1F:
                case 0x20:
                    SendNetworkEvent( "AF_stop" );
                    break;

                case 0x21:  // F
                    SendNetworkEvent( "AF_toggle" );
                    break;

                case 0x2A:
                    SendNetworkEvent( "AF_shift_pressed" );
            }
        }
        else if ( e.Type == InputEvent.Type_KeyUp  &&  e.KeyScan == 0x2A )
        {
            SendNetworkEvent( "AF_shift_released" );
        }

        return false;
    }

    override void NetworkProcess( ConsoleEvent e )
    {
        if ( e.Name == "AF_stop" )
        {
            speed = 0.0;
        }
        else if ( e.Name == "AF_toggle" )
        {
            speed = ( speed == 0.0 ) ? VEL_WALK : 0.0;
        }
        else if ( e.Name == "AF_shift_pressed"  &&  speed == VEL_WALK )
        {
            speed = VEL_RUN;
        }
        else if ( e.Name == "AF_shift_released"  &&  speed == VEL_RUN )
        {
            speed = VEL_WALK;
        }
    }

    override void PlayerEntered( PlayerEvent e )
    {
        players[ e.PlayerNumber ].mo.A_GiveInventory( "DoAutoForward", 1 );
    }
}

class DoAutoForward : Inventory
{
    AutoForwardHandler h;

    override void Tick()
    {
        if ( Owner && Owner is "PlayerPawn" )
        {
            // since each level has its own handler
            h = AutoForwardHandler( EventHandler.Find("AutoForwardHandler") );

            if ( h.speed > 0.0 )
            {
                /* This works perfectly with mouselook. The mouse
                   controls the angle, so use the dummy value here.
                 */
                PlayerPawn( Owner ).ForwardThrust( h.speed, 1e37 );
            }
        }

        Super.Tick();
    }
}
User avatar
Dan_The_Noob
Posts: 880
Joined: Tue May 07, 2019 12:24 pm
Graphics Processor: nVidia with Vulkan support

Re: [success!] create AutoMove functionality

Post by Dan_The_Noob »

all this work, couldn't you just map forward and sprint to one button and make it a toggle?
fcas3t
Posts: 43
Joined: Sun Nov 29, 2020 6:46 pm

Re: [success] create AutoMove functionality

Post by fcas3t »

Important Update

Turns out the solution has a few deficiencies, but thankfully I've figured out the best possible solution.

The problem with yesterday's solution (for which I am grateful; I was elated to finally see movement!) is that it bypasses the adjustments made in PlayerPawn.MovePlayer() when the player is airborne, walking down stairs, and other special considerations. So when I was playing earlier today, I was getting jarring doses of acceleration during AutoMove when jumping or walking down stairs.

Taking a fresh look at the GZ code revealed the solution, which is to invoke the function I just mentioned after setting cmd.forwardmove. So, in a roundabout way, I now have the first version of the code working. And it works beautifully. Automatic movement is now just like manual in every way, since the mod does the same thing the game normally does.

I'm not going to post another version of the full code. Instead, here's a patch for the first version of the code above:

Code: Select all

            if ( h.thrust )
            {
                Owner.player.cmd.forwardmove = h.thrust;

                PlayerPawn( Owner ).MovePlayer();
            }
In other words, replace the Printfs section with this new code.

I also recommend making constants like I did in the second version of the code, plus the full WASD detection in the second version.

But I leave all of that as an exercise to anyone interested in using this code.

(And to acknowledge the other item in this thread, the custom keyboard binds that m8f graciously posted and Dan asked about, I don't know anything about it and have no interest. I prefer AutoMove to be a slower speed than manual movement, and only my code can do that.)

Return to “Scripting”