CharmedBaryon / CommonLibSSE-NG

This is a reverse engineered library for Skyrim Special Edition and Skyrim VR.
MIT License
142 stars 31 forks source link

InventoryChanges::GetArmorInSlot may be incorrect in VR #11

Closed alandtse closed 2 years ago

alandtse commented 2 years ago

I have not tested it in NG, but when I implemented a fix in PapyrusExtender, one of the issues was related to the use of Biped_slots vs armor slots. This of course could be related to Papyrus, but just wanted to flag it since your fix was my initial fix.

Here's the current implementation here: https://github.com/CharmedBaryon/CommonLibSSE-NG/blob/main/src/RE/I/InventoryChanges.cpp#L47-L61

    TESObjectARMO* InventoryChanges::GetArmorInSlot(std::int32_t a_slot)
    {
        if SKYRIM_REL_VR_CONSTEXPR (REL::Module::IsVR()) {
            auto armorowner = this->owner;
            auto actor = armorowner ? armorowner->As<RE::Actor>() : nullptr;
            if (actor) {
                return actor->GetWornArmor(a_slot);
            }
            return nullptr;
        } else {
            using func_t = decltype(&InventoryChanges::GetArmorInSlot);
            REL::Relocation<func_t> func{ RELOCATION_ID(15873, 16113) };
            return func(this, a_slot);
        }
    }

Here's how I fixed it in PE. Note the biped_slot conversion. https://github.com/alandtse/PapyrusExtenderSSE/blob/master/src/C%2B%2B/Papyrus/Functions/Actor.h#L484-L498

#ifdef SKYRIMVR
    // Fix missing GetEquippedArmorInSlot declared in SKSEVR but that doesn't exist in VR.
    // https://www.creationkit.com/index.php?title=Actor_Script#Special_Edition_Exclusive_Functions
    inline RE::TESObjectARMO* GetEquippedArmorInSlot(VM* a_vm, StackID a_stackID, RE::Actor* a_actor, std::int32_t a_slot)
    {
        if (!a_actor) {
            a_vm->TraceStack("Actor is None", a_stackID);
            return nullptr;
        }
        auto biped_slot = (a_slot - 30) >= 0 ? 1 << (a_slot - 30) : 0;
        auto result = a_actor->GetWornArmor(static_cast<RE::BGSBipedObjectForm::BipedObjectSlot>(biped_slot));
        logger::info("GetEquippedArmor running on {} with slot {} returning biped_slot {} with {}", a_actor->GetDisplayFullName(), a_slot, biped_slot, result ? result->GetFullName() : "None");
        return result;
    }
#endif
jpstewart commented 2 years ago

Initial tests look good with a sample in Skyrim VR. Will test more tonight to verify the same results for the same inputs in SE/AE as well.

alandtse commented 2 years ago

Great thanks!