From fb658baff0872d8d2f4df8af6e4f5376f49148bb Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 19 May 2026 12:01:24 -0700 Subject: [PATCH] Add MVP ambient biome audio hook --- AGRARIAN_DEVELOPMENT_ROADMAP.md | 2 +- Docs/TechnicalDesignDocument.md | 22 ++++--- Scripts/verify_ambient_biome_audio.py | 58 +++++++++++++++++++ .../AgrarianWeatherAudioController.cpp | 10 +++- .../AgrarianWeatherAudioController.h | 9 +++ 5 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 Scripts/verify_ambient_biome_audio.py diff --git a/AGRARIAN_DEVELOPMENT_ROADMAP.md b/AGRARIAN_DEVELOPMENT_ROADMAP.md index df0186f..12e18b1 100644 --- a/AGRARIAN_DEVELOPMENT_ROADMAP.md +++ b/AGRARIAN_DEVELOPMENT_ROADMAP.md @@ -833,7 +833,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe ## 0.1.P MVP Audio And Atmosphere -- [ ] Add ambient biome audio. +- [x] Add ambient biome audio. Extended the placed `AAgrarianWeatherAudioController` so its ambient component explicitly owns a Ground Zero coastal-scrub biome loop slot with separate day/night volume targets, keeping the current MVP silent until placeholder or final audio assets are assigned while giving the map a real ambient audio attachment point. - [ ] Add footstep placeholders. - [ ] Add gathering sounds. - [ ] Add fire sounds. diff --git a/Docs/TechnicalDesignDocument.md b/Docs/TechnicalDesignDocument.md index 7d2f8b8..26e0082 100644 --- a/Docs/TechnicalDesignDocument.md +++ b/Docs/TechnicalDesignDocument.md @@ -302,15 +302,19 @@ the represented local day/night cycle and current weather without hard-coded static light settings. The Ground Zero map setup script places this controller and removes the earlier static demo sun/skylight/fog actors. -First-pass weather audio uses `AAgrarianWeatherAudioController`. The controller -owns ambient, rain, wind, and storm audio components with assignable loop sound -slots. It reads replicated weather state, provider wind speed, provider cloud -data, and local night/day state, then fades component volumes so rain, wind, and -storm cues follow the same authoritative weather mapping used by temperature and -lighting. The current MVP can ship without final sound assets because the -controller is silent until loops are assigned; placeholder or final audio can be -added by setting the exposed sound properties on the placed controller or a -Blueprint child. +First-pass biome and weather audio uses `AAgrarianWeatherAudioController`. The +controller owns ambient, rain, wind, and storm audio components with assignable +loop sound slots. The ambient component is explicitly the Ground Zero +coastal-scrub biome bed through `BiomeAmbientLoopSound`, with separate day and +night volume targets so the map can carry wind, surf, insects, distant birds, or +other realistic low-level biome texture before final authored assets are +available. It reads replicated weather state, provider wind speed, provider +cloud data, and local night/day state, then fades component volumes so rain, +wind, and storm cues follow the same authoritative weather mapping used by +temperature and lighting. The current MVP can ship without final sound assets +because the controller is silent until loops are assigned; placeholder or final +audio can be added by setting the exposed sound properties on the placed +controller or a Blueprint child. Campfires expose native extinguish logic through `AAgrarianCampfire::Extinguish`. Extinguishing clears remaining fuel, turns off replicated lit state, and reuses diff --git a/Scripts/verify_ambient_biome_audio.py b/Scripts/verify_ambient_biome_audio.py new file mode 100644 index 0000000..6b8d2b5 --- /dev/null +++ b/Scripts/verify_ambient_biome_audio.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Verify the MVP ambient biome audio attachment point is wired.""" + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +AUDIO_H = ROOT / "Source" / "AgrarianGame" / "AgrarianWeatherAudioController.h" +AUDIO_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianWeatherAudioController.cpp" +MAP_SETUP = ROOT / "Scripts" / "setup_ground_zero_demo_map.py" +TDD = ROOT / "Docs" / "TechnicalDesignDocument.md" +ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md" + + +REQUIRED = { + AUDIO_H: [ + "TObjectPtr BiomeAmbientLoopSound;", + "float BiomeAmbientDayVolume", + "float BiomeAmbientNightVolume", + "TObjectPtr AmbientAudio;", + ], + AUDIO_CPP: [ + "BiomeAmbientLoopSound", + "AmbientAudio->SetSound(BiomeAmbientLoopSound)", + "FMath::Max(AmbientDayVolume, BiomeAmbientDayVolume)", + "FMath::Max(AmbientNightVolume, BiomeAmbientNightVolume)", + ], + MAP_SETUP: [ + "AGR_DemoWeatherAudioController", + "unreal.AgrarianWeatherAudioController", + ], + TDD: [ + "coastal-scrub biome bed", + "`BiomeAmbientLoopSound`", + "separate day and\nnight volume targets", + ], + ROADMAP: [ + "[x] Add ambient biome audio.", + ], +} + + +def main() -> None: + missing = [] + for path, snippets in REQUIRED.items(): + text = path.read_text(encoding="utf-8") + for snippet in snippets: + if snippet not in text: + missing.append(f"{path.relative_to(ROOT)} missing {snippet!r}") + + if missing: + raise SystemExit("FAILED: " + "; ".join(missing)) + + print("OK: ambient biome audio is wired through the placed weather audio controller.") + + +if __name__ == "__main__": + main() diff --git a/Source/AgrarianGame/AgrarianWeatherAudioController.cpp b/Source/AgrarianGame/AgrarianWeatherAudioController.cpp index ed0b494..a67d3d3 100644 --- a/Source/AgrarianGame/AgrarianWeatherAudioController.cpp +++ b/Source/AgrarianGame/AgrarianWeatherAudioController.cpp @@ -83,7 +83,9 @@ void AAgrarianWeatherAudioController::RefreshWeatherAudio(float DeltaSeconds) break; } - const float TargetAmbientVolume = GameState->IsNight() ? AmbientNightVolume : AmbientDayVolume; + const float DayAmbient = FMath::Max(AmbientDayVolume, BiomeAmbientDayVolume); + const float NightAmbient = FMath::Max(AmbientNightVolume, BiomeAmbientNightVolume); + const float TargetAmbientVolume = GameState->IsNight() ? NightAmbient : DayAmbient; const float InterpSpeed = FMath::Max(0.1f, VolumeInterpSpeed); CurrentAmbientVolume = FMath::FInterpTo(CurrentAmbientVolume, TargetAmbientVolume, DeltaSeconds, InterpSpeed); CurrentRainVolume = FMath::FInterpTo(CurrentRainVolume, TargetRainVolume, DeltaSeconds, InterpSpeed); @@ -106,7 +108,11 @@ void AAgrarianWeatherAudioController::RefreshWeatherAudio(float DeltaSeconds) void AAgrarianWeatherAudioController::AssignConfiguredSounds() { - if (AmbientAudio && ClearAmbientSound) + if (AmbientAudio && BiomeAmbientLoopSound) + { + AmbientAudio->SetSound(BiomeAmbientLoopSound); + } + else if (AmbientAudio && ClearAmbientSound) { AmbientAudio->SetSound(ClearAmbientSound); } diff --git a/Source/AgrarianGame/AgrarianWeatherAudioController.h b/Source/AgrarianGame/AgrarianWeatherAudioController.h index 556c243..7f34ece 100644 --- a/Source/AgrarianGame/AgrarianWeatherAudioController.h +++ b/Source/AgrarianGame/AgrarianWeatherAudioController.h @@ -40,6 +40,9 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio") TObjectPtr ClearAmbientSound; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio|Biome") + TObjectPtr BiomeAmbientLoopSound; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio") TObjectPtr RainLoopSound; @@ -58,6 +61,12 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0")) float AmbientNightVolume = 0.22f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio|Biome", meta = (ClampMin = "0.0", ClampMax = "1.0")) + float BiomeAmbientDayVolume = 0.35f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio|Biome", meta = (ClampMin = "0.0", ClampMax = "1.0")) + float BiomeAmbientNightVolume = 0.22f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Audio", meta = (ClampMin = "0.0", ClampMax = "1.0")) float MaxRainVolume = 0.8f;