mwilsnd / SkyrimSE-SmoothCam

A thirdperson camera mod for Skyrim Special Edition
79 stars 19 forks source link

How you manage to toggle the crosshair on/off in game? #28

Closed max-su-2019 closed 3 years ago

max-su-2019 commented 3 years ago

This's not a issue, just a question.

I really want to know what method you are using to toggle the crosshair on and off just in time.

Do you achieve that by simply move the position of the crosshair out of the screen, or you actually be able to disable it just likes toggle off the option in the system menu crosshair setting in the game?

Thanks!

mwilsnd commented 3 years ago

Its simple enough, the HUD menu has a callback that lets you toggle it.

auto menu = MenuManager::GetSingleton()->GetMenu(&UIStringHolder::GetSingleton()->hudMenu);
if (menu && menu->view) {
    GFxValue result;
    GFxValue args[2];
    args[0].SetString("SetCrosshairEnabled");
    args[1].SetBool(enabled);
    menu->view->Invoke("call", &result, args, 2);
}

You can also directly set properties of flash objects like so:

GFxValue va;
va.SetNumber(static_cast<double>(size.x));
menu->view->SetVariable("_root.HUDMovieBaseInstance.Crosshair._width", &va, 0);
max-su-2019 commented 3 years ago

Thanks, mwilsnd!

That's extremely helpful!

max-su-2019 commented 3 years ago

Hi, mwilsnd!

I try to reproduce this function in my project which based on "CommonLibSSE"( https://github.com/Ryan-rsm-McKenzie/CommonLibSSE)

The codes are as below:

void DisableCrosshair(RE::StaticFunctionTag*)
{
    auto menu = RE::UI::GetSingleton()->GetMenu(RE::InterfaceStrings::GetSingleton()->hudMenu);

    if (menu && menu->uiMovie)
    {
            RE::GFxValue result;
        RE::GFxValue args[2];
        args[0].SetString("SetCrosshairEnabled");
        args[1].SetBoolean(false);
        menu->uiMovie->Invoke("call", &result, static_cast<RE::GFxValue*>(args), 2);

        //logger::debug("Start Disable Crosshair!");
    }

        //logger::debug("Disable Crosshair!")
}

I registered it as a papyrus function and trigger it using a spell in game, but unfortunately it seems doesn't work at all. (I can ensure all the codes were fired since I saw the debug message printed in the log)

Could you please give me some suggestions on that? Thanks!

max-su-2019 commented 3 years ago

Hi, mwilsnd!

I had figure out the reason and able to solve the issue now!

It's that the SetString and SetBoolean Function in "CommonLibSSE" have not call CleanManaged() function before set it value.

I fix it by runningCleanManaged() before run set value function and everything works pretty well for me now!

Thanks for your help!

max-su-2019 commented 3 years ago

BTW, There is one more thing I want to ask, hope you don't mind.

Is there any way to get the current crosshair enable state from the flash?

I saw from your codes that you just store the enable state in your own data handler, but didn't get the true enable state from the game. What if I want to get the true crosshair enable state from the game's hud menu itself?

I suppose I may have to know the string name of the certain GFxValue and use GetVariable to get the poniter to the address of that GFXValue? But I know nothing about Flash and ActionScript, Don't know how to find the GFxValue name out.

Could you pls give me some suggestion on that? I believe this the last thing I need to learn about.

Thanks!

mwilsnd commented 3 years ago

Happy to see you figured out the problem, I imagine there are plenty of differences like that between SKSE and CommonLib.

As for getting the actual crosshair state, you are on the right track. Take a look at the UI kit the SkyUI team provides, the specific code being executed in action script when you run SetCrosshairEnabled can be found here.

max-su-2019 commented 3 years ago

Thanks for your detailed answer! You are such a kind person!

BTW, I have made the toggle crosshair functions works on SKSE64 library, but still can't reproduce it in CommonLib even I add the functions I consider lacking before.

I will ask CommonLibSSE's arthuor Ryan, see if he can produce some help.

I will write down the solution here once I figure that out.

max-su-2019 commented 3 years ago

Managed to get the crosshair enable state directly from the Flash, the codes in the project based on SKSE64 are as below:

if (menu && menu->view)
{
    GFxValue result;

    result.type = GFxValue::kType_Bool;

    menu->view->GetVariable(&result, "_root.HUDMovieBaseInstance.bCrosshairEnabled");

    result.GetBool() ? _DMESSAGE("Crosshair is Enable!") : _DMESSAGE("Crosshair is Disable!");
}

I try to reproduce that function use CommonLib, but still didn't work, don't know what the hell is wrong in CommonLib....

max-su-2019 commented 3 years ago

Okay, finally figure that out. It really has so many difference :

    bool DisableCrosshair()
    {
        static const std::string FuncName = "SetCrosshairEnabled";

        logger::debug("Disable Crosshair Function!");

        auto hud = RE::UI::GetSingleton()->GetMenu<RE::HUDMenu>();

        if (!hud) 
        {
            logger::debug("Hud menu is not open!");
            return false;
        }

        auto view = hud ? hud->uiMovie : nullptr;
        auto delegate = hud ? hud->fxDelegate : nullptr;

        if (view && delegate)
        {
            logger::debug("Start Disable Crosshair!");

            RE::FxResponseArgsEx<1> arg;
            arg[0].SetBoolean(false);

            delegate->Invoke(view.get(), FuncName.c_str(), arg);

            logger::debug("Disable Crosshair Successfully!");
            return true;
        }

        logger::debug("Fail to Disable Crosshair!");
        return false;
    }