Add unattended campfire risk state

This commit is contained in:
2026-05-19 12:10:48 -07:00
parent 280fa76af2
commit 14cd8234e6
5 changed files with 237 additions and 1 deletions
+120
View File
@@ -140,6 +140,7 @@ void AAgrarianCampfire::Tick(float DeltaSeconds)
CookingProgressSeconds = FMath::Min(CookingSecondsRequired, CookingProgressSeconds + DeltaSeconds);
}
UpdateFireRisk(DeltaSeconds);
WarmNearbyCharacters(DeltaSeconds);
}
}
@@ -152,6 +153,11 @@ void AAgrarianCampfire::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Ou
DOREPLIFETIME(AAgrarianCampfire, bCookingPlaceholderEnabled);
DOREPLIFETIME(AAgrarianCampfire, CookingSecondsRequired);
DOREPLIFETIME(AAgrarianCampfire, CookingProgressSeconds);
DOREPLIFETIME(AAgrarianCampfire, FireRiskScore);
DOREPLIFETIME(AAgrarianCampfire, LitDurationSeconds);
DOREPLIFETIME(AAgrarianCampfire, SecondsSinceMaintenance);
DOREPLIFETIME(AAgrarianCampfire, bFireAreaCleared);
DOREPLIFETIME(AAgrarianCampfire, bFireContained);
}
FText AAgrarianCampfire::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
@@ -190,6 +196,11 @@ void AAgrarianCampfire::CapturePersistentState_Implementation(UAgrarianPersisten
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);
PersistentComponent->NumberState.Add(TEXT("fire_risk_score"), FireRiskScore);
PersistentComponent->NumberState.Add(TEXT("lit_duration_seconds"), LitDurationSeconds);
PersistentComponent->NumberState.Add(TEXT("seconds_since_maintenance"), SecondsSinceMaintenance);
PersistentComponent->NumberState.Add(TEXT("fire_area_cleared"), bFireAreaCleared ? 1.0f : 0.0f);
PersistentComponent->NumberState.Add(TEXT("fire_contained"), bFireContained ? 1.0f : 0.0f);
}
void AAgrarianCampfire::ApplyPersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent)
@@ -204,6 +215,11 @@ void AAgrarianCampfire::ApplyPersistentState_Implementation(UAgrarianPersistentA
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"));
const float* SavedRiskScore = PersistentComponent->NumberState.Find(TEXT("fire_risk_score"));
const float* SavedLitDuration = PersistentComponent->NumberState.Find(TEXT("lit_duration_seconds"));
const float* SavedSecondsSinceMaintenance = PersistentComponent->NumberState.Find(TEXT("seconds_since_maintenance"));
const float* SavedAreaCleared = PersistentComponent->NumberState.Find(TEXT("fire_area_cleared"));
const float* SavedContained = PersistentComponent->NumberState.Find(TEXT("fire_contained"));
if (SavedFuelSeconds)
{
@@ -225,6 +241,31 @@ void AAgrarianCampfire::ApplyPersistentState_Implementation(UAgrarianPersistentA
CookingProgressSeconds = FMath::Clamp(*SavedCookingProgress, 0.0f, CookingSecondsRequired);
}
if (SavedRiskScore)
{
FireRiskScore = FMath::Clamp(*SavedRiskScore, 0.0f, 100.0f);
}
if (SavedLitDuration)
{
LitDurationSeconds = FMath::Max(0.0f, *SavedLitDuration);
}
if (SavedSecondsSinceMaintenance)
{
SecondsSinceMaintenance = FMath::Max(0.0f, *SavedSecondsSinceMaintenance);
}
if (SavedAreaCleared)
{
bFireAreaCleared = *SavedAreaCleared > 0.5f;
}
if (SavedContained)
{
bFireContained = *SavedContained > 0.5f;
}
SetLit(SavedLit && *SavedLit > 0.5f && FuelSeconds > 0.0f);
}
@@ -249,10 +290,39 @@ void AAgrarianCampfire::Extinguish()
if (HasAuthority())
{
FuelSeconds = 0.0f;
FireRiskScore = 0.0f;
LitDurationSeconds = 0.0f;
SecondsSinceMaintenance = 0.0f;
SetLit(false);
}
}
void AAgrarianCampfire::MaintainFire(bool bClearArea, bool bContainFire)
{
if (!HasAuthority())
{
return;
}
SecondsSinceMaintenance = 0.0f;
if (bClearArea)
{
bFireAreaCleared = true;
}
if (bContainFire)
{
bFireContained = true;
}
const float RiskReduction = (bFireAreaCleared ? 12.0f : 4.0f) + (bFireContained ? 12.0f : 4.0f);
FireRiskScore = FMath::Clamp(FireRiskScore - RiskReduction, 0.0f, 100.0f);
}
float AAgrarianCampfire::GetFireRiskRatio() const
{
return FMath::Clamp(FireRiskScore / 100.0f, 0.0f, 1.0f);
}
bool AAgrarianCampfire::CanCook() const
{
return bLit && bCookingPlaceholderEnabled && CookingSecondsRequired > 0.0f;
@@ -328,6 +398,10 @@ void AAgrarianCampfire::SetLit(bool bNewLit)
if (bLit != bNewLit)
{
bLit = bNewLit;
if (bLit)
{
SecondsSinceMaintenance = 0.0f;
}
}
if (HasAuthority() && bChanged)
@@ -397,3 +471,49 @@ void AAgrarianCampfire::WarmNearbyCharacters(float DeltaSeconds)
}
}
}
void AAgrarianCampfire::UpdateFireRisk(float DeltaSeconds)
{
if (!HasAuthority() || !bLit)
{
return;
}
LitDurationSeconds += DeltaSeconds;
SecondsSinceMaintenance += DeltaSeconds;
const float RiskGrowth = GetFireRiskGrowthPerSecond();
FireRiskScore = FMath::Clamp(FireRiskScore + (RiskGrowth * DeltaSeconds), 0.0f, 100.0f);
}
float AAgrarianCampfire::GetFireRiskGrowthPerSecond() const
{
float RiskGrowth = 0.0f;
if (SecondsSinceMaintenance >= UnmaintainedRiskDelaySeconds)
{
RiskGrowth += PoorMaintenanceRiskPerSecond;
}
if (FuelSeconds >= HighFuelRiskThresholdSeconds)
{
RiskGrowth += HighFuelRiskPerSecond;
}
if (bFireAreaCleared)
{
RiskGrowth *= FMath::Clamp(ClearedAreaRiskMultiplier, 0.0f, 1.0f);
}
if (bFireContained)
{
RiskGrowth *= FMath::Clamp(ContainedFireRiskMultiplier, 0.0f, 1.0f);
}
if (IsWetWeatherActive())
{
RiskGrowth *= 0.25f;
}
return FMath::Max(0.0f, RiskGrowth);
}
+41
View File
@@ -81,6 +81,39 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Cooking", meta = (ClampMin = "0"))
float CookingProgressSeconds = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0", ClampMax = "100"))
float FireRiskScore = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
float LitDurationSeconds = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
float SecondsSinceMaintenance = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Risk")
bool bFireAreaCleared = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Risk")
bool bFireContained = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
float UnmaintainedRiskDelaySeconds = 120.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
float PoorMaintenanceRiskPerSecond = 0.055f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
float HighFuelRiskThresholdSeconds = 180.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
float HighFuelRiskPerSecond = 0.025f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0", ClampMax = "1"))
float ClearedAreaRiskMultiplier = 0.4f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0", ClampMax = "1"))
float ContainedFireRiskMultiplier = 0.35f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather", meta = (ClampMin = "1"))
float RainFuelDrainMultiplier = 1.5f;
@@ -114,6 +147,12 @@ public:
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire")
void Extinguish();
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire|Risk")
void MaintainFire(bool bClearArea, bool bContainFire);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Risk")
float GetFireRiskRatio() const;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Cooking")
bool CanCook() const;
@@ -137,4 +176,6 @@ protected:
void SetLit(bool bNewLit);
void UpdateVisualState();
void WarmNearbyCharacters(float DeltaSeconds);
void UpdateFireRisk(float DeltaSeconds);
float GetFireRiskGrowthPerSecond() const;
};