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] 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.
|
||||
- [ ] 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 mix settings.
|
||||
- [ ] Add volume sliders.
|
||||
|
||||
@@ -318,6 +318,11 @@ controller or a Blueprint child.
|
||||
|
||||
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
|
||||
`AAgrarianGameCharacter`. The character owns a spatialized
|
||||
`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 "AgrarianInventoryComponent.h"
|
||||
#include "AIController.h"
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
@@ -86,6 +87,11 @@ AAgrarianWildlifeBase::AAgrarianWildlifeBase()
|
||||
WildlifeTailProxy->SetRelativeRotation(FRotator(0.0f, 90.0f, 90.0f));
|
||||
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"));
|
||||
}
|
||||
|
||||
@@ -168,6 +174,10 @@ void AAgrarianWildlifeBase::SetWildlifeState(EAgrarianWildlifeState NewState)
|
||||
}
|
||||
|
||||
WildlifeState = NewState;
|
||||
if (HasAuthority())
|
||||
{
|
||||
MulticastPlayWildlifeStateSound(WildlifeState);
|
||||
}
|
||||
BroadcastStateChanged();
|
||||
}
|
||||
|
||||
@@ -204,6 +214,34 @@ void AAgrarianWildlifeBase::OnRep_WildlifeState()
|
||||
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)
|
||||
{
|
||||
if (!bEnablePerformanceLimits || WildlifeState == EAgrarianWildlifeState::Dead)
|
||||
@@ -524,6 +562,7 @@ bool AAgrarianWildlifeBase::Harvest(AAgrarianGameCharacter* Interactor)
|
||||
}
|
||||
|
||||
bHarvested = true;
|
||||
MulticastPlayWildlifeHarvestSound();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -536,3 +575,19 @@ void AAgrarianWildlifeBase::BroadcastStateChanged()
|
||||
{
|
||||
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"
|
||||
|
||||
class AAgrarianGameCharacter;
|
||||
class UAudioComponent;
|
||||
class USoundBase;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAgrarianWildlifeStateChangedSignature, EAgrarianWildlifeState, NewState);
|
||||
@@ -46,6 +48,9 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals")
|
||||
TObjectPtr<UStaticMeshComponent> WildlifeTailProxy;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Audio")
|
||||
TObjectPtr<UAudioComponent> WildlifeAudioComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife")
|
||||
FName WildlifeId = TEXT("wildlife");
|
||||
|
||||
@@ -109,6 +114,18 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Wildlife|Harvest")
|
||||
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")
|
||||
bool IsAlive() const;
|
||||
|
||||
@@ -131,6 +148,12 @@ protected:
|
||||
UFUNCTION()
|
||||
void OnRep_WildlifeState();
|
||||
|
||||
UFUNCTION(NetMulticast, Unreliable)
|
||||
void MulticastPlayWildlifeStateSound(EAgrarianWildlifeState NewState);
|
||||
|
||||
UFUNCTION(NetMulticast, Unreliable)
|
||||
void MulticastPlayWildlifeHarvestSound();
|
||||
|
||||
bool ShouldRunServerThink(float DeltaSeconds);
|
||||
void ServerThink(float DeltaSeconds);
|
||||
void ChooseWanderTarget();
|
||||
@@ -146,6 +169,7 @@ protected:
|
||||
bool Harvest(AAgrarianGameCharacter* Interactor);
|
||||
void BroadcastHealthChanged();
|
||||
void BroadcastStateChanged();
|
||||
USoundBase* GetSoundForWildlifeState(EAgrarianWildlifeState State) const;
|
||||
|
||||
UPROPERTY()
|
||||
FVector SpawnLocation = FVector::ZeroVector;
|
||||
|
||||
Reference in New Issue
Block a user