// Copyright Pacificao. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "AgrarianInteractable.h" #include "AgrarianTypes.h" #include "AgrarianWildlifeBase.generated.h" class AAgrarianGameCharacter; class UStaticMeshComponent; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAgrarianWildlifeStateChangedSignature, EAgrarianWildlifeState, NewState); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAgrarianWildlifeHealthChangedSignature, float, Health, float, MaxHealth); UCLASS(Blueprintable) class AAgrarianWildlifeBase : public ACharacter, public IAgrarianInteractable { GENERATED_BODY() public: AAgrarianWildlifeBase(); virtual void Tick(float DeltaSeconds) override; virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; UPROPERTY(BlueprintAssignable, Category = "Agrarian|Wildlife") FAgrarianWildlifeStateChangedSignature OnWildlifeStateChanged; UPROPERTY(BlueprintAssignable, Category = "Agrarian|Wildlife") FAgrarianWildlifeHealthChangedSignature OnWildlifeHealthChanged; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals") TObjectPtr WildlifeBodyProxy; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals") TObjectPtr WildlifeHeadProxy; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals") TObjectPtr WildlifeEarProxyA; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals") TObjectPtr WildlifeEarProxyB; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Wildlife|Visuals") TObjectPtr WildlifeTailProxy; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife") FName WildlifeId = TEXT("wildlife"); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife") FText DisplayName; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife") EAgrarianWildlifeDisposition Disposition = EAgrarianWildlifeDisposition::Passive; UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_WildlifeState, Category = "Agrarian|Wildlife") EAgrarianWildlifeState WildlifeState = EAgrarianWildlifeState::Idle; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife", meta = (ClampMin = "1")) float MaxHealth = 25.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_Health, Category = "Agrarian|Wildlife", meta = (ClampMin = "0")) float Health = 25.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "0")) float WanderRadius = 1200.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "0")) float WanderSpeed = 180.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "0")) float FleeSpeed = 420.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "0")) float AggroRadius = 650.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "0")) float FleeRadius = 700.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "0.1")) float DecisionIntervalSeconds = 2.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement") bool bUseNavigationMovement = true; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "1")) float NavigationAcceptanceRadius = 85.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement", meta = (ClampMin = "1")) float NavigationRepathDistance = 175.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Movement") FVector NavigationProjectionExtent = FVector(500.0f, 500.0f, 900.0f); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Performance") bool bEnablePerformanceLimits = true; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Performance", meta = (ClampMin = "0")) float FullUpdateRadius = 6500.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Performance", meta = (ClampMin = "0.1")) float FarUpdateIntervalSeconds = 2.5f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Wildlife|Harvest") TArray HarvestYields; UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Agrarian|Wildlife|Harvest") bool bHarvested = false; UFUNCTION(BlueprintCallable, Category = "Agrarian|Wildlife") bool IsAlive() const; UFUNCTION(BlueprintCallable, Category = "Agrarian|Wildlife") void ApplyWildlifeDamage(float Amount, AActor* InstigatorActor); UFUNCTION(BlueprintCallable, Category = "Agrarian|Wildlife") void SetWildlifeState(EAgrarianWildlifeState NewState); virtual FText GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const override; virtual bool CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const override; virtual void Interact_Implementation(AAgrarianGameCharacter* Interactor) override; protected: virtual void BeginPlay() override; UFUNCTION() void OnRep_Health(); UFUNCTION() void OnRep_WildlifeState(); bool ShouldRunServerThink(float DeltaSeconds); void ServerThink(float DeltaSeconds); void ChooseWanderTarget(); void MoveTowardTarget(); bool ChooseReachableWanderTarget(FVector& OutTarget) const; bool ProjectPointToNavigation(const FVector& CandidateLocation, FVector& OutProjectedLocation) const; bool RequestNavigationMove(const FVector& TargetLocation); void ClearNavigationMove(); void DirectMoveTowardLocation(const FVector& TargetLocation, float MovementSpeed); void EnterDeadState(); AAgrarianGameCharacter* FindNearestPlayer(float Radius) const; float GetNearestPlayerDistanceSquared() const; bool Harvest(AAgrarianGameCharacter* Interactor); void BroadcastHealthChanged(); void BroadcastStateChanged(); UPROPERTY() FVector SpawnLocation = FVector::ZeroVector; UPROPERTY() FVector CurrentMoveTarget = FVector::ZeroVector; UPROPERTY() TObjectPtr FocusActor; UPROPERTY() FVector LastNavigationMoveTarget = FVector::ZeroVector; bool bHasNavigationMoveTarget = false; float DecisionTimer = 0.0f; float ServerThinkAccumulator = 0.0f; };