Add campfire cooking placeholder
This commit is contained in:
@@ -587,7 +587,9 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
|||||||
update path used by natural fuel depletion.
|
update path used by natural fuel depletion.
|
||||||
- [x] Add warmth radius.
|
- [x] Add warmth radius.
|
||||||
- [x] Add light source.
|
- [x] Add light source.
|
||||||
- [ ] Add cooking placeholder if needed.
|
- [x] Add cooking placeholder if needed. Added replicated campfire cooking
|
||||||
|
placeholder state, a native cookability check, and progress ratio helpers so
|
||||||
|
later recipe UI and food systems have a stable hook.
|
||||||
- [ ] Add smoke/visual effect placeholder.
|
- [ ] Add smoke/visual effect placeholder.
|
||||||
- [x] Add replication.
|
- [x] Add replication.
|
||||||
- [ ] Add persistence.
|
- [ ] Add persistence.
|
||||||
|
|||||||
@@ -292,6 +292,11 @@ Campfires expose native extinguish logic through `AAgrarianCampfire::Extinguish`
|
|||||||
Extinguishing clears remaining fuel, turns off replicated lit state, and reuses
|
Extinguishing clears remaining fuel, turns off replicated lit state, and reuses
|
||||||
the same visual update path as natural fuel depletion.
|
the same visual update path as natural fuel depletion.
|
||||||
|
|
||||||
|
Campfires also expose a minimal replicated cooking placeholder. While lit and
|
||||||
|
enabled, `AAgrarianCampfire` advances `CookingProgressSeconds` toward
|
||||||
|
`CookingSecondsRequired` on the server and exposes `CanCook` plus a normalized
|
||||||
|
progress ratio for future recipe UI, food transformation, and interaction hooks.
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
@@ -207,6 +207,22 @@ def make_stack(data):
|
|||||||
return stack
|
return stack
|
||||||
|
|
||||||
|
|
||||||
|
def set_default_property(cdo, property_name, value):
|
||||||
|
property_names = [property_name]
|
||||||
|
if isinstance(value, bool):
|
||||||
|
property_names.append(f"b_{property_name}")
|
||||||
|
property_names.append("b" + "".join(part.capitalize() for part in property_name.split("_")))
|
||||||
|
|
||||||
|
for reflected_name in property_names:
|
||||||
|
try:
|
||||||
|
cdo.set_editor_property(reflected_name, value)
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise RuntimeError(f"Could not set default property {property_name}")
|
||||||
|
|
||||||
|
|
||||||
def apply_defaults(blueprint, config):
|
def apply_defaults(blueprint, config):
|
||||||
blueprint_path = f"{config['folder']}/{config['asset']}"
|
blueprint_path = f"{config['folder']}/{config['asset']}"
|
||||||
unreal.BlueprintEditorLibrary.compile_blueprint(blueprint)
|
unreal.BlueprintEditorLibrary.compile_blueprint(blueprint)
|
||||||
@@ -222,7 +238,7 @@ def apply_defaults(blueprint, config):
|
|||||||
elif property_name == "harvest_yields":
|
elif property_name == "harvest_yields":
|
||||||
value = [make_stack(stack_data) for stack_data in value]
|
value = [make_stack(stack_data) for stack_data in value]
|
||||||
|
|
||||||
cdo.set_editor_property(property_name, value)
|
set_default_property(cdo, property_name, value)
|
||||||
|
|
||||||
mesh_path = config.get("mesh")
|
mesh_path = config.get("mesh")
|
||||||
if mesh_path:
|
if mesh_path:
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
REQUIRED = {
|
||||||
|
ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.h": [
|
||||||
|
"bool bCookingPlaceholderEnabled = true;",
|
||||||
|
"float CookingSecondsRequired = 30.0f;",
|
||||||
|
"float CookingProgressSeconds = 0.0f;",
|
||||||
|
"bool CanCook() const;",
|
||||||
|
"float GetCookingProgressRatio() const;",
|
||||||
|
],
|
||||||
|
ROOT / "Source" / "AgrarianGame" / "AgrarianCampfire.cpp": [
|
||||||
|
"DOREPLIFETIME(AAgrarianCampfire, bCookingPlaceholderEnabled);",
|
||||||
|
"DOREPLIFETIME(AAgrarianCampfire, CookingSecondsRequired);",
|
||||||
|
"DOREPLIFETIME(AAgrarianCampfire, CookingProgressSeconds);",
|
||||||
|
"CookingProgressSeconds = FMath::Min(CookingSecondsRequired, CookingProgressSeconds + DeltaSeconds);",
|
||||||
|
"bool AAgrarianCampfire::CanCook() const",
|
||||||
|
"float AAgrarianCampfire::GetCookingProgressRatio() const",
|
||||||
|
],
|
||||||
|
ROOT / "Scripts" / "setup_playable_blueprints.py": [
|
||||||
|
"def set_default_property(cdo, property_name, value):",
|
||||||
|
"property_names.append(f\"b_{property_name}\")",
|
||||||
|
"property_names.append(\"b\" + \"\".join(part.capitalize() for part in property_name.split(\"_\")))",
|
||||||
|
],
|
||||||
|
ROOT / "Scripts" / "verify_playable_blueprints.py": [
|
||||||
|
"def get_property(cdo, property_name, expected_value):",
|
||||||
|
"property_names.append(f\"b_{property_name}\")",
|
||||||
|
"property_names.append(\"b\" + \"\".join(part.capitalize() for part in property_name.split(\"_\")))",
|
||||||
|
],
|
||||||
|
ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md": [
|
||||||
|
"- [x] Add cooking placeholder if needed.",
|
||||||
|
],
|
||||||
|
ROOT / "Docs" / "TechnicalDesignDocument.md": [
|
||||||
|
"minimal replicated cooking placeholder",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 cooking placeholder verification failed:\n" + "\n".join(missing))
|
||||||
|
|
||||||
|
print("PASS: campfire cooking placeholder is implemented and documented.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -94,6 +94,21 @@ def nearly_equal(left, right):
|
|||||||
return left == right
|
return left == right
|
||||||
|
|
||||||
|
|
||||||
|
def get_property(cdo, property_name, expected_value):
|
||||||
|
property_names = [property_name]
|
||||||
|
if isinstance(expected_value, bool):
|
||||||
|
property_names.append(f"b_{property_name}")
|
||||||
|
property_names.append("b" + "".join(part.capitalize() for part in property_name.split("_")))
|
||||||
|
|
||||||
|
for reflected_name in property_names:
|
||||||
|
try:
|
||||||
|
return cdo.get_editor_property(reflected_name)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise RuntimeError(f"Could not read property {property_name}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
failures = []
|
failures = []
|
||||||
|
|
||||||
@@ -110,7 +125,7 @@ def main():
|
|||||||
|
|
||||||
cdo = unreal.get_default_object(generated_class)
|
cdo = unreal.get_default_object(generated_class)
|
||||||
for property_name, expected_value in expected.get("properties", {}).items():
|
for property_name, expected_value in expected.get("properties", {}).items():
|
||||||
actual_value = cdo.get_editor_property(property_name)
|
actual_value = get_property(cdo, property_name, expected_value)
|
||||||
if str(actual_value) != expected_value and not nearly_equal(actual_value, expected_value):
|
if str(actual_value) != expected_value and not nearly_equal(actual_value, expected_value):
|
||||||
failures.append(f"{path} {property_name} expected {expected_value}, got {actual_value}")
|
failures.append(f"{path} {property_name} expected {expected_value}, got {actual_value}")
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ void AAgrarianCampfire::Tick(float DeltaSeconds)
|
|||||||
SetLit(false);
|
SetLit(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CanCook())
|
||||||
|
{
|
||||||
|
CookingProgressSeconds = FMath::Min(CookingSecondsRequired, CookingProgressSeconds + DeltaSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
WarmNearbyCharacters(DeltaSeconds);
|
WarmNearbyCharacters(DeltaSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +50,9 @@ void AAgrarianCampfire::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Ou
|
|||||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
DOREPLIFETIME(AAgrarianCampfire, bLit);
|
DOREPLIFETIME(AAgrarianCampfire, bLit);
|
||||||
DOREPLIFETIME(AAgrarianCampfire, FuelSeconds);
|
DOREPLIFETIME(AAgrarianCampfire, FuelSeconds);
|
||||||
|
DOREPLIFETIME(AAgrarianCampfire, bCookingPlaceholderEnabled);
|
||||||
|
DOREPLIFETIME(AAgrarianCampfire, CookingSecondsRequired);
|
||||||
|
DOREPLIFETIME(AAgrarianCampfire, CookingProgressSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
FText AAgrarianCampfire::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
|
FText AAgrarianCampfire::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||||
@@ -96,6 +104,21 @@ void AAgrarianCampfire::Extinguish()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AAgrarianCampfire::CanCook() const
|
||||||
|
{
|
||||||
|
return bLit && bCookingPlaceholderEnabled && CookingSecondsRequired > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AAgrarianCampfire::GetCookingProgressRatio() const
|
||||||
|
{
|
||||||
|
if (CookingSecondsRequired <= 0.0f)
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FMath::Clamp(CookingProgressSeconds / CookingSecondsRequired, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void AAgrarianCampfire::OnRep_FireState()
|
void AAgrarianCampfire::OnRep_FireState()
|
||||||
{
|
{
|
||||||
UpdateVisualState();
|
UpdateVisualState();
|
||||||
|
|||||||
@@ -39,6 +39,15 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire", meta = (ClampMin = "0"))
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Fire", meta = (ClampMin = "0"))
|
||||||
float WarmthPerSecond = 0.02f;
|
float WarmthPerSecond = 0.02f;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Cooking")
|
||||||
|
bool bCookingPlaceholderEnabled = true;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Cooking", meta = (ClampMin = "0"))
|
||||||
|
float CookingSecondsRequired = 30.0f;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Fire|Cooking", meta = (ClampMin = "0"))
|
||||||
|
float CookingProgressSeconds = 0.0f;
|
||||||
|
|
||||||
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;
|
||||||
@@ -49,6 +58,12 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire")
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|Fire")
|
||||||
void Extinguish();
|
void Extinguish();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Cooking")
|
||||||
|
bool CanCook() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Agrarian|Fire|Cooking")
|
||||||
|
float GetCookingProgressRatio() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void OnRep_FireState();
|
void OnRep_FireState();
|
||||||
|
|||||||
Reference in New Issue
Block a user