diff --git a/Content/GameData/Items/Plants/I_AloeVera.uasset b/Content/GameData/Items/Plants/I_AloeVera.uasset index 0a15e0c6..225b6b37 100644 --- a/Content/GameData/Items/Plants/I_AloeVera.uasset +++ b/Content/GameData/Items/Plants/I_AloeVera.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0d2223f4702f2e5f932f04fc136c1c3ad02c52434c406ac8ac191ed51139748 -size 7235 +oid sha256:5cd8f79b40b421dcdf687c7e058ece77b6bf5fe302051f575e595ff216e4c053 +size 5534 diff --git a/Content/GameData/Items/Plants/I_ArnicaMontana.uasset b/Content/GameData/Items/Plants/I_ArnicaMontana.uasset index a5393070..e70e4cfc 100644 --- a/Content/GameData/Items/Plants/I_ArnicaMontana.uasset +++ b/Content/GameData/Items/Plants/I_ArnicaMontana.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79c66b6948bd726a65f5d33efe5788a6861798e7115bbd901bf0b85b59365b16 -size 7248 +oid sha256:cd3a6e02902eaa8614f658268ea52e9731b87e5cc2eb5e813407c057e0225e6b +size 4970 diff --git a/Content/GameData/Items/Plants/I_Carrot.uasset b/Content/GameData/Items/Plants/I_Carrot.uasset index ae2264e6..647bd38b 100644 --- a/Content/GameData/Items/Plants/I_Carrot.uasset +++ b/Content/GameData/Items/Plants/I_Carrot.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:452133df12b23a4135994b1877f5e66b581be5c5bb19f52329297895e21d7aa0 -size 6939 +oid sha256:974c2d7421161dc948a5d21cc6ca14172f0283778466500e195bf5522f049475 +size 6935 diff --git a/Content/GameData/Items/Plants/I_Garlic.uasset b/Content/GameData/Items/Plants/I_Garlic.uasset index 24ece8fe..050f346e 100644 --- a/Content/GameData/Items/Plants/I_Garlic.uasset +++ b/Content/GameData/Items/Plants/I_Garlic.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ebb67ba82cffe55f417cf80900f92bfc4997bd524952cfd4db95e2832456d39 -size 13014 +oid sha256:2cecff96d5e85fba0048d13b856f4c4575e1640c8d11758384cc88b5365d20a3 +size 13034 diff --git a/Content/GameData/Items/Plants/I_Lemon.uasset b/Content/GameData/Items/Plants/I_Lemon.uasset index 25c8448d..c19bfa2c 100644 --- a/Content/GameData/Items/Plants/I_Lemon.uasset +++ b/Content/GameData/Items/Plants/I_Lemon.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3602d74793fdbd0c95d57a590d4d57d5b4cb53ae394ed57ba4abe6e30fa5872 -size 7424 +oid sha256:d0aa0353863747bde243cd4a9d8216bdc5f3d607a356cf06fb47fff79257a00d +size 5968 diff --git a/Content/Materials/Sprite/MI_SpriteShadow_Inst.uasset b/Content/Materials/Sprite/MI_SpriteShadow_Inst.uasset new file mode 100644 index 00000000..f491b242 --- /dev/null +++ b/Content/Materials/Sprite/MI_SpriteShadow_Inst.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf9d1f773d7ef20ed61e043118e49ccd4393280abe6811e8cd462505dd334f4f +size 6776 diff --git a/Content/Materials/Sprite/M_SpriteShadow.uasset b/Content/Materials/Sprite/M_SpriteShadow.uasset new file mode 100644 index 00000000..0968d931 --- /dev/null +++ b/Content/Materials/Sprite/M_SpriteShadow.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d94c784c56a445b1f5f37f511c72956b137ce3aa26eac0d9252308ca96fbccc2 +size 13884 diff --git a/Content/Scripts/BookOfAlchemy/BookOfAlchemyUI.uasset b/Content/Scripts/BookOfAlchemy/BookOfAlchemyUI.uasset index 3756500d..f30d58af 100644 --- a/Content/Scripts/BookOfAlchemy/BookOfAlchemyUI.uasset +++ b/Content/Scripts/BookOfAlchemy/BookOfAlchemyUI.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f19116282c4abb72df7f86b5ee2f7b357e54b162b018c43a6e2e8560253ad509 -size 1048661 +oid sha256:9d209fd3eac5c0a3ae07a34c56ffb69f62642324e26f42d2fd8cd07e406573f0 +size 973642 diff --git a/Content/Scripts/DialogueSystem/UI/DialogueScreen.uasset b/Content/Scripts/DialogueSystem/UI/DialogueScreen.uasset index c7314b4c..dcd5d605 100644 --- a/Content/Scripts/DialogueSystem/UI/DialogueScreen.uasset +++ b/Content/Scripts/DialogueSystem/UI/DialogueScreen.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e93e494f50e4d2d4737db4d481325c3b7a1d03aa56f8257c00d3fd6f2d16816 -size 661431 +oid sha256:7f5c8826b7e8578596b39fa081d650ce1c1318ef83af55e04251c7e4c36dbb3d +size 655926 diff --git a/Content/Scripts/InventorySystem/Seed/BP_SeedBedActor.uasset b/Content/Scripts/InventorySystem/Seed/BP_SeedBedActor.uasset index 8bc99865..2043b993 100644 --- a/Content/Scripts/InventorySystem/Seed/BP_SeedBedActor.uasset +++ b/Content/Scripts/InventorySystem/Seed/BP_SeedBedActor.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eca1bd593ff760890a775b22a64c2bc018eeaf495d4843b51f4159f3a19b4a72 -size 98351 +oid sha256:ce7fca25d7c925555ef0aade19a11fb6a42df955f9b6936eaf743083438e3226 +size 98217 diff --git a/Content/Scripts/InventorySystem/Seed/BP_SeedCarousel.uasset b/Content/Scripts/InventorySystem/Seed/BP_SeedCarousel.uasset index 7c9503a6..2aadcfbf 100644 --- a/Content/Scripts/InventorySystem/Seed/BP_SeedCarousel.uasset +++ b/Content/Scripts/InventorySystem/Seed/BP_SeedCarousel.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:509f78a16e2916720726f46bc4c8d98f22a66fed2fb319504d2b7770796875e6 -size 1036753 +oid sha256:9e79196bc25eb0820ae5b58b5a00e4c94d2798c42645e8cba560bf65e235a279 +size 1010017 diff --git a/Content/ThirdPersonBP/Blueprints/BP_ThirdPersonCharacter.uasset b/Content/ThirdPersonBP/Blueprints/BP_ThirdPersonCharacter.uasset index f6fed70e..b046978b 100644 --- a/Content/ThirdPersonBP/Blueprints/BP_ThirdPersonCharacter.uasset +++ b/Content/ThirdPersonBP/Blueprints/BP_ThirdPersonCharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b1d824e6b82c17d14d427b4cd3d3aab279fdf523646d62bc3af7bfb1cf4daf1 -size 1292362 +oid sha256:1c215c05ed6c5d5a58bf641478876da7284de27ca3b32bf99a821896663eb5aa +size 1286130 diff --git a/Content/UI/MainUI.uasset b/Content/UI/MainUI.uasset index c719b568..f31578e4 100644 --- a/Content/UI/MainUI.uasset +++ b/Content/UI/MainUI.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:303176577e50e1dad9eefc1dff0505cc870bc557ea315a057280a384a0cbb4c1 -size 637990 +oid sha256:cdc73eb0baabf986091a192dafcfca7a301326a4749321358f374af670eb0dd3 +size 637366 diff --git a/Source/ProjectEleri/Components/EleriFlipbookComponent.cpp b/Source/ProjectEleri/Components/EleriFlipbookComponent.cpp new file mode 100644 index 00000000..519ddee6 --- /dev/null +++ b/Source/ProjectEleri/Components/EleriFlipbookComponent.cpp @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "EleriFlipbookComponent.h" + +#include "Components/DecalComponent.h" + +void UEleriFlipbookComponent::BeginPlay() +{ + Super::BeginPlay(); + + DecalComponent = NewObject(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)); + DecalComponent->SetUsingAbsoluteRotation(true); + DecalComponent->SetRelativeRotation(FRotator(-90.f, 0.f, 0.f)); + + SetCastShadow(false); +} diff --git a/Source/ProjectEleri/Components/EleriFlipbookComponent.h b/Source/ProjectEleri/Components/EleriFlipbookComponent.h new file mode 100644 index 00000000..07fa4e50 --- /dev/null +++ b/Source/ProjectEleri/Components/EleriFlipbookComponent.h @@ -0,0 +1,32 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "PaperFlipbookComponent.h" +#include "EleriFlipbookComponent.generated.h" + + +UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) +class PROJECTELERI_API UEleriFlipbookComponent : public UPaperFlipbookComponent +{ + GENERATED_BODY() + +public: + virtual void BeginPlay() override; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UMaterialInterface* DecalMaterial = nullptr; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + FVector DecalSize = FVector::OneVector; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + FVector DecalOffset = FVector::ZeroVector; + +protected: + + UPROPERTY(EditAnywhere, BlueprintReadOnly) + UDecalComponent* DecalComponent = nullptr; + +}; diff --git a/Source/ProjectEleri/Items/DataAssets/ItemDataAsset.h b/Source/ProjectEleri/Items/DataAssets/ItemDataAsset.h index eaad29aa..ebbf052e 100644 --- a/Source/ProjectEleri/Items/DataAssets/ItemDataAsset.h +++ b/Source/ProjectEleri/Items/DataAssets/ItemDataAsset.h @@ -140,10 +140,6 @@ public: /*--------------------------------------------------------------------------------------------------------------------------*/ - /** What part of the world grants faster growth and higher chance to harvest an HQ ingredient */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Book",EditCondition="ItemCategory==Category::Plant", EditConditionHides)) - TArray ItemGeography; - /** What base will the seed spawn when planting initiates */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) BaseType TypeOfBase; @@ -157,32 +153,17 @@ public: int32 SeedCost; /** Grow time for a seed in game days, if the seed is a tree or wine this is the growth for them */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Inventory, Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, DisplayName = "Grow Time", Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Inventory, Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) int32 GrowTimeSelf; - /** Only used on seeds that have wines or trees, after GrowTimeSelf has been reached - this is the amount of days that it takes for the plant to grow it's produce*/ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Inventory, Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) - int32 GrowTimeProduct; - /** Stages of a plant, set a mesh for certain days */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) TArray SeedStages; - /** In case of wine or tree this mesh will be used to display the produce that has beed grown - every GrowTimeProduct days*/ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) - TSoftObjectPtr ProduceGrownMesh; - - /** In case of wine or tree this mesh will be used to display the produce that has beed grown - every GrowTimeProduct days*/ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) - TSoftObjectPtr ProduceGrownSprite; - - /** What is the prefered temperature in alchemy that the ingredient releases it's + /** What is the preferred temperature in alchemy that the ingredient releases it's properties in the most effective way*/ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed", AssetRegistrySearchable, meta = (AssetBundles = "Book", EditCondition="ItemCategory==Category::Plant", EditConditionHides)) - int32 PreferedTemperature; + int32 PreferredTemperature; /*-------------------------------------------------------------------------------------------------------------------------*/ diff --git a/Source/ProjectEleri/Items/ItemContainer.h b/Source/ProjectEleri/Items/ItemContainer.h index c97d1b85..0a3343ae 100644 --- a/Source/ProjectEleri/Items/ItemContainer.h +++ b/Source/ProjectEleri/Items/ItemContainer.h @@ -101,35 +101,20 @@ struct FItemDatabaseRow : public FTableRowBase USTRUCT(BlueprintType) struct FSeedStage { - GENERATED_USTRUCT_BODY() - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - TSoftObjectPtr Mesh; - + GENERATED_BODY() + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) TSoftObjectPtr Sprite; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - float SpriteScale; + float SpriteScale = 1.f; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - FVector MeshOffset; - - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - FVector MeshScale; + FVector Offset = FVector::ZeroVector; /** In game days after planting at which this stage is shown, the seed bed ages its plants per hour and scales it */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - int32 ChangeOnDay; - - FSeedStage() - { - Mesh = nullptr; - MeshOffset = FVector::ZeroVector; - MeshScale = FVector::OneVector; - ChangeOnDay = 0; - SpriteScale = 1.f; - } + int32 ChangeOnDay = 0; }; USTRUCT(BlueprintType) @@ -158,10 +143,6 @@ struct FItemBase { of items in the array*/ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ingredient", meta = (MultiLine = true)) TArray ItemDescriptionLevels; - - /** What part of the world grants faster growth and higher chance to harvest an HQ ingredient */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") - Geography ItemGeography; /** Category the item belongs to */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Info") @@ -184,10 +165,6 @@ struct FItemBase { UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ingredient") TArray Defects; - /** What base will the seed spawn when planting initiates */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") - BaseType TypeOfBase; - /** Icon of the item */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Info") UTexture2D* ItemImage; @@ -222,6 +199,10 @@ struct FItemBase { /** Array of item ids that are required to craft the potion if it has a fixed recipe */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Potion") TArray RecipeItemsID; + + /** What part of the world grants faster growth and higher chance to harvest an HQ ingredient */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") + Geography ItemGeography; /** Does this ingredient have a seed? Ignored in potions */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") @@ -233,27 +214,12 @@ struct FItemBase { /** Grow time for a seed, if the seed is a tree or wine this is the growth for them */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") - int32 GrowTimeSelf; - - /** Only used on seeds that have wines or trees, after GrowTimeSelf has been reached - this is the amount of days that it takes for the plant to grow it's produce*/ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") - int32 GrowTimeProduct; + int32 GrowTime; /** Stages of a plant, set a mesh for certain days */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") TArray SeedStages; - /** In case of wine or tree this mesh will be used to display the produce that has beed grown - every GrowTimeProduct days*/ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") - UStaticMesh* ProduceGrownMesh; - - /** In case of wine or tree this mesh will be used to display the produce that has beed grown - every GrowTimeProduct days*/ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed") - UPaperSprite* ProduceGrownSprite; - /** What is the prefered temperature in alchemy that the ingredient releases it's properties in the most effective way*/ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ingredient") diff --git a/Source/ProjectEleri/Items/PlantItem.cpp b/Source/ProjectEleri/Items/PlantItem.cpp index ee3b7b00..616f3b72 100644 --- a/Source/ProjectEleri/Items/PlantItem.cpp +++ b/Source/ProjectEleri/Items/PlantItem.cpp @@ -21,7 +21,7 @@ void UPlantItem::SetupData(UItemDataAsset* BaseItem, bool HighQualityItem) if (!IsValid(PlantAsset)) return; bHasSeed = PlantAsset->IsSeed; - PreferedTemperature = PlantAsset->PreferedTemperature; + PreferedTemperature = PlantAsset->PreferredTemperature; } void UPlantItem::SetupDataFromOtherItem(UItem* BaseItem) diff --git a/Source/ProjectEleri/Items/SeedBedActor.cpp b/Source/ProjectEleri/Items/SeedBedActor.cpp index 884dd724..726e90fe 100644 --- a/Source/ProjectEleri/Items/SeedBedActor.cpp +++ b/Source/ProjectEleri/Items/SeedBedActor.cpp @@ -12,6 +12,7 @@ #include "Engine/StaticMesh.h" #include "Engine/World.h" #include "ItemContainer.h" +#include "PaperSpriteComponent.h" #include "Kismet/GameplayStatics.h" #include "Kismet/KismetMaterialLibrary.h" #include "Materials/MaterialInstanceDynamic.h" @@ -23,32 +24,49 @@ namespace void DestroySlotComponents(FSeedBedSeedSlot& Slot) { if (Slot.SeedBedDecal) Slot.SeedBedDecal->DestroyComponent(); - if (Slot.PlantStaticMesh) Slot.PlantStaticMesh->DestroyComponent(); + if (Slot.PlantSprite) Slot.PlantSprite->DestroyComponent(); Slot.SeedBedDecal = nullptr; - Slot.PlantStaticMesh = nullptr; + Slot.PlantSprite = 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) + UPaperSpriteComponent* CreatePaperSpriteComponent(AActor* Owner, USceneComponent* AttachParent, FName ComponentName, TSoftObjectPtr Material) { if (!IsValid(Owner) || !IsValid(AttachParent)) { return nullptr; } - UStaticMeshComponent* MeshComponent = NewObject(Owner, ComponentName); - if (!MeshComponent) + UPaperSpriteComponent* SpriteComponent = NewObject(Owner, ComponentName); + if (!SpriteComponent) { return nullptr; } - MeshComponent->SetMobility(EComponentMobility::Movable); - MeshComponent->SetupAttachment(AttachParent); - MeshComponent->RegisterComponent(); - Owner->AddInstanceComponent(MeshComponent); + SpriteComponent->SetMobility(EComponentMobility::Movable); + SpriteComponent->SetupAttachment(AttachParent); + SpriteComponent->RegisterComponent(); + SpriteComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); + SpriteComponent->SetVisibility(false); + TWeakObjectPtr SpriteComp = SpriteComponent; + if (Material.IsValid()) + { + SpriteComp->SetMaterial(0, Material.Get()); + } + else + { + Material.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(Owner, [Owner, SpriteComp](const FSoftObjectPath& Path, UObject* MaterialObj) + { + if (SpriteComp.IsValid()) + { + SpriteComp->SetMaterial(0, Cast(MaterialObj)); + } + })); + } + Owner->AddInstanceComponent(SpriteComponent); - return MeshComponent; + return SpriteComponent; } UDecalComponent* CreateSlotDecalComponent(AActor* Owner, USceneComponent* AttachParent, FName ComponentName) @@ -73,18 +91,6 @@ namespace return DecalComponent; } - /** Soft references are filled in by hand, an unset mesh or material must never break the visuals */ - template - TObjectType* ResolveSoftObject(TSoftObjectPtr 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) { @@ -128,7 +134,21 @@ void ASeedBedActor::BeginPlay() HighlightDecal->SetVisibility(false); HighlightDecal->SetRelativeRotation(HighlightDecalRotation); AddInstanceComponent(HighlightDecal); - HighlightDecal->SetDecalMaterial(ResolveSoftObject(HighlightDecalMaterial)); + TWeakObjectPtr WeakDecal = HighlightDecal; + if (HighlightDecalMaterial.IsValid()) + { + HighlightDecal->SetDecalMaterial(HighlightDecalMaterial.Get()); + } + else + { + HighlightDecalMaterial.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [WeakDecal](const FSoftObjectPath& Path, UObject* MaterialObj) + { + if (WeakDecal.IsValid()) + { + WeakDecal->SetDecalMaterial(Cast(MaterialObj)); + } + })); + } } ClearSeedBedGrid(); @@ -550,8 +570,24 @@ FSeedBedSeedSlot& ASeedBedActor::CreateSlot(FIntVector2 SlotCoordinate, bool bRe } 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); + if (SeedBedDecalMaterial.IsValid()) + { + Slot.SeedBedDynamicMat = UKismetMaterialLibrary::CreateDynamicMaterialInstance(this, SeedBedDecalMaterial.Get(), NAME_None, EMIDCreationFlags::Transient); + Slot.SeedBedDecal->SetDecalMaterial(Slot.SeedBedDynamicMat); + } + else + { + SeedBedDecalMaterial.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [this, SlotCoordinate](const FSoftObjectPath& Path, UObject* MaterialObj) + { + FSeedBedSeedSlot& Slot = SeedBedSeedSlotsMap.FindOrAdd(SlotCoordinate); + if (Slot.SeedBedDecal) + { + Slot.SeedBedDynamicMat = UKismetMaterialLibrary::CreateDynamicMaterialInstance(this, SeedBedDecalMaterial.Get(), 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; @@ -564,14 +600,9 @@ FSeedBedSeedSlot& ASeedBedActor::CreateSlot(FIntVector2 SlotCoordinate, bool bRe 0.f)); // The plant mesh of a slot lives as long as the slot does, the visuals only ever show or hide it - if (!IsValid(Slot.PlantStaticMesh)) + if (!IsValid(Slot.PlantSprite)) { - Slot.PlantStaticMesh = CreateSlotMeshComponent(this, GetRootComponent(), GetSlotComponentName(SlotCoordinate, TEXT("Plant"))); - if (IsValid(Slot.PlantStaticMesh)) - { - Slot.PlantStaticMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); - Slot.PlantStaticMesh->SetVisibility(false); - } + Slot.PlantSprite = CreatePaperSpriteComponent(this, GetRootComponent(), GetSlotComponentName(SlotCoordinate, TEXT("Plant")), DefaultPlantSpriteMaterial); } ApplySlotVisuals(SlotCoordinate, Slot); @@ -598,44 +629,50 @@ void ASeedBedActor::ApplySlotVisuals(FIntVector2 SlotCoordinate, FSeedBedSeedSlo } // PLANT ---------------------------------------------------------------------------------------- - const bool bHasPlant = SlotHasLivingPlant(Slot); - - if (!IsValid(Slot.PlantStaticMesh)) + if (!IsValid(Slot.PlantSprite)) { return; } - if (!bHasPlant) + if (!SlotHasLivingPlant(Slot)) { // A plant that died or was harvested keeps its mesh, the slot is only emptied when it is cleared for real - Slot.PlantStaticMesh->SetVisibility(false); + Slot.PlantSprite->SetVisibility(false); return; } - UStaticMesh* PlantMesh = nullptr; - FVector PlantOffset = FVector::ZeroVector; - FVector PlantScale = FVector::OneVector; + FVector PlantOffset = GetSlotRelativeLocation(SlotCoordinate) + FVector(0.f, 0.f, 75.f); + FVector PlantScale = FVector(5.f); 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); + PlantScale *= FVector(SeedData->SeedStages[StageIndex].SpriteScale); + PlantOffset += SeedData->SeedStages[StageIndex].Offset; } - // A seed without stage meshes still has to show up, the fallback keeps the plant visible - if (!PlantMesh) + Slot.PlantSprite->SetRelativeScale3D(PlantScale); + Slot.PlantSprite->SetRelativeLocation(PlantOffset); + + if (SeedData->SeedStages[StageIndex].Sprite.IsValid()) { - PlantMesh = ResolveSoftObject(DefaultPlantMesh); + Slot.PlantSprite->SetSprite(SeedData->SeedStages[StageIndex].Sprite.Get()); + Slot.PlantSprite->SetVisibility(true); + } + else + { + SeedData->SeedStages[StageIndex].Sprite.LoadAsync(FLoadSoftObjectPathAsyncDelegate::CreateWeakLambda(this, [this, SlotCoordinate](const FSoftObjectPath& Path, UObject* SpriteObj) + { + FSeedBedSeedSlot* Slot = SeedBedSeedSlotsMap.Find(SlotCoordinate); + if (!Slot || !IsValid(Slot->PlantSprite)) return; + + UPaperSprite* Sprite = Cast(SpriteObj); + if (!Sprite) return; + + Slot->PlantSprite->SetSprite(Sprite); + })); } - - Slot.PlantStaticMesh->SetStaticMesh(PlantMesh); - Slot.PlantStaticMesh->SetRelativeLocation(SlotLocation + PlantOffset); - Slot.PlantStaticMesh->SetRelativeScale3D(PlantScale); - Slot.PlantStaticMesh->SetVisibility(PlantMesh != nullptr); } void ASeedBedActor::RefreshSlotVisual(FIntVector2 SlotCoordinate) { @@ -654,7 +691,7 @@ void ASeedBedActor::RefreshAllSlotVisuals() { for (TPair& SlotPair : SeedBedSeedSlotsMap) { - ApplySlotVisuals(SlotPair.Key, SlotPair.Value); + RefreshSlotVisual(SlotPair.Key); } } diff --git a/Source/ProjectEleri/Items/SeedBedActor.h b/Source/ProjectEleri/Items/SeedBedActor.h index ea1b01bb..07d41c26 100644 --- a/Source/ProjectEleri/Items/SeedBedActor.h +++ b/Source/ProjectEleri/Items/SeedBedActor.h @@ -7,6 +7,7 @@ #include "ProjectEleri/System/TimeOfDayStruct.h" #include "SeedBedActor.generated.h" +class UPaperSpriteComponent; class ATimeOfDayManager; class UDecalComponent; class UMaterialInstanceDynamic; @@ -80,7 +81,7 @@ struct FSeedBedSeedSlot UMaterialInstanceDynamic* SeedBedDynamicMat = nullptr; UPROPERTY(VisibleAnywhere, BlueprintReadOnly) - UStaticMeshComponent* PlantStaticMesh; + UPaperSpriteComponent* PlantSprite; }; USTRUCT(BlueprintType) @@ -137,7 +138,7 @@ public: /** Fallback mesh used when the planted seed has no growth stage set up */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Seed Bed|Setup") - TSoftObjectPtr DefaultPlantMesh; + TSoftObjectPtr DefaultPlantSpriteMaterial; /** Dimensions of the grid generated by GenerateSeedBedGrid, X = columns, Y = rows */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Seed Bed|Setup") diff --git a/Source/ProjectEleri/Private/MyCharacter.cpp b/Source/ProjectEleri/Private/MyCharacter.cpp index be8570ba..4d487229 100644 --- a/Source/ProjectEleri/Private/MyCharacter.cpp +++ b/Source/ProjectEleri/Private/MyCharacter.cpp @@ -17,6 +17,7 @@ #include "Components/CapsuleComponent.h" #include "Components/StatForgeComponent.h" #include "GameFramework/SpringArmComponent.h" +#include "ProjectEleri/Components/EleriFlipbookComponent.h" #include "ProjectEleri/Items/SeedBedActor.h" #include "Subsystem/InteractionSubsystem.h" @@ -38,6 +39,8 @@ AMyCharacter::AMyCharacter() PrimaryActorTick.bCanEverTick = true; StatForgeComponent = CreateDefaultSubobject(TEXT("StatForgeComponent")); + SpriteComponent = CreateDefaultSubobject(TEXT("Sprite Component")); + SpriteComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); } bool AMyCharacter::IsEditor() const { diff --git a/Source/ProjectEleri/Private/TimeOfDayManager.cpp b/Source/ProjectEleri/Private/TimeOfDayManager.cpp index e0d262e5..df258a4a 100644 --- a/Source/ProjectEleri/Private/TimeOfDayManager.cpp +++ b/Source/ProjectEleri/Private/TimeOfDayManager.cpp @@ -469,6 +469,26 @@ float ATimeOfDayManager::GetMinutesPassedSoFar() const (GetYear() * 60.f * 24.f * 28.f * 4.f); } +FText ATimeOfDayManager::GetFormattedTime() const +{ + // AddMinutes() clamps to 0..24 / 0..60 while a step is being applied, so the displayed values are + // clamped here too and a transient "24:60" never reaches the UI. + const int32 DisplayHours = FMath::Clamp(currentHours, 0, 23); + const int32 DisplayMinutes = FMath::Clamp(static_cast(currentMinutes), 0, 59); + + // MinimumIntegralDigits pads single digit values with a leading zero, so "7:5" becomes "07:05". + FNumberFormattingOptions TwoDigitOptions; + TwoDigitOptions.SetUseGrouping(false); + TwoDigitOptions.SetMinimumIntegralDigits(2); + TwoDigitOptions.SetMaximumIntegralDigits(2); + + FFormatNamedArguments Arguments; + Arguments.Add(TEXT("Hours"), FText::AsNumber(DisplayHours, &TwoDigitOptions)); + Arguments.Add(TEXT("Minutes"), FText::AsNumber(DisplayMinutes, &TwoDigitOptions)); + + return FText::Format(NSLOCTEXT("TimeOfDayManager", "ClockTimeFormat", "{Hours}:{Minutes}"), Arguments); +} + void ATimeOfDayManager::GetDateMinusDays(int32 days, int32 &daysOut, int32 &monthsOut, int32 &yearsOut) { int32 currDays = GetDaysPassed() - days; diff --git a/Source/ProjectEleri/Public/MyCharacter.h b/Source/ProjectEleri/Public/MyCharacter.h index 6d387504..bef76418 100644 --- a/Source/ProjectEleri/Public/MyCharacter.h +++ b/Source/ProjectEleri/Public/MyCharacter.h @@ -16,6 +16,7 @@ #include "MyCharacter.generated.h" +class UEleriFlipbookComponent; class ASeedBedActor; class USpringArmComponent; class UStatForgeComponent; @@ -78,6 +79,9 @@ protected: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) UStatForgeComponent* StatForgeComponent; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UEleriFlipbookComponent* SpriteComponent; UPROPERTY() TObjectPtr CameraRef; diff --git a/Source/ProjectEleri/Public/TimeOfDayManager.h b/Source/ProjectEleri/Public/TimeOfDayManager.h index 4f27b963..91eb2ccc 100644 --- a/Source/ProjectEleri/Public/TimeOfDayManager.h +++ b/Source/ProjectEleri/Public/TimeOfDayManager.h @@ -309,6 +309,11 @@ public: UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Time of day") inline int32 GetYear() const { return currentYear; } + /** Clock time formatted for display. Both fields are always two digits wide, so a clock reading + * 7 hours and 5 minutes is shown as "07:05". */ + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Time of day") + FText GetFormattedTime() const; + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Time of day") inline int32 GetAllDaysPassed() const {