Compare commits
2 Commits
861657d7ce
...
f9384cc3a0
| Author | SHA1 | Date | |
|---|---|---|---|
| f9384cc3a0 | |||
| 4f029309d5 |
@@ -193,6 +193,8 @@ CameraZoomLevels=(((TagName="Camera.BoomOffset.Default"), (X=-1200.000000,Y=0.00
|
||||
+ProfessionExpArray=240000.000000
|
||||
ExpIncreaseEffectClass=/Script/Engine.BlueprintGeneratedClass'/Game/Blueprints/GameEffects/GSE_AddExp_Template.GSE_AddExp_Template_C'
|
||||
GlobalSettingsDataSheet=/Game/GameData/CSV/GlobalKeyValueSettings.GlobalKeyValueSettings
|
||||
ItemSeedCarouselClass=None
|
||||
RemovableStaticMeshClass=/Script/Engine.BlueprintGeneratedClass'/Game/Prefabs/BP_RemovableStaticMeshActor.BP_RemovableStaticMeshActor_C'
|
||||
|
||||
[/Script/InteractionSystem.InteractionSettings]
|
||||
DefaultInteractionWidgetClass=/Script/UMG.WidgetBlueprintGeneratedClass'/Game/UI/InteractionUI/WBP_Eleri_Interaction.WBP_Eleri_Interaction_C'
|
||||
|
||||
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.
Binary file not shown.
Binary file not shown.
BIN
Content/Input/IA_PlantSeedAction.uasset
LFS
Normal file
BIN
Content/Input/IA_PlantSeedAction.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/Input/IMC_PlantingContext.uasset
LFS
Normal file
BIN
Content/Input/IMC_PlantingContext.uasset
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.
BIN
Content/Prefabs/Bush1.uasset
LFS
BIN
Content/Prefabs/Bush1.uasset
LFS
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Prefabs/Bush2.uasset
LFS
BIN
Content/Prefabs/Bush2.uasset
LFS
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Prefabs/Bush3.uasset
LFS
BIN
Content/Prefabs/Bush3.uasset
LFS
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Prefabs/Bush4.uasset
LFS
BIN
Content/Prefabs/Bush4.uasset
LFS
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
Content/Prefabs/Tree1.uasset
LFS
BIN
Content/Prefabs/Tree1.uasset
LFS
Binary file not shown.
BIN
Content/Prefabs/Tree2.uasset
LFS
BIN
Content/Prefabs/Tree2.uasset
LFS
Binary file not shown.
BIN
Content/Prefabs/Tree3.uasset
LFS
BIN
Content/Prefabs/Tree3.uasset
LFS
Binary file not shown.
BIN
Content/Prefabs/Tree4.uasset
LFS
BIN
Content/Prefabs/Tree4.uasset
LFS
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
Content/Scripts/InventorySystem/Seed/BP_SeedBedActor.uasset
LFS
Normal file
BIN
Content/Scripts/InventorySystem/Seed/BP_SeedBedActor.uasset
LFS
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Scripts/InventorySystem/Seed/MI_SeedBedDecal_Inst.uasset
LFS
Normal file
BIN
Content/Scripts/InventorySystem/Seed/MI_SeedBedDecal_Inst.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/Scripts/InventorySystem/Seed/MI_SeedBedMat.uasset
LFS
Normal file
BIN
Content/Scripts/InventorySystem/Seed/MI_SeedBedMat.uasset
LFS
Normal file
Binary file not shown.
Binary file not shown.
BIN
Content/Scripts/InventorySystem/Seed/M_SeedBedDecal.uasset
LFS
Normal file
BIN
Content/Scripts/InventorySystem/Seed/M_SeedBedDecal.uasset
LFS
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Textures/Vegetation/T_SeedBedMask.png
LFS
Normal file
BIN
Content/Textures/Vegetation/T_SeedBedMask.png
LFS
Normal file
Binary file not shown.
BIN
Content/Textures/Vegetation/T_SeedBedMask.uasset
LFS
Normal file
BIN
Content/Textures/Vegetation/T_SeedBedMask.uasset
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.
@@ -426,18 +426,6 @@ bool UMoveActorsComponent::PlaceableActionUsed(ERemovalAction ActionUsed)
|
||||
case ERemovalAction::PLACE:
|
||||
if (PlayerController && MovingActor->IsPlaceable())
|
||||
{
|
||||
FPrimaryAssetId ItemId = MovingActor->ItemStack.ItemId;
|
||||
if (!ItemId.IsValid())
|
||||
{
|
||||
ItemId = MovingActor->DataAssetId;
|
||||
}
|
||||
if (ItemId.IsValid())
|
||||
{
|
||||
if (UInventoryComponent* Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld()))
|
||||
{
|
||||
Inventory->RemoveItemFromInventory(ItemId, 1, false, false);
|
||||
}
|
||||
}
|
||||
PlaceObjectForPlacing();
|
||||
return true;
|
||||
}
|
||||
@@ -445,26 +433,16 @@ bool UMoveActorsComponent::PlaceableActionUsed(ERemovalAction ActionUsed)
|
||||
|
||||
case ERemovalAction::REMOVE:
|
||||
{
|
||||
// A world object is returned to the player's inventory before it is removed.
|
||||
if (MovingActor->DataAssetId.IsValid() && !MovingActor->bFromInventory)
|
||||
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<UObjectPersistenceSubsystem>())
|
||||
{
|
||||
if (UInventoryComponent* Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld()))
|
||||
{
|
||||
Inventory->AddItemToInventory(MovingActor->DataAssetId, 1, false, false);
|
||||
}
|
||||
ObjectPersistenceSubsystem->RemoveActorFromLevel(MovingActor);
|
||||
}
|
||||
|
||||
if (UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(GetWorld()))
|
||||
|
||||
if (UItemDatabase* ItemDatabase = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<UItemDatabase>())
|
||||
{
|
||||
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GameInstance->GetSubsystem<UObjectPersistenceSubsystem>())
|
||||
{
|
||||
ObjectPersistenceSubsystem->RemoveActorFromLevel(MovingActor);
|
||||
return true;
|
||||
}
|
||||
ItemDatabase->RemoveDecorationItem(MovingActor);
|
||||
}
|
||||
|
||||
// Fallback if the persistence subsystem is unavailable.
|
||||
MovingActor->Destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -473,9 +451,10 @@ bool UMoveActorsComponent::PlaceableActionUsed(ERemovalAction ActionUsed)
|
||||
// Cancel the current placement/move without committing it.
|
||||
if (MovingActor->bFromInventory)
|
||||
{
|
||||
// A preview spawned from the inventory: return the item and destroy the preview.
|
||||
MovingActor->ReturnToInventory();
|
||||
MovingActor->Destroy();
|
||||
if (UItemDatabase* ItemDatabase = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<UItemDatabase>())
|
||||
{
|
||||
ItemDatabase->RemoveDecorationItem(MovingActor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -519,7 +498,6 @@ bool UMoveActorsComponent::PlaceObjectForPlacing()
|
||||
}
|
||||
MovingActor->Place();
|
||||
|
||||
RemovalModePlacingStack--;
|
||||
return RemoveObjectForPlacing();
|
||||
}
|
||||
|
||||
@@ -527,22 +505,15 @@ bool UMoveActorsComponent::RemoveObjectForPlacing()
|
||||
{
|
||||
if (!PlayerController) return false;
|
||||
|
||||
// Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller.
|
||||
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
|
||||
{
|
||||
if (RemovalModePlacingStack <= 0)
|
||||
{
|
||||
Subsystem->RemoveMappingContext(PlayerController->RemoveModeContext);
|
||||
}
|
||||
}
|
||||
|
||||
MovingActor = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
|
||||
void UMoveActorsComponent::StartObjectRemoval(bool bStart)
|
||||
{
|
||||
if (bStart && bRemovalMode) return;
|
||||
|
||||
if (!PlayerCharacter) return;
|
||||
|
||||
if (!bStart)
|
||||
@@ -551,7 +522,6 @@ void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
|
||||
}
|
||||
PlayerCharacter->CurrentlyDoing = bStart ? ECurrentlyDoing::OBJECT_PLACING : ECurrentlyDoing::NOTHING;
|
||||
|
||||
RemovalModePlacingStack = Stack;
|
||||
if (bStart && !bRemovalModePossible) return;
|
||||
|
||||
if (!bStart)
|
||||
@@ -566,9 +536,6 @@ void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
|
||||
bRemovalMode = bStart;
|
||||
OnRemovalModeToggled.Broadcast(bRemovalMode);
|
||||
|
||||
PlayerCharacter->SetNewCameraPitches(
|
||||
bStart ? PlayerCharacter->CameraPitchMin - 50 : PlayerCharacter->CameraPitchMin,
|
||||
bStart ? PlayerCharacter->CameraPitchMax + 5 : PlayerCharacter->CameraPitchMax);
|
||||
|
||||
if (PlayerController && PlayerController->GetMainGameWidget())
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "ProjectEleri/GameObjects/RemovableStaticMeshActor.h"
|
||||
|
||||
#include "MoveActorsComponent.generated.h"
|
||||
|
||||
@@ -53,9 +54,6 @@ public:
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
ARemovableStaticMeshActor* MovingActor;
|
||||
|
||||
UPROPERTY()
|
||||
int32 RemovalModePlacingStack;
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<TSubclassOf<AActor>> ActorsToIgnore;
|
||||
@@ -135,9 +133,9 @@ protected:
|
||||
ARemovableStaticMeshActor* HighligtedMovingActor;
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
bool bRemovalModePossible;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
bool bRemovalMode;
|
||||
UPROPERTY()
|
||||
bool bGridMovementActive;
|
||||
@@ -188,6 +186,8 @@ public:
|
||||
bool IsRemovingModePossible() const { return bRemovalModePossible; }
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
bool IsRemovingMode() const { return bRemovalMode; }
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
bool IsPlacingObject() const { return IsValid(MovingActor); }
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
bool RemoveObjectForPlacing();
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void StartObjectRemoval(bool bStart = true, int32 Stack = 9999);
|
||||
void StartObjectRemoval(bool bStart = true);
|
||||
UFUNCTION(BlueprintCallable)
|
||||
bool HighlightRemovableObject(ARemovableStaticMeshActor* Placable);
|
||||
UFUNCTION(BlueprintCallable)
|
||||
|
||||
@@ -98,10 +98,17 @@ bool ARemovableStaticMeshActor::IsInteractable_Implementation() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void ARemovableStaticMeshActor::BindToItem(UInventoryComponent* InInventory, FItemStack InItemStack)
|
||||
void ARemovableStaticMeshActor::Initialize(UStaticMesh* StaticMesh, UItemDataAsset* InItemDataAsset)
|
||||
{
|
||||
GetStaticMeshComponent()->SetStaticMesh(StaticMesh);
|
||||
GetStaticMeshComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
MeshMaterials = GetStaticMeshComponent()->GetMaterials();
|
||||
ItemDataAsset = InItemDataAsset;
|
||||
DataAssetId = ItemDataAsset->GetPrimaryAssetId();
|
||||
}
|
||||
|
||||
void ARemovableStaticMeshActor::BindToItem(UInventoryComponent* InInventory)
|
||||
{
|
||||
InventoryRef = InInventory;
|
||||
ItemStack = InItemStack;
|
||||
bFromInventory = true;
|
||||
}
|
||||
|
||||
@@ -126,8 +133,6 @@ void ARemovableStaticMeshActor::Place(bool ReturnToOldLocation, bool bForce)
|
||||
if (bPlaced)
|
||||
{
|
||||
//Reset this because we've placed the item in the world
|
||||
InventoryRef = nullptr;
|
||||
ItemStack = FItemStack();
|
||||
bFromInventory = false;
|
||||
|
||||
if (ReturnToOldLocation)
|
||||
@@ -147,6 +152,7 @@ void ARemovableStaticMeshActor::Place(bool ReturnToOldLocation, bool bForce)
|
||||
}
|
||||
}
|
||||
CurrentMemorizedTransform = GetActorTransform();
|
||||
GetStaticMeshComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||||
OnPlaced.Broadcast();
|
||||
}
|
||||
}
|
||||
@@ -192,10 +198,3 @@ void ARemovableStaticMeshActor::ReportGridFootprint()
|
||||
CellsX * Pitch - Size.X, CellsY * Pitch - Size.Y, FillScale);
|
||||
}
|
||||
|
||||
void ARemovableStaticMeshActor::ReturnToInventory()
|
||||
{
|
||||
if (InventoryRef)
|
||||
{
|
||||
InventoryRef->AddItemToInventory(ItemStack.ItemId, 1, false, ItemStack.HQ);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,14 +52,12 @@ public:
|
||||
|
||||
UPROPERTY()
|
||||
FTransform CurrentMemorizedTransform;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UInventoryComponent* InventoryRef;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FItemStack ItemStack;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
AActor* PlayerRef;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UItemDataAsset* ItemDataAsset;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bPlaced;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
@@ -115,8 +113,6 @@ public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
FORCEINLINE bool IsPlaceable() const { return bPlaceable; }
|
||||
UFUNCTION(BlueprintCallable, BLueprintPure)
|
||||
FORCEINLINE bool FromInventory() const { return InventoryRef != nullptr; }
|
||||
|
||||
UPROPERTY(BlueprintAssignable, EditAnywhere, BLueprintReadWrite)
|
||||
FOnPlaced OnPlaced;
|
||||
@@ -125,11 +121,13 @@ public:
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UInteractionWidgetComponent* WidgetComponent;
|
||||
|
||||
void Initialize(UStaticMesh* StaticMesh, UItemDataAsset* InItemDataAsset);
|
||||
|
||||
//Used when placing stuff from inventory so we can return it to inventory
|
||||
//if we cancel placing item
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void BindToItem(UInventoryComponent* InInventory, FItemStack InItemStack);
|
||||
void BindToItem(UInventoryComponent* InInventory);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void PickUp();
|
||||
@@ -140,9 +138,6 @@ public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void Rotate(bool bPositive);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void ReturnToInventory();
|
||||
|
||||
virtual bool CanPlace() { return true; }
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
|
||||
|
||||
@@ -65,7 +65,7 @@ class ARemovableStaticMeshActor;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
UCLASS(BlueprintType, PrioritizeCategories = ("Default", "Info", "Placeable", "Craftable", "Property Map", "Seed"))
|
||||
class PROJECTELERI_API UItemDataAsset : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
@@ -120,10 +120,16 @@ public:
|
||||
bool Stackable = true;
|
||||
|
||||
/** Mesh that the item / potion have, ignored for seeds */
|
||||
UPROPERTY()
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Placeable", meta=(EditCondition="ItemCategory==Category::Decoration", EditConditionHides))
|
||||
TSoftObjectPtr<UStaticMesh> ItemMesh;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Info", meta=(EditCondition="ItemCategory==Category::Decoration", EditConditionHides, MetaClass="/Script/ProjectEleri.RemovableStaticMeshActor"))
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Placeable", meta=(EditCondition="ItemCategory==Category::Decoration", EditConditionHides))
|
||||
int32 GridScale = 1;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Placeable", meta=(EditCondition="ItemCategory==Category::Decoration", EditConditionHides))
|
||||
FVector BoxExtentOverride;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Placeable", meta=(EditCondition="ItemCategory==Category::Decoration", EditConditionHides, MetaClass="/Script/ProjectEleri.RemovableStaticMeshActor"))
|
||||
FSoftClassPath DecorationActorClassPath;
|
||||
|
||||
|
||||
@@ -132,9 +138,6 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info", AssetRegistrySearchable, meta = (AssetBundles = "Inventory, Book"))
|
||||
int32 BaseCost;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Info", AssetRegistrySearchable, meta = (AssetBundles = "Inventory, Book"))
|
||||
TSoftObjectPtr<UTexture2D> PropertyMapTexture;
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/** What part of the world grants faster growth and higher chance to harvest an HQ ingredient */
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#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()) {
|
||||
@@ -52,6 +55,9 @@ void UItemDatabase::Initialize(FSubsystemCollectionBase& Collection) {
|
||||
else {
|
||||
UAssetManager::CallOrRegister_OnCompletedInitialScan(FStopRenderingThreadDelegate::CreateLambda(LoadDataLambda));
|
||||
}
|
||||
|
||||
RemovableStaticMeshClass = GetDefault<UEleriGameSettings>()->RemovableStaticMeshClass;
|
||||
ItemSeedCarouselClass = GetDefault<UEleriGameSettings>()->ItemSeedCarouselClass;
|
||||
}
|
||||
|
||||
|
||||
@@ -427,6 +433,53 @@ Element UItemDatabase::GetStrongestElement(const TArray<UItemDataAsset*>& Potion
|
||||
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();
|
||||
|
||||
@@ -58,67 +58,76 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Item Database")
|
||||
TSubclassOf<AActor> ItemSeedCarouselClass;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Item Database")
|
||||
TSubclassOf<ARemovableStaticMeshActor> RemovableStaticMeshClass;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item Database")
|
||||
TArray<FPrimaryAssetId> AllAssetIds;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item Database")
|
||||
TArray<UItemDataAsset*> LoadedItems;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item Database")
|
||||
TMap<Category, FArrayOfItemRefs> LoadedItemsByCategory;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item Database")
|
||||
TArray<ASeedCarousel*> SeedsPlanted;
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
void WaterAllSeeds();
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
bool DoChemicalAlignmentsMatch(TArray<int32> Alignments, TArray<int32> PotionAlignments);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
UItemDataAsset* GetPotionFromItems(TArray<UItemDataAsset*> Items, bool& Found);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
UItemDataAsset* GetPotionDataFromItems(TArray<UItemDataAsset*> Items);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
TArray<UItemDataAsset*> GetPotionsMatchingChemicalAlignment(UItemDataAsset* Ingredient);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
TArray<UItemDataAsset*> GetPotionsMatchingProperties(const TArray<UItemDataAsset*> &PotionList, TMap<ItemProperty, int32> PropertyList);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
UItemDataAsset* GetDataAssetFromId(FPrimaryAssetId AssetId);
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
TArray<UItemDataAsset*> GetDataAssetsFromIds(TArray<FPrimaryAssetId> AssetIds);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
float CalculatePotionPurity(TMap<ItemProperty, int32> PropertyList, TArray<FPropertyStrength> MainProperties);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
int32 GetCrystalCostForPotion(UItemDataAsset* Potion);
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
int32 GetCrystalCostForItems(TArray<UItemDataAsset*> Items);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
Element GetStrongestElement(const TArray<UItemDataAsset*>& PotionList);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Item Database")
|
||||
void SpawnDecorationItem(UItemDataAsset* ItemAsset);
|
||||
void RemoveDecorationItem(ARemovableStaticMeshActor* DecorationItem);
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item Database")
|
||||
TArray<FItemPriceData> ItemPriceDataList;
|
||||
|
||||
protected:
|
||||
void PrepareItemPriceData();
|
||||
|
||||
UFUNCTION()
|
||||
void OnDecorationItemLoaded(const FSoftObjectPath& Path, UObject* Object, UItemDataAsset* ItemAsset);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Item Database")
|
||||
int32 GetCurrentPriceForItem(FPrimaryAssetId ItemId);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure)
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Item Database")
|
||||
TArray<int32> GetPriceHistoryForItem(FPrimaryAssetId ItemId);
|
||||
};
|
||||
|
||||
552
Source/ProjectEleri/Items/SeedBedActor.cpp
Normal file
552
Source/ProjectEleri/Items/SeedBedActor.cpp
Normal file
@@ -0,0 +1,552 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "SeedBedActor.h"
|
||||
|
||||
#include "EleriGameState.h"
|
||||
#include "Components/DecalComponent.h"
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "DataAssets/ItemDataAsset.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/World.h"
|
||||
#include "ItemContainer.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Kismet/KismetMaterialLibrary.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
/** Destroys the components of a slot and clears the pointers so nothing stale is left in the map */
|
||||
void DestroySlotComponents(FSeedBedSeedSlot& Slot)
|
||||
{
|
||||
if (Slot.SeedBedDecal) Slot.SeedBedDecal->DestroyComponent();
|
||||
if (Slot.PlantStaticMesh) Slot.PlantStaticMesh->DestroyComponent();
|
||||
|
||||
Slot.SeedBedDecal = nullptr;
|
||||
Slot.PlantStaticMesh = nullptr;
|
||||
}
|
||||
|
||||
/** Slots are created at runtime as well, so the components are created with stable, deterministic names */
|
||||
UStaticMeshComponent* CreateSlotMeshComponent(AActor* Owner, USceneComponent* AttachParent, FName ComponentName)
|
||||
{
|
||||
if (!IsValid(Owner) || !IsValid(AttachParent))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UStaticMeshComponent* MeshComponent = NewObject<UStaticMeshComponent>(Owner, ComponentName);
|
||||
if (!MeshComponent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MeshComponent->SetMobility(EComponentMobility::Movable);
|
||||
MeshComponent->SetupAttachment(AttachParent);
|
||||
MeshComponent->RegisterComponent();
|
||||
Owner->AddInstanceComponent(MeshComponent);
|
||||
|
||||
return MeshComponent;
|
||||
}
|
||||
|
||||
UDecalComponent* CreateSlotDecalComponent(AActor* Owner, USceneComponent* AttachParent, FName ComponentName)
|
||||
{
|
||||
if (!IsValid(Owner) || !IsValid(AttachParent))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UDecalComponent* DecalComponent = NewObject<UDecalComponent>(Owner, ComponentName);
|
||||
if (!DecalComponent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DecalComponent->SetMobility(EComponentMobility::Movable);
|
||||
DecalComponent->SetupAttachment(AttachParent);
|
||||
DecalComponent->RegisterComponent();
|
||||
DecalComponent->SetVisibility(false);
|
||||
Owner->AddInstanceComponent(DecalComponent);
|
||||
|
||||
return DecalComponent;
|
||||
}
|
||||
|
||||
/** Soft references are filled in by hand, an unset mesh or material must never break the visuals */
|
||||
template <typename TObjectType>
|
||||
TObjectType* ResolveSoftObject(TSoftObjectPtr<TObjectType> SoftObject)
|
||||
{
|
||||
if (SoftObject.IsNull())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SoftObject.Get() ? SoftObject.Get() : SoftObject.LoadSynchronous();
|
||||
}
|
||||
|
||||
/** A slot counts as planted as soon as an asset or its id is stored, saves may only restore the id */
|
||||
bool SlotHasPlant(const FSeedBedSeedSlot& Slot)
|
||||
{
|
||||
return IsValid(Slot.SlotData.ItemDataAsset) || Slot.SlotData.ItemAssetID.IsValid();
|
||||
}
|
||||
|
||||
/** Growth stages are set up by hand, so a zeroed scale should never make a plant invisible */
|
||||
FVector SanitizeScale(const FVector& Scale)
|
||||
{
|
||||
return FVector(
|
||||
FMath::IsNearlyZero(Scale.X) ? 1.f : Scale.X,
|
||||
FMath::IsNearlyZero(Scale.Y) ? 1.f : Scale.Y,
|
||||
FMath::IsNearlyZero(Scale.Z) ? 1.f : Scale.Z);
|
||||
}
|
||||
}
|
||||
|
||||
ASeedBedActor::ASeedBedActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
|
||||
Root->SetMobility(EComponentMobility::Movable);
|
||||
SetRootComponent(Root);
|
||||
}
|
||||
|
||||
void ASeedBedActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
HighlightDecal = NewObject<UDecalComponent>(this, TEXT("Highlight Decal Component"));
|
||||
if (HighlightDecal)
|
||||
{
|
||||
HighlightDecal->SetMobility(EComponentMobility::Movable);
|
||||
HighlightDecal->SetupAttachment(RootComponent);
|
||||
HighlightDecal->RegisterComponent();
|
||||
HighlightDecal->SetVisibility(false);
|
||||
HighlightDecal->SetRelativeRotation(HighlightDecalRotation);
|
||||
AddInstanceComponent(HighlightDecal);
|
||||
HighlightDecal->SetDecalMaterial(ResolveSoftObject(HighlightDecalMaterial));
|
||||
}
|
||||
|
||||
ClearSeedBedGrid();
|
||||
GenerateSeedBedGrid();
|
||||
|
||||
AEleriGameState* GameState = Cast<AEleriGameState>(UGameplayStatics::GetGameState(this));
|
||||
ensure(GameState);
|
||||
|
||||
FSeedBedSaveData SaveData;
|
||||
if (GameState->GetSeedData(SeedBedIndex, SaveData))
|
||||
{
|
||||
PropagateSeedSaveData(SaveData);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameState->CacheSeedData(SeedBedIndex, PrepareSeedSaveData());
|
||||
}
|
||||
|
||||
// The bed can be built in the editor, but slots added through blueprints need their visuals as well
|
||||
RefreshAllSlotVisuals();
|
||||
}
|
||||
|
||||
void ASeedBedActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
AEleriGameState* GameState = Cast<AEleriGameState>(UGameplayStatics::GetGameState(this));
|
||||
ensure(GameState);
|
||||
GameState->CacheSeedData(SeedBedIndex, PrepareSeedSaveData());
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void ASeedBedActor::OnConstruction(const FTransform& Transform)
|
||||
{
|
||||
Super::OnConstruction(Transform);
|
||||
|
||||
RefreshAllSlotVisuals();
|
||||
}
|
||||
|
||||
void ASeedBedActor::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// GRID GENERATION
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
void ASeedBedActor::GenerateSeedBedGrid()
|
||||
{
|
||||
GenerateSeedBedGridSized(GridSize.X, GridSize.Y, true);
|
||||
}
|
||||
|
||||
void ASeedBedActor::GenerateSeedBedGridSized(int32 Columns, int32 Rows, bool bRebuildExistingSlots)
|
||||
{
|
||||
const int32 SafeColumns = FMath::Max(1, Columns);
|
||||
const int32 SafeRows = FMath::Max(1, Rows);
|
||||
|
||||
GridSize = FIntVector2(SafeColumns, SafeRows);
|
||||
|
||||
if (bRebuildExistingSlots)
|
||||
{
|
||||
// Slots falling outside of the new grid are dropped, the rest is rebuilt in place
|
||||
TArray<FIntVector2> CoordinatesToRemove;
|
||||
for (const TPair<FIntVector2, FSeedBedSeedSlot>& SlotPair : SeedBedSeedSlotsMap)
|
||||
{
|
||||
if (SlotPair.Key.X < 0 || SlotPair.Key.Y < 0 || SlotPair.Key.X >= SafeColumns || SlotPair.Key.Y >= SafeRows)
|
||||
{
|
||||
CoordinatesToRemove.Add(SlotPair.Key);
|
||||
}
|
||||
}
|
||||
|
||||
for (const FIntVector2& Coordinate : CoordinatesToRemove)
|
||||
{
|
||||
if (FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(Coordinate))
|
||||
{
|
||||
DestroySlotComponents(*Slot);
|
||||
}
|
||||
|
||||
SeedBedSeedSlotsMap.Remove(Coordinate);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 Column = 0; Column < SafeColumns; ++Column)
|
||||
{
|
||||
for (int32 Row = 0; Row < SafeRows; ++Row)
|
||||
{
|
||||
const FIntVector2 Coordinate(Column, Row);
|
||||
if (bRebuildExistingSlots || !SeedBedSeedSlotsMap.Contains(Coordinate))
|
||||
{
|
||||
CreateSlot(Coordinate, bRebuildExistingSlots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slot visuals also refresh the highlight, so the decals follow the new grid
|
||||
RefreshAllSlotVisuals();
|
||||
}
|
||||
|
||||
void ASeedBedActor::ClearSeedBedGrid()
|
||||
{
|
||||
for (TPair<FIntVector2, FSeedBedSeedSlot>& SlotPair : SeedBedSeedSlotsMap)
|
||||
{
|
||||
DestroySlotComponents(SlotPair.Value);
|
||||
}
|
||||
|
||||
SeedBedSeedSlotsMap.Empty();
|
||||
bPlantingHighlightActive = false;
|
||||
}
|
||||
|
||||
FName ASeedBedActor::GetSlotComponentName(FIntVector2 SlotCoordinate, const TCHAR* Prefix)
|
||||
{
|
||||
return FName(*FString::Printf(TEXT("%s_%d_%d"), Prefix ? Prefix : TEXT("Slot"), SlotCoordinate.X, SlotCoordinate.Y));
|
||||
}
|
||||
|
||||
FVector ASeedBedActor::GetSlotRelativeLocation(FIntVector2 SlotCoordinate) const
|
||||
{
|
||||
// The grid is centered on the actor, so the coordinates are shifted by half a cell
|
||||
if (const FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate))
|
||||
{
|
||||
return Slot->SeedBedDecal->GetRelativeLocation();
|
||||
}
|
||||
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
FSeedBedSaveData ASeedBedActor::PrepareSeedSaveData()
|
||||
{
|
||||
FSeedBedSaveData SaveData;
|
||||
SaveData.Index = SeedBedIndex;
|
||||
for (const auto& Slot : SeedBedSeedSlotsMap)
|
||||
{
|
||||
SaveData.SlotData.Add(Slot.Key, Slot.Value.SlotData);
|
||||
}
|
||||
|
||||
return SaveData;
|
||||
}
|
||||
|
||||
void ASeedBedActor::PropagateSeedSaveData(const FSeedBedSaveData& SeedSaveData)
|
||||
{
|
||||
UItemDatabase* ItemDatabase = UGameplayStatics::GetGameInstance(this)->GetSubsystem<UItemDatabase>();
|
||||
ensure(ItemDatabase);
|
||||
|
||||
SeedBedIndex = SeedSaveData.Index;
|
||||
for (const auto& SavedSlot : SeedSaveData.SlotData)
|
||||
{
|
||||
if (FSeedBedSeedSlot* SeedSlot = SeedBedSeedSlotsMap.Find(SavedSlot.Key))
|
||||
{
|
||||
SeedSlot->SlotData = SavedSlot.Value;
|
||||
|
||||
SeedSlot->SlotData.ItemDataAsset = ItemDatabase->GetDataAssetFromId(SeedSlot->SlotData.ItemAssetID);
|
||||
}
|
||||
}
|
||||
|
||||
RefreshAllSlotVisuals();
|
||||
}
|
||||
|
||||
FSeedBedSeedSlot& ASeedBedActor::CreateSlot(FIntVector2 SlotCoordinate, bool bRebuildIfExisting)
|
||||
{
|
||||
FSeedBedSeedSlot& Slot = SeedBedSeedSlotsMap.FindOrAdd(SlotCoordinate);
|
||||
|
||||
if (bRebuildIfExisting)
|
||||
{
|
||||
DestroySlotComponents(Slot);
|
||||
}
|
||||
|
||||
Slot.SeedBedDecal = CreateSlotDecalComponent(this, GetRootComponent(), GetSlotComponentName(SlotCoordinate, TEXT("Slot")));
|
||||
Slot.SeedBedDynamicMat = UKismetMaterialLibrary::CreateDynamicMaterialInstance(this, ResolveSoftObject(SeedBedDecalMaterial), NAME_None, EMIDCreationFlags::Transient);
|
||||
Slot.SeedBedDecal->SetDecalMaterial(Slot.SeedBedDynamicMat);
|
||||
Slot.SeedBedDecal->DecalSize = SeedBedDecalSize;
|
||||
Slot.SeedBedDecal->SetRelativeRotation(SeedBedDecalRotation);
|
||||
const FVector2D Offset = FVector2D(GridSize.X * SeedBedDecalSize.Z, GridSize.Y * SeedBedDecalSize.Y) * -1.f;
|
||||
const float X = SlotCoordinate.Y * SeedBedDecalSize.Z * 2.f;
|
||||
const float Y = SlotCoordinate.X * SeedBedDecalSize.Y * 2.f;
|
||||
Slot.SeedBedDecal->SetRelativeLocation(
|
||||
FVector(
|
||||
Offset.X + X + SeedBedDecalSize.Z,
|
||||
Offset.Y + Y + SeedBedDecalSize.Y,
|
||||
0.f));
|
||||
|
||||
ApplySlotVisuals(SlotCoordinate, Slot);
|
||||
|
||||
return Slot;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// VISUALS
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
void ASeedBedActor::ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlot& Slot)
|
||||
{
|
||||
const FVector SlotLocation = GetSlotRelativeLocation(SlotCoordinate);
|
||||
|
||||
// SOIL -----------------------------------------------------------------------------------------
|
||||
if (IsValid(Slot.SeedBedDecal))
|
||||
{
|
||||
Slot.SeedBedDecal->SetVisibility(Slot.SlotData.ItemAssetID.IsValid());
|
||||
Slot.SeedBedDynamicMat->SetScalarParameterValue(TEXT("Wetness"), Slot.SlotData.bWateredToday);
|
||||
}
|
||||
|
||||
// PLANT ----------------------------------------------------------------------------------------
|
||||
const bool bHasPlant = SlotHasPlant(Slot) && !Slot.SlotData.bDied;
|
||||
|
||||
if (IsValid(Slot.PlantStaticMesh))
|
||||
{
|
||||
UStaticMesh* PlantMesh = nullptr;
|
||||
FVector PlantOffset = FVector::ZeroVector;
|
||||
FVector PlantScale = FVector::OneVector;
|
||||
|
||||
if (bHasPlant)
|
||||
{
|
||||
const int32 StageIndex = ResolveGrowthStageIndex(Slot);
|
||||
const UItemDataAsset* SeedData = Slot.SlotData.ItemDataAsset;
|
||||
if (IsValid(SeedData) && SeedData->SeedStages.IsValidIndex(StageIndex))
|
||||
{
|
||||
const FSeedStage& Stage = SeedData->SeedStages[StageIndex];
|
||||
PlantMesh = ResolveSoftObject(Stage.Mesh);
|
||||
PlantOffset = Stage.MeshOffset;
|
||||
PlantScale = SanitizeScale(Stage.MeshScale);
|
||||
}
|
||||
|
||||
// A seed without stage meshes still has to show up, the fallback keeps the plant visible
|
||||
if (!PlantMesh)
|
||||
{
|
||||
PlantMesh = ResolveSoftObject(DefaultPlantMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void ASeedBedActor::RefreshSlotVisual(FIntVector2 SlotCoordinate)
|
||||
{
|
||||
FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
if (!Slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplySlotVisuals(SlotCoordinate, *Slot);
|
||||
|
||||
OnSeedBedSlotChanged.Broadcast(SlotCoordinate, SlotHasPlant(*Slot));
|
||||
}
|
||||
|
||||
void ASeedBedActor::RefreshAllSlotVisuals()
|
||||
{
|
||||
for (TPair<FIntVector2, FSeedBedSeedSlot>& SlotPair : SeedBedSeedSlotsMap)
|
||||
{
|
||||
ApplySlotVisuals(SlotPair.Key, SlotPair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
bool ASeedBedActor::IsPlantableSeedData(const UItemDataAsset* SeedData)
|
||||
{
|
||||
// Only seeds of plants make it into a bed, the meshes are optional and fall back to DefaultPlantMesh
|
||||
return IsValid(SeedData) && SeedData->IsSeed;
|
||||
}
|
||||
|
||||
void ASeedBedActor::HighlightSlot(FIntVector2 SlotCoordinate)
|
||||
{
|
||||
if (!IsSlotFree(SlotCoordinate)) return;
|
||||
|
||||
const FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
if (!Slot) return;
|
||||
|
||||
const FVector SlotLocation = GetSlotRelativeLocation(SlotCoordinate);
|
||||
|
||||
if (IsValid(HighlightDecal))
|
||||
{
|
||||
HighlightDecal->SetRelativeLocation(SlotLocation + FVector(0.f, 0.f, 0.f));
|
||||
HighlightDecal->DecalSize = HighlightDecalSize;
|
||||
HighlightDecal->SetVisibility(true);
|
||||
}
|
||||
|
||||
HighlightedSlotIndex = SlotCoordinate;
|
||||
}
|
||||
|
||||
void ASeedBedActor::StopHighlightingSlot()
|
||||
{
|
||||
HighlightDecal->SetVisibility(false);
|
||||
HighlightedSlotIndex = FIntVector2::NoneValue;
|
||||
}
|
||||
|
||||
bool ASeedBedActor::IsSlotFree(FIntVector2 SlotCoordinate) const
|
||||
{
|
||||
const FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
|
||||
return Slot != nullptr && !SlotHasPlant(*Slot);
|
||||
}
|
||||
|
||||
int32 ASeedBedActor::GetFreeSlotCount() const
|
||||
{
|
||||
int32 FreeSlotCount = 0;
|
||||
|
||||
for (const TPair<FIntVector2, FSeedBedSeedSlot>& SlotPair : SeedBedSeedSlotsMap)
|
||||
{
|
||||
if (!SlotHasPlant(SlotPair.Value))
|
||||
{
|
||||
++FreeSlotCount;
|
||||
}
|
||||
}
|
||||
|
||||
return FreeSlotCount;
|
||||
}
|
||||
bool ASeedBedActor::FindClosestFreeSlot(const FVector& WorldLocation, FIntVector2& OutSlotCoordinate) const
|
||||
{
|
||||
bool bFoundSlot = false;
|
||||
float ClosestDistanceSquared = 0.f;
|
||||
|
||||
for (const TPair<FIntVector2, FSeedBedSeedSlot>& SlotPair : SeedBedSeedSlotsMap)
|
||||
{
|
||||
if (SlotHasPlant(SlotPair.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FVector SlotWorldLocation;
|
||||
if (!GetSlotWorldLocation(SlotPair.Key, SlotWorldLocation))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const float DistanceSquared = FVector::DistSquared(WorldLocation, SlotWorldLocation);
|
||||
if (!bFoundSlot || DistanceSquared < ClosestDistanceSquared)
|
||||
{
|
||||
bFoundSlot = true;
|
||||
ClosestDistanceSquared = DistanceSquared;
|
||||
OutSlotCoordinate = SlotPair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
return bFoundSlot;
|
||||
}
|
||||
|
||||
bool ASeedBedActor::GetSlotWorldLocation(FIntVector2 SlotCoordinate, FVector& OutWorldLocation) const
|
||||
{
|
||||
if (!SeedBedSeedSlotsMap.Contains(SlotCoordinate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The location is derived from the grid and the actor transform, so it is valid without any component
|
||||
OutWorldLocation = GetActorTransform().TransformPosition(GetSlotRelativeLocation(SlotCoordinate));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 ASeedBedActor::ResolveGrowthStageIndex(const FSeedBedSeedSlot& Slot) const
|
||||
{
|
||||
const UItemDataAsset* SeedData = Slot.SlotData.ItemDataAsset;
|
||||
if (!IsValid(SeedData))
|
||||
{
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
// Stages are set up ordered by ChangeOnDay, so the last day that has been reached is the visible one
|
||||
int32 StageIndex = 0;
|
||||
|
||||
for (int32 Index = 0; Index < SeedData->SeedStages.Num(); ++Index)
|
||||
{
|
||||
if (Slot.SlotData.SeedAge < SeedData->SeedStages[Index].ChangeOnDay)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
StageIndex = Index;
|
||||
}
|
||||
|
||||
return StageIndex;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// PLANTING
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
bool ASeedBedActor::PlantSeedInSlot(FIntVector2 SlotCoordinate, UItemDataAsset* SeedData, bool bHighQuality)
|
||||
{
|
||||
if (!IsSlotFree(SlotCoordinate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
if (!Slot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// A fresh plant starts at day zero, everything that is stored about it is the state of the new seed
|
||||
Slot->SlotData.ItemDataAsset = SeedData;
|
||||
Slot->SlotData.ItemAssetID = SeedData->GetPrimaryAssetId();
|
||||
Slot->SlotData.GrowTime = FMath::Max(0, SeedData->GrowTimeSelf);
|
||||
Slot->SlotData.SeedAge = 0;
|
||||
Slot->SlotData.WateredTimes = 0;
|
||||
Slot->SlotData.bBloomed = false;
|
||||
Slot->SlotData.bWateredToday = false;
|
||||
Slot->SlotData.bDied = false;
|
||||
Slot->SlotData.bHighQuality = bHighQuality;
|
||||
|
||||
Slot->SeedBedDynamicMat->SetScalarParameterValue(TEXT("Wetness"), 0.f);
|
||||
|
||||
RefreshSlotVisual(SlotCoordinate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ASeedBedActor::ClearSeedFromSlot(FIntVector2 SlotCoordinate, bool bRemovePlantMesh)
|
||||
{
|
||||
FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
if (!Slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Slot->SlotData.ItemDataAsset = nullptr;
|
||||
Slot->SlotData.ItemAssetID = FPrimaryAssetId();
|
||||
Slot->SlotData.GrowTime = 0;
|
||||
Slot->SlotData.SeedAge = 0;
|
||||
Slot->SlotData.WateredTimes = 0;
|
||||
Slot->SlotData.bBloomed = false;
|
||||
Slot->SlotData.bWateredToday = false;
|
||||
Slot->SlotData.bDied = false;
|
||||
Slot->SlotData.bHighQuality = false;
|
||||
|
||||
if (bRemovePlantMesh)
|
||||
{
|
||||
RefreshSlotVisual(SlotCoordinate);
|
||||
return;
|
||||
}
|
||||
|
||||
// The mesh is left alone on purpose, a harvested plant stays visible until the slot is cleared for real
|
||||
OnSeedBedSlotChanged.Broadcast(SlotCoordinate, false);
|
||||
}
|
||||
|
||||
247
Source/ProjectEleri/Items/SeedBedActor.h
Normal file
247
Source/ProjectEleri/Items/SeedBedActor.h
Normal file
@@ -0,0 +1,247 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "SeedBedActor.generated.h"
|
||||
|
||||
class ATimeOfDayManager;
|
||||
class UDecalComponent;
|
||||
class UMaterialInterface;
|
||||
class UStaticMesh;
|
||||
class UStaticMeshComponent;
|
||||
class UItemDataAsset;
|
||||
|
||||
/** Called every time a slot of the seed bed has been planted, cleared or visually refreshed */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSeedBedSlotChanged, FIntVector2, SlotCoordinate, bool, bHasPlant);
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSeedBedSeedSlotData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient)
|
||||
UItemDataAsset* ItemDataAsset;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FPrimaryAssetId ItemAssetID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 GrowTime = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 SeedAge = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 WateredTimes = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bBloomed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bWateredToday = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bDied = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bHighQuality = false;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSeedBedSeedSlot
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FSeedBedSeedSlotData SlotData;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UDecalComponent* SeedBedDecal = nullptr;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UMaterialInstanceDynamic* SeedBedDynamicMat = nullptr;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UStaticMeshComponent* PlantStaticMesh;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSeedBedSaveData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
int32 Index = 0;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
TMap<FIntVector2, FSeedBedSeedSlotData> SlotData;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class PROJECTELERI_API ASeedBedActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ASeedBedActor();
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// DATA
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** Every slot of the seed bed, keyed by its X / Y coordinate inside the grid */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TMap<FIntVector2, FSeedBedSeedSlot> SeedBedSeedSlotsMap;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnSeedBedSlotChanged OnSeedBedSlotChanged;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
int32 SeedBedIndex = 0;
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// SEED BED SETUP
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
TSoftObjectPtr<UMaterialInterface> SeedBedDecalMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
FVector SeedBedDecalSize = FVector(40.f, 40.f, 40.f);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
FRotator SeedBedDecalRotation = FRotator(-90.f, 0.f, 0.f);
|
||||
|
||||
/** Fallback mesh used when the planted seed has no growth stage set up */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
TSoftObjectPtr<UStaticMesh> DefaultPlantMesh;
|
||||
|
||||
/** Dimensions of the grid generated by GenerateSeedBedGrid, X = columns, Y = rows */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
FIntVector2 GridSize = FIntVector2(3, 3);
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// HIGHLIGHT DECAL
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Highlight")
|
||||
TSoftObjectPtr<UMaterialInterface> HighlightDecalMaterial;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Highlight")
|
||||
FVector HighlightDecalSize = FVector(40.f, 40.f, 40.f);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Highlight")
|
||||
FRotator HighlightDecalRotation = FRotator(-90.f, 0.f, 0.f);
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// GRID GENERATION
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** Generates the seed bed grid using the GridSize set up above (editor helper) */
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Seed Bed|Setup")
|
||||
void GenerateSeedBedGrid();
|
||||
|
||||
/** Generates / regenerates the seed bed grid with an explicit size (editor helper) */
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Seed Bed|Setup")
|
||||
void GenerateSeedBedGridSized(int32 Columns, int32 Rows, bool bRebuildExistingSlots);
|
||||
|
||||
/** Destroys every slot of the seed bed and clears the slot map (editor helper) */
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "Seed Bed|Setup")
|
||||
void ClearSeedBedGrid();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed")
|
||||
int32 GetSlotCount() const { return SeedBedSeedSlotsMap.Num(); }
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// PLANTING
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/** Plants a seed into the given slot, returns false when the slot is unknown or already taken */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed")
|
||||
bool PlantSeedInSlot(FIntVector2 SlotCoordinate, UItemDataAsset* SeedData, bool bHighQuality = false);
|
||||
|
||||
/** Empties a slot, optionally removing the plant mesh as well */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed")
|
||||
void ClearSeedFromSlot(FIntVector2 SlotCoordinate, bool bRemovePlantMesh = true);
|
||||
|
||||
/** Finds the free slot that is closest to the given world location */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed")
|
||||
bool FindClosestFreeSlot(const FVector& WorldLocation, FIntVector2& OutSlotCoordinate) const;
|
||||
|
||||
/** World location a plant of the given slot is placed at */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed")
|
||||
bool GetSlotWorldLocation(FIntVector2 SlotCoordinate, FVector& OutWorldLocation) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed")
|
||||
bool IsSlotFree(FIntVector2 SlotCoordinate) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed")
|
||||
int32 GetFreeSlotCount() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed")
|
||||
bool HasFreeSlot() const { return GetFreeSlotCount() > 0; }
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// VISUALS
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed|Highlight")
|
||||
bool IsHighlightActive() const { return bPlantingHighlightActive; }
|
||||
|
||||
/** Re-applies mesh, material and decal of a single slot */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed|Visuals")
|
||||
void RefreshSlotVisual(FIntVector2 SlotCoordinate);
|
||||
|
||||
/** Re-applies mesh, material and decal of every slot of the bed */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed|Visuals")
|
||||
void RefreshAllSlotVisuals();
|
||||
|
||||
/** True when the given asset can be planted in a seed bed at all */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Seed Bed")
|
||||
static bool IsPlantableSeedData(const UItemDataAsset* SeedData);
|
||||
|
||||
FIntVector2 GetHighlightedSlotIndex() const { return HighlightedSlotIndex; }
|
||||
void HighlightSlot(FIntVector2 SlotCoordinate);
|
||||
void StopHighlightingSlot();
|
||||
FSeedBedSaveData PrepareSeedSaveData();
|
||||
void PropagateSeedSaveData(const FSeedBedSaveData& SeedSaveData);
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void OnConstruction(const FTransform& Transform) override;
|
||||
|
||||
/** Creates (or rebuilds) the components of a single slot and adds it to the slot map */
|
||||
FSeedBedSeedSlot& CreateSlot(FIntVector2 SlotCoordinate, bool bRebuildIfExisting);
|
||||
|
||||
/** Re-applies material, plant mesh and highlight decal of a slot */
|
||||
void ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlot& Slot);
|
||||
|
||||
/** Resolves which growth stage mesh of the planted seed has to be shown */
|
||||
int32 ResolveGrowthStageIndex(const FSeedBedSeedSlot& Slot) const;
|
||||
|
||||
/** Name of a mesh / decal component of a slot, names have to stay stable for serialization */
|
||||
static FName GetSlotComponentName(FIntVector2 SlotCoordinate, const TCHAR* Prefix);
|
||||
|
||||
FVector GetSlotRelativeLocation(FIntVector2 SlotCoordinate) const;
|
||||
|
||||
|
||||
/** Whether the highlight decals of the bed are currently shown */
|
||||
bool bPlantingHighlightActive = false;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FIntVector2 HighlightedSlotIndex = FIntVector2::NoneValue;
|
||||
|
||||
/** Decal used to highlight this slot when the player is looking for a place to plant a seed */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UDecalComponent* HighlightDecal = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
ATimeOfDayManager* TimeOfDayManager = nullptr;
|
||||
|
||||
};
|
||||
|
||||
@@ -6,67 +6,71 @@
|
||||
#include "MyCharacter.h"
|
||||
#include "../System/Subsystem/ObjectPersistenceSubsystem.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "TimeOfDayManager.h"
|
||||
#include "Components/StatForgeComponent.h"
|
||||
#include "ProjectEleri/System/MainBlueprintFunctionLibrary.h"
|
||||
#include "ProjectEleri/GameObjects/EleriBaseActor.h"
|
||||
|
||||
AEleriGameState* AEleriGameState::Get(const UObject* WorldContext) {
|
||||
if (WorldContext && WorldContext->GetWorld() && WorldContext->GetWorld()->GetGameState()) {
|
||||
return Cast<AEleriGameState>(WorldContext->GetWorld()->GetGameState());
|
||||
}
|
||||
return nullptr;
|
||||
AEleriGameState* AEleriGameState::Get(const UObject* WorldContext)
|
||||
{
|
||||
if (WorldContext && WorldContext->GetWorld() && WorldContext->GetWorld()->GetGameState())
|
||||
{
|
||||
return Cast<AEleriGameState>(WorldContext->GetWorld()->GetGameState());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32 AEleriGameState::GetFriendshipAmount(const UObject* WorldContext, FGameplayTag NpcTag) {
|
||||
if (!Get(WorldContext)) return 0;
|
||||
int32 AEleriGameState::GetFriendshipAmount(const UObject* WorldContext, FGameplayTag NpcTag)
|
||||
{
|
||||
if (!Get(WorldContext)) return 0;
|
||||
|
||||
if (const int32* Value = Get(WorldContext)->FriendshipData.Find(NpcTag)) {
|
||||
return *Value;
|
||||
}
|
||||
return 0;
|
||||
if (const int32* Value = Get(WorldContext)->FriendshipData.Find(NpcTag))
|
||||
{
|
||||
return *Value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 AEleriGameState::ModifyFriendshipAmount(const UObject* WorldContext, FGameplayTag NpcTag, int32 Amount) {
|
||||
if (!Get(WorldContext)) return 0;
|
||||
int32 AEleriGameState::ModifyFriendshipAmount(const UObject* WorldContext, FGameplayTag NpcTag, int32 Amount)
|
||||
{
|
||||
if (!Get(WorldContext)) return 0;
|
||||
|
||||
if (int32* Value = Get(WorldContext)->FriendshipData.Find(NpcTag)) {
|
||||
*Value += Amount;
|
||||
return *Value;
|
||||
}
|
||||
else {
|
||||
Get(WorldContext)->FriendshipData.Add(NpcTag, Amount);
|
||||
return Amount;
|
||||
}
|
||||
if (int32* Value = Get(WorldContext)->FriendshipData.Find(NpcTag))
|
||||
{
|
||||
*Value += Amount;
|
||||
return *Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Get(WorldContext)->FriendshipData.Add(NpcTag, Amount);
|
||||
return Amount;
|
||||
}
|
||||
}
|
||||
|
||||
void AEleriGameState::SpawnAllSeeds() {
|
||||
void AEleriGameState::PropagateAllSeedData()
|
||||
{
|
||||
ItemDatabase = GetWorld()->GetGameInstance()->GetSubsystem<UItemDatabase>();
|
||||
if (!ItemDatabase) return;
|
||||
|
||||
CurrentSpawnIndex = 0;
|
||||
GetWorld()->GetTimerManager().SetTimer(WaitActorSpawnHandle, [this]() {
|
||||
int32 CurrentLoopIndex = 0;
|
||||
for (int32 i = 0; i < SaveData.PlantedSeedsList.Num(); i++) {
|
||||
if (i == this->CurrentSpawnIndex && CurrentLoopIndex < 10 && this->CurrentSpawnIndex < SaveData.PlantedSeedsList.Num()) {
|
||||
CurrentLoopIndex++;
|
||||
this->CurrentSpawnIndex++;
|
||||
const FTransform ObjectTransform = SaveData.PlantedSeedsList[i].ObjectTransform;
|
||||
if (ASeedCarousel* SpawnedSeed = Cast<ASeedCarousel>(UGameplayStatics::BeginDeferredActorSpawnFromClass(GetWorld(), SeedCarouselBpClass, ObjectTransform))) {
|
||||
SpawnedSeed->LoadDataFromSaveStruct(SaveData.PlantedSeedsList[i], ItemDatabase);
|
||||
UE_LOG(LogTemp, Verbose, TEXT("Spawning actor: %s"), *SpawnedSeed->GetName());
|
||||
UGameplayStatics::FinishSpawningActor(SpawnedSeed, ObjectTransform, ESpawnActorScaleMethod::OverrideRootScale);
|
||||
SpawnedSeed->OnUpdatePlantEvent();
|
||||
}
|
||||
TArray<AActor*> OutActors;
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASeedBedActor::StaticClass(), OutActors);
|
||||
for (AActor* Actor : OutActors)
|
||||
{
|
||||
if (ASeedBedActor* SeedActor = Cast<ASeedBedActor>(Actor))
|
||||
{
|
||||
if (FSeedBedSaveData* SeedSaveData = SaveData.SeedBedData.Find(SeedActor->SeedBedIndex))
|
||||
{
|
||||
SeedActor->PropagateSeedSaveData(*SeedSaveData);
|
||||
}
|
||||
else
|
||||
{
|
||||
CacheSeedData(SeedActor->SeedBedIndex, SeedActor->PrepareSeedSaveData());
|
||||
}
|
||||
}
|
||||
if (this->CurrentSpawnIndex == SaveData.PlantedSeedsList.Num() - 1) {
|
||||
GetWorld()->GetTimerManager().ClearTimer(this->WaitActorSpawnHandle);
|
||||
OnPropagateSaveDataComplete.Broadcast();
|
||||
}
|
||||
}, 0.1f, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AEleriGameState::PropagateSaveData(UEleriSaveGame* InSaveData) {
|
||||
void AEleriGameState::PropagateSaveData(UEleriSaveGame* InSaveData)
|
||||
{
|
||||
check(InSaveData);
|
||||
AMyCharacter* PlayerCharacter = Cast<AMyCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
|
||||
check(PlayerCharacter);
|
||||
@@ -77,61 +81,56 @@ void AEleriGameState::PropagateSaveData(UEleriSaveGame* InSaveData) {
|
||||
|
||||
// Load all inventories
|
||||
UGameplayStatics::GetAllActorsWithTag(GetWorld(), INVENTORY_TAG, OutActors);
|
||||
for (int32 i = 0; i < OutActors.Num(); i++) {
|
||||
if (UInventoryComponent* Inv = Cast<UInventoryComponent>(OutActors[i]->GetComponentByClass(UInventoryComponent::StaticClass()))) {
|
||||
for (int32 i = 0; i < OutActors.Num(); i++)
|
||||
{
|
||||
if (UInventoryComponent* Inv = Cast<UInventoryComponent>(
|
||||
OutActors[i]->GetComponentByClass(UInventoryComponent::StaticClass())))
|
||||
{
|
||||
Inv->LoadFromSave(SaveData.InventoryData[FName(OutActors[i]->GetName())]);
|
||||
}
|
||||
}
|
||||
|
||||
//Load placed objects
|
||||
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UObjectPersistenceSubsystem>()) {
|
||||
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<
|
||||
UObjectPersistenceSubsystem>())
|
||||
{
|
||||
ObjectPersistenceSubsystem->LoadSavedData(SaveData.PlacedActors);
|
||||
}
|
||||
|
||||
|
||||
//Seed delete and spawn should always go last
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASeedCarousel::StaticClass(), OutActors);
|
||||
ActorsToDestroy = OutActors;
|
||||
GetWorld()->GetTimerManager().SetTimer(WaitActorDestructionHandle, [this]() {
|
||||
for (int i = CurrentSpawnIndex; i <= 100; i++) {
|
||||
if (i < ActorsToDestroy.Num()) {
|
||||
UE_LOG(LogTemp, Verbose, TEXT("Destroying actor: %s"), *ActorsToDestroy[i]->GetName());
|
||||
ActorsToDestroy[i]->Destroy();
|
||||
CurrentSpawnIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentSpawnIndex >= ActorsToDestroy.Num()) {
|
||||
ActorsToDestroy.Empty();
|
||||
GetWorld()->GetTimerManager().ClearTimer(WaitActorDestructionHandle);
|
||||
SpawnAllSeeds();
|
||||
}
|
||||
}, 0.1f, true);
|
||||
|
||||
//Promote actor data
|
||||
for (FActorIterator It(GetWorld()); It; ++It) {
|
||||
for (FActorIterator It(GetWorld()); It; ++It)
|
||||
{
|
||||
AActor* Actor = *It;
|
||||
if (!Actor->IsValidLowLevel() || Actor->IsPendingKillPending() || !Cast<ISaveableObjectInterface>(Actor) || !Cast<IActorPersistentIdInterface>(Actor)) {
|
||||
if (!Actor->IsValidLowLevel() || Actor->IsPendingKillPending() || !Cast<ISaveableObjectInterface>(Actor) || !
|
||||
Cast<IActorPersistentIdInterface>(Actor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (auto ActorSaveData = SaveData.ActorSaveData.Find(Cast<IActorPersistentIdInterface>(Actor)->GetPersistentId())) {
|
||||
if (auto ActorSaveData = SaveData.ActorSaveData.Find(
|
||||
Cast<IActorPersistentIdInterface>(Actor)->GetPersistentId()))
|
||||
{
|
||||
Cast<ISaveableObjectInterface>(Actor)->RequestLoad(*ActorSaveData);
|
||||
}
|
||||
}
|
||||
|
||||
//Game event data
|
||||
if (GetWorld()->GetGameInstance()) {
|
||||
if (UGameEventSubsystem* GES = GetWorld()->GetGameInstance()->GetSubsystem<UGameEventSubsystem>()) {
|
||||
if (GetWorld()->GetGameInstance())
|
||||
{
|
||||
if (UGameEventSubsystem* GES = GetWorld()->GetGameInstance()->GetSubsystem<UGameEventSubsystem>())
|
||||
{
|
||||
GES->LoadGameEventMapData(SaveData.EventDataMap);
|
||||
}
|
||||
}
|
||||
|
||||
//Load player data
|
||||
PlayerCharacter->SetActorTransform(SaveData.PlayerTransform);
|
||||
if (UStatForgeComponent* StatForgeComponent = UMainBlueprintFunctionLibrary::GetStatForgeComponentFromPlayer(GetWorld())) {
|
||||
if (UStatForgeComponent* StatForgeComponent =
|
||||
UMainBlueprintFunctionLibrary::GetStatForgeComponentFromPlayer(GetWorld()))
|
||||
{
|
||||
StatForgeComponent->RemoveAllGameplayStatEffects();
|
||||
|
||||
for (const FStatRuntimeDef& Stat : SaveData.StatData) {
|
||||
for (const FStatRuntimeDef& Stat : SaveData.StatData)
|
||||
{
|
||||
FGameplayStatEffectMod Mod;
|
||||
Mod.AffectedStat = Stat.StatDef.StatName;
|
||||
Mod.CalculationType = EGameplayStatEffectModCalculation::Flat;
|
||||
@@ -143,22 +142,28 @@ void AEleriGameState::PropagateSaveData(UEleriSaveGame* InSaveData) {
|
||||
|
||||
for (const FActiveGameplayStatEffect_SerializedData& SavedGe : SaveData.GameEffects)
|
||||
{
|
||||
const FGuid Id = StatForgeComponent->ApplyGameplayStatEffect(SavedGe.GameplayEffectClass, SavedGe.DataValueMap, SavedGe.StackCount);
|
||||
const FGuid Id = StatForgeComponent->ApplyGameplayStatEffect(
|
||||
SavedGe.GameplayEffectClass, SavedGe.DataValueMap, SavedGe.StackCount);
|
||||
StatForgeComponent->ModifyGeDuration(Id, SavedGe.RemainingDuration, SavedGe.NextTickDuration);
|
||||
}
|
||||
}
|
||||
|
||||
//Seed delete and spawn should always go last
|
||||
PropagateAllSeedData();
|
||||
}
|
||||
|
||||
const FSaveData& AEleriGameState::PrepareSaveData() {
|
||||
SaveData.Initialize();
|
||||
|
||||
const FSaveData& AEleriGameState::PrepareSaveData()
|
||||
{
|
||||
//SAVE TIME OF DAY
|
||||
ATimeOfDayManager* TimeOfDay = Cast<ATimeOfDayManager>(UGameplayStatics::GetActorOfClass(GetWorld(), ATimeOfDayManager::StaticClass()));
|
||||
ATimeOfDayManager* TimeOfDay = Cast<ATimeOfDayManager>(
|
||||
UGameplayStatics::GetActorOfClass(GetWorld(), ATimeOfDayManager::StaticClass()));
|
||||
check(TimeOfDay);
|
||||
SaveData.SetTimeOFDayData(TimeOfDay->GetDaysPassed(), TimeOfDay->GetMonth(), TimeOfDay->GetYear(), TimeOfDay->GetExactMinutes(), TimeOfDay->GetCurrentHours());
|
||||
SaveData.SetTimeOFDayData(TimeOfDay->GetDaysPassed(), TimeOfDay->GetMonth(), TimeOfDay->GetYear(),
|
||||
TimeOfDay->GetExactMinutes(), TimeOfDay->GetCurrentHours());
|
||||
|
||||
//SAVE BOOK
|
||||
const ABookOfAlchemyManager* Book = Cast<ABookOfAlchemyManager>(UGameplayStatics::GetActorOfClass(GetWorld(), ABookOfAlchemyManager::StaticClass()));
|
||||
const ABookOfAlchemyManager* Book = Cast<ABookOfAlchemyManager>(
|
||||
UGameplayStatics::GetActorOfClass(GetWorld(), ABookOfAlchemyManager::StaticClass()));
|
||||
check(Book);
|
||||
SaveData.SetBookProgressionData(Book->BookProgression);
|
||||
|
||||
@@ -166,24 +171,32 @@ const FSaveData& AEleriGameState::PrepareSaveData() {
|
||||
|
||||
//SAVE INVENTORIES
|
||||
UGameplayStatics::GetAllActorsWithTag(GetWorld(), INVENTORY_TAG, OutActors);
|
||||
for (int32 i = 0; i < OutActors.Num(); i++) {
|
||||
if (UInventoryComponent* Inv = Cast<UInventoryComponent>(OutActors[i]->GetComponentByClass(UInventoryComponent::StaticClass()))) {
|
||||
for (int32 i = 0; i < OutActors.Num(); i++)
|
||||
{
|
||||
if (UInventoryComponent* Inv = Cast<UInventoryComponent>(
|
||||
OutActors[i]->GetComponentByClass(UInventoryComponent::StaticClass())))
|
||||
{
|
||||
SaveData.SetInventoryDataForActor(OutActors[i], Inv);
|
||||
}
|
||||
}
|
||||
|
||||
//SAVE SEEDS IN WORLD
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASeedCarousel::StaticClass(), OutActors);
|
||||
for (int32 i = 0; i < OutActors.Num(); i++) {
|
||||
if (ASeedCarousel* SeedActor = Cast<ASeedCarousel>(OutActors[i])) {
|
||||
const FSeedSaveData SeedSaveData = FSeedSaveData(SeedActor);
|
||||
SaveData.SetPlantedSeedData(FName(SeedActor->GetName()), SeedSaveData);
|
||||
}
|
||||
//SAVE PLACED OBJECTS
|
||||
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<
|
||||
UObjectPersistenceSubsystem>())
|
||||
{
|
||||
SaveData.SetPlacedObjectsData(ObjectPersistenceSubsystem->PrepareSaveData());
|
||||
}
|
||||
|
||||
//SAVE PLACED OBJECTS
|
||||
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UObjectPersistenceSubsystem>()) {
|
||||
SaveData.SetPlacedObjectsData(ObjectPersistenceSubsystem->PrepareSaveData());
|
||||
//SAVE SEEDS
|
||||
OutActors.Empty();
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASeedBedActor::StaticClass(), OutActors);
|
||||
for (AActor* SeedActor : OutActors)
|
||||
{
|
||||
if (ASeedBedActor* SeedBedActor = Cast<ASeedBedActor>(SeedActor))
|
||||
{
|
||||
FSeedBedSaveData SeedSaveData = SeedBedActor->PrepareSeedSaveData();
|
||||
CacheSeedData(SeedSaveData.Index, SeedSaveData);
|
||||
}
|
||||
}
|
||||
|
||||
//SAVE ACTORS
|
||||
@@ -199,22 +212,43 @@ const FSaveData& AEleriGameState::PrepareSaveData() {
|
||||
return SaveData;
|
||||
}
|
||||
|
||||
void AEleriGameState::CacheActorData(AEleriBaseActor* EleriActor) {
|
||||
void AEleriGameState::CacheActorData(AEleriBaseActor* EleriActor)
|
||||
{
|
||||
if (!IsValid(EleriActor)) return;
|
||||
if (auto FoundActorData = SaveData.ActorSaveData.Find(EleriActor->GetPersistentId())) {
|
||||
if (auto FoundActorData = SaveData.ActorSaveData.Find(EleriActor->GetPersistentId()))
|
||||
{
|
||||
*FoundActorData = Cast<ISaveableObjectInterface>(EleriActor)->RequestSave();
|
||||
}
|
||||
else {
|
||||
SaveData.ActorSaveData.Add(Cast<IActorPersistentIdInterface>(EleriActor)->GetPersistentId(), Cast<ISaveableObjectInterface>(EleriActor)->RequestSave());
|
||||
else
|
||||
{
|
||||
SaveData.ActorSaveData.Add(Cast<IActorPersistentIdInterface>(EleriActor)->GetPersistentId(),
|
||||
Cast<ISaveableObjectInterface>(EleriActor)->RequestSave());
|
||||
}
|
||||
}
|
||||
|
||||
bool AEleriGameState::RequestActorSaveData(const AEleriBaseActor* EleriActor, FActorSaveData& OutData) const {
|
||||
bool AEleriGameState::RequestActorSaveData(const AEleriBaseActor* EleriActor, FActorSaveData& OutData) const
|
||||
{
|
||||
if (!IsValid(EleriActor)) return false;
|
||||
|
||||
if (auto FoundActorData = SaveData.ActorSaveData.Find(EleriActor->GetPersistentId())) {
|
||||
if (auto FoundActorData = SaveData.ActorSaveData.Find(EleriActor->GetPersistentId()))
|
||||
{
|
||||
OutData = *FoundActorData;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AEleriGameState::CacheSeedData(int32 Index, const FSeedBedSaveData& Data)
|
||||
{
|
||||
SaveData.SetPlantedSeedData(Index, Data);
|
||||
}
|
||||
|
||||
bool AEleriGameState::GetSeedData(int32 Index, FSeedBedSaveData& OutData)
|
||||
{
|
||||
if (const FSeedBedSaveData* FoundData = SaveData.SeedBedData.Find(Index))
|
||||
{
|
||||
OutData = *FoundData;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "ProjectEleri/DialogueSystem/DialogueWidget.h"
|
||||
#include "Subsystem/InteractionSubsystem.h"
|
||||
#include "Interface/InteractableActorInterface.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "ProjectEleri/Items/SeedBedActor.h"
|
||||
|
||||
AEleriPlayerController::AEleriPlayerController(const FObjectInitializer &ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
@@ -188,6 +190,11 @@ void AEleriPlayerController::SetupInputComponent()
|
||||
PlayerEnhancedInputComponent->BindAction(WaterSeedAction, ETriggerEvent::Started, this, &AEleriPlayerController::OnHarvestSeed);
|
||||
}
|
||||
|
||||
if (PlantSeedAction)
|
||||
{
|
||||
PlayerEnhancedInputComponent->BindAction(PlantSeedAction, ETriggerEvent::Started, this, &AEleriPlayerController::OnPlantSeedAction);
|
||||
}
|
||||
|
||||
if (UiBackAction)
|
||||
{
|
||||
PlayerEnhancedInputComponent->BindAction(UiBackAction, ETriggerEvent::Started, this, &AEleriPlayerController::OnControllerBackAction);
|
||||
@@ -570,7 +577,6 @@ void AEleriPlayerController::OnRemoveAction(const FInputActionValue &Value)
|
||||
{
|
||||
MoveActorsComp->PlaceableActionUsed(ERemovalAction::REMOVE);
|
||||
MoveActorsComp->MovingActor = nullptr;
|
||||
MoveActorsComp->RemovalModePlacingStack = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,43 +623,18 @@ void AEleriPlayerController::OnPlaceObjectAction(const FInputActionValue &Value)
|
||||
|
||||
if (MoveActorsComp->MovingActor)
|
||||
{
|
||||
UClass *ClassToLoad = MoveActorsComp->MovingActor->GetClass();
|
||||
FVector Location = MoveActorsComp->MovingActor->GetActorLocation();
|
||||
UInventoryComponent *Inventory = MoveActorsComp->MovingActor->InventoryRef;
|
||||
FItemStack Stack = MoveActorsComp->MovingActor->ItemStack;
|
||||
UItemDatabase* ItemDatabase = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<UItemDatabase>();
|
||||
UInventoryComponent *Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(this);
|
||||
UItemDataAsset* DataAsset = MoveActorsComp->MovingActor->ItemDataAsset;
|
||||
|
||||
if (!MoveActorsComp->PlaceableActionUsed(ERemovalAction::PLACE))
|
||||
return;
|
||||
if (Inventory)
|
||||
|
||||
int32 StackCount = Inventory->GetStackForItem(DataAsset->GetPrimaryAssetId(), false, false);
|
||||
if (StackCount > 0)
|
||||
{
|
||||
Stack.Stack = Inventory->GetStackForItem(Stack.ItemId, Stack.Seed, Stack.HQ);
|
||||
}
|
||||
else
|
||||
{
|
||||
Stack.Stack = 0;
|
||||
}
|
||||
|
||||
if (ClassToLoad && Stack.Stack > 0)
|
||||
{
|
||||
FTransform PlaceableTransform;
|
||||
PlaceableTransform.SetLocation(Location);
|
||||
if (ARemovableStaticMeshActor *SpawnedActor = UMainBlueprintFunctionLibrary::SpawnPlaceable(GetWorld(), ClassToLoad, PlaceableTransform, FVector::OneVector))
|
||||
{
|
||||
if (!MoveActorsComp || !MoveActorsComp->IsRemovingMode())
|
||||
{
|
||||
SpawnedActor->Destroy();
|
||||
MoveActorsComp->MovingActor = nullptr;
|
||||
return;
|
||||
}
|
||||
SpawnedActor->BindToItem(Inventory, Stack);
|
||||
SpawnedActor->PickUp();
|
||||
MoveActorsComp->MovingActor = SpawnedActor;
|
||||
}
|
||||
}
|
||||
|
||||
if (MoveActorsComp->RemovalModePlacingStack <= 0)
|
||||
{
|
||||
MoveActorsComp->StartObjectRemoval(false);
|
||||
ensure(ItemDatabase);
|
||||
ItemDatabase->SpawnDecorationItem(DataAsset);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1151,7 +1132,7 @@ void AEleriPlayerController::OnInteract(const FInputActionValue &Value)
|
||||
return;
|
||||
|
||||
if (PlayerCharacter->GetIsPlanting())
|
||||
PlayerCharacter->PlantSeed();
|
||||
TryPlantSelectedSeed();
|
||||
|
||||
// Ask the InteractionSystem plugin's subsystem for the closest valid interactable
|
||||
// and fire the interaction on it if we have one.
|
||||
@@ -1178,6 +1159,115 @@ void AEleriPlayerController::OnHarvestSeed(const FInputActionValue &Value)
|
||||
{
|
||||
}
|
||||
|
||||
// PLANTING
|
||||
|
||||
void AEleriPlayerController::SetSeedToPlant(UItemDataAsset *SeedData, bool bHighQuality)
|
||||
{
|
||||
// Anything that is not a seed that fits into a seed bed is rejected, the UI cannot leave us in a bad state
|
||||
SeedToPlant = ASeedBedActor::IsPlantableSeedData(SeedData) ? SeedData : nullptr;
|
||||
bSeedToPlantHighQuality = IsValid(SeedToPlant) && bHighQuality;
|
||||
|
||||
// Selecting a seed is what starts the planting, the beds in range light up for it right away
|
||||
if (IsValid(SeedToPlant))
|
||||
{
|
||||
SetPlantingMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AEleriPlayerController::ClearSeedToPlant()
|
||||
{
|
||||
if (!IsValid(SeedToPlant) && !bSeedToPlantHighQuality)
|
||||
return;
|
||||
|
||||
SeedToPlant = nullptr;
|
||||
bSeedToPlantHighQuality = false;
|
||||
|
||||
// Nothing is being planted anymore, so the beds stop being highlighted
|
||||
if (IsPlantingModeActive())
|
||||
{
|
||||
SetPlantingMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AEleriPlayerController::IsPlantingModeActive() const
|
||||
{
|
||||
return IsValid(PlayerCharacter) && PlayerCharacter->GetIsPlanting();
|
||||
}
|
||||
|
||||
void AEleriPlayerController::SetPlantingMode(bool bActive)
|
||||
{
|
||||
if (!IsValid(PlayerCharacter))
|
||||
return;
|
||||
|
||||
PlayerCharacter->SetPlanting(bActive);
|
||||
|
||||
// Planting gets its own context so it can take a key over without touching the other contexts
|
||||
if (IsValid(PlantingContext))
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem *Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
|
||||
{
|
||||
if (bActive)
|
||||
{
|
||||
Subsystem->AddMappingContext(PlantingContext, 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
Subsystem->RemoveMappingContext(PlantingContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AEleriPlayerController::TryPlantSelectedSeed()
|
||||
{
|
||||
if (!IsValid(PlayerCharacter))
|
||||
return;
|
||||
|
||||
// A seed that is not plantable anymore, for example when the inventory was emptied, drops out of planting
|
||||
if (!ASeedBedActor::IsPlantableSeedData(SeedToPlant))
|
||||
{
|
||||
ClearSeedToPlant();
|
||||
return;
|
||||
}
|
||||
|
||||
ASeedBedActor *SeedBed = PlayerCharacter->GetClosestSeedBedActor();
|
||||
if (!IsValid(SeedBed))
|
||||
return;
|
||||
|
||||
const FVector PlayerLocation = PlayerCharacter->GetActorLocation();
|
||||
|
||||
FIntVector2 SlotCoordinate = SeedBed->GetHighlightedSlotIndex();
|
||||
if (SlotCoordinate == FIntVector2::NoneValue)
|
||||
return;
|
||||
|
||||
UInventoryComponent *Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(this);
|
||||
if (!IsValid(Inventory))
|
||||
return;
|
||||
|
||||
// The seed is paid for before it is planted, so a slot can never hold a seed the inventory never had
|
||||
const FPrimaryAssetId SeedAssetId = SeedToPlant->GetPrimaryAssetId();
|
||||
if (Inventory->HasItem(SeedAssetId, true, bSeedToPlantHighQuality) <= 0)
|
||||
return;
|
||||
|
||||
if (!SeedBed->PlantSeedInSlot(SlotCoordinate, SeedToPlant, bSeedToPlantHighQuality))
|
||||
return;
|
||||
|
||||
Inventory->RemoveItemFromInventory(SeedAssetId, 1, true, bSeedToPlantHighQuality);
|
||||
|
||||
if (Inventory->HasItem(SeedAssetId, true, bSeedToPlantHighQuality) == INDEX_NONE)
|
||||
{
|
||||
ClearSeedToPlant();
|
||||
}
|
||||
}
|
||||
|
||||
void AEleriPlayerController::OnPlantSeedAction(const FInputActionValue &Value)
|
||||
{
|
||||
if (!IsValid(PlayerCharacter))
|
||||
return;
|
||||
|
||||
TryPlantSelectedSeed();
|
||||
}
|
||||
|
||||
void AEleriPlayerController::StartMinigame(EMinigameType MinigameType, const TSoftObjectPtr<UBaseMinigameDataAsset> &DataAsset)
|
||||
{
|
||||
if (!IsValid(PlayerCharacter) ||
|
||||
|
||||
@@ -14,7 +14,7 @@ void FSaveData::Initialize()
|
||||
BookProgression.Empty();
|
||||
InventoryData.Empty();
|
||||
QuestList.Empty();
|
||||
PlantedSeedsList.Empty();
|
||||
SeedBedData.Empty();
|
||||
PlacedActors.Empty();
|
||||
ActorSaveData.Empty();
|
||||
EventDataMap.Empty();
|
||||
@@ -46,11 +46,11 @@ void FSaveData::SetInventoryDataForActor(AActor* Actor, UInventoryComponent* Inv
|
||||
InventoryData.Add(ActorID, Inventory->PrepareSaveData());
|
||||
}
|
||||
|
||||
void FSaveData::SetPlantedSeedData(FName ActorId, FSeedSaveData SeedSaveData)
|
||||
void FSaveData::SetPlantedSeedData(int32 Index, const FSeedBedSaveData& SeedSaveData)
|
||||
{
|
||||
if(ActorId.IsNone()) return;
|
||||
if(Index < 0) return;
|
||||
|
||||
PlantedSeedsList.Add(SeedSaveData);
|
||||
SeedBedData.Add(Index, SeedSaveData);
|
||||
}
|
||||
|
||||
void FSaveData::SetPlacedObjectsData(TArray<FPlaceableActorInfo> InPlacedActors)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user