Add MVP wildlife audio hooks
This commit is contained in:
@@ -846,7 +846,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
|||||||
- [x] Persist active grass, forest, and structure fires across save/load without corrupting world state. Extended campfire persistence coverage for ignition flags, ignition risk scores, active grass/forest/structure fire intensities, spread radius, and suppression pressure so save/load recovery preserves active and partially suppressed fire state.
|
- [x] Persist active grass, forest, and structure fires across save/load without corrupting world state. Extended campfire persistence coverage for ignition flags, ignition risk scores, active grass/forest/structure fire intensities, spread radius, and suppression pressure so save/load recovery preserves active and partially suppressed fire state.
|
||||||
- [x] Add QA coverage for safe campfires, unsafe campfires, vegetation spread, shelter ignition, suppression, and save/load recovery. Added a fire-risk QA coverage document and verifier requiring safe/unsafe campfire, vegetation spread, shelter ignition, suppression, and save/load recovery scenarios plus the supporting fire-risk verification scripts.
|
- [x] Add QA coverage for safe campfires, unsafe campfires, vegetation spread, shelter ignition, suppression, and save/load recovery. Added a fire-risk QA coverage document and verifier requiring safe/unsafe campfire, vegetation spread, shelter ignition, suppression, and save/load recovery scenarios plus the supporting fire-risk verification scripts.
|
||||||
- [x] Add weather sounds. Formalized the existing placed weather audio controller as the MVP weather-sound path, documenting rain, wind, storm, clear ambient, and biome loop slots plus verification that weather playback follows replicated weather state, provider wind speed, and day/night state while remaining silent until assets are assigned.
|
- [x] Add weather sounds. Formalized the existing placed weather audio controller as the MVP weather-sound path, documenting rain, wind, storm, clear ambient, and biome loop slots plus verification that weather playback follows replicated weather state, provider wind speed, and day/night state while remaining silent until assets are assigned.
|
||||||
- [ ] Add wildlife sounds.
|
- [x] Add wildlife sounds. Added spatialized wildlife audio hooks with assignable idle, flee/chase, death, and harvest sound slots plus server-triggered multicast playback from authoritative wildlife state changes and harvest events.
|
||||||
- [ ] Add UI sounds.
|
- [ ] Add UI sounds.
|
||||||
- [ ] Add mix settings.
|
- [ ] Add mix settings.
|
||||||
- [ ] Add volume sliders.
|
- [ ] Add volume sliders.
|
||||||
|
|||||||
@@ -318,6 +318,11 @@ controller or a Blueprint child.
|
|||||||
|
|
||||||
Weather sound requirements are tracked in `Docs/Audio/WeatherSounds.md`.
|
Weather sound requirements are tracked in `Docs/Audio/WeatherSounds.md`.
|
||||||
|
|
||||||
|
Wildlife sounds are optional, spatialized hooks on `AAgrarianWildlifeBase`.
|
||||||
|
Wildlife expose idle, flee/chase, death, and harvest sound slots. The server
|
||||||
|
multicasts state-change and harvest cues so clients hear wildlife reactions from
|
||||||
|
the authoritative AI state while the dedicated server remains silent.
|
||||||
|
|
||||||
Player movement audio starts with native footstep placeholders on
|
Player movement audio starts with native footstep placeholders on
|
||||||
`AAgrarianGameCharacter`. The character owns a spatialized
|
`AAgrarianGameCharacter`. The character owns a spatialized
|
||||||
`FootstepAudioComponent` plus assignable walk, sprint, crouch, and prone sound
|
`FootstepAudioComponent` plus assignable walk, sprint, crouch, and prone sound
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Verify MVP wildlife sound hooks are authoritative and spatialized."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
WILDLIFE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianWildlifeBase.h"
|
||||||
|
WILDLIFE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianWildlifeBase.cpp"
|
||||||
|
TDD = ROOT / "Docs" / "TechnicalDesignDocument.md"
|
||||||
|
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||||
|
|
||||||
|
REQUIRED = {
|
||||||
|
WILDLIFE_H: [
|
||||||
|
"TObjectPtr<UAudioComponent> WildlifeAudioComponent;",
|
||||||
|
"TObjectPtr<USoundBase> IdleWildlifeSound;",
|
||||||
|
"TObjectPtr<USoundBase> FleeWildlifeSound;",
|
||||||
|
"TObjectPtr<USoundBase> DeathWildlifeSound;",
|
||||||
|
"TObjectPtr<USoundBase> HarvestWildlifeSound;",
|
||||||
|
"UFUNCTION(NetMulticast, Unreliable)",
|
||||||
|
"void MulticastPlayWildlifeStateSound(EAgrarianWildlifeState NewState);",
|
||||||
|
"void MulticastPlayWildlifeHarvestSound();",
|
||||||
|
"USoundBase* GetSoundForWildlifeState(EAgrarianWildlifeState State) const;",
|
||||||
|
],
|
||||||
|
WILDLIFE_CPP: [
|
||||||
|
"#include \"Components/AudioComponent.h\"",
|
||||||
|
"WildlifeAudioComponent = CreateDefaultSubobject<UAudioComponent>",
|
||||||
|
"WildlifeAudioComponent->bAllowSpatialization = true",
|
||||||
|
"MulticastPlayWildlifeStateSound(WildlifeState);",
|
||||||
|
"AAgrarianWildlifeBase::MulticastPlayWildlifeStateSound_Implementation",
|
||||||
|
"AAgrarianWildlifeBase::MulticastPlayWildlifeHarvestSound_Implementation",
|
||||||
|
"MulticastPlayWildlifeHarvestSound();",
|
||||||
|
"EAgrarianWildlifeState::Fleeing",
|
||||||
|
"EAgrarianWildlifeState::Dead",
|
||||||
|
],
|
||||||
|
TDD: [
|
||||||
|
"Wildlife sounds are optional, spatialized hooks",
|
||||||
|
"idle, flee/chase, death, and harvest",
|
||||||
|
"server\nmulticasts state-change and harvest cues",
|
||||||
|
],
|
||||||
|
ROADMAP: [
|
||||||
|
"[x] Add wildlife sounds.",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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: wildlife sound hooks are authoritative and spatialized.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "AgrarianGameCharacter.h"
|
#include "AgrarianGameCharacter.h"
|
||||||
#include "AgrarianInventoryComponent.h"
|
#include "AgrarianInventoryComponent.h"
|
||||||
#include "AIController.h"
|
#include "AIController.h"
|
||||||
|
#include "Components/AudioComponent.h"
|
||||||
#include "Components/StaticMeshComponent.h"
|
#include "Components/StaticMeshComponent.h"
|
||||||
#include "Engine/StaticMesh.h"
|
#include "Engine/StaticMesh.h"
|
||||||
#include "GameFramework/CharacterMovementComponent.h"
|
#include "GameFramework/CharacterMovementComponent.h"
|
||||||
@@ -86,6 +87,11 @@ AAgrarianWildlifeBase::AAgrarianWildlifeBase()
|
|||||||
WildlifeTailProxy->SetRelativeRotation(FRotator(0.0f, 90.0f, 90.0f));
|
WildlifeTailProxy->SetRelativeRotation(FRotator(0.0f, 90.0f, 90.0f));
|
||||||
WildlifeTailProxy->SetRelativeScale3D(FVector(0.12f, 0.12f, 0.24f));
|
WildlifeTailProxy->SetRelativeScale3D(FVector(0.12f, 0.12f, 0.24f));
|
||||||
|
|
||||||
|
WildlifeAudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("WildlifeAudioComponent"));
|
||||||
|
WildlifeAudioComponent->SetupAttachment(RootComponent);
|
||||||
|
WildlifeAudioComponent->bAutoActivate = false;
|
||||||
|
WildlifeAudioComponent->bAllowSpatialization = true;
|
||||||
|
|
||||||
DisplayName = FText::FromString(TEXT("Wildlife"));
|
DisplayName = FText::FromString(TEXT("Wildlife"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +174,10 @@ void AAgrarianWildlifeBase::SetWildlifeState(EAgrarianWildlifeState NewState)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WildlifeState = NewState;
|
WildlifeState = NewState;
|
||||||
|
if (HasAuthority())
|
||||||
|
{
|
||||||
|
MulticastPlayWildlifeStateSound(WildlifeState);
|
||||||
|
}
|
||||||
BroadcastStateChanged();
|
BroadcastStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +214,34 @@ void AAgrarianWildlifeBase::OnRep_WildlifeState()
|
|||||||
BroadcastStateChanged();
|
BroadcastStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAgrarianWildlifeBase::MulticastPlayWildlifeStateSound_Implementation(EAgrarianWildlifeState NewState)
|
||||||
|
{
|
||||||
|
if (GetNetMode() == NM_DedicatedServer || !WildlifeAudioComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
USoundBase* StateSound = GetSoundForWildlifeState(NewState);
|
||||||
|
if (!StateSound)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WildlifeAudioComponent->SetSound(StateSound);
|
||||||
|
WildlifeAudioComponent->Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAgrarianWildlifeBase::MulticastPlayWildlifeHarvestSound_Implementation()
|
||||||
|
{
|
||||||
|
if (GetNetMode() == NM_DedicatedServer || !WildlifeAudioComponent || !HarvestWildlifeSound)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WildlifeAudioComponent->SetSound(HarvestWildlifeSound);
|
||||||
|
WildlifeAudioComponent->Play();
|
||||||
|
}
|
||||||
|
|
||||||
bool AAgrarianWildlifeBase::ShouldRunServerThink(float DeltaSeconds)
|
bool AAgrarianWildlifeBase::ShouldRunServerThink(float DeltaSeconds)
|
||||||
{
|
{
|
||||||
if (!bEnablePerformanceLimits || WildlifeState == EAgrarianWildlifeState::Dead)
|
if (!bEnablePerformanceLimits || WildlifeState == EAgrarianWildlifeState::Dead)
|
||||||
@@ -524,6 +562,7 @@ bool AAgrarianWildlifeBase::Harvest(AAgrarianGameCharacter* Interactor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bHarvested = true;
|
bHarvested = true;
|
||||||
|
MulticastPlayWildlifeHarvestSound();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,3 +575,19 @@ void AAgrarianWildlifeBase::BroadcastStateChanged()
|
|||||||
{
|
{
|
||||||
OnWildlifeStateChanged.Broadcast(WildlifeState);
|
OnWildlifeStateChanged.Broadcast(WildlifeState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
USoundBase* AAgrarianWildlifeBase::GetSoundForWildlifeState(EAgrarianWildlifeState State) const
|
||||||
|
{
|
||||||
|
switch (State)
|
||||||
|
{
|
||||||
|
case EAgrarianWildlifeState::Fleeing:
|
||||||
|
case EAgrarianWildlifeState::Chasing:
|
||||||
|
return FleeWildlifeSound;
|
||||||
|
case EAgrarianWildlifeState::Dead:
|
||||||
|
return DeathWildlifeSound;
|
||||||
|
case EAgrarianWildlifeState::Idle:
|
||||||
|
case EAgrarianWildlifeState::Wandering:
|
||||||
|
default:
|
||||||
|
return IdleWildlifeSound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "AgrarianWildlifeBase.generated.h"
|
#include "AgrarianWildlifeBase.generated.h"
|
||||||
|
|
||||||
class AAgrarianGameCharacter;
|
class AAgrarianGameCharacter;
|
||||||
|
class UAudioComponent;
|
||||||
|
class USoundBase;
|
||||||
class UStaticMeshComponent;
|
class UStaticMeshComponent;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAgrarianWildlifeStateChangedSignature, EAgrarianWildlifeState, NewState);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAgrarianWildlifeStateChangedSignature, EAgrarianWildlifeState, NewState);
|
||||||
@@ -46,6 +48,9 @@ public:
|
|||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals")
|
||||||
TObjectPtr<UStaticMeshComponent> WildlifeTailProxy;
|
TObjectPtr<UStaticMeshComponent> WildlifeTailProxy;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Audio")
|
||||||
|
TObjectPtr<UAudioComponent> WildlifeAudioComponent;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife")
|
||||||
FName WildlifeId = TEXT("wildlife");
|
FName WildlifeId = TEXT("wildlife");
|
||||||
|
|
||||||
@@ -109,6 +114,18 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Wildlife|Harvest")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Wildlife|Harvest")
|
||||||
bool bHarvested = false;
|
bool bHarvested = false;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Audio")
|
||||||
|
TObjectPtr<USoundBase> IdleWildlifeSound;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Audio")
|
||||||
|
TObjectPtr<USoundBase> FleeWildlifeSound;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Audio")
|
||||||
|
TObjectPtr<USoundBase> DeathWildlifeSound;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Audio")
|
||||||
|
TObjectPtr<USoundBase> HarvestWildlifeSound;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Wildlife")
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Wildlife")
|
||||||
bool IsAlive() const;
|
bool IsAlive() const;
|
||||||
|
|
||||||
@@ -131,6 +148,12 @@ protected:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void OnRep_WildlifeState();
|
void OnRep_WildlifeState();
|
||||||
|
|
||||||
|
UFUNCTION(NetMulticast, Unreliable)
|
||||||
|
void MulticastPlayWildlifeStateSound(EAgrarianWildlifeState NewState);
|
||||||
|
|
||||||
|
UFUNCTION(NetMulticast, Unreliable)
|
||||||
|
void MulticastPlayWildlifeHarvestSound();
|
||||||
|
|
||||||
bool ShouldRunServerThink(float DeltaSeconds);
|
bool ShouldRunServerThink(float DeltaSeconds);
|
||||||
void ServerThink(float DeltaSeconds);
|
void ServerThink(float DeltaSeconds);
|
||||||
void ChooseWanderTarget();
|
void ChooseWanderTarget();
|
||||||
@@ -146,6 +169,7 @@ protected:
|
|||||||
bool Harvest(AAgrarianGameCharacter* Interactor);
|
bool Harvest(AAgrarianGameCharacter* Interactor);
|
||||||
void BroadcastHealthChanged();
|
void BroadcastHealthChanged();
|
||||||
void BroadcastStateChanged();
|
void BroadcastStateChanged();
|
||||||
|
USoundBase* GetSoundForWildlifeState(EAgrarianWildlifeState State) const;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FVector SpawnLocation = FVector::ZeroVector;
|
FVector SpawnLocation = FVector::ZeroVector;
|
||||||
|
|||||||
Reference in New Issue
Block a user