// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #include "GIS_EquipmentStructLibrary.h" #include "GIS_EquipmentInstance.h" #include "GIS_EquipmentSystemComponent.h" #include "GIS_ItemFragment_Equippable.h" #include "GIS_ItemInstance.h" #include "GIS_LogChannels.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GIS_EquipmentStructLibrary) FString FGIS_EquipmentEntry::GetDebugString() const { return FString::Printf(TEXT("%s"), *GetNameSafe(Instance)); } bool FGIS_EquipmentEntry::CheckClientDataReady() const { bool bDataReady = Instance != nullptr && ItemInstance != nullptr && EquippedSlot.IsValid(); if (!bDataReady) { return false; } UGIS_EquipmentInstance* EquipmentInstance = Cast(Instance); if (!EquipmentInstance) return true; const UGIS_ItemFragment_Equippable* Equippable = ItemInstance->FindFragmentByClass(); if (!Equippable || Equippable->bActorBased || Equippable->ActorsToSpawn.IsEmpty()) return true; TArray EquipmentActors = EquipmentInstance->GetEquipmentActors(); // actor num doesn't match. if (EquipmentActors.Num() != Equippable->ActorsToSpawn.Num()) { return false; } bool bValid = true; for (int32 i = 0; i < EquipmentActors.Num(); i++) { if (!::IsValid(EquipmentActors[i])) { bValid = false; break; } } return bValid; } bool FGIS_EquipmentEntry::IsValidEntry() const { return Instance != nullptr && ItemInstance != nullptr && EquippedSlot.IsValid(); } void FGIS_EquipmentContainer::PreReplicatedRemove(const TArrayView RemovedIndices, int32 FinalSize) { for (int32 Index : RemovedIndices) { FGIS_EquipmentEntry& Entry = Entries[Index]; // already in the list. if (OwningComponent->SlotToInstanceMap.Contains(Entry.EquippedSlot)) { OwningComponent->OnEquipmentEntryRemoved(Entry, Index); } else if (OwningComponent->PendingEquipmentEntries.Contains(Index)) { GIS_OWNED_CLOG(OwningComponent, Warning, "Discard pending equipment(%s).", *OwningComponent->PendingEquipmentEntries[Index].GetDebugString()) OwningComponent->PendingEquipmentEntries.Remove(Index); } Entry.bPrevActive = Entry.bActive; Entry.PrevEquippedGroup = Entry.EquippedGroup; } } void FGIS_EquipmentContainer::PostReplicatedAdd(const TArrayView AddedIndices, int32 FinalSize) { for (int32 Index : AddedIndices) { FGIS_EquipmentEntry& Entry = Entries[Index]; if (OwningComponent) { if (OwningComponent->GetOwner() && OwningComponent->IsEquipmentSystemInitialized() && Entry.CheckClientDataReady()) { OwningComponent->OnEquipmentEntryAdded(Entry, Index); } else { OwningComponent->PendingEquipmentEntries.Add(Index, Entry); } } Entry.bPrevActive = Entry.bActive; } } void FGIS_EquipmentContainer::PostReplicatedChange(const TArrayView ChangedIndices, int32 FinalSize) { for (int32 Index : ChangedIndices) { FGIS_EquipmentEntry& Entry = Entries[Index]; if (OwningComponent->SlotToInstanceMap.Contains(Entry.EquippedSlot)) // Already Added. { OwningComponent->OnEquipmentEntryChanged(Entry, Index); } else if (OwningComponent->PendingEquipmentEntries.Contains(Index)) // In pending list. { OwningComponent->PendingEquipmentEntries.Emplace(Index, Entry); } else { OwningComponent->PendingEquipmentEntries.Add(Index, Entry); // Add to pending list. } Entry.bPrevActive = Entry.bActive; Entry.PrevEquippedGroup = Entry.EquippedGroup; } } int32 FGIS_EquipmentContainer::IndexOfBySlot(const FGameplayTag& Slot) const { if (!Slot.IsValid()) { return INDEX_NONE; } return Entries.IndexOfByPredicate([Slot](const FGIS_EquipmentEntry& Entry) { return Entry.EquippedSlot == Slot; }); } int32 FGIS_EquipmentContainer::IndexOfByGroup(const FGameplayTag& Group) const { if (!Group.IsValid()) { return INDEX_NONE; } return Entries.IndexOfByPredicate([Group](const FGIS_EquipmentEntry& Entry) { return Entry.EquippedGroup.IsValid() && Entry.EquippedGroup == Group; }); } int32 FGIS_EquipmentContainer::IndexOfByInstance(const UObject* Instance) const { if (!IsValid(Instance)) { return INDEX_NONE; } return Entries.IndexOfByPredicate([Instance](const FGIS_EquipmentEntry& Entry) { return Entry.Instance && Entry.Instance == Instance; }); } int32 FGIS_EquipmentContainer::IndexOfByItem(const UGIS_ItemInstance* Item) const { if (!IsValid(Item)) { return INDEX_NONE; } return Entries.IndexOfByPredicate([Item](const FGIS_EquipmentEntry& Entry) { return Entry.ItemInstance && Entry.ItemInstance == Item; }); } int32 FGIS_EquipmentContainer::IndexOfByItemId(const FGuid& ItemId) const { if (!ItemId.IsValid()) { return INDEX_NONE; } return Entries.IndexOfByPredicate([ItemId](const FGIS_EquipmentEntry& Entry) { return Entry.ItemInstance && Entry.ItemInstance->GetItemId() == ItemId; }); }