#pragma once #include "CoreMinimal.h" #include "Core/Tuning/DescentTune_Carrier.h" /** * FCarrierLayout (DESIGN_UE.md sections 3.2, 3.3): the static tables of the CANTICLE. Plain data and pure functions, no UObject: * the blockout builds meshes from it, fixtures are spawned from its anchors, the dry volume and the room lookup answer from it, * and the automation tests check it without a world. All coordinates are carrier-local METRES (+X bow, +Y starboard, +Z up). * Real models later replace the blockout through FRoomDef::OverrideMesh and HullOverrideMesh; nothing else here changes. */ namespace Descent::Carrier { /** Section 3.3: HullRadius(x). Nose ellipse for x > 40, 12 m cylinder for |x| <= 40, taper to 7 m at x = -60. */ THEDESCENT_API double HullRadius(double X); /** Section 3.3 ADescentCarrier::IsDry, local metres: inside the hull capsule (minus 0.3 m) and not in the moon-pool water. */ THEDESCENT_API bool IsDryLocal(const FVector& LocalM); struct FLampDef { double X = 0.0, Y = 0.0; /** Height above the room floor; < 0 means "just under the ceiling". */ double ZAboveFloor = -1.0; }; struct FRoomDef { FName Id; FString Name; int32 Deck = 3; // deck of the floor: 1, 2 or 3 double X0 = 0, X1 = 0, Y0 = 0, Y1 = 0; bool bTall = false; double FloorZ = 0, CeilZ = 0; FLinearColor LampColor = FLinearColor::White; double LampIntensity = 1.0; // design's unitless value bool bEmergency = false; // red cot room bool bPulses = false; // engine room: 1.0 * (0.85 + 0.15 sin 2.4 t) TArray Lamps; FVector2D Stop = FVector2D::ZeroVector; // free floor for Place / MoveTo double StopYawDeg = 0.0; FString OverrideMesh; // asset path of a real model; empty = blockout FVector CentreM() const { return FVector(0.5 * (X0 + X1), 0.5 * (Y0 + Y1), 0.5 * (FloorZ + CeilZ)); } double LampRadiusM() const { return bTall ? Descent::Tune::Carrier::TallLampRadiusM : Descent::Tune::Carrier::LampRadiusM; } bool ContainsXY(double X, double Y) const { return X >= X0 && X <= X1 && Y >= Y0 && Y <= Y1; } }; /** A door rectangle as the design lists it: (y range, x range) on the deck; the thin dimension is the wall thickness. */ struct FDoorDef { int32 Deck = 3; double Y0 = 0, Y1 = 0, X0 = 0, X1 = 0; /** True: the wall plane is at constant X (door runs along Y). */ bool WallAtConstX() const { return (X1 - X0) <= (Y1 - Y0); } FVector2D Mid() const { return FVector2D(0.5 * (X0 + X1), 0.5 * (Y0 + Y1)); } double Width() const { return WallAtConstX() ? (Y1 - Y0) : (X1 - X0); } double FloorZ() const; }; struct FWindowDef { FName Room; bool bWallAtConstX = false; double At = 0, A0 = 0, A1 = 0; // wall plane and the range along the wall double H0 = 0, H1 = 0; // heights above the room floor }; /** A fixed station (section 3.4): the fixture actor is spawned here, attached to the carrier. */ struct FAnchorDef { FName Id; FName Kind; // Helm, Chart, Bed, Port, Vat, Packer, Storage, Terminal, Carousel, Ladder FVector PosM = FVector::ZeroVector; // the focus point (what the E prompt aims at) double YawDeg = 0.0; FString Prompt; double RangeM = 2.4, MinDot = 0.55; }; /** A walkable connection between two route nodes; Via are XY waypoints from A to B. */ struct FLink { FName A, B; TArray Via; }; class THEDESCENT_API FLayout { public: static const FLayout& Get(); TArray Rooms; TArray Doors; TArray Windows; TArray Anchors; TArray Links; const FRoomDef* FindRoom(FName Id) const; /** The room containing a carrier-local point (z is checked from the floor minus 0.3 m to the ceiling), or null. */ const FRoomDef* RoomAt(const FVector& LocalM) const; /** The two rooms a door joins (null if a side has no room). */ bool DoorRooms(const FDoorDef& Door, const FRoomDef*& OutA, const FRoomDef*& OutB) const; /** Doors of a room's wall (wall plane At, along range) on the room's deck, plus the special holes (stair D3 doors, bay gallery). */ void WallHoles(const FRoomDef& Room, bool bConstX, double At, double Lo, double Hi, TArray& OutAlongRanges, TArray& OutZRanges) const; /** * Route as XY waypoints from a start point to a room's stop point (or to ToPoint when bToPoint) through doors, the stair ramp * and the bay stairs. False if the rooms are not connected. */ bool FindRoute(const FVector& FromM, FName ToRoom, const FVector2D* ToPoint, TArray& OutXY) const; FLayout(); private: FName NodeOf(const FRoomDef& R, int32 DoorDeck) const; void Build(); }; }