From 8bfea451f643d869a780f270988f30ec1aebc659 Mon Sep 17 00:00:00 2001 From: Kris Date: Sun, 18 Jan 2026 17:18:12 +0100 Subject: [PATCH 1/5] collision changes test --- src/Papyrus/sslThreadModel.cpp | 52 +++++++++++---------- src/Thread/Collision/CollisionHandler.cpp | 55 +++++++++++++++++++++++ src/Thread/Collision/CollisionHandler.h | 25 +++++++++++ 3 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 src/Thread/Collision/CollisionHandler.cpp create mode 100644 src/Thread/Collision/CollisionHandler.h diff --git a/src/Papyrus/sslThreadModel.cpp b/src/Papyrus/sslThreadModel.cpp index 85675d0..29df064 100644 --- a/src/Papyrus/sslThreadModel.cpp +++ b/src/Papyrus/sslThreadModel.cpp @@ -5,6 +5,7 @@ #include "Registry/Util/RayCast.h" #include "Registry/Util/RayCast/ObjectBound.h" #include "Registry/Util/Scale.h" +#include "Thread/Collision/CollisionHandler.h" #include "Thread/NiNode/Node.h" #include "Thread/Thread.h" #include "UserData/StripData.h" @@ -15,25 +16,25 @@ using Offset = Registry::CoordinateType; namespace Papyrus::ThreadModel { -#define GET_INSTANCE(ret) \ - auto instance = Thread::Instance::GetInstance(a_qst); \ - if (!instance) { \ +#define GET_INSTANCE(ret) \ + auto instance = Thread::Instance::GetInstance(a_qst); \ + if (!instance) { \ a_vm->TraceStack("Thread instance not found", a_stackID); \ return ret; \ } namespace ActorAlias { -#define GET_POSITION(ret) \ - const auto actor = a_alias->GetActorReference(); \ - if (!actor) { \ +#define GET_POSITION(ret) \ + const auto actor = a_alias->GetActorReference(); \ + if (!actor) { \ a_vm->TraceStack("ReferenceAlias must be filled with an actor reference", a_stackID); \ return ret; \ - } \ - const auto a_qst = a_alias->owningQuest; \ - GET_INSTANCE(ret); \ - auto position = instance->GetPosition(actor); \ - if (!position) { \ + } \ + const auto a_qst = a_alias->owningQuest; \ + GET_INSTANCE(ret); \ + auto position = instance->GetPosition(actor); \ + if (!position) { \ a_vm->TraceStack("Position not found", a_stackID); \ return ret; \ } @@ -109,11 +110,13 @@ namespace Papyrus::ThreadModel } } + Thread::Collision::CollisionHandler::GetSingleton()->AddActor(actor->GetFormID()); + actor->StopCombat(); actor->EndDialogue(); actor->InterruptCast(false); actor->StopInteractingQuick(true); - actor->SetCollision(false); + // actor->SetCollision(false); if (const auto process = actor->currentProcess) { process->ClearMuzzleFlashes(); @@ -142,7 +145,8 @@ namespace Papyrus::ThreadModel } else { actor->SetActorValue(RE::ActorValue::kVariable05, 0.0f); } - actor->SetCollision(true); + Thread::Collision::CollisionHandler::GetSingleton()->RemoveActor(actor->GetFormID()); + // actor->SetCollision(true); } std::vector StripByData(ALIASARGS, int32_t a_stripdata, std::vector a_defaults, std::vector a_overwrite) @@ -151,10 +155,10 @@ namespace Papyrus::ThreadModel } std::vector StripByDataEx(ALIASARGS, - int32_t a_stripdata, - std::vector a_defaults, // use if a_stripData == default - std::vector a_overwrite, // use if exists - std::vector a_mergewith) // [HighHeelSpell, WeaponRight, WeaponLeft, Armor...] + int32_t a_stripdata, + std::vector a_defaults, // use if a_stripData == default + std::vector a_overwrite, // use if exists + std::vector a_mergewith) // [HighHeelSpell, WeaponRight, WeaponLeft, Armor...] { using Strip = Registry::Position::StripData; using SlotMask = RE::BIPED_MODEL::BipedObjectSlot; @@ -265,7 +269,7 @@ namespace Papyrus::ThreadModel } #undef GET_POSITION - } // namespace ActorAlias + } // namespace ActorAlias RE::BSFixedString GetActiveScene(QUESTARGS) { @@ -316,11 +320,11 @@ namespace Papyrus::ThreadModel } void CreateInstance(QUESTARGS, - std::vector a_submissives, - std::vector a_scenesPrimary, - std::vector a_scenesLeadIn, - std::vector a_scenesCustom, - int a_furniturepref) + std::vector a_submissives, + std::vector a_scenesPrimary, + std::vector a_scenesLeadIn, + std::vector a_scenesCustom, + int a_furniturepref) { const auto library = Registry::Library::GetSingleton(); const auto toVector = [&](const auto& a_list) { @@ -784,4 +788,4 @@ namespace Papyrus::ThreadModel instance->UpdateTimer(a_time); } -} // namespace Papyrus::ThreadModel +} // namespace Papyrus::ThreadModel diff --git a/src/Thread/Collision/CollisionHandler.cpp b/src/Thread/Collision/CollisionHandler.cpp new file mode 100644 index 0000000..6334d81 --- /dev/null +++ b/src/Thread/Collision/CollisionHandler.cpp @@ -0,0 +1,55 @@ +#include "CollisionHandler.h" + +namespace Thread::Collision +{ + + void CollisionHandler::Install() + { + // SE: 36359, AE: 37350 Todo: Test VR... + REL::Relocation target{ RELOCATION_ID(36359, 37350) }; + + // std::uintptr_t offset = REL::Module::GetRuntime() != REL::Module::Runtime::AE ? 0xF0 : 0xFB; + + auto& trampoline = SKSE::GetTrampoline(); + _originalApplyMovementDelta = trampoline.write_call<5>(target.address() + 0xFB, Hook_ApplyMovementDelta); + + logger::info("CollisionHandler hooks installed."); + } + + void CollisionHandler::AddActor(RE::FormID a_actor) + { + std::unique_lock lock(_mutex); + if (!std::ranges::contains(_cache, a_actor)) { + _cache.push_back(a_actor); + } + } + + void CollisionHandler::RemoveActor(RE::FormID a_actor) + { + std::unique_lock lock(_mutex); + auto it = std::ranges::find(_cache, a_actor); + if (it != _cache.end()) { + _cache.erase(it); + } + } + + void CollisionHandler::Clear() + { + std::unique_lock lock(_mutex); + _cache.clear(); + } + + void CollisionHandler::Hook_ApplyMovementDelta(RE::Actor* a_actor, float a_delta) + { + if (a_actor) { + std::shared_lock lock(_mutex); + + if (std::ranges::contains(_cache, a_actor->GetFormID())) { + return; + } + } + + _originalApplyMovementDelta(a_actor, a_delta); + } + +} // namespace Thread::Collision diff --git a/src/Thread/Collision/CollisionHandler.h b/src/Thread/Collision/CollisionHandler.h new file mode 100644 index 0000000..d1135c4 --- /dev/null +++ b/src/Thread/Collision/CollisionHandler.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace Thread::Collision +{ + class CollisionHandler : + public Singleton + { + public: + static void Install(); + + static void AddActor(RE::FormID a_actor); + static void RemoveActor(RE::FormID a_actor); + static void Clear(); + + private: + static inline std::shared_mutex _mutex; + static inline std::vector _cache; + + static void Hook_ApplyMovementDelta(RE::Actor* a_actor, float a_delta); + static inline REL::Relocation _originalApplyMovementDelta; + }; + +} // namespace Thread::Collision From dfa4b39959bee48ee8d422578a33e0b8141bf30a Mon Sep 17 00:00:00 2001 From: BabyImpala Date: Thu, 22 Jan 2026 00:44:00 +0500 Subject: [PATCH 2/5] fix: install new collision hook (by @asdt123123) --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 1aa0369..b9ad7a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "Thread/Interface/SelectionMenu.h" #include "Thread/NiNode/NiUpdate.h" #include "UserData/StripData.h" +#include "Thread/Collision/CollisionHandler.h" // class EventHandler : // public Singleton, @@ -51,6 +52,8 @@ static void SKSEMessageHandler(SKSE::MessagingInterface::Message* message) std::_Exit(EXIT_FAILURE); return; } + SKSE::GetTrampoline().create(14); + Thread::Collision::CollisionHandler::Install(); Registry::Library::GetSingleton()->Initialize(); UserData::StripData::GetSingleton()->Load(); Settings::InitializeData(); From 24cb904dffd77b0bd1623bc43027f5762b89ea7b Mon Sep 17 00:00:00 2001 From: BabyImpala Date: Thu, 22 Jan 2026 01:45:17 +0500 Subject: [PATCH 3/5] remove MinSetupDelay --- src/Papyrus/sslSystemConfig.cpp | 5 ----- src/Papyrus/sslSystemConfig.h | 2 -- src/UserData/config.def | 3 +-- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Papyrus/sslSystemConfig.cpp b/src/Papyrus/sslSystemConfig.cpp index 097b171..eacb0e5 100644 --- a/src/Papyrus/sslSystemConfig.cpp +++ b/src/Papyrus/sslSystemConfig.cpp @@ -215,9 +215,4 @@ namespace Papyrus::SystemConfig return ret; } - float GetMinSetupTime(RE::StaticFunctionTag*) - { - return std::min(Settings::fMinSetupTime, 0.1f); - } - } // namespace Papyrus diff --git a/src/Papyrus/sslSystemConfig.h b/src/Papyrus/sslSystemConfig.h index 1cfdd93..04b393e 100644 --- a/src/Papyrus/sslSystemConfig.h +++ b/src/Papyrus/sslSystemConfig.h @@ -25,8 +25,6 @@ namespace Papyrus::SystemConfig inline bool Register(VM* a_vm) { - REGISTERFUNC(GetMinSetupTime, "sslSystemConfig", true); - REGISTERFUNC(GetAnimationCount, "sslSystemConfig", true); REGISTERFUNC(GetEnjoymentFactors, "sslSystemConfig", true); REGISTERFUNC(GetEnjoymentFactor, "sslSystemConfig", true); diff --git a/src/UserData/config.def b/src/UserData/config.def index 1775423..706d54d 100644 --- a/src/UserData/config.def +++ b/src/UserData/config.def @@ -4,7 +4,6 @@ INI_SETTING(fFurniturePreference, 0.75f, "Animation") INI_SETTING(fFurnitureScanRadius, 750.0f, "Animation") INI_SETTING(fMinScale, 0.88f, "Animation") INI_SETTING(bAllowDead, false, "Animation") -INI_SETTING(fMinSetupTime, 0.7f, "Animation") INI_SETTING(fAdjustStepSizeIncrement, 0.1f, "Animation") INI_SETTING(bAdjustNodes, true, "Animation") INI_SETTING(fGhostModeAlpha, 0.6f, "Animation") @@ -17,7 +16,7 @@ INI_SETTING(fFurnitureTiltTolerance, 10.0f, "Animation") INI_SETTING(iScoreAcceptThreshold, 20, "Filter") INI_SETTING(iWeightSexStrict, 100, "Filter") INI_SETTING(iWeightSexLight, 50, "Filter") -INI_SETTING(iWeightSexMismatch, -30, "Filter") +INI_SETTING(iWeightSexMismatch, -50, "Filter") INI_SETTING(iWeightVampire, 10, "Filter") INI_SETTING(iWeightSubmissive, 20, "Filter") INI_SETTING(iWeightUnconscious, 10, "Filter") From e13e143663b575c2a954640017bc9c76e67cf90f Mon Sep 17 00:00:00 2001 From: BabyImpala Date: Thu, 22 Jan 2026 21:29:18 +0500 Subject: [PATCH 4/5] rely on std::map for EnjFactorsMap --- src/Papyrus/sslSystemConfig.cpp | 2 +- src/UserData/mcm.def | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Papyrus/sslSystemConfig.cpp b/src/Papyrus/sslSystemConfig.cpp index eacb0e5..ecce8fe 100644 --- a/src/Papyrus/sslSystemConfig.cpp +++ b/src/Papyrus/sslSystemConfig.cpp @@ -140,7 +140,7 @@ namespace Papyrus::SystemConfig return static_cast(Registry::Library::GetSingleton()->GetSceneCount()); } - static const std::unordered_map EnjoymentFactorsMap = { + static const std::map EnjoymentFactorsMap = { {"pStimulation", &Settings::f_pStimulation}, {"aAnimObjFace", &Settings::f_aAnimObjFace}, {"pAnimObjFace", &Settings::f_pAnimObjFace}, diff --git a/src/UserData/mcm.def b/src/UserData/mcm.def index b410d2c..16a7f9a 100644 --- a/src/UserData/mcm.def +++ b/src/UserData/mcm.def @@ -87,7 +87,7 @@ MCM_SETTING(fTimers, std::vector({ 10.0f, 15.0f, 25.0f, 7.0f })) // Enjoyment Related MCM_SETTING(bInternalEnjoymentEnabled, true) -MCM_SETTING(iInterDetectionStrength, 3) +MCM_SETTING(bUsePhysicBasedDetection, true) MCM_SETTING(fEnjRaiseMultInter, 0.8f) MCM_SETTING(bGameEnabled, true) From 0f04d893d737ed3fe64405b57d3e04e19797f382 Mon Sep 17 00:00:00 2001 From: BabyImpala Date: Mon, 26 Jan 2026 00:24:46 +0500 Subject: [PATCH 5/5] test 1: CollisionHandle update (by @asdt123123) --- src/Thread/Collision/CollisionHandler.cpp | 116 +++++++++++++++++----- src/Thread/Collision/CollisionHandler.h | 24 +++-- src/main.cpp | 2 +- 3 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/Thread/Collision/CollisionHandler.cpp b/src/Thread/Collision/CollisionHandler.cpp index 6334d81..73797f8 100644 --- a/src/Thread/Collision/CollisionHandler.cpp +++ b/src/Thread/Collision/CollisionHandler.cpp @@ -2,23 +2,67 @@ namespace Thread::Collision { - - void CollisionHandler::Install() + namespace { - // SE: 36359, AE: 37350 Todo: Test VR... - REL::Relocation target{ RELOCATION_ID(36359, 37350) }; + [[nodiscard]] auto GetTESObjectREFR(const RE::hkpCollidable* a_collidable) -> RE::TESObjectREFR* + { + if (!a_collidable || a_collidable->ownerOffset >= 0) { + return nullptr; + } + + using enum RE::hkpWorldObject::BroadPhaseType; + switch (static_cast(a_collidable->broadPhaseHandle.type)) { + case kEntity: + if (auto* body = a_collidable->GetOwner()) { + return body->GetUserData(); + } + break; + case kPhantom: + if (auto* phantom = a_collidable->GetOwner()) { + return phantom->GetUserData(); + } + break; + default: + break; + } + + return nullptr; + } - // std::uintptr_t offset = REL::Module::GetRuntime() != REL::Module::Runtime::AE ? 0xF0 : 0xFB; + [[nodiscard]] auto GetCollisionLayer(const RE::hkpCollidable* a_collidable) noexcept -> RE::COL_LAYER + { + if (!a_collidable) { + return RE::COL_LAYER::kUnidentified; + } + auto info = *reinterpret_cast(&a_collidable->broadPhaseHandle.collisionFilterInfo); + return static_cast(info & 0x7F); + } - auto& trampoline = SKSE::GetTrampoline(); - _originalApplyMovementDelta = trampoline.write_call<5>(target.address() + 0xFB, Hook_ApplyMovementDelta); + [[nodiscard]] constexpr bool IsBipedCollisionLayer(RE::COL_LAYER a_layer) noexcept + { + using enum RE::COL_LAYER; + switch (a_layer) { + case kBiped: + case kCharController: + case kDeadBip: + case kBipedNoCC: + return true; + default: + return false; + } + } + } - logger::info("CollisionHandler hooks installed."); + void CollisionHandler::Install() + { + REL::Relocation vtbl{ RE::VTABLE_bhkCollisionFilter[1] }; + _IsCollisionEnabled = vtbl.write_vfunc(0x1, IsCollisionEnabled); + logger::info("CollisionHandler hook installed."); } void CollisionHandler::AddActor(RE::FormID a_actor) { - std::unique_lock lock(_mutex); + const std::unique_lock lock{ _mutex }; if (!std::ranges::contains(_cache, a_actor)) { _cache.push_back(a_actor); } @@ -26,30 +70,54 @@ namespace Thread::Collision void CollisionHandler::RemoveActor(RE::FormID a_actor) { - std::unique_lock lock(_mutex); - auto it = std::ranges::find(_cache, a_actor); - if (it != _cache.end()) { - _cache.erase(it); - } + const std::unique_lock lock{ _mutex }; + std::erase(_cache, a_actor); } - void CollisionHandler::Clear() + void CollisionHandler::Clear() noexcept { - std::unique_lock lock(_mutex); + const std::unique_lock lock{ _mutex }; _cache.clear(); } - void CollisionHandler::Hook_ApplyMovementDelta(RE::Actor* a_actor, float a_delta) + bool CollisionHandler::HasActor(RE::FormID a_actor) + { + const std::shared_lock lock{ _mutex }; + return std::ranges::contains(_cache, a_actor); + } + + bool* CollisionHandler::IsCollisionEnabled(RE::hkpCollidableCollidableFilter* a_this, bool* a_result, const RE::hkpCollidable* a_collidableA, const RE::hkpCollidable* a_collidableB) { - if (a_actor) { - std::shared_lock lock(_mutex); + a_result = _IsCollisionEnabled(a_this, a_result, a_collidableA, a_collidableB); - if (std::ranges::contains(_cache, a_actor->GetFormID())) { - return; + if (!*a_result) { + return a_result; + } + + if (!IsBipedCollisionLayer(GetCollisionLayer(a_collidableA)) || + !IsBipedCollisionLayer(GetCollisionLayer(a_collidableB))) { + return a_result; + } + + auto* refA = GetTESObjectREFR(a_collidableA); + auto* refB = GetTESObjectREFR(a_collidableB); + + if (!refA || !refB || refA == refB) { + return a_result; + } + + auto* actorA = refA->As(); + auto* actorB = refB->As(); + + if (actorA && actorB) { + auto idA = actorA->GetFormID(); + auto idB = actorB->GetFormID(); + const std::shared_lock lock{ _mutex }; + if (std::ranges::any_of(_cache, [=](RE::FormID id) { return id == idA || id == idB; })) { + *a_result = false; } } - _originalApplyMovementDelta(a_actor, a_delta); + return a_result; } - -} // namespace Thread::Collision +} diff --git a/src/Thread/Collision/CollisionHandler.h b/src/Thread/Collision/CollisionHandler.h index d1135c4..a39d566 100644 --- a/src/Thread/Collision/CollisionHandler.h +++ b/src/Thread/Collision/CollisionHandler.h @@ -1,25 +1,29 @@ #pragma once #include +#include namespace Thread::Collision { - class CollisionHandler : - public Singleton + class CollisionHandler : public Singleton { public: static void Install(); - static void AddActor(RE::FormID a_actor); static void RemoveActor(RE::FormID a_actor); - static void Clear(); + static void Clear() noexcept; + + [[nodiscard]] static bool HasActor(RE::FormID a_actor); private: - static inline std::shared_mutex _mutex; - static inline std::vector _cache; + static bool* IsCollisionEnabled( + RE::hkpCollidableCollidableFilter* a_this, + bool* a_result, + const RE::hkpCollidable* a_collidableA, + const RE::hkpCollidable* a_collidableB); - static void Hook_ApplyMovementDelta(RE::Actor* a_actor, float a_delta); - static inline REL::Relocation _originalApplyMovementDelta; + static inline REL::Relocation _IsCollisionEnabled; + static inline std::vector _cache; + static inline std::shared_mutex _mutex; }; - -} // namespace Thread::Collision +} diff --git a/src/main.cpp b/src/main.cpp index b9ad7a7..36aa2b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,7 +52,7 @@ static void SKSEMessageHandler(SKSE::MessagingInterface::Message* message) std::_Exit(EXIT_FAILURE); return; } - SKSE::GetTrampoline().create(14); + SKSE::GetTrampoline().create(14 * 1); Thread::Collision::CollisionHandler::Install(); Registry::Library::GetSingleton()->Initialize(); UserData::StripData::GetSingleton()->Load();