Jump to content

Enhanced First Person Camera by LogicDragon


Nepro

Recommended Posts

So, yeah, it's not unconsciousness. If you disable fixation of X/Y rotation in xLoversMainScriptModuleAdjustPosition from Lovers with PK.esp, you can freely look up and down from first person mode, though such change breaks poses where you're oriented differently (e.g. lying on side). You can disable Z rotation fixation as well, but it'll break everything then.

 

 

post-12765-0-14487900-1384698921_thumb.jpg

 

 

Here's a modified Lovers with PK, back up your old Lovers with PK.esp, use at your own risk, etc. I haven't figured out how to edit Lovers with PK Extender.esp without breaking it, so I haven't figured out how to disable third person camera in Lovers, but you can manually press your first-person view key and enter "tfc" in console.

 

Lovers with PK.esp

Link to comment

Thanks it works now, how do i keep animations from playing in third person? lovers animations forces me in third person when i hit to first person i change back to third.

Thanks :)

 

EDIT : was clumsy again, fixed it, so camera rotation on first person while on position is no no yet?

Link to comment

Anyone have any ideas how to modify a .dll file?

 

I'd like to replicate it for fallout if i can lol.

Messing with tfc wishing fallout has this.

attachicon.gif2013-11-20_00009.jpg attachicon.gif2013-11-20_00010.jpg

 

Wow, well you're off to a Great Start Mailamea!

 

None the less please check this link out.

 

http://www.wikihow.com/Edit-DLL-Files

 

It should get you started on modifying those DLL filre

 

PS: Sorry if it does not work, I've never tried it myself :(.

 

 

Also for any of you who may still not be sure about downloading this mod, please allow me to give you 2 reasons why you should ;).

 

 

Enjoy,

 

-HappySparkles

Link to comment

Not working, I have "double melons" = 4 tits in first Person mode.

attachicon.gifDouble melons.jpg

 

Edit: I also see in the Video 4 tits. The bigger the tits the more clearly you can see the top pair.

Hope the modder will fix it.

To fix the 'double tits' bug you have to go into the OBSE/Plugins folder and open EnhancedCamera.ini . In there set

 

[Main]

; Enables first person during events where the game normally switches to third person

bFirstPersonSitting=1

bFirstPersonKnockout=1

bFirstPersonDeath=1

; Enables first person for mods that switch to third person to play animations

bFirstPersonModAnim=1

; Enables the head node to make hair visible when in first person

bEnableHeadFirstPerson=0

; Enables a visible body while riding a horse in first person

bHorseFirstPersonBody=0

; Enables experimental third person arms when in first person

bUseThirdPersonArms=0

bRotateThirdPersonArms=0

 

to

 

[Main]

; Enables first person during events where the game normally switches to third person

bFirstPersonSitting=1

bFirstPersonKnockout=1

bFirstPersonDeath=1

; Enables first person for mods that switch to third person to play animations

bFirstPersonModAnim=1

; Enables the head node to make hair visible when in first person

bEnableHeadFirstPerson=0

; Enables a visible body while riding a horse in first person

bHorseFirstPersonBody=0

; Enables experimental third person arms when in first person

bUseThirdPersonArms=1

bRotateThirdPersonArms=1

Link to comment

Anyone have any ideas how to modify a .dll file?

 

I'd like to replicate it for fallout if i can lol.

Messing with tfc wishing fallout has this.

attachicon.gif2013-11-20_00009.jpg attachicon.gif2013-11-20_00010.jpg

 

you should ask Hydrogen:

http://www.loverslab.com/topic/19451-hdt-support-unoff-release-v9-28-minor-v11-16-see-minor-rls-links-for-info-xp32-new-physics-enabled-outfit/

 

Hydrogen made HDT system for skyrim. maybe he can help you convert it to new Vegas.

Link to comment

Sent Hydrogen a message. Lets hope its not so utterly complicated.

 

 

LogicDragon personally replied to it and he said he might take a look for fallout if he has some time.

 

 

I think if already out of their leauge. LOL

 

#include "obse/PluginAPI.h"
#include "obse/CommandTable.h"

#include "obse/GameAPI.h"
#include "obse/GameData.h"
#include "obse/GameForms.h"
#include "obse/GameMenus.h"
#include "obse/GameObjects.h"
#include "obse/GameProcess.h"
#include "obse/NiAPI.h"
#include "obse/NiControllers.h"
#include "obse/NiNodes.h"
#include "obse/NiObjects.h"
#include "obse/NiTypes.h"

#include "obse_common/SafeWrite.h"
#include "obse/Utilities.h"

#include <windows.h>

#include "matrix.h"


PluginHandle g_pluginHandle = kPluginHandle_Invalid;

OBSEScriptInterface * g_scriptInterface = NULL;    // make sure you assign to this
#define ExtractArgsEx(...) g_scriptInterface->ExtractArgsEx(__VA_ARGS__)


IDebugLog        gLog("EnhancedCamera.log");

char inipath[] = "Data\\obse\\plugins\\EnhancedCamera.ini";

// ini settings
static int g_bFirstPersonSitting = 0;
static int g_bFirstPersonKnockout = 0;
static int g_bFirstPersonDeath = 0;
static int g_bFirstPersonModAnim = 0;
static int g_bEnableHeadFirstPerson = 0;
static int g_bHorseFirstPersonBody = 0;
static int g_bUseThirdPersonArms = 0;
static int g_bRotateThirdPersonArms = 0;


// Stores if the game is in 1st or 3rd person
static int g_isThirdPerson = 0;

// This should be set when the game force switches to 3rd person (sitting anim, knockout)
// if set the camera should be moved in front of the player's face
static int g_forceThirdPerson = 0;

// Should not enable visible body while riding a horse
// (can sometimes see yourself without a head/arms)
static int g_horseToggle = 0;

// Used to detect when a mod switches to third person to play an animation
static int g_checkIdle = 0;


