// 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& 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(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(); }