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>
130 lines
3.4 KiB
C++
130 lines
3.4 KiB
C++
// Copyright ExByte Studios. Item Interaction Ecosystem.
|
|
|
|
#include "WorldItemSlotComponent.h"
|
|
#include "PickupItemActor.h"
|
|
#include "ItemWorldLog.h"
|
|
#include "ItemDatabaseSubsystem.h"
|
|
#include "ItemDefinitionRow.h"
|
|
#include "Net/UnrealNetwork.h"
|
|
|
|
UWorldItemSlotComponent::UWorldItemSlotComponent()
|
|
{
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
SetIsReplicatedByDefault(true);
|
|
}
|
|
|
|
void UWorldItemSlotComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
DOREPLIFETIME(UWorldItemSlotComponent, InstalledItem);
|
|
DOREPLIFETIME(UWorldItemSlotComponent, InstalledVisual);
|
|
}
|
|
|
|
bool UWorldItemSlotComponent::HasAuthority() const
|
|
{
|
|
return GetOwner() && GetOwner()->HasAuthority();
|
|
}
|
|
|
|
USceneComponent* UWorldItemSlotComponent::GetAttachTarget() const
|
|
{
|
|
return GetOwner() ? GetOwner()->GetRootComponent() : nullptr;
|
|
}
|
|
|
|
bool UWorldItemSlotComponent::CanInstall(const FItemInstanceData& Item) const
|
|
{
|
|
if (InstalledItem.bHasItem || !Item.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(this);
|
|
const FItemDefinitionRow* Def = DB ? DB->GetItemDefinition(Item.ItemId) : nullptr;
|
|
if (!Def || !Def->bCanInstallIntoWorldSlot)
|
|
{
|
|
return false;
|
|
}
|
|
// The item must declare this slot as compatible.
|
|
if (!Def->CompatibleWorldSlots.HasTag(SlotTag))
|
|
{
|
|
return false;
|
|
}
|
|
// Optional extra tag gate.
|
|
if (!AcceptedItemTags.IsEmpty())
|
|
{
|
|
FGameplayTagContainer ItemTags = Def->ItemTags;
|
|
ItemTags.AddTag(Def->ItemType);
|
|
if (!ItemTags.HasAny(AcceptedItemTags))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool UWorldItemSlotComponent::InstallItem(const FItemInstanceData& Item, AActor* ProvidedVisual)
|
|
{
|
|
if (!HasAuthority() || !CanInstall(Item))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
InstalledItem.bHasItem = true;
|
|
InstalledItem.Item = Item;
|
|
InstalledItem.Item.LocationType = EItemLocationType::Installed;
|
|
|
|
if (ProvidedVisual)
|
|
{
|
|
// Reuse the carried actor as the visual.
|
|
if (USceneComponent* Target = GetAttachTarget())
|
|
{
|
|
ProvidedVisual->AttachToComponent(Target, FAttachmentTransformRules::SnapToTargetIncludingScale, AttachSocketName);
|
|
}
|
|
if (IWorldItemProvider* Provider = Cast<IWorldItemProvider>(ProvidedVisual))
|
|
{
|
|
Provider->SetCarried(GetOwner(), true); // keep collision off, locked
|
|
}
|
|
InstalledVisual = ProvidedVisual;
|
|
}
|
|
else
|
|
{
|
|
// Spawn a fresh visual pickup and attach it.
|
|
const FTransform Xf = GetAttachTarget() ? GetAttachTarget()->GetComponentTransform() : GetOwner()->GetActorTransform();
|
|
if (APickupItemActor* Visual = APickupItemActor::SpawnPickupFromInstance(this, Item, Xf))
|
|
{
|
|
Visual->SetCarried(GetOwner(), true);
|
|
if (USceneComponent* Target = GetAttachTarget())
|
|
{
|
|
Visual->AttachToComponent(Target, FAttachmentTransformRules::SnapToTargetIncludingScale, AttachSocketName);
|
|
}
|
|
InstalledVisual = Visual;
|
|
}
|
|
}
|
|
|
|
OnSlotChanged.Broadcast();
|
|
UE_LOG(LogItemWorld, Log, TEXT("[Interaction] installed %s into %s"), *Item.ItemId.ToString(), *SlotTag.ToString());
|
|
return true;
|
|
}
|
|
|
|
bool UWorldItemSlotComponent::RemoveItem(FItemInstanceData& OutItem)
|
|
{
|
|
if (!HasAuthority() || !InstalledItem.bHasItem)
|
|
{
|
|
return false;
|
|
}
|
|
OutItem = InstalledItem.Item;
|
|
|
|
if (InstalledVisual)
|
|
{
|
|
InstalledVisual->Destroy();
|
|
InstalledVisual = nullptr;
|
|
}
|
|
|
|
InstalledItem = FWorldSlotInstalledState();
|
|
OnSlotChanged.Broadcast();
|
|
return true;
|
|
}
|
|
|
|
void UWorldItemSlotComponent::OnRep_InstalledItem()
|
|
{
|
|
OnSlotChanged.Broadcast();
|
|
}
|