// Pointer to the First Person camera node stored in Oblivion
// See: GameObjects.cpp, g_1stPersonCameraNode
static NiNode ** g_FirstPersonCameraNode = (NiNode**)0x00B3BB0C;


// Calls Oblivion's built-in procedure for scanning nif trees - NiAVObject::Unk_16()
NiNode * FindNode(NiNode * node, const char * name) {
    _asm
    {
        mov edx, name
        mov ecx, node
        push edx
        mov eax, dword ptr [ecx]
        mov edx, dword ptr [eax+0x58]
        call edx // This automatically sets the return value
    }
}


// Returns if an actor is currently vampire feeding
bool IsVampireFeeding(Actor * act) {
    _asm
    {
        mov ecx, act
        mov edx, [ecx]
        mov eax, [edx+0x358]
        call eax // HasVampireFeedPackage(), sets return value
    }
}

// Applies MagicShaderHitEffect to player
bool ApplyMagicEffectShader(MagicShaderHitEffect * shader) {
    _asm
    {
        mov ecx, shader
        mov edx, [ecx]
        mov eax, [edx+0x68]
        call eax // Applies the shader, sets return value
    }
}

// Returns true if dialog menu is open
bool InDialogMenu() {
    InterfaceManager *im = InterfaceManager::GetSingleton();
    return (!im->IsGameMode() && (im->MenuModeHasFocus(kMenuType_Dialog) || im->MenuModeHasFocus(kMenuType_Persuasion)));
}

// Returns true if first person body is visible
bool IsFirstPerson() {
    return !g_isThirdPerson;
}


void *idlePlaying = (void *)0x00472EA0;

// Returns true if a special idle is playing on the player
bool IsIdlePlaying() {
    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
    ActorAnimData* anim = proc->animData;
    _asm
    {
        mov ecx, anim
        call idlePlaying
        not al
        and al, 0x1
    }
}


// Sets and resets scale of 3rd person skeleton nodes
// Must also call ThisStdCall(0x00471F20, anim) to update them properly
void UpdateSkeletonNodes(bool toggleNodes) {
    NiNode * node;
    if (toggleNodes) {
        if (g_bUseThirdPersonArms == 0 && ((*g_thePlayer)->horseOrRider == 0 || g_bHorseFirstPersonBody != 1)) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 L Clavicle");
            node->m_fLocalScale = 1;
            node = FindNode((*g_thePlayer)->niNode, "Bip01 R Clavicle");
            node->m_fLocalScale = 1;
        }
        if (g_bEnableHeadFirstPerson == 0 || InDialogMenu()) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 Head");
            node->m_fLocalScale = 1;
        }
    } else {
        if (g_bUseThirdPersonArms == 0 && ((*g_thePlayer)->horseOrRider == 0 || g_bHorseFirstPersonBody != 1)) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 L Clavicle");
            node->m_fLocalScale = 0;
            node = FindNode((*g_thePlayer)->niNode, "Bip01 R Clavicle");
            node->m_fLocalScale = 0;
        }
        if (g_bEnableHeadFirstPerson == 0 || InDialogMenu()) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 Head");
            node->m_fLocalScale = 0;
        }
    }
}


// Hooks the code to switch POV
static void __stdcall HookSwitchPOV(void) {

    g_isThirdPerson = (*g_thePlayer)->isThirdPerson;

    NiNode * n;
    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
    int isVanityMode = *(UInt8*)0x00B3BB04;
    if ((*g_thePlayer)->isThirdPerson == 0 && isVanityMode == 0) {

        if ((*g_thePlayer)->horseOrRider && g_bHorseFirstPersonBody == 0) {
            n = (*g_thePlayer)->niNode;
            n->m_flags = n->m_flags | 0x0001;
        } else {
            n = (*g_thePlayer)->niNode;
            n->m_flags = n->m_flags & 0xfffe;
        }

        // Note: Visible first person body while riding a horse results in visual glitches
        if (g_bUseThirdPersonArms || ((*g_thePlayer)->horseOrRider && g_bHorseFirstPersonBody == 1)) {
            n = (*g_thePlayer)->firstPersonNiNode;
            n->m_flags = n->m_flags | 0x0001;
        } else {
            n = (*g_thePlayer)->firstPersonNiNode;
            n->m_flags = n->m_flags & 0xfffe;
        }

        if ((*g_thePlayer)->horseOrRider) {
            g_horseToggle = 1;
        }

        // Reset this here, just in case
        g_forceThirdPerson = 0;
        g_checkIdle = 0;

    } else {
        g_horseToggle = 0;

        n = (*g_thePlayer)->niNode;
        n->m_flags = n->m_flags & 0xfffe;

        n = (*g_thePlayer)->firstPersonNiNode;
        n->m_flags = n->m_flags | 0x0001;

        if (g_bFirstPersonModAnim) {
            g_forceThirdPerson = 2;
            g_checkIdle = 1;
        } else {
            g_forceThirdPerson = 0;
            g_checkIdle = 0;
        }
    }
}


