diff --git a/AGRARIAN_DEVELOPMENT_ROADMAP.md b/AGRARIAN_DEVELOPMENT_ROADMAP.md index e62e6cb..8d2f179 100644 --- a/AGRARIAN_DEVELOPMENT_ROADMAP.md +++ b/AGRARIAN_DEVELOPMENT_ROADMAP.md @@ -426,7 +426,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe - [x] Verify neighboring tile edge coordinates against the registry before multi-tile stitching. - [x] Add foliage pass. - [~] Add resource nodes. -- [ ] Add biome-appropriate natural resources based on Ground Zero. +- [x] Add biome-appropriate natural resources based on Ground Zero. - [ ] Add water source. - [ ] Add weather exposure zones if needed. - [ ] Add landmark or ruin placeholder. @@ -1402,4 +1402,4 @@ Next version .01 priorities: Immediate next item: -- [ ] Add biome-appropriate natural resources based on Ground Zero. +- [ ] Add water source. diff --git a/Content/Agrarian/Blueprints/Resources/BP_StoneResourceNode.uasset b/Content/Agrarian/Blueprints/Resources/BP_StoneResourceNode.uasset new file mode 100644 index 0000000..f3bf3a2 --- /dev/null +++ b/Content/Agrarian/Blueprints/Resources/BP_StoneResourceNode.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14b455202260d7af6dc923120ac6e644defc5b11bee2c95e28db104df36d8f44 +size 24603 diff --git a/Content/Agrarian/Maps/L_GroundZeroTerrain_Test.umap b/Content/Agrarian/Maps/L_GroundZeroTerrain_Test.umap index b66ab38..a89176e 100644 --- a/Content/Agrarian/Maps/L_GroundZeroTerrain_Test.umap +++ b/Content/Agrarian/Maps/L_GroundZeroTerrain_Test.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a97ad4279d404f86a44d0e8c36712bbac9db70843d68747f8ad2465889fc9b7 -size 7464126 +oid sha256:78cee5c9cef2ce4b93f6cfa642fa7fc9a5f85a55e6e0813fcb207ae231694d9f +size 7483554 diff --git a/Docs/Terrain/GroundZeroResourcePass.md b/Docs/Terrain/GroundZeroResourcePass.md new file mode 100644 index 0000000..37510f5 --- /dev/null +++ b/Docs/Terrain/GroundZeroResourcePass.md @@ -0,0 +1,39 @@ +# Ground Zero Biome Resource Pass + +The Ground Zero map now has a first pass of biome-appropriate natural resources +based on the selected tile's coastal California scrub/woodland biome, terrain +height, slope, and drainage-candidate analysis. + +## Resource Types + +- Wood: scrub/woodland and hillside patches. +- Fiber: grassland, scrub, and drainage-candidate areas. +- Stone: slope, exposed terrain, and valley-edge areas. + +Freshwater remains separate because the water-source roadmap item follows this +pass and should be implemented as its own gameplay actor/system. + +## Implementation + +- Added `BP_StoneResourceNode` through `Scripts/setup_playable_blueprints.py`. +- Updated `Scripts/setup_ground_zero_demo_map.py` to place deterministic Ground + Zero resource nodes. +- Added `Scripts/verify_ground_zero_resources.py` to validate node presence, + yield item IDs, and remaining harvests. + +## Counts + +The map now contains: + +- Wood nodes: `4` +- Fiber nodes: `5` +- Stone nodes: `4` + +These counts include the original demo wood and fiber nodes so the first player +path remains intact while expanding the broader tile resource layer. + +## Follow-Up + +Future passes should replace the prototype meshes with real coastal scrub, +grass, rock, and woodland assets, then drive density from land-cover and +hydrography data instead of fixed authored points. diff --git a/Scripts/setup_ground_zero_demo_map.py b/Scripts/setup_ground_zero_demo_map.py index 731d603..c9b6bc7 100644 --- a/Scripts/setup_ground_zero_demo_map.py +++ b/Scripts/setup_ground_zero_demo_map.py @@ -98,6 +98,87 @@ DEMO_ACTORS = [ ] +BIOME_RESOURCE_ACTORS = [ + { + "label": "AGR_GZ_Wood_CoastalScrub_01", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode", + "location_xy": unreal.Vector(-28600.0, 7400.0, 0.0), + "z_offset": 70.0, + "rotation": unreal.Rotator(0.0, 18.0, 0.0), + }, + { + "label": "AGR_GZ_Wood_CoastalScrub_02", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode", + "location_xy": unreal.Vector(-5400.0, 21400.0, 0.0), + "z_offset": 70.0, + "rotation": unreal.Rotator(0.0, -42.0, 0.0), + }, + { + "label": "AGR_GZ_Wood_Hillside_03", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode", + "location_xy": unreal.Vector(18400.0, 32200.0, 0.0), + "z_offset": 70.0, + "rotation": unreal.Rotator(0.0, 86.0, 0.0), + }, + { + "label": "AGR_GZ_Fiber_Grassland_01", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode", + "location_xy": unreal.Vector(-33400.0, -16200.0, 0.0), + "z_offset": 65.0, + "rotation": unreal.Rotator(0.0, 6.0, 0.0), + }, + { + "label": "AGR_GZ_Fiber_Grassland_02", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode", + "location_xy": unreal.Vector(8200.0, -18200.0, 0.0), + "z_offset": 65.0, + "rotation": unreal.Rotator(0.0, -28.0, 0.0), + }, + { + "label": "AGR_GZ_Fiber_Scrub_03", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode", + "location_xy": unreal.Vector(29200.0, -4200.0, 0.0), + "z_offset": 65.0, + "rotation": unreal.Rotator(0.0, 54.0, 0.0), + }, + { + "label": "AGR_GZ_Fiber_DrainageCandidate_04", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode", + "location_xy": unreal.Vector(-6200.0, 9200.0, 0.0), + "z_offset": 65.0, + "rotation": unreal.Rotator(0.0, 114.0, 0.0), + }, + { + "label": "AGR_GZ_Stone_Slope_01", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode", + "location_xy": unreal.Vector(22600.0, 17600.0, 0.0), + "z_offset": 45.0, + "rotation": unreal.Rotator(0.0, 12.0, 0.0), + }, + { + "label": "AGR_GZ_Stone_Slope_02", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode", + "location_xy": unreal.Vector(37600.0, 28600.0, 0.0), + "z_offset": 45.0, + "rotation": unreal.Rotator(0.0, -36.0, 0.0), + }, + { + "label": "AGR_GZ_Stone_ExposedTerrain_03", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode", + "location_xy": unreal.Vector(11800.0, 38200.0, 0.0), + "z_offset": 45.0, + "rotation": unreal.Rotator(0.0, 71.0, 0.0), + }, + { + "label": "AGR_GZ_Stone_ValleyEdge_04", + "class_path": "/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode", + "location_xy": unreal.Vector(-24800.0, 28600.0, 0.0), + "z_offset": 45.0, + "rotation": unreal.Rotator(0.0, 133.0, 0.0), + }, +] + + FOLIAGE_ZONES = { "trees": { "count": 42, @@ -313,11 +394,14 @@ def main(): raise RuntimeError(f"Could not load map: {MAP_PATH}") labels = {spec["label"] for spec in DEMO_ACTORS} + labels.update(spec["label"] for spec in BIOME_RESOURCE_ACTORS) labels.add(FOLIAGE_LABEL) remove_existing_demo_actors(labels) height_values = load_heightmap() spawn_foliage_actor(height_values) + for spec in BIOME_RESOURCE_ACTORS: + spawn_demo_actor(spec, height_values) for spec in DEMO_ACTORS: spawn_demo_actor(spec, height_values) diff --git a/Scripts/setup_playable_blueprints.py b/Scripts/setup_playable_blueprints.py index 8924767..1245447 100644 --- a/Scripts/setup_playable_blueprints.py +++ b/Scripts/setup_playable_blueprints.py @@ -8,6 +8,7 @@ WILDLIFE_FOLDER = f"{BLUEPRINT_ROOT}/Wildlife" WOOD_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Wood" FIBER_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Fiber" +STONE_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Stone" MESH_CUBE_PATH = "/Game/LevelPrototyping/Meshes/SM_Cube" MESH_CYLINDER_PATH = "/Game/LevelPrototyping/Meshes/SM_Cylinder" @@ -37,6 +38,18 @@ BLUEPRINTS = [ "mesh": MESH_CYLINDER_PATH, "scale": unreal.Vector(0.8, 0.8, 1.0), }, + { + "asset": "BP_StoneResourceNode", + "folder": RESOURCE_FOLDER, + "parent": unreal.AgrarianResourceNode, + "defaults": { + "yield_item_definition": STONE_ITEM_PATH, + "remaining_harvests": 12, + "quantity_per_harvest": 2, + }, + "mesh": MESH_CUBE_PATH, + "scale": unreal.Vector(0.9, 0.75, 0.45), + }, { "asset": "BP_Campfire", "folder": STRUCTURE_FOLDER, diff --git a/Scripts/verify_ground_zero_resources.py b/Scripts/verify_ground_zero_resources.py new file mode 100644 index 0000000..fd654ce --- /dev/null +++ b/Scripts/verify_ground_zero_resources.py @@ -0,0 +1,74 @@ +import unreal + + +MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test" + +EXPECTED_RESOURCE_LABELS = { + "wood": [ + "AGR_DemoWoodResource_01", + "AGR_GZ_Wood_CoastalScrub_01", + "AGR_GZ_Wood_CoastalScrub_02", + "AGR_GZ_Wood_Hillside_03", + ], + "fiber": [ + "AGR_DemoFiberResource_01", + "AGR_GZ_Fiber_Grassland_01", + "AGR_GZ_Fiber_Grassland_02", + "AGR_GZ_Fiber_Scrub_03", + "AGR_GZ_Fiber_DrainageCandidate_04", + ], + "stone": [ + "AGR_GZ_Stone_Slope_01", + "AGR_GZ_Stone_Slope_02", + "AGR_GZ_Stone_ExposedTerrain_03", + "AGR_GZ_Stone_ValleyEdge_04", + ], +} + + +def get_actor_label(actor): + try: + return actor.get_actor_label() + except Exception: + return actor.get_name() + + +def resource_item_id(actor): + item_asset = actor.get_editor_property("yield_item_definition") + definition = item_asset.get_editor_property("definition") if item_asset else None + return str(definition.get_editor_property("item_id")) if definition else "" + + +def main(): + if not unreal.EditorLevelLibrary.load_level(MAP_PATH): + raise RuntimeError(f"Could not load map: {MAP_PATH}") + + actors_by_label = {get_actor_label(actor): actor for actor in unreal.EditorLevelLibrary.get_all_level_actors()} + failures = [] + + for expected_item_id, labels in EXPECTED_RESOURCE_LABELS.items(): + for label in labels: + actor = actors_by_label.get(label) + if not actor: + failures.append(f"{label} missing") + continue + + actual_item_id = resource_item_id(actor) + if actual_item_id != expected_item_id: + failures.append(f"{label} expected {expected_item_id}, got {actual_item_id}") + + if actor.get_editor_property("remaining_harvests") <= 0: + failures.append(f"{label} has no remaining harvests") + + if failures: + raise RuntimeError("Ground Zero resource verification failed: " + "; ".join(failures)) + + unreal.log( + "Ground Zero resource verification complete: " + f"{len(EXPECTED_RESOURCE_LABELS['wood'])} wood, " + f"{len(EXPECTED_RESOURCE_LABELS['fiber'])} fiber, " + f"{len(EXPECTED_RESOURCE_LABELS['stone'])} stone nodes." + ) + + +main() diff --git a/Scripts/verify_playable_blueprints.py b/Scripts/verify_playable_blueprints.py index d6f4fb1..7d4b1ae 100644 --- a/Scripts/verify_playable_blueprints.py +++ b/Scripts/verify_playable_blueprints.py @@ -16,6 +16,13 @@ EXPECTED = { }, "yield_item_id": "fiber", }, + "/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode": { + "properties": { + "remaining_harvests": 12, + "quantity_per_harvest": 2, + }, + "yield_item_id": "stone", + }, "/Game/Agrarian/Blueprints/Structures/BP_Campfire": { "properties": { "fuel_seconds": 180.0,