// The one drill-plus-suction tool of the Tender (DESIGN_UE.md section 4.5, requirement U10): LMB drills, RMB sucks, the drill wins. // The tool sits on the starboard side; the port hardpoint stays empty. Part of UTenderFlightComponent (sim step 5, TenderTool). #include "Vehicles/TenderFlight.h" #include "Core/DescentMath.h" #include "Core/Tuning/DescentTune_Tender.h" #include "Core/Tuning/DescentTune_World.h" #include "DescentUnits.h" #include "Life/DescentFauna.h" #include "Systems/DescentLooseOre.h" #include "TheDescent.h" #include "UI/DescentHudSubsystem.h" #include "World/DescentTerrainSubsystem.h" #include "World/DescentWorldDef.h" #include "GameFramework/Actor.h" namespace ToolTune = Descent::Tune::Tender; bool UTenderFlightComponent::AddToHold(FName Ore) { const int32 Cap = Stats().HoldCap; UDescentHudSubsystem* Hud = UDescentHudSubsystem::Get(GetWorld()); if (HoldOrder.Num() >= Cap) { if (HoldFullTimer <= 0.0 && Hud) { Hud->Model().PushToast(TEXT("HOLD FULL — dock to unload"), EDescentHudToastKind::Warn); HoldFullTimer = ToolTune::HoldFullToastEverySec; } return false; } HoldOrder.Add(Ore); ++CapturedTotal; if (HoldOrder.Num() >= Cap && Hud) { Hud->Model().PushToast(TEXT("HOLD FULL — dock to unload"), EDescentHudToastKind::Warn); HoldFullTimer = ToolTune::HoldFullToastEverySec; } return true; } void UTenderFlightComponent::StepTool(const FDescentIntent* Intent, double Dt) { AActor* Owner = GetOwner(); bool bWantDrill = Intent && Intent->bPrimary; bool bWantSuck = Intent && Intent->bSecondary; if (DebugDrill >= 0) { bWantDrill = DebugDrill > 0; } if (DebugSuck >= 0) { bWantSuck = DebugSuck > 0; } if (!Owner || !IsPowered() || bImploded) { bWantDrill = bWantSuck = false; } bDrilling = bWantDrill; bSucking = bWantSuck && !bWantDrill; // one motor: the drill wins bBasalt = false; bDrillHit = false; HoldFullTimer = FMath::Max(0.0, HoldFullTimer - Dt); if (!Owner || (!bDrilling && !bSucking)) { return; } const Descent::Tender::FTenderStats St = Stats(); const FTransform Xf = Owner->GetActorTransform(); const FVector Dir = Xf.GetRotation().GetForwardVector(); const FVector EyeM = Descent::UUToMetres(Xf.TransformPosition(Descent::MetresToUU(FVector(ToolTune::EyeX, ToolTune::EyeY, ToolTune::EyeZ)))); const FVector TipM = Descent::UUToMetres(Xf.TransformPosition(Descent::MetresToUU(FVector(ToolTune::ToolTipX, ToolTune::ToolSide * 0.69, ToolTune::ToolTipZ)))); double Drain = 0.0; if (bDrilling) { // Aim: a ray from the cockpit camera along the view. Drill reach 16 m, the laser head 28 m. UDescentTerrainSubsystem* Terrain = UDescentTerrainSubsystem::Get(GetWorld()); const double Reach = bLaserHead ? ToolTune::LaserReachM : ToolTune::DrillReachM; const double Hit = Terrain ? Terrain->Raycast(EyeM, Dir, Reach) : -1.0; Drain = bLaserHead ? ToolTune::DrainLaser : ToolTune::DrainDrill; if (Hit >= 0.0) { DrillHitM = EyeM + Dir * Hit; bDrillHit = true; if (Descent::World::FDescentWorldDef::ClaimDist(DrillHitM) > Descent::Tune::World::BasaltRampM) { bBasalt = true; // dense basalt: white sparks, no carve, the dash says so } else { const FVector Centre = DrillHitM + Dir * (bLaserHead ? ToolTune::LaserAheadM : ToolTune::DrillAheadM); const double R = bLaserHead ? FMath::Max(1.6, 0.6 * St.DrillR) : St.DrillR; DrillRemovedTotal += Terrain->Carve(Centre, R, St.DrillRate * (bLaserHead ? 1.6 : 1.0), Dt); CleanupTimer -= Dt; if (CleanupTimer <= 0.0) { CleanupTimer = ToolTune::DrillCleanupEverySec; Terrain->CleanupFloating(Centre, R + 2.0); } } } } else { Drain = ToolTune::DrainSuction; if (UDescentLooseOreSubsystem* Loose = UDescentLooseOreSubsystem::Get(GetWorld())) { Loose->Suck(TipM, Dir, St.SuctionRange, St.SuctionPull, Dt, [this](uint8 Type) { return AddToHold(UDescentLooseOreSubsystem::TypeName(Type)); }); } // M8 (U3): fish in the cone go into the bio-tank if (UDescentFaunaSubsystem* Fauna = UDescentFaunaSubsystem::Get(GetWorld())) { bool bFull = false; BioFu = FMath::Min(St.BioCap, BioFu + Fauna->Suck(TipM, Dir, St.SuctionRange, St.SuctionPull, Dt, St.BioCap - BioFu, bFull)); BioFullToast -= Dt; if (bFull && BioFullToast <= 0.0) { BioFullToast = 4.0; if (UDescentHudSubsystem* Hud = UDescentHudSubsystem::Get(GetWorld())) { Hud->Model().PushToast(TEXT("BIO-TANK FULL"), EDescentHudToastKind::Warn); } } } } PowerPct = FMath::Max(0.0, PowerPct - 100.0 * Drain / St.Capacity * Dt); }