Add replicated wildlife base actor
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
// Copyright Pacificao. All Rights Reserved.
|
||||
|
||||
#include "AgrarianWildlifeBase.h"
|
||||
#include "AgrarianGameCharacter.h"
|
||||
#include "AgrarianInventoryComponent.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
AAgrarianWildlifeBase::AAgrarianWildlifeBase()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
bReplicates = true;
|
||||
|
||||
GetCharacterMovement()->MaxWalkSpeed = WanderSpeed;
|
||||
GetCharacterMovement()->bOrientRotationToMovement = true;
|
||||
bUseControllerRotationYaw = false;
|
||||
|
||||
DisplayName = FText::FromString(TEXT("Wildlife"));
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
SpawnLocation = GetActorLocation();
|
||||
Health = FMath::Clamp(Health, 0.0f, MaxHealth);
|
||||
ChooseWanderTarget();
|
||||
BroadcastHealthChanged();
|
||||
BroadcastStateChanged();
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
|
||||
if (HasAuthority())
|
||||
{
|
||||
ServerThink(DeltaSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(AAgrarianWildlifeBase, WildlifeState);
|
||||
DOREPLIFETIME(AAgrarianWildlifeBase, Health);
|
||||
DOREPLIFETIME(AAgrarianWildlifeBase, bHarvested);
|
||||
}
|
||||
|
||||
bool AAgrarianWildlifeBase::IsAlive() const
|
||||
{
|
||||
return WildlifeState != EAgrarianWildlifeState::Dead && Health > 0.0f;
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::ApplyWildlifeDamage(float Amount, AActor* InstigatorActor)
|
||||
{
|
||||
if (!HasAuthority() || !IsAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Health = FMath::Clamp(Health - FMath::Max(0.0f, Amount), 0.0f, MaxHealth);
|
||||
FocusActor = InstigatorActor;
|
||||
BroadcastHealthChanged();
|
||||
|
||||
if (Health <= 0.0f)
|
||||
{
|
||||
EnterDeadState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Disposition == EAgrarianWildlifeDisposition::Passive || Disposition == EAgrarianWildlifeDisposition::Fleeing)
|
||||
{
|
||||
SetWildlifeState(EAgrarianWildlifeState::Fleeing);
|
||||
}
|
||||
else if (Disposition == EAgrarianWildlifeDisposition::Aggressive)
|
||||
{
|
||||
SetWildlifeState(EAgrarianWildlifeState::Chasing);
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::SetWildlifeState(EAgrarianWildlifeState NewState)
|
||||
{
|
||||
if (WildlifeState == NewState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WildlifeState = NewState;
|
||||
BroadcastStateChanged();
|
||||
}
|
||||
|
||||
FText AAgrarianWildlifeBase::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
if (WildlifeState == EAgrarianWildlifeState::Dead)
|
||||
{
|
||||
return bHarvested ? FText::FromString(TEXT("Harvested")) : FText::FromString(TEXT("Harvest"));
|
||||
}
|
||||
|
||||
return DisplayName;
|
||||
}
|
||||
|
||||
bool AAgrarianWildlifeBase::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
|
||||
{
|
||||
return Interactor && WildlifeState == EAgrarianWildlifeState::Dead && !bHarvested;
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::Interact_Implementation(AAgrarianGameCharacter* Interactor)
|
||||
{
|
||||
if (HasAuthority())
|
||||
{
|
||||
Harvest(Interactor);
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::OnRep_Health()
|
||||
{
|
||||
BroadcastHealthChanged();
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::OnRep_WildlifeState()
|
||||
{
|
||||
BroadcastStateChanged();
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::ServerThink(float DeltaSeconds)
|
||||
{
|
||||
if (!IsAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DecisionTimer -= DeltaSeconds;
|
||||
if (DecisionTimer <= 0.0f)
|
||||
{
|
||||
DecisionTimer = DecisionIntervalSeconds;
|
||||
|
||||
if (Disposition == EAgrarianWildlifeDisposition::Aggressive)
|
||||
{
|
||||
FocusActor = FindNearestPlayer(AggroRadius);
|
||||
SetWildlifeState(FocusActor ? EAgrarianWildlifeState::Chasing : EAgrarianWildlifeState::Wandering);
|
||||
}
|
||||
else
|
||||
{
|
||||
FocusActor = FindNearestPlayer(FleeRadius);
|
||||
SetWildlifeState(FocusActor ? EAgrarianWildlifeState::Fleeing : EAgrarianWildlifeState::Wandering);
|
||||
}
|
||||
|
||||
if (WildlifeState == EAgrarianWildlifeState::Wandering && FVector::DistSquared(GetActorLocation(), CurrentMoveTarget) < FMath::Square(125.0f))
|
||||
{
|
||||
ChooseWanderTarget();
|
||||
}
|
||||
}
|
||||
|
||||
MoveTowardTarget();
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::ChooseWanderTarget()
|
||||
{
|
||||
const FVector RandomOffset = FVector(
|
||||
FMath::FRandRange(-WanderRadius, WanderRadius),
|
||||
FMath::FRandRange(-WanderRadius, WanderRadius),
|
||||
0.0f);
|
||||
|
||||
CurrentMoveTarget = SpawnLocation + RandomOffset;
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::MoveTowardTarget()
|
||||
{
|
||||
FVector DesiredDirection = FVector::ZeroVector;
|
||||
|
||||
if (WildlifeState == EAgrarianWildlifeState::Fleeing && FocusActor)
|
||||
{
|
||||
DesiredDirection = GetActorLocation() - FocusActor->GetActorLocation();
|
||||
GetCharacterMovement()->MaxWalkSpeed = FleeSpeed;
|
||||
}
|
||||
else if (WildlifeState == EAgrarianWildlifeState::Chasing && FocusActor)
|
||||
{
|
||||
DesiredDirection = FocusActor->GetActorLocation() - GetActorLocation();
|
||||
GetCharacterMovement()->MaxWalkSpeed = FleeSpeed;
|
||||
}
|
||||
else if (WildlifeState == EAgrarianWildlifeState::Wandering)
|
||||
{
|
||||
DesiredDirection = CurrentMoveTarget - GetActorLocation();
|
||||
GetCharacterMovement()->MaxWalkSpeed = WanderSpeed;
|
||||
}
|
||||
|
||||
DesiredDirection.Z = 0.0f;
|
||||
if (!DesiredDirection.IsNearlyZero())
|
||||
{
|
||||
AddMovementInput(DesiredDirection.GetSafeNormal());
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::EnterDeadState()
|
||||
{
|
||||
Health = 0.0f;
|
||||
SetWildlifeState(EAgrarianWildlifeState::Dead);
|
||||
GetCharacterMovement()->StopMovementImmediately();
|
||||
GetCharacterMovement()->DisableMovement();
|
||||
SetLifeSpan(0.0f);
|
||||
}
|
||||
|
||||
AAgrarianGameCharacter* AAgrarianWildlifeBase::FindNearestPlayer(float Radius) const
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AAgrarianGameCharacter* NearestCharacter = nullptr;
|
||||
float BestDistanceSq = FMath::Square(Radius);
|
||||
|
||||
TArray<AActor*> PlayerPawns;
|
||||
UGameplayStatics::GetAllActorsOfClass(World, AAgrarianGameCharacter::StaticClass(), PlayerPawns);
|
||||
for (AActor* Actor : PlayerPawns)
|
||||
{
|
||||
AAgrarianGameCharacter* Candidate = Cast<AAgrarianGameCharacter>(Actor);
|
||||
if (!Candidate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const float DistanceSq = FVector::DistSquared(GetActorLocation(), Candidate->GetActorLocation());
|
||||
if (DistanceSq < BestDistanceSq)
|
||||
{
|
||||
BestDistanceSq = DistanceSq;
|
||||
NearestCharacter = Candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return NearestCharacter;
|
||||
}
|
||||
|
||||
bool AAgrarianWildlifeBase::Harvest(AAgrarianGameCharacter* Interactor)
|
||||
{
|
||||
if (!Interactor || WildlifeState != EAgrarianWildlifeState::Dead || bHarvested)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UAgrarianInventoryComponent* Inventory = Interactor->GetInventoryComponent();
|
||||
if (!Inventory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FAgrarianItemStack& Yield : HarvestYields)
|
||||
{
|
||||
if (Yield.IsValidStack())
|
||||
{
|
||||
Inventory->AddItem(Yield);
|
||||
}
|
||||
}
|
||||
|
||||
bHarvested = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::BroadcastHealthChanged()
|
||||
{
|
||||
OnWildlifeHealthChanged.Broadcast(Health, MaxHealth);
|
||||
}
|
||||
|
||||
void AAgrarianWildlifeBase::BroadcastStateChanged()
|
||||
{
|
||||
OnWildlifeStateChanged.Broadcast(WildlifeState);
|
||||
}
|
||||
Reference in New Issue
Block a user