第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GIS_CraftingStructLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GIS_CraftingStructLibrary)

View File

@@ -0,0 +1,252 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GIS_CraftingSystemComponent.h"
#include "Engine/World.h"
#include "GIS_InventorySystemComponent.h"
#include "GIS_CurrencySystemComponent.h"
#include "GIS_InventoryFunctionLibrary.h"
#include "GIS_InventorySubsystem.h"
#include "GIS_ItemCollection.h"
#include "Items/GIS_ItemDefinition.h"
#include "GIS_ItemFragment_CraftingRecipe.h"
#include "Items/GIS_ItemInstance.h"
#include "Kismet/KismetMathLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GIS_CraftingSystemComponent)
// Sets default values for this component's properties
UGIS_CraftingSystemComponent::UGIS_CraftingSystemComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = false;
NumOfSelectedItemsCache = 0;
// ...
}
bool UGIS_CraftingSystemComponent::Craft(const UGIS_ItemDefinition* Recipe, UGIS_InventorySystemComponent* CostInventory, int32 Quantity)
{
const UGIS_ItemFragment_CraftingRecipe* RecipeFragment = GetRecipeFragment(Recipe);
if (RecipeFragment == nullptr)
{
return false;
}
return CraftInternal(Recipe, CostInventory, Quantity);
}
bool UGIS_CraftingSystemComponent::CanCraft(const UGIS_ItemDefinition* RecipeDefinition, UGIS_InventorySystemComponent* Inventory, int32 Quantity)
{
return CanCraftInternal(RecipeDefinition, Inventory, Quantity);
}
bool UGIS_CraftingSystemComponent::RemoveItemIngredients(UGIS_InventorySystemComponent* Inventory, const TArray<FGIS_ItemInfo>& ItemIngredients)
{
for (int32 i = 0; i < ItemIngredients.Num(); i++)
{
FGIS_ItemInfo ItemInfoToRemove;
ItemInfoToRemove.Item = ItemIngredients[i].Item;
ItemInfoToRemove.Amount = ItemIngredients[i].Amount;
if (Inventory->GetDefaultCollection()->RemoveItem(ItemInfoToRemove).Amount == ItemInfoToRemove.Amount) { continue; }
return false;
}
return false;
}
bool UGIS_CraftingSystemComponent::IsValidRecipe_Implementation(const UGIS_ItemDefinition* RecipeDefinition) const
{
if (const UGIS_ItemFragment_CraftingRecipe* Fragment = GetRecipeFragment(RecipeDefinition))
{
return !Fragment->InputItems.IsEmpty() && !Fragment->OutputItems.IsEmpty();
}
return false;
}
bool UGIS_CraftingSystemComponent::CanCraftInternal(const UGIS_ItemDefinition* RecipeDefinition, UGIS_InventorySystemComponent* Inventory,
int32 Quantity)
{
if (!IsValidRecipe(RecipeDefinition))
{
return false;
}
const UGIS_ItemFragment_CraftingRecipe* RecipeFragment = GetRecipeFragment(RecipeDefinition);
check(RecipeFragment != nullptr);
if (!SelectItemForIngredients(Inventory, RecipeFragment->InputItems, Quantity))
{
return false;
}
TArray<FGIS_ItemInfo> Selected;
int32 SelectedCount = 0;
// Has enough items? 有足够的道具?
if (!CheckIfEnoughItemIngredients(RecipeFragment->InputItems, Quantity, SelectedItemsCache, Selected, SelectedCount))
{
return false;
}
UGIS_CurrencySystemComponent* CurrencySystem = Inventory->GetCurrencySystem();
if (CurrencySystem == nullptr)
{
return false;
}
// Has enough currencies? 有足够货币?
if (!CurrencySystem->HasCurrencies(UGIS_InventoryFunctionLibrary::MultiplyCurrencies(RecipeFragment->InputCurrencies, Quantity)))
{
return false;
}
return true;
}
bool UGIS_CraftingSystemComponent::CraftInternal(const UGIS_ItemDefinition* RecipeDefinition, UGIS_InventorySystemComponent* Inventory, int32 Quantity)
{
const UGIS_ItemFragment_CraftingRecipe* RecipeFragment = GetRecipeFragment(RecipeDefinition);
if (RecipeFragment == nullptr)
{
return false;
}
if (!SelectItemForIngredients(Inventory, RecipeFragment->InputItems, Quantity))
{
return false;
}
if (!CanCraftInternal(RecipeDefinition, Inventory, Quantity))
{
return false;
}
UGIS_CurrencySystemComponent* CurrencySystem = Inventory->GetCurrencySystem();
bool bCurrencyRemoveSuccess = CurrencySystem->RemoveCurrencies(UGIS_InventoryFunctionLibrary::MultiplyCurrencies(RecipeFragment->InputCurrencies, Quantity));
bool bItemRemoveSuccess = RemoveItemIngredients(Inventory, SelectedItemsCache);
if (bCurrencyRemoveSuccess && bItemRemoveSuccess)
{
ProduceCraftingOutput(RecipeDefinition, Inventory, Quantity);
return true;
}
return false;
}
void UGIS_CraftingSystemComponent::ProduceCraftingOutput(const UGIS_ItemDefinition* RecipeDefinition, UGIS_InventorySystemComponent* Inventory, int32 Quantity)
{
const UGIS_ItemFragment_CraftingRecipe* RecipeFragment = GetRecipeFragment(RecipeDefinition);
if (RecipeFragment == nullptr)
{
return;
}
TArray<FGIS_ItemDefinitionAmount> ItemAmounts = UGIS_InventoryFunctionLibrary::MultiplyItemAmounts(RecipeFragment->OutputItems, Quantity);
for (int32 i = 0; i < ItemAmounts.Num(); i++)
{
FGIS_ItemInfo ItemInfoToAdd;
ItemInfoToAdd.Item = UGIS_InventorySubsystem::Get(GetWorld())->CreateItem(Inventory->GetOwner(), ItemAmounts[i].Definition);
ItemInfoToAdd.Amount = ItemAmounts[i].Amount;
Inventory->AddItem(ItemInfoToAdd);
}
}
const UGIS_ItemFragment_CraftingRecipe* UGIS_CraftingSystemComponent::GetRecipeFragment(const UGIS_ItemDefinition* RecipeDefinition) const
{
return RecipeDefinition ? RecipeDefinition->FindFragment<UGIS_ItemFragment_CraftingRecipe>() : nullptr;
}
bool UGIS_CraftingSystemComponent::SelectItemForIngredients(const UGIS_InventorySystemComponent* Inventory, const TArray<FGIS_ItemDefinitionAmount>& ItemIngredients, int32 Quantity)
{
SelectedItemsCache.Empty();
NumOfSelectedItemsCache = 0;
for (int32 i = 0; i < ItemIngredients.Num(); i++)
{
auto RequiredItem = ItemIngredients[i];
int32 NeededAmount = RequiredItem.Amount * Quantity;
TArray<FGIS_ItemInfo> FilteredItemInfos;
if (Inventory->GetItemInfosByDefinition(RequiredItem.Definition, FilteredItemInfos))
{
for (int32 j = 0; j < FilteredItemInfos.Num(); j++)
{
const FGIS_ItemInfo& itemInfo = FilteredItemInfos[j];
int32 HaveAmount = itemInfo.Amount;
bool FoundSelectedItem = false;
for (int k = 0; k < NumOfSelectedItemsCache; k++)
{
const FGIS_ItemInfo& SelectedItemInfo = SelectedItemsCache[k];
if (itemInfo.Item != SelectedItemInfo.Item) { continue; }
if (itemInfo.StackId != SelectedItemInfo.StackId) { continue; }
FoundSelectedItem = true;
HaveAmount = FMath::Max(0, HaveAmount - SelectedItemInfo.Amount);
int32 additionalIgnoreAmount = FMath::Clamp(HaveAmount, 0, NeededAmount);
//更新已经选择道具信息。
SelectedItemsCache[k] = FGIS_ItemInfo(SelectedItemInfo.Amount + additionalIgnoreAmount, SelectedItemInfo);
}
int32 SelectedAmount = FMath::Clamp(HaveAmount, 0, NeededAmount);
//记录已选择道具信息。
if (FoundSelectedItem == false)
{
SelectedItemsCache.SetNum(NumOfSelectedItemsCache + 1);
SelectedItemsCache[NumOfSelectedItemsCache] = FGIS_ItemInfo(SelectedAmount, itemInfo);
NumOfSelectedItemsCache++;
}
NeededAmount -= SelectedAmount;
if (NeededAmount <= 0) { break; }
}
}
if (NeededAmount <= 0) { continue; }
return false;
}
return true;
}
bool UGIS_CraftingSystemComponent::CheckIfEnoughItemIngredients(const TArray<FGIS_ItemDefinitionAmount>& ItemIngredients, int32 Quantity, const TArray<FGIS_ItemInfo>& SelectedItems,
TArray<FGIS_ItemInfo>& ItemsToIgnore, int32& NumOfItemsToIgnore)
{
for (int32 i = 0; i < ItemIngredients.Num(); i++)
{
const FGIS_ItemDefinitionAmount& ingredientAmount = ItemIngredients[i];
int32 neededAmount = Quantity * ingredientAmount.Amount;
for (int32 j = 0; j < SelectedItems.Num(); j++)
{
const FGIS_ItemInfo& itemInfo = SelectedItems[j];
if (ingredientAmount.Definition != itemInfo.Item->GetDefinition()) { continue; }
int32 haveAmount = itemInfo.Amount;
bool foundMatch = false;
int32 selectedAmount = FMath::Clamp(haveAmount, 0, neededAmount);
if (foundMatch == false)
{
ItemsToIgnore.SetNum(NumOfItemsToIgnore + 1);
ItemsToIgnore[NumOfItemsToIgnore] = FGIS_ItemInfo(selectedAmount, itemInfo);
NumOfItemsToIgnore++;
}
neededAmount -= selectedAmount;
if (neededAmount <= 0) { break; }
}
if (neededAmount > 0)
{
return false;
}
}
return true;
}

View File

@@ -0,0 +1,20 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GIS_ItemFragment_CraftingRecipe.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GIS_ItemFragment_CraftingRecipe)
#if WITH_EDITOR
#include "UObject/ObjectSaveContext.h"
void UGIS_ItemFragment_CraftingRecipe::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
for (FGIS_ItemDefinitionAmount& DefaultItem : OutputItems)
{
DefaultItem.EditorFriendlyName = FString::Format(TEXT("{0}x {1}"), {DefaultItem.Amount, DefaultItem.Definition.IsNull() ? TEXT("Invalid Item") : DefaultItem.Definition.GetAssetName()});
}
}
#endif