// Used to rotate a node in the root node's bonespace
void RotateNode(Actor * act, NiNode * node, float rad) {

    NiMatrix33 mRootInverse;
    NiMatrix33 mNodeWorld;
    NiMatrix33 mParentWorld;
    NiMatrix33 mParentInverse;
    NiMatrix33 mRotate1;
    NiMatrix33 mRotate2;

    // Call UpdateTransform() to update the node's world rotation matrix.
    // It seems to not be modified in the animations, so not updating it causes a bug.
    node->UpdateTransform();

    // Calculate the inverse of the root node
    NiNode * nRoot;
    nRoot = act->niNode;

    MatrixInverse(&mRootInverse, &(nRoot->m_worldRotate));


    // Calculates the NodeWorldMatrix and ParentWorldMatrix without the root node
    NiNode * nParent = static_cast<NiNode *>(node->m_parent);
    MatrixMultiply(&mNodeWorld, &mRootInverse, &(node->m_worldRotate));
    MatrixMultiply(&mParentWorld, &mRootInverse, &(nParent->m_worldRotate));


    // Calculates the rotation matrix in Z-axis
    mRotate1.data[0] = 1.0f;
    mRotate1.data[1] = 0.0f;
    mRotate1.data[2] = 0.0f;
    mRotate1.data[3] = 0.0f;
    mRotate1.data[4] = cos(rad);
    mRotate1.data[5] = -sin(rad);
    mRotate1.data[6] = 0.0f;
    mRotate1.data[7] = sin(rad);
    mRotate1.data[8] = cos(rad);


    // Applies rotation to NodeWorldMatrix
    MatrixMultiply(&mRotate2, &mRotate1, &mNodeWorld);

    // Applies the ParentWorldMatrix inverse to get the node local matrix
    MatrixInverse(&mParentInverse, &mParentWorld);
    MatrixMultiply(&(node->m_localRotate), &mParentInverse, &mRotate2);
}


// Rotate the arms when the player looks up and down
void RotateArms() {

    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
    ActorAnimData* animData = proc->animData;

    float playerAngle = (*g_thePlayer)->rotX;
    float rotateLClavicle = 0.0f;
    float rotateRClavicle = 0.0f;


    // Determines the type of the equipped weapon
    int weaponType = -1;
    ExtraContainerChanges::EntryData* weaponData = proc->equippedWeaponData;
    if (weaponData) {
        TESForm * form = weaponData->type;
        if (form && form->typeID == kFormType_Weapon) {
            TESObjectWEAP * weaponForm = static_cast<TESObjectWEAP *>(form);
            weaponType = weaponForm->type;
        }
    }


    if (animData && animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastSelf, TESAnimGroup::kAnimGroup_CastTargetAlt)) {
        if (animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTouch, TESAnimGroup::kAnimGroup_CastTarget) || animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTouchAlt, TESAnimGroup::kAnimGroup_CastTargetAlt)) {
            rotateLClavicle = playerAngle;
            rotateRClavicle = playerAngle;
        }
    } else if (proc->IsAttacking()) {
        if (weaponType == TESObjectWEAP::kType_BladeOneHand || weaponType == TESObjectWEAP::kType_BluntOneHand) {
            rotateRClavicle = playerAngle * 0.35;
        } else if (weaponType == TESObjectWEAP::kType_Bow) {
            rotateLClavicle = playerAngle * 0.35;
            rotateRClavicle = playerAngle * 0.35 * 0.5;
        } else {
            rotateLClavicle = playerAngle * 0.35;
            rotateRClavicle = playerAngle * 0.35;
        }
    } else if (proc->IsBlocking()) {
        if (weaponType == TESObjectWEAP::kType_BladeOneHand || weaponType == TESObjectWEAP::kType_BluntOneHand) {
            if (proc->GetEquippedAmmoData(1)) { // GetEquippedShieldData()
                rotateLClavicle = playerAngle * 0.4;
            } else {
                rotateRClavicle = playerAngle * 0.4;
            }
        } else if (weaponType == TESObjectWEAP::kType_BladeTwoHand || weaponType == TESObjectWEAP::kType_BluntTwoHand) {
            rotateLClavicle = playerAngle * 0.4;
            rotateRClavicle = playerAngle * 0.4;
        } else if (weaponType == TESObjectWEAP::kType_Bow) {
            rotateLClavicle = playerAngle * 0.4;
        } else if (weaponType == TESObjectWEAP::kType_Staff) {
            rotateLClavicle = playerAngle * 0.4;
            rotateRClavicle = playerAngle * 0.4;
        } else { // HandToHand
            rotateLClavicle = playerAngle * 0.4;
            rotateRClavicle = playerAngle * 0.4;
        }
    } else if (proc->GetEquippedWeaponData(1)) { // GetEquippedTorchData()
        rotateLClavicle = playerAngle * 0.6;
    }

    // Fix magicNode position for casting animations
    if (animData && (animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTouch, TESAnimGroup::kAnimGroup_CastTarget) || animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTouchAlt, TESAnimGroup::kAnimGroup_CastTargetAlt))) {
        NiNode * nMagicNode;
        NiNode * nParentNode;
        NiNode * node;
        NiVector3 v, v1, v2;

        nMagicNode = FindNode((*g_thePlayer)->niNode, "magicNode");
        nParentNode = static_cast<NiNode *>(nMagicNode->m_parent);

        if (animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTouch)) {
            NiNode *n1, *n2;
            NiVector3 w1, w2;
            n1 = FindNode((*g_thePlayer)->niNode, "Bip01 L Hand");
            n2 = FindNode((*g_thePlayer)->niNode, "Bip01 R Hand");

            v.x = 12.0;
            v.y = 4.0;
            v.z = 0.0;
            MatrixVectorMultiply(&w1, &(n1->m_worldRotate), &v);
            MatrixVectorMultiply(&w2, &(n2->m_worldRotate), &v);

            v.x = 0.5 * ((n1->m_worldTranslate).x + w1.x + (n2->m_worldTranslate).x + w2.x);
            v.y = 0.5 * ((n1->m_worldTranslate).y + w1.y + (n2->m_worldTranslate).y + w2.y);
            v.z = 0.5 * ((n1->m_worldTranslate).z + w1.z + (n2->m_worldTranslate).z + w2.z);

        } else if (animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTouchAlt)) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 R Hand");
            v1.x = 6.58;
            v1.y = 3.05;
            v1.z = -0.1;
            MatrixVectorMultiply(&v2, &(node->m_worldRotate), &v1);

            v.x = (node->m_worldTranslate).x + v2.x;
            v.y = (node->m_worldTranslate).y + v2.y;
            v.z = (node->m_worldTranslate).z + v2.z;

        } else if (animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTarget)) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 L Hand");
            v1.x = 12.2;
            v1.y = 4.1;
            v1.z = 0.0;
            MatrixVectorMultiply(&v2, &(node->m_worldRotate), &v1);

            v.x = (node->m_worldTranslate).x + v2.x;
            v.y = (node->m_worldTranslate).y + v2.y;
            v.z = (node->m_worldTranslate).z + v2.z;

        } else if (animData->FindAnimInRange(TESAnimGroup::kAnimGroup_CastTargetAlt)) {
            node = FindNode((*g_thePlayer)->niNode, "Bip01 R Hand");
            v1.x = 6.62;
            v1.y = 3.02;
            v1.z = -0.075;
            MatrixVectorMultiply(&v2, &(node->m_worldRotate), &v1);

            v.x = (node->m_worldTranslate).x + v2.x;
            v.y = (node->m_worldTranslate).y + v2.y;
            v.z = (node->m_worldTranslate).z + v2.z;

        }

        NiVector3 wTranslate;
        wTranslate.x = v.x - (nParentNode->m_worldTranslate).x;
        wTranslate.y = v.y - (nParentNode->m_worldTranslate).y;
        wTranslate.z = v.z - (nParentNode->m_worldTranslate).z;

        NiMatrix33 mParentInverse;
        MatrixInverse(&mParentInverse, &(nParentNode->m_worldRotate));
        MatrixVectorMultiply(&(nMagicNode->m_localTranslate), &mParentInverse, &wTranslate);
    }

    NiNode * node;
    node = FindNode((*g_thePlayer)->niNode, "Bip01 L Clavicle");
    RotateNode((*g_thePlayer), node, -rotateLClavicle);

    node = FindNode((*g_thePlayer)->niNode, "Bip01 R Clavicle");
    RotateNode((*g_thePlayer), node, -rotateRClavicle);
}



