diff --git a/AGRARIAN_DEVELOPMENT_ROADMAP.md b/AGRARIAN_DEVELOPMENT_ROADMAP.md index 8acca44..62f2090 100644 --- a/AGRARIAN_DEVELOPMENT_ROADMAP.md +++ b/AGRARIAN_DEVELOPMENT_ROADMAP.md @@ -474,7 +474,11 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe `AAgrarianMapBoundaryVolume`, placed `AGR_GroundZeroMapBoundary` around the loaded 1 km Ground Zero tile, clamps server-authoritative player pawns back inside the tile with padding, and exposes a warning-zone hook for later UI. -- [ ] Add developer travel command. +- [x] Add developer travel command. Added server-authoritative + `AgrarianTravel X Y Z` and `AgrarianTravelHome` exec commands on the Agrarian + player controller, teleports the controlled pawn to explicit coordinates or + the validated Ground Zero safe-spawn fallback, stops active movement after + travel, and verifies the command wiring. ## 0.1.E Inventory System diff --git a/Docs/TechnicalDesignDocument.md b/Docs/TechnicalDesignDocument.md index ef9f32a..1558089 100644 --- a/Docs/TechnicalDesignDocument.md +++ b/Docs/TechnicalDesignDocument.md @@ -180,6 +180,14 @@ tile with a small padding rather than allowing players to walk into missing neighbor terrain. The actor exposes a warning distance hook so later UI can present an in-world or HUD notice before a clamp occurs. +Developer testing supports server-authoritative developer travel through +`AgrarianTravel X Y Z` on `AAgrarianGamePlayerController`. The command teleports +the controlled pawn to explicit Unreal world coordinates, stops any current +character movement, and reports the destination to the issuing player. +`AgrarianTravelHome` returns the player to the validated Ground Zero safe-spawn +fallback near `AGR_DemoPlayerStart`, above sea level and above terrain by the +same safe offset used by the map setup pass. + First-pass sky and lighting use `AAgrarianSkyLightingController`. The controller owns movable sun, skylight, and exponential-height-fog components and reads the replicated `AAgrarianGameState` time, active tile sunrise/sunset, weather state, diff --git a/Scripts/verify_developer_travel_command.py b/Scripts/verify_developer_travel_command.py new file mode 100644 index 0000000..b3e37da --- /dev/null +++ b/Scripts/verify_developer_travel_command.py @@ -0,0 +1,54 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +FILES = { + "AgrarianGamePlayerController.h": ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.h", + "AgrarianGamePlayerController.cpp": ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.cpp", + "TechnicalDesignDocument.md": ROOT / "Docs" / "TechnicalDesignDocument.md", + "AGRARIAN_DEVELOPMENT_ROADMAP.md": ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md", +} + +EXPECTED = { + "AgrarianGamePlayerController.h": [ + "void AgrarianTravel(float X, float Y, float Z);", + "void AgrarianTravelHome();", + "void ServerAgrarianTravel(FVector Destination);", + ], + "AgrarianGamePlayerController.cpp": [ + "GroundZeroDeveloperTravelHomeLocation(-22000.0f, -3500.0f, 1148.0f)", + "void AAgrarianGamePlayerController::AgrarianTravel(float X, float Y, float Z)", + "void AAgrarianGamePlayerController::AgrarianTravelHome()", + "ServerAgrarianTravel(FVector(X, Y, Z));", + "void AAgrarianGamePlayerController::ServerAgrarianTravel_Implementation(FVector Destination)", + "ControlledPawn->TeleportTo(Destination, ControlledPawn->GetActorRotation(), false, true);", + "Movement->StopMovementImmediately();", + "Destination.ContainsNaN()", + ], + "TechnicalDesignDocument.md": [ + "`AgrarianTravel X Y Z`", + "`AgrarianTravelHome`", + "server-authoritative developer travel", + ], + "AGRARIAN_DEVELOPMENT_ROADMAP.md": [ + "[x] Add developer travel command.", + ], +} + + +def main(): + missing = [] + for label, path in FILES.items(): + text = path.read_text(encoding="utf-8") + for snippet in EXPECTED[label]: + if snippet not in text: + missing.append(f"{label}: {snippet}") + + if missing: + raise RuntimeError("Developer travel command verification failed: " + "; ".join(missing)) + + print("Agrarian developer travel command verification complete.") + + +if __name__ == "__main__": + main() diff --git a/Source/AgrarianGame/AgrarianGamePlayerController.cpp b/Source/AgrarianGame/AgrarianGamePlayerController.cpp index 14abbf9..b0b5ae3 100644 --- a/Source/AgrarianGame/AgrarianGamePlayerController.cpp +++ b/Source/AgrarianGame/AgrarianGamePlayerController.cpp @@ -9,11 +9,18 @@ #include "AgrarianSurvivalComponent.h" #include "EnhancedInputSubsystems.h" #include "Engine/LocalPlayer.h" +#include "GameFramework/Character.h" +#include "GameFramework/CharacterMovementComponent.h" #include "InputMappingContext.h" #include "Blueprint/UserWidget.h" #include "AgrarianGame.h" #include "Widgets/Input/SVirtualJoystick.h" +namespace +{ + const FVector GroundZeroDeveloperTravelHomeLocation(-22000.0f, -3500.0f, 1148.0f); +} + void AAgrarianGamePlayerController::BeginPlay() { Super::BeginPlay(); @@ -120,6 +127,16 @@ void AAgrarianGamePlayerController::AgrarianHeal() ServerAgrarianHeal(); } +void AAgrarianGamePlayerController::AgrarianTravel(float X, float Y, float Z) +{ + ServerAgrarianTravel(FVector(X, Y, Z)); +} + +void AAgrarianGamePlayerController::AgrarianTravelHome() +{ + ServerAgrarianTravel(GroundZeroDeveloperTravelHomeLocation); +} + void AAgrarianGamePlayerController::ServerAgrarianGrantItem_Implementation(FName ItemId, int32 Quantity) { AAgrarianGameCharacter* AgrarianCharacter = GetPawn(); @@ -196,3 +213,35 @@ void AAgrarianGamePlayerController::ServerAgrarianHeal_Implementation() SurvivalComponent->AddWarmth(37.0f - SurvivalComponent->Survival.BodyTemperature); ClientMessage(TEXT("Agrarian survival restored.")); } + +void AAgrarianGamePlayerController::ServerAgrarianTravel_Implementation(FVector Destination) +{ + APawn* ControlledPawn = GetPawn(); + if (!ControlledPawn) + { + ClientMessage(TEXT("No controlled pawn found for developer travel.")); + return; + } + + if (!Destination.ContainsNaN()) + { + ControlledPawn->TeleportTo(Destination, ControlledPawn->GetActorRotation(), false, true); + if (ACharacter* ControlledCharacter = Cast(ControlledPawn)) + { + if (UCharacterMovementComponent* Movement = ControlledCharacter->GetCharacterMovement()) + { + Movement->StopMovementImmediately(); + } + } + + ClientMessage(FString::Printf( + TEXT("Developer travel complete: X %.1f Y %.1f Z %.1f"), + Destination.X, + Destination.Y, + Destination.Z)); + } + else + { + ClientMessage(TEXT("Developer travel failed: invalid destination.")); + } +} diff --git a/Source/AgrarianGame/AgrarianGamePlayerController.h b/Source/AgrarianGame/AgrarianGamePlayerController.h index 7cad121..3adb8f0 100644 --- a/Source/AgrarianGame/AgrarianGamePlayerController.h +++ b/Source/AgrarianGame/AgrarianGamePlayerController.h @@ -66,6 +66,12 @@ public: UFUNCTION(Exec) void AgrarianHeal(); + UFUNCTION(Exec) + void AgrarianTravel(float X, float Y, float Z); + + UFUNCTION(Exec) + void AgrarianTravelHome(); + protected: UFUNCTION(Server, Reliable) void ServerAgrarianGrantItem(FName ItemId, int32 Quantity); @@ -78,4 +84,7 @@ protected: UFUNCTION(Server, Reliable) void ServerAgrarianHeal(); + + UFUNCTION(Server, Reliable) + void ServerAgrarianTravel(FVector Destination); };