Migration
This commit is contained in:
BIN
Plugins/SlotBasedInventorySystem/Content/Tests/TestMap_InventorySystem.umap
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Content/Tests/TestMap_InventorySystem.umap
LFS
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/SlotBasedInventorySystem/Content/UI/BPI_InventorySlotWidget.uasset
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Content/UI/BPI_InventorySlotWidget.uasset
LFS
Normal file
Binary file not shown.
BIN
Plugins/SlotBasedInventorySystem/Content/UI/DD_InventorySlotDragged.uasset
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Content/UI/DD_InventorySlotDragged.uasset
LFS
Normal file
Binary file not shown.
BIN
Plugins/SlotBasedInventorySystem/Content/UI/WBP_SlotInventory_BaseSlot.uasset
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Content/UI/WBP_SlotInventory_BaseSlot.uasset
LFS
Normal file
Binary file not shown.
BIN
Plugins/SlotBasedInventorySystem/Content/UI/WBP_SlotInventory_DragVisual.uasset
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Content/UI/WBP_SlotInventory_DragVisual.uasset
LFS
Normal file
Binary file not shown.
BIN
Plugins/SlotBasedInventorySystem/Content/UI/WBP_SlotInventory_Grid.uasset
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Content/UI/WBP_SlotInventory_Grid.uasset
LFS
Normal file
Binary file not shown.
BIN
Plugins/SlotBasedInventorySystem/Resources/Icon128.png
LFS
Normal file
BIN
Plugins/SlotBasedInventorySystem/Resources/Icon128.png
LFS
Normal file
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "0.1",
|
||||
"FriendlyName": "SlotBasedInventorySystem",
|
||||
"Description": "Inventory system with slots",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "Amasson",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "SlotBasedInventorySystem",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "LibAmasson",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
// Amasson
|
||||
|
||||
|
||||
#include "Components/SlotInventoryComponent.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
|
||||
USlotInventoryComponent::USlotInventoryComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
|
||||
SetComponentTickEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
/** Public Content Management */
|
||||
|
||||
const FInventoryContent& USlotInventoryComponent::GetContent() const
|
||||
{
|
||||
return Content;
|
||||
}
|
||||
|
||||
int32 USlotInventoryComponent::GetContentCapacity() const
|
||||
{
|
||||
return Content.Slots.Num();
|
||||
}
|
||||
|
||||
void USlotInventoryComponent::SetContentCapacity(int32 NewCapacity)
|
||||
{
|
||||
if (NewCapacity < 0)
|
||||
NewCapacity = 0;
|
||||
|
||||
const int32 OldCapacity = GetContentCapacity();
|
||||
|
||||
Content.Slots.SetNum(NewCapacity, true);
|
||||
|
||||
if (NewCapacity > OldCapacity)
|
||||
{
|
||||
for (int32 NewSlotIndex = OldCapacity; NewSlotIndex < NewCapacity; NewSlotIndex++)
|
||||
{
|
||||
ClearSlotAtIndex(NewSlotIndex);
|
||||
}
|
||||
}
|
||||
OnInventoryCapacityChanged.Broadcast(this, NewCapacity);
|
||||
}
|
||||
|
||||
|
||||
/** Public Slot Management */
|
||||
|
||||
bool USlotInventoryComponent::GetSlotValueAtIndex(int32 Index, FInventorySlot& SlotValue) const
|
||||
{
|
||||
if (const FInventorySlot* SlotPtr = Content.GetSlotConstPtrAtIndex(Index))
|
||||
{
|
||||
SlotValue = *SlotPtr;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::SetSlotValueAtIndex(int32 Index, const FInventorySlot& NewSlotValue)
|
||||
{
|
||||
if (FInventorySlot* SlotPtr = Content.GetSlotPtrAtIndex(Index))
|
||||
{
|
||||
*SlotPtr = NewSlotValue;
|
||||
MarkDirtySlot(Index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::IsEmptySlotAtIndex(int32 Index) const
|
||||
{
|
||||
if (const FInventorySlot* SlotPtr = Content.GetSlotConstPtrAtIndex(Index))
|
||||
return SlotPtr->IsEmpty();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::ClearSlotAtIndex(int32 Index)
|
||||
{
|
||||
if (FInventorySlot* SlotPtr = Content.GetSlotPtrAtIndex(Index))
|
||||
{
|
||||
bool bIsEmpty = SlotPtr->IsEmpty();
|
||||
SlotPtr->Reset();
|
||||
if (!bIsEmpty)
|
||||
{
|
||||
MarkDirtySlot(Index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 USlotInventoryComponent::GetEmptySlotCounts() const
|
||||
{
|
||||
int32 Total = 0;
|
||||
|
||||
for (const FInventorySlot& Slot : Content.Slots)
|
||||
{
|
||||
if (Slot.IsEmpty())
|
||||
++Total;
|
||||
}
|
||||
return Total;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::ContainsOnlyEmptySlots() const
|
||||
{
|
||||
for (const FInventorySlot& Slot : Content.Slots)
|
||||
{
|
||||
if (!Slot.IsEmpty())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void USlotInventoryComponent::ModifySlotCountAtIndex(int32 Index, int32 ModifyAmount, bool bAllOrNothing, int32& Overflow)
|
||||
{
|
||||
FInventorySlot* SlotPtr = Content.GetSlotPtrAtIndex(Index);
|
||||
|
||||
if (!SlotPtr || SlotPtr->IsEmpty())
|
||||
{
|
||||
Overflow = ModifyAmount;
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8 MaxStackSize = GetMaxStackSizeForID(SlotPtr->ID);
|
||||
|
||||
if (bAllOrNothing)
|
||||
{
|
||||
bool bModified = SlotPtr->TryModifyCountByExact(ModifyAmount, MaxStackSize);
|
||||
if (bModified)
|
||||
{
|
||||
Overflow = 0;
|
||||
MarkDirtySlot(Index);
|
||||
}
|
||||
else
|
||||
Overflow = ModifyAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
SlotPtr->ModifyCountWithOverflow(ModifyAmount, Overflow, MaxStackSize);
|
||||
if (Overflow != ModifyAmount)
|
||||
MarkDirtySlot(Index);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 USlotInventoryComponent::GetMaxStackSizeForID(const FName& ID) const
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
void USlotInventoryComponent::GetMaxStackSizeForIds(const TSet<FName>& Ids, TMap<FName, uint8>& MaxStackSizes) const
|
||||
{
|
||||
for (const FName& Id : Ids)
|
||||
{
|
||||
const uint8 MaxStackSize = GetMaxStackSizeForID(Id);
|
||||
MaxStackSizes.Add(Id, MaxStackSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Content Management */
|
||||
|
||||
int32 USlotInventoryComponent::GetContentIdCount(FName Id) const
|
||||
{
|
||||
int32 Total = 0;
|
||||
|
||||
for (const FInventorySlot& Slot : Content.Slots)
|
||||
{
|
||||
if (Slot.IsEmpty()) continue;
|
||||
|
||||
if (Slot.ID == Id)
|
||||
Total += Slot.Count;
|
||||
}
|
||||
return Total;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::ModifyContentWithOverflow(const TMap<FName, int32>& IdsAndCounts, TMap<FName, int32>& Overflows)
|
||||
{
|
||||
const TMap<FName, uint8>& MaxStackSizes = GetMaxStackSizesFromIds(IdsAndCounts);
|
||||
|
||||
TSet<int32> ModifiedSlots;
|
||||
FInventoryContent::FContentModificationResult ModificationResult(&ModifiedSlots, &Overflows);
|
||||
Content.ModifyContentWithValues(IdsAndCounts, MaxStackSizes, ModificationResult);
|
||||
|
||||
for (int32 ModifiedSlotIndex : ModifiedSlots)
|
||||
{
|
||||
MarkDirtySlot(ModifiedSlotIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::TryModifyContentWithoutOverflow(const TMap<FName, int32>& IdsAndCounts)
|
||||
{
|
||||
const TMap<FName, uint8>& MaxStackSizes = GetMaxStackSizesFromIds(IdsAndCounts);
|
||||
|
||||
TSet<int32> ModifiedSlots;
|
||||
|
||||
FInventoryContent TmpContent = Content;
|
||||
|
||||
TMap<FName, int32> TmpOverflows;
|
||||
FInventoryContent::FContentModificationResult ModificationResult(&ModifiedSlots, &TmpOverflows);
|
||||
|
||||
TmpContent.ModifyContentWithValues(IdsAndCounts, MaxStackSizes, ModificationResult);
|
||||
|
||||
if (!TmpOverflows.IsEmpty())
|
||||
return false;
|
||||
|
||||
Content = TmpContent;
|
||||
|
||||
for (int32 ModifiedSlotIndex : ModifiedSlots)
|
||||
MarkDirtySlot(ModifiedSlotIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::DropSlotTowardOtherInventoryAtIndex(int32 SourceIndex, USlotInventoryComponent* DestinationInventory, int32 DestinationIndex, uint8 MaxAmount)
|
||||
{
|
||||
if (!IsValid(DestinationInventory)) return false;
|
||||
|
||||
FInventorySlot* SourceSlot = Content.GetSlotPtrAtIndex(SourceIndex);
|
||||
if (!SourceSlot)
|
||||
return false;
|
||||
|
||||
const uint8 MaxStackSize = DestinationInventory->GetMaxStackSizeForID(SourceSlot->ID);
|
||||
|
||||
if (DestinationInventory->Content.ReceiveSlotAtIndex(*SourceSlot, DestinationIndex, MaxStackSize, MaxAmount))
|
||||
{
|
||||
MarkDirtySlot(SourceIndex);
|
||||
DestinationInventory->MarkDirtySlot(DestinationIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool USlotInventoryComponent::DropSlotTowardOtherInventory(int32 SourceIndex, USlotInventoryComponent* Destination)
|
||||
{
|
||||
if (!IsValid(Destination)) return false;
|
||||
|
||||
FInventorySlot SourceSlot;
|
||||
if (!GetSlotValueAtIndex(SourceIndex, SourceSlot))
|
||||
return false;
|
||||
|
||||
if (SourceSlot.IsEmpty())
|
||||
return false;
|
||||
|
||||
TMap<FName, int32> Modifications;
|
||||
Modifications.Add(SourceSlot.ID, SourceSlot.Count);
|
||||
|
||||
TMap<FName, int32> Overflows;
|
||||
if (!Destination->ModifyContentWithOverflow(Modifications, Overflows))
|
||||
return false;
|
||||
|
||||
if (Overflows.IsEmpty())
|
||||
{
|
||||
ClearSlotAtIndex(SourceIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
checkf(Overflows.Contains(SourceSlot.ID), TEXT("Overflow is not empty but does not contains SourceSlot.ID"));
|
||||
const int32 NewCount = Overflows[SourceSlot.ID];
|
||||
|
||||
if (SourceSlot.Count == NewCount)
|
||||
return false;
|
||||
|
||||
SourceSlot.Count = NewCount;
|
||||
return SetSlotValueAtIndex(SourceIndex, SourceSlot);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent::RegroupSlotAtIndexWithSimilarIds(int32 Index)
|
||||
{
|
||||
TSet<int32> ModifiedSlots;
|
||||
FInventoryContent::FContentModificationResult ModificationResult(&ModifiedSlots, nullptr);
|
||||
|
||||
FInventorySlot* Slot = Content.GetSlotPtrAtIndex(Index);
|
||||
if (!Slot) return;
|
||||
|
||||
const uint8 MaxStackSize = GetMaxStackSizeForID(Slot->ID);
|
||||
|
||||
Content.RegroupSlotsWithSimilarIdsAtIndex(Index, ModificationResult, MaxStackSize, Slot);
|
||||
|
||||
for (const int32 ModifiedSlotIndex : ModifiedSlots)
|
||||
MarkDirtySlot(ModifiedSlotIndex);
|
||||
}
|
||||
|
||||
|
||||
/** Private Content Management */
|
||||
|
||||
const TMap<FName, uint8> USlotInventoryComponent::GetMaxStackSizesFromIds(const TMap<FName, int32>& IdsAndCounts) const
|
||||
{
|
||||
TSet<FName> Ids;
|
||||
IdsAndCounts.GetKeys(Ids);
|
||||
TMap<FName, uint8> MaxStackSizes;
|
||||
GetMaxStackSizeForIds(Ids, MaxStackSizes);
|
||||
return MaxStackSizes;
|
||||
}
|
||||
|
||||
/** Slot Updating */
|
||||
|
||||
void USlotInventoryComponent::BroadcastContentUpdate()
|
||||
{
|
||||
OnInventoryContentChanged.Broadcast(this, DirtySlots.Array());
|
||||
DirtySlots.Reset();
|
||||
}
|
||||
|
||||
void USlotInventoryComponent::MarkDirtySlot(int32 SlotIndex)
|
||||
{
|
||||
DirtySlots.Add(SlotIndex);
|
||||
MarkSlotsHaveBeenModified();
|
||||
}
|
||||
|
||||
void USlotInventoryComponent::MarkSlotsHaveBeenModified()
|
||||
{
|
||||
SetComponentTickEnabled(true);
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
// Amasson
|
||||
|
||||
|
||||
#include "Components/SlotInventoryComponent_Networked.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
|
||||
USlotInventoryComponent_Networked::USlotInventoryComponent_Networked()
|
||||
{
|
||||
SetIsReplicatedByDefault(true);
|
||||
bHasAuthority = GetOwner() ? GetOwner()->HasAuthority() : false;
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (bHasAuthority)
|
||||
{
|
||||
OnInventoryCapacityChanged.AddDynamic(this, &ThisClass::OnCapacityChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_BroadcastFullInventory_Implementation(bool bOwnerOnly)
|
||||
{
|
||||
TArray<int32> AllIndices;
|
||||
AllIndices.Reserve(GetContentCapacity() + 1); // Reserve space for N+1 elements
|
||||
for (int32 i = 0; i <= GetContentCapacity(); i++)
|
||||
AllIndices.Add(i);
|
||||
|
||||
if (bOwnerOnly)
|
||||
Client_UpdateSlotsValues_Implementation(AllIndices, Content.Slots);
|
||||
else
|
||||
NetMulticast_UpdateSlotsValues_Implementation(AllIndices, Content.Slots);
|
||||
}
|
||||
|
||||
|
||||
/** Client Request */
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestSetContentCapacity_Implementation(int32 NewCapacity)
|
||||
{
|
||||
SetContentCapacity(NewCapacity);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestSetSlotValueAtIndex_Implementation(int32 Index, const FInventorySlot& NewSlotValue)
|
||||
{
|
||||
SetSlotValueAtIndex(Index, NewSlotValue);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestClearSlotAtIndex_Implementation(int32 Index)
|
||||
{
|
||||
ClearSlotAtIndex(Index);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestDropSlotTowardOtherInventoryAtIndex_Implementation(int32 SourceIndex, USlotInventoryComponent* DestinationInventory, int32 DestinationIndex, uint8 MaxAmount)
|
||||
{
|
||||
DropSlotTowardOtherInventoryAtIndex(SourceIndex, DestinationInventory, DestinationIndex, MaxAmount);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestDropSlotTowardOtherInventory_Implementation(int32 SourceIndex, USlotInventoryComponent* DestinationInventory)
|
||||
{
|
||||
DropSlotTowardOtherInventory(SourceIndex, DestinationInventory);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestDropSlotFromOtherInventoryAtIndex_Implementation(int32 DestinationIndex, USlotInventoryComponent* SourceInventory, int32 SourceIndex, uint8 MaxAmount)
|
||||
{
|
||||
if (IsValid(SourceInventory))
|
||||
{
|
||||
SourceInventory->DropSlotTowardOtherInventoryAtIndex(SourceIndex, this, DestinationIndex, MaxAmount);
|
||||
}
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestDropSlotFromOtherInventory_Implementation(USlotInventoryComponent* SourceInventory, int32 SourceIndex)
|
||||
{
|
||||
if (IsValid(SourceInventory))
|
||||
{
|
||||
SourceInventory->DropSlotTowardOtherInventory(SourceIndex, this);
|
||||
}
|
||||
}
|
||||
|
||||
static AActor* GetLastValidOwner(AActor* Actor)
|
||||
{
|
||||
if (IsValid(Actor))
|
||||
{
|
||||
AActor* Owner = Actor->GetOwner();
|
||||
AActor* ValidOwner = GetLastValidOwner(Owner);
|
||||
return IsValid(ValidOwner) ? ValidOwner : Actor;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool IsValidAndCanCallRPC(UActorComponent* Component)
|
||||
{
|
||||
if (IsValid(Component))
|
||||
{
|
||||
AActor* LastOwner = GetLastValidOwner(Component->GetOwner());
|
||||
if (IsValid(LastOwner))
|
||||
{
|
||||
ENetRole NetRole = LastOwner->GetLocalRole();
|
||||
bool bCanCallRPC = NetRole >= ENetRole::ROLE_AutonomousProxy;
|
||||
return bCanCallRPC;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::DropInventorySlotFromSourceToDestinationAtIndex(USlotInventoryComponent_Networked* SourceInventory, int32 SourceIndex, USlotInventoryComponent_Networked* DestinationInventory, int32 DestinationIndex, uint8 MaxAmount)
|
||||
{
|
||||
if (IsValidAndCanCallRPC(SourceInventory))
|
||||
{
|
||||
SourceInventory->Server_RequestDropSlotTowardOtherInventoryAtIndex(SourceIndex, DestinationInventory, DestinationIndex, MaxAmount);
|
||||
}
|
||||
else if (IsValidAndCanCallRPC(DestinationInventory))
|
||||
{
|
||||
DestinationInventory->Server_RequestDropSlotFromOtherInventoryAtIndex(DestinationIndex, SourceInventory, SourceIndex, MaxAmount);
|
||||
}
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::DropInventorySlotFromSourceToDestination(USlotInventoryComponent_Networked* SourceInventory, int32 SourceIndex, USlotInventoryComponent_Networked* DestinationInventory)
|
||||
{
|
||||
if (IsValidAndCanCallRPC(SourceInventory))
|
||||
{
|
||||
SourceInventory->Server_RequestDropSlotTowardOtherInventory(SourceIndex, DestinationInventory);
|
||||
}
|
||||
else if (IsValidAndCanCallRPC(DestinationInventory))
|
||||
{
|
||||
DestinationInventory->Server_RequestDropSlotFromOtherInventory(SourceInventory, SourceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Server_RequestRegroupSlotAtIndexWithSimilarIds_Implementation(int32 Index)
|
||||
{
|
||||
RegroupSlotAtIndexWithSimilarIds(Index);
|
||||
}
|
||||
|
||||
|
||||
/** Slot Update */
|
||||
|
||||
void USlotInventoryComponent_Networked::NetMulticast_UpdateSlotsValues_Implementation(const TArray<int32>& Indices, const TArray<FInventorySlot>& Values)
|
||||
{
|
||||
ReceievedUpdateSlotsValues(Indices, Values);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::Client_UpdateSlotsValues_Implementation(const TArray<int32>& Indices, const TArray<FInventorySlot>& Values)
|
||||
{
|
||||
ReceievedUpdateSlotsValues(Indices, Values);
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::ReceievedUpdateSlotsValues(const TArray<int32>& Indices, const TArray<FInventorySlot>& Values)
|
||||
{
|
||||
checkf(Indices.Num() == Values.Num(), TEXT("SlotInventoryComponent_Networked::ReceievedUpdateSlotsValues: Received miss matching arrays"));
|
||||
|
||||
if (bHasAuthority)
|
||||
return;
|
||||
|
||||
for (int32 i = 0; i < Indices.Num(); i++)
|
||||
{
|
||||
SetSlotValueAtIndex(Indices[i], Values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Capacity Update */
|
||||
|
||||
void USlotInventoryComponent_Networked::OnCapacityChanged(USlotInventoryComponent* SlotInventoryComponent, int32 NewCapacity)
|
||||
{
|
||||
if (SlotInventoryComponent == this)
|
||||
{
|
||||
NetMulticast_UpdateCapacity(NewCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::NetMulticast_UpdateCapacity_Implementation(int32 NewCapacity)
|
||||
{
|
||||
if (bHasAuthority)
|
||||
return;
|
||||
|
||||
SetContentCapacity(NewCapacity);
|
||||
}
|
||||
|
||||
|
||||
/** Content Update */
|
||||
|
||||
void USlotInventoryComponent_Networked::BroadcastContentUpdate()
|
||||
{
|
||||
if (bHasAuthority)
|
||||
BroadcastModifiedSlotsToClients();
|
||||
|
||||
Super::BroadcastContentUpdate();
|
||||
}
|
||||
|
||||
void USlotInventoryComponent_Networked::BroadcastModifiedSlotsToClients()
|
||||
{
|
||||
TArray<int32> Indices;
|
||||
TArray<FInventorySlot> Values;
|
||||
|
||||
for (int32 DirtySlotIndex : DirtySlots)
|
||||
{
|
||||
FInventorySlot SlotValue;
|
||||
if (GetSlotValueAtIndex(DirtySlotIndex, SlotValue))
|
||||
{
|
||||
Indices.Add(DirtySlotIndex);
|
||||
Values.Add(SlotValue);
|
||||
}
|
||||
}
|
||||
NetMulticast_UpdateSlotsValues(Indices, Values);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "SlotBasedInventorySystem.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSlotBasedInventorySystemModule"
|
||||
|
||||
void FSlotBasedInventorySystemModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FSlotBasedInventorySystemModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FSlotBasedInventorySystemModule, SlotBasedInventorySystem)
|
||||
@@ -0,0 +1,15 @@
|
||||
// Amasson
|
||||
|
||||
|
||||
#include "SlotInventoryBlueprintLibrary.h"
|
||||
|
||||
|
||||
bool USlotInventoryBlueprintLibrary::IsValidIndex(const FInventoryContent& Content, int32 Index)
|
||||
{
|
||||
return Content.IsValidIndex(Index);
|
||||
}
|
||||
|
||||
bool USlotInventoryBlueprintLibrary::IsEmptySlot(const FInventorySlot& Slot)
|
||||
{
|
||||
return Slot.IsEmpty();
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
// Amasson
|
||||
|
||||
|
||||
#include "Structures/SlotInventorySystemStructs.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
#include "Templates/UnrealTemplate.h"
|
||||
|
||||
|
||||
bool FInventorySlot::IsEmpty() const
|
||||
{
|
||||
return ID == NAME_None || Count == 0;
|
||||
}
|
||||
|
||||
void FInventorySlot::Reset()
|
||||
{
|
||||
ID = NAME_None;
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
void FInventorySlot::ModifyCountWithOverflow(int32 ModifyAmount, int32& Overflow, uint8 MaxStackSize)
|
||||
{
|
||||
int32 NewCount = Count + ModifyAmount;
|
||||
|
||||
if (NewCount < 0)
|
||||
{
|
||||
Overflow = Count + ModifyAmount;
|
||||
Count = 0;
|
||||
}
|
||||
else if (NewCount > MaxStackSize)
|
||||
{
|
||||
Overflow = Count + ModifyAmount - MaxStackSize;
|
||||
Count = MaxStackSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
Overflow = 0;
|
||||
Count = NewCount;
|
||||
}
|
||||
}
|
||||
|
||||
bool FInventorySlot::TryModifyCountByExact(int32 ModifyAmount, uint8 MaxStackSize)
|
||||
{
|
||||
int32 NewCount = Count + ModifyAmount;
|
||||
|
||||
if (NewCount < 0 || NewCount > MaxStackSize)
|
||||
return false;
|
||||
|
||||
Count = NewCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FInventorySlot::AddIdAndCount(const FName& SlotId, int32 ModifyAmount, int32& Overflow, uint8 MaxStackSize)
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
if (ModifyAmount < 0)
|
||||
{
|
||||
Overflow = ModifyAmount;
|
||||
return false;
|
||||
}
|
||||
ID = SlotId;
|
||||
Count = FMath::Min(ModifyAmount, int32(MaxStackSize));
|
||||
Overflow = ModifyAmount - Count;
|
||||
return Overflow != ModifyAmount;
|
||||
}
|
||||
|
||||
if (ID != SlotId)
|
||||
{
|
||||
Overflow = ModifyAmount;
|
||||
return false;
|
||||
}
|
||||
|
||||
ModifyCountWithOverflow(ModifyAmount, Overflow, MaxStackSize);
|
||||
return Overflow != ModifyAmount;
|
||||
}
|
||||
|
||||
|
||||
/** Inventory Content */
|
||||
|
||||
|
||||
bool FInventoryContent::IsValidIndex(int32 Index) const
|
||||
{
|
||||
return Index >= 0 && Index < Slots.Num();
|
||||
}
|
||||
|
||||
FInventorySlot* FInventoryContent::GetSlotPtrAtIndex(int32 Index)
|
||||
{
|
||||
if (!IsValidIndex(Index))
|
||||
return nullptr;
|
||||
|
||||
return &(Slots[Index]);
|
||||
}
|
||||
|
||||
const FInventorySlot* FInventoryContent::GetSlotConstPtrAtIndex(int32 Index) const
|
||||
{
|
||||
if (!IsValidIndex(Index))
|
||||
return nullptr;
|
||||
|
||||
return &(Slots[Index]);
|
||||
}
|
||||
|
||||
FInventoryContent::FContentModificationResult::FContentModificationResult(TSet<int32>* InModifiedSlots, TMap<FName, int32>* InOverflows)
|
||||
: bModifiedSomething(false), bCreatedEmptySlot(false),
|
||||
ModifiedSlots(InModifiedSlots), Overflows(InOverflows)
|
||||
{}
|
||||
|
||||
void FInventoryContent::ModifyContentWithValues(const TMap<FName, int32>& IdsAndCounts, const TMap<FName, uint8>& MaxStackSizes, FContentModificationResult& ModificationResult)
|
||||
{
|
||||
bool bModified = false;
|
||||
bool bHasPositiveOverflow = false;
|
||||
bool bHasNewEmptySlots = false;
|
||||
|
||||
for (const TPair<FName, int32>& IdAndCount : IdsAndCounts)
|
||||
{
|
||||
const FName& SlotId = IdAndCount.Key;
|
||||
int32 ModifyAmount = IdAndCount.Value;
|
||||
const uint8 MaxStackSize = MaxStackSizes[SlotId];
|
||||
|
||||
FContentModificationResult Result(ModificationResult.ModifiedSlots, nullptr);
|
||||
ReceiveSlotOverflow(SlotId, ModifyAmount, MaxStackSize, false, Result);
|
||||
ReceiveSlotOverflow(SlotId, ModifyAmount, MaxStackSize, true, Result);
|
||||
bModified = Result.bModifiedSomething;
|
||||
bHasNewEmptySlots = Result.bCreatedEmptySlot;
|
||||
|
||||
if (ModifyAmount != 0)
|
||||
{
|
||||
if (ModificationResult.Overflows)
|
||||
ModificationResult.Overflows->Add(SlotId, ModifyAmount);
|
||||
if (ModifyAmount > 0)
|
||||
bHasPositiveOverflow = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bModified && bHasPositiveOverflow && bHasNewEmptySlots && ModificationResult.Overflows)
|
||||
{
|
||||
if (ModificationResult.Overflows)
|
||||
{
|
||||
const TMap<FName, int32> NewModifications = *ModificationResult.Overflows;
|
||||
ModificationResult.Overflows->Reset();
|
||||
ModifyContentWithValues(NewModifications, MaxStackSizes, ModificationResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FInventoryContent::ReceiveSlotOverflow(const FName& SlotId, int32& InoutOverflow, uint8 MaxStackSize, bool bTargetEmptySlots, FContentModificationResult& ModificationResult)
|
||||
{
|
||||
for (int32 i = 0; i < Slots.Num() && InoutOverflow != 0; i++)
|
||||
{
|
||||
FInventorySlot& Slot(Slots[i]);
|
||||
|
||||
if (Slot.IsEmpty() != bTargetEmptySlots)
|
||||
continue;
|
||||
|
||||
if (Slot.AddIdAndCount(SlotId, InoutOverflow, InoutOverflow, MaxStackSize))
|
||||
{
|
||||
ModificationResult.bModifiedSomething = true;
|
||||
if (Slot.IsEmpty())
|
||||
ModificationResult.bCreatedEmptySlot = true;
|
||||
if (ModificationResult.ModifiedSlots)
|
||||
ModificationResult.ModifiedSlots->Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FInventoryContent::ReceiveSlotAtIndex(FInventorySlot& InoutSlot, int32 Index, uint8 MaxStackSize, uint8 MaxTransferAmount)
|
||||
{
|
||||
FInventorySlot* LocalSlot = GetSlotPtrAtIndex(Index);
|
||||
if (!LocalSlot)
|
||||
return false;
|
||||
|
||||
if (LocalSlot->IsEmpty())
|
||||
{
|
||||
if (InoutSlot.IsEmpty())
|
||||
return false;
|
||||
|
||||
LocalSlot->ID = InoutSlot.ID;
|
||||
LocalSlot->Count = 0;
|
||||
}
|
||||
|
||||
if (LocalSlot->ID == InoutSlot.ID)
|
||||
{
|
||||
return MergeSlotsWithSimilarIds(*LocalSlot, InoutSlot, MaxStackSize, MaxTransferAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool bCanReceiveSlot = InoutSlot.Count <= MaxTransferAmount && InoutSlot.Count <= MaxStackSize;
|
||||
if (!bCanReceiveSlot)
|
||||
return false;
|
||||
|
||||
SwapSlots(InoutSlot, *LocalSlot);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FInventoryContent::RegroupSlotsWithSimilarIdsAtIndex(int32 Index, FContentModificationResult& ModificationResult, uint8 MaxStackSize, FInventorySlot* CachedSlotPtr)
|
||||
{
|
||||
FInventorySlot* TargetSlot = CachedSlotPtr ? CachedSlotPtr : GetSlotPtrAtIndex(Index);
|
||||
|
||||
if (!TargetSlot)
|
||||
return;
|
||||
|
||||
if (TargetSlot->IsEmpty())
|
||||
return;
|
||||
|
||||
for (int32 SlotIndex = 0; SlotIndex < Slots.Num() && TargetSlot->Count < MaxStackSize; SlotIndex++)
|
||||
{
|
||||
if (TargetSlot->Count >= MaxStackSize)
|
||||
break;
|
||||
|
||||
if (SlotIndex == Index)
|
||||
continue;
|
||||
|
||||
FInventorySlot& Slot = Slots[SlotIndex];
|
||||
|
||||
if (!Slot.IsEmpty() && Slot.ID == TargetSlot->ID)
|
||||
{
|
||||
bool bMerged = MergeSlotsWithSimilarIds(*TargetSlot, Slot, MaxStackSize);
|
||||
if (bMerged)
|
||||
{
|
||||
ModificationResult.bModifiedSomething = true;
|
||||
if (ModificationResult.ModifiedSlots)
|
||||
ModificationResult.ModifiedSlots->Add(SlotIndex);
|
||||
if (Slot.IsEmpty())
|
||||
ModificationResult.bCreatedEmptySlot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ModificationResult.bModifiedSomething)
|
||||
if (ModificationResult.ModifiedSlots)
|
||||
ModificationResult.ModifiedSlots->Add(Index);
|
||||
}
|
||||
|
||||
bool FInventoryContent::MergeSlotsWithSimilarIds(FInventorySlot& DestinationSlot, FInventorySlot& SourceSlot, uint8 MaxStackSize, uint8 MaxTransferAmount)
|
||||
{
|
||||
checkf(SourceSlot.ID == DestinationSlot.ID, TEXT("Tried to merge slots with different Ids (%s!=%s)"), *SourceSlot.ID.ToString(), *DestinationSlot.ID.ToString());
|
||||
|
||||
const int32 LocalCount = DestinationSlot.Count;
|
||||
const int32 ReceiveCount = SourceSlot.Count;
|
||||
|
||||
const int32 TotalCount = LocalCount + ReceiveCount;
|
||||
const int32 StackCount = FMath::Min(TotalCount, int32(MaxStackSize));
|
||||
|
||||
const int32 TotalTransferAmount = StackCount - LocalCount;
|
||||
const int32 TransferAmount = FMath::Min(TotalTransferAmount, int32(MaxTransferAmount));
|
||||
|
||||
if (TransferAmount == 0)
|
||||
return false;
|
||||
|
||||
SourceSlot.Count -= TransferAmount;
|
||||
DestinationSlot.Count += TransferAmount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FInventoryContent::SwapSlots(FInventorySlot& FirstSlot, FInventorySlot& SecondSlot)
|
||||
{
|
||||
Swap(FirstSlot, SecondSlot);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Amasson
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Structures/SlotInventorySystemStructs.h"
|
||||
#include "SlotInventoryComponent.generated.h"
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInventoryCapacityChangedSignature, USlotInventoryComponent*, SlotInventoryComponent, int32, NewCapacity);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInventoryContentChangedSignature, USlotInventoryComponent*, SlotInventoryComponent, const TArray<int32>&, ChangedSlots);
|
||||
|
||||
UCLASS( Blueprintable, ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||
class SLOTBASEDINVENTORYSYSTEM_API USlotInventoryComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
USlotInventoryComponent();
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnInventoryCapacityChangedSignature OnInventoryCapacityChanged;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnInventoryContentChangedSignature OnInventoryContentChanged;
|
||||
|
||||
|
||||
/** Content Management */
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content")
|
||||
const FInventoryContent& GetContent() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Capacity")
|
||||
int32 GetContentCapacity() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Capacity")
|
||||
void SetContentCapacity(int32 NewCapacity);
|
||||
|
||||
|
||||
/** Slot Management */
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot")
|
||||
bool GetSlotValueAtIndex(int32 Index, FInventorySlot& SlotValue) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot")
|
||||
bool SetSlotValueAtIndex(int32 Index, const FInventorySlot& NewSlotValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
bool IsEmptySlotAtIndex(int32 Index) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
bool ClearSlotAtIndex(int32 Index);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
int32 GetEmptySlotCounts() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
bool ContainsOnlyEmptySlots() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
void ModifySlotCountAtIndex(int32 Index, int32 ModifyAmount, bool bAllOrNothing, int32& Overflow);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
virtual uint8 GetMaxStackSizeForID(const FName& ID) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Slot|Count")
|
||||
void GetMaxStackSizeForIds(const TSet<FName>& Ids, TMap<FName, uint8>& MaxStackSizes) const;
|
||||
|
||||
|
||||
/** Content Management */
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Count")
|
||||
int32 GetContentIdCount(FName Id) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Modify")
|
||||
bool ModifyContentWithOverflow(const TMap<FName, int32>& IdsAndCounts, TMap<FName, int32>& Overflows);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Modify")
|
||||
bool TryModifyContentWithoutOverflow(const TMap<FName, int32>& IdsAndCounts);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Action")
|
||||
bool DropSlotTowardOtherInventoryAtIndex(int32 SourceIndex, USlotInventoryComponent* Destination, int32 DestinationIndex, uint8 MaxAmount = 255);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Action")
|
||||
bool DropSlotTowardOtherInventory(int32 SourceIndex, USlotInventoryComponent* Destination);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Content|Action")
|
||||
void RegroupSlotAtIndexWithSimilarIds(int32 Index);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/** Content Management */
|
||||
|
||||
const TMap<FName, uint8> GetMaxStackSizesFromIds(const TMap<FName, int32>& IdsAndCounts) const;
|
||||
|
||||
|
||||
/** Slot Updating */
|
||||
|
||||
virtual void BroadcastContentUpdate();
|
||||
|
||||
void MarkDirtySlot(int32 SlotIndex);
|
||||
|
||||
void MarkSlotsHaveBeenModified();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* The purpose of the tick function is to trigger an update broadcast only once.
|
||||
* We can use mark modified content multiple times in a same tick but they will
|
||||
* be cached and only broadcast in the next tick all at once.
|
||||
*/
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
SetComponentTickEnabled(false);
|
||||
BroadcastContentUpdate();
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Content", meta = (AllowPrivateAccess = true))
|
||||
FInventoryContent Content;
|
||||
|
||||
TSet<int32> DirtySlots;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
// Amasson
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/SlotInventoryComponent.h"
|
||||
#include "SlotInventoryComponent_Networked.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS( Blueprintable, ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
|
||||
class SLOTBASEDINVENTORYSYSTEM_API USlotInventoryComponent_Networked : public USlotInventoryComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
USlotInventoryComponent_Networked();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Update")
|
||||
void Server_BroadcastFullInventory(bool bOwnerOnly = true);
|
||||
|
||||
|
||||
/** Client Request */
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Content|Capacity")
|
||||
void Server_RequestSetContentCapacity(int32 NewCapacity);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Content|Slot")
|
||||
void Server_RequestSetSlotValueAtIndex(int32 Index, const FInventorySlot& NewSlotValue);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Content|Slot")
|
||||
void Server_RequestClearSlotAtIndex(int32 Index);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Action|Drop")
|
||||
void Server_RequestDropSlotTowardOtherInventoryAtIndex(int32 SourceIndex, USlotInventoryComponent* DestinationInventory, int32 DestinationIndex, uint8 MaxAmount = 255);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Action|Drop")
|
||||
void Server_RequestDropSlotTowardOtherInventory(int32 SourceIndex, USlotInventoryComponent* DestinationInventory);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Action|Drop")
|
||||
void Server_RequestDropSlotFromOtherInventoryAtIndex(int32 DestinationIndex, USlotInventoryComponent* SourceInventory, int32 SourceIndex, uint8 MaxAmount = 255);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Action|Drop")
|
||||
void Server_RequestDropSlotFromOtherInventory(USlotInventoryComponent* SourceInventory, int32 SourceIndex);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "ClientRequest|Action|Drop")
|
||||
static void DropInventorySlotFromSourceToDestinationAtIndex(USlotInventoryComponent_Networked* SourceInventory, int32 SourceIndex, USlotInventoryComponent_Networked* DestinationInventory, int32 DestinationIndex, uint8 MaxAmount = 255);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "ClientRequest|Action|Drop")
|
||||
static void DropInventorySlotFromSourceToDestination(USlotInventoryComponent_Networked* SourceInventory, int32 SourceIndex, USlotInventoryComponent_Networked* DestinationInventory);
|
||||
|
||||
UFUNCTION(Server, Reliable, BlueprintCallable, Category = "ClientRequest|Action")
|
||||
void Server_RequestRegroupSlotAtIndexWithSimilarIds(int32 Index);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
/** Slot Update */
|
||||
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void NetMulticast_UpdateSlotsValues(const TArray<int32>& Indices, const TArray<FInventorySlot>& Values);
|
||||
|
||||
UFUNCTION(Client, Reliable)
|
||||
void Client_UpdateSlotsValues(const TArray<int32>& Indices, const TArray<FInventorySlot>& Values);
|
||||
|
||||
void ReceievedUpdateSlotsValues(const TArray<int32>& Indices, const TArray<FInventorySlot>& Values);
|
||||
|
||||
|
||||
/** Capacity Update */
|
||||
|
||||
UFUNCTION()
|
||||
void OnCapacityChanged(USlotInventoryComponent* SlotInventoryComponent, int32 NewCapacity);
|
||||
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void NetMulticast_UpdateCapacity(int32 NewCapacity);
|
||||
|
||||
|
||||
/** Content Update */
|
||||
|
||||
virtual void BroadcastContentUpdate() override;
|
||||
|
||||
void BroadcastModifiedSlotsToClients();
|
||||
|
||||
bool bHasAuthority;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FSlotBasedInventorySystemModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
// Amasson
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Structures/SlotInventorySystemStructs.h"
|
||||
#include "SlotInventoryBlueprintLibrary.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SLOTBASEDINVENTORYSYSTEM_API USlotInventoryBlueprintLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/** Inventory Content */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SlotInventory|Content")
|
||||
static bool IsValidIndex(const FInventoryContent& Content, int32 Index);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SlotInventory|Slot")
|
||||
static bool IsEmptySlot(const FInventorySlot& Slot);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
// Amasson
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataTable.h"
|
||||
#include "SlotInventorySystemStructs.generated.h"
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FInventorySlot
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame)
|
||||
FName ID;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame)
|
||||
uint8 Count = 0;
|
||||
|
||||
|
||||
bool IsEmpty() const;
|
||||
void Reset();
|
||||
void ModifyCountWithOverflow(int32 ModifyAmount, int32& Overflow, uint8 MaxStackSize = 255);
|
||||
bool TryModifyCountByExact(int32 ModifyAmount, uint8 MaxStackSize = 255);
|
||||
bool AddIdAndCount(const FName& SlotId, int32 ModifyAmount, int32& Overflow, uint8 MaxStackSize = 255);
|
||||
|
||||
};
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct SLOTBASEDINVENTORYSYSTEM_API FInventoryContent
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame)
|
||||
TArray<FInventorySlot> Slots;
|
||||
|
||||
|
||||
bool IsValidIndex(int32 Index) const;
|
||||
|
||||
FInventorySlot* GetSlotPtrAtIndex(int32 Index);
|
||||
const FInventorySlot* GetSlotConstPtrAtIndex(int32 Index) const;
|
||||
|
||||
struct FContentModificationResult
|
||||
{
|
||||
bool bModifiedSomething;
|
||||
bool bCreatedEmptySlot;
|
||||
TSet<int32>* ModifiedSlots;
|
||||
TMap<FName, int32>* Overflows;
|
||||
|
||||
FContentModificationResult(TSet<int32>* InModifiedSlots, TMap<FName, int32>* Overflows);
|
||||
};
|
||||
|
||||
void ModifyContentWithValues(const TMap<FName, int32>& IdsAndCounts, const TMap<FName, uint8>& MaxStackSizes, FContentModificationResult& ModificationResult);
|
||||
|
||||
void ReceiveSlotOverflow(const FName& SlotId, int32& InoutOverflow, uint8 MaxStackSize, bool bTargetEmptySlots, FContentModificationResult& ModificationResult);
|
||||
bool ReceiveSlotAtIndex(FInventorySlot& InoutSlot, int32 Index, uint8 MaxStackSize = 255, uint8 MaxTransferAmount = 255);
|
||||
|
||||
void RegroupSlotsWithSimilarIdsAtIndex(int32 Index, FContentModificationResult& ModificationResult, uint8 MaxStackSize = 255, FInventorySlot* CachedSlotPtr = nullptr);
|
||||
|
||||
static bool MergeSlotsWithSimilarIds(FInventorySlot& DestinationSlot, FInventorySlot& SourceSlot, uint8 MaxStackSize = 255, uint8 MaxTransferAmount = 255);
|
||||
|
||||
static void SwapSlots(FInventorySlot& FirstSlot, FInventorySlot& SecondSlot);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class SlotBasedInventorySystem : ModuleRules
|
||||
{
|
||||
public SlotBasedInventorySystem(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user