Jump to content

[Solved] F4SE: unresolved external symbol


ScottyDoesKnow

Recommended Posts

 

Why not to use FormId of Fusion Core? It is pretty known

 

If you're still around, I've found a new issue. My code is triggering on weird ammo like what bloatflies fire. And apparently it finds an ammo stack to put it in too. Now this doesn't seem to matter for things like bloatflies, but I'd still rather not run the code. Currently I added checks for weird ammo types like bloatfly larva, mirelurk king sonic wave, etc... which works fine but is obviously not ideal. But that's not the real issue.

 

The real issue is I tried killing a sentry bot and it took the 5.56 ammo out of its gun. So I need some way to check if a gun is lootable. I've found a keyword (ObjectTypeWeapon) which seems to be on all the normal guns but not the weird ones, but I can't figure out how to check for keywords in F4SE. Do you know if it's possible? Currently I'm checking for sentry bot guns and turret guns, which again isn't ideal. At this point it would be easier just to whitelist all the standard guns, but that would mean it doesn't work with mods.

 

So being able to check for keywords seems like the best solution if it's possible, but I'm open to any other ideas as well.

 

Edit: there is a "Can't Drop" flag. I'm checking out TESForm->flags now.

 

Edit2: That did it!

Edited by ScottyDoesKnow
Link to comment
Share on other sites

 

 

but I can't figure out how to check for keywords in F4SE. Do you know if it's possible?

 

 

you can just cast your object (TESForm) to BGSKeywordForm and iterate keywords.
Something like that:
const BGSKeywordForm* pKeywords = DYNAMIC_CAST(equipData->item, TESForm, BGSKeywordForm);
Link to comment
Share on other sites

I don't think that cast will work. TESObjectREFR doesn't inherit BGSKeywordForm. Weapon does

Actually, I don't know how ObjectReference.Addkeyword() Papyrus function works, so I can't say what object is used to store adding keywords. Maybe it is a class derived from TBO_InstanceData that is indirectly referenced by TESObjectREFR ( see data-> objectReference->CreateInstance() )

Edited by DlinnyLag
Link to comment
Share on other sites

 

you can just cast your object (TESForm) to BGSKeywordForm and iterate keywords.
Something like that:
const BGSKeywordForm* pKeywords = DYNAMIC_CAST(equipData->item, TESForm, BGSKeywordForm);

 

Thanks for the info. I'm glad I didn't figure it out because the flag check is a much cleaner solution in the end, I later found some weird weapons with that keyword.

 

Now the code checks for the "Can't Drop" flag rather than looking at specific weapons or ammo. It also checks for fusion cores by finding the charge value rather than by editor ID to make it more compatible with mods.

 

Here's (hopefully the final) code if you feel like checking it out. I think I managed to do the upgraded checks pretty cleanly.

// https://github.com/powerof3/SKSEPlugins/blob/master/po3_FEC/main.cpp
class TESDeathEventHandler : public BSTEventSink<TESDeathEvent> {
public:
    static TESDeathEventHandler* GetSingleton() {
        static TESDeathEventHandler singleton;
        return &singleton;
    }

    virtual EventResult ReceiveEvent(TESDeathEvent* evn, void* dispatcher) override {
        if (!evn || !evn->source) {
            return kEvent_Continue;
        }

        Actor* actor = DYNAMIC_CAST(evn->source, TESObjectREFR, Actor);
        if (!actor) {
            return kEvent_Continue;
        }

        AmmoData ammoData;
        if (!LoadUnloadAmmo(actor, ammoData, false)) {
            return kEvent_Continue;
        }

        if (!AddAmmo(actor, ammoData)) {
            LoadUnloadAmmo(actor, ammoData, true);
        }

        return kEvent_Continue;
    }

private:
    static const UInt64 LOWER_32_MASK = 0x00000000FFFFFFFF;
    static const UInt64 UPPER_32_MASK = 0xFFFFFFFF00000000;

    static const size_t CANT_DROP_FLAG_POS = 2;
    static const int AMMO_CHARGE_INDEX = 1;

    // https://stackoverflow.com/a/1008289
    // Using C++ 98, so no delete
    TESDeathEventHandler() {}
    TESDeathEventHandler(TESDeathEventHandler const&);
    void operator=(TESDeathEventHandler const&);

    struct AmmoData
    {
        TESAmmo    * ammo;
        UInt64    ammoCount;
    };

