#include "Core/DescentStreamFocus.h" #include "Debug/DescentStateRegistry.h" #include "DescentUnits.h" #include "TheDescent.h" #include "Engine/World.h" #include "GameFramework/Actor.h" #include "GameFramework/PlayerController.h" UDescentStreamFocusSubsystem* UDescentStreamFocusSubsystem::Get(const UWorld* World) { return World ? World->GetSubsystem() : nullptr; } FDescentHandle UDescentStreamFocusSubsystem::RegisterFocus(FName Owner, FFocusFn Provider) { FDescentHandle Handle; if (!Provider) { UE_LOG(LogDescent, Error, TEXT("StreamFocus: focus of '%s' has an empty function"), *Owner.ToString()); return Handle; } FFocus F; F.Id = NextId++; F.Owner = Owner; F.Provider = MoveTemp(Provider); Handle.Id = F.Id; Foci.Add(MoveTemp(F)); Foci.StableSort([](const FFocus& A, const FFocus& B) { const int32 C = A.Owner.Compare(B.Owner); return C != 0 ? C < 0 : A.Id < B.Id; }); return Handle; } FDescentHandle UDescentStreamFocusSubsystem::RegisterActorFocus(FName Owner, const AActor* Actor, double RadiusM) { TWeakObjectPtr Weak(Actor); return RegisterFocus(Owner, [Weak, RadiusM, Owner](FDescentStreamPoint& Out) { const AActor* A = Weak.Get(); if (!A) { return false; } Out.LocationUU = A->GetActorLocation(); Out.RadiusM = RadiusM; Out.Owner = Owner; return true; }); } bool UDescentStreamFocusSubsystem::UnregisterFocus(FDescentHandle Handle) { if (!Handle.IsValid()) { return false; } return Foci.RemoveAll([&](const FFocus& F) { return F.Id == Handle.Id; }) > 0; } bool UDescentStreamFocusSubsystem::GetViewPoint(FDescentStreamPoint& Out) const { const UWorld* World = GetWorld(); const APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr; if (!PC) { return false; } FVector Loc; FRotator Rot; PC->GetPlayerViewPoint(Loc, Rot); Out.LocationUU = Loc; Out.RadiusM = 0.0; Out.Owner = TEXT("View"); return true; } void UDescentStreamFocusSubsystem::GetFocusPoints(TArray& Out) const { Out.Reset(); FDescentStreamPoint P; if (bUseViewLocation && GetViewPoint(P)) { Out.Add(P); } for (const FFocus& F : Foci) { FDescentStreamPoint Q; Q.Owner = F.Owner; if (F.Provider(Q)) { Out.Add(Q); } } } // --------------------------------------------------------------------------------------------------------- // State section "stream": the points the world is being asked to load around. // --------------------------------------------------------------------------------------------------------- static FDescentStateProviderRegistrar GStreamSection(TEXT("stream"), [](UWorld* World, FJsonObject& Out) { const UDescentStreamFocusSubsystem* S = UDescentStreamFocusSubsystem::Get(World); Out.SetBoolField(TEXT("available"), S != nullptr); TArray> Points; if (S) { Out.SetBoolField(TEXT("use_view_location"), S->GetUseViewLocation()); TArray List; S->GetFocusPoints(List); for (const FDescentStreamPoint& P : List) { TSharedRef E = MakeShared(); E->SetStringField(TEXT("owner"), P.Owner.ToString()); E->SetField(TEXT("pos_m"), Descent::Json::Vec3(Descent::UUToMetres(P.LocationUU))); E->SetNumberField(TEXT("radius_m"), Descent::Json::Round(P.RadiusM)); Points.Add(MakeShared(E)); } } Out.SetArrayField(TEXT("points"), Points); });