Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/Papyrus/sslSystemConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ namespace Papyrus::SystemConfig
return static_cast<int32_t>(Registry::Library::GetSingleton()->GetSceneCount());
}

static const std::unordered_map<std::string, float*> EnjoymentFactorsMap = {
static const std::map<std::string, float*> EnjoymentFactorsMap = {
{"pStimulation", &Settings::f_pStimulation},
{"aAnimObjFace", &Settings::f_aAnimObjFace},
{"pAnimObjFace", &Settings::f_pAnimObjFace},
Expand Down Expand Up @@ -215,9 +215,4 @@ namespace Papyrus::SystemConfig
return ret;
}

float GetMinSetupTime(RE::StaticFunctionTag*)
{
return std::min<float>(Settings::fMinSetupTime, 0.1f);
}

} // namespace Papyrus
2 changes: 0 additions & 2 deletions src/Papyrus/sslSystemConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
52 changes: 28 additions & 24 deletions src/Papyrus/sslThreadModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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; \
}
Expand Down Expand Up @@ -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->GetActorRuntimeData().currentProcess) {
process->ClearMuzzleFlashes();
Expand Down Expand Up @@ -142,7 +145,8 @@ namespace Papyrus::ThreadModel
} else {
actor->AsActorValueOwner()->SetActorValue(RE::ActorValue::kVariable05, 0.0f);
}
actor->SetCollision(true);
Thread::Collision::CollisionHandler::GetSingleton()->RemoveActor(actor->GetFormID());
// actor->SetCollision(true);
}

std::vector<RE::TESForm*> StripByData(ALIASARGS, int32_t a_stripdata, std::vector<uint32_t> a_defaults, std::vector<uint32_t> a_overwrite)
Expand All @@ -151,10 +155,10 @@ namespace Papyrus::ThreadModel
}

std::vector<RE::TESForm*> StripByDataEx(ALIASARGS,
int32_t a_stripdata,
std::vector<uint32_t> a_defaults, // use if a_stripData == default
std::vector<uint32_t> a_overwrite, // use if exists
std::vector<RE::TESForm*> a_mergewith) // [HighHeelSpell, WeaponRight, WeaponLeft, Armor...]
int32_t a_stripdata,
std::vector<uint32_t> a_defaults, // use if a_stripData == default
std::vector<uint32_t> a_overwrite, // use if exists
std::vector<RE::TESForm*> a_mergewith) // [HighHeelSpell, WeaponRight, WeaponLeft, Armor...]
{
using Strip = Registry::Position::StripData;
using SlotMask = RE::BIPED_MODEL::BipedObjectSlot;
Expand Down Expand Up @@ -268,7 +272,7 @@ namespace Papyrus::ThreadModel
}

#undef GET_POSITION
} // namespace ActorAlias
} // namespace ActorAlias

RE::BSFixedString GetActiveScene(QUESTARGS)
{
Expand Down Expand Up @@ -319,11 +323,11 @@ namespace Papyrus::ThreadModel
}

void CreateInstance(QUESTARGS,
std::vector<RE::Actor*> a_submissives,
std::vector<RE::BSFixedString> a_scenesPrimary,
std::vector<RE::BSFixedString> a_scenesLeadIn,
std::vector<RE::BSFixedString> a_scenesCustom,
int a_furniturepref)
std::vector<RE::Actor*> a_submissives,
std::vector<RE::BSFixedString> a_scenesPrimary,
std::vector<RE::BSFixedString> a_scenesLeadIn,
std::vector<RE::BSFixedString> a_scenesCustom,
int a_furniturepref)
{
const auto library = Registry::Library::GetSingleton();
const auto toVector = [&](const auto& a_list) {
Expand Down Expand Up @@ -787,4 +791,4 @@ namespace Papyrus::ThreadModel
instance->UpdateTimer(a_time);
}

} // namespace Papyrus::ThreadModel
} // namespace Papyrus::ThreadModel
123 changes: 123 additions & 0 deletions src/Thread/Collision/CollisionHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "CollisionHandler.h"

namespace Thread::Collision
{
namespace
{
[[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<RE::hkpWorldObject::BroadPhaseType>(a_collidable->broadPhaseHandle.type)) {
case kEntity:
if (auto* body = a_collidable->GetOwner<RE::hkpRigidBody>()) {
return body->GetUserData();
}
break;
case kPhantom:
if (auto* phantom = a_collidable->GetOwner<RE::hkpPhantom>()) {
return phantom->GetUserData();
}
break;
default:
break;
}

return nullptr;
}

[[nodiscard]] auto GetCollisionLayer(const RE::hkpCollidable* a_collidable) noexcept -> RE::COL_LAYER
{
if (!a_collidable) {
return RE::COL_LAYER::kUnidentified;
}
auto info = *reinterpret_cast<const std::uint32_t*>(&a_collidable->broadPhaseHandle.collisionFilterInfo);
return static_cast<RE::COL_LAYER>(info & 0x7F);
}

[[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;
}
}
}

void CollisionHandler::Install()
{
REL::Relocation<std::uintptr_t> vtbl{ RE::VTABLE_bhkCollisionFilter[1] };
_IsCollisionEnabled = vtbl.write_vfunc(0x1, IsCollisionEnabled);
logger::info("CollisionHandler hook installed.");
}

void CollisionHandler::AddActor(RE::FormID a_actor)
{
const std::unique_lock lock{ _mutex };
if (!std::ranges::contains(_cache, a_actor)) {
_cache.push_back(a_actor);
}
}

void CollisionHandler::RemoveActor(RE::FormID a_actor)
{
const std::unique_lock lock{ _mutex };
std::erase(_cache, a_actor);
}

void CollisionHandler::Clear() noexcept
{
const std::unique_lock lock{ _mutex };
_cache.clear();
}

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)
{
a_result = _IsCollisionEnabled(a_this, a_result, a_collidableA, a_collidableB);

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<RE::Actor>();
auto* actorB = refB->As<RE::Actor>();

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;
}
}

return a_result;
}
}
29 changes: 29 additions & 0 deletions src/Thread/Collision/CollisionHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <shared_mutex>
#include <vector>

namespace Thread::Collision
{
class CollisionHandler : public Singleton<CollisionHandler>
{
public:
static void Install();
static void AddActor(RE::FormID a_actor);
static void RemoveActor(RE::FormID a_actor);
static void Clear() noexcept;

[[nodiscard]] static bool HasActor(RE::FormID a_actor);

private:
static bool* IsCollisionEnabled(
RE::hkpCollidableCollidableFilter* a_this,
bool* a_result,
const RE::hkpCollidable* a_collidableA,
const RE::hkpCollidable* a_collidableB);

static inline REL::Relocation<decltype(IsCollisionEnabled)> _IsCollisionEnabled;
static inline std::vector<RE::FormID> _cache;
static inline std::shared_mutex _mutex;
};
}
3 changes: 1 addition & 2 deletions src/UserData/config.def
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion src/UserData/mcm.def
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ MCM_SETTING(fTimers, std::vector<float>({ 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)
Expand Down
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<EventHandler>,
Expand Down Expand Up @@ -51,6 +52,8 @@ static void SKSEMessageHandler(SKSE::MessagingInterface::Message* message)
std::_Exit(EXIT_FAILURE);
return;
}
SKSE::GetTrampoline().create(14 * 1);
Thread::Collision::CollisionHandler::Install();
Registry::Library::GetSingleton()->Initialize();
UserData::StripData::GetSingleton()->Load();
Settings::InitializeData();
Expand Down