Add Ground Zero weather exposure zones

This commit is contained in:
2026-05-16 03:08:12 -07:00
parent b5d13598f8
commit 8b0b5fff92
10 changed files with 291 additions and 5 deletions
+2
View File
@@ -103,6 +103,7 @@ void AAgrarianDebugHUD::DrawCriticalStats(const UAgrarianSurvivalComponent* Surv
DrawScaledLine(FString::Printf(TEXT("Water %3.0f"), Survival.Thirst), X, Y, CriticalStatsTextScale, StatusColor(Survival.Thirst));
DrawScaledLine(FString::Printf(TEXT("Temp %4.1f C"), Survival.BodyTemperature), X, Y, CriticalStatsTextScale, Survival.BodyTemperature < 35.0f ? CriticalColor : StableColor);
DrawScaledLine(FString::Printf(TEXT("Shelter %3.0f%%"), SurvivalComponent->CurrentWeatherProtection * 100.0f), X, Y, CriticalStatsTextScale, SurvivalComponent->CurrentWeatherProtection > 0.0f ? StableColor : WarningColor);
DrawScaledLine(FString::Printf(TEXT("Expose x%.2f %+3.1f C"), SurvivalComponent->CurrentWeatherExposureMultiplier, SurvivalComponent->CurrentWeatherTemperatureOffsetC), X, Y, CriticalStatsTextScale, SurvivalComponent->CurrentWeatherExposureMultiplier > 1.0f ? WarningColor : StableColor);
DrawScaledLine(FString::Printf(TEXT("Exhaust %3.0f"), Survival.Exhaustion), X, Y, CriticalStatsTextScale, StatusColor(Survival.Exhaustion, true));
DrawScaledLine(FString::Printf(TEXT("Injury %3.0f"), Survival.InjurySeverity), X, Y, CriticalStatsTextScale, StatusColor(Survival.InjurySeverity, true));
DrawScaledLine(FString::Printf(TEXT("Sickness %3.0f"), Survival.SicknessSeverity), X, Y, CriticalStatsTextScale, StatusColor(Survival.SicknessSeverity, true));
@@ -173,6 +174,7 @@ void AAgrarianDebugHUD::DrawSurvival(const UAgrarianSurvivalComponent* SurvivalC
DrawLine(FString::Printf(TEXT("Thirst: %.0f"), Survival.Thirst), X, Y);
DrawLine(FString::Printf(TEXT("Temp: %.1f C"), Survival.BodyTemperature), X, Y);
DrawLine(FString::Printf(TEXT("Shelter: %.0f%%"), SurvivalComponent->CurrentWeatherProtection * 100.0f), X, Y);
DrawLine(FString::Printf(TEXT("Expose: x%.2f %+3.1f C"), SurvivalComponent->CurrentWeatherExposureMultiplier, SurvivalComponent->CurrentWeatherTemperatureOffsetC), X, Y);
DrawLine(FString::Printf(TEXT("Injury: %.0f"), Survival.InjurySeverity), X, Y);
DrawLine(FString::Printf(TEXT("Sick: %.0f"), Survival.SicknessSeverity), X, Y);
const FAgrarianCareHistorySnapshot& Care = SurvivalComponent->CareHistory;
@@ -3,6 +3,7 @@
#include "AgrarianSurvivalComponent.h"
#include "AgrarianGameState.h"
#include "AgrarianShelterActor.h"
#include "AgrarianWeatherExposureZone.h"
#include "Components/BoxComponent.h"
#include "Engine/World.h"
#include "Net/UnrealNetwork.h"
@@ -35,6 +36,8 @@ void UAgrarianSurvivalComponent::TickComponent(float DeltaTime, ELevelTick TickT
Survival.Thirst -= ThirstDecayPerMinute * Minutes;
Survival.Stamina += StaminaRecoveryPerSecond * DeltaTime;
CurrentWeatherProtection = CalculateCurrentWeatherProtection();
CurrentWeatherExposureMultiplier = CalculateCurrentWeatherExposureMultiplier();
CurrentWeatherTemperatureOffsetC = CalculateCurrentWeatherTemperatureOffsetC();
CareHistory.ShelterQuality = FMath::FInterpTo(CareHistory.ShelterQuality, CurrentWeatherProtection, DeltaTime, 0.02f);
if (Survival.Stamina <= LowStaminaExhaustionThreshold)
@@ -62,7 +65,8 @@ void UAgrarianSurvivalComponent::TickComponent(float DeltaTime, ELevelTick TickT
if (const AAgrarianGameState* AgrarianGameState = World->GetGameState<AAgrarianGameState>())
{
const float ExposureProtectionMultiplier = 1.0f - FMath::Clamp(CurrentWeatherProtection, 0.0f, 1.0f);
const float ExposureDelta = (AgrarianGameState->AmbientTemperatureC - 18.0f) * 0.002f * DeltaTime * ExposureProtectionMultiplier;
const float EffectiveAmbientTemperatureC = AgrarianGameState->AmbientTemperatureC + CurrentWeatherTemperatureOffsetC;
const float ExposureDelta = (EffectiveAmbientTemperatureC - 18.0f) * 0.002f * DeltaTime * ExposureProtectionMultiplier * CurrentWeatherExposureMultiplier;
Survival.BodyTemperature += FMath::Clamp(ExposureDelta, -0.035f, 0.02f);
}
}
@@ -79,7 +83,7 @@ void UAgrarianSurvivalComponent::TickComponent(float DeltaTime, ELevelTick TickT
if (Survival.BodyTemperature < 35.0f)
{
Survival.Health -= ColdDamagePerMinute * Minutes * (1.0f - FMath::Clamp(CurrentWeatherProtection, 0.0f, 1.0f));
Survival.Health -= ColdDamagePerMinute * Minutes * (1.0f - FMath::Clamp(CurrentWeatherProtection, 0.0f, 1.0f)) * CurrentWeatherExposureMultiplier;
}
if (Survival.SicknessSeverity >= 60.0f)
@@ -98,6 +102,8 @@ void UAgrarianSurvivalComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProp
DOREPLIFETIME(UAgrarianSurvivalComponent, Survival);
DOREPLIFETIME(UAgrarianSurvivalComponent, CareHistory);
DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherProtection);
DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherExposureMultiplier);
DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherTemperatureOffsetC);
}
bool UAgrarianSurvivalComponent::IsAlive() const
@@ -261,6 +267,66 @@ float UAgrarianSurvivalComponent::CalculateCurrentWeatherProtection() const
return BestProtection;
}
float UAgrarianSurvivalComponent::CalculateCurrentWeatherExposureMultiplier() const
{
const AActor* Owner = GetOwner();
if (!Owner)
{
return 1.0f;
}
TArray<AActor*> OverlappingZoneActors;
Owner->GetOverlappingActors(OverlappingZoneActors, AAgrarianWeatherExposureZone::StaticClass());
float StrongestMultiplierDelta = 0.0f;
for (const AActor* Actor : OverlappingZoneActors)
{
const AAgrarianWeatherExposureZone* Zone = Cast<AAgrarianWeatherExposureZone>(Actor);
if (!Zone || !Zone->ExposureVolume || !Zone->ExposureVolume->IsOverlappingActor(Owner))
{
continue;
}
const float ZoneDelta = FMath::Clamp(Zone->ExposureMultiplier, 0.0f, 3.0f) - 1.0f;
if (FMath::Abs(ZoneDelta) > FMath::Abs(StrongestMultiplierDelta))
{
StrongestMultiplierDelta = ZoneDelta;
}
}
return FMath::Clamp(1.0f + StrongestMultiplierDelta, 0.0f, 3.0f);
}
float UAgrarianSurvivalComponent::CalculateCurrentWeatherTemperatureOffsetC() const
{
const AActor* Owner = GetOwner();
if (!Owner)
{
return 0.0f;
}
TArray<AActor*> OverlappingZoneActors;
Owner->GetOverlappingActors(OverlappingZoneActors, AAgrarianWeatherExposureZone::StaticClass());
float StrongestOffset = 0.0f;
for (const AActor* Actor : OverlappingZoneActors)
{
const AAgrarianWeatherExposureZone* Zone = Cast<AAgrarianWeatherExposureZone>(Actor);
if (!Zone || !Zone->ExposureVolume || !Zone->ExposureVolume->IsOverlappingActor(Owner))
{
continue;
}
const float ZoneOffset = FMath::Clamp(Zone->TemperatureOffsetC, -20.0f, 20.0f);
if (FMath::Abs(ZoneOffset) > FMath::Abs(StrongestOffset))
{
StrongestOffset = ZoneOffset;
}
}
return StrongestOffset;
}
void UAgrarianSurvivalComponent::OnRep_Survival()
{
BroadcastSurvivalChanged();
@@ -33,6 +33,12 @@ public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = "Agrarian|Survival|Shelter")
float CurrentWeatherProtection = 0.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = "Agrarian|Survival|Weather Exposure")
float CurrentWeatherExposureMultiplier = 1.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = "Agrarian|Survival|Weather Exposure")
float CurrentWeatherTemperatureOffsetC = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Survival|Rates", meta = (ClampMin = "0"))
float HungerDecayPerMinute = 0.55f;
@@ -108,6 +114,12 @@ public:
UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Shelter")
float CalculateCurrentWeatherProtection() const;
UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Weather Exposure")
float CalculateCurrentWeatherExposureMultiplier() const;
UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Weather Exposure")
float CalculateCurrentWeatherTemperatureOffsetC() const;
protected:
UFUNCTION()
void OnRep_Survival();
@@ -0,0 +1,14 @@
// Copyright Pacificao. All Rights Reserved.
#include "AgrarianWeatherExposureZone.h"
#include "Components/BoxComponent.h"
AAgrarianWeatherExposureZone::AAgrarianWeatherExposureZone()
{
bReplicates = true;
ExposureVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("ExposureVolume"));
RootComponent = ExposureVolume;
ExposureVolume->SetBoxExtent(FVector(500.0f, 500.0f, 250.0f));
ExposureVolume->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
}
@@ -0,0 +1,30 @@
// Copyright Pacificao. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AgrarianWeatherExposureZone.generated.h"
class UBoxComponent;
UCLASS(Blueprintable)
class AAgrarianWeatherExposureZone : public AActor
{
GENERATED_BODY()
public:
AAgrarianWeatherExposureZone();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather Exposure")
TObjectPtr<UBoxComponent> ExposureVolume;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Exposure", meta = (ClampMin = "0", ClampMax = "3"))
float ExposureMultiplier = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Exposure", meta = (ClampMin = "-20", ClampMax = "20"))
float TemperatureOffsetC = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather Exposure")
FName ExposureZoneId = NAME_None;
};