Server-authoritative, data-driven item/inventory/interaction/carry/equipment/ world-slot/placement/crafting framework. 8 modules, compiles against UE 5.7. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
152 lines
5.0 KiB
C++
152 lines
5.0 KiB
C++
// Copyright ExByte Studios. Item Interaction Ecosystem.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "GameplayTagContainer.h"
|
|
#include "ItemEnums.h"
|
|
#include "ItemInstanceData.h"
|
|
#include "ItemInteractionInterfaces.h"
|
|
#include "HeldItemComponent.generated.h"
|
|
|
|
class APickupItemActor;
|
|
|
|
/** Replicated snapshot of what the character is currently holding (section 11.1). */
|
|
USTRUCT(BlueprintType)
|
|
struct ITEMWORLD_API FHeldItemState
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
bool bHasHeldItem = false;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
FGuid InstanceId;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
FName ItemId;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
EHeldItemMode Mode = EHeldItemMode::None;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
TObjectPtr<AActor> HeldWorldActor = nullptr;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
FName HoldSocket = NAME_None;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
float MoveSpeedMultiplier = 1.f;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
bool bBlocksSprint = false;
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
bool bBlocksClimb = false;
|
|
|
|
/** Snapshot of the carried item (so we can drop/install it without the actor). */
|
|
UPROPERTY(BlueprintReadOnly, Category = "Held")
|
|
FItemInstanceData Item;
|
|
};
|
|
|
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHeldItemChanged, const FHeldItemState&, NewState);
|
|
|
|
/**
|
|
* Physically carries one bulky item attached to the character (section 11.2-11.3).
|
|
*
|
|
* This is the "two-handed carry" path for batteries, canisters, wheels, crates,
|
|
* corpses, etc. - distinct from equipment (which keeps the item in the inventory
|
|
* and only spawns a visual). Server-authoritative; visuals/penalties apply on all
|
|
* clients via OnRep. Implements IHeldItemInterface so the resolver can build
|
|
* held+target interactions.
|
|
*/
|
|
UCLASS(ClassGroup = (Inventory), meta = (BlueprintSpawnableComponent))
|
|
class ITEMWORLD_API UHeldItemComponent : public UActorComponent, public IHeldItemInterface
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UHeldItemComponent();
|
|
|
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
|
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Held")
|
|
FName CarrySocket = FName("CarrySocket");
|
|
|
|
/** Hold distance in front of the view for physics-handle carry (section 11.4). */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Held")
|
|
float PhysicsHoldDistance = 200.f;
|
|
|
|
UPROPERTY(BlueprintAssignable, Category = "Held")
|
|
FOnHeldItemChanged OnHeldItemChanged;
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Held")
|
|
bool HasHeldItemBP() const { return HeldItem.bHasHeldItem; }
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Held")
|
|
const FHeldItemState& GetHeldItemState() const { return HeldItem; }
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Held")
|
|
bool CanHoldItem(const FItemInstanceData& Item) const;
|
|
|
|
/** Client entry: ask the server to drop the carried item (bind to the Drop input). */
|
|
UFUNCTION(BlueprintCallable, Category = "Held")
|
|
void RequestDrop();
|
|
|
|
// --- Server entry points (section 11.1) ---
|
|
|
|
/** Server: pick up and carry a world item actor attached to a socket (World -> Held). */
|
|
bool ServerHoldWorldItem(AActor* WorldItem);
|
|
|
|
/** Server: carry a world item via a physics handle instead of a fixed socket (section 11.4). */
|
|
bool ServerHoldWorldItemPhysics(AActor* WorldItem);
|
|
|
|
/** Server: drop the carried item back into the world (Held -> World). */
|
|
bool ServerDropHeldItem();
|
|
|
|
/** Server: relinquish the carried item without spawning a drop (e.g. on install). */
|
|
bool ServerConsumeHeldItem(FItemInstanceData& OutItem);
|
|
|
|
/**
|
|
* Server: hand off the carried item AND its actor to a new owner (e.g. a world
|
|
* slot) without destroying the actor. Clears the held state. Returns false if
|
|
* nothing was held.
|
|
*/
|
|
bool ServerHandOffHeldItem(FItemInstanceData& OutItem, AActor*& OutActor);
|
|
|
|
/** Server: update the carried item's data in place (e.g. after pouring out fuel). */
|
|
bool ServerUpdateHeldItem(const FItemInstanceData& NewItem);
|
|
|
|
//~ IHeldItemInterface
|
|
virtual bool GetHeldItem(FItemInstanceData& OutItem) const override;
|
|
virtual bool HasHeldItem() const override { return HeldItem.bHasHeldItem; }
|
|
|
|
protected:
|
|
UPROPERTY(ReplicatedUsing = OnRep_HeldItem)
|
|
FHeldItemState HeldItem;
|
|
|
|
UFUNCTION(Server, Reliable)
|
|
void Server_Drop();
|
|
|
|
UFUNCTION()
|
|
void OnRep_HeldItem(FHeldItemState OldState);
|
|
|
|
/** Applies/removes the movement-speed penalty on the owning character. */
|
|
void ApplyMovementState();
|
|
|
|
bool HasAuthority() const;
|
|
|
|
private:
|
|
/** Owning character's unmodified MaxWalkSpeed, captured the first time we modify it. */
|
|
UPROPERTY(Transient)
|
|
float CachedBaseWalkSpeed = 0.f;
|
|
|
|
/** Created on demand for physics-handle carry. */
|
|
UPROPERTY(Transient)
|
|
TObjectPtr<class UPhysicsHandleComponent> PhysicsHandle = nullptr;
|
|
|
|
void UpdatePhysicsCarryTarget();
|
|
};
|