2023-09-26 17:00:30 -04:00
|
|
|
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "Tangible.h"
|
|
|
|
|
#include "TangibleManager.h"
|
2025-03-17 18:04:55 -04:00
|
|
|
#include "LuprexGameModeBase.h"
|
2023-09-26 17:00:30 -04:00
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
#define DEFAULT_BLUEPRINT (TEXT("StaticMesh"))
|
2024-01-24 14:51:21 -05:00
|
|
|
|
2023-09-26 17:00:30 -04:00
|
|
|
UlxTangible::UlxTangible()
|
|
|
|
|
{
|
2023-09-28 14:32:48 -04:00
|
|
|
Manager = nullptr;
|
2023-09-26 19:26:09 -04:00
|
|
|
TangibleId = -1;
|
2023-09-28 14:32:48 -04:00
|
|
|
CurrentActor = nullptr;
|
2024-01-24 14:51:21 -05:00
|
|
|
ActorBlueprintName = TEXT("");
|
2023-09-26 19:26:09 -04:00
|
|
|
NearAccordingToLuprex = false;
|
2023-09-28 14:32:48 -04:00
|
|
|
NearAccordingToUnreal = false;
|
2023-09-26 17:00:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxTangible::Init(UlxTangibleManager* tm, int64 id)
|
|
|
|
|
{
|
|
|
|
|
Manager = tm;
|
|
|
|
|
TangibleId = id;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
void UlxTangible::DeleteCurrentActor()
|
|
|
|
|
{
|
|
|
|
|
if (CurrentActor == nullptr) return;
|
|
|
|
|
|
|
|
|
|
// Remove the tangible component. This is probably
|
|
|
|
|
// unnecessary, but it makes it more likely that we'll
|
|
|
|
|
// catch bugs early.
|
|
|
|
|
UlxTangibleComponent* comp = CurrentActor->GetComponentByClass<UlxTangibleComponent>();
|
|
|
|
|
if (comp != nullptr) {
|
|
|
|
|
comp->DestroyComponent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now destroy the actor itself. According to various
|
|
|
|
|
// documents I've read online, it may be necessary to take
|
|
|
|
|
// further steps to delete the object. Not clear.
|
|
|
|
|
CurrentActor->Destroy();
|
|
|
|
|
|
|
|
|
|
// If this actor previously was posessed by a player controller,
|
|
|
|
|
// then it's not posessed anymore, because there is no actor any more.
|
|
|
|
|
if (Manager->PossessedTangible == this) {
|
|
|
|
|
Manager->PossessedTangible = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CurrentActor = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
void UlxTangible::SetActorBlueprint(const FString &XName) {
|
|
|
|
|
FString Name = XName.ToLower();
|
|
|
|
|
|
2023-09-26 17:00:30 -04:00
|
|
|
// If we're already of the right class, do nothing.
|
2025-03-28 23:31:44 -04:00
|
|
|
if (ActorBlueprintName == Name) {
|
2023-09-26 17:00:30 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-23 15:33:18 -04:00
|
|
|
UClass *blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, Name);
|
2025-03-28 23:31:44 -04:00
|
|
|
if (blueprint == nullptr)
|
|
|
|
|
{
|
2025-05-23 15:33:18 -04:00
|
|
|
blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, DEFAULT_BLUEPRINT);
|
2024-01-24 14:51:21 -05:00
|
|
|
check(blueprint != nullptr);
|
|
|
|
|
}
|
2023-09-26 17:00:30 -04:00
|
|
|
|
2024-01-24 14:51:21 -05:00
|
|
|
// Update the blueprint name
|
2025-03-28 23:31:44 -04:00
|
|
|
ActorBlueprintName = Name;
|
2023-09-26 17:00:30 -04:00
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
// If there's already an actor, delete it.
|
|
|
|
|
DeleteCurrentActor();
|
|
|
|
|
|
|
|
|
|
// Now create a new actor.
|
|
|
|
|
UWorld* w = Manager->GetWorld();
|
|
|
|
|
FActorSpawnParameters params;
|
|
|
|
|
|
|
|
|
|
// Give the new actor a reasonable name.
|
|
|
|
|
params.Name = FName(*FString::Printf(TEXT("%s_%ld"), *Name, TangibleId));
|
|
|
|
|
|
|
|
|
|
// Currently, the actor is spawned at (0,0,0), which is not good.
|
|
|
|
|
// We should spawn at the actor's current location. I'll get to it
|
|
|
|
|
// eventually.
|
|
|
|
|
FTransform transform;
|
|
|
|
|
transform.SetLocation(FVector(0,0,0));
|
|
|
|
|
transform.SetRotation(FQuat(FRotator(0,0,0)));
|
|
|
|
|
|
|
|
|
|
// Create the actor at the specified location even if there's something in the way.
|
|
|
|
|
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
|
|
|
|
|
|
|
|
|
// Normally, SpawnActor runs the BeginPlay entry point. We
|
|
|
|
|
// want to delay that until after we've had a chance to set
|
|
|
|
|
// up the TangibleComponent.
|
|
|
|
|
params.bDeferConstruction = true;
|
|
|
|
|
AActor* a = w->SpawnActor(blueprint, &transform, params);
|
|
|
|
|
check(a != nullptr);
|
|
|
|
|
|
|
|
|
|
// Make sure the label and the name are the same.
|
|
|
|
|
a->SetActorLabel(params.Name.ToString());
|
|
|
|
|
|
|
|
|
|
// Insert a TangibleComponent into the actor.
|
|
|
|
|
// Link the actor to its tangible, and the tangible to its actor.
|
|
|
|
|
UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false);
|
|
|
|
|
UlxTangibleComponent* tc = Cast<UlxTangibleComponent>(ac);
|
|
|
|
|
check(tc != nullptr);
|
|
|
|
|
tc->Tangible = this;
|
|
|
|
|
CurrentActor = a;
|
|
|
|
|
|
|
|
|
|
// This executes the BeginPlay entry point. We have to do this here
|
|
|
|
|
// because we deferred it in SpawnActor.
|
|
|
|
|
a->FinishSpawning(transform, true);
|
2023-09-26 17:00:30 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-28 14:32:48 -04:00
|
|
|
void UlxTangible::UpdateAnimationQueue(std::string_view aq) {
|
|
|
|
|
AnimTracker.Update(aq);
|
2024-02-13 16:49:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxTangible::MaybeExecuteAnimStateChanged() {
|
2023-10-02 15:48:42 -04:00
|
|
|
int limit = 3;
|
2025-07-02 16:01:18 -04:00
|
|
|
|
|
|
|
|
bool AnyChange = AnimTracker.IsChanged();
|
|
|
|
|
|
2023-10-02 15:48:42 -04:00
|
|
|
while (AnimTracker.IsChanged()) {
|
|
|
|
|
if (limit == 0) break;
|
|
|
|
|
limit -= 1;
|
|
|
|
|
AnimTracker.ClearChanged();
|
2024-01-24 14:51:21 -05:00
|
|
|
FString blueprint = AnimTracker.GetCurrentBlueprintName();
|
|
|
|
|
if (blueprint.IsEmpty()) blueprint = DEFAULT_BLUEPRINT;
|
|
|
|
|
SetActorBlueprint(blueprint);
|
2024-09-24 22:56:49 -04:00
|
|
|
AActor *actor = GetActor();
|
2025-04-01 22:31:27 -04:00
|
|
|
UFunction *aqchanged = actor->GetClass()->FindFunctionByName(FName(TEXT("Animation Queue Changed")));
|
2025-07-02 16:01:18 -04:00
|
|
|
if (aqchanged != nullptr)
|
|
|
|
|
{
|
2024-09-24 22:56:49 -04:00
|
|
|
actor->ProcessEvent(aqchanged, nullptr);
|
|
|
|
|
}
|
2023-09-28 14:32:48 -04:00
|
|
|
}
|
2025-07-02 16:01:18 -04:00
|
|
|
|
|
|
|
|
// Refresh the pending animation state.
|
|
|
|
|
double Now = GetWorld()->GetTimeSeconds();
|
|
|
|
|
if (AnyChange)
|
|
|
|
|
{
|
|
|
|
|
const FlxAnimationStep *Unfinished = AnimTracker.FirstUnfinished();
|
|
|
|
|
if (Unfinished == nullptr)
|
|
|
|
|
{
|
|
|
|
|
PendingAnimationHash = 0;
|
|
|
|
|
PendingAnimationTimeout = 0.0;
|
|
|
|
|
}
|
|
|
|
|
else if (Unfinished->Hash != PendingAnimationHash)
|
|
|
|
|
{
|
|
|
|
|
PendingAnimationHash = Unfinished->Hash;
|
|
|
|
|
PendingAnimationTimeout = Now + 10.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implement the animation timeout.
|
|
|
|
|
if ((PendingAnimationHash != 0) && (Now > PendingAnimationTimeout))
|
|
|
|
|
{
|
|
|
|
|
const FlxAnimationStep *Step = AnimTracker.FindAnimation(PendingAnimationHash);
|
|
|
|
|
if (Step != nullptr)
|
|
|
|
|
{
|
|
|
|
|
FString DS = UlxAnimationStepLibrary::AnimationStepDebugString(*Step);
|
|
|
|
|
UE_LOG(LogLuprex, Warning, TEXT("Timeout - blueprint didn't finish animation: %s"), *DS);
|
|
|
|
|
AnimTracker.FinishedAnimation(PendingAnimationHash);
|
|
|
|
|
}
|
|
|
|
|
AutoUpdatePosition();
|
|
|
|
|
PendingAnimationHash = 0;
|
|
|
|
|
PendingAnimationTimeout = 0.0;
|
|
|
|
|
}
|
2023-09-28 14:32:48 -04:00
|
|
|
}
|
|
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
FVector UlxTangible::GetLocation() const
|
|
|
|
|
{
|
2023-09-28 14:32:48 -04:00
|
|
|
if (CurrentActor == nullptr) {
|
|
|
|
|
return FVector(0,0,0);
|
|
|
|
|
} else {
|
|
|
|
|
return CurrentActor->GetActorLocation();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
void UlxTangible::Destroy()
|
|
|
|
|
{
|
|
|
|
|
DeleteCurrentActor();
|
2023-09-28 14:32:48 -04:00
|
|
|
Manager = nullptr;
|
|
|
|
|
TangibleId = -1;
|
|
|
|
|
CurrentActor = nullptr;
|
2024-01-24 14:51:21 -05:00
|
|
|
ActorBlueprintName = "";
|
2023-09-28 14:32:48 -04:00
|
|
|
AnimTracker.Clear();
|
|
|
|
|
NearAccordingToLuprex = false;
|
|
|
|
|
NearAccordingToUnreal = false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
UlxTangible *UlxTangible::GetActorTangibleQuiet(AActor *actor)
|
|
|
|
|
{
|
2025-03-19 16:01:38 -04:00
|
|
|
if (actor == nullptr) return nullptr;
|
2023-09-26 17:00:30 -04:00
|
|
|
UlxTangibleComponent* comp = actor->GetComponentByClass<UlxTangibleComponent>();
|
2025-03-17 15:35:51 -04:00
|
|
|
if (comp == nullptr) return nullptr;
|
|
|
|
|
return comp->Tangible.Get();
|
2023-09-26 17:00:30 -04:00
|
|
|
}
|
|
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
UlxTangible *UlxTangible::GetActorTangibleOrLog(AActor *actor)
|
|
|
|
|
{
|
2025-03-17 15:35:51 -04:00
|
|
|
UlxTangible *tan = GetActorTangibleQuiet(actor);
|
|
|
|
|
if (tan == nullptr) {
|
2025-03-19 16:01:38 -04:00
|
|
|
if (actor == nullptr) {
|
|
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("Not a luprex tangible: passed in a null pointer"));
|
|
|
|
|
} else {
|
|
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("Not a luprex tangible: %s"), *actor->GetName());
|
|
|
|
|
}
|
2025-03-17 15:35:51 -04:00
|
|
|
}
|
|
|
|
|
return tan;
|
2023-10-02 15:48:42 -04:00
|
|
|
}
|
|
|
|
|
|
2025-03-17 15:35:51 -04:00
|
|
|
void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) {
|
|
|
|
|
UlxTangible *tan = GetActorTangibleOrLog(target);
|
|
|
|
|
if (tan == nullptr) {
|
|
|
|
|
step = FlxAnimationStep();
|
2025-02-26 14:47:53 -05:00
|
|
|
return;
|
|
|
|
|
}
|
2025-03-17 15:35:51 -04:00
|
|
|
step = tan->AnimTracker.GetCurrentAnimation();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-02 16:01:18 -04:00
|
|
|
void UlxTangible::AutoUpdatePosition()
|
|
|
|
|
{
|
|
|
|
|
const FlxAnimationStep *Step = AnimTracker.LastFinished();
|
|
|
|
|
if (Step != nullptr)
|
|
|
|
|
{
|
|
|
|
|
Step->AutoUpdateXYZ(GetActor());
|
|
|
|
|
Step->AutoUpdateFacing(GetActor());
|
|
|
|
|
Step->AutoUpdatePlane(&(this->Plane));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-17 15:35:51 -04:00
|
|
|
void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool AutoUpdate) {
|
|
|
|
|
UlxTangible *tan = GetActorTangibleOrLog(target);
|
|
|
|
|
if (tan == nullptr) return;
|
2023-10-09 14:59:48 -04:00
|
|
|
tan->AnimTracker.FinishedAnimation(step.Hash);
|
2025-07-02 16:01:18 -04:00
|
|
|
if (AutoUpdate) tan->AutoUpdatePosition();
|
2025-02-26 14:47:53 -05:00
|
|
|
FString DebugString = UlxAnimationStepLibrary::AnimationStepDebugString(step);
|
2025-07-02 16:01:18 -04:00
|
|
|
UE_LOG(LogLuprex, Display, TEXT("Animation Finished: %s"), *DebugString);
|
2023-10-02 15:48:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FString UlxTangible::GetTangiblePlane(AActor* target) {
|
2025-03-17 15:35:51 -04:00
|
|
|
UlxTangible *tan = GetActorTangibleOrLog(target);
|
|
|
|
|
if (tan == nullptr) return TEXT("");
|
|
|
|
|
return tan->Plane.ToString();
|
2023-10-02 15:48:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxTangible::SetTangiblePlane(AActor* target, const FString& plane) {
|
2025-03-17 15:35:51 -04:00
|
|
|
UlxTangible *tan = GetActorTangibleOrLog(target);
|
|
|
|
|
if (tan == nullptr) return;
|
|
|
|
|
tan->Plane = FName(plane);
|
2023-09-26 17:00:30 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-02 15:48:27 -05:00
|
|
|
bool UlxTangible::IsCurrentPlayer(AActor* target) {
|
2025-03-17 15:35:51 -04:00
|
|
|
UlxTangible *tan = GetActorTangibleQuiet(target);
|
|
|
|
|
if (tan == nullptr) return false;
|
2025-03-17 18:04:55 -04:00
|
|
|
ALuprexGameModeBase *gamemode = tan->Manager->GetGameMode();
|
2024-02-02 15:48:27 -05:00
|
|
|
return (tan->TangibleId == gamemode->PlayerId);
|
2024-02-16 15:48:22 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-17 15:35:51 -04:00
|
|
|
bool UlxTangible::IsLuprexTangible(AActor* target) {
|
|
|
|
|
UlxTangible *tan = GetActorTangibleQuiet(target);
|
|
|
|
|
return tan != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-16 15:48:22 -05:00
|
|
|
void UlxTangible::SetAutoFinish(AActor *target, const FString &action, const FVector &xyz) {
|
2025-03-17 15:35:51 -04:00
|
|
|
UlxTangible *tan = GetActorTangibleOrLog(target);
|
|
|
|
|
if (tan == nullptr) return;
|
2024-02-16 15:48:22 -05:00
|
|
|
tan->AnimTracker.SetAutoFinish(action, xyz);
|
2024-08-29 17:46:12 -04:00
|
|
|
}
|
|
|
|
|
|