Add campfire maintenance gameplay hooks
This commit is contained in:
@@ -841,7 +841,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [x] Add grass and forest ignition checks from irresponsible fire placement, wind/weather, dry fuel, nearby vegetation, and burn duration. Added foliage fuel counting, campfire vegetation ignition risk scores, grass/brush and forest ignition flags, and weather/wind/burn-duration modifiers so unsafe fire placement near dry fuel can now become a server-authoritative ignition risk.
|
||||
- [x] Add shelter/structure ignition risk when fires are placed too close to primitive shelters, wood piles, flammable crafting stations, or settlement objects. Added campfire structure ignition risk for nearby primitive shelters and flammable wood/fiber resource nodes, with containment, burn-duration, weather/wind, and fire-risk modifiers before a replicated structure ignition flag is set.
|
||||
- [x] Add server-authoritative fire spread rules for grass, brush, trees, shelters, and other burnable actors, including fuel, distance, wind, weather, and suppression hooks. Added replicated grass, forest, and structure fire intensities plus active spread radius that grow only on the server from nearby fuel, ignition distance, wind/weather, and a suppression-pressure hook for later rain, carried water, dirt/sand, firebreaks, and tools.
|
||||
- [ ] Add fire maintenance gameplay so watched, cleared, contained, or extinguished fires are safe, while neglected fires can become dangerous.
|
||||
- [x] Add fire maintenance gameplay so watched, cleared, contained, or extinguished fires are safe, while neglected fires can become dangerous. Updated lit campfire interaction to maintain the fire, added watch, clear-area, and contain-fire hooks, and made maintenance reduce campfire, vegetation, forest, and structure ignition risks while extinguishing resets active risk state.
|
||||
- [ ] Add fire suppression hooks for rain, water carrying, dirt/sand, cleared firebreaks, and future firefighting tools.
|
||||
- [ ] Persist active grass, forest, and structure fires across save/load without corrupting world state.
|
||||
- [ ] Add QA coverage for safe campfires, unsafe campfires, vegetation spread, shelter ignition, suppression, and save/load recovery.
|
||||
|
||||
@@ -369,6 +369,13 @@ modifiers, and a suppression-pressure hook that future rain, carried water,
|
||||
dirt/sand, firebreaks, and tools can drive. This establishes deterministic fire
|
||||
state for clients without letting clients decide whether the world is burning.
|
||||
|
||||
Fire maintenance gameplay uses the same authority path as ignition and fuel.
|
||||
Interacting with a lit campfire now presents as maintaining the fire; if the
|
||||
player has wood it still adds fuel, and if not it watches/maintains the fire.
|
||||
Native hooks also let future UI/actions explicitly clear the fire area or
|
||||
contain the fire. Watched, cleared, contained, and extinguished fires reduce
|
||||
risk, while neglected fires continue accumulating ignition and spread pressure.
|
||||
|
||||
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,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify fire maintenance gameplay hooks reduce fire risk."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
FIRE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.h"
|
||||
FIRE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.cpp"
|
||||
TDD = ROOT / "Docs" / "TechnicalDesignDocument.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
FIRE_H: [
|
||||
"WatchedMaintenanceRiskReduction",
|
||||
"ClearedAreaRiskReduction",
|
||||
"ContainedFireRiskReduction",
|
||||
"void MaintainFire(bool bClearArea, bool bContainFire);",
|
||||
"void WatchFire();",
|
||||
"void ClearAreaAroundFire();",
|
||||
"void ContainFire();",
|
||||
"void ReduceFireRisks(float Amount);",
|
||||
],
|
||||
FIRE_CPP: [
|
||||
"FText::FromString(TEXT(\"Maintain fire\"))",
|
||||
"WatchFire();",
|
||||
"AAgrarianCampfire::WatchFire",
|
||||
"AAgrarianCampfire::ClearAreaAroundFire",
|
||||
"AAgrarianCampfire::ContainFire",
|
||||
"AAgrarianCampfire::ReduceFireRisks",
|
||||
"FireRiskScore = FMath::Clamp(FireRiskScore - SafeAmount",
|
||||
"GrassIgnitionRiskScore = FMath::Clamp",
|
||||
"StructureIgnitionRiskScore = FMath::Clamp",
|
||||
"FireRiskScore = 0.0f;",
|
||||
],
|
||||
TDD: [
|
||||
"Fire maintenance gameplay uses the same authority path",
|
||||
"clear the fire area",
|
||||
"contain the fire",
|
||||
"neglected fires continue accumulating",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add fire maintenance gameplay",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: fire maintenance gameplay hooks reduce risk.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -178,7 +178,7 @@ void AAgrarianCampfire::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Ou
|
||||
|
||||
FText AAgrarianCampfire::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
return bLit ? FText::FromString(TEXT("Add fuel")) : FText::FromString(TEXT("Light fire"));
|
||||
return bLit ? FText::FromString(TEXT("Maintain fire")) : FText::FromString(TEXT("Light fire"));
|
||||
}
|
||||
|
||||
bool AAgrarianCampfire::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||
@@ -198,6 +198,10 @@ void AAgrarianCampfire::Interact_Implementation(AAgrarianGameCharacter* Interact
|
||||
{
|
||||
AddFuel(90.0f);
|
||||
}
|
||||
else if (bLit)
|
||||
{
|
||||
WatchFire();
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::CapturePersistentState_Implementation(UAgrarianPersistentActorComponent* PersistentComponent) const
|
||||
@@ -407,8 +411,32 @@ void AAgrarianCampfire::MaintainFire(bool bClearArea, bool 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 RiskReduction = WatchedMaintenanceRiskReduction;
|
||||
if (bClearArea)
|
||||
{
|
||||
RiskReduction += ClearedAreaRiskReduction;
|
||||
}
|
||||
if (bContainFire)
|
||||
{
|
||||
RiskReduction += ContainedFireRiskReduction;
|
||||
}
|
||||
|
||||
ReduceFireRisks(RiskReduction);
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::WatchFire()
|
||||
{
|
||||
MaintainFire(false, false);
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::ClearAreaAroundFire()
|
||||
{
|
||||
MaintainFire(true, false);
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::ContainFire()
|
||||
{
|
||||
MaintainFire(false, true);
|
||||
}
|
||||
|
||||
float AAgrarianCampfire::GetFireRiskRatio() const
|
||||
@@ -826,3 +854,12 @@ float AAgrarianCampfire::GetActiveBurningFuelScore() const
|
||||
|
||||
return FMath::Max(0.0f, VegetationFuelScore + StructureFuelScore);
|
||||
}
|
||||
|
||||
void AAgrarianCampfire::ReduceFireRisks(float Amount)
|
||||
{
|
||||
const float SafeAmount = FMath::Max(0.0f, Amount);
|
||||
FireRiskScore = FMath::Clamp(FireRiskScore - SafeAmount, 0.0f, 100.0f);
|
||||
GrassIgnitionRiskScore = FMath::Clamp(GrassIgnitionRiskScore - (SafeAmount * 0.75f), 0.0f, 100.0f);
|
||||
ForestIgnitionRiskScore = FMath::Clamp(ForestIgnitionRiskScore - (SafeAmount * 0.5f), 0.0f, 100.0f);
|
||||
StructureIgnitionRiskScore = FMath::Clamp(StructureIgnitionRiskScore - (SafeAmount * 0.75f), 0.0f, 100.0f);
|
||||
}
|
||||
|
||||
@@ -114,6 +114,15 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float ContainedFireRiskMultiplier = 0.35f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
|
||||
float WatchedMaintenanceRiskReduction = 8.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
|
||||
float ClearedAreaRiskReduction = 24.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire|Risk", meta = (ClampMin = "0"))
|
||||
float ContainedFireRiskReduction = 28.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Vegetation", meta = (ClampMin = "0", ClampMax = "100"))
|
||||
float GrassIgnitionRiskScore = 0.0f;
|
||||
|
||||
@@ -210,6 +219,15 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire|Risk")
|
||||
void MaintainFire(bool bClearArea, bool bContainFire);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire|Risk")
|
||||
void WatchFire();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire|Risk")
|
||||
void ClearAreaAroundFire();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire|Risk")
|
||||
void ContainFire();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Risk")
|
||||
float GetFireRiskRatio() const;
|
||||
|
||||
@@ -246,4 +264,5 @@ protected:
|
||||
void UpdateServerAuthoritativeFireSpread(float DeltaSeconds);
|
||||
float GetFireSpreadWeatherMultiplier() const;
|
||||
float GetActiveBurningFuelScore() const;
|
||||
void ReduceFireRisks(float Amount);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user