From de6ca08be4be337b5528fe3eab1969b27c981008 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:32:24 +0200 Subject: [PATCH 1/6] update follow player base to allow ignoring all player behaviour --- .../Actors/ZActorsFollowPlayerEffect.cpp | 4 +- .../Camera/ZFollowingActorCameraEffect.cpp | 14 +-- .../Dbg/ZActorFollowPlayerDbgEffect.cpp | 98 +++++++++++++++++++ src/Effects/Dbg/ZActorFollowPlayerDbgEffect.h | 25 +++++ src/Entity/BehaviorTreeMatch.h | 18 ++++ src/Entity/Bindings/SAIModifierBinding.h | 35 +++++++ src/Entity/Bindings/SAIModifierRoleBinding.h | 23 ----- .../Bindings/SFollowPlayerHelperBinding.h | 51 +++++++++- 8 files changed, 228 insertions(+), 40 deletions(-) create mode 100644 src/Effects/Dbg/ZActorFollowPlayerDbgEffect.cpp create mode 100644 src/Effects/Dbg/ZActorFollowPlayerDbgEffect.h create mode 100644 src/Entity/BehaviorTreeMatch.h create mode 100644 src/Entity/Bindings/SAIModifierBinding.h delete mode 100644 src/Entity/Bindings/SAIModifierRoleBinding.h diff --git a/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp b/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp index 0c8364c..bbe58f2 100644 --- a/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp +++ b/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp @@ -25,8 +25,8 @@ void ZActorsFollowPlayerEffect::SetActorsFollowPlayer(const bool p_bFollow) if (p_bFollow) { // make following actors ignore sillyness - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreAnnoyingHitman = true; - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreSillyHitman = true; + s_FollowHelper.m_AIModifierBinding.m_bIgnoreAnnoyingHitman = true; + s_FollowHelper.m_AIModifierBinding.m_bIgnoreSillyHitman = true; s_FollowHelper.m_fMinTetherRange = 2.f; s_FollowHelper.m_fMaxTetherRange = 5.f; diff --git a/src/Effects/Camera/ZFollowingActorCameraEffect.cpp b/src/Effects/Camera/ZFollowingActorCameraEffect.cpp index 5abf7ef..0f3bcce 100644 --- a/src/Effects/Camera/ZFollowingActorCameraEffect.cpp +++ b/src/Effects/Camera/ZFollowingActorCameraEffect.cpp @@ -4,6 +4,7 @@ #include #include "Registry.h" +#include "ZConfigurationAccessor.h" #include "Helpers/ActorUtils.h" #include "Helpers/PlayerUtils.h" #include "Helpers/EntityUtils.h" @@ -84,7 +85,7 @@ void ZFollowingActorCameraEffect::Stop() if (m_rFollowingActor) { auto s_FollowHelper = GetFollowHelperFor(m_rFollowingActor); - s_FollowHelper.StopFollowHitman(); + s_FollowHelper.StopFollowHitmanIgnoreEverything(); m_rFollowingActor = {}; m_rFollowingActorHeadAttach = {}; @@ -132,15 +133,8 @@ void ZFollowingActorCameraEffect::MakeActorFollowingCameraPerson(const TEntityRe s_FollowHelper.m_fMinTetherRange = 2.f; s_FollowHelper.m_fMaxTetherRange = 5.f; - // make actor ignore most everything so they keep following - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreLowNoise = true; - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreSillyHitman = true; - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreAnnoyingHitman = true; - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreDistractions = true; - s_FollowHelper.m_AIModifierRoleBinding.m_bIgnoreAccidents = true; - s_FollowHelper.m_AIModifierRoleBinding.m_bNeverSpectate = true; - - s_FollowHelper.StartFollowHitman(); + // make the actor follow the player, ignoring everything they do (e.g. shooting etc) + s_FollowHelper.StartFollowHitmanIgnoreEverything(); // attach camera to the actor's head m_rFollowingActorHeadAttach = Utils::GetActorHeadAttachEntity(p_rActor); diff --git a/src/Effects/Dbg/ZActorFollowPlayerDbgEffect.cpp b/src/Effects/Dbg/ZActorFollowPlayerDbgEffect.cpp new file mode 100644 index 0000000..ea6eb0a --- /dev/null +++ b/src/Effects/Dbg/ZActorFollowPlayerDbgEffect.cpp @@ -0,0 +1,98 @@ +#ifdef _DEBUG +#include "ZActorFollowPlayerDbgEffect.h" + +#include + +#include "Registry.h" +#include "Helpers/ActorUtils.h" +#include "Helpers/PlayerUtils.h" + +#define TAG "[ZActorFollowPlayerDbgEffect] " + +void ZActorFollowPlayerDbgEffect::OnDrawDebugUI() +{ + if (ImGui::Button("Select Nearest Actor")) + { + SMatrix s_mPlayerTransform; + if (Utils::GetPlayerTransform(s_mPlayerTransform)) + { + const auto s_vPlayerPosition = s_mPlayerTransform.Pos; + if (const auto s_aNearby = Utils::GetNearbyActors(s_vPlayerPosition, 1); !s_aNearby.empty()) + { + m_rTargetActor = s_aNearby.front().first; + } + } + } + + ImGui::TextUnformatted(fmt::format("Selected Actor: {}", m_rTargetActor ? m_rTargetActor.m_pInterfaceRef->GetActorName() : "").c_str()); + + if (!m_rTargetActor) + { + ImGui::TextUnformatted("Select a target actor to get started!"); + return; + } + + auto s_Binding = GetFollowHelperFor(m_rTargetActor); + if (!s_Binding) + { + ImGui::TextUnformatted("Did not get follow helper!"); + return; + } + + if (auto s_foMinTetherRange = s_Binding.m_fMinTetherRange; s_foMinTetherRange.has_value()) + { + auto s_fMinTetherRange = s_foMinTetherRange.value(); + if (ImGui::DragFloat("Min Tether Range", &s_fMinTetherRange)) + { + s_Binding.m_fMinTetherRange = s_fMinTetherRange; + } + } + + if (auto s_foMaxTetherRange = s_Binding.m_fMaxTetherRange; s_foMaxTetherRange.has_value()) + { + auto s_fMaxTetherRange = s_foMaxTetherRange.value(); + if (ImGui::DragFloat("Max Tether Range", &s_fMaxTetherRange)) + { + s_Binding.m_fMaxTetherRange = s_fMaxTetherRange; + } + } + + if (auto s_foMaxSightDistance = s_Binding.m_fMaxSightDistance; s_foMaxSightDistance.has_value()) + { + auto s_fMaxSightDistance = s_foMaxSightDistance.value(); + if (ImGui::DragFloat("Max Sight Range", &s_fMaxSightDistance)) + { + s_Binding.m_fMaxSightDistance = s_fMaxSightDistance; + } + } + + if (auto s_soMatch = s_Binding.m_sMatch; s_soMatch.has_value()) + { + std::string s_sMatch = s_soMatch.value().c_str(); + ImGui::TextUnformatted(fmt::format("Behavior Tree Match Node: {}", s_sMatch).c_str()); + } + + if (ImGui::Button("StartFollow (normal)")) + { + s_Binding.StartFollowHitman(); + } + ImGui::SameLine(); + if (ImGui::Button("StopFollow (normal)")) + { + s_Binding.StopFollowHitman(); + } + + if (ImGui::Button("StartFollow (ignore everything)")) + { + s_Binding.StartFollowHitmanIgnoreEverything(); + } + ImGui::SameLine(); + if (ImGui::Button("StopFollow (ignore everything)")) + { + s_Binding.StopFollowHitmanIgnoreEverything(); + } +} + +REGISTER_CHAOS_EFFECT(ZActorFollowPlayerDbgEffect); + +#endif // _DEBUG diff --git a/src/Effects/Dbg/ZActorFollowPlayerDbgEffect.h b/src/Effects/Dbg/ZActorFollowPlayerDbgEffect.h new file mode 100644 index 0000000..71d3c09 --- /dev/null +++ b/src/Effects/Dbg/ZActorFollowPlayerDbgEffect.h @@ -0,0 +1,25 @@ +#pragma once +#include "Effects/Base/Companion/ZActorFollowPlayerHelperEffectBase.h" + +#include + +class ZActor; + +/** + * Debug Effect for testing actor follow player helper. + */ +class ZActorFollowPlayerDbgEffect final : public ZActorFollowPlayerHelperEffectBase +{ + public: + void Start() override {} + + void OnDrawDebugUI() override; + + bool IsEnabled() const override + { + return false; + } + + private: + TEntityRef m_rTargetActor; +}; \ No newline at end of file diff --git a/src/Entity/BehaviorTreeMatch.h b/src/Entity/BehaviorTreeMatch.h new file mode 100644 index 0000000..111db46 --- /dev/null +++ b/src/Entity/BehaviorTreeMatch.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/// Behavior tree node names for match properties of spsystem / legacy behaviors. +/// Extract using: strings HITMAN3.exe | grep '^BT_' +namespace BehaviorTreeMatch +{ + static const ZString BT_AMBIENCE("BT_AMBIENCE"); // lowest priority + + static const ZString BT_OVERRIDE("BT_OVERRIDE"); + static const ZString BT_OVERRIDE_STANDING("BT_OVERRIDE_STANDING"); + static const ZString BT_OVERRIDE_CURIOUS("BT_OVERRIDE_CURIOUS"); + static const ZString BT_OVERRIDE_CAUTIOUS("BT_OVERRIDE_CAUTIOUS"); + static const ZString BT_OVERRIDE_SENTRY("BT_OVERRIDE_SENTRY"); + static const ZString BT_CLOSECOMBAT_ONLY("BT_CLOSECOMBAT_ONLY"); + + static const ZString BT_OVERRIDE_ALL("BT_OVERRIDE_ALL"); // highest priority, will make the actor ignore every other behavior +} // namespace BehaviorTreeMatch \ No newline at end of file diff --git a/src/Entity/Bindings/SAIModifierBinding.h b/src/Entity/Bindings/SAIModifierBinding.h new file mode 100644 index 0000000..bcd444f --- /dev/null +++ b/src/Entity/Bindings/SAIModifierBinding.h @@ -0,0 +1,35 @@ +#pragma once +#include "Entity/Bindings/EntityBinding.h" + +#include + +// [modules:/zaimodifieractor.class].pc_entitytype +// [modules:/zaimodifierrole.class].pc_entitytype +struct SAIModifierBinding : public SEntityBinding +{ + using SEntityBinding::SEntityBinding; + + PROPERTY(EAIModifierScope, m_nScope); + PROPERTY(bool, m_bIgnoreLowNoise); + PROPERTY(bool, m_bIgnoreSillyHitman); + PROPERTY(bool, m_bIgnoreAnnoyingHitman); + PROPERTY(bool, m_bIgnoreDistractions); + PROPERTY(bool, m_bIgnoreAccidents); + PROPERTY(bool, m_bIgnoreDeadBodies); + PROPERTY(bool, m_bDisableDeadBodySensor); + PROPERTY(bool, m_bIgnoreWeapons); + PROPERTY(bool, m_bIgnoreTrespassing); + PROPERTY(bool, m_bIgnoreLockdown); + PROPERTY(bool, m_bDisableHelpCivilian); + PROPERTY(bool, m_bNeverSpectate); + PROPERTY(bool, m_bDeafAndBlind); + PROPERTY(bool, m_bWantsPrivacy); + PROPERTY(bool, m_bOneHitpoint); + PROPERTY(bool, m_bBlockDeadlyThrow); + PROPERTY(bool, m_bBlockMelee); + PROPERTY(bool, m_bBlockDeath); + PROPERTY(bool, m_bSuppressSocialGreeting); + + INPUT_PIN(Set); // Set the modifiers + INPUT_PIN(Clear); // Clear the modifiers +}; diff --git a/src/Entity/Bindings/SAIModifierRoleBinding.h b/src/Entity/Bindings/SAIModifierRoleBinding.h deleted file mode 100644 index 353c6c3..0000000 --- a/src/Entity/Bindings/SAIModifierRoleBinding.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "Entity/Bindings/EntityBinding.h" - -#include - -struct SAIModifierRoleBinding : SEntityBinding -{ - using SEntityBinding::SEntityBinding; - - PROPERTY(bool, m_bIgnoreLowNoise); - PROPERTY(bool, m_bIgnoreSillyHitman); - PROPERTY(bool, m_bIgnoreAnnoyingHitman); - PROPERTY(bool, m_bIgnoreDistractions); - PROPERTY(bool, m_bIgnoreAccidents); - PROPERTY(bool, m_bNeverSpectate); - PROPERTY(bool, m_bConversationHelper); - PROPERTY(bool, m_bConversationHelperFast); - PROPERTY(bool, m_bWantsPrivacy); - PROPERTY(bool, m_bOneHitpoint); - PROPERTY(bool, m_bBlockDeadlyThrow); - PROPERTY(bool, m_bBlockMelee); - PROPERTY(bool, m_bSuppressSocialGreeting); -}; diff --git a/src/Entity/Bindings/SFollowPlayerHelperBinding.h b/src/Entity/Bindings/SFollowPlayerHelperBinding.h index dcca619..7adfd22 100644 --- a/src/Entity/Bindings/SFollowPlayerHelperBinding.h +++ b/src/Entity/Bindings/SFollowPlayerHelperBinding.h @@ -1,7 +1,8 @@ #pragma once #include "Entity/Bindings/EntityBinding.h" -#include "SAIModifierRoleBinding.h" +#include "SAIModifierBinding.h" +#include "Entity/BehaviorTreeMatch.h" #include @@ -12,12 +13,52 @@ struct SFollowPlayerHelperBinding : SEntityBinding { using SEntityBinding::SEntityBinding; - PROPERTY_RO(ZEntityRef, m_rAIModifierRole); // Reference to ZAIModifierRole sub-entity - MEMBER_BINDING(SAIModifierRoleBinding, m_AIModifierRoleBinding, m_rAIModifierRole); + PROPERTY_RO(ZEntityRef, m_rAIModifier); // Reference to ZAIModifier sub-entity + MEMBER_BINDING(SAIModifierBinding, m_AIModifierBinding, m_rAIModifier); - PROPERTY(float32, m_fMinTetherRange); // Minimum distance to the player before the stops walking - PROPERTY(float32, m_fMaxTetherRange); // Maximum distance to the player before the actor starts moving to catch up + PROPERTY(float32, m_fMinTetherRange); // Minimum distance to the player before the stops walking + PROPERTY(float32, m_fMaxTetherRange); // Maximum distance to the player before the actor starts moving to catch up + PROPERTY(float32, m_fMaxSightDistance); // Maximum distance at which the actor can see the player and start following + PROPERTY(EMoveSpeed, m_eMaxMoveSpeed); // Maximum speed at which the actor will move when following the player. when nearby, the actor will slow down to regular walking speed + PROPERTY(ZString, m_sMatch); // Behavior tree node name to match. Default (and lowest) ist "BT_AMBIENCE"; use "BT_OVERRIDE_ALL" to make actor ignore every other behavior. See BehaviorTreeMatch.h INPUT_PIN(StartFollowHitman); // Start following the player INPUT_PIN(StopFollowHitman); // Stop following the player + + // Start following the player, ignoring everything else (including illegal actions, distractions, etc.). + // After Stopping, the actor will have no memory of what happened during the follow. + // Must stop using StopFollowHitmanIgnoreEverything, otherwise the actor will be permanently stuck in follow mode. + inline void StartFollowHitmanIgnoreEverything() + { + if (auto s_AIModifier = m_AIModifierBinding; s_AIModifier) + { + s_AIModifier.m_bIgnoreDistractions = true; + s_AIModifier.m_bIgnoreAccidents = true; + s_AIModifier.m_bIgnoreDeadBodies = true; + s_AIModifier.m_bDisableDeadBodySensor = true; + s_AIModifier.m_bIgnoreWeapons = true; + s_AIModifier.m_bIgnoreTrespassing = true; + s_AIModifier.m_bDeafAndBlind = true; + } + + m_sMatch = BehaviorTreeMatch::BT_OVERRIDE_ALL; + StartFollowHitman(); + } + + inline void StopFollowHitmanIgnoreEverything() + { + if (auto s_AIModifier = m_AIModifierBinding; s_AIModifier) + { + s_AIModifier.m_bIgnoreDistractions = false; + s_AIModifier.m_bIgnoreAccidents = false; + s_AIModifier.m_bIgnoreDeadBodies = false; + s_AIModifier.m_bDisableDeadBodySensor = false; + s_AIModifier.m_bIgnoreWeapons = false; + s_AIModifier.m_bIgnoreTrespassing = false; + s_AIModifier.m_bDeafAndBlind = false; + } + + m_sMatch = BehaviorTreeMatch::BT_AMBIENCE; + StopFollowHitman(); + } }; From 66c3ad8fba87e25fcb2ea7cfbc05d46e6d366e69 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Sun, 12 Jul 2026 11:51:06 +0200 Subject: [PATCH 2/6] add situation type to act library bindings; add movement and situation type to act library dbg effect --- src/Effects/Dbg/ZActLibraryDbgEffect.cpp | 215 ++++++++++-------- .../SFlamingoDanceActEntityBinding.h | 1 + .../ActLibrary/SLambicDanceActEntityBinding.h | 1 + .../SStandDanceMatActEntityBinding.h | 1 + .../SStandWaitingActEntityBinding.h | 1 + 5 files changed, 122 insertions(+), 97 deletions(-) diff --git a/src/Effects/Dbg/ZActLibraryDbgEffect.cpp b/src/Effects/Dbg/ZActLibraryDbgEffect.cpp index 88de99d..e2d98f7 100644 --- a/src/Effects/Dbg/ZActLibraryDbgEffect.cpp +++ b/src/Effects/Dbg/ZActLibraryDbgEffect.cpp @@ -40,58 +40,149 @@ void ZActLibraryDbgEffect::OnDrawDebugUI() DrawUIForFlamingoDance(); } -void ZActLibraryDbgEffect::DrawUIForStandWaiting() +static inline std::string_view SituationTypeToName(const ESituationAvailability p_eSituation) { - ImGui::PushID("##stand_waiting"); - - if (ImGui::CollapsingHeader("Act_MR_Stand_Waiting")) + switch (p_eSituation) { + case ESituationAvailability::ESA_AMBIENCE: + return "ESA_AMBIENCE"; + case ESituationAvailability::ESA_AMBIENCE_RESV: + return "ESA_AMBIENCE_RESV"; + case ESituationAvailability::ESA_OVR_STANDING: + return "ESA_OVR_STANDING"; + case ESituationAvailability::ESA_OVR_CURIOUS: + return "ESA_OVR_CURIOUS"; + case ESituationAvailability::ESA_OVR_SENTRY: + return "ESA_OVR_SENTRY"; + case ESituationAvailability::ESA_OVR_CAUTIOUS: + return "ESA_OVR_CAUTIOUS"; + case ESituationAvailability::ESA_OVR_COMBAT: + return "ESA_OVR_COMBAT"; + case ESituationAvailability::ESA_OVR_ALL: + return "ESA_OVR_ALL"; + default: + return ""; + } +} - auto s_Binding = GetStandWaitingBinding(m_rTargetActor); - auto s_bActive = s_Binding.m_bActive.value_or(false); - auto s_bEndOnReached = s_Binding.m_bEndOnReached.value_or(false); - auto s_fEndOnReachedDistance = s_Binding.m_fEndOnReachedDistance.value_or(0.0f); - +/** + * Draw UI for common act library bindings: + * - Active + * - Start Act + * - Cancel Act + * - Set Spatial to Player Position + * - Situation type + * - Movement type + */ +template +static inline void DrawCommonBindingUI(T& p_Binding) +{ + if constexpr (requires { p_Binding.m_bActive; }) + { + auto s_bActive = p_Binding.m_bActive.value_or(false); ImGui::Checkbox("Active", &s_bActive); - ImGui::Checkbox("End On Reached", &s_bEndOnReached); + } - if (ImGuiEx::DragFloat("End on Reached Distance", &s_fEndOnReachedDistance, 0.1f, 10.0f)) + if constexpr (requires { p_Binding.m_MovementType; }) + { + const auto s_eMovementType = p_Binding.m_MovementType.value_or(ZActBehaviorEntity_EMovementType::MT_WALK); + auto s_bMovementTypeSnap = (s_eMovementType == ZActBehaviorEntity_EMovementType::MT_SNAP); + if (ImGui::Checkbox("Movement Type MT_SNAP?", &s_bMovementTypeSnap)) { - s_Binding.m_fEndOnReachedDistance = s_fEndOnReachedDistance; + p_Binding.m_MovementType = s_bMovementTypeSnap ? ZActBehaviorEntity_EMovementType::MT_SNAP : ZActBehaviorEntity_EMovementType::MT_WALK; } + } - if (ImGui::Button("Start Act")) - { - s_Binding.Start(); - } + if constexpr (requires { p_Binding.m_eSituationType; }) + { + static const std::vector s_aSituationTypes = { + ESituationAvailability::ESA_AMBIENCE, + ESituationAvailability::ESA_AMBIENCE_RESV, + ESituationAvailability::ESA_OVR_STANDING, + ESituationAvailability::ESA_OVR_CURIOUS, + ESituationAvailability::ESA_OVR_SENTRY, + ESituationAvailability::ESA_OVR_CAUTIOUS, + ESituationAvailability::ESA_OVR_COMBAT, + ESituationAvailability::ESA_OVR_ALL, + }; + + const auto s_eSituationType = p_Binding.m_eSituationType.value_or(ESituationAvailability::ESA_AMBIENCE); + if (ImGui::BeginCombo("Situation Type", SituationTypeToName(s_eSituationType).data())) + { + for (const auto s_eType : s_aSituationTypes) + { + const bool s_bSelected = (s_eType == s_eSituationType); + if (ImGui::Selectable(SituationTypeToName(s_eType).data(), s_bSelected)) + { + p_Binding.m_eSituationType = s_eType; + } + } - if (ImGui::Button("Cancel Act")) - { - s_Binding.Cancel(); + ImGui::EndCombo(); } + } - if (ImGui::Button("Enable End-On-Reached")) + if constexpr (requires { p_Binding.Start(); }) + { + if (ImGui::Button("Start Act")) { - s_Binding.EnableEndOnReached(); + p_Binding.Start(); } + } - if (ImGui::Button("Disable End-On-Reached")) + if constexpr (requires { p_Binding.Cancel(); }) + { + if (ImGui::Button("Cancel Act")) { - s_Binding.DisableEndOnReached(); + p_Binding.Cancel(); } + } + if constexpr (requires { p_Binding.QuerySpatial(); }) + { if (ImGui::Button("Set Spatial to Player Position")) { SMatrix s_mPlayerTransform; if (Utils::GetPlayerTransform(s_mPlayerTransform)) { - if (const auto s_rWaypointSpatial = s_Binding.QuerySpatial()) + if (const auto s_rWaypointSpatial = p_Binding.QuerySpatial()) { s_rWaypointSpatial.m_pInterfaceRef->SetObjectToWorldMatrixFromEditor(s_mPlayerTransform); } } } } +} + +void ZActLibraryDbgEffect::DrawUIForStandWaiting() +{ + ImGui::PushID("##stand_waiting"); + + if (ImGui::CollapsingHeader("Act_MR_Stand_Waiting")) + { + auto s_Binding = GetStandWaitingBinding(m_rTargetActor); + auto s_bEndOnReached = s_Binding.m_bEndOnReached.value_or(false); + auto s_fEndOnReachedDistance = s_Binding.m_fEndOnReachedDistance.value_or(0.0f); + + ImGui::Checkbox("End On Reached", &s_bEndOnReached); + + if (ImGuiEx::DragFloat("End on Reached Distance", &s_fEndOnReachedDistance, 0.1f, 10.0f)) + { + s_Binding.m_fEndOnReachedDistance = s_fEndOnReachedDistance; + } + + if (ImGui::Button("Enable End-On-Reached")) + { + s_Binding.EnableEndOnReached(); + } + + if (ImGui::Button("Disable End-On-Reached")) + { + s_Binding.DisableEndOnReached(); + } + + DrawCommonBindingUI(s_Binding); + } ImGui::PopID(); } @@ -102,24 +193,11 @@ void ZActLibraryDbgEffect::DrawUIForStandDanceMat() if (ImGui::CollapsingHeader("Act_MR_Stand_Dance_Mat")) { - auto s_Binding = GetStandDanceMatBinding(m_rTargetActor); - auto s_bActive = s_Binding.m_bActive.value_or(false); auto s_bExpertMode = s_Binding.m_bExpertMode.value_or(false); - ImGui::Checkbox("Active", &s_bActive); ImGui::Checkbox("Expert Mode", &s_bExpertMode); - if (ImGui::Button("Start Act")) - { - s_Binding.Start(); - } - - if (ImGui::Button("Cancel Act")) - { - s_Binding.Cancel(); - } - if (ImGui::Button("Enable Expert Mode")) { s_Binding.SetExpertMode(); @@ -130,17 +208,7 @@ void ZActLibraryDbgEffect::DrawUIForStandDanceMat() s_Binding.SetNormalMode(); } - if (ImGui::Button("Set Spatial to Player Position")) - { - SMatrix s_mPlayerTransform; - if (Utils::GetPlayerTransform(s_mPlayerTransform)) - { - if (const auto s_rWaypointSpatial = s_Binding.QuerySpatial()) - { - s_rWaypointSpatial.m_pInterfaceRef->SetObjectToWorldMatrixFromEditor(s_mPlayerTransform); - } - } - } + DrawCommonBindingUI(s_Binding); } ImGui::PopID(); @@ -152,33 +220,9 @@ void ZActLibraryDbgEffect::DrawUIForLambicDance() if (ImGui::CollapsingHeader("Act_MR_Lambic_Dance")) { - auto s_Binding = GetLambicDanceBinding(m_rTargetActor); - auto s_bActive = s_Binding.m_bActive.value_or(false); - ImGui::Checkbox("Active", &s_bActive); - - if (ImGui::Button("Start Act")) - { - s_Binding.Start(); - } - - if (ImGui::Button("Cancel Act")) - { - s_Binding.Cancel(); - } - - if (ImGui::Button("Set Spatial to Player Position")) - { - SMatrix s_mPlayerTransform; - if (Utils::GetPlayerTransform(s_mPlayerTransform)) - { - if (const auto s_rWaypointSpatial = s_Binding.QuerySpatial()) - { - s_rWaypointSpatial.m_pInterfaceRef->SetObjectToWorldMatrixFromEditor(s_mPlayerTransform); - } - } - } + DrawCommonBindingUI(s_Binding); } ImGui::PopID(); @@ -191,37 +235,14 @@ void ZActLibraryDbgEffect::DrawUIForFlamingoDance() if (ImGui::CollapsingHeader("Act_MR_Stand_Mascot_Entertain")) { auto s_Binding = GetFlamingoDanceBinding(m_rTargetActor); - auto s_bActive = s_Binding.m_bActive.value_or(false); auto s_nMode = s_Binding.m_nMode.value_or(0); - ImGui::Checkbox("Active", &s_bActive); - if (ImGui::InputInt("Mode", &s_nMode)) { s_Binding.m_nMode = s_nMode; } - if (ImGui::Button("Start Act")) - { - s_Binding.Start(); - } - - if (ImGui::Button("Cancel Act")) - { - s_Binding.Cancel(); - } - - if (ImGui::Button("Set Spatial to Player Position")) - { - SMatrix s_mPlayerTransform; - if (Utils::GetPlayerTransform(s_mPlayerTransform)) - { - if (const auto s_rWaypointSpatial = s_Binding.QuerySpatial()) - { - s_rWaypointSpatial.m_pInterfaceRef->SetObjectToWorldMatrixFromEditor(s_mPlayerTransform); - } - } - } + DrawCommonBindingUI(s_Binding); } ImGui::PopID(); diff --git a/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h b/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h index f90aada..dac11ec 100644 --- a/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h +++ b/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h @@ -22,6 +22,7 @@ struct SFlamingoDanceActEntityBinding : public SEntityBinding PROPERTY(ZActBehaviorEntity_EMovementType, m_MovementType); // NPC movement type. either MT_WALK or MT_SNAP. Cannot be changed after Start is triggered. PROPERTY_RO(bool, m_bActive); // Whether act is active. PROPERTY_RO(TEntityRef, m_rWaypointSpatial); // Spatial that NPC will perform act at. Location cannot be changed after Start is triggered. + PROPERTY(ESituationAvailability, m_eSituationType); // Set Situation type / priority. ESA_AMBIENCE can be overriden by most other behavior, while ESA_OVR_ALL overrides all. PROPERTY(int32, m_nMode); // Flamingo dance mode / type. Not well tested yet, keep at MODE_DEFAULT to be safe. Refer to MODE_* constants for available values. Cannot be changed after Start is triggered. diff --git a/src/Entity/Bindings/ActLibrary/SLambicDanceActEntityBinding.h b/src/Entity/Bindings/ActLibrary/SLambicDanceActEntityBinding.h index 6e84a7e..625b213 100644 --- a/src/Entity/Bindings/ActLibrary/SLambicDanceActEntityBinding.h +++ b/src/Entity/Bindings/ActLibrary/SLambicDanceActEntityBinding.h @@ -14,6 +14,7 @@ struct SLambicDanceActEntityBinding : public SEntityBinding PROPERTY(ZActBehaviorEntity_EMovementType, m_MovementType); // NPC movement type. either MT_WALK or MT_SNAP. Cannot be changed after Start is triggered. PROPERTY_RO(bool, m_bActive); // Whether act is active. PROPERTY_RO(TEntityRef, m_rWaypointSpatial); // Spatial that NPC will perform act at. Location cannot be changed after Start is triggered. + PROPERTY(ESituationAvailability, m_eSituationType); // Set Situation type / priority. ESA_AMBIENCE can be overriden by most other behavior, while ESA_OVR_ALL overrides all. INPUT_PIN(Start); // Start act. NPC will stop current act and start moving towards m_rWaypointSpatial, then perform dance animation there. INPUT_PIN(Cancel); // Cancel act. NPC will resume normal behaviour. diff --git a/src/Entity/Bindings/ActLibrary/SStandDanceMatActEntityBinding.h b/src/Entity/Bindings/ActLibrary/SStandDanceMatActEntityBinding.h index abe87e3..d87a7f7 100644 --- a/src/Entity/Bindings/ActLibrary/SStandDanceMatActEntityBinding.h +++ b/src/Entity/Bindings/ActLibrary/SStandDanceMatActEntityBinding.h @@ -15,6 +15,7 @@ struct SStandDanceMatActEntityBinding : public SEntityBinding PROPERTY_RO(bool, m_bActive); // Whether act is active. PROPERTY(bool, m_bExpertMode); // Whether dance animation uses normal or expert mode. Expert mode is faster. Cannot be changed after Start is triggered. PROPERTY_RO(TEntityRef, m_rWaypointSpatial); // Spatial that NPC will perform act at. Location cannot be changed after Start is triggered. + PROPERTY(ESituationAvailability, m_eSituationType); // Set Situation type / priority. ESA_AMBIENCE can be overriden by most other behavior, while ESA_OVR_ALL overrides all. INPUT_PIN(Start); // Start act. NPC will stop current act and start moving towards m_rWaypointSpatial, then perform dance animation there. If m_bExpertMode is true, NPC will perform expert mode dance animation, which is faster than normal mode. INPUT_PIN(Cancel); // Cancel act. NPC will resume normal behaviour. diff --git a/src/Entity/Bindings/ActLibrary/SStandWaitingActEntityBinding.h b/src/Entity/Bindings/ActLibrary/SStandWaitingActEntityBinding.h index 86a3a88..8862708 100644 --- a/src/Entity/Bindings/ActLibrary/SStandWaitingActEntityBinding.h +++ b/src/Entity/Bindings/ActLibrary/SStandWaitingActEntityBinding.h @@ -16,6 +16,7 @@ struct SStandWaitingActEntityBinding : public SEntityBinding PROPERTY_RO(bool, m_bEndOnReached); // Whether end-on-reached is enabled. PROPERTY(float32, m_fEndOnReachedDistance); // Distance for end-on-reached. PROPERTY_RO(TEntityRef, m_rWaypointSpatial); // Spatial that NPC will perform act at. Location cannot be changed after Start is triggered. + PROPERTY(ESituationAvailability, m_eSituationType); // Set Situation type / priority. ESA_AMBIENCE can be overriden by most other behavior, while ESA_OVR_ALL overrides all. INPUT_PIN(Start); // Start act. NPC will stop current act and start moving towards m_rWaypointSpatial, then wait there. INPUT_PIN(Cancel); // Cancel act. NPC will resume normal behaviour. From 1725c045545d53b4a92ba4ccaad4c43d6649bb09 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Sun, 12 Jul 2026 11:52:53 +0200 Subject: [PATCH 3/6] autoformat --- src/Entity/BehaviorTreeMatch.h | 2 +- .../Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h | 2 +- src/Entity/Bindings/SAIModifierBinding.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Entity/BehaviorTreeMatch.h b/src/Entity/BehaviorTreeMatch.h index 111db46..9a1b21f 100644 --- a/src/Entity/BehaviorTreeMatch.h +++ b/src/Entity/BehaviorTreeMatch.h @@ -8,7 +8,7 @@ namespace BehaviorTreeMatch static const ZString BT_AMBIENCE("BT_AMBIENCE"); // lowest priority static const ZString BT_OVERRIDE("BT_OVERRIDE"); - static const ZString BT_OVERRIDE_STANDING("BT_OVERRIDE_STANDING"); + static const ZString BT_OVERRIDE_STANDING("BT_OVERRIDE_STANDING"); static const ZString BT_OVERRIDE_CURIOUS("BT_OVERRIDE_CURIOUS"); static const ZString BT_OVERRIDE_CAUTIOUS("BT_OVERRIDE_CAUTIOUS"); static const ZString BT_OVERRIDE_SENTRY("BT_OVERRIDE_SENTRY"); diff --git a/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h b/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h index dac11ec..ad1cae2 100644 --- a/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h +++ b/src/Entity/Bindings/ActLibrary/SFlamingoDanceActEntityBinding.h @@ -22,7 +22,7 @@ struct SFlamingoDanceActEntityBinding : public SEntityBinding PROPERTY(ZActBehaviorEntity_EMovementType, m_MovementType); // NPC movement type. either MT_WALK or MT_SNAP. Cannot be changed after Start is triggered. PROPERTY_RO(bool, m_bActive); // Whether act is active. PROPERTY_RO(TEntityRef, m_rWaypointSpatial); // Spatial that NPC will perform act at. Location cannot be changed after Start is triggered. - PROPERTY(ESituationAvailability, m_eSituationType); // Set Situation type / priority. ESA_AMBIENCE can be overriden by most other behavior, while ESA_OVR_ALL overrides all. + PROPERTY(ESituationAvailability, m_eSituationType); // Set Situation type / priority. ESA_AMBIENCE can be overriden by most other behavior, while ESA_OVR_ALL overrides all. PROPERTY(int32, m_nMode); // Flamingo dance mode / type. Not well tested yet, keep at MODE_DEFAULT to be safe. Refer to MODE_* constants for available values. Cannot be changed after Start is triggered. diff --git a/src/Entity/Bindings/SAIModifierBinding.h b/src/Entity/Bindings/SAIModifierBinding.h index bcd444f..001918b 100644 --- a/src/Entity/Bindings/SAIModifierBinding.h +++ b/src/Entity/Bindings/SAIModifierBinding.h @@ -30,6 +30,6 @@ struct SAIModifierBinding : public SEntityBinding PROPERTY(bool, m_bBlockDeath); PROPERTY(bool, m_bSuppressSocialGreeting); - INPUT_PIN(Set); // Set the modifiers - INPUT_PIN(Clear); // Clear the modifiers + INPUT_PIN(Set); // Set the modifiers + INPUT_PIN(Clear); // Clear the modifiers }; From fa815e115fd62878b3b2b54c70da6da398ff02c0 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:25:48 +0200 Subject: [PATCH 4/6] ZActorsDanceEffect: make actors ignore player actions while active --- src/Effects/Actors/ZActorsDanceEffect.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Effects/Actors/ZActorsDanceEffect.cpp b/src/Effects/Actors/ZActorsDanceEffect.cpp index ad466af..236e3ab 100644 --- a/src/Effects/Actors/ZActorsDanceEffect.cpp +++ b/src/Effects/Actors/ZActorsDanceEffect.cpp @@ -45,6 +45,9 @@ void ZActorsDanceEffect::SetAllActorsDancing(const bool p_bDancing) // move target spatial to actor position s_rActSpatial.m_pInterfaceRef->SetObjectToWorldMatrixFromEditor(s_rActorSpatial.m_pInterfaceRef->GetObjectToWorldMatrix()); + // ignore everything else + s_Helper.m_eSituationType = ESituationAvailability::ESA_OVR_ALL; + // start act, without snapping to pos s_Helper.m_MovementType = ZActBehaviorEntity_EMovementType::MT_WALK; s_Helper.Start(); @@ -72,6 +75,9 @@ void ZActorsDanceEffect::SetAllActorsDancing(const bool p_bDancing) // randomly set expert mode for 20% of actors s_Helper.m_bExpertMode = Math::GetRandomBool(0.2f); + // ignore everything else + s_Helper.m_eSituationType = ESituationAvailability::ESA_OVR_ALL; + // start act, without snapping to pos s_Helper.m_MovementType = ZActBehaviorEntity_EMovementType::MT_WALK; s_Helper.Start(); @@ -100,6 +106,9 @@ void ZActorsDanceEffect::SetAllActorsDancing(const bool p_bDancing) // TODO: use different modes ??? s_Helper.m_nMode = SFlamingoDanceActEntityBinding::MODE_DEFAULT; + // ignore everything else + s_Helper.m_eSituationType = ESituationAvailability::ESA_OVR_ALL; + // start act, without snapping to pos s_Helper.m_MovementType = ZActBehaviorEntity_EMovementType::MT_WALK; s_Helper.Start(); From a7f5071d28cc580bee240d3d394c6c4a11162b63 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Sun, 12 Jul 2026 12:26:09 +0200 Subject: [PATCH 5/6] ZActorsFollowPlayerEffect: add variation where all player actions are ignored --- .../Actors/ZActorsFollowPlayerEffect.cpp | 24 ++++++++++++++++--- .../Actors/ZActorsFollowPlayerEffect.h | 21 +++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp b/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp index bbe58f2..813965e 100644 --- a/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp +++ b/src/Effects/Actors/ZActorsFollowPlayerEffect.cpp @@ -31,15 +31,33 @@ void ZActorsFollowPlayerEffect::SetActorsFollowPlayer(const bool p_bFollow) s_FollowHelper.m_fMinTetherRange = 2.f; s_FollowHelper.m_fMaxTetherRange = 5.f; + // go extra fast to be a lot more unsetteling + s_FollowHelper.m_eMaxMoveSpeed = EMoveSpeed::MS_Flash; + // go! - s_FollowHelper.StartFollowHitman(); + if (m_bActorsIgnoreAllElse) + { + s_FollowHelper.StartFollowHitmanIgnoreEverything(); + } + else + { + s_FollowHelper.StartFollowHitman(); + } } else { - s_FollowHelper.StopFollowHitman(); + if (m_bActorsIgnoreAllElse) + { + s_FollowHelper.StopFollowHitmanIgnoreEverything(); + } + else + { + s_FollowHelper.StopFollowHitman(); + } } } } } -REGISTER_CHAOS_EFFECT(ZActorsFollowPlayerEffect); +REGISTER_CHAOS_EFFECT_PARAM(normal, ZActorsFollowPlayerEffect, /* Ignore All Else? */ false); +REGISTER_CHAOS_EFFECT_PARAM(nobrain, ZActorsFollowPlayerEffect, /* Ignore All Else? */ true); diff --git a/src/Effects/Actors/ZActorsFollowPlayerEffect.h b/src/Effects/Actors/ZActorsFollowPlayerEffect.h index 7d62ddd..10e72a7 100644 --- a/src/Effects/Actors/ZActorsFollowPlayerEffect.h +++ b/src/Effects/Actors/ZActorsFollowPlayerEffect.h @@ -4,14 +4,33 @@ class ZActorsFollowPlayerEffect : public ZActorFollowPlayerHelperEffectBase { public: + ZActorsFollowPlayerEffect(const bool p_bIgnoreAllElse) + : ZActorFollowPlayerHelperEffectBase(), + m_bActorsIgnoreAllElse(p_bIgnoreAllElse) + { + } + void Start() override; void Stop() override; + std::string GetName() const override + { + const auto s_sSuffix = m_bActorsIgnoreAllElse ? "_nobrain" : "_normal"; + return IChaosEffect::GetName() + s_sSuffix; + } + std::string GetDisplayName(const bool p_bVoting) const override { - return "You Are Famous"; + return m_bActorsIgnoreAllElse ? "The Walking Dead" : "You Are Famous"; + } + + EDuration GetDuration() const override + { + return m_bActorsIgnoreAllElse ? EDuration::Short : EDuration::Full; } private: + const bool m_bActorsIgnoreAllElse; + void SetActorsFollowPlayer(const bool p_bFollow); }; From b9cf13c7c470b071b6fe35b09c84f42051848e24 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Sun, 12 Jul 2026 15:17:57 +0200 Subject: [PATCH 6/6] change BehaviorTreeMatch constants to inline const --- src/Entity/BehaviorTreeMatch.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Entity/BehaviorTreeMatch.h b/src/Entity/BehaviorTreeMatch.h index 9a1b21f..2a09081 100644 --- a/src/Entity/BehaviorTreeMatch.h +++ b/src/Entity/BehaviorTreeMatch.h @@ -5,14 +5,14 @@ /// Extract using: strings HITMAN3.exe | grep '^BT_' namespace BehaviorTreeMatch { - static const ZString BT_AMBIENCE("BT_AMBIENCE"); // lowest priority + inline const ZString BT_AMBIENCE("BT_AMBIENCE"); // lowest priority - static const ZString BT_OVERRIDE("BT_OVERRIDE"); - static const ZString BT_OVERRIDE_STANDING("BT_OVERRIDE_STANDING"); - static const ZString BT_OVERRIDE_CURIOUS("BT_OVERRIDE_CURIOUS"); - static const ZString BT_OVERRIDE_CAUTIOUS("BT_OVERRIDE_CAUTIOUS"); - static const ZString BT_OVERRIDE_SENTRY("BT_OVERRIDE_SENTRY"); - static const ZString BT_CLOSECOMBAT_ONLY("BT_CLOSECOMBAT_ONLY"); + inline const ZString BT_OVERRIDE("BT_OVERRIDE"); + inline const ZString BT_OVERRIDE_STANDING("BT_OVERRIDE_STANDING"); + inline const ZString BT_OVERRIDE_CURIOUS("BT_OVERRIDE_CURIOUS"); + inline const ZString BT_OVERRIDE_CAUTIOUS("BT_OVERRIDE_CAUTIOUS"); + inline const ZString BT_OVERRIDE_SENTRY("BT_OVERRIDE_SENTRY"); + inline const ZString BT_CLOSECOMBAT_ONLY("BT_CLOSECOMBAT_ONLY"); - static const ZString BT_OVERRIDE_ALL("BT_OVERRIDE_ALL"); // highest priority, will make the actor ignore every other behavior + inline const ZString BT_OVERRIDE_ALL("BT_OVERRIDE_ALL"); // highest priority, will make the actor ignore every other behavior } // namespace BehaviorTreeMatch \ No newline at end of file