From 6b8c86a03c8b119a143397731e6cd48abef50d94 Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 17 Mar 2025 15:35:51 -0400 Subject: [PATCH] Working on look-at Widgets, not done yet. --- Content/Luprex/lxGameMode.uasset | 4 +- Content/Luprex/lxUtilityMacroLibrary.uasset | 4 +- .../Integration/IntegrationGameModeBase.cpp | 24 ++++++--- Source/Integration/IntegrationGameModeBase.h | 34 ++++++++----- Source/Integration/LuaCall.cpp | 38 +++++++------- Source/Integration/Tangible.cpp | 50 +++++++++++++------ Source/Integration/Tangible.h | 13 ++++- 7 files changed, 107 insertions(+), 60 deletions(-) mode change 100755 => 100644 Content/Luprex/lxUtilityMacroLibrary.uasset diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index f8f28a0f..979a1014 100644 --- a/Content/Luprex/lxGameMode.uasset +++ b/Content/Luprex/lxGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3388383be8e9d8cf2cf5ba6f115be6e15578c8a1953aea7d16052a91b06a8cf -size 123371 +oid sha256:df608f4a88a1490a5ab407def1f8ceb4b3cf0cd630642c2fde945ce9e18465e6 +size 116280 diff --git a/Content/Luprex/lxUtilityMacroLibrary.uasset b/Content/Luprex/lxUtilityMacroLibrary.uasset old mode 100755 new mode 100644 index dc0014c8..32d991d5 --- a/Content/Luprex/lxUtilityMacroLibrary.uasset +++ b/Content/Luprex/lxUtilityMacroLibrary.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5014684a07eaa726dcda56c5a876af3e0f03d27a2edb1101d8c24b480802c4c4 -size 15290 +oid sha256:ac178219b6e91d02f78602e70da47ba8f361f86e41b15888fac6453952ca6829 +size 19388 diff --git a/Source/Integration/IntegrationGameModeBase.cpp b/Source/Integration/IntegrationGameModeBase.cpp index c860c31b..25f77384 100644 --- a/Source/Integration/IntegrationGameModeBase.cpp +++ b/Source/Integration/IntegrationGameModeBase.cpp @@ -182,7 +182,12 @@ void AIntegrationGameModeBase::LuaCallEnd(InvocationKind kind, AActor *place) { if (place == nullptr) { LuaCallEnd(kind, int64(0)); } else { - LuaCallEnd(kind, UlxTangible::GetActorTangible(place)->TangibleId); + UlxTangible *tan = UlxTangible::GetActorTangibleOrLog(place); + if (tan == nullptr) { + LuaCallResult.clear(); + } else { + LuaCallEnd(kind, tan->TangibleId); + } } } @@ -334,16 +339,17 @@ int64 AIntegrationGameModeBase::GetPlayerId() { return PlayerId; } -AIntegrationGameModeBase *AIntegrationGameModeBase::GetFromWorld(UWorld *world) { - AIntegrationGameModeBase *result = world->GetAuthGameMode(); +AIntegrationGameModeBase *AIntegrationGameModeBase::GetLuprexGameMode(UObject *context) { + AIntegrationGameModeBase *result = context->GetWorld()->GetAuthGameMode(); if (result == nullptr) { - UE_LOG(LogBlueprint, Fatal, TEXT("No IntegrationGameModeBase in this context")); + UE_LOG(LogBlueprint, Fatal, TEXT("Not currently using a Luprex Game Mode.")); } return result; } -AIntegrationGameModeBase *AIntegrationGameModeBase::GetFromContext(UObject *context) { - return GetFromWorld(context->GetWorld()); +bool AIntegrationGameModeBase::IsLookAtChanged(UObject *context) { + AIntegrationGameModeBase *mode = GetLuprexGameMode(context); + return mode->CurrentLookAt.HitObjectHandle != mode->PreviousLookAt.HitObjectHandle; } void AIntegrationGameModeBase::UpdateLookAt() { @@ -362,6 +368,10 @@ void AIntegrationGameModeBase::UpdateLookAt() { APlayerCameraManager *cam = pc->PlayerCameraManager; if (cam == nullptr) return; - CalculateLookAt(pawn, pc, cam); + CalculateLookAt(pc); + + if (IsLookAtChanged(this)) { + LookAtChanged(); + } } diff --git a/Source/Integration/IntegrationGameModeBase.h b/Source/Integration/IntegrationGameModeBase.h index 2312ed32..42032f31 100644 --- a/Source/Integration/IntegrationGameModeBase.h +++ b/Source/Integration/IntegrationGameModeBase.h @@ -46,17 +46,32 @@ public: UFUNCTION(BlueprintCallable, Category = "Luprex|Miscellaneous") int64 GetPlayerId(); - UFUNCTION(BlueprintCallable, Category = "Luprex|Look-At Detection") - void SetLookAt(const FHitResult &hit) { CurrentLookAt = hit; } + UFUNCTION(BlueprintPure, meta = (CompactNodeTitle="LxGameMode", WorldContext = "context"), Category = "Luprex|Miscellaneous") + static AIntegrationGameModeBase *GetLuprexGameMode(UObject *context); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"),Category = "Luprex|Look-At Detection") + static void SetLookAt(UObject *context, const FHitResult &hit) { GetLuprexGameMode(context)->CurrentLookAt = hit; } - UFUNCTION(BlueprintCallable, Category = "Luprex|Look-At Detection") - const FHitResult &GetLookAt() const { return CurrentLookAt; } + UFUNCTION(BlueprintPure, meta = (WorldContext = "context"),Category = "Luprex|Look-At Detection") + static const FHitResult &GetLookAt(UObject *context) { return GetLuprexGameMode(context)->CurrentLookAt; } - UFUNCTION(BlueprintCallable, Category = "Luprex|Look-At Detection") - bool LookAtChanged() const { return CurrentLookAt.HitObjectHandle != PreviousLookAt.HitObjectHandle; } + UFUNCTION(BlueprintPure, meta = (WorldContext = "context"),Category = "Luprex|Look-At Detection") + static const AActor *GetLookAtActor(UObject *context) { return GetLuprexGameMode(context)->CurrentLookAt.GetActor(); } + + UFUNCTION(BlueprintPure, meta = (WorldContext = "context"),Category = "Luprex|Look-At Detection") + static const FHitResult &GetPreviousLookAt(UObject *context) { return GetLuprexGameMode(context)->PreviousLookAt; } + + UFUNCTION(BlueprintPure, meta = (WorldContext = "context"),Category = "Luprex|Look-At Detection") + static const AActor *GetPreviousLookAtActor(UObject *context) { return GetLuprexGameMode(context)->PreviousLookAt.GetActor(); } + + UFUNCTION(BlueprintPure, meta = (WorldContext = "context"),Category = "Luprex|Look-At Detection") + static bool IsLookAtChanged(UObject *context); + + UFUNCTION(BlueprintImplementableEvent, Category = "Luprex|Look-At Detection") + void CalculateLookAt(APlayerController *PlayerController); UFUNCTION(BlueprintImplementableEvent, Category = "Luprex|Look-At Detection") - void CalculateLookAt(AActor *Player, APlayerController *PlayerController, APlayerCameraManager *Camera); + void LookAtChanged(); // Assemble a lua call. To call into lua: // @@ -103,11 +118,6 @@ public: // to update luprex sockets and update luprex itself. virtual uint32 Run() override; - // Get the AIntegrationGameModeBase given any UObject - // If there is no AIntegrationGameModeBase in that world context, fatal error - static AIntegrationGameModeBase *GetFromWorld(UWorld *world); - static AIntegrationGameModeBase *GetFromContext(UObject *context); - UPROPERTY() UlxTangibleManager *TangibleManager; diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 3ca4475c..6f51fcc9 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -204,7 +204,7 @@ FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix) ///////////////////////////////////////////////////////////////// void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); mode->LuaCallBegin(); sb.write_string(cname); @@ -213,7 +213,7 @@ void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, con void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); mode->LuaCallEnd(InvocationKind::LUA_INVOKE, place); @@ -221,7 +221,7 @@ void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); @@ -229,7 +229,7 @@ void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) { void UlxLuaCallLibrary::InvokeEngioMove(UObject *context, const FString &action, const FVector &xyz, double facing) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallBegin(); sb.write_string("engio"); sb.write_string("move"); @@ -244,7 +244,7 @@ void UlxLuaCallLibrary::InvokeEngioMove(UObject *context, const FString &action, ELpxSimpleDynamicTag UlxLuaCallLibrary::LuaCallNextResultType(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); if (sb.empty()) return ELpxSimpleDynamicTag::None; int64_t total_reads = sb.total_reads(); @@ -267,7 +267,7 @@ ELpxSimpleDynamicTag UlxLuaCallLibrary::LuaCallNextResultType(UObject *context) ///////////////////////////////////////////////////////////////// void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); @@ -281,7 +281,7 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam if ((tokvalue == 0) && !namestr.empty()) { FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); } - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); @@ -289,7 +289,7 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam } void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); @@ -297,7 +297,7 @@ void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { } void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); @@ -305,7 +305,7 @@ void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { } void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &pvector) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); @@ -313,7 +313,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector & } void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector2D &pvector) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); @@ -323,7 +323,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector } void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); @@ -339,7 +339,7 @@ void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { FString UlxLuaCallLibrary::LuaCallReturnValue_string(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::STRING) FatalBlueprintError(TEXT("expected lua to return a string")); @@ -347,7 +347,7 @@ FString UlxLuaCallLibrary::LuaCallReturnValue_string(UObject *context) { } FName UlxLuaCallLibrary::LuaCallReturnValue_name(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::TOKEN) FatalBlueprintError(TEXT("expected lua to return a name")); @@ -355,7 +355,7 @@ FName UlxLuaCallLibrary::LuaCallReturnValue_name(UObject *context) { } double UlxLuaCallLibrary::LuaCallReturnValue_float(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::NUMBER) FatalBlueprintError(TEXT("expected lua to return a float")); @@ -363,7 +363,7 @@ double UlxLuaCallLibrary::LuaCallReturnValue_float(UObject *context) { } int UlxLuaCallLibrary::LuaCallReturnValue_int(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::NUMBER) FatalBlueprintError(TEXT("expected lua to return a number")); @@ -371,7 +371,7 @@ int UlxLuaCallLibrary::LuaCallReturnValue_int(UObject *context) { } FVector UlxLuaCallLibrary::LuaCallReturnValue_vector(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::VECTOR) FatalBlueprintError(TEXT("expected lua to return a vector")); @@ -379,7 +379,7 @@ FVector UlxLuaCallLibrary::LuaCallReturnValue_vector(UObject *context) { } FVector2D UlxLuaCallLibrary::LuaCallReturnValue_vector2d(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::VECTOR) FatalBlueprintError(TEXT("expected lua to return a vector")); @@ -388,7 +388,7 @@ FVector2D UlxLuaCallLibrary::LuaCallReturnValue_vector2d(UObject *context) { } bool UlxLuaCallLibrary::LuaCallReturnValue_boolean(UObject *context) { - AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetLuprexGameMode(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::BOOLEAN) FatalBlueprintError(TEXT("expected lua to return a boolean")); diff --git a/Source/Integration/Tangible.cpp b/Source/Integration/Tangible.cpp index eed894b5..335d4cbc 100644 --- a/Source/Integration/Tangible.cpp +++ b/Source/Integration/Tangible.cpp @@ -141,25 +141,32 @@ void UlxTangible::Destroy() { NearAccordingToUnreal = false; } -UlxTangible *UlxTangible::GetActorTangible(AActor *actor) { +UlxTangible *UlxTangible::GetActorTangibleQuiet(AActor *actor) { UlxTangibleComponent* comp = actor->GetComponentByClass(); - check(comp != nullptr); - UlxTangible *result = comp->Tangible.Get(); - check(result != nullptr); - return result; + if (comp == nullptr) return nullptr; + return comp->Tangible.Get(); +} + +UlxTangible *UlxTangible::GetActorTangibleOrLog(AActor *actor) { + UlxTangible *tan = GetActorTangibleQuiet(actor); + if (tan == nullptr) { + UE_LOG(LogBlueprint, Error, TEXT("Not a luprex tangible: %s"), *actor->GetName()); + } + return tan; } void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) { - step = GetActorTangible(target)->AnimTracker.GetCurrentAnimation(); + UlxTangible *tan = GetActorTangibleOrLog(target); + if (tan == nullptr) { + step = FlxAnimationStep(); + return; + } + step = tan->AnimTracker.GetCurrentAnimation(); } void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool AutoUpdate) { - if (target == nullptr) - { - UE_LOG(LogBlueprint, Error, TEXT("In FinishedAnimation, tangible cannot be null")); - return; - } - UlxTangible *tan = GetActorTangible(target); + UlxTangible *tan = GetActorTangibleOrLog(target); + if (tan == nullptr) return; if (AutoUpdate) { step.AutoUpdateXYZ(target); @@ -172,21 +179,32 @@ void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step } FString UlxTangible::GetTangiblePlane(AActor* target) { - return GetActorTangible(target)->Plane.ToString(); + UlxTangible *tan = GetActorTangibleOrLog(target); + if (tan == nullptr) return TEXT(""); + return tan->Plane.ToString(); } void UlxTangible::SetTangiblePlane(AActor* target, const FString& plane) { - GetActorTangible(target)->Plane = FName(plane); + UlxTangible *tan = GetActorTangibleOrLog(target); + if (tan == nullptr) return; + tan->Plane = FName(plane); } bool UlxTangible::IsCurrentPlayer(AActor* target) { - UlxTangible *tan = GetActorTangible(target); + UlxTangible *tan = GetActorTangibleQuiet(target); + if (tan == nullptr) return false; AIntegrationGameModeBase *gamemode = tan->Manager->GetGameMode(); return (tan->TangibleId == gamemode->PlayerId); } +bool UlxTangible::IsLuprexTangible(AActor* target) { + UlxTangible *tan = GetActorTangibleQuiet(target); + return tan != nullptr; +} + void UlxTangible::SetAutoFinish(AActor *target, const FString &action, const FVector &xyz) { - UlxTangible *tan = GetActorTangible(target); + UlxTangible *tan = GetActorTangibleOrLog(target); + if (tan == nullptr) return; tan->AnimTracker.SetAutoFinish(action, xyz); } diff --git a/Source/Integration/Tangible.h b/Source/Integration/Tangible.h index d30b302c..e4844c41 100644 --- a/Source/Integration/Tangible.h +++ b/Source/Integration/Tangible.h @@ -115,7 +115,13 @@ public: // Convert an actor to a tangible. // - static UlxTangible *GetActorTangible(AActor *actor); + // The logging version of this function will generate a log + // message if the actor is not a tangible, and it will then return + // nullptr. The quiet version will just return nullptr. + // + static UlxTangible *GetActorTangibleQuiet(AActor *actor); + static UlxTangible *GetActorTangibleOrLog(AActor *actor); + private: // Set the actor's blueprint, and recreate the actor if necessary. @@ -143,8 +149,11 @@ public: UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible") static void SetTangiblePlane(AActor* target, const FString& plane); - UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible") + UFUNCTION(BlueprintPure, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible") static bool IsCurrentPlayer(AActor *target); + + UFUNCTION(BlueprintPure, Meta = (DefaultToSelf = "target"), Category = "Luprex|Tangible") + static bool IsLuprexTangible(AActor *target); };