#include "World/DescentOreSubsystem.h" #include "Systems/DescentLooseOre.h" #include "World/DescentWorldDef.h" #include "Core/Tuning/DescentTune_World.h" #include "Debug/DescentStateRegistry.h" #include "Engine/World.h" UDescentOreSubsystem* UDescentOreSubsystem::Get(const UWorld* World) { return World ? World->GetSubsystem() : nullptr; } void UDescentOreSubsystem::EnsureStates() const { const int32 N = Descent::World::FDescentWorldDef::Get().Deposits.Num(); if (States.Num() != N) { States.SetNum(N); } } int32 UDescentOreSubsystem::NumDeposits() const { return Descent::World::FDescentWorldDef::Get().Deposits.Num(); } bool UDescentOreSubsystem::IsExhausted(int32 Deposit) const { EnsureStates(); return States.IsValidIndex(Deposit) && States[Deposit].Mined >= Descent::World::FDescentWorldDef::Get().Deposits[Deposit].Yield; } int32 UDescentOreSubsystem::GetMined(int32 Deposit) const { EnsureStates(); return States.IsValidIndex(Deposit) ? States[Deposit].Mined : 0; } void UDescentOreSubsystem::NoteSampleRemoved(int32 Deposit, const FVector& PosM) { EnsureStates(); if (!States.IsValidIndex(Deposit) || IsExhausted(Deposit)) { return; } FDescentDepositState& S = States[Deposit]; if (++S.Removed >= Descent::Tune::World::DepositSamplesPerChunk) { S.Removed = 0; ++S.Mined; ++MinedTotal; OnOreFreed.Broadcast(Deposit, PosM); } } bool UDescentOreSubsystem::QueryNearest(const FVector& PosM, int32 TypeFilter) { const Descent::World::FDescentWorldDef& Def = Descent::World::FDescentWorldDef::Get(); const int32 I = Def.NearestDeposit(PosM, TypeFilter); LastQuery = FQuery(); if (I < 0) { return false; } const Descent::World::FDeposit& D = Def.Deposits[I]; LastQuery.bValid = true; LastQuery.Index = I; LastQuery.PosM = D.P; LastQuery.NormalM = D.N; LastQuery.Type = static_cast(D.Type); LastQuery.Yield = D.Yield; LastQuery.Mined = GetMined(I); return true; } void UDescentOreSubsystem::GetState(TArray& OutMined, TArray& OutRemoved) const { EnsureStates(); OutMined.Reset(); OutRemoved.Reset(); for (const FDescentDepositState& S : States) { OutMined.Add(S.Mined); OutRemoved.Add(S.Removed); } } void UDescentOreSubsystem::SetState(const TArray& InMined, const TArray& InRemoved) { EnsureStates(); MinedTotal = 0; for (int32 I = 0; I < States.Num(); ++I) { States[I].Mined = InMined.IsValidIndex(I) ? InMined[I] : 0; States[I].Removed = InRemoved.IsValidIndex(I) ? InRemoved[I] : 0; MinedTotal += States[I].Mined; } } void UDescentOreSubsystem::ResetState() { States.Reset(); MinedTotal = 0; LastQuery = FQuery(); } // --------------------------------------------------------------------------------------------------------- // State section "ore" (DESIGN_UE.md section 9.4) // --------------------------------------------------------------------------------------------------------- static FDescentStateProviderRegistrar GOreSection(TEXT("ore"), [](UWorld* World, FJsonObject& Out) { const UDescentOreSubsystem* Ore = UDescentOreSubsystem::Get(World); Out.SetBoolField(TEXT("available"), Ore != nullptr); if (!Ore) { return; } Out.SetNumberField(TEXT("deposits"), Ore->NumDeposits()); Out.SetNumberField(TEXT("mined_total"), static_cast(Ore->GetMinedTotal())); Out.SetNumberField(TEXT("loose"), UDescentLooseOreSubsystem::Get(World) ? UDescentLooseOreSubsystem::Get(World)->Num() : 0); Out.SetNumberField(TEXT("crystals"), 0); // so do crystals TSharedRef Q = MakeShared(); const UDescentOreSubsystem::FQuery& Qr = Ore->LastQuery; Q->SetBoolField(TEXT("valid"), Qr.bValid); if (Qr.bValid) { Q->SetNumberField(TEXT("index"), Qr.Index); Q->SetField(TEXT("pos"), Descent::Json::Vec3(Qr.PosM)); Q->SetField(TEXT("normal"), Descent::Json::Vec3(Qr.NormalM)); Q->SetStringField(TEXT("type"), Descent::World::OreName(static_cast(Qr.Type))); Q->SetNumberField(TEXT("yield"), Qr.Yield); Q->SetNumberField(TEXT("mined"), Qr.Mined); } Out.SetObjectField(TEXT("query"), Q); });