252 lines
7.4 KiB
C++
252 lines
7.4 KiB
C++
// Copyright Pacificao. All Rights Reserved.
|
|
|
|
#include "AgrarianResourceNode.h"
|
|
#include "AgrarianGameCharacter.h"
|
|
#include "AgrarianInventoryComponent.h"
|
|
#include "AgrarianItemDefinitionAsset.h"
|
|
#include "AgrarianSaveGame.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Engine/StaticMesh.h"
|
|
#include "Materials/MaterialInterface.h"
|
|
#include "TimerManager.h"
|
|
#include "Net/UnrealNetwork.h"
|
|
#include "UObject/ConstructorHelpers.h"
|
|
|
|
AAgrarianResourceNode::AAgrarianResourceNode()
|
|
{
|
|
bReplicates = true;
|
|
NetCullDistanceSquared = FMath::Square(4500.0f);
|
|
|
|
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
|
|
RootComponent = Mesh;
|
|
Mesh->SetCollisionProfileName(TEXT("BlockAll"));
|
|
|
|
static ConstructorHelpers::FObjectFinder<UStaticMesh> ChamferCubeMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_ChamferCube.SM_AGR_Placeholder_ChamferCube"));
|
|
static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderMesh(TEXT("/Game/Agrarian/Environment/PlaceholderMeshes/SM_AGR_Placeholder_Cylinder.SM_AGR_Placeholder_Cylinder"));
|
|
static ConstructorHelpers::FObjectFinder<UMaterialInterface> WoodMaterial(TEXT("/Game/Agrarian/Materials/M_AGR_GZ_Wood_Resource.M_AGR_GZ_Wood_Resource"));
|
|
static ConstructorHelpers::FObjectFinder<UMaterialInterface> FiberMaterial(TEXT("/Game/Agrarian/Materials/M_AGR_GZ_Fiber_Resource.M_AGR_GZ_Fiber_Resource"));
|
|
|
|
if (ChamferCubeMesh.Succeeded())
|
|
{
|
|
Mesh->SetStaticMesh(ChamferCubeMesh.Object);
|
|
Mesh->SetRelativeScale3D(FVector(0.8f, 0.58f, 0.44f));
|
|
}
|
|
if (WoodMaterial.Succeeded())
|
|
{
|
|
Mesh->SetMaterial(0, WoodMaterial.Object);
|
|
}
|
|
|
|
ResourceClusterProxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ResourceClusterProxy"));
|
|
ResourceClusterProxy->SetupAttachment(RootComponent);
|
|
ResourceClusterProxy->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
ResourceClusterProxy->SetGenerateOverlapEvents(false);
|
|
if (CylinderMesh.Succeeded())
|
|
{
|
|
ResourceClusterProxy->SetStaticMesh(CylinderMesh.Object);
|
|
}
|
|
if (WoodMaterial.Succeeded())
|
|
{
|
|
ResourceClusterProxy->SetMaterial(0, WoodMaterial.Object);
|
|
}
|
|
ResourceClusterProxy->SetRelativeLocation(FVector(0.0f, -18.0f, 28.0f));
|
|
ResourceClusterProxy->SetRelativeRotation(FRotator(0.0f, 90.0f, 90.0f));
|
|
ResourceClusterProxy->SetRelativeScale3D(FVector(0.12f, 0.12f, 0.7f));
|
|
|
|
HarvestableMarkerProxy = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("HarvestableMarkerProxy"));
|
|
HarvestableMarkerProxy->SetupAttachment(RootComponent);
|
|
HarvestableMarkerProxy->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
HarvestableMarkerProxy->SetGenerateOverlapEvents(false);
|
|
if (ChamferCubeMesh.Succeeded())
|
|
{
|
|
HarvestableMarkerProxy->SetStaticMesh(ChamferCubeMesh.Object);
|
|
}
|
|
if (FiberMaterial.Succeeded())
|
|
{
|
|
HarvestableMarkerProxy->SetMaterial(0, FiberMaterial.Object);
|
|
}
|
|
HarvestableMarkerProxy->SetRelativeLocation(FVector(0.0f, 22.0f, 36.0f));
|
|
HarvestableMarkerProxy->SetRelativeScale3D(FVector(0.28f, 0.18f, 0.22f));
|
|
|
|
YieldItem.ItemId = TEXT("wood");
|
|
YieldItem.DisplayName = FText::FromString(TEXT("Wood"));
|
|
YieldItem.Quantity = 1;
|
|
YieldItem.UnitWeight = 1.0f;
|
|
}
|
|
|
|
void AAgrarianResourceNode::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
if (HasAuthority())
|
|
{
|
|
MaxHarvests = FMath::Max(1, MaxHarvests);
|
|
if (RemainingHarvests > MaxHarvests)
|
|
{
|
|
MaxHarvests = RemainingHarvests;
|
|
}
|
|
}
|
|
|
|
UpdateDepletedState();
|
|
ScheduleRespawnIfNeeded();
|
|
}
|
|
|
|
void AAgrarianResourceNode::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
DOREPLIFETIME(AAgrarianResourceNode, RemainingHarvests);
|
|
}
|
|
|
|
FText AAgrarianResourceNode::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
|
|
{
|
|
if (RemainingHarvests <= 0)
|
|
{
|
|
return FText::FromString(TEXT("Depleted"));
|
|
}
|
|
|
|
if (RequiredToolItemId != NAME_None && !HasRequiredTool(Interactor) && bAllowBareHandGathering)
|
|
{
|
|
return FText::FromString(TEXT("Gather by hand"));
|
|
}
|
|
|
|
return FText::FromString(TEXT("Gather"));
|
|
}
|
|
|
|
bool AAgrarianResourceNode::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
|
|
{
|
|
if (RemainingHarvests <= 0 || !Interactor)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return RequiredToolItemId == NAME_None || bAllowBareHandGathering || HasRequiredTool(Interactor);
|
|
}
|
|
|
|
void AAgrarianResourceNode::Interact_Implementation(AAgrarianGameCharacter* Interactor)
|
|
{
|
|
if (!HasAuthority() || !Interactor || RemainingHarvests <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (UAgrarianInventoryComponent* Inventory = Interactor->GetInventoryComponent())
|
|
{
|
|
const FAgrarianItemStack Granted = MakeYieldStack(Interactor);
|
|
if (Inventory->AddItem(Granted))
|
|
{
|
|
RemainingHarvests--;
|
|
UpdateDepletedState();
|
|
ScheduleRespawnIfNeeded();
|
|
}
|
|
}
|
|
}
|
|
|
|
void AAgrarianResourceNode::OnRep_RemainingHarvests()
|
|
{
|
|
UpdateDepletedState();
|
|
}
|
|
|
|
FName AAgrarianResourceNode::GetResourcePersistenceId() const
|
|
{
|
|
return PersistenceNodeId != NAME_None ? PersistenceNodeId : GetFName();
|
|
}
|
|
|
|
FAgrarianSavedResourceNode AAgrarianResourceNode::CaptureResourceSaveState() const
|
|
{
|
|
FAgrarianSavedResourceNode SavedNode;
|
|
SavedNode.ResourceNodeId = GetResourcePersistenceId();
|
|
SavedNode.RemainingHarvests = RemainingHarvests;
|
|
SavedNode.bRespawnsForMvp = bRespawnsForMvp;
|
|
return SavedNode;
|
|
}
|
|
|
|
void AAgrarianResourceNode::ApplyResourceSaveState(const FAgrarianSavedResourceNode& SavedNode)
|
|
{
|
|
if (!HasAuthority() || SavedNode.ResourceNodeId == NAME_None || SavedNode.ResourceNodeId != GetResourcePersistenceId())
|
|
{
|
|
return;
|
|
}
|
|
|
|
RemainingHarvests = FMath::Clamp(SavedNode.RemainingHarvests, 0, FMath::Max(1, MaxHarvests));
|
|
if (RemainingHarvests > 0)
|
|
{
|
|
if (UWorld* World = GetWorld())
|
|
{
|
|
World->GetTimerManager().ClearTimer(RespawnTimerHandle);
|
|
}
|
|
}
|
|
|
|
UpdateDepletedState();
|
|
ScheduleRespawnIfNeeded();
|
|
}
|
|
|
|
bool AAgrarianResourceNode::HasRequiredTool(const AAgrarianGameCharacter* Interactor) const
|
|
{
|
|
if (RequiredToolItemId == NAME_None)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const UAgrarianInventoryComponent* Inventory = Interactor ? Interactor->GetInventoryComponent() : nullptr;
|
|
return Inventory && Inventory->HasItem(RequiredToolItemId, 1);
|
|
}
|
|
|
|
int32 AAgrarianResourceNode::GetHarvestQuantityFor(const AAgrarianGameCharacter* Interactor) const
|
|
{
|
|
const int32 ToolBonus = HasRequiredTool(Interactor) ? FMath::Max(0, ToolQuantityBonus) : 0;
|
|
return FMath::Max(1, QuantityPerHarvest + ToolBonus);
|
|
}
|
|
|
|
FAgrarianItemStack AAgrarianResourceNode::MakeYieldStack(const AAgrarianGameCharacter* Interactor) const
|
|
{
|
|
const int32 HarvestQuantity = GetHarvestQuantityFor(Interactor);
|
|
if (YieldItemDefinition)
|
|
{
|
|
return YieldItemDefinition->MakeStack(HarvestQuantity);
|
|
}
|
|
|
|
FAgrarianItemStack Granted = YieldItem;
|
|
Granted.Quantity = HarvestQuantity;
|
|
return Granted;
|
|
}
|
|
|
|
void AAgrarianResourceNode::UpdateDepletedState()
|
|
{
|
|
if (Mesh)
|
|
{
|
|
Mesh->SetVisibility(RemainingHarvests > 0, true);
|
|
Mesh->SetCollisionEnabled(RemainingHarvests > 0 ? ECollisionEnabled::QueryAndPhysics : ECollisionEnabled::NoCollision);
|
|
}
|
|
}
|
|
|
|
void AAgrarianResourceNode::ScheduleRespawnIfNeeded()
|
|
{
|
|
if (!HasAuthority() || !bRespawnsForMvp || RemainingHarvests > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UWorld* World = GetWorld();
|
|
if (!World || World->GetTimerManager().IsTimerActive(RespawnTimerHandle))
|
|
{
|
|
return;
|
|
}
|
|
|
|
World->GetTimerManager().SetTimer(
|
|
RespawnTimerHandle,
|
|
this,
|
|
&AAgrarianResourceNode::RespawnNode,
|
|
FMath::Max(1.0f, RespawnDelaySeconds),
|
|
false);
|
|
}
|
|
|
|
void AAgrarianResourceNode::RespawnNode()
|
|
{
|
|
if (!HasAuthority() || !bRespawnsForMvp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RemainingHarvests = FMath::Max(1, MaxHarvests);
|
|
UpdateDepletedState();
|
|
}
|