#pragma once #include "CoreMinimal.h" #include "Systems/DescentBuild.h" #include "DescentAutomation.generated.h" class ADescentOreBox; class ADescentRefinery; /** The arm's choice (M7, requirement U14, DESIGN_UE.md section 6.6) as a pure function. Ranks: packer outfeed 0, rack 1, refinery hopper 2. */ namespace Descent::Cargo { struct FSocketView { int32 Rank = 0; bool bOccupied = false; // holds a box or is reserved for one int32 BoxCount = 0; // pieces in the box it holds (0: nothing to take) int32 Capacity = 6; // pieces it accepts while not occupied }; /** The occupied socket of the lowest rank whose box some free socket of a HIGHER rank accepts, and the highest-rank such target. */ THEDESCENT_API bool ChooseMove(TConstArrayView Sockets, int32& OutFrom, int32& OutTo); /** Two links in a vertical plane reaching (Dist, Height) from the shoulder: shoulder pitch and elbow pitch (relative), degrees. */ THEDESCENT_API void TwoLinkIK(double Dist, double Height, double L1, double L2, double& OutShoulderDeg, double& OutElbowDeg); } /** Box Rack: four buffer sockets of rank 1. */ UCLASS() class THEDESCENT_API ADescentBoxRack : public ADescentPlaceable { GENERATED_BODY() public: virtual void InitKit(const Descent::Build::FPlacement& P, FName InKit, int32 InId) override; virtual void EndPlay(const EEndPlayReason::Type Reason) override; virtual bool CanPickUp(FString* OutWhyNot = nullptr) const override; virtual FString GetPrompt() const override; virtual void Use(ADescentCharacter* User) override; int32 SlotBox[4] = { 0, 0, 0, 0 }; bool SlotReserved[4] = { false, false, false, false }; FVector SocketLocalM(int32 Slot) const; // carrier-local metres int32 FreeSocket() const; int32 NumBoxes() const; bool PutBox(ADescentOreBox* Box, int32 Slot); }; /** Handling Arm Mk I / II: moves boxes from a socket to one of a higher rank within its reach. Every joint is its own component, origin on its axis. */ UCLASS() class THEDESCENT_API ADescentRobotArm : public ADescentPlaceable { GENERATED_BODY() public: virtual void InitKit(const Descent::Build::FPlacement& P, FName InKit, int32 InId) override; virtual void EndPlay(const EEndPlayReason::Type Reason) override; virtual bool CanPickUp(FString* OutWhyNot = nullptr) const override; virtual FString GetPrompt() const override; virtual void Use(ADescentCharacter* User) override; void StepArm(double Dt); bool IsMoving() const { return CycleT >= 0.0; } double CycleFraction() const { return CycleT < 0.0 ? 0.0 : CycleT / CycleSec; } int32 Mk = 1; double ReachM = 3.2, CycleSec = 4.0; bool bPower = true; int32 Moved = 0; FString Status = TEXT("IDLE - nothing in reach"); UPROPERTY(VisibleAnywhere) TObjectPtr YawJoint; UPROPERTY(VisibleAnywhere) TObjectPtr Shoulder; UPROPERTY(VisibleAnywhere) TObjectPtr Elbow; UPROPERTY(VisibleAnywhere) TObjectPtr Wrist; UPROPERTY(VisibleAnywhere) TObjectPtr Gripper; private: void Pose(const FVector& TargetLocalM); double CycleT = -1.0; TWeakObjectPtr Box; TWeakObjectPtr ToRack; TWeakObjectPtr ToRefinery; int32 ToSlot = -1, HeldCount = 0; FVector FromM = FVector::ZeroVector, ToM = FVector::ZeroVector, RestM = FVector::ZeroVector; double L1 = 1.8, L2 = 1.8; };