// This code translates the first person skeleton to the correct position
// and allows you to see the hands on the 1st person body while seeing legs from 3rd person body
void TranslateFirstPerson() {

    // Update the skeleton to get correct world translation values
    ThisStdCall(0x00471F20, (*g_thePlayer)->firstPersonAnimData); // applies animData to skeleton


    // Calculate displacement of first person camera
    NiNode * nRootFirst = (*g_thePlayer)->firstPersonNiNode;
    NiNode * nCameraFirst = *g_FirstPersonCameraNode;
    NiVector3 vFirstPerson;
    NiVector3 vThirdPerson;

    vFirstPerson.x = nCameraFirst->m_worldTranslate.x - nRootFirst->m_worldTranslate.x;
    vFirstPerson.y = nCameraFirst->m_worldTranslate.y - nRootFirst->m_worldTranslate.y;
    vFirstPerson.z = nCameraFirst->m_worldTranslate.z - nRootFirst->m_worldTranslate.z;


    // Calculate displacement of third person camera
    NiNode * nRootThird = (*g_thePlayer)->niNode;
    NiNode * nHead = FindNode((*g_thePlayer)->niNode, "Bip01 Head");
    NiVector3 * tHead = &(nHead->m_worldTranslate);
    NiVector3 v0, v1;

    v0.x = 0.0;
    v0.y = 14.0;
    v0.z = 6.0;
    MatrixVectorMultiply(&v1, &(nRootThird->m_worldRotate), &v0);


    vThirdPerson.x = (tHead->x + v1.x) - nRootThird->m_worldTranslate.x;
    vThirdPerson.y = (tHead->y + v1.y) - nRootThird->m_worldTranslate.y;
    vThirdPerson.z = (tHead->z + v1.z) - nRootThird->m_worldTranslate.z;


    // This should give the difference between the root node positions
    NiVector3 vTranslateRoot;
    vTranslateRoot.x = nRootThird->m_localTranslate.x - nRootFirst->m_localTranslate.x;
    vTranslateRoot.y = nRootThird->m_localTranslate.y - nRootFirst->m_localTranslate.y;
    vTranslateRoot.z = nRootThird->m_localTranslate.z - nRootFirst->m_localTranslate.z;


    // Translate 1st person root node to new position
    nRootFirst->m_localTranslate.x += vThirdPerson.x - vFirstPerson.x + vTranslateRoot.x;
    nRootFirst->m_localTranslate.y += vThirdPerson.y - vFirstPerson.y + vTranslateRoot.y;
    nRootFirst->m_localTranslate.z += vThirdPerson.z - vFirstPerson.z + vTranslateRoot.z;


    // Hide 1st person arms when sleeping
    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
    int sitSleepFlag = proc->unk11D;

    if (sitSleepFlag >= 8) {
        nRootFirst->m_localTranslate.z -= 100;
    }
}


