Add MVP campfire audio hooks
This commit is contained in:
@@ -836,7 +836,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
|||||||
- [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.
|
- [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.
|
||||||
- [x] Add footstep placeholders. Added native player-character footstep hooks with assignable walk, sprint, crouch, and prone sound slots plus movement-state cadence, keeping the MVP silent until placeholder or final surface-aware audio assets are assigned.
|
- [x] Add footstep placeholders. Added native player-character footstep hooks with assignable walk, sprint, crouch, and prone sound slots plus movement-state cadence, keeping the MVP silent until placeholder or final surface-aware audio assets are assigned.
|
||||||
- [x] Add gathering sounds. Added spatialized resource-node gathering audio hooks with assignable normal/depleted gathering cues and a server-authoritative multicast trigger after successful harvests, keeping multiplayer clients aligned while remaining silent until audio assets are assigned.
|
- [x] Add gathering sounds. Added spatialized resource-node gathering audio hooks with assignable normal/depleted gathering cues and a server-authoritative multicast trigger after successful harvests, keeping multiplayer clients aligned while remaining silent until audio assets are assigned.
|
||||||
- [ ] Add fire sounds.
|
- [x] Add fire sounds. Added campfire loop, ignition, and extinguish audio hooks with spatialized components, replicated lit-state loop control, and server-triggered multicast event cues so fire audio follows the authoritative campfire state once assets are assigned.
|
||||||
- [ ] Add unattended and poorly maintained fire risk for campfires and other open-flame sources.
|
- [ ] Add unattended and poorly maintained fire risk for campfires and other open-flame sources.
|
||||||
- [ ] Add grass and forest ignition checks from irresponsible fire placement, wind/weather, dry fuel, nearby vegetation, and burn duration.
|
- [ ] Add grass and forest ignition checks from irresponsible fire placement, wind/weather, dry fuel, nearby vegetation, and burn duration.
|
||||||
- [ ] Add shelter/structure ignition risk when fires are placed too close to primitive shelters, wood piles, flammable crafting stations, or settlement objects.
|
- [ ] Add shelter/structure ignition risk when fires are placed too close to primitive shelters, wood piles, flammable crafting stations, or settlement objects.
|
||||||
|
|||||||
@@ -330,6 +330,13 @@ Each resource node exposes `GatheringSound` and `DepletedGatheringSound` slots,
|
|||||||
with a spatialized `GatheringAudioComponent`; the system remains silent until
|
with a spatialized `GatheringAudioComponent`; the system remains silent until
|
||||||
resource-specific placeholder or final cues are assigned.
|
resource-specific placeholder or final cues are assigned.
|
||||||
|
|
||||||
|
Campfire audio is split between a persistent spatialized loop and short
|
||||||
|
server-triggered event cues. `AAgrarianCampfire` exposes `FireLoopSound`,
|
||||||
|
`IgniteSound`, and `ExtinguishSound` slots. Replicated lit-state updates start
|
||||||
|
or stop the loop on clients, while the authoritative server multicasts ignition
|
||||||
|
and extinguish events so the audio follows the same state changes as light,
|
||||||
|
smoke, warmth, fuel, and persistence.
|
||||||
|
|
||||||
Campfires expose native extinguish logic through `AAgrarianCampfire::Extinguish`.
|
Campfires expose native extinguish logic through `AAgrarianCampfire::Extinguish`.
|
||||||
Extinguishing clears remaining fuel, turns off replicated lit state, and reuses
|
Extinguishing clears remaining fuel, turns off replicated lit state, and reuses
|
||||||
the same visual update path as natural fuel depletion.
|
the same visual update path as natural fuel depletion.
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Verify MVP fire sound hooks follow authoritative campfire state."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
FIRE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.h"
|
||||||
|
FIRE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.cpp"
|
||||||
|
TDD = ROOT / "Docs" / "TechnicalDesignDocument.md"
|
||||||
|
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||||
|
|
||||||
|
REQUIRED = {
|
||||||
|
FIRE_H: [
|
||||||
|
"TObjectPtr<UAudioComponent> FireLoopAudioComponent;",
|
||||||
|
"TObjectPtr<UAudioComponent> FireEventAudioComponent;",
|
||||||
|
"TObjectPtr<USoundBase> FireLoopSound;",
|
||||||
|
"TObjectPtr<USoundBase> IgniteSound;",
|
||||||
|
"TObjectPtr<USoundBase> ExtinguishSound;",
|
||||||
|
"UFUNCTION(NetMulticast, Unreliable)",
|
||||||
|
"void MulticastPlayFireEventSound(bool bIgnited);",
|
||||||
|
],
|
||||||
|
FIRE_CPP: [
|
||||||
|
"#include \"Components/AudioComponent.h\"",
|
||||||
|
"FireLoopAudioComponent = CreateDefaultSubobject<UAudioComponent>",
|
||||||
|
"FireEventAudioComponent = CreateDefaultSubobject<UAudioComponent>",
|
||||||
|
"FireLoopAudioComponent->bAllowSpatialization = true",
|
||||||
|
"FireEventAudioComponent->bAllowSpatialization = true",
|
||||||
|
"AAgrarianCampfire::MulticastPlayFireEventSound_Implementation",
|
||||||
|
"MulticastPlayFireEventSound(bLit);",
|
||||||
|
"FireLoopAudioComponent->Play();",
|
||||||
|
"FireLoopAudioComponent->Stop();",
|
||||||
|
],
|
||||||
|
TDD: [
|
||||||
|
"Campfire audio is split between a persistent spatialized loop",
|
||||||
|
"`FireLoopSound`",
|
||||||
|
"`IgniteSound`",
|
||||||
|
"`ExtinguishSound`",
|
||||||
|
],
|
||||||
|
ROADMAP: [
|
||||||
|
"[x] Add fire 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: fire sound hooks follow authoritative campfire state.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "AgrarianPersistentActorComponent.h"
|
#include "AgrarianPersistentActorComponent.h"
|
||||||
#include "AgrarianSurvivalComponent.h"
|
#include "AgrarianSurvivalComponent.h"
|
||||||
#include "Particles/ParticleSystemComponent.h"
|
#include "Particles/ParticleSystemComponent.h"
|
||||||
|
#include "Components/AudioComponent.h"
|
||||||
#include "Components/PointLightComponent.h"
|
#include "Components/PointLightComponent.h"
|
||||||
#include "Components/StaticMeshComponent.h"
|
#include "Components/StaticMeshComponent.h"
|
||||||
#include "Engine/StaticMesh.h"
|
#include "Engine/StaticMesh.h"
|
||||||
@@ -108,6 +109,16 @@ AAgrarianCampfire::AAgrarianCampfire()
|
|||||||
SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
|
SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
|
||||||
SmokeEffect->SetVisibility(false);
|
SmokeEffect->SetVisibility(false);
|
||||||
|
|
||||||
|
FireLoopAudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("FireLoopAudioComponent"));
|
||||||
|
FireLoopAudioComponent->SetupAttachment(RootComponent);
|
||||||
|
FireLoopAudioComponent->bAutoActivate = false;
|
||||||
|
FireLoopAudioComponent->bAllowSpatialization = true;
|
||||||
|
|
||||||
|
FireEventAudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("FireEventAudioComponent"));
|
||||||
|
FireEventAudioComponent->SetupAttachment(RootComponent);
|
||||||
|
FireEventAudioComponent->bAutoActivate = false;
|
||||||
|
FireEventAudioComponent->bAllowSpatialization = true;
|
||||||
|
|
||||||
PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
|
PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
|
||||||
PersistentActorComponent->ActorTypeId = TEXT("campfire");
|
PersistentActorComponent->ActorTypeId = TEXT("campfire");
|
||||||
}
|
}
|
||||||
@@ -281,6 +292,23 @@ void AAgrarianCampfire::OnRep_FireState()
|
|||||||
UpdateVisualState();
|
UpdateVisualState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAgrarianCampfire::MulticastPlayFireEventSound_Implementation(bool bIgnited)
|
||||||
|
{
|
||||||
|
if (GetNetMode() == NM_DedicatedServer || !FireEventAudioComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
USoundBase* EventSound = bIgnited ? IgniteSound : ExtinguishSound;
|
||||||
|
if (!EventSound)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FireEventAudioComponent->SetSound(EventSound);
|
||||||
|
FireEventAudioComponent->Play();
|
||||||
|
}
|
||||||
|
|
||||||
EAgrarianWeatherType AAgrarianCampfire::GetCurrentWeather() const
|
EAgrarianWeatherType AAgrarianCampfire::GetCurrentWeather() const
|
||||||
{
|
{
|
||||||
if (const UWorld* World = GetWorld())
|
if (const UWorld* World = GetWorld())
|
||||||
@@ -296,11 +324,17 @@ EAgrarianWeatherType AAgrarianCampfire::GetCurrentWeather() const
|
|||||||
|
|
||||||
void AAgrarianCampfire::SetLit(bool bNewLit)
|
void AAgrarianCampfire::SetLit(bool bNewLit)
|
||||||
{
|
{
|
||||||
|
const bool bChanged = bLit != bNewLit;
|
||||||
if (bLit != bNewLit)
|
if (bLit != bNewLit)
|
||||||
{
|
{
|
||||||
bLit = bNewLit;
|
bLit = bNewLit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasAuthority() && bChanged)
|
||||||
|
{
|
||||||
|
MulticastPlayFireEventSound(bLit);
|
||||||
|
}
|
||||||
|
|
||||||
UpdateVisualState();
|
UpdateVisualState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,6 +357,25 @@ void AAgrarianCampfire::UpdateVisualState()
|
|||||||
SmokeEffect->DeactivateSystem();
|
SmokeEffect->DeactivateSystem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FireLoopAudioComponent)
|
||||||
|
{
|
||||||
|
if (bLit && FireLoopSound)
|
||||||
|
{
|
||||||
|
if (FireLoopAudioComponent->Sound != FireLoopSound)
|
||||||
|
{
|
||||||
|
FireLoopAudioComponent->SetSound(FireLoopSound);
|
||||||
|
}
|
||||||
|
if (!FireLoopAudioComponent->IsPlaying())
|
||||||
|
{
|
||||||
|
FireLoopAudioComponent->Play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (FireLoopAudioComponent->IsPlaying())
|
||||||
|
{
|
||||||
|
FireLoopAudioComponent->Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AAgrarianCampfire::WarmNearbyCharacters(float DeltaSeconds)
|
void AAgrarianCampfire::WarmNearbyCharacters(float DeltaSeconds)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
class UPointLightComponent;
|
class UPointLightComponent;
|
||||||
class UParticleSystemComponent;
|
class UParticleSystemComponent;
|
||||||
class UAgrarianPersistentActorComponent;
|
class UAgrarianPersistentActorComponent;
|
||||||
|
class UAudioComponent;
|
||||||
|
class USoundBase;
|
||||||
class UStaticMeshComponent;
|
class UStaticMeshComponent;
|
||||||
|
|
||||||
UCLASS(Blueprintable)
|
UCLASS(Blueprintable)
|
||||||
@@ -49,6 +51,12 @@ public:
|
|||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Effects")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Effects")
|
||||||
TObjectPtr<UParticleSystemComponent> SmokeEffect;
|
TObjectPtr<UParticleSystemComponent> SmokeEffect;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Audio")
|
||||||
|
TObjectPtr<UAudioComponent> FireLoopAudioComponent;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Audio")
|
||||||
|
TObjectPtr<UAudioComponent> FireEventAudioComponent;
|
||||||
|
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Persistence")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Persistence")
|
||||||
TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;
|
TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;
|
||||||
|
|
||||||
@@ -85,6 +93,15 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather")
|
||||||
bool bWetWeatherCanExtinguish = true;
|
bool bWetWeatherCanExtinguish = true;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Audio")
|
||||||
|
TObjectPtr<USoundBase> FireLoopSound;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Audio")
|
||||||
|
TObjectPtr<USoundBase> IgniteSound;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Audio")
|
||||||
|
TObjectPtr<USoundBase> ExtinguishSound;
|
||||||
|
|
||||||
virtual FText GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const override;
|
virtual FText GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const override;
|
||||||
virtual bool CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const override;
|
virtual bool CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const override;
|
||||||
virtual void Interact_Implementation(AAgrarianGameCharacter* Interactor) override;
|
virtual void Interact_Implementation(AAgrarianGameCharacter* Interactor) override;
|
||||||
@@ -113,6 +130,9 @@ protected:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void OnRep_FireState();
|
void OnRep_FireState();
|
||||||
|
|
||||||
|
UFUNCTION(NetMulticast, Unreliable)
|
||||||
|
void MulticastPlayFireEventSound(bool bIgnited);
|
||||||
|
|
||||||
EAgrarianWeatherType GetCurrentWeather() const;
|
EAgrarianWeatherType GetCurrentWeather() const;
|
||||||
void SetLit(bool bNewLit);
|
void SetLit(bool bNewLit);
|
||||||
void UpdateVisualState();
|
void UpdateVisualState();
|
||||||
|
|||||||
Reference in New Issue
Block a user