Finish Ground Zero playable map resources
This commit is contained in:
@@ -445,7 +445,11 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [x] Choose Ground Zero real-world 1 km x 1 km MVP tile.
|
||||
- [x] Choose MVP biome based on Ground Zero location.
|
||||
- [x] Link Ground Zero tile coordinates to real-world weather lookup coordinates. Added explicit Ground Zero `weather_lookup_metadata` with tile-center latitude `37.5925`, longitude `-122.4995`, Open-Meteo primary routing, NOAA/NWS fallback eligibility, manifest generation support, registry/schema documentation, and a verifier so future source-backed tiles use declared lookup metadata instead of one-off hard-coded coordinates.
|
||||
- [~] Create playable test map.
|
||||
- [x] Create playable test map. Updated the Ground Zero playable-map verifier
|
||||
to validate the current real-terrain demo map instead of the deprecated flat
|
||||
prototype labels: safe player start, nearby wood/fiber/water/fire/shelter/
|
||||
wildlife loop actors, map boundary, sky/weather controllers, and absence of
|
||||
legacy flat-test placements.
|
||||
- [x] Add terrain base from real elevation data.
|
||||
- [x] Add first-pass water depth/shoreline handling if applicable.
|
||||
- [x] Add first-pass hill, mountain, river, stream, lake, and coastline handling if present in Ground Zero.
|
||||
@@ -455,7 +459,10 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [x] Verify terrain tile origin and centered Unreal bounds for the Ground Zero test map.
|
||||
- [x] Verify neighboring tile edge coordinates against the registry before multi-tile stitching.
|
||||
- [x] Add foliage pass.
|
||||
- [~] Add resource nodes.
|
||||
- [x] Add resource nodes. Locked the Ground Zero wood, fiber, edible-plant,
|
||||
and stone node pass with verification for stable node IDs, yield item IDs,
|
||||
remaining harvests, and readable first-pass resource material assignments so
|
||||
the MVP map proves both gameplay function and investor-facing readability.
|
||||
- [x] Add biome-appropriate natural resources based on Ground Zero.
|
||||
- [x] Add water source.
|
||||
- [x] Replace grey-box environment presentation with an MVP natural environment pass: terrain material, grass, shrubs, bushes, trees, water-source visuals, and clearer Ground Zero biome dressing. Added repeatable Ground Zero environment material generation, applied terrain/foliage/resource/water materials in the map setup, regenerated the demo map, documented the pass, and added verification for material assets plus map assignments.
|
||||
|
||||
@@ -38,6 +38,7 @@ space.
|
||||
- `/Game/Agrarian/Materials/M_AGR_GZ_Grass_DryCoastal`
|
||||
- `/Game/Agrarian/Materials/M_AGR_GZ_Wood_Resource`
|
||||
- `/Game/Agrarian/Materials/M_AGR_GZ_Fiber_Resource`
|
||||
- `/Game/Agrarian/Materials/M_AGR_GZ_EdiblePlant_Resource`
|
||||
- `/Game/Agrarian/Materials/M_AGR_GZ_Stone_Sandstone`
|
||||
- `/Game/Agrarian/Materials/M_AGR_GZ_FreshWater`
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ the survival component.
|
||||
- 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.
|
||||
yield item IDs, remaining harvests, stable persistence IDs, and readable
|
||||
first-pass resource material assignments.
|
||||
- Freshwater placement and interaction are covered by
|
||||
`Scripts/verify_ground_zero_water_source.py` and
|
||||
`Scripts/verify_water_gathering_interaction.py`.
|
||||
|
||||
@@ -10,6 +10,7 @@ MATERIALS = {
|
||||
"grass": "/Game/Agrarian/Materials/M_AGR_GZ_Grass_DryCoastal",
|
||||
"wood_resource": "/Game/Agrarian/Materials/M_AGR_GZ_Wood_Resource",
|
||||
"fiber_resource": "/Game/Agrarian/Materials/M_AGR_GZ_Fiber_Resource",
|
||||
"edible_plant_resource": "/Game/Agrarian/Materials/M_AGR_GZ_EdiblePlant_Resource",
|
||||
"stone_resource": "/Game/Agrarian/Materials/M_AGR_GZ_Stone_Sandstone",
|
||||
"fresh_water": "/Game/Agrarian/Materials/M_AGR_GZ_FreshWater",
|
||||
}
|
||||
@@ -21,6 +22,7 @@ EXPECTED_FOLIAGE_COUNTS = {
|
||||
RESOURCE_MATERIALS = {
|
||||
"AGR_GZ_Wood": "wood_resource",
|
||||
"AGR_GZ_Fiber": "fiber_resource",
|
||||
"AGR_GZ_EdiblePlant": "edible_plant_resource",
|
||||
"AGR_GZ_Stone": "stone_resource",
|
||||
"AGR_DemoWoodResource": "wood_resource",
|
||||
"AGR_DemoFiberResource": "fiber_resource",
|
||||
|
||||
@@ -2,6 +2,12 @@ import unreal
|
||||
|
||||
|
||||
MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test"
|
||||
MATERIALS = {
|
||||
"wood": "/Game/Agrarian/Materials/M_AGR_GZ_Wood_Resource",
|
||||
"fiber": "/Game/Agrarian/Materials/M_AGR_GZ_Fiber_Resource",
|
||||
"food": "/Game/Agrarian/Materials/M_AGR_GZ_EdiblePlant_Resource",
|
||||
"stone": "/Game/Agrarian/Materials/M_AGR_GZ_Stone_Sandstone",
|
||||
}
|
||||
|
||||
EXPECTED_RESOURCE_LABELS = {
|
||||
"wood": [
|
||||
@@ -31,6 +37,12 @@ EXPECTED_RESOURCE_LABELS = {
|
||||
}
|
||||
|
||||
|
||||
def material_path(material):
|
||||
if not material:
|
||||
return ""
|
||||
return material.get_path_name().split(".", 1)[0]
|
||||
|
||||
|
||||
def get_actor_label(actor):
|
||||
try:
|
||||
return actor.get_actor_label()
|
||||
@@ -48,6 +60,10 @@ def main():
|
||||
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
|
||||
raise RuntimeError(f"Could not load map: {MAP_PATH}")
|
||||
|
||||
for path in MATERIALS.values():
|
||||
if not unreal.EditorAssetLibrary.load_asset(path):
|
||||
raise RuntimeError(f"Missing required resource material: {path}")
|
||||
|
||||
actors_by_label = {get_actor_label(actor): actor for actor in unreal.EditorLevelLibrary.get_all_level_actors()}
|
||||
failures = []
|
||||
|
||||
@@ -69,6 +85,14 @@ def main():
|
||||
if persistence_node_id != label:
|
||||
failures.append(f"{label} persistence node id expected {label}, got {persistence_node_id}")
|
||||
|
||||
mesh_components = actor.get_components_by_class(unreal.StaticMeshComponent)
|
||||
if not mesh_components:
|
||||
failures.append(f"{label} has no static mesh component for MVP resource readability")
|
||||
continue
|
||||
expected_material = MATERIALS[expected_item_id]
|
||||
if not any(material_path(component.get_material(0)) == expected_material for component in mesh_components):
|
||||
failures.append(f"{label} expected readable material {expected_material}")
|
||||
|
||||
if failures:
|
||||
raise RuntimeError("Ground Zero resource verification failed: " + "; ".join(failures))
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ FRAME_RECIPE_PATH = "/Game/Agrarian/DataAssets/Recipes/DA_Recipe_PrimitiveFrame"
|
||||
WALL_PANEL_RECIPE_PATH = "/Game/Agrarian/DataAssets/Recipes/DA_Recipe_PrimitiveWallPanel"
|
||||
ROOF_PANEL_RECIPE_PATH = "/Game/Agrarian/DataAssets/Recipes/DA_Recipe_PrimitiveRoofPanel"
|
||||
SHELTER_CLASS_PATH = "/Game/Agrarian/Blueprints/Structures/BP_PrimitiveShelter"
|
||||
WOOD_NODE_LABEL = "AGR_WoodResourceNode_01"
|
||||
FIBER_NODE_LABEL = "AGR_FiberResourceNode_01"
|
||||
RABBIT_LABEL = "AGR_RabbitWildlife_01"
|
||||
WOOD_NODE_LABEL = "AGR_DemoWoodResource_01"
|
||||
FIBER_NODE_LABEL = "AGR_DemoFiberResource_01"
|
||||
RABBIT_LABEL = "AGR_DemoRabbitWildlife_01"
|
||||
|
||||
|
||||
def get_actor_label(actor):
|
||||
|
||||
@@ -3,46 +3,82 @@ import unreal
|
||||
|
||||
MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test"
|
||||
|
||||
EXPECTED_PLACEMENTS = {
|
||||
"AGR_WoodResourceNode_01": {
|
||||
EXPECTED_PLAYABLE_ACTORS = {
|
||||
"AGR_DemoPlayerStart": {
|
||||
"class": unreal.PlayerStart,
|
||||
},
|
||||
"AGR_DemoWoodResource_01": {
|
||||
"class_path": "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode",
|
||||
"location": unreal.Vector(650.0, -150.0, 120.0),
|
||||
"properties": {
|
||||
"remaining_harvests": 16,
|
||||
"quantity_per_harvest": 2,
|
||||
"persistence_node_id": "AGR_DemoWoodResource_01",
|
||||
},
|
||||
},
|
||||
"AGR_FiberResourceNode_01": {
|
||||
"AGR_DemoFiberResource_01": {
|
||||
"class_path": "/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode",
|
||||
"location": unreal.Vector(560.0, 140.0, 90.0),
|
||||
"properties": {
|
||||
"remaining_harvests": 10,
|
||||
"quantity_per_harvest": 3,
|
||||
"persistence_node_id": "AGR_DemoFiberResource_01",
|
||||
},
|
||||
},
|
||||
"AGR_Campfire_01": {
|
||||
"AGR_DemoCampfire_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": {
|
||||
"AGR_DemoPrimitiveShelter_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": {
|
||||
"AGR_DemoRabbitWildlife_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,
|
||||
},
|
||||
},
|
||||
"AGR_GZ_FreshWaterSource_01": {
|
||||
"class_path": "/Game/Agrarian/Blueprints/World/BP_FreshWaterSource",
|
||||
},
|
||||
"AGR_GroundZeroMapBoundary": {
|
||||
"class": unreal.AgrarianMapBoundaryVolume,
|
||||
"properties": {
|
||||
"boundary_id": "ground_zero_mvp_tile",
|
||||
"clamp_players_at_boundary": True,
|
||||
},
|
||||
},
|
||||
"AGR_DemoSkyLightingController": {
|
||||
"class": unreal.AgrarianSkyLightingController,
|
||||
},
|
||||
"AGR_DemoWeatherAudioController": {
|
||||
"class": unreal.AgrarianWeatherAudioController,
|
||||
},
|
||||
"AGR_DemoNoticeActor": {
|
||||
"class": unreal.AgrarianDemoNoticeActor,
|
||||
},
|
||||
}
|
||||
|
||||
LEGACY_FLAT_TEST_LABELS = {
|
||||
"AGR_WoodResourceNode_01",
|
||||
"AGR_FiberResourceNode_01",
|
||||
"AGR_Campfire_01",
|
||||
"AGR_PrimitiveShelter_01",
|
||||
"AGR_RabbitWildlife_01",
|
||||
}
|
||||
|
||||
NEAR_SPAWN_ACTORS = {
|
||||
"AGR_DemoWoodResource_01": 12000.0,
|
||||
"AGR_DemoFiberResource_01": 12000.0,
|
||||
"AGR_DemoCampfire_01": 12000.0,
|
||||
"AGR_DemoPrimitiveShelter_01": 12000.0,
|
||||
"AGR_DemoRabbitWildlife_01": 12000.0,
|
||||
"AGR_GZ_FreshWaterSource_01": 25000.0,
|
||||
}
|
||||
|
||||
|
||||
@@ -50,12 +86,12 @@ 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 distance_2d(left, right):
|
||||
return ((left.x - right.x) ** 2.0 + (left.y - right.y) ** 2.0) ** 0.5
|
||||
|
||||
|
||||
def class_path(actor):
|
||||
return actor.get_class().get_path_name()
|
||||
|
||||
|
||||
def get_actor_label(actor):
|
||||
@@ -65,6 +101,18 @@ def get_actor_label(actor):
|
||||
return actor.get_name()
|
||||
|
||||
|
||||
def verify_class(label, actor, expected, failures):
|
||||
expected_class = expected.get("class")
|
||||
if expected_class and not isinstance(actor, expected_class):
|
||||
failures.append(f"{label} expected class {expected_class.__name__}, got {actor.get_class().get_name()}")
|
||||
|
||||
expected_class_path = expected.get("class_path")
|
||||
if expected_class_path:
|
||||
actual_class_path = class_path(actor)
|
||||
if not actual_class_path.startswith(expected_class_path + "."):
|
||||
failures.append(f"{label} expected class under {expected_class_path}, got {actual_class_path}")
|
||||
|
||||
|
||||
def main():
|
||||
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
|
||||
raise RuntimeError(f"Could not load map: {MAP_PATH}")
|
||||
@@ -72,28 +120,50 @@ def main():
|
||||
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():
|
||||
for legacy_label in LEGACY_FLAT_TEST_LABELS:
|
||||
if legacy_label in actors_by_label:
|
||||
failures.append(f"legacy flat-test actor should not remain in Ground Zero map: {legacy_label}")
|
||||
|
||||
for label, expected in EXPECTED_PLAYABLE_ACTORS.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}")
|
||||
verify_class(label, actor, expected, failures)
|
||||
|
||||
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 isinstance(expected_value, bool):
|
||||
if bool(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}")
|
||||
|
||||
player_start = actors_by_label.get("AGR_DemoPlayerStart")
|
||||
if player_start:
|
||||
spawn_location = player_start.get_actor_location()
|
||||
if spawn_location.z < 150.0:
|
||||
failures.append(f"AGR_DemoPlayerStart expected safe above-terrain offset, got z {spawn_location.z}")
|
||||
|
||||
for label, max_distance in NEAR_SPAWN_ACTORS.items():
|
||||
actor = actors_by_label.get(label)
|
||||
if not actor:
|
||||
continue
|
||||
actual_distance = distance_2d(spawn_location, actor.get_actor_location())
|
||||
if actual_distance > max_distance:
|
||||
failures.append(f"{label} is {actual_distance:.0f}cm from spawn, above {max_distance:.0f}cm playable radius")
|
||||
|
||||
if failures:
|
||||
raise RuntimeError("Test map placement verification failed: " + "; ".join(failures))
|
||||
|
||||
unreal.log("Agrarian test map placement verification complete.")
|
||||
unreal.log(
|
||||
"Agrarian Ground Zero playable test map verification complete: "
|
||||
f"{len(EXPECTED_PLAYABLE_ACTORS)} critical actors checked."
|
||||
)
|
||||
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user