    bool LoadUnloadAmmo(Actor* actor, AmmoData& ammoData, bool load) {
        auto middleProcess = actor->middleProcess;
        if (!middleProcess) {
            return false;
        }

        auto unk08 = middleProcess->unk08;
        if (!unk08) {
            return false;
        }

        SimpleLocker locker(&unk08->lock);

        auto equipDataArray = unk08->equipData;
        for (UInt32 i = 0; i < equipDataArray.count; ++i) {

            Actor::MiddleProcess::Data08::EquipData equipData;
            if (!equipDataArray.GetNthItem(i, equipData)) {
                return false; // If one fails, all the rest will fail
            }

            auto item = equipData.item;
            if (!item) {
                continue;
            }

            if (std::bitset<sizeof(UInt32*CHAR_BIT)>(item->flags).test(CANT_DROP_FLAG_POS)) {
                continue; // Ignore weapons marked as Can't Drop
            }

            auto equippedData = equipData.equippedData;
            if (!equippedData) {
                continue;
            }

            auto ammo = equippedData->ammo;
            if (!ammo) {
                continue;
            }

            auto unk160 = ammo->unk160;
            if (unk160 && sizeof(unk160) > AMMO_CHARGE_INDEX) {
                auto ammoCharge = unk160[AMMO_CHARGE_INDEX] & LOWER_32_MASK;
                if (ammoCharge != 0) {
                    continue; // Ignore ammo with charge
                }
            }

            auto ammoCount = equippedData->unk18 & LOWER_32_MASK; // Ammo count is lower 32 bits

            if (load) {
                if (ammoCount != 0) {
                    continue; // Don't load ammo if ammo isn't empty
                }

                if (ammo != ammoData.ammo) {
                    continue;
                }

                // Load ammo
                equippedData->unk18 += ammoData.ammoCount;
            }
            else {
                if (ammoCount == 0) {
                    continue; // Sometimes this method gets called twice, this will skip it on subsequent calls
                }

                // Unload ammo
                equippedData->unk18 &= UPPER_32_MASK; // Clear lower 32 bits

                // Store data
                ammoData.ammo = ammo;
                ammoData.ammoCount = ammoCount;
            }

            // Stop searching
            return true;
        }

        return false;
    }

    bool AddAmmo(Actor* actor, AmmoData ammoData) {
        auto inventoryList = actor->inventoryList;
        if (!inventoryList) {
            return false;
        }

        BSWriteLocker locker(&inventoryList->inventoryLock);

        auto inventoryItems = inventoryList->items;
        for (UInt32 j = 0; j < inventoryItems.count; ++j) {

            BGSInventoryItem item;
            if (!inventoryItems.GetNthItem(j, item)) {
                return false; // If one fails, all the rest will fail
            }

            if (item.form == ammoData.ammo) {
                // Add to count
                item.stack->count += ammoData.ammoCount;

                // Stop searching
                return true;
            }
        }

        return false;
    }
};
Edited by ScottyDoesKnow
Link to comment
Share on other sites

  • 1 year later...
On 9/17/2022 at 5:04 AM, DlinnyLag said:

You have to include appropriate F4SE .cpp files to you project. Including .h file is not enough to compile binary file if compiler/linker doesn't have access to body of functions that just declared (but not implemented) in a header file.

 

SimpleLock implementation is in the GameTypes.cpp

Heap_Allocate implementation is in the GameAPI.cpp

It's been quite a while, but I think I found the more correct way than including .cpp files (which maybe you were getting at, I see you mentioned the linker). You just need to add the f4se_<version>.lib in the project's properties under Linker -> Input -> Additional Dependencies. Just let me know if you already knew this and there's some reason not to do it.

Link to comment
Share on other sites

On 5/24/2024 at 5:15 PM, ScottyDoesKnow said:

It's been quite a while, but I think I found the more correct way than including .cpp files (which maybe you were getting at, I see you mentioned the linker). You just need to add the f4se_<version>.lib in the project's properties under Linker -> Input -> Additional Dependencies. Just let me know if you already knew this and there's some reason not to do it.

I do not see any way how it might work.

f4se_1_10_163.lib has only single function for linker - StartF4SE. This is the only function that you can call from f4se_1_10_163.dll 

I suppose your code do not call it %) Actually, I do not see any code that calls this function. It looks like a rudiment.

 

Perhaps, you have changed process of f4se project building and it produces something different now. Perhaps, you've changed f4se project type from dynamic library to static library, like mentioned in another thread

 

 

Edited by DlinnyLag
Link to comment
Share on other sites

34 minutes ago, DlinnyLag said:

I do not see any way how it might work.

f4se_1_10_163.lib has only single function for linker - StartF4SE. This is the only function that you can call from f4se_1_10_163.dll 

I suppose your code do not call it %) Actually, I do not see any code that calls this function. It looks like a rudiment.

 

Perhaps, you have changed process of f4se project building and it produces something different now. Perhaps, you've changed f4se project type from dynamic library to static library, like mentioned in another thread

 

 

You are correct. I was trying a bunch of things to make it work but it looks like it's compiling f4se as a static library that actually fixes it, which is great because the f4se lib name changes with each version. Thanks once again!

  • Like 1
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...