513 lines
14 KiB
C++
513 lines
14 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "ItemDatabase.h"
|
|
#include <EngineUtils.h>
|
|
#include "PlantItem.h"
|
|
#include "DataAssets/ItemDataAsset.h"
|
|
#include "Engine/AssetManager.h"
|
|
#include "BookOfAlchemyManager.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "ProjectEleri/Components/MoveActorsComponent.h"
|
|
#include "ProjectEleri/Settings/EleriGameSettings.h"
|
|
#include "ProjectEleri/System/MainBlueprintFunctionLibrary.h"
|
|
|
|
UWorld* UItemDatabase::GetWorld() const {
|
|
if (const UGameInstance* GameInstance = GetGameInstance()) {
|
|
return GameInstance->GetWorld();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UItemDatabase::Initialize(FSubsystemCollectionBase& Collection) {
|
|
Super::Initialize(Collection);
|
|
|
|
auto LoadDataLambda = [this]() {
|
|
if (UAssetManager* AssetManager = UAssetManager::GetIfInitialized()) {
|
|
AssetManager->GetPrimaryAssetIdList(FPrimaryAssetType("ItemDataAsset"), AllAssetIds);
|
|
FArrayOfItemRefs ArrayOfItemCategories[(int)Category::MAX];
|
|
TArray<FAssetData> DataList;
|
|
AssetManager->GetPrimaryAssetDataList(FName("ItemDataAsset"), DataList);
|
|
for (int32 i = 0; i < DataList.Num(); i++) {
|
|
UItemDataAsset* DataAsset = Cast<UItemDataAsset>(DataList[i].GetAsset());
|
|
LoadedItems.Add(DataAsset);
|
|
ArrayOfItemCategories[(int)DataAsset->ItemCategory].ItemArray.Add(DataAsset);
|
|
}
|
|
for (int32 i = 0; i < (int)Category::MAX; i++) {
|
|
LoadedItemsByCategory.Add((Category)i, ArrayOfItemCategories[i]);
|
|
}
|
|
for (int32 i = 0; i < (int)Category::MAX; i++) {
|
|
if (LoadedItemsByCategory[(Category)i].ItemArray.Num() > 0)
|
|
LoadedItemsByCategory[(Category)i].SortByName();
|
|
}
|
|
|
|
if (ABookOfAlchemyManager* AlchemyBook = Cast<ABookOfAlchemyManager>(UGameplayStatics::GetActorOfClass(GetWorld(), ABookOfAlchemyManager::StaticClass()))) {
|
|
AlchemyBook->PreloadLetters();
|
|
}
|
|
|
|
PrepareItemPriceData();
|
|
}
|
|
};
|
|
|
|
if (UAssetManager* AssetManager = UAssetManager::GetIfInitialized()) {
|
|
LoadDataLambda();
|
|
}
|
|
else {
|
|
UAssetManager::CallOrRegister_OnCompletedInitialScan(FStopRenderingThreadDelegate::CreateLambda(LoadDataLambda));
|
|
}
|
|
|
|
RemovableStaticMeshClass = GetDefault<UEleriGameSettings>()->RemovableStaticMeshClass;
|
|
ItemSeedCarouselClass = GetDefault<UEleriGameSettings>()->ItemSeedCarouselClass;
|
|
}
|
|
|
|
|
|
TArray<UItemDataAsset*> UItemDatabase::GetItemsOfElement(Element InElement, Category InCategory)
|
|
{
|
|
TArray<UItemDataAsset*> ItemList;
|
|
|
|
for (int32 i = 0; i < LoadedItemsByCategory[InCategory].ItemArray.Num(); i++)
|
|
{
|
|
if (LoadedItemsByCategory[InCategory].ItemArray[i]->ItemElement == InElement)
|
|
ItemList.Add(LoadedItemsByCategory[InCategory].ItemArray[i]);
|
|
}
|
|
|
|
return ItemList;
|
|
}
|
|
|
|
bool UItemDatabase::DoChemicalAlignmentsMatch(TArray<int32> Alignments, TArray<int32> PotionAlignments)
|
|
{
|
|
for (int32 i = 0; i < PotionAlignments.Num(); i++)
|
|
{
|
|
if (Alignments.Find(PotionAlignments[i]) == INDEX_NONE)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
UItemDataAsset* UItemDatabase::GetPotionFromItems(TArray<UItemDataAsset*> Items, bool& Found)
|
|
{
|
|
TArray<int32> itemChemicalAlignments;
|
|
TMap<ItemProperty, int32> potionPropertyMap;
|
|
//int32 checkCount;
|
|
|
|
for (int32 i = 0; i < Items.Num(); i++)
|
|
{
|
|
for (int32 j = 0; j < Items[i]->ChemicalAlignments.Num(); j++)
|
|
{
|
|
itemChemicalAlignments.AddUnique(Items[i]->ChemicalAlignments[j]);
|
|
}
|
|
}
|
|
|
|
for (int32 i = 0; i < Items.Num(); i++)
|
|
{
|
|
for (int32 j = 0; j < Items[i]->Properties.Num(); j++)
|
|
{
|
|
if (potionPropertyMap.Find(Items[i]->Properties[j].Property) == nullptr)
|
|
potionPropertyMap.Add(Items[i]->Properties[j].Property, 1);
|
|
else
|
|
potionPropertyMap.Add(Items[i]->Properties[j].Property, *potionPropertyMap.Find(Items[i]->Properties[j].Property) + 1);
|
|
}
|
|
}
|
|
|
|
ItemProperty strongestProperty = ItemProperty::Heal;
|
|
int32 strongestValue = 0;
|
|
TArray<UItemDataAsset*> potentialPotions;
|
|
|
|
TArray<ItemProperty> keys;
|
|
potionPropertyMap.GetKeys(keys);
|
|
|
|
for (int32 i = 0; i < keys.Num(); i++)
|
|
{
|
|
int32 val = *potionPropertyMap.Find(keys[i]);
|
|
if (val > strongestValue) {
|
|
strongestValue = val;
|
|
strongestProperty = keys[i];
|
|
}
|
|
}
|
|
|
|
//for (int32 i = 0; i < AllPotions.Num(); i++)
|
|
//{
|
|
// if (AllPotions[i]->Properties.Num() > 0 &&
|
|
// AllPotions[i]->Properties[0].Property == strongestProperty && AllPotions[i]->Effectiveness <= strongestValue &&
|
|
// DoChemicalAlignmentsMatch(itemChemicalAlignments, AllPotions[i]->ChemicalAlignments)) {
|
|
// potentialPotions.Add(AllPotions[i]);
|
|
// }
|
|
//}
|
|
|
|
|
|
int32 strongestPotionIndex = 0;
|
|
if (potentialPotions.Num() > 0) {
|
|
for (int32 i = 0; i < potentialPotions.Num(); i++)
|
|
{
|
|
if (potentialPotions[i]->Effectiveness >= potentialPotions[strongestPotionIndex]->Effectiveness)
|
|
strongestPotionIndex = i;
|
|
}
|
|
}
|
|
|
|
if (IsValid(potentialPotions[strongestPotionIndex])) {
|
|
Found = true;
|
|
return potentialPotions[strongestPotionIndex];
|
|
}
|
|
|
|
Found = false;
|
|
return nullptr;
|
|
}
|
|
|
|
UItemDataAsset* UItemDatabase::GetPotionDataFromItems(TArray<UItemDataAsset*> Items)
|
|
{
|
|
for (int32 i = 0; i < Items.Num(); i++)
|
|
{
|
|
if (!Items[i])
|
|
{
|
|
Items.RemoveAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
TArray<int32> itemChemicalAlignments;
|
|
TMap<ItemProperty, int32> potionPropertyMap;
|
|
int32 checkCount;
|
|
|
|
for (int32 i = 0; i < Items.Num(); i++)
|
|
{
|
|
if (Items[i])
|
|
{
|
|
for (int32 j = 0; j < Items[i]->ChemicalAlignments.Num(); j++)
|
|
{
|
|
itemChemicalAlignments.AddUnique(Items[i]->ChemicalAlignments[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (int32 i = 0; i < LoadedItems.Num(); i++)
|
|
{
|
|
if (!IsValid(LoadedItems[i])) continue;
|
|
|
|
checkCount = 0;
|
|
for (int32 j = 0; j < Items.Num(); j++)
|
|
{
|
|
for (int32 k = 0; k < LoadedItems[i]->RecipeItemsID.Num(); k++)
|
|
{
|
|
if (LoadedItems[i]->RecipeItemsID[k] == Items[j]->GetPrimaryAssetId())
|
|
{
|
|
checkCount++;
|
|
}
|
|
}
|
|
|
|
if (LoadedItems[i]->RecipeItemsID.Num() == Items.Num() && Items.Num() == checkCount)
|
|
{
|
|
return LoadedItems[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int32 i = 0; i < Items.Num(); i++)
|
|
{
|
|
for (int32 j = 0; j < Items[i]->Properties.Num(); j++)
|
|
{
|
|
if (potionPropertyMap.Find(Items[i]->Properties[j].Property) == nullptr)
|
|
potionPropertyMap.Add(Items[i]->Properties[j].Property, 1);
|
|
else
|
|
potionPropertyMap.Add(Items[i]->Properties[j].Property, *potionPropertyMap.Find(Items[i]->Properties[j].Property) + 1);
|
|
}
|
|
}
|
|
|
|
ItemProperty strongestProperty = ItemProperty::Heal;
|
|
int32 strongestValue = 0;
|
|
TArray<int32> potentialPotions;
|
|
|
|
TArray<ItemProperty> keys;
|
|
potionPropertyMap.GetKeys(keys);
|
|
|
|
for (int32 i = 0; i < keys.Num(); i++)
|
|
{
|
|
int32 val = *potionPropertyMap.Find(keys[i]);
|
|
if (val > strongestValue)
|
|
{
|
|
strongestValue = val;
|
|
strongestProperty = keys[i];
|
|
}
|
|
}
|
|
|
|
TArray<UItemDataAsset*> Potions = LoadedItemsByCategory[Category::Potion].ItemArray;
|
|
|
|
for (int32 i = 0; i < Potions.Num(); i++)
|
|
{
|
|
if (Potions[i]->Properties.Num() > 0 &&
|
|
Potions[i]->Properties[0].Property == strongestProperty && Potions[i]->Effectiveness <= strongestValue &&
|
|
DoChemicalAlignmentsMatch(itemChemicalAlignments, Potions[i]->ChemicalAlignments))
|
|
{
|
|
potentialPotions.Add(i);
|
|
}
|
|
}
|
|
|
|
|
|
int32 strongestPotionIndex = 0;
|
|
if (potentialPotions.Num() > 0)
|
|
{
|
|
for (int32 i = 0; i < potentialPotions.Num(); i++)
|
|
{
|
|
if (Potions[potentialPotions[i]]->Effectiveness >= Potions[potentialPotions[strongestPotionIndex]]->Effectiveness)
|
|
strongestPotionIndex = i;
|
|
}
|
|
}
|
|
|
|
if (potentialPotions.Num() > strongestPotionIndex && IsValid(Potions[potentialPotions[strongestPotionIndex]]))
|
|
{
|
|
return Potions[strongestPotionIndex];
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TArray<UItemDataAsset*> UItemDatabase::GetPotionsMatchingChemicalAlignment(UItemDataAsset* Ingredient)
|
|
{
|
|
TArray<UItemDataAsset*> PotionList;
|
|
if (!Ingredient) return PotionList;
|
|
|
|
const TArray<UItemDataAsset*> &AllPotionArray = LoadedItemsByCategory[Category::Potion].ItemArray;
|
|
for (int32 i = 0; i < AllPotionArray.Num(); i++)
|
|
{
|
|
if (AllPotionArray[i]->ChemicalAlignment == Ingredient->ChemicalAlignment)
|
|
{
|
|
PotionList.Add(AllPotionArray[i]);
|
|
}
|
|
}
|
|
|
|
return PotionList;
|
|
}
|
|
|
|
TArray<UItemDataAsset*> UItemDatabase::GetPotionsMatchingProperties(const TArray<UItemDataAsset*>& PotionList, TMap<ItemProperty, int32> PropertyList)
|
|
{
|
|
TArray<UItemDataAsset*> SortedPotionList;
|
|
int32 StrongestPropertyIndex = 0;
|
|
ItemProperty StrongestProperty = ItemProperty::Heal;
|
|
|
|
TArray<UItemDataAsset*> AllPotionsOfCategory = LoadedItemsByCategory[Category::Potion].ItemArray;
|
|
|
|
for (auto& PropertySet : PropertyList)
|
|
{
|
|
if (PropertySet.Value >= StrongestPropertyIndex)
|
|
{
|
|
StrongestPropertyIndex = PropertySet.Value;
|
|
StrongestProperty = PropertySet.Key;
|
|
}
|
|
}
|
|
|
|
for (int32 i = 0; i < AllPotionsOfCategory.Num(); i++)
|
|
{
|
|
int32 PropStrength = AllPotionsOfCategory[i]->GetPropertyStrength(StrongestProperty);
|
|
if (PropStrength > -1 && StrongestPropertyIndex >= PropStrength)
|
|
{
|
|
bool bPropertyNotInList = false;
|
|
for (int32 j = 0; j < AllPotionsOfCategory[i]->Properties.Num(); j++)
|
|
{
|
|
if (PropertyList[AllPotionsOfCategory[i]->Properties[j].Property] < AllPotionsOfCategory[i]->Properties[j].Strength)
|
|
{
|
|
bPropertyNotInList = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!bPropertyNotInList)
|
|
SortedPotionList.Add(AllPotionsOfCategory[i]);
|
|
}
|
|
}
|
|
|
|
return SortedPotionList;
|
|
}
|
|
|
|
|
|
UItemDataAsset* UItemDatabase::GetDataAssetFromId(FPrimaryAssetId AssetId)
|
|
{
|
|
FAssetData AssetData;
|
|
UAssetManager::Get().GetPrimaryAssetData(AssetId, AssetData);
|
|
return Cast<UItemDataAsset>(AssetData.GetAsset());
|
|
}
|
|
|
|
TArray<UItemDataAsset*> UItemDatabase::GetDataAssetsFromIds(TArray<FPrimaryAssetId> AssetIds)
|
|
{
|
|
TArray<UItemDataAsset*> AssetList;
|
|
for (int32 i = 0; i < AssetIds.Num(); i++)
|
|
{
|
|
UItemDataAsset* FoundAsset = GetDataAssetFromId(AssetIds[i]);
|
|
if (IsValid(FoundAsset))
|
|
AssetList.Add(FoundAsset);
|
|
}
|
|
|
|
return AssetList;
|
|
}
|
|
|
|
float UItemDatabase::CalculatePotionPurity(TMap<ItemProperty, int32> PropertyList, TArray<FPropertyStrength> MainProperties)
|
|
{
|
|
int32 OtherPropertiesStrength = 0;
|
|
int32 MainPropertyValue = 0;
|
|
for (auto& Prop : MainProperties)
|
|
{
|
|
MainPropertyValue += *PropertyList.Find(Prop.Property);
|
|
}
|
|
|
|
for (auto& PropertySet : PropertyList)
|
|
{
|
|
bool found = false;
|
|
for (auto& Prop : MainProperties)
|
|
{
|
|
if (Prop.Property == PropertySet.Key)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
OtherPropertiesStrength += PropertySet.Value;
|
|
}
|
|
}
|
|
|
|
return (float)MainPropertyValue / ((float)OtherPropertiesStrength + (float)MainPropertyValue);
|
|
}
|
|
|
|
int32 UItemDatabase::GetCrystalCostForPotion(UItemDataAsset* Potion)
|
|
{
|
|
if (!Potion) return -1;
|
|
|
|
return (int32)FMath::Pow(2.0f, (float)(Potion->ProfessionLevelNeeded + 1));
|
|
}
|
|
|
|
int32 UItemDatabase::GetCrystalCostForItems(TArray<UItemDataAsset*> Items)
|
|
{
|
|
int32 FinalCount = 0;
|
|
for (int32 i = 0; i < Items.Num(); i++)
|
|
{
|
|
if (!IsValid(Items[i])) continue;
|
|
|
|
for (int32 j = 0; j < Items[i]->Properties.Num(); j++)
|
|
{
|
|
FinalCount += Items[i]->Properties[j].Strength;
|
|
}
|
|
}
|
|
return FinalCount;
|
|
}
|
|
|
|
Element UItemDatabase::GetStrongestElement(const TArray<UItemDataAsset*>& PotionList)
|
|
{
|
|
if (IsValid(PotionList[0]))
|
|
{
|
|
return PotionList[0]->ItemElement;
|
|
}
|
|
|
|
TArray<int32> Strengths = {0, 0, 0, 0, 0, 0};
|
|
|
|
for (int32 i = 0; i < PotionList.Num(); i++)
|
|
{
|
|
if (!PotionList[i] || !IsValid(PotionList[i])) continue;
|
|
|
|
Strengths[(int32)PotionList[i]->ItemElement] += ElementStrength[PotionList[i]->ItemElement];
|
|
}
|
|
|
|
int32 StrongestIndex = 0;
|
|
int32 Strongest = Strengths[StrongestIndex];
|
|
|
|
for (int32 i = 0; i < Strengths.Num(); i++)
|
|
{
|
|
if (Strengths[i] >= Strongest)
|
|
{
|
|
Strongest = Strengths[i];
|
|
StrongestIndex = i;
|
|
}
|
|
}
|
|
|
|
return (Element)StrongestIndex;
|
|
}
|
|
|
|
void UItemDatabase::SpawnDecorationItem(UItemDataAsset* ItemAsset)
|
|
{
|
|
if (!ItemAsset || ItemAsset->ItemMesh.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ItemAsset->ItemMesh.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateUObject(this, &UItemDatabase::OnDecorationItemLoaded, ItemAsset));
|
|
}
|
|
|
|
void UItemDatabase::RemoveDecorationItem(ARemovableStaticMeshActor* DecorationItem)
|
|
{
|
|
if (DecorationItem->ItemDataAsset && DecorationItem->bFromInventory)
|
|
{
|
|
UInventoryComponent* InventoryComponent = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld());
|
|
InventoryComponent->AddItemToInventory(DecorationItem->ItemDataAsset->GetPrimaryAssetId(), 1);
|
|
}
|
|
|
|
DecorationItem->Destroy();
|
|
}
|
|
|
|
void UItemDatabase::OnDecorationItemLoaded(const FSoftObjectPath& Path, UObject* Object, UItemDataAsset* ItemAsset)
|
|
{
|
|
UStaticMesh* StaticMesh = Cast<UStaticMesh>(Object);
|
|
if (!StaticMesh || !ItemAsset) return;
|
|
|
|
ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
|
|
const FVector Location = PlayerCharacter->GetActorLocation() + (PlayerCharacter->GetActorForwardVector() * 1000.f);
|
|
FActorSpawnParameters SpawnParams;
|
|
SpawnParams.Instigator = PlayerCharacter;
|
|
SpawnParams.Owner = PlayerCharacter;
|
|
ARemovableStaticMeshActor* SpawnedActor = GetWorld()->SpawnActor<ARemovableStaticMeshActor>(RemovableStaticMeshClass, Location, FRotator::ZeroRotator, SpawnParams);
|
|
if (!SpawnedActor) return;
|
|
SpawnedActor->SetMobility(EComponentMobility::Movable);
|
|
SpawnedActor->Initialize(StaticMesh, ItemAsset);
|
|
|
|
UInventoryComponent* InventoryComponent = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld());
|
|
SpawnedActor->BindToItem(InventoryComponent);
|
|
InventoryComponent->RemoveItemFromInventory(ItemAsset->GetPrimaryAssetId(), 1);
|
|
|
|
UMoveActorsComponent* MoveActorsComponent = PlayerCharacter->GetComponentByClass<UMoveActorsComponent>();
|
|
if (!MoveActorsComponent) return;
|
|
|
|
MoveActorsComponent->StartObjectRemoval(true);
|
|
MoveActorsComponent->PickUpObjectForPlacing(SpawnedActor);
|
|
}
|
|
|
|
void FArrayOfItemRefs::SortByName()
|
|
{
|
|
ItemArray.Sort();
|
|
}
|
|
|
|
void UItemDatabase::PrepareItemPriceData() {
|
|
ItemPriceDataList.Empty();
|
|
|
|
for (auto LoadedItem : LoadedItems) {
|
|
FItemPriceData PriceData;
|
|
PriceData.ItemId = LoadedItem->GetPrimaryAssetId();
|
|
PriceData.PriceHistory.Empty();
|
|
PriceData.PriceHistory.Add(LoadedItem->BaseCost);
|
|
for (int32 i = 1; i < 30; i++) {
|
|
bool bIncrease = FMath::RandBool();
|
|
if (PriceData.PriceHistory[i - 1] < LoadedItem->BaseCost * 0.5)
|
|
bIncrease = true;
|
|
int32 NewPriceChange = (LoadedItem->BaseCost * FMath::RandRange(0.f, 0.5f)) * (bIncrease? 1 : -1);
|
|
PriceData.PriceHistory.Add(PriceData.PriceHistory[i - 1] + NewPriceChange);
|
|
}
|
|
PriceData.CurrentPrice = PriceData.PriceHistory[29];
|
|
ItemPriceDataList.Add(PriceData);
|
|
}
|
|
}
|
|
|
|
int32 UItemDatabase::GetCurrentPriceForItem(FPrimaryAssetId ItemId) {
|
|
for (auto PriceData : ItemPriceDataList) {
|
|
if (PriceData.ItemId == ItemId) {
|
|
return PriceData.CurrentPrice;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TArray<int32> UItemDatabase::GetPriceHistoryForItem(FPrimaryAssetId ItemId) {
|
|
for (auto PriceData : ItemPriceDataList) {
|
|
if (PriceData.ItemId == ItemId) {
|
|
return PriceData.PriceHistory;
|
|
}
|
|
}
|
|
return TArray<int32>();
|
|
}
|