Connect campfires to weather

This commit is contained in:
2026-05-17 19:21:26 -07:00
parent 7291c4844b
commit 879a4805c5
5 changed files with 117 additions and 4 deletions
+3 -1
View File
@@ -598,7 +598,9 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
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.
- [x] Connect rain/weather to fire behavior. Campfires now read replicated
game-state weather, burn fuel faster in rain and storms, and deterministically
extinguish when wet weather pushes remaining fuel below the low-fuel threshold.
## 0.1.I Shelter Building
+6
View File
@@ -308,6 +308,12 @@ 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.
Campfires now read the replicated `AAgrarianGameState::Weather` value while
burning. Rain and storms increase fuel drain through tunable multipliers, and
wet weather can deterministically extinguish a low-fuel fire so weather affects
fire reliability without adding random outcomes to save/load or multiplayer
state.
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,
+52
View File
@@ -0,0 +1,52 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REQUIRED = {
ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.h": [
"#include \"AgrarianTypes.h\"",
"float RainFuelDrainMultiplier = 1.5f;",
"float StormFuelDrainMultiplier = 2.5f;",
"float WetWeatherExtinguishFuelThresholdSeconds = 6.0f;",
"bool bWetWeatherCanExtinguish = true;",
"float GetWeatherFuelDrainMultiplier() const;",
"bool IsWetWeatherActive() const;",
"EAgrarianWeatherType GetCurrentWeather() const;",
],
ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.cpp": [
"#include \"AgrarianGameState.h\"",
"DeltaSeconds * GetWeatherFuelDrainMultiplier()",
"bWetWeatherCanExtinguish && IsWetWeatherActive()",
"WetWeatherExtinguishFuelThresholdSeconds",
"Extinguish();",
"case EAgrarianWeatherType::Rain:",
"case EAgrarianWeatherType::Storm:",
"World->GetGameState<AAgrarianGameState>()",
"return EAgrarianWeatherType::Clear;",
],
ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md": [
"- [x] Connect rain/weather to fire behavior.",
],
ROOT / "Docs" / "TechnicalDesignDocument.md": [
"Campfires now read the replicated `AAgrarianGameState::Weather` value",
],
}
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 weather behavior verification failed:\n" + "\n".join(missing))
print("PASS: campfire weather behavior is implemented and documented.")
if __name__ == "__main__":
main()
+36 -3
View File
@@ -2,6 +2,7 @@
#include "AgrarianCampfire.h"
#include "AgrarianGameCharacter.h"
#include "AgrarianGameState.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianPersistentActorComponent.h"
#include "AgrarianSurvivalComponent.h"
@@ -41,10 +42,10 @@ void AAgrarianCampfire::Tick(float DeltaSeconds)
if (HasAuthority() && bLit)
{
FuelSeconds = FMath::Max(0.0f, FuelSeconds - DeltaSeconds);
if (FuelSeconds <= 0.0f)
FuelSeconds = FMath::Max(0.0f, FuelSeconds - (DeltaSeconds * GetWeatherFuelDrainMultiplier()));
if (FuelSeconds <= 0.0f || (bWetWeatherCanExtinguish && IsWetWeatherActive() && FuelSeconds <= WetWeatherExtinguishFuelThresholdSeconds))
{
SetLit(false);
Extinguish();
}
if (CanCook())
@@ -180,11 +181,43 @@ float AAgrarianCampfire::GetCookingProgressRatio() const
return FMath::Clamp(CookingProgressSeconds / CookingSecondsRequired, 0.0f, 1.0f);
}
float AAgrarianCampfire::GetWeatherFuelDrainMultiplier() const
{
switch (GetCurrentWeather())
{
case EAgrarianWeatherType::Rain:
return FMath::Max(1.0f, RainFuelDrainMultiplier);
case EAgrarianWeatherType::Storm:
return FMath::Max(1.0f, StormFuelDrainMultiplier);
default:
return 1.0f;
}
}
bool AAgrarianCampfire::IsWetWeatherActive() const
{
const EAgrarianWeatherType CurrentWeather = GetCurrentWeather();
return CurrentWeather == EAgrarianWeatherType::Rain || CurrentWeather == EAgrarianWeatherType::Storm;
}
void AAgrarianCampfire::OnRep_FireState()
{
UpdateVisualState();
}
EAgrarianWeatherType AAgrarianCampfire::GetCurrentWeather() const
{
if (const UWorld* World = GetWorld())
{
if (const AAgrarianGameState* GameState = World->GetGameState<AAgrarianGameState>())
{
return GameState->Weather;
}
}
return EAgrarianWeatherType::Clear;
}
void AAgrarianCampfire::SetLit(bool bNewLit)
{
if (bLit != bNewLit)
+20
View File
@@ -6,6 +6,7 @@
#include "GameFramework/Actor.h"
#include "AgrarianInteractable.h"
#include "AgrarianPersistentStateProvider.h"
#include "AgrarianTypes.h"
#include "AgrarianCampfire.generated.h"
class UPointLightComponent;
@@ -57,6 +58,18 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Cooking", meta = (ClampMin = "0"))
float CookingProgressSeconds = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather", meta = (ClampMin = "1"))
float RainFuelDrainMultiplier = 1.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather", meta = (ClampMin = "1"))
float StormFuelDrainMultiplier = 2.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather", meta = (ClampMin = "0"))
float WetWeatherExtinguishFuelThresholdSeconds = 6.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Weather")
bool bWetWeatherCanExtinguish = true;
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;
@@ -75,10 +88,17 @@ public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Cooking")
float GetCookingProgressRatio() const;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Weather")
float GetWeatherFuelDrainMultiplier() const;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Weather")
bool IsWetWeatherActive() const;
protected:
UFUNCTION()
void OnRep_FireState();
EAgrarianWeatherType GetCurrentWeather() const;
void SetLit(bool bNewLit);
void UpdateVisualState();
void WarmNearbyCharacters(float DeltaSeconds);