// Fill out your copyright notice in the Description page of Project Settings. #include "Tangible.h" #include "TangibleManager.h" #include "LuprexGameModeBase.h" #define DEFAULT_BLUEPRINT (TEXT("StaticMesh")) UlxTangible::UlxTangible() { Manager = nullptr; TangibleId = -1; CurrentActor = nullptr; ActorBlueprintName = TEXT(""); NearAccordingToLuprex = false; NearAccordingToUnreal = false; } void UlxTangible::Init(UlxTangibleManager* tm, int64 id) { Manager = tm; TangibleId = id; } 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(); 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; } void UlxTangible::SetActorBlueprint(const FString &XName) { FString Name = XName.ToLower(); // If we're already of the right class, do nothing. if (ActorBlueprintName == Name) { return; } UClass *blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, Name); if (blueprint == nullptr) { blueprint = UlxAssetLookup::LoadTangibleBlueprintAsset(this, DEFAULT_BLUEPRINT); check(blueprint != nullptr); } // Update the blueprint name ActorBlueprintName = Name; // 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(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); } void UlxTangible::UpdateAnimationQueue(std::string_view aq) { AnimTracker.Update(aq); } void UlxTangible::MaybeExecuteAnimStateChanged() { int limit = 3; bool AnyChange = AnimTracker.IsChanged(); while (AnimTracker.IsChanged()) { if (limit == 0) break; limit -= 1; AnimTracker.ClearChanged(); FString blueprint = AnimTracker.GetCurrentBlueprintName(); if (blueprint.IsEmpty()) blueprint = DEFAULT_BLUEPRINT; SetActorBlueprint(blueprint); AActor *actor = GetActor(); UFunction *aqchanged = actor->GetClass()->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); if (aqchanged != nullptr) { actor->ProcessEvent(aqchanged, nullptr); } } // 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; } } FVector UlxTangible::GetLocation() const { if (CurrentActor == nullptr) { return FVector(0,0,0); } else { return CurrentActor->GetActorLocation(); } } void UlxTangible::Destroy() { DeleteCurrentActor(); Manager = nullptr; TangibleId = -1; CurrentActor = nullptr; ActorBlueprintName = ""; AnimTracker.Clear(); NearAccordingToLuprex = false; NearAccordingToUnreal = false; } UlxTangible *UlxTangible::GetActorTangibleQuiet(AActor *actor) { if (actor == nullptr) return nullptr; UlxTangibleComponent* comp = actor->GetComponentByClass(); if (comp == nullptr) return nullptr; return comp->Tangible.Get(); } UlxTangible *UlxTangible::GetActorTangibleOrLog(AActor *actor) { UlxTangible *tan = GetActorTangibleQuiet(actor); if (tan == nullptr) { 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()); } } return tan; } void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) { UlxTangible *tan = GetActorTangibleOrLog(target); if (tan == nullptr) { step = FlxAnimationStep(); return; } step = tan->AnimTracker.GetCurrentAnimation(); } void UlxTangible::AutoUpdatePosition() { const FlxAnimationStep *Step = AnimTracker.LastFinished(); if (Step != nullptr) { Step->AutoUpdateXYZ(GetActor()); Step->AutoUpdateFacing(GetActor()); Step->AutoUpdatePlane(&(this->Plane)); } } void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool AutoUpdate) { UlxTangible *tan = GetActorTangibleOrLog(target); if (tan == nullptr) return; tan->AnimTracker.FinishedAnimation(step.Hash); if (AutoUpdate) tan->AutoUpdatePosition(); FString DebugString = UlxAnimationStepLibrary::AnimationStepDebugString(step); UE_LOG(LogLuprex, Display, TEXT("Animation Finished: %s"), *DebugString); } FString UlxTangible::GetTangiblePlane(AActor* target) { UlxTangible *tan = GetActorTangibleOrLog(target); if (tan == nullptr) return TEXT(""); return tan->Plane.ToString(); } void UlxTangible::SetTangiblePlane(AActor* target, const FString& plane) { UlxTangible *tan = GetActorTangibleOrLog(target); if (tan == nullptr) return; tan->Plane = FName(plane); } bool UlxTangible::IsCurrentPlayer(AActor* target) { UlxTangible *tan = GetActorTangibleQuiet(target); if (tan == nullptr) return false; ALuprexGameModeBase *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 = GetActorTangibleOrLog(target); if (tan == nullptr) return; tan->AnimTracker.SetAutoFinish(action, xyz); }