Add item pickup actor

This commit is contained in:
2026-05-17 00:55:16 -07:00
parent c511ae904a
commit 03d856efbf
5 changed files with 197 additions and 1 deletions
+4 -1
View File
@@ -490,7 +490,10 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
- [x] Create item definition data asset. - [x] Create item definition data asset.
- [x] Create item stack structure. - [x] Create item stack structure.
- [x] Create inventory component. - [x] Create inventory component.
- [ ] Add item pickup. - [x] Add item pickup. Added native `AAgrarianItemPickup` interactable world
actor with definition-backed or inline stack data, server-authoritative
inventory add, prompt text, and destroy-on-success behavior so failed pickups
remain available.
- [ ] Add item drop. - [ ] Add item drop.
- [ ] Add stack splitting. - [ ] Add stack splitting.
- [ ] Add item use. - [ ] Add item use.
+8
View File
@@ -91,6 +91,14 @@ and save-game `FAgrarianItemStack` records, and a server-authoritative
splitting, item use, equipment, carry capacity, persistence, and UI work should splitting, item use, equipment, carry capacity, persistence, and UI work should
extend that contract rather than inventing parallel inventory state. extend that contract rather than inventing parallel inventory state.
World pickups use `AAgrarianItemPickup`, an interactable replicated actor with a
static mesh and either a definition-backed or inline `FAgrarianItemStack`.
Player interaction already routes to the server through the Agrarian character,
so pickups validate authority, produce a valid stack, add it to the player's
inventory, and only then remove the pickup by destroying the world pickup actor.
If the inventory is full or the stack is invalid, the pickup remains in the
world for another attempt.
## Time And Environment ## Time And Environment
The MVP gameplay calendar target is: The MVP gameplay calendar target is:
+65
View File
@@ -0,0 +1,65 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
FILES = {
"AgrarianItemPickup.h": ROOT / "Source" / "AgrarianGame" / "AgrarianItemPickup.h",
"AgrarianItemPickup.cpp": ROOT / "Source" / "AgrarianGame" / "AgrarianItemPickup.cpp",
"InventoryDataModel.md": ROOT / "Docs" / "InventoryDataModel.md",
"TechnicalDesignDocument.md": ROOT / "Docs" / "TechnicalDesignDocument.md",
"AGRARIAN_DEVELOPMENT_ROADMAP.md": ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md",
}
EXPECTED = {
"AgrarianItemPickup.h": [
"class AGRARIANGAME_API AAgrarianItemPickup : public AActor, public IAgrarianInteractable",
"TObjectPtr<UStaticMeshComponent> Mesh;",
"FAgrarianItemStack PickupStack;",
"TObjectPtr<UAgrarianItemDefinitionAsset> ItemDefinition;",
"int32 Quantity = 1;",
"virtual FText GetInteractionText_Implementation",
"virtual bool CanInteract_Implementation",
"virtual void Interact_Implementation",
"FAgrarianItemStack MakePickupStack() const;",
],
"AgrarianItemPickup.cpp": [
"bReplicates = true;",
"Mesh->SetCollisionProfileName(TEXT(\"BlockAll\"));",
"ItemDefinition->MakeStack(Quantity)",
"MakePickupStack().IsValidStack()",
"if (!HasAuthority() || !Interactor)",
"Interactor->GetInventoryComponent()",
"Inventory->AddItem(Stack)",
"Destroy();",
],
"InventoryDataModel.md": [
"Pickup:",
"world item validates range, authority, stack data, and available",
],
"TechnicalDesignDocument.md": [
"`AAgrarianItemPickup`",
"definition-backed or inline `FAgrarianItemStack`",
"destroying the world pickup actor",
],
"AGRARIAN_DEVELOPMENT_ROADMAP.md": [
"[x] Add item pickup.",
],
}
def main():
missing = []
for label, path in FILES.items():
text = path.read_text(encoding="utf-8")
for snippet in EXPECTED[label]:
if snippet not in text:
missing.append(f"{label}: {snippet}")
if missing:
raise RuntimeError("Item pickup verification failed: " + "; ".join(missing))
print("Agrarian item pickup verification complete.")
if __name__ == "__main__":
main()
@@ -0,0 +1,76 @@
// Copyright Pacificao. All Rights Reserved.
#include "AgrarianItemPickup.h"
#include "AgrarianGameCharacter.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianItemDefinitionAsset.h"
#include "Components/StaticMeshComponent.h"
AAgrarianItemPickup::AAgrarianItemPickup()
{
bReplicates = true;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
RootComponent = Mesh;
Mesh->SetCollisionProfileName(TEXT("BlockAll"));
PickupStack.ItemId = TEXT("wood");
PickupStack.DisplayName = FText::FromString(TEXT("Wood"));
PickupStack.Quantity = 1;
PickupStack.UnitWeight = 1.0f;
InteractionVerb = FText::FromString(TEXT("Pick up"));
}
FText AAgrarianItemPickup::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
{
const FAgrarianItemStack Stack = MakePickupStack();
const FText DisplayName = Stack.DisplayName.IsEmpty() ? FText::FromName(Stack.ItemId) : Stack.DisplayName;
const FString Verb = InteractionVerb.IsEmpty() ? FString(TEXT("Pick up")) : InteractionVerb.ToString();
if (Stack.Quantity > 1)
{
return FText::FromString(FString::Printf(TEXT("%s %s x%d"), *Verb, *DisplayName.ToString(), Stack.Quantity));
}
return FText::FromString(FString::Printf(TEXT("%s %s"), *Verb, *DisplayName.ToString()));
}
bool AAgrarianItemPickup::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
{
return Interactor != nullptr && MakePickupStack().IsValidStack();
}
void AAgrarianItemPickup::Interact_Implementation(AAgrarianGameCharacter* Interactor)
{
if (!HasAuthority() || !Interactor)
{
return;
}
UAgrarianInventoryComponent* Inventory = Interactor->GetInventoryComponent();
if (!Inventory)
{
return;
}
const FAgrarianItemStack Stack = MakePickupStack();
if (Inventory->AddItem(Stack))
{
Destroy();
}
}
FAgrarianItemStack AAgrarianItemPickup::MakePickupStack() const
{
if (ItemDefinition)
{
return ItemDefinition->MakeStack(Quantity);
}
FAgrarianItemStack Stack = PickupStack;
if (Quantity > 0)
{
Stack.Quantity = Quantity;
}
return Stack;
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright Pacificao. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AgrarianInteractable.h"
#include "AgrarianTypes.h"
#include "AgrarianItemPickup.generated.h"
class AAgrarianGameCharacter;
class UAgrarianItemDefinitionAsset;
class UStaticMeshComponent;
UCLASS(Blueprintable)
class AGRARIANGAME_API AAgrarianItemPickup : public AActor, public IAgrarianInteractable
{
GENERATED_BODY()
public:
AAgrarianItemPickup();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Pickup")
TObjectPtr<UStaticMeshComponent> Mesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Pickup")
FAgrarianItemStack PickupStack;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Pickup")
TObjectPtr<UAgrarianItemDefinitionAsset> ItemDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Pickup", meta = (ClampMin = "1"))
int32 Quantity = 1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Pickup")
FText InteractionVerb;
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;
UFUNCTION(BlueprintCallable, Category = "Agrarian|Pickup")
FAgrarianItemStack MakePickupStack() const;
};