// Fill out your copyright notice in the Description page of Project Settings. #include "Tangible.h" #include "TangibleManager.h" #include "IntegrationGameModeBase.h" #define DEFAULT_BLUEPRINT (TEXT("TangibleStaticMesh")) #define LOCTEXT_NAMESPACE "Tangible" 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; } #pragma optimize("", off) void UlxTangible::SetActorBlueprint(const FString &name) { // If we're already of the right class, do nothing. if (ActorBlueprintName == name) { return; } // 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) { // 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; }; } // Update the blueprint name ActorBlueprintName = name; // Now create a new actor, unless the BP is nullptr. if (blueprint != nullptr) { UWorld* w = Manager->GetWorld(); FActorSpawnParameters params; // 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); // 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; while (AnimTracker.IsChanged()) { if (limit == 0) break; limit -= 1; AnimTracker.ClearChanged(); FString blueprint = AnimTracker.GetCurrentBlueprintName(); if (blueprint.IsEmpty()) blueprint = DEFAULT_BLUEPRINT; SetActorBlueprint(blueprint); IlxTangibleInterface::Execute_AnimationQueueChanged(GetActor()); } } FVector UlxTangible::GetLocation() const { if (CurrentActor == nullptr) { return FVector(0,0,0); } else { return CurrentActor->GetActorLocation(); } } void UlxTangible::Destroy() { SetActorBlueprint(""); Manager = nullptr; TangibleId = -1; CurrentActor = nullptr; ActorBlueprintName = ""; AnimTracker.Clear(); NearAccordingToLuprex = false; NearAccordingToUnreal = false; } static UlxTangible *GetActorTangible(AActor *actor) { UlxTangibleComponent* comp = actor->GetComponentByClass(); check(comp != nullptr); UlxTangible *result = comp->Tangible.Get(); check(result != nullptr); return result; } void UlxTangible::GetCurrentAnimation(AActor *target, FlxAnimationStep &step) { step = GetActorTangible(target)->AnimTracker.GetCurrentAnimation(); } void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool autoxyz, bool autofacing, bool autoplane) { UlxTangible *tan = GetActorTangible(target); if (autoxyz) step.AutoUpdateXYZ(target); if (autofacing) step.AutoUpdateFacing(target); if (autoplane) step.AutoUpdatePlane(&(tan->Plane)); tan->AnimTracker.FinishedAnimation(step.Hash); } FString UlxTangible::GetTangiblePlane(AActor* target) { return GetActorTangible(target)->Plane.ToString(); } void UlxTangible::SetTangiblePlane(AActor* target, const FString& plane) { GetActorTangible(target)->Plane = FName(plane); } bool UlxTangible::IsCurrentPlayer(AActor* target) { UlxTangible *tan = GetActorTangible(target); AIntegrationGameModeBase *gamemode = tan->Manager->GetGameMode(); return (tan->TangibleId == gamemode->PlayerId); } void UlxTangible::SetAutoFinish(AActor *target, const FString &action, const FVector &xyz) { UlxTangible *tan = GetActorTangible(target); tan->AnimTracker.SetAutoFinish(action, xyz); } void UlxTangible::Assert(bool condition, const FString &message) { if (!condition) { FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::FatalError, FText::FromString(message)); FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); } } void UlxTangible::CallFunctionByName(UObject *object, const FString &namepart1, const FString &namepart2, const FString &fallback) { FString fullname = namepart1 + namepart2; if (!IsValid(object)) { const FBlueprintExceptionInfo ExceptionInfo( EBlueprintExceptionType::FatalError, LOCTEXT("CallFunctionByName_ObjectIsNotValid", "In CallFunctionByName, object passed in is not valid.") ); FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); return; } UFunction* function = object->FindFunction(FName(*fullname)); if (function == nullptr) { function = object->FindFunction(FName(*fallback)); if (function == nullptr) { const FBlueprintExceptionInfo ExceptionInfo( EBlueprintExceptionType::FatalError, LOCTEXT("CallFunctionByName_NoSuchFunction", "In CallFunctionByName, cannot find the named function or the fallback function.") ); FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); return; } } if (function->ParmsSize != 0) { const FBlueprintExceptionInfo ExceptionInfo( EBlueprintExceptionType::FatalError, LOCTEXT("CallFunctionByName_FunctionHasParameters", "CallFunctionByName can only call functions that have no parameters and no return values.") ); FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); return; } object->ProcessEvent(function, nullptr); }