#pragma once #include "CoreMinimal.h" #include "Core/DescentHandle.h" #include "Core/DescentWorldSubsystem.h" #include "DescentLooseOre.generated.h" class UInstancedStaticMeshComponent; /** One loose ore chunk (DESIGN_UE.md section 2.12). Type: 0 ferrite, 1 cobalt, 2 pearl. */ struct FDescentLooseChunk { FVector PosM = FVector::ZeroVector; FVector VelMps = FVector::ZeroVector; uint8 Type = 0; }; /** * Loose ore (M4): a pool of at most 160 chunks. A chunk appears where the drill completes one inside a vein (the ore subsystem's * OnOreFreed), sinks slowly, drifts with the Shear, bounces off rock (the density field), and is pulled in and captured by a Tender's * suction. Stepped in sim step 8 (Ore); the Tender's tool (step 5) pulls first. */ UCLASS() class THEDESCENT_API UDescentLooseOreSubsystem : public UDescentWorldSubsystem { GENERATED_BODY() public: static UDescentLooseOreSubsystem* Get(const UWorld* World); static FName TypeName(uint8 Type); virtual void OnWorldBeginPlay(UWorld& InWorld) override; virtual void Deinitialize() override; int32 Num() const { return Chunks.Num(); } const TArray& All() const { return Chunks; } void Spawn(uint8 Type, const FVector& PosM, const FVector& VelMps); void Clear() { Chunks.Reset(); } /** * Suction (section 4.5): every chunk within Range of the nozzle and inside the cone (cos >= 0.55 to Dir, or closer than 3 m) is * accelerated toward the nozzle; within 1.6 m TryCapture is asked: true removes the chunk, false pushes it back. Returns captures. */ int32 Suck(const FVector& NozzleM, const FVector& Dir, double Range, double Pull, double Dt, TFunctionRef TryCapture); /** A swimmer takes the nearest chunk within Range; -1 if none (returns the type). */ int32 TakeNearest(const FVector& PosM, double Range); private: void Step(double Dt); void UpdateVisuals(); TArray Chunks; FDescentHandle StepHandle; FDelegateHandle FreedHandle; uint32 Seed = 9001u; UPROPERTY(Transient) TObjectPtr VisualActor; UPROPERTY(Transient) TArray> Meshes; };