diff --git a/Source/Integration/AnimQueue.cpp b/Source/Integration/AnimQueue.cpp index cea94d5b..b3066b1e 100644 --- a/Source/Integration/AnimQueue.cpp +++ b/Source/Integration/AnimQueue.cpp @@ -376,6 +376,15 @@ void FlxAnimTracker::Update(std::string_view encqueue) { } } +FString FlxAnimTracker::GetCurrentBlueprintName() { + for (int i = 0; i < AQ.Num(); i++) { + if (!AQ[i].Finished) { + return AQ[i].Blueprint; + } + } + return AQ.Last().Blueprint; +} + FlxAnimationStep FlxAnimTracker::GetCurrentAnimation() { FlxAnimationStep result; for (int i = 0; i < AQ.Num(); i++) { diff --git a/Source/Integration/AnimQueue.h b/Source/Integration/AnimQueue.h index 105fc882..18c94184 100644 --- a/Source/Integration/AnimQueue.h +++ b/Source/Integration/AnimQueue.h @@ -278,6 +278,10 @@ public: // void Update(std::string_view encqueue); + // Get the current blueprint name, as a string. + // + FString GetCurrentBlueprintName(); + // Get the current animation step. // // Get the current animation step. This is the step that the diff --git a/Source/Integration/Tangible.cpp b/Source/Integration/Tangible.cpp index 259776a2..ede168be 100644 --- a/Source/Integration/Tangible.cpp +++ b/Source/Integration/Tangible.cpp @@ -4,12 +4,14 @@ #include "Tangible.h" #include "TangibleManager.h" +#define DEFAULT_BLUEPRINT (TEXT("TangibleStaticMesh")) + UlxTangible::UlxTangible() { Manager = nullptr; TangibleId = -1; CurrentActor = nullptr; - ActorBlueprint = nullptr; + ActorBlueprintName = TEXT(""); NearAccordingToLuprex = false; NearAccordingToUnreal = false; } @@ -20,19 +22,20 @@ void UlxTangible::Init(UlxTangibleManager* tm, int64 id) TangibleId = id; } -bool UlxTangible::BlueprintIsTangible(TSubclassOf bp) { - if (bp == nullptr) return true; - return bp->ImplementsInterface(UlxTangibleInterface::StaticClass()); -} - -void UlxTangible::SetActorBlueprintClass(TSubclassOf bp) { +void UlxTangible::SetActorBlueprint(const FString &name) { // If we're already of the right class, do nothing. - if (ActorBlueprint == bp) { + if (ActorBlueprintName == name) { return; } - // Sanity check the blueprint. Nullptr is allowed. - check(BlueprintIsTangible(bp)); + // Get the blueprint. + // If the blueprint doesn't exist, or doesn't implement + // TangibleInterface, then use the fallback blueprint. + UClass *blueprint = Manager->GetTangibleClass(name); + if ((!name.IsEmpty()) && (blueprint == nullptr)) { + blueprint = Manager->GetTangibleClass(DEFAULT_BLUEPRINT); + check(blueprint != nullptr); + } // If there's already an actor, delete it. if (CurrentActor != nullptr) { @@ -50,17 +53,17 @@ void UlxTangible::SetActorBlueprintClass(TSubclassOf bp) { CurrentActor->Destroy(); } - // Update the blueprint reference. - ActorBlueprint = bp; + // Update the blueprint name + ActorBlueprintName = name; // Now create a new actor, unless the BP is nullptr. - if (ActorBlueprint != nullptr) { + if (blueprint != nullptr) { // Create the actor. FActorSpawnParameters params; FVector location(0, 0, 0); FRotator rotation(0, 0, 0); UWorld* w = Manager->GetWorld(); - AActor* a = w->SpawnActor(ActorBlueprint, &location, &rotation, params); + AActor* a = w->SpawnActor(blueprint, &location, &rotation, params); // Insert a TangibleComponent into the actor. UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false); @@ -80,6 +83,9 @@ void UlxTangible::UpdateAnimationQueue(std::string_view aq) { if (limit == 0) break; limit -= 1; AnimTracker.ClearChanged(); + FString blueprint = AnimTracker.GetCurrentBlueprintName(); + if (blueprint.IsEmpty()) blueprint = DEFAULT_BLUEPRINT; + SetActorBlueprint(blueprint); IlxTangibleInterface::Execute_AnimationStateChanged(GetActor()); } } @@ -93,11 +99,11 @@ FVector UlxTangible::GetLocation() const { } void UlxTangible::Destroy() { - SetActorBlueprintClass(nullptr); + SetActorBlueprint(""); Manager = nullptr; TangibleId = -1; CurrentActor = nullptr; - ActorBlueprint = nullptr; + ActorBlueprintName = ""; AnimTracker.Clear(); NearAccordingToLuprex = false; NearAccordingToUnreal = false; diff --git a/Source/Integration/Tangible.h b/Source/Integration/Tangible.h index 4545a33b..50e372b9 100644 --- a/Source/Integration/Tangible.h +++ b/Source/Integration/Tangible.h @@ -76,9 +76,9 @@ public: UPROPERTY() TWeakObjectPtr CurrentActor; - // The blueprint class of the actor. + // The name of the current blueprint. UPROPERTY() - TSubclassOf ActorBlueprint; + FString ActorBlueprintName; // Animation tracker FlxAnimTracker AnimTracker; @@ -128,26 +128,6 @@ public: // FVector GetLocation() const; - // Check a blueprint class to see if it is valid as a Tangible. - // - // In order for a blueprint class to be used as a tangible, - // it must implement the interface IlxTangibleInterface. - // This function also returns true for nullptr. - // - static bool BlueprintIsTangible(TSubclassOf bp); - - // Change the blueprint class of the tangible. - // - // This requires the deletion and recreation of the Actor. - // The blueprint class must satisfy 'BlueprintIsTangible' above. - // - // It is legal to pass in nullptr for the blueprint class. - // Whenever the blueprint class is nullptr, the Actor is - // also nullptr. Ie, a tangible will have no Actor associated - // with it if its blueprint class is nullptr. - // - void SetActorBlueprintClass(TSubclassOf bp); - // Update the animation queue from Luprex. // // This reads the animation queue, and then based on @@ -155,13 +135,18 @@ public: // the Actor's TangibleInterface, calling methods such // as 'StartAnimation' and 'AbortAnimation' as necessary. // - // If the animation queue specifies a blueprint change, - // this function will eventually implement that by calling - // SetActorBlueprintClass above. This is not implemented - // yet. - // void UpdateAnimationQueue(std::string_view aq); +private: + // Set the actor's blueprint, and recreate the actor if necessary. + // + // If the blueprint is the empty string, deletes the actor. + // If the blueprint is not the empty string, creates an actor of the + // correct class. This is a no-op if the actor already exists and + // is already of the correct blueprint class. + // + void SetActorBlueprint(const FString &name); + public: UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = Luprex) static void GetCurrentAnimation(AActor *target, FlxAnimationStep &step); diff --git a/Source/Integration/TangibleManager.cpp b/Source/Integration/TangibleManager.cpp index 30e0661e..8e21d986 100644 --- a/Source/Integration/TangibleManager.cpp +++ b/Source/Integration/TangibleManager.cpp @@ -9,10 +9,31 @@ using namespace DebugPrint; using TanArray = UlxTangibleManager::TanArray; using IdArray = UlxTangibleManager::IdArray; +UClass *UlxTangibleManager::GetTangibleClass(const FString &name) { + if (name.IsEmpty()) { + return nullptr; + } + + FString path(TEXT("/Game/Tangibles/")); + path += name; + path += TCHAR('.'); + path += name; + path += TCHAR('_'); + path += TCHAR('C'); + UClass *result = LoadObject(nullptr, *path); + if (result != nullptr) { + if (!result->IsChildOf(AActor::StaticClass())) { + return nullptr; + } + if (!result->ImplementsInterface(UlxTangibleInterface::StaticClass())) { + return nullptr; + } + } + return result; +} + UlxTangibleManager::UlxTangibleManager() { World = nullptr; - ClassTangibleStaticMesh = LoadObject(nullptr, TEXT("/Game/Tangibles/TangibleStaticMesh.TangibleStaticMesh_C")); - check(ClassTangibleStaticMesh != nullptr); } void UlxTangibleManager::Init(UWorld* world) { @@ -34,11 +55,6 @@ UlxTangible* UlxTangibleManager::MakeTangible(int64 id) { if (t == nullptr) { t = NewObject(); t->Init(this, id); - - // TODO: fix this. The blueprint needs to be assigned - // during animation queue parsing, based on the animation - // queue keyword 'bp'. - t->SetActorBlueprintClass(ClassTangibleStaticMesh); } return t; } diff --git a/Source/Integration/TangibleManager.h b/Source/Integration/TangibleManager.h index 98bc04a7..7f94de6e 100644 --- a/Source/Integration/TangibleManager.h +++ b/Source/Integration/TangibleManager.h @@ -23,11 +23,6 @@ public: UPROPERTY() TWeakObjectPtr World; - // A pointer to uclass TangibleStaticMesh. This is the blueprint - // we use when all else fails. - UPROPERTY() - TSubclassOf ClassTangibleStaticMesh; - // Given a tangible ID, look up the TangibleComponent of that actor. UPROPERTY() TMap IdToTangible; @@ -80,5 +75,9 @@ public: // Given an array of tangibles, return an array of tangible Ids. // static IdArray GetIds(const TanArray &tans); + + // Convert a blueprint name to a blueprint class. + // + UClass *GetTangibleClass(const FString &name); };