Add MVP gathering audio hooks
This commit is contained in:
@@ -835,7 +835,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.
|
||||
- [ ] Add gathering sounds.
|
||||
- [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.
|
||||
- [ ] 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.
|
||||
|
||||
@@ -323,6 +323,13 @@ slots. Cadence is state-aware and driven by horizontal movement, so the MVP can
|
||||
remain silent until placeholder or final surface-aware cues are assigned while
|
||||
still giving designers a real hook for step audio in packaged builds.
|
||||
|
||||
Gathering audio is owned by `AAgrarianResourceNode`. Successful
|
||||
server-authoritative harvests call a multicast placeholder cue so nearby
|
||||
clients can hear the action without trusting client-side gathering requests.
|
||||
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.
|
||||
|
||||
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,54 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify MVP gathering sound hooks are server-triggered and multicast."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RESOURCE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianResourceNode.h"
|
||||
RESOURCE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianResourceNode.cpp"
|
||||
TDD = ROOT / "Docs" / "TechnicalDesignDocument.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
RESOURCE_H: [
|
||||
"TObjectPtr<UAudioComponent> GatheringAudioComponent;",
|
||||
"TObjectPtr<USoundBase> GatheringSound;",
|
||||
"TObjectPtr<USoundBase> DepletedGatheringSound;",
|
||||
"UFUNCTION(NetMulticast, Unreliable)",
|
||||
"void MulticastPlayGatheringSound(bool bDepletedAfterGather);",
|
||||
],
|
||||
RESOURCE_CPP: [
|
||||
"#include \"Components/AudioComponent.h\"",
|
||||
"GatheringAudioComponent = CreateDefaultSubobject<UAudioComponent>",
|
||||
"GatheringAudioComponent->bAllowSpatialization = true",
|
||||
"MulticastPlayGatheringSound(RemainingHarvests <= 0);",
|
||||
"AAgrarianResourceNode::MulticastPlayGatheringSound_Implementation",
|
||||
"GetNetMode() == NM_DedicatedServer",
|
||||
"GatheringAudioComponent->Play();",
|
||||
],
|
||||
TDD: [
|
||||
"Gathering audio is owned by `AAgrarianResourceNode`",
|
||||
"server-authoritative harvests call a multicast placeholder cue",
|
||||
"`GatheringSound` and `DepletedGatheringSound`",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add gathering 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: gathering sound hooks are server-triggered and multicast.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "AgrarianInventoryComponent.h"
|
||||
#include "AgrarianItemDefinitionAsset.h"
|
||||
#include "AgrarianSaveGame.h"
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
@@ -67,6 +68,11 @@ AAgrarianResourceNode::AAgrarianResourceNode()
|
||||
HarvestableMarkerProxy->SetRelativeLocation(FVector(0.0f, 22.0f, 36.0f));
|
||||
HarvestableMarkerProxy->SetRelativeScale3D(FVector(0.28f, 0.18f, 0.22f));
|
||||
|
||||
GatheringAudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("GatheringAudioComponent"));
|
||||
GatheringAudioComponent->SetupAttachment(RootComponent);
|
||||
GatheringAudioComponent->bAutoActivate = false;
|
||||
GatheringAudioComponent->bAllowSpatialization = true;
|
||||
|
||||
YieldItem.ItemId = TEXT("wood");
|
||||
YieldItem.DisplayName = FText::FromString(TEXT("Wood"));
|
||||
YieldItem.Quantity = 1;
|
||||
@@ -134,6 +140,7 @@ void AAgrarianResourceNode::Interact_Implementation(AAgrarianGameCharacter* Inte
|
||||
if (Inventory->AddItem(Granted))
|
||||
{
|
||||
RemainingHarvests--;
|
||||
MulticastPlayGatheringSound(RemainingHarvests <= 0);
|
||||
UpdateDepletedState();
|
||||
ScheduleRespawnIfNeeded();
|
||||
}
|
||||
@@ -145,6 +152,23 @@ void AAgrarianResourceNode::OnRep_RemainingHarvests()
|
||||
UpdateDepletedState();
|
||||
}
|
||||
|
||||
void AAgrarianResourceNode::MulticastPlayGatheringSound_Implementation(bool bDepletedAfterGather)
|
||||
{
|
||||
if (GetNetMode() == NM_DedicatedServer || !GatheringAudioComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USoundBase* SoundToPlay = (bDepletedAfterGather && DepletedGatheringSound) ? DepletedGatheringSound : GatheringSound;
|
||||
if (!SoundToPlay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GatheringAudioComponent->SetSound(SoundToPlay);
|
||||
GatheringAudioComponent->Play();
|
||||
}
|
||||
|
||||
FName AAgrarianResourceNode::GetResourcePersistenceId() const
|
||||
{
|
||||
return PersistenceNodeId != NAME_None ? PersistenceNodeId : GetFName();
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "AgrarianResourceNode.generated.h"
|
||||
|
||||
class UStaticMeshComponent;
|
||||
class UAudioComponent;
|
||||
class USoundBase;
|
||||
class UAgrarianItemDefinitionAsset;
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
@@ -33,6 +35,9 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Resource|Visuals")
|
||||
TObjectPtr<UStaticMeshComponent> HarvestableMarkerProxy;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Resource|Audio")
|
||||
TObjectPtr<UAudioComponent> GatheringAudioComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource")
|
||||
FAgrarianItemStack YieldItem;
|
||||
|
||||
@@ -66,6 +71,12 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Respawn", meta = (ClampMin = "1"))
|
||||
int32 MaxHarvests = 5;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Audio")
|
||||
TObjectPtr<USoundBase> GatheringSound;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Audio")
|
||||
TObjectPtr<USoundBase> DepletedGatheringSound;
|
||||
|
||||
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;
|
||||
@@ -83,6 +94,9 @@ protected:
|
||||
UFUNCTION()
|
||||
void OnRep_RemainingHarvests();
|
||||
|
||||
UFUNCTION(NetMulticast, Unreliable)
|
||||
void MulticastPlayGatheringSound(bool bDepletedAfterGather);
|
||||
|
||||
bool HasRequiredTool(const AAgrarianGameCharacter* Interactor) const;
|
||||
int32 GetHarvestQuantityFor(const AAgrarianGameCharacter* Interactor) const;
|
||||
FAgrarianItemStack MakeYieldStack(const AAgrarianGameCharacter* Interactor) const;
|
||||
|
||||
Reference in New Issue
Block a user