// Runs animation editing code
// Note: This function is actually called twice by the game for both 1st/3rd person skeletons
//       (*g_thePlayer)->isThirdPerson gets set to corresponding values
void UpdateActor(Actor * act) {

    // type must be npc and not creature
    if (act->typeID != kFormType_ACHR) {
        return;
    }

    if (act == (*g_thePlayer)) {

        // Enables body if the horse dies while riding in 1st person
        if (g_horseToggle && (*g_thePlayer)->horseOrRider == 0) {
            NiNode * n = (*g_thePlayer)->niNode;
            n->m_flags = n->m_flags & 0xfffe;

            if (g_bUseThirdPersonArms) {
                n = (*g_thePlayer)->firstPersonNiNode;
                n->m_flags = n->m_flags | 0x0001;
            } else {
                n = (*g_thePlayer)->firstPersonNiNode;
                n->m_flags = n->m_flags & 0xfffe;
            }

            g_horseToggle = 0;
        }

        // Moves the camera when sleeping/vampire feeding to avoid clipping
        HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
        int sitSleepFlag = proc->unk11D;
        int isVanityMode = *(UInt8*)0x00B3BB04;

        if (sitSleepFlag >= 8 && isVanityMode == 0) {
            if (IsFirstPerson() || g_forceThirdPerson) {
                NiNode * nRoot = (*g_thePlayer)->niNode;
                if (IsVampireFeeding(*g_thePlayer)) {
                    NiVector3 v0, v1;
                    v0.x = 0.0;
                    v0.y = -16.0;
                    v0.z = 0.0;
                    MatrixVectorMultiply(&v1, &(nRoot->m_worldRotate), &v0);

                    nRoot->m_localTranslate.x += v1.x;
                    nRoot->m_localTranslate.y += v1.y;
                    nRoot->m_localTranslate.z += v1.z + 10;
                } else {
                    nRoot->m_localTranslate.z += 10;
                }
            }
        }


        if (IsFirstPerson() && g_forceThirdPerson == 0 && ((*g_thePlayer)->horseOrRider == 0 || g_bHorseFirstPersonBody)) {
            // Disabled in dialog to avoid zoom glitch
            if (!InDialogMenu()) {
                // Disable 3rd person arms so we don't end up with duplicates
                if ((*g_thePlayer)->isThirdPerson) {

                    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
                    int sitSleepFlag = proc->unk11D;

                    // Move body to prevent seeing into objects
                    if (sitSleepFlag == 0) {
                        NiNode * nRoot = (*g_thePlayer)->niNode;
                        NiVector3 v0, v1;
                        v0.x = 0.0;
                        v0.y = -25.0;
                        v0.z = 0.0;
                        MatrixVectorMultiply(&v1, &(nRoot->m_worldRotate), &v0);

                        nRoot->m_localTranslate.x += v1.x;
                        nRoot->m_localTranslate.y += v1.y;
                        nRoot->m_localTranslate.z += v1.z;
                    }

                    // Rotate Arms
                    if (g_bRotateThirdPersonArms & 1) {
                        RotateArms();
                    }

                // Translate 1st person skeleton to correct position
                } else {
                    TranslateFirstPerson();
                }
            }
        } else {
            HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
            int knockedFlag = proc->unk11C;

            // Translate camera up to avoid clipping
            // with the ground when getting up after knockout
            if (knockedFlag == 6 && g_forceThirdPerson) {
                NiNode * nRoot = (*g_thePlayer)->niNode;
                nRoot->m_localTranslate.z += 10;
            }

            // Rotate Arms
            if (g_bRotateThirdPersonArms & 2) {
                RotateArms();
            }
        }
    }
}


// Modifies camera position
void UpdateCamera(NiNode * nCamera) {

    g_isThirdPerson = (*g_thePlayer)->isThirdPerson;

    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
    int isVanityMode = *(UInt8*)0x00B3BB04;
    int knockedFlag = proc->unk11C;
    static int sitSleepFlag = 0;

    if (g_forceThirdPerson == 2) {
        if (IsIdlePlaying()) {
            g_forceThirdPerson = 1;
            g_checkIdle = 0;
        } else {
            g_checkIdle--;
            if (g_checkIdle == 0) {
                g_forceThirdPerson = 0;
            }
        }
    }

    // Do not show body if dead or knocked out
    // (Camera placed inside head to avoid ground clipping)
    NiNode * nRoot = (*g_thePlayer)->niNode;
    if (isVanityMode == 0 && g_forceThirdPerson && ((*g_thePlayer)->IsDead() || (knockedFlag != 0 && knockedFlag != 6))) {
        if ((nRoot->m_flags & 0x0001) == 0) {
            nRoot->m_flags = nRoot->m_flags | 0x0001;
        }
    } else {
        if ((nRoot->m_flags & 0x0001) == 1) {
            nRoot->m_flags = nRoot->m_flags & 0xfffe;
        }
    }


    // Set sittingFlag to detect if player is transitioning
    // into sit state (3), out of sit state (5), or is in a bed state (>=8)
    // unk11D is SitSleepState
    if (proc->unk11D == 3 || proc->unk11D == 5 || proc->unk11D >= 8) {
        sitSleepFlag = 5;
    // Must wait a few frames after the GetSitting state changes
    // or the camera does not update properly
    } else if (sitSleepFlag > 0) {
        sitSleepFlag -= 1;
    }


    // Disabled when in 3rd person
    if ((*g_thePlayer)->isThirdPerson && g_forceThirdPerson == 0) {
        return;

    // Disabled when riding a horse
    } else if ((*g_thePlayer)->horseOrRider && g_bHorseFirstPersonBody == 0 && proc->unk11D == 4 && sitSleepFlag == 0) {
        return;

    // Disabled in dialog to avoid zoom glitch
    } else if (InDialogMenu() || isVanityMode) {
        return;
    }


    NiNode * nHead = FindNode((*g_thePlayer)->niNode, "Bip01 Head");
    NiVector3 * tHead = &(nHead->m_worldTranslate);
    NiVector3 * tCamera = &(nCamera->m_localTranslate);
    NiVector3 v0, v1;


    // Positions camera in front of the head
    if ((*g_thePlayer)->IsDead() != 0 || knockedFlag != 0 || sitSleepFlag != 0) {
        if (sitSleepFlag || knockedFlag == 6) {
            v0.x = 6.0;
            v0.y = 6.0;
            v0.z = 0.0;
        } else { // Dead, knocked out
            v0.x = 6.0;
            v0.y = -6.0;
            v0.z = 0.0;
        }
        MatrixVectorMultiply(&v1, &(nHead->m_worldRotate), &v0);

        tCamera->x = tHead->x + v1.x;
        tCamera->y = tHead->y + v1.y;
        tCamera->z = tHead->z + v1.z;

        // Translate camera up to avoid clipping
        // with the ground when dead or knocked out
        if (sitSleepFlag == 0 && knockedFlag != 6) {
            tCamera->z += 6;
        }

        // Rotate camera to face forward
        NiMatrix33 mCameraRot;
        mCameraRot.data[0] = 0;
        mCameraRot.data[1] = 0;
        mCameraRot.data[2] = 1;
        mCameraRot.data[3] = 0;
        mCameraRot.data[4] = 1;
        mCameraRot.data[5] = 0;
        mCameraRot.data[6] = -1;
        mCameraRot.data[7] = 0;
        mCameraRot.data[8] = 0;

        MatrixMultiply(&(nCamera->m_localRotate), &(nHead->m_worldRotate), &mCameraRot);

    } else if (IsIdlePlaying()) {

        v0.x = 6.0;
        v0.y = 14.0;
        v0.z = 0.0;
        MatrixVectorMultiply(&v1, &(nHead->m_worldRotate), &v0);

        tCamera->x = tHead->x + v1.x;
        tCamera->y = tHead->y + v1.y;
        tCamera->z = tHead->z + v1.z;


        NiMatrix33 m;

        NiVector3 v;
        v.x = -90.0*PI/180;
        v.y = 90.0*PI/180 - (*g_thePlayer)->rotX;
        v.z = 90.0*PI/180;
        //EulerToMatrix(&mCameraRot, &v);

        m.data[0] = cos(v.y)*cos(v.z);
        m.data[1] = -cos(v.y)*sin(v.z);
        m.data[2] = sin(v.y);
        m.data[3] = cos(v.z)*sin(v.x)*sin(v.y) + cos(v.x)*sin(v.z);
        m.data[4] = cos(v.x)*cos(v.z) - sin(v.x)*sin(v.y)*sin(v.z);
        m.data[5] = -cos(v.y)*sin(v.x);
        m.data[6] = -cos(v.x)*cos(v.z)*sin(v.y) + sin(v.x)*sin(v.z);
        m.data[7] = cos(v.z)*sin(v.x) + cos(v.x)*sin(v.y)*sin(v.z);
        m.data[8] = cos(v.x)*cos(v.y);

        MatrixMultiply(&(nCamera->m_localRotate), &(nHead->m_worldRotate), &m);

    } else {
        NiNode * node = (*g_thePlayer)->niNode;
        v0.x = 0.0;
        v0.y = 14.0;
        v0.z = 6.0;
        MatrixVectorMultiply(&v1, &(node->m_worldRotate), &v0);

        tCamera->x = tHead->x + v1.x;
        tCamera->y = tHead->y + v1.y;
        tCamera->z = tHead->z + v1.z;

    }
}


