#pragma once #include "CoreMinimal.h" #include "Core/DescentHandle.h" #include "Core/DescentWorldSubsystem.h" #include "DescentStreamFocus.generated.h" class AActor; /** One point the world should be loaded around. */ struct FDescentStreamPoint { /** Unreal units. */ FVector LocationUU = FVector::ZeroVector; /** Metres around the point to keep loaded; 0 = the consumer's own default radius (the terrain's StreamRadius). */ double RadiusM = 0.0; /** Who asked, for logs and the `stream` state section. The default point is named "View". */ FName Owner; }; /** * The stream focus seam: which points the world (the terrain track's chunks, later the ore and fauna) should stream * around. A system that moves things the player cannot see yet registers a focus for them: * * // carrier (M2): keep the rock around the carrier loaded even while the player is out in a Tender * FocusHandle = UDescentStreamFocusSubsystem::Get(World)->RegisterActorFocus(TEXT("Carrier"), this, 120.0); // radius in metres * // or any dynamic point: * FocusHandle = Focus->RegisterFocus(TEXT("Beacon"), [this](FDescentStreamPoint& Out) { Out.LocationUU = Where(); return true; }); * * The consumer (terrain) calls GetFocusPoints() each time it plans the streaming and unions the spheres. The * player's view location is ALWAYS included, as the default: what the camera sees must be loaded whatever else is registered * (SetUseViewLocation(false) turns that off for tests that want an exact list). */ UCLASS() class THEDESCENT_API UDescentStreamFocusSubsystem : public UDescentWorldSubsystem { GENERATED_BODY() public: /** Fills Out and returns true, or returns false to say "no point this time" (the actor is gone, not spawned yet). */ using FFocusFn = TFunction; static UDescentStreamFocusSubsystem* Get(const UWorld* World); FDescentHandle RegisterFocus(FName Owner, FFocusFn Provider); /** Follows an actor (weakly: a destroyed actor simply stops contributing). */ FDescentHandle RegisterActorFocus(FName Owner, const AActor* Actor, double RadiusM = 0.0); bool UnregisterFocus(FDescentHandle Handle); /** Includes (default) or leaves out the player's view location. */ void SetUseViewLocation(bool bUse) { bUseViewLocation = bUse; } bool GetUseViewLocation() const { return bUseViewLocation; } /** The view location first (if enabled and a player exists), then every registered focus that has a point this time, ordered by owner name. */ void GetFocusPoints(TArray& Out) const; /** The default focus alone: where the first player's camera is. False when there is no player controller yet. */ bool GetViewPoint(FDescentStreamPoint& Out) const; private: struct FFocus { uint32 Id = 0; FName Owner; FFocusFn Provider; }; TArray Foci; // sorted by (owner name, id) uint32 NextId = 1; bool bUseViewLocation = true; };