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 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.
|
||||
- [ ] 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 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.
|
||||
|
||||
@@ -330,6 +330,13 @@ Each resource node exposes `GatheringSound` and `DepletedGatheringSound` slots,
|
||||
with a spatialized `GatheringAudioComponent`; the system remains silent until
|
||||
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`.
|
||||
Extinguishing clears remaining fuel, turns off replicated lit state, and reuses
|
||||
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 "AgrarianSurvivalComponent.h"
|
||||
#include "Particles/ParticleSystemComponent.h"
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "Components/PointLightComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
@@ -108,6 +109,16 @@ AAgrarianCampfire::AAgrarianCampfire()
|
||||
SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
|
||||
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->ActorTypeId = TEXT("campfire");
|
||||
}
|
||||
@@ -281,6 +292,23 @@ void AAgrarianCampfire::OnRep_FireState()
|
||||
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
|
||||
{
|
||||
if (const UWorld* World = GetWorld())
|
||||
@@ -296,11 +324,17 @@ EAgrarianWeatherType AAgrarianCampfire::GetCurrentWeather() const
|
||||
|
||||
void AAgrarianCampfire::SetLit(bool bNewLit)
|
||||
{
|
||||
const bool bChanged = bLit != bNewLit;
|
||||
if (bLit != bNewLit)
|
||||
{
|
||||
bLit = bNewLit;
|
||||
}
|
||||
|
||||
if (HasAuthority() && bChanged)
|
||||
{
|
||||
MulticastPlayFireEventSound(bLit);
|
||||
}
|
||||
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
@@ -323,6 +357,25 @@ void AAgrarianCampfire::UpdateVisualState()
|
||||
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)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
class UPointLightComponent;
|
||||
class UParticleSystemComponent;
|
||||
class UAgrarianPersistentActorComponent;
|
||||
class UAudioComponent;
|
||||
class USoundBase;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
@@ -49,6 +51,12 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Effects")
|
||||
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")
|
||||
TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;
|
||||
|
||||
@@ -85,6 +93,15 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather")
|
||||
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 bool CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const override;
|
||||
virtual void Interact_Implementation(AAgrarianGameCharacter* Interactor) override;
|
||||
@@ -113,6 +130,9 @@ protected:
|
||||
UFUNCTION()
|
||||
void OnRep_FireState();
|
||||
|
||||
UFUNCTION(NetMulticast, Unreliable)
|
||||
void MulticastPlayFireEventSound(bool bIgnited);
|
||||
|
||||
EAgrarianWeatherType GetCurrentWeather() const;
|
||||
void SetLit(bool bNewLit);
|
||||
void UpdateVisualState();
|
||||
|
||||
Reference in New Issue
Block a user