Fixes for seed planting
This commit is contained in:
@@ -10,14 +10,33 @@ void UEleriFlipbookComponent::BeginPlay()
|
||||
Super::BeginPlay();
|
||||
|
||||
DecalComponent = NewObject<UDecalComponent>(GetOwner(), UDecalComponent::StaticClass(), TEXT("Shadow Decal"));
|
||||
DecalComponent->SetDecalMaterial(DecalMaterial);
|
||||
DecalComponent->DecalSize = DecalSize;
|
||||
DecalComponent->SetFadeScreenSize(0.f);
|
||||
DecalComponent->SetupAttachment(GetOwner()->GetRootComponent());
|
||||
DecalComponent->RegisterComponent();
|
||||
DecalComponent->SetRelativeLocation(DecalOffset - FVector(0.f, 0.f, DecalSize.Z));
|
||||
UpdateRelativeLocation(FVector::ZeroVector);
|
||||
DecalComponent->SetUsingAbsoluteRotation(true);
|
||||
DecalComponent->SetRelativeRotation(FRotator(-90.f, 0.f, 0.f));
|
||||
|
||||
SetCastShadow(false);
|
||||
|
||||
if (DecalMaterial.IsValid())
|
||||
{
|
||||
DecalComponent->SetDecalMaterial(DecalMaterial.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
DecalMaterial.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [this](const FSoftObjectPath& Path, UObject* MaterialObj)
|
||||
{
|
||||
if (UMaterialInterface* Interface = Cast<UMaterialInterface>(MaterialObj))
|
||||
{
|
||||
DecalComponent->SetDecalMaterial(Interface);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void UEleriFlipbookComponent::UpdateRelativeLocation(const FVector& NewLoc) const
|
||||
{
|
||||
DecalComponent->SetRelativeLocation(NewLoc + (DecalOffset - FVector(0.f, 0.f, DecalSize.Z)));
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ class PROJECTELERI_API UEleriFlipbookComponent : public UPaperFlipbookComponent
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
UMaterialInterface* DecalMaterial = nullptr;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
TSoftObjectPtr<UMaterialInterface> DecalMaterial = nullptr;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FVector DecalSize = FVector::OneVector;
|
||||
@@ -24,9 +24,9 @@ public:
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FVector DecalOffset = FVector::ZeroVector;
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
UDecalComponent* DecalComponent = nullptr;
|
||||
|
||||
|
||||
// Will provide a new relative location that we will update with the offset
|
||||
void UpdateRelativeLocation(const FVector& NewLoc) const;
|
||||
};
|
||||
|
||||
44
Source/ProjectEleri/Components/EleriPaperSpriteComponent.cpp
Normal file
44
Source/ProjectEleri/Components/EleriPaperSpriteComponent.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "EleriPaperSpriteComponent.h"
|
||||
|
||||
#include "Components/DecalComponent.h"
|
||||
|
||||
|
||||
void UEleriPaperSpriteComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
DecalComponent = NewObject<UDecalComponent>(GetOwner(), UDecalComponent::StaticClass(), TEXT("Shadow Decal"));
|
||||
DecalComponent->DecalSize = DecalSize;
|
||||
DecalComponent->SetFadeScreenSize(0.f);
|
||||
DecalComponent->SetupAttachment(GetOwner()->GetRootComponent());
|
||||
DecalComponent->RegisterComponent();
|
||||
UpdateRelativeLocation(FVector::ZeroVector);
|
||||
DecalComponent->SetUsingAbsoluteRotation(true);
|
||||
DecalComponent->SetRelativeRotation(FRotator(-90.f, 0.f, 0.f));
|
||||
|
||||
SetCastShadow(false);
|
||||
|
||||
if (DecalMaterial.IsValid())
|
||||
{
|
||||
DecalComponent->SetDecalMaterial(DecalMaterial.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
DecalMaterial.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [this](const FSoftObjectPath& Path, UObject* MaterialObj)
|
||||
{
|
||||
if (UMaterialInterface* Interface = Cast<UMaterialInterface>(MaterialObj))
|
||||
{
|
||||
DecalComponent->SetDecalMaterial(Interface);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void UEleriPaperSpriteComponent::UpdateRelativeLocation(const FVector& NewLoc) const
|
||||
{
|
||||
DecalComponent->SetRelativeLocation(NewLoc + (DecalOffset - FVector(0.f, 0.f, DecalSize.Z)));
|
||||
}
|
||||
|
||||
32
Source/ProjectEleri/Components/EleriPaperSpriteComponent.h
Normal file
32
Source/ProjectEleri/Components/EleriPaperSpriteComponent.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "PaperSpriteComponent.h"
|
||||
#include "EleriPaperSpriteComponent.generated.h"
|
||||
|
||||
|
||||
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
|
||||
class PROJECTELERI_API UEleriPaperSpriteComponent : public UPaperSpriteComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
TSoftObjectPtr<UMaterialInterface> DecalMaterial = nullptr;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FVector DecalSize = FVector::OneVector;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FVector DecalOffset = FVector::ZeroVector;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
UDecalComponent* DecalComponent = nullptr;
|
||||
|
||||
// Will provide a new relative location that we will update with the offset
|
||||
void UpdateRelativeLocation(const FVector& NewLoc) const;
|
||||
};
|
||||
@@ -12,11 +12,15 @@
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/World.h"
|
||||
#include "ItemContainer.h"
|
||||
#include "NiagaraComponent.h"
|
||||
#include "PaperSpriteComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Kismet/KismetMaterialLibrary.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "ProjectEleri/Components/EleriPaperSpriteComponent.h"
|
||||
#include "ProjectEleri/Settings/EleriGameSettings.h"
|
||||
#include "NiagaraComponent.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -31,14 +35,14 @@ namespace
|
||||
}
|
||||
|
||||
/** Slots are created at runtime as well, so the components are created with stable, deterministic names */
|
||||
UPaperSpriteComponent* CreatePaperSpriteComponent(AActor* Owner, USceneComponent* AttachParent, FName ComponentName, TSoftObjectPtr<UMaterialInterface> Material)
|
||||
UEleriPaperSpriteComponent* CreatePaperSpriteComponent(AActor* Owner, USceneComponent* AttachParent, FName ComponentName, TSoftObjectPtr<UMaterialInterface> Material)
|
||||
{
|
||||
if (!IsValid(Owner) || !IsValid(AttachParent))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UPaperSpriteComponent* SpriteComponent = NewObject<UPaperSpriteComponent>(Owner, ComponentName);
|
||||
UEleriPaperSpriteComponent* SpriteComponent = NewObject<UEleriPaperSpriteComponent>(Owner, ComponentName);
|
||||
if (!SpriteComponent)
|
||||
{
|
||||
return nullptr;
|
||||
@@ -46,10 +50,15 @@ namespace
|
||||
|
||||
SpriteComponent->SetMobility(EComponentMobility::Movable);
|
||||
SpriteComponent->SetupAttachment(AttachParent);
|
||||
SpriteComponent->DecalSize = FVector(150.f);
|
||||
SpriteComponent->DecalOffset = FVector(0.f, 0.f, 40.f);
|
||||
SpriteComponent->DecalMaterial = UEleriGameSettings::GetEleriGameSettings()->SpriteShadowMaterial;
|
||||
SpriteComponent->SetReceivesDecals(false);
|
||||
SpriteComponent->RegisterComponent();
|
||||
SpriteComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
SpriteComponent->SetVisibility(false);
|
||||
TWeakObjectPtr<UPaperSpriteComponent> SpriteComp = SpriteComponent;
|
||||
SpriteComponent->DecalComponent->SetVisibility(false);
|
||||
TWeakObjectPtr<UEleriPaperSpriteComponent> SpriteComp = SpriteComponent;
|
||||
if (Material.IsValid())
|
||||
{
|
||||
SpriteComp->SetMaterial(0, Material.Get());
|
||||
@@ -265,7 +274,7 @@ void ASeedBedActor::AdvanceSeedBedByHours(int32 Hours)
|
||||
// Every hour is applied on its own, a jump over several of them grows, dries and kills hour by hour
|
||||
for (int32 HourIndex = 0; HourIndex < Hours && SlotHasLivingPlant(SlotPair.Value); ++HourIndex)
|
||||
{
|
||||
ApplyHourPassedToSlot(SlotPair.Value, Now);
|
||||
ApplyHourPassedToSlot(SlotPair.Value, Now, SlotPair.Key);
|
||||
}
|
||||
|
||||
if (bWasAlive && !SlotHasLivingPlant(SlotPair.Value))
|
||||
@@ -283,9 +292,14 @@ void ASeedBedActor::AdvanceSeedBedByHours(int32 Hours)
|
||||
}
|
||||
}
|
||||
|
||||
void ASeedBedActor::ApplyHourPassedToSlot(FSeedBedSeedSlot& Slot, const FTimeOfDayData& Now)
|
||||
void ASeedBedActor::ApplyHourPassedToSlot(FSeedBedSeedSlot& Slot, const FTimeOfDayData& Now, const FIntVector2& SlotCoordinate)
|
||||
{
|
||||
FSeedBedSeedSlotData& SlotData = Slot.SlotData;
|
||||
|
||||
if (SlotData.bBloomed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The moisture of the soil drains with every in game hour, so the wetness is a clock of its own
|
||||
const bool bWasMoist = SlotData.MoistureHoursRemaining > 0;
|
||||
@@ -308,6 +322,11 @@ void ASeedBedActor::ApplyHourPassedToSlot(FSeedBedSeedSlot& Slot, const FTimeOfD
|
||||
}
|
||||
|
||||
SlotData.SeedAgeHours = FMath::Min(SlotData.SeedAgeHours + 1, FMath::Max(0, SlotData.GrowTime) * SeedBedHoursPerDay);
|
||||
SlotData.bBloomed = SlotData.SeedAgeHours >= SlotData.GrowTime * SeedBedHoursPerDay;
|
||||
if (SlotData.bBloomed)
|
||||
{
|
||||
RefreshSlotVisual(SlotCoordinate);
|
||||
}
|
||||
}
|
||||
|
||||
void ASeedBedActor::ApplyOfflineProgression()
|
||||
@@ -604,6 +623,18 @@ FSeedBedSeedSlot& ASeedBedActor::CreateSlot(FIntVector2 SlotCoordinate, bool bRe
|
||||
{
|
||||
Slot.PlantSprite = CreatePaperSpriteComponent(this, GetRootComponent(), GetSlotComponentName(SlotCoordinate, TEXT("Plant")), DefaultPlantSpriteMaterial);
|
||||
}
|
||||
|
||||
Slot.NiagaraComponent = NewObject<UNiagaraComponent>(this, GetSlotComponentName(SlotCoordinate, TEXT("Vfx")));
|
||||
Slot.NiagaraComponent->SetMobility(EComponentMobility::Movable);
|
||||
Slot.NiagaraComponent->SetupAttachment(GetRootComponent());
|
||||
Slot.NiagaraComponent->SetAutoActivate(false);
|
||||
Slot.NiagaraComponent->RegisterComponent();
|
||||
Slot.NiagaraComponent->SetRelativeLocation(FVector(
|
||||
Offset.X + X + SeedBedDecalSize.Z,
|
||||
Offset.Y + Y + SeedBedDecalSize.Y,
|
||||
50.f));
|
||||
Slot.NiagaraComponent->SetVisibility(false);
|
||||
AddInstanceComponent(Slot.NiagaraComponent);
|
||||
|
||||
ApplySlotVisuals(SlotCoordinate, Slot);
|
||||
|
||||
@@ -638,6 +669,7 @@ void ASeedBedActor::ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlo
|
||||
{
|
||||
// A plant that died or was harvested keeps its mesh, the slot is only emptied when it is cleared for real
|
||||
Slot.PlantSprite->SetVisibility(false);
|
||||
Slot.PlantSprite->DecalComponent->SetVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -645,6 +677,13 @@ void ASeedBedActor::ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlo
|
||||
FVector PlantScale = FVector(5.f);
|
||||
|
||||
const int32 StageIndex = ResolveGrowthStageIndex(Slot);
|
||||
if (StageIndex == INDEX_NONE)
|
||||
{
|
||||
Slot.PlantSprite->SetVisibility(false);
|
||||
Slot.PlantSprite->DecalComponent->SetVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const UItemDataAsset* SeedData = Slot.SlotData.ItemDataAsset;
|
||||
if (IsValid(SeedData) && SeedData->SeedStages.IsValidIndex(StageIndex))
|
||||
{
|
||||
@@ -654,11 +693,13 @@ void ASeedBedActor::ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlo
|
||||
|
||||
Slot.PlantSprite->SetRelativeScale3D(PlantScale);
|
||||
Slot.PlantSprite->SetRelativeLocation(PlantOffset);
|
||||
Slot.PlantSprite->UpdateRelativeLocation(FVector(PlantOffset.X, PlantOffset.Y, 0.f));
|
||||
|
||||
if (SeedData->SeedStages[StageIndex].Sprite.IsValid())
|
||||
{
|
||||
Slot.PlantSprite->SetSprite(SeedData->SeedStages[StageIndex].Sprite.Get());
|
||||
Slot.PlantSprite->SetVisibility(true);
|
||||
Slot.PlantSprite->DecalComponent->SetVisibility(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -673,8 +714,36 @@ void ASeedBedActor::ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlo
|
||||
Slot->PlantSprite->SetSprite(Sprite);
|
||||
}));
|
||||
}
|
||||
|
||||
if (Slot.SlotData.bBloomed && !Slot.NiagaraComponent->IsActive())
|
||||
{
|
||||
Slot.NiagaraComponent->SetAsset(nullptr);
|
||||
Slot.NiagaraComponent->Activate(false);
|
||||
|
||||
if (PlantBloomedVfx.IsValid())
|
||||
{
|
||||
Slot.NiagaraComponent->SetAsset(PlantBloomedVfx.Get());
|
||||
Slot.NiagaraComponent->Activate(true);
|
||||
Slot.NiagaraComponent->SetVisibility(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlantBloomedVfx.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [this, SlotCoordinate](const FSoftObjectPath& Path, UObject* EffectObj)
|
||||
{
|
||||
FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
if (!Slot || !IsValid(Slot->NiagaraComponent)) return;
|
||||
|
||||
UNiagaraSystem* NiagaraSystem = Cast<UNiagaraSystem>(EffectObj);
|
||||
if (!IsValid(NiagaraSystem)) return;
|
||||
|
||||
Slot->NiagaraComponent->SetAsset(NiagaraSystem);
|
||||
Slot->NiagaraComponent->ResetSystem();
|
||||
Slot->NiagaraComponent->SetVisibility(true);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
void ASeedBedActor::RefreshSlotVisual(FIntVector2 SlotCoordinate)
|
||||
void ASeedBedActor::RefreshSlotVisual(const FIntVector2& SlotCoordinate)
|
||||
{
|
||||
FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate);
|
||||
if (!Slot)
|
||||
@@ -698,7 +767,7 @@ void ASeedBedActor::RefreshAllSlotVisuals()
|
||||
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;
|
||||
return IsValid(SeedData) && SeedData->ItemCategory == Category::Seed;
|
||||
}
|
||||
|
||||
void ASeedBedActor::HighlightSlot(FIntVector2 SlotCoordinate)
|
||||
@@ -816,7 +885,7 @@ int32 ASeedBedActor::ResolveGrowthStageIndex(const FSeedBedSeedSlot& Slot) const
|
||||
}
|
||||
|
||||
// Stages are set up per day while the age of a plant counts hours, so the last day that was reached wins
|
||||
int32 StageIndex = 0;
|
||||
int32 StageIndex = INDEX_NONE;
|
||||
|
||||
for (int32 Index = 0; Index < SeedData->SeedStages.Num(); ++Index)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "ProjectEleri/System/TimeOfDayStruct.h"
|
||||
#include "SeedBedActor.generated.h"
|
||||
|
||||
class UNiagaraSystem;
|
||||
class UNiagaraComponent;
|
||||
class UEleriPaperSpriteComponent;
|
||||
class UPaperSpriteComponent;
|
||||
class ATimeOfDayManager;
|
||||
class UDecalComponent;
|
||||
@@ -81,7 +84,10 @@ struct FSeedBedSeedSlot
|
||||
UMaterialInstanceDynamic* SeedBedDynamicMat = nullptr;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UPaperSpriteComponent* PlantSprite;
|
||||
UEleriPaperSpriteComponent* PlantSprite = nullptr;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UNiagaraComponent* NiagaraComponent = nullptr;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
@@ -143,6 +149,9 @@ public:
|
||||
/** Dimensions of the grid generated by GenerateSeedBedGrid, X = columns, Y = rows */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
FIntVector2 GridSize = FIntVector2(3, 3);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup")
|
||||
TSoftObjectPtr<UNiagaraSystem> PlantBloomedVfx;
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// HIGHLIGHT DECAL
|
||||
@@ -249,7 +258,7 @@ public:
|
||||
|
||||
/** Re-applies mesh, material and decal of a single slot */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed|Visuals")
|
||||
void RefreshSlotVisual(FIntVector2 SlotCoordinate);
|
||||
void RefreshSlotVisual(const FIntVector2& SlotCoordinate);
|
||||
|
||||
/** Re-applies mesh, material and decal of every slot of the bed */
|
||||
UFUNCTION(BlueprintCallable, Category = "Seed Bed|Visuals")
|
||||
@@ -287,7 +296,7 @@ protected:
|
||||
void BindToTimeOfDay();
|
||||
|
||||
/** Applies one in game hour of growth, moisture handling and death checks to a single slot */
|
||||
void ApplyHourPassedToSlot(FSeedBedSeedSlot& Slot, const FTimeOfDayData& Now);
|
||||
void ApplyHourPassedToSlot(FSeedBedSeedSlot& Slot, const FTimeOfDayData& Now, const FIntVector2& SlotCoordinate);
|
||||
|
||||
/** Catches the plants up on the in game time that passed while the bed was not in the world */
|
||||
void ApplyOfflineProgression();
|
||||
|
||||
@@ -50,6 +50,9 @@ public:
|
||||
|
||||
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "Ability System|Settings")
|
||||
TSoftObjectPtr<UDataTable> GlobalSettingsDataSheet;
|
||||
|
||||
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "Materials")
|
||||
TSoftObjectPtr<UMaterialInterface> SpriteShadowMaterial;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user