void *cameraReturn = (void *)0x0066BE7C;

// Hooks Oblivion's code for updating the camera
static _declspec(naked) void HookCamera(void)
{
    _asm
    {
        push ebx
        push eax
        call UpdateCamera
        add esp, 4
        pop ebx

        jmp cameraReturn
    }
}


// Address of function called by overwritten code
void *applyAnimData = (void *)0x00471F20;

void *animationReturn = (void *)0x006043E1;

// Hooks Oblivion's animation code in order to edit the animations
static _declspec(naked) void HookAnimation(void)
{
    _asm
    {
        // Calls my update function on the actor (stored in ebp)
        pushad
        push ebp
        call UpdateActor
        add esp, 4
        popad

        // overwritten code
        call applyAnimData

        jmp animationReturn
    }
}


// Checks if head tracking should be disabled on the player
bool CheckDisableHeadtracking(Actor * act) {
    if (act->typeID == kFormType_ACHR) {
        if (act == (*g_thePlayer)) {
            if (IsFirstPerson() || g_forceThirdPerson) {
                return true;
            }
        }
    }

    return false;
}


void *headTrackingReturn01 = (void *)0x00603AB0;
void *headTrackingReturn02 = (void *)0x00603BB7;

// Hooks Oblivion's head tracking code
static _declspec(naked) void HookHeadTracking(void)
{
    _asm
    {
        // overwritten code
        jz disable_head_ik

        // Check if head ik should be disabled on the player
        push esi
        call CheckDisableHeadtracking
        add esp, 4

        test al, al
        jnz disable_head_ik

        jmp headTrackingReturn01

    disable_head_ik:
        jmp headTrackingReturn02
    }
}


// Fix magic shader not appearing in 1st person
// Shader is applied to both 1st and 3rd person
bool UpdateMagicShader(MagicShaderHitEffect * shader) {
    bool retVal;
    if (shader->target == (*g_thePlayer)) {

        // Restore nodes to avoid bug with paralysis
        if ((*g_thePlayer)->GetActorValue(kActorVal_Paralysis) != 0) {
            UpdateSkeletonNodes(1);

            HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
            ActorAnimData* anim = proc->animData;
            ThisStdCall(0x00471F20, anim); // applies animData to skeleton
        }

        NiNode * n;
        n = (*g_thePlayer)->niNode;
        UInt16 flags = n->m_flags;

        if (g_isThirdPerson || g_bUseThirdPersonArms) {
            n->m_flags = n->m_flags | 0x0001;
            ApplyMagicEffectShader(shader);

            n->m_flags = n->m_flags & 0xfffe;
            retVal = ApplyMagicEffectShader(shader);
        } else {
            n->m_flags = n->m_flags & 0xfffe;
            ApplyMagicEffectShader(shader);

            n->m_flags = n->m_flags | 0x0001;
            retVal = ApplyMagicEffectShader(shader);
        }

        n->m_flags = flags;
    } else {
        retVal = ApplyMagicEffectShader(shader);
    }
    return retVal;
}


void *fixMagicShader01Return = (void *)0x00657603;

// Fix body magic shader 1
static _declspec(naked) void HookFixMagicShader01(void)
{
    _asm
    {
        push eax
        call UpdateMagicShader
        add esp, 4

        jmp fixMagicShader01Return
    }
}


void *fixMagicShader02Return = (void *)0x0069DB00;

// Fix body magic shader 2
static _declspec(naked) void HookFixMagicShader02(void)
{
    _asm
    {
        // overwritten code
        mov [esp+0x18], -1

        push esi
        call UpdateMagicShader
        add esp, 4

        jmp fixMagicShader02Return
    }
}


