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
|
particle component placeholder that activates and hides with replicated fire
|
||||||
state, leaving final visual assets assignable later.
|
state, leaving final visual assets assignable later.
|
||||||
- [x] Add replication.
|
- [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.
|
- [x] Connect fire to body temperature.
|
||||||
- [ ] Connect rain/weather to fire behavior.
|
- [ ] 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
|
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.
|
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
|
The first real-weather adapter is `UAgrarianWeatherProviderSubsystem`. It uses
|
||||||
Open-Meteo forecast requests keyed by tile center latitude/longitude, parses the
|
Open-Meteo forecast requests keyed by tile center latitude/longitude, parses the
|
||||||
current temperature, daily low/high, precipitation, wind, humidity, cloud cover,
|
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 "AgrarianCampfire.h"
|
||||||
#include "AgrarianGameCharacter.h"
|
#include "AgrarianGameCharacter.h"
|
||||||
#include "AgrarianInventoryComponent.h"
|
#include "AgrarianInventoryComponent.h"
|
||||||
|
#include "AgrarianPersistentActorComponent.h"
|
||||||
#include "AgrarianSurvivalComponent.h"
|
#include "AgrarianSurvivalComponent.h"
|
||||||
#include "Particles/ParticleSystemComponent.h"
|
#include "Particles/ParticleSystemComponent.h"
|
||||||
#include "Components/PointLightComponent.h"
|
#include "Components/PointLightComponent.h"
|
||||||
@@ -29,6 +30,9 @@ AAgrarianCampfire::AAgrarianCampfire()
|
|||||||
SmokeEffect->bAutoActivate = false;
|
SmokeEffect->bAutoActivate = false;
|
||||||
SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
|
SmokeEffect->SetRelativeLocation(FVector(0.0f, 0.0f, 80.0f));
|
||||||
SmokeEffect->SetVisibility(false);
|
SmokeEffect->SetVisibility(false);
|
||||||
|
|
||||||
|
PersistentActorComponent = CreateDefaultSubobject<UAgrarianPersistentActorComponent>(TEXT("PersistentActorComponent"));
|
||||||
|
PersistentActorComponent->ActorTypeId = TEXT("campfire");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AAgrarianCampfire::Tick(float DeltaSeconds)
|
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)
|
void AAgrarianCampfire::AddFuel(float Seconds)
|
||||||
{
|
{
|
||||||
if (HasAuthority())
|
if (HasAuthority())
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/Actor.h"
|
#include "GameFramework/Actor.h"
|
||||||
#include "AgrarianInteractable.h"
|
#include "AgrarianInteractable.h"
|
||||||
|
#include "AgrarianPersistentStateProvider.h"
|
||||||
#include "AgrarianCampfire.generated.h"
|
#include "AgrarianCampfire.generated.h"
|
||||||
|
|
||||||
class UPointLightComponent;
|
class UPointLightComponent;
|
||||||
class UParticleSystemComponent;
|
class UParticleSystemComponent;
|
||||||
|
class UAgrarianPersistentActorComponent;
|
||||||
class UStaticMeshComponent;
|
class UStaticMeshComponent;
|
||||||
|
|
||||||
UCLASS(Blueprintable)
|
UCLASS(Blueprintable)
|
||||||
class AAgrarianCampfire : public AActor, public IAgrarianInteractable
|
class AAgrarianCampfire : public AActor, public IAgrarianInteractable, public IAgrarianPersistentStateProvider
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@@ -31,6 +33,9 @@ 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|Persistence")
|
||||||
|
TObjectPtr<UAgrarianPersistentActorComponent> PersistentActorComponent;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_FireState, Category = "Agrarian|Fire")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_FireState, Category = "Agrarian|Fire")
|
||||||
bool bLit = false;
|
bool bLit = false;
|
||||||
|
|
||||||
@@ -55,6 +60,8 @@ public:
|
|||||||
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;
|
||||||
|
virtual void CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const override;
|
||||||
|
virtual void ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) override;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire")
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire")
|
||||||
void AddFuel(float Seconds);
|
void AddFuel(float Seconds);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "AgrarianGamePlayerController.h"
|
#include "AgrarianGamePlayerController.h"
|
||||||
|
#include "AgrarianCampfire.h"
|
||||||
#include "AgrarianCraftingComponent.h"
|
#include "AgrarianCraftingComponent.h"
|
||||||
#include "AgrarianGameCharacter.h"
|
#include "AgrarianGameCharacter.h"
|
||||||
#include "AgrarianInventoryComponent.h"
|
#include "AgrarianInventoryComponent.h"
|
||||||
@@ -296,6 +297,7 @@ void AAgrarianGamePlayerController::ServerAgrarianLoadWorld_Implementation()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), AAgrarianShelterActor::StaticClass());
|
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), AAgrarianShelterActor::StaticClass());
|
||||||
|
Persistence->RegisterWorldActorClass(TEXT("campfire"), AAgrarianCampfire::StaticClass());
|
||||||
int32 RestoredPlayerCount = 0;
|
int32 RestoredPlayerCount = 0;
|
||||||
int32 RestoredActorCount = 0;
|
int32 RestoredActorCount = 0;
|
||||||
const bool bLoaded = Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount);
|
const bool bLoaded = Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright Pacificao. All Rights Reserved.
|
// Copyright Pacificao. All Rights Reserved.
|
||||||
|
|
||||||
#include "AgrarianPersistentActorComponent.h"
|
#include "AgrarianPersistentActorComponent.h"
|
||||||
|
#include "AgrarianPersistentStateProvider.h"
|
||||||
|
|
||||||
UAgrarianPersistentActorComponent::UAgrarianPersistentActorComponent()
|
UAgrarianPersistentActorComponent::UAgrarianPersistentActorComponent()
|
||||||
{
|
{
|
||||||
@@ -14,6 +15,13 @@ bool UAgrarianPersistentActorComponent::IsSaveable() const
|
|||||||
|
|
||||||
FAgrarianSavedWorldActor UAgrarianPersistentActorComponent::CaptureSaveState() 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;
|
FAgrarianSavedWorldActor SavedActor;
|
||||||
SavedActor.ActorTypeId = ActorTypeId;
|
SavedActor.ActorTypeId = ActorTypeId;
|
||||||
SavedActor.StringState = StringState;
|
SavedActor.StringState = StringState;
|
||||||
@@ -41,4 +49,9 @@ void UAgrarianPersistentActorComponent::ApplySaveState(const FAgrarianSavedWorld
|
|||||||
{
|
{
|
||||||
GetOwner()->SetActorTransform(SavedActor.Transform);
|
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