Add MVP resource tool rules
This commit is contained in:
@@ -544,7 +544,9 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [x] Add respawn rules for MVP. Added configurable resource-node respawn
|
||||
fields and timer logic: renewable surface resources respawn after MVP delays,
|
||||
while stone remains nonrenewable for the first survival loop.
|
||||
- [ ] Add tool requirement rules.
|
||||
- [x] Add tool requirement rules. Added inventory-based resource tool rules:
|
||||
current MVP nodes preserve bare-hand gathering, while a basic tool in
|
||||
inventory improves yields for wood, fiber, and stone.
|
||||
- [x] Add bare-hand gathering fallback.
|
||||
- [ ] Add resource node persistence.
|
||||
- [x] Add replicated gathering feedback.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -448,6 +448,16 @@ gone. Respawn timing is configurable per Blueprint through
|
||||
uses a server-side timer and restores replicated `RemainingHarvests` when the
|
||||
delay expires.
|
||||
|
||||
Tool requirement rules remain inventory-based for the MVP because dedicated
|
||||
equipment slots are deferred. A resource node can declare `RequiredToolItemId`,
|
||||
`bAllowBareHandGathering`, and `ToolQuantityBonus`. Current Ground Zero wood,
|
||||
fiber, and stone nodes still allow bare-hand gathering so the first survival
|
||||
loop cannot deadlock before the player can craft a tool. When the player has a
|
||||
`basic_tool` in inventory, those nodes grant an extra unit per harvest. Edible
|
||||
plants remain pure bare-hand gathering. Later nodes can disable
|
||||
`bAllowBareHandGathering` when the game has proper tool equipment, durability,
|
||||
and feedback UI.
|
||||
|
||||
### Wildlife Navigation
|
||||
|
||||
MVP wildlife movement is server authoritative. `AAgrarianWildlifeBase` uses an
|
||||
|
||||
@@ -54,6 +54,21 @@ Respawn restores the node to its configured `MaxHarvests` value and re-enables
|
||||
visibility/collision through the same replicated depletion state used by
|
||||
gathering.
|
||||
|
||||
## Tool Rules
|
||||
|
||||
MVP tool rules keep the first loop accessible while giving crafted tools an
|
||||
immediate benefit:
|
||||
|
||||
- Wood, fiber, and stone nodes declare `basic_tool` as their useful tool.
|
||||
- Those nodes still allow bare-hand gathering so players can gather the
|
||||
ingredients needed to craft the first tool.
|
||||
- A carried `basic_tool` adds `1` extra unit to each wood, fiber, or stone
|
||||
harvest.
|
||||
- Edible plant nodes remain pure bare-hand gathering.
|
||||
|
||||
The rule is inventory-based until equipment slots, durability, and explicit
|
||||
active-hand state are implemented.
|
||||
|
||||
## Follow-Up
|
||||
|
||||
Future passes should replace the prototype meshes with real coastal scrub,
|
||||
|
||||
@@ -33,6 +33,9 @@ BLUEPRINTS = [
|
||||
"yield_item_definition": WOOD_ITEM_PATH,
|
||||
"remaining_harvests": 16,
|
||||
"quantity_per_harvest": 2,
|
||||
"required_tool_item_id": "basic_tool",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 1,
|
||||
"respawns_for_mvp": True,
|
||||
"respawn_delay_seconds": 900.0,
|
||||
"max_harvests": 16,
|
||||
@@ -48,6 +51,9 @@ BLUEPRINTS = [
|
||||
"yield_item_definition": FIBER_ITEM_PATH,
|
||||
"remaining_harvests": 10,
|
||||
"quantity_per_harvest": 3,
|
||||
"required_tool_item_id": "basic_tool",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 1,
|
||||
"respawns_for_mvp": True,
|
||||
"respawn_delay_seconds": 600.0,
|
||||
"max_harvests": 10,
|
||||
@@ -63,6 +69,9 @@ BLUEPRINTS = [
|
||||
"yield_item_definition": STONE_ITEM_PATH,
|
||||
"remaining_harvests": 12,
|
||||
"quantity_per_harvest": 2,
|
||||
"required_tool_item_id": "basic_tool",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 1,
|
||||
"respawns_for_mvp": False,
|
||||
"respawn_delay_seconds": 1800.0,
|
||||
"max_harvests": 12,
|
||||
@@ -78,6 +87,8 @@ BLUEPRINTS = [
|
||||
"yield_item_definition": FOOD_ITEM_PATH,
|
||||
"remaining_harvests": 8,
|
||||
"quantity_per_harvest": 1,
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 0,
|
||||
"respawns_for_mvp": True,
|
||||
"respawn_delay_seconds": 1200.0,
|
||||
"max_harvests": 8,
|
||||
|
||||
@@ -6,6 +6,9 @@ EXPECTED = {
|
||||
"properties": {
|
||||
"remaining_harvests": 16,
|
||||
"quantity_per_harvest": 2,
|
||||
"required_tool_item_id": "basic_tool",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 1,
|
||||
"respawns_for_mvp": True,
|
||||
"respawn_delay_seconds": 900.0,
|
||||
"max_harvests": 16,
|
||||
@@ -16,6 +19,9 @@ EXPECTED = {
|
||||
"properties": {
|
||||
"remaining_harvests": 10,
|
||||
"quantity_per_harvest": 3,
|
||||
"required_tool_item_id": "basic_tool",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 1,
|
||||
"respawns_for_mvp": True,
|
||||
"respawn_delay_seconds": 600.0,
|
||||
"max_harvests": 10,
|
||||
@@ -26,6 +32,9 @@ EXPECTED = {
|
||||
"properties": {
|
||||
"remaining_harvests": 12,
|
||||
"quantity_per_harvest": 2,
|
||||
"required_tool_item_id": "basic_tool",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 1,
|
||||
"respawns_for_mvp": False,
|
||||
"respawn_delay_seconds": 1800.0,
|
||||
"max_harvests": 12,
|
||||
@@ -36,6 +45,9 @@ EXPECTED = {
|
||||
"properties": {
|
||||
"remaining_harvests": 8,
|
||||
"quantity_per_harvest": 1,
|
||||
"required_tool_item_id": "None",
|
||||
"allow_bare_hand_gathering": True,
|
||||
"tool_quantity_bonus": 0,
|
||||
"respawns_for_mvp": True,
|
||||
"respawn_delay_seconds": 1200.0,
|
||||
"max_harvests": 8,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
FILES = {
|
||||
"AgrarianResourceNode.h": ROOT / "Source" / "AgrarianGame" / "AgrarianResourceNode.h",
|
||||
"AgrarianResourceNode.cpp": ROOT / "Source" / "AgrarianGame" / "AgrarianResourceNode.cpp",
|
||||
"setup_playable_blueprints.py": ROOT / "Scripts" / "setup_playable_blueprints.py",
|
||||
"verify_playable_blueprints.py": ROOT / "Scripts" / "verify_playable_blueprints.py",
|
||||
"TechnicalDesignDocument.md": ROOT / "Docs" / "TechnicalDesignDocument.md",
|
||||
"GroundZeroResourcePass.md": ROOT / "Docs" / "Terrain" / "GroundZeroResourcePass.md",
|
||||
"Roadmap": ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md",
|
||||
}
|
||||
|
||||
REQUIRED_SNIPPETS = {
|
||||
"AgrarianResourceNode.h": [
|
||||
"FName RequiredToolItemId;",
|
||||
"bool bAllowBareHandGathering = true;",
|
||||
"int32 ToolQuantityBonus = 0;",
|
||||
"bool HasRequiredTool",
|
||||
"int32 GetHarvestQuantityFor",
|
||||
],
|
||||
"AgrarianResourceNode.cpp": [
|
||||
"Gather by hand",
|
||||
"RequiredToolItemId == NAME_None || bAllowBareHandGathering || HasRequiredTool(Interactor)",
|
||||
"Inventory->HasItem(RequiredToolItemId, 1)",
|
||||
"QuantityPerHarvest + ToolBonus",
|
||||
"MakeYieldStack(Interactor)",
|
||||
],
|
||||
"setup_playable_blueprints.py": [
|
||||
'"required_tool_item_id": "basic_tool"',
|
||||
'"allow_bare_hand_gathering": True',
|
||||
'"tool_quantity_bonus": 1',
|
||||
'"tool_quantity_bonus": 0',
|
||||
],
|
||||
"verify_playable_blueprints.py": [
|
||||
'"required_tool_item_id": "basic_tool"',
|
||||
'"allow_bare_hand_gathering": True',
|
||||
'"tool_quantity_bonus": 1',
|
||||
'"required_tool_item_id": "None"',
|
||||
],
|
||||
"TechnicalDesignDocument.md": [
|
||||
"Tool requirement rules remain inventory-based",
|
||||
"`basic_tool` in inventory",
|
||||
],
|
||||
"GroundZeroResourcePass.md": [
|
||||
"Wood, fiber, and stone nodes declare `basic_tool`",
|
||||
"Edible plant nodes remain pure bare-hand gathering",
|
||||
],
|
||||
"Roadmap": [
|
||||
"[x] Add tool requirement rules.",
|
||||
"basic tool in\n inventory improves yields",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
missing = []
|
||||
for label, path in FILES.items():
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for snippet in REQUIRED_SNIPPETS[label]:
|
||||
if snippet not in text:
|
||||
missing.append(f"{label}: missing {snippet!r}")
|
||||
|
||||
if missing:
|
||||
raise SystemExit("Resource tool requirement verification failed:\n" + "\n".join(missing))
|
||||
|
||||
print(
|
||||
"PASS: MVP resource tool requirement rules are inventory-based, "
|
||||
"preserve bare-hand gathering, and add basic-tool yield bonuses."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -47,12 +47,27 @@ void AAgrarianResourceNode::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>
|
||||
|
||||
FText AAgrarianResourceNode::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
return RemainingHarvests > 0 ? FText::FromString(TEXT("Gather")) : FText::FromString(TEXT("Depleted"));
|
||||
if (RemainingHarvests <= 0)
|
||||
{
|
||||
return FText::FromString(TEXT("Depleted"));
|
||||
}
|
||||
|
||||
if (RequiredToolItemId != NAME_None && !HasRequiredTool(Interactor) && bAllowBareHandGathering)
|
||||
{
|
||||
return FText::FromString(TEXT("Gather by hand"));
|
||||
}
|
||||
|
||||
return FText::FromString(TEXT("Gather"));
|
||||
}
|
||||
|
||||
bool AAgrarianResourceNode::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
return RemainingHarvests > 0 && Interactor != nullptr;
|
||||
if (RemainingHarvests <= 0 || !Interactor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return RequiredToolItemId == NAME_None || bAllowBareHandGathering || HasRequiredTool(Interactor);
|
||||
}
|
||||
|
||||
void AAgrarianResourceNode::Interact_Implementation(AAgrarianGameCharacter* Interactor)
|
||||
@@ -64,7 +79,7 @@ void AAgrarianResourceNode::Interact_Implementation(AAgrarianGameCharacter* Inte
|
||||
|
||||
if (UAgrarianInventoryComponent* Inventory = Interactor->GetInventoryComponent())
|
||||
{
|
||||
const FAgrarianItemStack Granted = MakeYieldStack();
|
||||
const FAgrarianItemStack Granted = MakeYieldStack(Interactor);
|
||||
if (Inventory->AddItem(Granted))
|
||||
{
|
||||
RemainingHarvests--;
|
||||
@@ -79,15 +94,33 @@ void AAgrarianResourceNode::OnRep_RemainingHarvests()
|
||||
UpdateDepletedState();
|
||||
}
|
||||
|
||||
FAgrarianItemStack AAgrarianResourceNode::MakeYieldStack() const
|
||||
bool AAgrarianResourceNode::HasRequiredTool(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
if (RequiredToolItemId == NAME_None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const UAgrarianInventoryComponent* Inventory = Interactor ? Interactor->GetInventoryComponent() : nullptr;
|
||||
return Inventory && Inventory->HasItem(RequiredToolItemId, 1);
|
||||
}
|
||||
|
||||
int32 AAgrarianResourceNode::GetHarvestQuantityFor(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
const int32 ToolBonus = HasRequiredTool(Interactor) ? FMath::Max(0, ToolQuantityBonus) : 0;
|
||||
return FMath::Max(1, QuantityPerHarvest + ToolBonus);
|
||||
}
|
||||
|
||||
FAgrarianItemStack AAgrarianResourceNode::MakeYieldStack(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
const int32 HarvestQuantity = GetHarvestQuantityFor(Interactor);
|
||||
if (YieldItemDefinition)
|
||||
{
|
||||
return YieldItemDefinition->MakeStack(QuantityPerHarvest);
|
||||
return YieldItemDefinition->MakeStack(HarvestQuantity);
|
||||
}
|
||||
|
||||
FAgrarianItemStack Granted = YieldItem;
|
||||
Granted.Quantity = QuantityPerHarvest;
|
||||
Granted.Quantity = HarvestQuantity;
|
||||
return Granted;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource", meta = (ClampMin = "1"))
|
||||
int32 QuantityPerHarvest = 1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Tools")
|
||||
FName RequiredToolItemId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Tools")
|
||||
bool bAllowBareHandGathering = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Tools", meta = (ClampMin = "0"))
|
||||
int32 ToolQuantityBonus = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Resource|Respawn")
|
||||
bool bRespawnsForMvp = false;
|
||||
|
||||
@@ -55,7 +64,9 @@ protected:
|
||||
UFUNCTION()
|
||||
void OnRep_RemainingHarvests();
|
||||
|
||||
FAgrarianItemStack MakeYieldStack() const;
|
||||
bool HasRequiredTool(const AAgrarianGameCharacter* Interactor) const;
|
||||
int32 GetHarvestQuantityFor(const AAgrarianGameCharacter* Interactor) const;
|
||||
FAgrarianItemStack MakeYieldStack(const AAgrarianGameCharacter* Interactor) const;
|
||||
void UpdateDepletedState();
|
||||
void ScheduleRespawnIfNeeded();
|
||||
void RespawnNode();
|
||||
|
||||
Reference in New Issue
Block a user