void *fixMagicShader03Return = (void *)0x00657764;

// Fix weapon magic shader 1
static _declspec(naked) void HookFixMagicShader03(void)
{
    _asm
    {
        push edi
        call UpdateMagicShader
        add esp, 4

        jmp fixMagicShader03Return
    }
}


void *fixMagicShader04Return = (void *)0x0065777C;

// Fix weapon magic shader 2
static _declspec(naked) void HookFixMagicShader04(void)
{
    _asm
    {
        push eax
        call UpdateMagicShader
        add esp, 4

        jmp fixMagicShader04Return
    }
}


void *fixMagicShader05Return = (void *)0x005060E3;

// Fix PMS magic shader
static _declspec(naked) void HookFixMagicShader05(void)
{
    _asm
    {
        // overwritten code
        mov [esp+0x20], -1

        push esi
        call UpdateMagicShader
        add esp, 4

        jmp fixMagicShader05Return
    }
}


// Returns if the camera should be forced if player is sitting or knocked out
void CheckForceCamera() {
    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
    //Console_Print("CheckForceCamera, SitSleepFlag: %x, KnockedFlag: %x", proc->unk11D, proc->unk11C);
    g_forceThirdPerson = ((proc->unk11C == 0) && g_bFirstPersonSitting) || ((proc->unk11C != 0) && g_bFirstPersonKnockout);
}

void *forcePOVCall = (void *)0x0066C580;
void *forcePOV01Return = (void *)0x0066C642;

static _declspec(naked) void HookForcePOV01(void)
{
    _asm
    {
        // overwritten code, call Oblivion's switch POV
        call forcePOVCall

        pushad
        call CheckForceCamera
        popad

        jmp forcePOV01Return
    }
}


void *forcePOV02Return = (void *)0x0066CEF3;

static _declspec(naked) void HookForcePOV02(void)
{
    _asm
    {
        // overwritten code, call Oblivion's switch POV
        call forcePOVCall

        mov g_forceThirdPerson, 1
        jmp forcePOV02Return
    }
}


void *forcePOV03Return = (void *)0x0066CF62;

static _declspec(naked) void HookForcePOV03(void)
{
    _asm
    {
        // overwritten code, call Oblivion's switch POV
        call forcePOVCall

        mov g_forceThirdPerson, 1
        jmp forcePOV03Return
    }
}

void *forcePOV04Return = (void *)0x0066CCC2;

static _declspec(naked) void HookForcePOV04(void)
{
    _asm
    {
        // overwritten code, call Oblivion's switch POV
        call forcePOVCall

        mov g_forceThirdPerson, 1
        jmp forcePOV04Return
    }
}

void *forcePOV05Return = (void *)0x00600BD1;

static _declspec(naked) void HookForcePOV05(void)
{
    _asm
    {
        // overwritten code, call Oblivion's switch POV
        call forcePOVCall

        mov g_forceThirdPerson, 1
        jmp forcePOV05Return
    }
}


// Updates 3rd person nodes
void update3rdPersonNodes(bool toggleNodes) {
    NiNode * node;
    int isVanityMode = *(UInt8*)0x00B3BB04;

    if ((*g_thePlayer)->isThirdPerson == 0 && isVanityMode == 0) {

        // Fix Trifle shadows while riding a horse
        if ((*g_thePlayer)->horseOrRider && g_bHorseFirstPersonBody == 0) {
            if (toggleNodes) {
                node = (*g_thePlayer)->niNode;
                node->m_flags = node->m_flags & 0xfffe;
            } else {
                node = (*g_thePlayer)->niNode;
                node->m_flags = node->m_flags | 0x0001;
            }
        } else {
            if (toggleNodes) {
                // Restore skeleton when in menu mode to prevent shadow missing head/arms
                InterfaceManager *im = InterfaceManager::GetSingleton();
                if (!im->IsGameMode()) {
                    HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
                    ActorAnimData* anim = proc->animData;
                    ThisStdCall(0x00471F20, anim); // applies animData to skeleton
                }
            } else {

                // Disables visibility of arms/head nodes
                UpdateSkeletonNodes(0);

                HighProcess * proc = static_cast<HighProcess *>((*g_thePlayer)->process);
                ActorAnimData* anim = proc->animData;
                ThisStdCall(0x00471F20, anim); // applies animData to skeleton

                // Resets scale to prevent bug with arms/head disappearing
                // when equipping items in the inventory menu
                UpdateSkeletonNodes(1);

                // Fix bug where game uses 3rd person magicNode causing spells to be
                // casted from wrong position. Move it to same position as first person node.
                NiNode * nMagicNodeFirst = FindNode((*g_thePlayer)->firstPersonNiNode, "magicNode");
                NiNode * nMagicNodeThird = FindNode((*g_thePlayer)->niNode, "magicNode");
                nMagicNodeThird->m_worldTranslate.x = nMagicNodeFirst->m_worldTranslate.x;
                nMagicNodeThird->m_worldTranslate.y = nMagicNodeFirst->m_worldTranslate.y;
                nMagicNodeThird->m_worldTranslate.z = nMagicNodeFirst->m_worldTranslate.z;
            }
        }
    }
}


void *shadowFunction = (void *)0x004073D0;
void *fixTrifleShadowsReturn = (void *)0x0040C920;

// Fix bug with Trifle's 1st person shadows missing arms/head
static _declspec(naked) void FixTrifleShadows(void)
{
    _asm
    {
        pushad
        push 1
        call update3rdPersonNodes
        add esp, 4
        popad

        // overwritten code, call Oblivion's shadow function
        call shadowFunction

        pushad
        push 0
        call update3rdPersonNodes
        add esp, 4
        popad

        jmp fixTrifleShadowsReturn
    }
}



