diff --git a/AGRARIAN_DEVELOPMENT_ROADMAP.md b/AGRARIAN_DEVELOPMENT_ROADMAP.md index e9a99ec..126332e 100644 --- a/AGRARIAN_DEVELOPMENT_ROADMAP.md +++ b/AGRARIAN_DEVELOPMENT_ROADMAP.md @@ -132,6 +132,11 @@ Completed in version .01: - [x] Created `IA_Interact` input action. - [x] Bound `IA_Interact` to `E` and `Gamepad_FaceButton_Left`. - [x] Assigned `IA_Interact` to the character Blueprint's `InteractAction`. +- [x] Created item definition assets for wood, stone, fiber, food, meat, hide, and primitive structure parts. +- [x] Created recipe data assets for campfire, primitive shelter, basic tool, and bandage. +- [x] Created item definition assets for craft result items: campfire, primitive shelter, basic tool, and bandage. +- [x] Created Blueprint child actors for wood resource, campfire, primitive shelter, and first wildlife species. +- [x] Placed and verified wood resource, campfire, primitive shelter, and rabbit wildlife Blueprints in the test map. Open version .01 tasks: @@ -139,10 +144,11 @@ Open version .01 tasks: - [x] Create `IA_Interact` input action. - [x] Bind `IA_Interact` to `E` and a gamepad button. - [x] Assign `IA_Interact` to the character Blueprint's `InteractAction`. -- [ ] Create item definition assets for wood, stone, fiber, food, meat, hide, and primitive structure parts. -- [ ] Create recipe data assets for campfire, primitive shelter, basic tool, and bandage. -- [ ] Create Blueprint child actors for wood resource, campfire, primitive shelter, and first wildlife species. -- [ ] Place resource nodes, campfire, shelter, and wildlife in the test map. +- [x] Create item definition assets for wood, stone, fiber, food, meat, hide, and primitive structure parts. +- [x] Create recipe data assets for campfire, primitive shelter, basic tool, and bandage. +- [x] Create item definition assets for craft result items: campfire, primitive shelter, basic tool, and bandage. +- [x] Create Blueprint child actors for wood resource, campfire, primitive shelter, and first wildlife species. +- [x] Place resource nodes, campfire, shelter, and wildlife in the test map. - [~] Add a simple HUD/debug display for survival and inventory. - [ ] Test gather -> inventory -> craft -> place shelter -> save/load loop. - [ ] Test wildlife damage/death/harvest loop. @@ -330,10 +336,10 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe ## 1.4 Single Biome MVP Map - [ ] Choose MVP biome. -- [ ] Create playable test map. +- [~] Create playable test map. - [ ] Add terrain base. - [ ] Add foliage pass. -- [ ] Add resource nodes. +- [~] Add resource nodes. - [ ] Add water source. - [ ] Add weather exposure zones if needed. - [ ] Add landmark or ruin placeholder. @@ -363,7 +369,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe ## 1.6 Gathering And Resources - [x] Create resource node base class. -- [~] Add wood resource. +- [x] Add wood resource. - [ ] Add stone resource. - [ ] Add fiber resource. - [ ] Add edible plant resource. @@ -381,11 +387,11 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe - [x] Create recipe data assets. - [x] Implement recipe validation. - [x] Implement crafting queue or instant crafting. -- [ ] Add primitive tool recipe. -- [ ] Add campfire recipe. -- [ ] Add shelter recipe. +- [x] Add primitive tool recipe. +- [x] Add campfire recipe. +- [x] Add shelter recipe. - [ ] Add simple container recipe. -- [ ] Add bandage or basic treatment recipe. +- [x] Add bandage or basic treatment recipe. - [ ] Add crafting UI. - [x] Add multiplayer authority checks. - [~] Add crafting debug tools. @@ -438,7 +444,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe ## 1.11 Wildlife Prototype -- [ ] Choose MVP wildlife species. +- [x] Choose MVP wildlife species. - [x] Create wildlife base pawn. - [x] Add simple AI wander. - [x] Add flee behavior. @@ -446,7 +452,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe - [x] Add health. - [ ] Add damage. - [x] Add harvesting interaction. -- [~] Add meat/hide/bone resources if needed. +- [x] Add meat/hide resources. - [ ] Add spawn manager. - [x] Add replication. - [ ] Add performance limits. @@ -1238,14 +1244,14 @@ Next version .01 priorities: - [ ] Open the project in Unreal Editor from the shared project path. - [ ] Create `IA_Interact` and bind it to `E` plus a gamepad button. - [ ] Assign `IA_Interact` to the Agrarian character Blueprint. -- [ ] Create the first editor assets needed for the playable loop. -- [ ] Place and test the first gatherable resource node. -- [ ] Place and test the campfire. -- [ ] Place and test the primitive shelter. -- [ ] Place and test the first wildlife Blueprint. +- [x] Create the first editor assets needed for the playable loop. +- [x] Place and test the wood resource node. +- [x] Place and test the campfire. +- [x] Place and test the primitive shelter. +- [x] Place and test the rabbit wildlife Blueprint. - [~] Add simple survival/inventory HUD feedback. - [ ] Run the first full gather -> craft -> place -> save -> load test. Immediate next item: -- [ ] Open the project in Unreal Editor on Windows-Builder using the shared project path, then create/bind `IA_Interact` so the C++ interaction path can be tested in the editor. +- [ ] Run the first full gather -> inventory -> craft -> place shelter -> save/load loop. diff --git a/Content/ThirdPerson/Lvl_ThirdPerson.umap b/Content/ThirdPerson/Lvl_ThirdPerson.umap index 8e3863f..3077cad 100644 --- a/Content/ThirdPerson/Lvl_ThirdPerson.umap +++ b/Content/ThirdPerson/Lvl_ThirdPerson.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e668a9b3098a76f49ce307759ab00662406c7fa0870eeee6e7a2dbf7f854386 -size 12330 +oid sha256:1ef11de3ea68f3e868dde2417f1df5d026bf571974c9eacbc22449f68e6aff84 +size 12810 diff --git a/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/2/P0/NOPB1M2DUIO1HDABM46T3L.uasset b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/2/P0/NOPB1M2DUIO1HDABM46T3L.uasset new file mode 100644 index 0000000..7348aa2 --- /dev/null +++ b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/2/P0/NOPB1M2DUIO1HDABM46T3L.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a64bea38c078a3e7d4b2a559ae388f8eaad9e369aa18b8f5c92f819ff09ec36 +size 3788 diff --git a/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/5/ZO/3WGRMTBAD2YUKY3FNVT5V2.uasset b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/5/ZO/3WGRMTBAD2YUKY3FNVT5V2.uasset new file mode 100644 index 0000000..12cd2cf --- /dev/null +++ b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/5/ZO/3WGRMTBAD2YUKY3FNVT5V2.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c384d56704ba0b2ef239e72d6fb5964a7e96edf1373e4f0e54ebbb0984763331 +size 5202 diff --git a/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/8/CI/4A33A30CINIP2MI3958ZT9.uasset b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/8/CI/4A33A30CINIP2MI3958ZT9.uasset new file mode 100644 index 0000000..8fcc596 --- /dev/null +++ b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/8/CI/4A33A30CINIP2MI3958ZT9.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32f622188b63eb169ea020ddec7f1a2241eed8f4ff5a20859f27c02f0b4b506a +size 4533 diff --git a/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/9/93/TMX6WY6IW83NE917Q47FKX.uasset b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/9/93/TMX6WY6IW83NE917Q47FKX.uasset new file mode 100644 index 0000000..a3e63c9 --- /dev/null +++ b/Content/__ExternalActors__/ThirdPerson/Lvl_ThirdPerson/9/93/TMX6WY6IW83NE917Q47FKX.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd5cfd28ee66c1300278edfc29a56b64297b02e5e1d0cbff21d8df47edbc50c2 +size 4011 diff --git a/Scripts/setup_test_map_placements.py b/Scripts/setup_test_map_placements.py new file mode 100644 index 0000000..2eb3e99 --- /dev/null +++ b/Scripts/setup_test_map_placements.py @@ -0,0 +1,85 @@ +import unreal + + +MAP_PATH = "/Game/ThirdPerson/Lvl_ThirdPerson" + +PLACEMENTS = [ + { + "label": "AGR_WoodResourceNode_01", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode", + "location": unreal.Vector(650.0, -150.0, 120.0), + "rotation": unreal.Rotator(0.0, 15.0, 0.0), + }, + { + "label": "AGR_Campfire_01", + "class_path": "/Game/Agrarian/Blueprints/Structures/BP_Campfire", + "location": unreal.Vector(900.0, 120.0, 60.0), + "rotation": unreal.Rotator(0.0, 0.0, 0.0), + }, + { + "label": "AGR_PrimitiveShelter_01", + "class_path": "/Game/Agrarian/Blueprints/Structures/BP_PrimitiveShelter", + "location": unreal.Vector(1200.0, -260.0, 140.0), + "rotation": unreal.Rotator(0.0, -20.0, 0.0), + }, + { + "label": "AGR_RabbitWildlife_01", + "class_path": "/Game/Agrarian/Blueprints/Wildlife/BP_RabbitWildlife", + "location": unreal.Vector(450.0, 420.0, 100.0), + "rotation": unreal.Rotator(0.0, 135.0, 0.0), + }, +] + + +def load_blueprint_class(path): + generated_class = unreal.EditorAssetLibrary.load_blueprint_class(path) + if not generated_class: + raise RuntimeError(f"Could not load Blueprint class: {path}") + return generated_class + + +def get_actor_label(actor): + try: + return actor.get_actor_label() + except Exception: + return actor.get_name() + + +def remove_existing_placed_actors(labels): + for actor in unreal.EditorLevelLibrary.get_all_level_actors(): + if get_actor_label(actor) in labels: + unreal.EditorLevelLibrary.destroy_actor(actor) + + +def set_actor_label(actor, label): + try: + actor.set_actor_label(label, mark_dirty=True) + except TypeError: + actor.set_actor_label(label) + + +def main(): + if not unreal.EditorLevelLibrary.load_level(MAP_PATH): + raise RuntimeError(f"Could not load map: {MAP_PATH}") + + labels = {placement["label"] for placement in PLACEMENTS} + remove_existing_placed_actors(labels) + + for placement in PLACEMENTS: + actor_class = load_blueprint_class(placement["class_path"]) + actor = unreal.AgrarianEditorAutomationLibrary.spawn_actor_in_editor_world( + actor_class, + placement["location"], + placement["rotation"], + placement["label"], + ) + if not actor: + raise RuntimeError(f"Could not spawn {placement['class_path']}") + + unreal.log(f"Placed {placement['label']} at {placement['location']}") + + unreal.EditorLevelLibrary.save_current_level() + unreal.log("Agrarian test map placement setup complete.") + + +main() diff --git a/Scripts/verify_test_map_placements.py b/Scripts/verify_test_map_placements.py new file mode 100644 index 0000000..cc47a37 --- /dev/null +++ b/Scripts/verify_test_map_placements.py @@ -0,0 +1,91 @@ +import unreal + + +MAP_PATH = "/Game/ThirdPerson/Lvl_ThirdPerson" + +EXPECTED_PLACEMENTS = { + "AGR_WoodResourceNode_01": { + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode", + "location": unreal.Vector(650.0, -150.0, 120.0), + "properties": { + "remaining_harvests": 6, + "quantity_per_harvest": 2, + }, + }, + "AGR_Campfire_01": { + "class_path": "/Game/Agrarian/Blueprints/Structures/BP_Campfire", + "location": unreal.Vector(900.0, 120.0, 60.0), + "properties": { + "fuel_seconds": 180.0, + "warmth_radius": 650.0, + }, + }, + "AGR_PrimitiveShelter_01": { + "class_path": "/Game/Agrarian/Blueprints/Structures/BP_PrimitiveShelter", + "location": unreal.Vector(1200.0, -260.0, 140.0), + "properties": { + "weather_protection": 0.7, + }, + }, + "AGR_RabbitWildlife_01": { + "class_path": "/Game/Agrarian/Blueprints/Wildlife/BP_RabbitWildlife", + "location": unreal.Vector(450.0, 420.0, 100.0), + "properties": { + "wildlife_id": "rabbit", + "max_health": 12.0, + }, + }, +} + + +def nearly_equal(left, right): + return abs(float(left) - float(right)) < 0.001 + + +def vectors_close(left, right): + return ( + abs(left.x - right.x) < 0.1 + and abs(left.y - right.y) < 0.1 + and abs(left.z - right.z) < 0.1 + ) + + +def get_actor_label(actor): + try: + return actor.get_actor_label() + except Exception: + return actor.get_name() + + +def main(): + if not unreal.EditorLevelLibrary.load_level(MAP_PATH): + raise RuntimeError(f"Could not load map: {MAP_PATH}") + + failures = [] + actors_by_label = {get_actor_label(actor): actor for actor in unreal.EditorLevelLibrary.get_all_level_actors()} + + for label, expected in EXPECTED_PLACEMENTS.items(): + actor = actors_by_label.get(label) + if not actor: + failures.append(f"{label} missing") + continue + + actor_location = actor.get_actor_location() + if not vectors_close(actor_location, expected["location"]): + failures.append(f"{label} expected location {expected['location']}, got {actor_location}") + + for property_name, expected_value in expected.get("properties", {}).items(): + actual_value = actor.get_editor_property(property_name) + if isinstance(expected_value, (int, float)): + if not nearly_equal(actual_value, expected_value): + failures.append(f"{label} {property_name} expected {expected_value}, got {actual_value}") + elif str(actual_value) != expected_value: + failures.append(f"{label} {property_name} expected {expected_value}, got {actual_value}") + + if failures: + raise RuntimeError("Test map placement verification failed: " + "; ".join(failures)) + + unreal.log("Agrarian test map placement verification complete.") + + +main() diff --git a/Source/AgrarianGame/AgrarianEditorAutomationLibrary.cpp b/Source/AgrarianGame/AgrarianEditorAutomationLibrary.cpp new file mode 100644 index 0000000..e0d8461 --- /dev/null +++ b/Source/AgrarianGame/AgrarianEditorAutomationLibrary.cpp @@ -0,0 +1,46 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AgrarianEditorAutomationLibrary.h" + +#include "Engine/World.h" + +#if WITH_EDITOR +#include "Editor.h" +#endif + +AActor* UAgrarianEditorAutomationLibrary::SpawnActorInEditorWorld(TSubclassOf ActorClass, const FVector& Location, const FRotator& Rotation, const FString& ActorLabel) +{ +#if WITH_EDITOR + if (!ActorClass) + { + return nullptr; + } + + UWorld* EditorWorld = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr; + if (!EditorWorld) + { + return nullptr; + } + + FActorSpawnParameters SpawnParameters; + SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + SpawnParameters.ObjectFlags = RF_Transactional; + + AActor* SpawnedActor = EditorWorld->SpawnActor(ActorClass, Location, Rotation, SpawnParameters); + if (!SpawnedActor) + { + return nullptr; + } + + if (!ActorLabel.IsEmpty()) + { + SpawnedActor->SetActorLabel(ActorLabel, true); + } + + SpawnedActor->MarkPackageDirty(); + EditorWorld->MarkPackageDirty(); + return SpawnedActor; +#else + return nullptr; +#endif +} diff --git a/Source/AgrarianGame/AgrarianEditorAutomationLibrary.h b/Source/AgrarianGame/AgrarianEditorAutomationLibrary.h new file mode 100644 index 0000000..0d80bf2 --- /dev/null +++ b/Source/AgrarianGame/AgrarianEditorAutomationLibrary.h @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "AgrarianEditorAutomationLibrary.generated.h" + +/** + * Editor automation helpers used by Python setup scripts. + */ +UCLASS() +class AGRARIANGAME_API UAgrarianEditorAutomationLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Agrarian|Editor Automation") + static AActor* SpawnActorInEditorWorld(TSubclassOf ActorClass, const FVector& Location, const FRotator& Rotation, const FString& ActorLabel); +}; diff --git a/Source/AgrarianGame/AgrarianGame.Build.cs b/Source/AgrarianGame/AgrarianGame.Build.cs index 031e561..9e5c816 100644 --- a/Source/AgrarianGame/AgrarianGame.Build.cs +++ b/Source/AgrarianGame/AgrarianGame.Build.cs @@ -23,6 +23,13 @@ public class AgrarianGame : ModuleRules PrivateDependencyModuleNames.AddRange(new string[] { }); + if (Target.bBuildEditor) + { + PrivateDependencyModuleNames.AddRange(new string[] { + "UnrealEd" + }); + } + PublicIncludePaths.AddRange(new string[] { "AgrarianGame", "AgrarianGame/Variant_Platforming",