Add campfire persistence state
This commit is contained in:
@@ -594,7 +594,9 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
particle component placeholder that activates and hides with replicated fire
|
||||
state, leaving final visual assets assignable later.
|
||||
- [x] Add replication.
|
||||
- [ ] Add persistence.
|
||||
- [x] Add persistence. Added campfire persistent actor support for transform,
|
||||
lit state, fuel seconds, and cooking placeholder progress using the shared
|
||||
world-actor persistence path.
|
||||
- [x] Connect fire to body temperature.
|
||||
- [ ] Connect rain/weather to fire behavior.
|
||||
|
||||
|
||||
@@ -302,6 +302,12 @@ placeholder. It is attached above the fire, starts inactive, and follows the
|
||||
same replicated lit-state visual update path as fire light intensity so final
|
||||
smoke or ember assets can be assigned later without changing gameplay code.
|
||||
|
||||
Campfire persistence uses the shared `UAgrarianPersistentActorComponent` world
|
||||
actor path. `AAgrarianCampfire` implements the persistence-state provider hook
|
||||
to write lit state, remaining fuel, cooking enabled state, required cook time,
|
||||
and cooking progress into numeric save state, then restores those values before
|
||||
reapplying the fire visual state on load.
|
||||
|
||||
The first real-weather adapter is `UAgrarianWeatherProviderSubsystem`. It uses
|
||||
Open-Meteo forecast requests keyed by tile center latitude/longitude, parses the
|
||||
current temperature, daily low/high, precipitation, wind, humidity, cloud cover,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
REQUIRED = {
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianPersistentStateProvider.h": [
|
||||
"class IAgrarianPersistentStateProvider",
|
||||
"void CapturePersistentState(UAgrarianPersistentActorComponent* PersistentComponent) const;",
|
||||
"void ApplyPersistentState(UAgrarianPersistentActorComponent* PersistentComponent);",
|
||||
],
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianPersistentActorComponent.cpp": [
|
||||
"IAgrarianPersistentStateProvider::Execute_CapturePersistentState",
|
||||
"IAgrarianPersistentStateProvider::Execute_ApplyPersistentState",
|
||||
],
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.h": [
|
||||
"public IAgrarianPersistentStateProvider",
|
||||
"TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;",
|
||||
"CapturePersistentState_Implementation",
|
||||
"ApplyPersistentState_Implementation",
|
||||
],
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.cpp": [
|
||||
"PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT(\"PersistentActorComponent\"));",
|
||||
"PersistentActorComponent->ActorTypeId = TEXT(\"campfire\");",
|
||||
"NumberState.Add(TEXT(\"lit\")",
|
||||
"NumberState.Add(TEXT(\"fuel_seconds\")",
|
||||
"NumberState.Add(TEXT(\"cooking_progress_seconds\")",
|
||||
"void AAgrarianCampfire::ApplyPersistentState_Implementation",
|
||||
"SetLit(SavedLit && *SavedLit > 0.5f && FuelSeconds > 0.0f);",
|
||||
],
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.cpp": [
|
||||
"Persistence->RegisterWorldActorClass(TEXT(\"campfire\"), AAgrarianCampfire::StaticClass());",
|
||||
],
|
||||
ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md": [
|
||||
"- [x] Add persistence.",
|
||||
],
|
||||
ROOT / "Docs" / "TechnicalDesignDocument.md": [
|
||||
"Campfire persistence uses the shared `UAgrarianPersistentActorComponent` world",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
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("Campfire persistence verification failed:\n" + "\n".join(missing))
|
||||
|
||||
print("PASS: campfire persistence is implemented and documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "AgrarianCampfire.h"
|
||||
#include "AgrarianGameCharacter.h"
|
||||
#include "AgrarianInventoryComponent.h"
|
||||
#include "AgrarianPersistentActorComponent.h"
|
||||
#include "AgrarianSurvivalComponent.h"
|
||||
#include "Particles/ParticleSystemComponent.h"
|
||||
#include "Components/PointLightComponent.h"
|
||||
@@ -29,6 +30,9 @@ AAgrarianCampfire::AAgrarianCampfire()
|
||||
SmokeEffect->bAutoActivate = false;
|
||||
SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
|
||||
SmokeEffect->SetVisibility(false);
|
||||
|
||||
PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
|
||||
PersistentActorComponent->ActorTypeId = TEXT("campfire");
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::Tick(float DeltaSeconds)
|
||||
@@ -86,6 +90,56 @@ void AAgrarianCampfire::Interact_Implementation(AAgrarianGameCharacter* Interact
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const
|
||||
{
|
||||
if (!PersistentComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PersistentComponent->NumberState.Add(TEXT("lit"), bLit ? 1.0f : 0.0f);
|
||||
PersistentComponent->NumberState.Add(TEXT("fuel_seconds"), FuelSeconds);
|
||||
PersistentComponent->NumberState.Add(TEXT("cooking_placeholder_enabled"), bCookingPlaceholderEnabled ? 1.0f : 0.0f);
|
||||
PersistentComponent->NumberState.Add(TEXT("cooking_seconds_required"), CookingSecondsRequired);
|
||||
PersistentComponent->NumberState.Add(TEXT("cooking_progress_seconds"), CookingProgressSeconds);
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent)
|
||||
{
|
||||
if (!HasAuthority() || !PersistentComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const float* SavedFuelSeconds = PersistentComponent->NumberState.Find(TEXT("fuel_seconds"));
|
||||
const float* SavedCookingEnabled = PersistentComponent->NumberState.Find(TEXT("cooking_placeholder_enabled"));
|
||||
const float* SavedCookingRequired = PersistentComponent->NumberState.Find(TEXT("cooking_seconds_required"));
|
||||
const float* SavedCookingProgress = PersistentComponent->NumberState.Find(TEXT("cooking_progress_seconds"));
|
||||
const float* SavedLit = PersistentComponent->NumberState.Find(TEXT("lit"));
|
||||
|
||||
if (SavedFuelSeconds)
|
||||
{
|
||||
FuelSeconds = FMath::Max(0.0f, *SavedFuelSeconds);
|
||||
}
|
||||
|
||||
if (SavedCookingEnabled)
|
||||
{
|
||||
bCookingPlaceholderEnabled = *SavedCookingEnabled > 0.5f;
|
||||
}
|
||||
|
||||
if (SavedCookingRequired)
|
||||
{
|
||||
CookingSecondsRequired = FMath::Max(0.0f, *SavedCookingRequired);
|
||||
}
|
||||
|
||||
if (SavedCookingProgress)
|
||||
{
|
||||
CookingProgressSeconds = FMath::Clamp(*SavedCookingProgress, 0.0f, CookingSecondsRequired);
|
||||
}
|
||||
|
||||
SetLit(SavedLit && *SavedLit > 0.5f && FuelSeconds > 0.0f);
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::AddFuel(float Seconds)
|
||||
{
|
||||
if (HasAuthority())
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "AgrarianInteractable.h"
|
||||
#include "AgrarianPersistentStateProvider.h"
|
||||
#include "AgrarianCampfire.generated.h"
|
||||
|
||||
class UPointLightComponent;
|
||||
class UParticleSystemComponent;
|
||||
class UAgrarianPersistentActorComponent;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class AAgrarianCampfire : public AActor, public IAgrarianInteractable
|
||||
class AAgrarianCampfire : public AActor, public IAgrarianInteractable, public IAgrarianPersistentStateProvider
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
@@ -31,6 +33,9 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Effects")
|
||||
TObjectPtr<UParticleSystemComponent> SmokeEffect;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Fire|Persistence")
|
||||
TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_FireState, Category = "Agrarian|Fire")
|
||||
bool bLit = false;
|
||||
|
||||
@@ -55,6 +60,8 @@ public:
|
||||
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;
|
||||
virtual void CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const override;
|
||||
virtual void ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire")
|
||||
void AddFuel(float Seconds);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
#include "AgrarianGamePlayerController.h"
|
||||
#include "AgrarianCampfire.h"
|
||||
#include "AgrarianCraftingComponent.h"
|
||||
#include "AgrarianGameCharacter.h"
|
||||
#include "AgrarianInventoryComponent.h"
|
||||
@@ -296,6 +297,7 @@ void AAgrarianGamePlayerController::ServerAgrarianLoadWorld_Implementation()
|
||||
}
|
||||
|
||||
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), AAgrarianShelterActor::StaticClass());
|
||||
Persistence->RegisterWorldActorClass(TEXT("campfire"), AAgrarianCampfire::StaticClass());
|
||||
int32 RestoredPlayerCount = 0;
|
||||
int32 RestoredActorCount = 0;
|
||||
const bool bLoaded = Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright Pacificao. All Rights Reserved.
|
||||
|
||||
#include "AgrarianPersistentActorComponent.h"
|
||||
#include "AgrarianPersistentStateProvider.h"
|
||||
|
||||
UAgrarianPersistentActorComponent::UAgrarianPersistentActorComponent()
|
||||
{
|
||||
@@ -14,6 +15,13 @@ bool UAgrarianPersistentActorComponent::IsSaveable() const
|
||||
|
||||
FAgrarianSavedWorldActor UAgrarianPersistentActorComponent::CaptureSaveState() const
|
||||
{
|
||||
if (const AActor* Owner = GetOwner(); Owner && Owner->Implements<UAgrarianPersistentStateProvider>())
|
||||
{
|
||||
IAgrarianPersistentStateProvider::Execute_CapturePersistentState(
|
||||
const_cast<AActor*>(Owner),
|
||||
const_cast<UAgrarianPersistentActorComponent*>(this));
|
||||
}
|
||||
|
||||
FAgrarianSavedWorldActor SavedActor;
|
||||
SavedActor.ActorTypeId = ActorTypeId;
|
||||
SavedActor.StringState = StringState;
|
||||
@@ -41,4 +49,9 @@ void UAgrarianPersistentActorComponent::ApplySaveState(const FAgrarianSavedWorld
|
||||
{
|
||||
GetOwner()->SetActorTransform(SavedActor.Transform);
|
||||
}
|
||||
|
||||
if (AActor* Owner = GetOwner(); Owner && Owner->Implements<UAgrarianPersistentStateProvider>())
|
||||
{
|
||||
IAgrarianPersistentStateProvider::Execute_ApplyPersistentState(Owner, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright Pacificao. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "AgrarianPersistentStateProvider.generated.h"
|
||||
|
||||
class UAgrarianPersistentActorComponent;
|
||||
|
||||
UINTERFACE(BlueprintType)
|
||||
class UAgrarianPersistentStateProvider : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class IAgrarianPersistentStateProvider
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Agrarian|Persistence")
|
||||
void CapturePersistentState(UAgrarianPersistentActorComponent* PersistentComponent) const;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Agrarian|Persistence")
|
||||
void ApplyPersistentState(UAgrarianPersistentActorComponent* PersistentComponent);
|
||||
};
|
||||
Reference in New Issue
Block a user