diff --git a/src/ActiveActor.cpp b/src/ActiveActor.cpp index 6a0d76d..9c6baa0 100644 --- a/src/ActiveActor.cpp +++ b/src/ActiveActor.cpp @@ -79,7 +79,9 @@ bool ActiveActor::UpdateClone() uint32_t filterInfo = 0; if (!Utils::GetActorCollisionFilterInfo(actor.get(), filterInfo)) { - actor->GetCollisionFilterInfo(filterInfo); + RE::CFilter filterInfo_cf; +actor->GetCollisionFilterInfo(filterInfo_cf); +filterInfo = filterInfo_cf.filter; } uint16_t currentCollisionGroup = filterInfo >> 16; @@ -112,8 +114,8 @@ bool ActiveActor::UpdateClone() if (auto collidable = static_cast(cloneNode->collisionObject.get())) { if (auto worldObject = collidable->body.get()) { if (auto hkpWorldObject = static_cast(worldObject->referencedObject.get())) { - hkpWorldObject->collidable.broadPhaseHandle.collisionFilterInfo &= (0x0000ffff); // zero out collision group - hkpWorldObject->collidable.broadPhaseHandle.collisionFilterInfo |= (static_cast(collisionGroup) << 16); // set collision group to current + hkpWorldObject->collidable.broadPhaseHandle.collisionFilterInfo.filter &= (0x0000ffff); // zero out collision group + hkpWorldObject->collidable.broadPhaseHandle.collisionFilterInfo.filter |= (static_cast(collisionGroup) << 16); // set collision group to current } } } @@ -264,7 +266,7 @@ void ActiveActor::FillCloneMap(RE::NiAVObject* a_clone, RE::NiAVObject* a_origin if (auto collidable = static_cast(cloneChild->collisionObject.get())) { if (auto worldObject = collidable->body.get()) { if (auto hkpWorldObject = static_cast(worldObject->referencedObject.get())) { - auto& collisionFilterInfo = hkpWorldObject->collidable.broadPhaseHandle.collisionFilterInfo; + auto& collisionFilterInfo = hkpWorldObject->collidable.broadPhaseHandle.collisionFilterInfo.filter; CollisionLayer layer = static_cast(collisionFilterInfo & 0x7f); // remove children that have the char controller layer diff --git a/src/AttackCollision.cpp b/src/AttackCollision.cpp index e1251db..dc2e882 100644 --- a/src/AttackCollision.cpp +++ b/src/AttackCollision.cpp @@ -603,7 +603,9 @@ bool AttackCollision::CreateCollision(RE::bhkWorld* a_world, RE::Actor* a_actor, uint32_t collisionFilterInfo = 0; if (!Utils::GetActorCollisionFilterInfo(a_actor, collisionFilterInfo)) { - a_actor->GetCollisionFilterInfo(collisionFilterInfo); + RE::CFilter collisionFilterInfo_cf; +a_actor->GetCollisionFilterInfo(collisionFilterInfo_cf); +collisionFilterInfo = collisionFilterInfo_cf.filter; } uint16_t collisionGroup = collisionFilterInfo >> 16; diff --git a/src/AttackTrail.cpp b/src/AttackTrail.cpp index e6634ba..c44961f 100644 --- a/src/AttackTrail.cpp +++ b/src/AttackTrail.cpp @@ -549,7 +549,7 @@ bool AttackTrail::GetTrailDefinition(RE::ActorHandle a_actorHandle, RE::Inventor RE::BSVisit::BSVisitControl AttackTrail::ApplyColorSettings(RE::BSGeometry* a_geometry, bool a_init, bool a_bExpired) { - const auto effect = a_geometry->properties[RE::BSGeometry::States::kEffect]; + const auto effect = a_geometry->GetGeometryRuntimeData().shaderProperty; const auto effectShader = netimmerse_cast(effect.get()); if (effectShader) { auto effectShaderMaterial = skyrim_cast(effectShader->material); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32b0e65..f497076 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,7 +89,7 @@ add_library( target_compile_features( "${PROJECT_NAME}" PRIVATE - cxx_std_20 + cxx_std_23 ) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") @@ -100,7 +100,9 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") "/utf-8" # Set Source and Executable character sets to UTF-8 # "/Zi" # Debug Information Format - "/await" + # "/await" removed: it is the pre-C++20 experimental coroutine switch. With it MSVC's + # disables itself (STL4039) and only provides std::experimental::coroutine_handle, + # so CommonLibSSE-NG 6.x fails to compile. C++23 has coroutines without any flag. "/permissive-" # Standards conformance "/Zc:preprocessor" # Enable preprocessor conformance mode diff --git a/src/Havok/ContactListener.cpp b/src/Havok/ContactListener.cpp index f16974c..54ee793 100644 --- a/src/Havok/ContactListener.cpp +++ b/src/Havok/ContactListener.cpp @@ -26,25 +26,30 @@ RE::hkVector4 GetParentNodePointVelocity(RE::NiAVObject* a_node, const RE::hkVec void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_event) { - if (a_event.contactPointProperties->flags & RE::hkContactPointMaterial::FlagEnum::kIsDisabled || - !a_event.contactPointProperties->flags & RE::hkContactPointMaterial::FlagEnum::kIsNew) { + // The original read `flags & kIsDisabled || !flags & kIsNew`. C++ binds ! tighter than &, + // so the second term is (!flags) & kIsNew, and with kIsNew == 1 that is simply "flags == 0". + // It looks like a precedence slip for !(flags & kIsNew), but the mod has shipped and been + // tuned with this behaviour for three years - writing the apparently intended version drops + // continuing contacts and takes weapon-versus-wall recoil with it. Kept exactly as it was. + if (a_event.contactPointProperties->flags.any(RE::hkContactPointMaterial::Flag::kIsDisabled) || + a_event.contactPointProperties->flags.underlying() == 0) { return; } // run callbacks and re-check flag PrecisionHandler::GetSingleton()->RunContactListenerCallbacks(a_event); - if (a_event.contactPointProperties->flags & RE::hkContactPointMaterial::FlagEnum::kIsDisabled) { + if (a_event.contactPointProperties->flags.any(RE::hkContactPointMaterial::Flag::kIsDisabled)) { return; } RE::hkpRigidBody* rigidBodyA = a_event.bodies[0]; RE::hkpRigidBody* rigidBodyB = a_event.bodies[1]; - CollisionLayer layerA = static_cast(rigidBodyA->collidable.broadPhaseHandle.collisionFilterInfo & 0x7f); - CollisionLayer layerB = static_cast(rigidBodyB->collidable.broadPhaseHandle.collisionFilterInfo & 0x7f); + CollisionLayer layerA = static_cast(rigidBodyA->collidable.broadPhaseHandle.collisionFilterInfo.filter & 0x7f); + CollisionLayer layerB = static_cast(rigidBodyB->collidable.broadPhaseHandle.collisionFilterInfo.filter & 0x7f); - //uint16_t groupA = rigidBodyA->collidable.broadPhaseHandle.collisionFilterInfo >> 16; - //uint16_t groupB = rigidBodyB->collidable.broadPhaseHandle.collisionFilterInfo >> 16; + //uint16_t groupA = rigidBodyA->collidable.broadPhaseHandle.collisionFilterInfo.filter >> 16; + //uint16_t groupB = rigidBodyB->collidable.broadPhaseHandle.collisionFilterInfo.filter >> 16; if (layerA != CollisionLayer::kPrecisionAttack && layerA != CollisionLayer::kPrecisionRecoil && layerB != CollisionLayer::kPrecisionAttack && layerB != CollisionLayer::kPrecisionRecoil) { return; // Every collision we care about involves the Precision Attack or recoil layer @@ -60,7 +65,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve if (!hitRigidBodyWrapper || !hittingRigidBodyWrapper) { if (hittingRigidBody->collidable.broadPhaseHandle.objectQualityType == RE::hkpCollidableQualityType::kKeyframedReporting && !Utils::IsMoveableEntity(hitRigidBody)) { // It's not a hit, so disable contact for keyframed/fixed objects in this case - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); } return; } @@ -77,7 +82,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve // RECOIL if (hittingLayer == CollisionLayer::kPrecisionRecoil) { // Recoil layer is only used for recoil, so disable contact - a_event.contactPointProperties->flags |= RE::hkContactPointMaterial::FlagEnum::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); if (auto attackerActor = attacker->As()) { auto precisionHandler = PrecisionHandler::GetSingleton(); @@ -106,7 +111,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve } if (pointVelocity.IsEqual(RE::hkVector4())) { // still zero, skip this collision - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } @@ -159,14 +164,14 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve } if (!attacker || attacker->formType != RE::FormType::ActorCharacter) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } if (!target && hitLayer != CollisionLayer::kGround) { if (hittingRigidBody->collidable.broadPhaseHandle.objectQualityType == RE::hkpCollidableQualityType::kKeyframedReporting && !Utils::IsMoveableEntity(hitRigidBody)) { // It's not a hit, so disable contact for keyframed/fixed objects in this case - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); } return; } @@ -182,12 +187,12 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve RE::Actor* targetActor = target ? target->As() : nullptr; if (!attackCollision) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } if (attackCollision->bIsRecoiling) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } @@ -201,7 +206,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve } if (pointVelocity.IsEqual(RE::hkVector4())) { // still zero, skip this collision - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } @@ -221,12 +226,12 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve for (auto& entry : callbackReturns) { if (entry.bIgnoreHit) { // abort hit - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } } else { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; // Disable weapon-weapon collisions } } @@ -249,12 +254,12 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve for (auto& entry : callbackReturns) { if (entry.bIgnoreHit) { // abort hit - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } } else { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; // Disable weapon-moving projectile collisions } } @@ -277,7 +282,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve if (hittingNode && visualWeaponLength > 0.f) { if (hitDistanceFromWeaponRoot > visualWeaponLength) { // skip collision if the contact point is farther away than visual weapon length - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } @@ -293,7 +298,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve if (!bIsMovableEntity && !bIsDestructible) { // check if already has hit the same material recently if (attackCollision->HasHitMaterial(materialID)) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } @@ -314,13 +319,13 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve // filter out self for whatever reason if (targetActor == attackerActor) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } if (attackCollision->HasHitRef(target ? target->GetHandle() : RE::ObjectRefHandle())) { // refr has already been recently hit, so disable the contact point and gtfo - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } @@ -329,7 +334,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve auto charController = targetActor->GetCharController(); if (charController) { if (!PrecisionHandler::IsCharacterControllerHittable(charController)) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } @@ -354,7 +359,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve if (Settings::bNoPlayerTeammateAttackCollision && bAttackerIsPlayer && bTargetIsTeammate) { if (targetActor->GetActorRuntimeData().currentCombatTarget != attackerActor->GetHandle()) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } @@ -364,28 +369,28 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve // don't let the player's teammates or summons hit the player if (Settings::bNoPlayerTeammateAttackCollision && bAttackerIsTeammate && bTargetIsPlayer) { if (attackerCombatTarget != targetActorHandle) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } // don't let the player's teammates hit each other if (Settings::bNoPlayerTeammateAttackCollision && bAttackerIsTeammate && bTargetIsTeammate) { if (attackerCombatTarget != targetActorHandle) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } // don't hit actors that aren't hostile and are in combat already if (Settings::bNoNonHostileAttackCollision && precisionHandler->CheckActorInCombat(targetActorHandle) && !targetActor->IsHostileToActor(attackerActor)) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); return; } } if (targetActor && targetActor->IsGhost()) { // skip hitting actors with iframes - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); if (Settings::bDebug && Settings::bDisplayIframeHits) { const glm::vec4 blue{ 0.2, 0.2, 1.0, 1.0 }; DrawHandler::AddPoint(niHitPos, 1.f, blue); @@ -397,7 +402,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve if (PrecisionHandler::HasJumpIframes(targetActor)) { if (auto hitNode = GetNiObjectFromCollidable(hitRigidBody->GetCollidable())) { if (!Utils::IsNodeOrChildOfNode(hitNode, Settings::jumpIframeNode)) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); if (Settings::bDebug && Settings::bDisplayIframeHits) { const glm::vec4 blue{ 0.2, 0.2, 1.0, 1.0 }; @@ -411,7 +416,7 @@ void ContactListener::ContactPointCallback(const RE::hkpContactPointEvent& a_eve // disable physical collision with actor if (targetActor || Settings::bDisablePhysicalCollisionOnHit) { - a_event.contactPointProperties->flags |= RE::hkpContactPointProperties::kIsDisabled; + a_event.contactPointProperties->flags.set(RE::hkContactPointMaterial::Flag::kIsDisabled); } // add to already hit refs so we don't hit the target again within the same attack diff --git a/src/Havok/Havok.h b/src/Havok/Havok.h index c1b736f..e508500 100644 --- a/src/Havok/Havok.h +++ b/src/Havok/Havok.h @@ -16,12 +16,6 @@ namespace RE kDoActivate }; - class hkClass - { - public: - const char* name; - }; - struct hkbGeneratorOutput { enum class StandardTracks @@ -163,31 +157,6 @@ namespace RE WorldFromModelMode mode; // 06 }; - class hkbEventInfo - { - public: - uint32_t flags; - }; - - class hkaBone - { - public: - hkStringPtr name; - bool lockTranslation; - }; - - class hkaSkeleton : hkReferencedObject - { - public: - hkStringPtr name; - hkArray parentIndices; - hkArray bones; - hkArray referencePose; - hkArray referenceFloats; - hkArray floatSlots; - hkArray localFrames; - }; - class hkaSkeletonMapperData { public: @@ -293,27 +262,6 @@ namespace RE } }; - class hkMemoryRouter - { - public: - uint64_t unk00; // 00 - uint64_t unk08; // 08 - uint64_t unk10; // 10 - uint64_t unk18; // 18 - uint64_t unk20; // 20 - uint64_t unk28; // 28 - uint64_t unk30; // 30 - uint64_t unk38; // 38 - uint64_t unk40; // 40 - uint64_t unk48; // 48 - hkMemoryAllocator* temp; // 50 - hkMemoryAllocator* heap; // 58 - hkMemoryAllocator* debug; // 60 - hkMemoryAllocator* solver; // 68 - void* userData; // 70 - }; - static_assert(offsetof(hkMemoryRouter, heap) == 0x58); - class bhkSphereRepShape : public bhkShape {}; @@ -401,55 +349,20 @@ namespace RE static_assert(offsetof(bhkRigidBodyCinfo, hkCinfo) == 0x30); static_assert(sizeof(bhkRigidBodyCinfo) == 0x110); - class hkpSolverResults - { - public: - float impulseApplied; - float internalSolverData; - }; - - class hkContactPointMaterial - { - public: - enum FlagEnum - { - kIsNew = 1, - kUsesSolverPath2 = 2, - kBreakoffObjectID = 4, - kIsDisabled = 8 - }; - - uint64_t userData; - hkUFloat8 friction; - uint8_t restitution; - hkUFloat8 maxImpulse; - uint8_t flags; - }; - - class hkpContactPointProperties : public hkpSolverResults, public hkContactPointMaterial + // CommonLibSSE-NG defines hkpConvexVerticesShape itself, but not its BuildConfig, + // which thkpConvexVerticesShape_ctor in Offsets.h needs. + struct hkpConvexVerticesShapeBuildConfig { - float internalDataA; + bool createConnectivity; + bool shrinkByConvexRadius; + bool useOptimizedShrinking; + float convexRadius; + int32_t maxVertices; + float maxRelativeShrink; + float maxShrinkingVerticesDisplacement; + float maxCosAngleForBevelPlanes; }; - - class hkpConvexVerticesShape : public hkpConvexShape - { - public: - struct BuildConfig - { - bool createConnectivity; - bool shrinkByConvexRadius; - bool useOptimizedShrinking; - float convexRadius; - int32_t maxVertices; - float maxRelativeShrink; - float maxShrinkingVerticesDisplacement; - float maxCosAngleForBevelPlanes; - }; - static_assert(sizeof(BuildConfig) == 0x18); - - uint64_t pad40[13]; - }; - static_assert(sizeof(hkpConvexVerticesShape) == 0x90); + static_assert(sizeof(hkpConvexVerticesShapeBuildConfig) == 0x18); struct hkStridedVertices { @@ -480,24 +393,6 @@ namespace RE } }; - struct hkbNodeInfo - { - void* unk00; //00 - int64_t unk08; //08 - int64_t unk10; //10 - void* unk18; //18 - char unk20[48]; //20 - hkbNode* nodeTemplate; //50 - hkbNode* nodeClone; //58 - hkbNode* behavior; //60 - int64_t unk68; //68 - int64_t unk70; //70 - int64_t unk78; //78 - int64_t unk80; //80 - bool unk88; //88 - }; - static_assert(sizeof(hkbNodeInfo) == 0x90); - using NodeList = hkArray; class bhkBlendCollisionObject : public bhkCollisionObject @@ -516,19 +411,6 @@ namespace RE }; static_assert(sizeof(bhkBlendCollisionObject) == 0x48); - class bhkRigidBodyT : public bhkRigidBody - { - public: - inline static constexpr auto RTTI = RTTI_bhkRigidBodyT; - inline static auto Ni_RTTI = NiRTTI_bhkRigidBodyT; - - ~bhkRigidBodyT() override; // 00 - - // members - hkQuaternion rotation; // 40 - hkVector4 translation; // 50 - }; - static_assert(sizeof(bhkRigidBodyT) == 0x60); } void hkpWorld_removeContactListener(RE::hkpWorld* a_this, RE::hkpContactListener* a_worldListener); @@ -537,7 +419,7 @@ bool hkpWorld_hasContactListener(RE::hkpWorld* a_this, RE::hkpContactListener* a RE::bhkCharProxyController* GetCharProxyController(RE::Actor* a_actor); RE::hkMemoryRouter& hkGetMemoryRouter(); -inline void* hkHeapAlloc(int numBytes) { return hkGetMemoryRouter().heap->BlockAlloc(numBytes); } +inline void* hkHeapAlloc(int numBytes) { return hkGetMemoryRouter().Heap->BlockAlloc(numBytes); } inline float* Track_getData(RE::hkbGeneratorOutput& a_output, RE::hkbGeneratorOutput::TrackHeader& a_header) { diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 29c6d79..8a36c84 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -425,7 +425,9 @@ namespace Hooks uint32_t filterInfo = 0; auto charController = actor->GetCharController(); if (charController) { - charController->GetCollisionFilterInfo(filterInfo); + RE::CFilter charFilter; + charController->GetCollisionFilterInfo(charFilter); + filterInfo = charFilter.filter; } uint16_t collisionGroup = filterInfo >> 16; @@ -433,7 +435,14 @@ namespace Hooks bool bIsActorDisabled = PrecisionHandler::IsActorDisabled(actorHandle); - bool bIsActorDead = actor->IsDead(); + // Actor::IsDead() is a virtual call at a fixed index and reports living NPCs + // as dead on Skyrim 1.7.99 - measured on 26 of 28 live actors, all of them + // essential or protected, while a cow and a guard came back correct. That + // points at the a_notEssential parameter rather than a shifted vtable. + // GetLifeState() reads a bitfield member instead, with no vtable involved. + const auto lifeState = actor->AsActorState()->GetLifeState(); + bool bIsActorDead = lifeState == RE::ACTOR_LIFE_STATE::kDead || + lifeState == RE::ACTOR_LIFE_STATE::kDying; bool bShouldAddToWorld = !Settings::bDisableMod && !bIsActorDisabled && !bIsActorDead && actor->GetPosition().GetSquaredDistance(playerCharacter->GetPosition()) < startDistanceSq; bool bShouldRemoveFromWorld = Settings::bDisableMod || bIsActorDisabled || bIsActorDead || actor->GetPosition().GetSquaredDistance(playerCharacter->GetPosition()) > endDistanceSq; @@ -1168,7 +1177,7 @@ namespace Hooks if (ragdollConstraint) { constraint->RemoveFromCurrentWorld(); - RE::bhkWorld* world = reinterpret_cast(wrapper->GetWorld2()->unk430); + RE::bhkWorld* world = reinterpret_cast(wrapper->GetWorld2()->userData); ragdollConstraint->MoveToWorld(world); unk = ragdollConstraint; } @@ -1999,8 +2008,16 @@ namespace Hooks if (auto actor = a_actorHandle.get()) { if (auto root = actor->Get3D(false)) { auto rootNode = root->AsNode(); - if (rootNode && actor->loadedData && actor->loadedData->unk58) { - auto pUnk58 = reinterpret_cast(actor->loadedData->unk58); + // CommonLibSSE-NG covers 0x30..0x68 of LOADED_REF_DATA with the handleList + // arena, so the field the old headers called unk58 has no name any more. + // Same pointer, read by offset. Verified against CommonLibSSE-NG v3.7.0, + // where LOADED_REF_DATA had a raw uint64 at 0x58 and data3D at 0x68 - the + // byte layout is unchanged. + void* unk58 = rootNode && actor->loadedData ? + *reinterpret_cast(reinterpret_cast(actor->loadedData) + 0x58) : + nullptr; + if (unk58) { + auto pUnk58 = reinterpret_cast(unk58); if (auto unk58 = *pUnk58) { if (auto skeleton = unk58->object) { if (auto cell = actor->GetParentCell()) { @@ -2021,7 +2038,9 @@ namespace Hooks }); uint32_t collisionFilterInfo; - actor->GetCollisionFilterInfo(collisionFilterInfo); + RE::CFilter actorFilter; + actor->GetCollisionFilterInfo(actorFilter); + collisionFilterInfo = actorFilter.filter; uint16_t collisionGroup = collisionFilterInfo >> 16; CollisionLayer collisionLayer = CollisionLayer::kPrecisionBody; diff --git a/src/Hooks.h b/src/Hooks.h index b6d2bea..2d228d8 100644 --- a/src/Hooks.h +++ b/src/Hooks.h @@ -62,11 +62,11 @@ namespace Hooks _ApplyPerkEntryPoint = trampoline.write_call<5>(hook1.address() + RELOCATION_OFFSET(0x343, 0x34F), ApplyPerkEntryPoint); // 627C73, 64D69F _HitData_Populate1 = trampoline.write_call<5>(hook2.address() + RELOCATION_OFFSET(0x1B7, 0x1C6), HitData_Populate1); // 628DD7, 64E926 _HitData_Populate2 = trampoline.write_call<5>(hook3.address() + RELOCATION_OFFSET(0xEB, 0x110), HitData_Populate2); // 62917B, 64EDD0 - _TESObjectCELL_PlaceParticleEffect = trampoline.write_call<5>(hook4.address() + RELOCATION_OFFSET(0xABD, 0xB39), TESObjectCELL_PlaceParticleEffect); // 5F92AD, 620109 + _TESObjectCELL_PlaceParticleEffect = trampoline.write_call<5>(hook4.address() + RELOCATION_OFFSET(0xABD, AEOffsetSince1799(0xB39, 0xB49)), TESObjectCELL_PlaceParticleEffect); // 5F92AD, 620109 _ApplyDeathForce = trampoline.write_call<5>(hook6.address() + RELOCATION_OFFSET(0x10A, 0x10A), ApplyDeathForce); // 62A1DA, 65008A _HitActor_GetAttackData = trampoline.write_call<5>(hook2.address() + RELOCATION_OFFSET(0xB3, 0xC2), HitActor_GetAttackData); // 628CD3, 64E822 - _CdPointCollectorCast = trampoline.write_call<5>(hook3.address() + RELOCATION_OFFSET(0x26A, 0x294), CdPointCollectorCast); // 6292FA, 64EF54 + _CdPointCollectorCast = trampoline.write_call<5>(hook3.address() + RELOCATION_OFFSET(0x26A, AEOffsetSince1799(0x294, 0x2AC)), CdPointCollectorCast); // 6292FA, 64EF54 _HitData_GetAttackData = trampoline.write_call<5>(hook5.address() + RELOCATION_OFFSET(0xD3, 0xDD), HitData_GetAttackData); // 742923, 76EE8D _HitData_GetWeaponDamage = trampoline.write_call<5>(hook5.address() + RELOCATION_OFFSET(0x1A5, 0x1A4), HitData_GetWeaponDamage); // 7429F5, 76EF54 diff --git a/src/Offsets.h b/src/Offsets.h index 0ad6ecf..7725e0a 100644 --- a/src/Offsets.h +++ b/src/Offsets.h @@ -294,7 +294,7 @@ inline static REL::Relocation Actor_GetEquippedShield{ typedef RE::hkStringPtr* (*thkStringPtr_ctor)(RE::hkStringPtr*, const char*); static REL::Relocation hkStringPtr_ctor{ RELOCATION_ID(56806, 57236) }; // 9CBA10, 9F0210 -typedef void (*thkpConvexVerticesShape_ctor)(RE::hkpConvexVerticesShape*, const RE::hkStridedVertices& a_vertices, const RE::hkpConvexVerticesShape::BuildConfig& a_buildConfig); +typedef void (*thkpConvexVerticesShape_ctor)(RE::hkpConvexVerticesShape*, const RE::hkStridedVertices& a_vertices, const RE::hkpConvexVerticesShapeBuildConfig& a_buildConfig); static REL::Relocation hkpConvexVerticesShape_ctor{ RELOCATION_ID(78843, 80831) }; // E43640, E895C0 //static REL::Relocation hkpConvexVerticesShape_ctor{ RELOCATION_ID(64063, 65089) }; // B5DDC0, B82F30 @@ -307,9 +307,6 @@ static REL::Relocation AIProcess_GetCurre typedef void (*tPlayWaterImpact)(float a_worldBoundRadius, const RE::NiPoint3& a_position); static REL::Relocation PlayWaterImpact{ RELOCATION_ID(31297, 32081) }; // 4C09F0, 4DABB0 -typedef bool (*tBSSoundHandle_SetPosition)(RE::BSSoundHandle* a_this, float a_x, float a_y, float a_z); -static REL::Relocation BSSoundHandle_SetPosition{ RE::Offset::BSSoundHandle::SetPosition }; - typedef RE::NiAVObject*(__fastcall* tNiAVObject_LookupBoneNodeByName)(RE::NiAVObject* a_this, const RE::BSFixedString& a_name, bool a3); static REL::Relocation NiAVObject_LookupBoneNodeByName{ RELOCATION_ID(74481, 76207) }; diff --git a/src/PCH.h b/src/PCH.h index d2d2e65..0089ac7 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -1,5 +1,10 @@ #pragma once +// CommonLibSSE-NG 6.x uses coroutines in its Papyrus interfaces (IVirtualMachine::Awaitable) and +// relies on its own precompiled header having pulled in. This project has its own PCH, +// so it has to be included here before any CommonLibSSE header. +#include + #pragma warning(push) #include #include @@ -128,4 +133,18 @@ enum class CollisionLayer #define RELOCATION_OFFSET(SE, AE) REL::VariantOffset(SE, AE, 0).offset() +// An AE offset that moved within its function in 1.7.99. +// +// The functions themselves were not changed; the executable was rebuilt and the compiler laid the +// same code out differently, so a call that used to sit at +0x294 now sits at +0x2AC. Only the +// offset moved, which is why the hook still works once it is pointed at the right place. +// +// Chosen at runtime rather than at build time, the way CommonLibSSE handles its own values that +// changed inside AE (see REL::RelocateMemberIfNewer and RUNTIME_DATA_ACCESSOR_VERSIONED), so one +// binary stays correct on 1.6.x and on 1.7.99 alike. +[[nodiscard]] inline std::size_t AEOffsetSince1799(std::size_t a_pre1799, std::size_t a_1799) noexcept +{ + return REL::Module::IsAtLeast(SKSE::RUNTIME_SSE_1_7_99) ? a_1799 : a_pre1799; +} + #include "Plugin.h" diff --git a/src/PrecisionAPI.h b/src/PrecisionAPI.h index 8ce5ff8..cda297d 100644 --- a/src/PrecisionAPI.h +++ b/src/PrecisionAPI.h @@ -374,8 +374,9 @@ namespace PRECISION_API /// The pointer to the API singleton, or nullptr if request failed [[nodiscard]] inline void* RequestPluginAPI(const InterfaceVersion a_interfaceVersion = InterfaceVersion::V4) { - auto pluginHandle = GetModuleHandle("Precision.dll"); - _RequestPluginAPI requestAPIFunction = (_RequestPluginAPI)SKSE::WinAPI::GetProcAddress(pluginHandle, "RequestPluginAPI"); + // SKSE::WinAPI was folded into REX::W32 in CommonLibSSE-NG 6.x. + auto pluginHandle = REX::W32::GetModuleHandleA("Precision.dll"); + _RequestPluginAPI requestAPIFunction = (_RequestPluginAPI)REX::W32::GetProcAddress(pluginHandle, "RequestPluginAPI"); if (requestAPIFunction) { return requestAPIFunction(a_interfaceVersion); } diff --git a/src/PrecisionHandler.cpp b/src/PrecisionHandler.cpp index e63a639..980983c 100644 --- a/src/PrecisionHandler.cpp +++ b/src/PrecisionHandler.cpp @@ -1920,7 +1920,9 @@ bool PrecisionHandler::IsCharacterControllerHittable(RE::bhkCharacterController* { if (a_controller) { uint32_t filterInfo; - a_controller->GetCollisionFilterInfo(filterInfo); + RE::CFilter filterInfo_cf; +a_controller->GetCollisionFilterInfo(filterInfo_cf); +filterInfo = filterInfo_cf.filter; return IsCharacterControllerHittableCollisionGroup(filterInfo >> 16); } diff --git a/src/Utils.cpp b/src/Utils.cpp index ef558fb..2ff37bf 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -385,7 +385,7 @@ namespace Utils if (RE::NiPointer root = RE::NiPointer(a_actor->Get3D())) { if (auto rb = GetRigidBody(root.get())) { if (auto hkpRigidBody = static_cast(rb->referencedObject.get())) { - a_outCollisionFilterInfo = hkpRigidBody->collidable.broadPhaseHandle.collisionFilterInfo; + a_outCollisionFilterInfo = hkpRigidBody->collidable.broadPhaseHandle.collisionFilterInfo.filter; return true; } } @@ -759,7 +759,7 @@ namespace Utils } // Skip anything that does not write into zbuffer - const auto effect = geom->properties[RE::BSGeometry::States::kEffect]; + const auto effect = geom->GetGeometryRuntimeData().shaderProperty; if (a_bStrict) { const auto effectShader = netimmerse_cast(effect.get()); if (effectShader && effectShader->flags.none(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) { @@ -988,7 +988,7 @@ namespace Utils a_cloningProcess.copyType = *g_unkCloneValue3; a_cloningProcess.appendChar = *g_unkCloneValue4; - a_cloningProcess.unk68 = a_scale; + a_cloningProcess.scale = a_scale; } RE::MATERIAL_ID GetHitMaterialID(RE::hkpRigidBody* a_hitRigidBody, const RE::hkpContactPointEvent& a_event, int a_hitBodyIdx) diff --git a/src/main.cpp b/src/main.cpp index c828ecd..960c6eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,9 +89,9 @@ extern "C" DLLEXPORT constinit auto SKSEPlugin_Version = []() { v.PluginVersion(Plugin::VERSION); v.PluginName(Plugin::NAME); v.AuthorName("Ersh"); - v.UsesAddressLibrary(true); + v.UsesAddressLibrary(); v.CompatibleVersions({ SKSE::RUNTIME_SSE_LATEST }); - v.HasNoStructUse(true); + v.UsesNoStructs(); return v; }(); @@ -130,7 +130,7 @@ extern "C" DLLEXPORT void* SKSEAPI RequestPluginAPI(const PRECISION_API::Interfa GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)retAddr, &hModule); if (hModule) { - SKSE::PluginVersionData* versionData = (SKSE::PluginVersionData*)SKSE::WinAPI::GetProcAddress(hModule, "SKSEPlugin_Version"); + SKSE::PluginVersionData* versionData = (SKSE::PluginVersionData*)REX::W32::GetProcAddress(reinterpret_cast(hModule), "SKSEPlugin_Version"); if (versionData && versionData->pluginName == "Accuracy"sv && versionData->pluginVersion < 0x20000) { return nullptr; } diff --git a/vcpkg.json b/vcpkg.json index 25d436c..8adae75 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,11 +2,15 @@ "name": "precision", "version": "2", "dependencies": [ + "directxmath", + "directxtk", "fmt", + "nlohmann-json", "rapidcsv", "rsm-binary-io", "simpleini", "spdlog", + "toml11", "tomlplusplus", "xbyak" ],