extern "C" {

bool OBSEPlugin_Query(const OBSEInterface * obse, PluginInfo * info)
{
    _MESSAGE("query");

    // fill out the info structure
    info->infoVersion = PluginInfo::kInfoVersion;
    info->name = "EnhancedCamera";
    info->version = 1;

    // version checks
    if(!obse->isEditor)
    {
        if(obse->obseVersion < OBSE_VERSION_INTEGER)
        {
            _ERROR("OBSE version too old (got %08X expected at least %08X)", obse->obseVersion, OBSE_VERSION_INTEGER);
            return false;
        }

        if(obse->oblivionVersion != OBLIVION_VERSION)
        {
            _ERROR("incorrect Oblivion version (got %08X need %08X)", obse->oblivionVersion, OBLIVION_VERSION);
            return false;
        }

    }
    else
    {
        //return false; // not using the editor
    }

    // version checks pass

    return true;
}


OBSEMessagingInterface* g_messageInterface;

void MessageHandler(OBSEMessagingInterface::Message* msg)
{
    switch (msg->type)
    {
    case OBSEMessagingInterface::kMessage_PostLoad:
        //_MESSAGE("Received PostLoad mesage");

        // Trifle compatibility fix
        WriteRelJump(0x0040C91B,(UInt32)&FixTrifleShadows);
        break;
    }
}


bool OBSEPlugin_Load(const OBSEInterface * obse)
{
    _MESSAGE("load");

    g_pluginHandle = obse->GetPluginHandle();


    // Load ini settings
    char filename[MAX_PATH];
    char *pch;
    GetModuleFileNameA(NULL, filename, MAX_PATH);
    pch = strrchr(filename, '\\') + 1;
    strcpy(pch, inipath);

    //_MESSAGE("filepath:");
    //_MESSAGE(filename);

    g_bFirstPersonSitting = GetPrivateProfileIntA("Main", "bFirstPersonSitting", 1, filename);
    g_bFirstPersonKnockout = GetPrivateProfileIntA("Main", "bFirstPersonKnockout", 1, filename);
    g_bFirstPersonDeath = GetPrivateProfileIntA("Main", "bFirstPersonDeath", 1, filename);
    g_bFirstPersonModAnim = GetPrivateProfileIntA("Main", "bFirstPersonModAnim", 1, filename);
    g_bEnableHeadFirstPerson = GetPrivateProfileIntA("Main", "bEnableHeadFirstPerson", 0, filename);
    g_bHorseFirstPersonBody = GetPrivateProfileIntA("Main", "bHorseFirstPersonBody", 0, filename);
    g_bUseThirdPersonArms = GetPrivateProfileIntA("Main", "bUseThirdPersonArms", 0, filename);
    g_bRotateThirdPersonArms = GetPrivateProfileIntA("Main", "bRotateThirdPersonArms", 0, filename);


    if(!obse->isEditor)
    {
        // get an OBSEScriptInterface to use for argument extraction
        g_scriptInterface = (OBSEScriptInterface*)obse->QueryInterface(kInterface_Script);

        // register to receive messages from OBSE
        g_messageInterface = (OBSEMessagingInterface*)obse->QueryInterface(kInterface_Messaging);
        g_messageInterface->RegisterListener(g_pluginHandle, "OBSE", MessageHandler);


        // Hook the Camera
        WriteRelJump(0x0066BE6E,(UInt32)&HookCamera);

        // Hook to somewhere in Oblivion's animation code
        // This allows you to modify the 1st/3rd person skeleton nodes
        WriteRelJump(0x006043DC,(UInt32)&HookAnimation);


        // Hook to Oblivion's head tracking code. It is disabled in 1st person
        // to prevent strange camera movements when near npcs/dead bodies.
        WriteRelJump(0x00603AAA,(UInt32)&HookHeadTracking);


        // Forces game to load 3rd person body when loading save game in 1st person
        SafeWrite16(0x00664FC6,0x9090);
        SafeWrite32(0x00664FC8,0x90909090);


        // Hook to switch POV code in Oblivion
        WriteRelCall(0x00665008,(UInt32)&HookSwitchPOV);
        WriteRelJump(0x0066500D,0x00665034);

        // The switch POV code broke the view switching, this should fix it...
        SafeWrite16(0x00664FFB,0x9090);
        SafeWrite32(0x00664FFD,0x90909090);


        // Fix the bug where magic shaders are only applied in 3rd person
        WriteRelJump(0x006575FA,(UInt32)&HookFixMagicShader01); // body 1
        WriteRelJump(0x0069DAEF,(UInt32)&HookFixMagicShader02); // body 2
        WriteRelJump(0x0065775B,(UInt32)&HookFixMagicShader03); // weapon 1
        WriteRelJump(0x006576DF,(UInt32)&HookFixMagicShader04); // weapon 2
        WriteRelJump(0x005060D2,(UInt32)&HookFixMagicShader05); // PMS


        // Hooks to where the game force switches to 3rd person,
        // enables a fake first person.
        WriteRelJump(0x0066C63D,(UInt32)&HookForcePOV01); // chair, fatigue knockout, paralysis
        if (g_bFirstPersonSitting) {
            WriteRelJump(0x0066CEEE,(UInt32)&HookForcePOV02); // mount horse
            WriteRelJump(0x0066CF5D,(UInt32)&HookForcePOV03); // dismount horse
            WriteRelJump(0x0066CCBD,(UInt32)&HookForcePOV04); // vampire feed
        }
        if (g_bFirstPersonDeath) {
            WriteRelJump(0x00600BCC,(UInt32)&HookForcePOV05); // death
        }

    }

    return true;
}

};

 

Link to comment

i must say this is an amazing tweak mod and i love it.

at first i had some strange issues, but that was due to an experimental skeleton from a previous first-person-mod - also it was solvedby reinstalling the max-compatibility skeleton(s).

 

it works with BBB and doesnt lower the FPS.

 

i havent tried to change LoversPK animations into first person yet, but i hope someone will make a patch soon.

Link to comment

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

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

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use