#include "Systems/DescentFixture.h" #include "Vehicles/CarrierBlockout.h" #include "Vehicles/CarrierBuild.h" #include "Vehicles/CarrierCarousel.h" #include "Player/DescentCharacter.h" #include "Player/DescentPlayerController.h" #include "Systems/DescentBio.h" #include "Vehicles/DescentCarrier.h" #include "DescentUnits.h" #include "TheDescent.h" #include "UI/DescentHudSubsystem.h" #include "UI/DescentUiSubsystem.h" #include "Components/SceneComponent.h" #include "Engine/World.h" #include "ProceduralMeshComponent.h" ADescentFixture::ADescentFixture() { PrimaryActorTick.bCanEverTick = false; USceneComponent* Root = CreateDefaultSubobject(TEXT("Root")); Root->SetMobility(EComponentMobility::Movable); SetRootComponent(Root); } ADescentFixture* ADescentFixture::SpawnFor(ADescentCarrier* Carrier, const Descent::Carrier::FAnchorDef& A) { if (!Carrier) { return nullptr; } UClass* Cls = nullptr; const FName K = A.Kind; if (K == TEXT("Helm")) { Cls = AFixtureHelm::StaticClass(); } else if (K == TEXT("Chart")) { Cls = AFixtureChartTable::StaticClass(); } else if (K == TEXT("Bed")) { Cls = AFixtureBed::StaticClass(); } else if (K == TEXT("Port")) { Cls = AFixtureNutrientPort::StaticClass(); } else if (K == TEXT("Vat")) { Cls = AFixtureVat::StaticClass(); } else if (K == TEXT("Packer")) { Cls = AFixturePacker::StaticClass(); } else if (K == TEXT("Storage")) { Cls = AFixtureStorage::StaticClass(); } else if (K == TEXT("Terminal")) { Cls = AFixtureTerminal::StaticClass(); } else if (K == TEXT("Carousel")) { Cls = AFixtureCarouselConsole::StaticClass(); } else if (K == TEXT("Ladder")) { Cls = AFixtureLadder::StaticClass(); } if (!Cls) { UE_LOG(LogDescent, Error, TEXT("Fixture: no class for kind '%s' (anchor %s)"), *K.ToString(), *A.Id.ToString()); return nullptr; } UWorld* World = Carrier->GetWorld(); FActorSpawnParameters Params; Params.Name = FName(*(TEXT("Fixture_") + A.Id.ToString())); Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ADescentFixture* F = World->SpawnActorDeferred(Cls, Carrier->GetActorTransform(), nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); if (!F) { return nullptr; } F->Anchor = A; F->FinishSpawning(Carrier->GetActorTransform()); F->AttachToActor(Carrier, FAttachmentTransformRules::KeepWorldTransform); return F; } void ADescentFixture::BeginPlay() { Super::BeginPlay(); Descent::Carrier::FMeshBuilder B; Descent::Carrier::BuildFixtureMesh(Anchor, B); Visual = NewObject(this, TEXT("Visual")); Visual->SetMobility(EComponentMobility::Movable); Visual->bUseAsyncCooking = false; Visual->SetupAttachment(GetRootComponent()); UCarrierBlockoutComponent::MakeSolid(Visual); UCarrierBlockoutComponent::MakeInterior(Visual); Visual->RegisterComponent(); if (B.Sections.Num() > 0) { B.Commit(Visual, true); } if (UDescentInteractionRegistry* Reg = UDescentInteractionRegistry::Get(GetWorld())) { Reg->Register(this); } } void ADescentFixture::EndPlay(const EEndPlayReason::Type Reason) { if (UDescentInteractionRegistry* Reg = UDescentInteractionRegistry::Get(GetWorld())) { Reg->Unregister(this); } Super::EndPlay(Reason); } FVector ADescentFixture::GetFocusPointUU() const { return GetActorTransform().TransformPosition(Descent::MetresToUU(Anchor.PosM)); } void ADescentFixture::Use(ADescentCharacter* User) { if (UDescentHudSubsystem* Hud = UDescentHudSubsystem::Get(GetWorld())) { Hud->Model().PushToast(FString::Printf(TEXT("%s: not built yet - %s"), *Anchor.Id.ToString(), MilestoneNote()), EDescentHudToastKind::Info); } UE_LOG(LogDescent, Display, TEXT("Fixture %s used (stub, %s)"), *Anchor.Id.ToString(), MilestoneNote()); } void AFixtureCarouselConsole::Use(ADescentCharacter* User) { ADescentCarrier* Carrier = Cast(GetAttachParentActor()); UDescentHudSubsystem* Hud = UDescentHudSubsystem::Get(GetWorld()); FString Why; if (Carrier && Carrier->Carousel && Carrier->Carousel->RequestRotate(&Why)) { if (Hud) { Hud->Model().PushToast(TEXT("Carousel: next Tender to the launch cradle"), EDescentHudToastKind::Info); } } else if (Hud) { Hud->Model().PushToast(FString::Printf(TEXT("Carousel locked: %s"), Why.IsEmpty() ? TEXT("no carousel") : *Why), EDescentHudToastKind::Warn); } } void AFixtureLadder::Use(ADescentCharacter* User) { if (ADescentPlayerController* PC = User ? Cast(User->GetController()) : nullptr) { PC->PoolDrop(); } } void AFixturePacker::Use(ADescentCharacter* User) { if (UDescentUiSubsystem* Ui = UDescentUiSubsystem::Get(GetWorld())) { Ui->Open(EDescentWindow::Packer); } } void AFixtureTerminal::Use(ADescentCharacter* User) { if (UDescentUiSubsystem* Ui = UDescentUiSubsystem::Get(GetWorld())) { Ui->Open(EDescentWindow::Terminal); } } void AFixtureNutrientPort::Use(ADescentCharacter* User) { // U3: the wall port draws from the carrier's finite reserve; the arm plays the plug sequence if (UDescentBioSubsystem* Bio = UDescentBioSubsystem::Get(GetWorld())) { Bio->BeginJackIn(User, Anchor.PosM, Anchor.YawDeg); } } FString AFixtureVat::GetPrompt() const { const UDescentBioSubsystem* Bio = UDescentBioSubsystem::Get(GetWorld()); return Bio ? FString::Printf(TEXT("Nutrient vat - BIOMASS %.1f / 20 FU (E loads the fauna you carry)"), Bio->ReserveFU) : Anchor.Prompt; } void AFixtureVat::Use(ADescentCharacter* User) { if (UDescentBioSubsystem* Bio = UDescentBioSubsystem::Get(GetWorld())) { Bio->UseVat(User); } } void AFixtureBed::Use(ADescentCharacter* User) { if (UDescentBioSubsystem* Bio = UDescentBioSubsystem::Get(GetWorld())) { Bio->Sleep(User); } } void AFixtureStorage::Use(ADescentCharacter* User) { if (UDescentUiSubsystem* Ui = UDescentUiSubsystem::Get(GetWorld())) { Ui->Open(EDescentWindow::Storage); } } void AFixtureHelm::Use(ADescentCharacter* User) { if (ADescentPlayerController* PC = User ? Cast(User->GetController()) : nullptr) { PC->EnterHelm(); } }