Files
2025-05-18 13:04:45 +08:00

182 lines
5.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SmartObjectRuntime.h"
#include "SmartObjectComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectRuntime)
const FSmartObjectClaimHandle FSmartObjectClaimHandle::InvalidHandle = {};
namespace UE::SmartObject
{
TArray<FGameplayTag> GEnabledReasonTags;
uint16 GetMaskForEnabledReasonTag(const FGameplayTag Tag)
{
int32 Index = GEnabledReasonTags.IndexOfByKey(Tag);
if (Index == INDEX_NONE)
{
checkf(GEnabledReasonTags.Num() <= FSmartObjectRuntime::MaxNumDisableFlags,
TEXT("Too many different Tags were used to control smart object enabled state. You need to limit to %d or change to a larger type for DisableFlags in FSmartObjectRuntime."),
FSmartObjectRuntime::MaxNumDisableFlags);
Index = GEnabledReasonTags.Add(Tag);
}
return static_cast<uint16>(1 << Index);
}
}
//----------------------------------------------------------------------//
// FSmartObjectRuntime
//----------------------------------------------------------------------//
FSmartObjectRuntime::FSmartObjectRuntime(const USmartObjectDefinition& InDefinition)
: Definition(&InDefinition)
{
}
bool FSmartObjectRuntime::IsEnabledForReason(const FGameplayTag ReasonTag) const
{
if (ensureMsgf(ReasonTag.IsValid(), TEXT("%hs expects a valid tag. If the intent is to test the enabled state regardless of the reason use IsEnabled() instead."), __FUNCTION__))
{
const uint16 TagMask = UE::SmartObject::GetMaskForEnabledReasonTag(ReasonTag);
return (DisableFlags & TagMask) == 0;
}
return DisableFlags == 0;
}
void FSmartObjectRuntime::SetEnabled(const bool bEnabled, const uint16 ReasonMask)
{
if (bEnabled)
{
// Enabling so removing the flag
DisableFlags &= ~ReasonMask;
}
else
{
// Disabling so adding the flag
DisableFlags |= ReasonMask;
}
}
void FSmartObjectRuntime::SetEnabled(const FGameplayTag ReasonTag, const bool bEnabled)
{
if (ensureMsgf(ReasonTag.IsValid(), TEXT("%hs expects a valid tag."), __FUNCTION__))
{
const uint16 ReasonMask = UE::SmartObject::GetMaskForEnabledReasonTag(ReasonTag);
SetEnabled(bEnabled, ReasonMask);
}
}
bool FSmartObjectRuntime::ResolveOwnerActor() const
{
if (OwnerComponent.IsExplicitlyNull())
{
if (const FSmartObjectActorOwnerData* ActorOwnerData = OwnerData.GetPtr<const FSmartObjectActorOwnerData>())
{
// Fetching the actor from the handle will either return the cached actor if already available
// or will try to create one synchronously.
// This actor is then expected to own a SmartObjectComponent that will register itself to the subsystem.
// On successful registration the subsystem will update the OwnerComponent of the current runtime object.
if (ActorOwnerData->Handle.FetchActor() != nullptr)
{
ensureMsgf(!OwnerComponent.IsExplicitlyNull(), TEXT("Successfully resolved actor is expected to register its smartobject component for '%s'."), *LexToString(GetRegisteredHandle()));
return true;
}
}
// Unable to find owner data or to fetch actor from the Actor instance handle
return false;
}
// Success since it was already resolved
return true;
}
AActor* FSmartObjectRuntime::GetOwnerActor(const ETrySpawnActorIfDehydrated TrySpawnActorIfDehydrated) const
{
const USmartObjectComponent* Component = GetOwnerComponent(TrySpawnActorIfDehydrated);
return Component != nullptr ? Component->GetOwner() : nullptr;
}
USmartObjectComponent* FSmartObjectRuntime::GetOwnerComponent(const ETrySpawnActorIfDehydrated TrySpawnActorIfDehydrated) const
{
if (TrySpawnActorIfDehydrated == ETrySpawnActorIfDehydrated::Yes)
{
ResolveOwnerActor();
}
return OwnerComponent.Get();
}
#if WITH_SMARTOBJECT_DEBUG
FString FSmartObjectRuntime::DebugGetDisableFlagsString() const
{
FStringBuilderBase DisableFlagsStringBuilder;
for (int TagIndex = 0; TagIndex < MaxNumDisableFlags; ++TagIndex)
{
const uint16 TagMask = (uint16)(1 << TagIndex);
if (!!(DisableFlags & TagMask))
{
check(UE::SmartObject::GEnabledReasonTags.IsValidIndex(TagIndex));
DisableFlagsStringBuilder += DisableFlagsStringBuilder.Len() ? TEXT(", ") : TEXT("Disabled by: ");
DisableFlagsStringBuilder += UE::SmartObject::GEnabledReasonTags[TagIndex].ToString();
}
}
return DisableFlagsStringBuilder.ToString();
}
#endif // WITH_SMARTOBJECT_DEBUG
//----------------------------------------------------------------------//
// FSmartObjectRuntimeSlot
//----------------------------------------------------------------------//
bool FSmartObjectRuntimeSlot::Claim(const FSmartObjectUserHandle& InUser, ESmartObjectClaimPriority ClaimPriority)
{
if (CanBeClaimed(ClaimPriority))
{
State = ESmartObjectSlotState::Claimed;
User = InUser;
ClaimedPriority = ClaimPriority;
return true;
}
return false;
}
bool FSmartObjectRuntimeSlot::Release(const FSmartObjectClaimHandle& ClaimHandle, const bool bAborted)
{
if (!ensureMsgf(ClaimHandle.IsValid(), TEXT("Attempting to release a slot using an invalid handle: %s"), *LexToString(ClaimHandle)))
{
return false;
}
bool bReleased = false;
if (State != ESmartObjectSlotState::Claimed && State != ESmartObjectSlotState::Occupied)
{
UE_LOG(LogSmartObject, Error, TEXT("Expected slot state is 'Claimed' or 'Occupied' but current state is '%s'. Slot will not be released"),
*UEnum::GetValueAsString(State));
}
else if (ClaimHandle.UserHandle != User)
{
UE_LOG(LogSmartObject, Error, TEXT("User '%s' is trying to release slot claimed or used by other user '%s'. Slot will not be released"),
*LexToString(ClaimHandle.UserHandle), *LexToString(User));
}
else
{
if (bAborted)
{
const bool bFunctionWasExecuted = OnSlotInvalidatedDelegate.ExecuteIfBound(ClaimHandle, State);
UE_LOG(LogSmartObject, Verbose, TEXT("Slot invalidated callback was%scalled for %s"), bFunctionWasExecuted ? TEXT(" ") : TEXT(" not "), *LexToString(ClaimHandle));
}
State = ESmartObjectSlotState::Free;
User.Invalidate();
UserData.Reset();
ClaimedPriority = ESmartObjectClaimPriority::None;
bReleased = true;
}
return bReleased;
}