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
+1 -1
View File
@@ -459,7 +459,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
- [x] Replace grey-box environment presentation with an MVP natural environment pass: terrain material, grass, shrubs, bushes, trees, water-source visuals, and clearer Ground Zero biome dressing. Added repeatable Ground Zero environment material generation, applied terrain/foliage/resource/water materials in the map setup, regenerated the demo map, documented the pass, and added verification for material assets plus map assignments. - [x] Replace grey-box environment presentation with an MVP natural environment pass: terrain material, grass, shrubs, bushes, trees, water-source visuals, and clearer Ground Zero biome dressing. Added repeatable Ground Zero environment material generation, applied terrain/foliage/resource/water materials in the map setup, regenerated the demo map, documented the pass, and added verification for material assets plus map assignments.
- [x] Add first-pass environment asset variation so trees, bushes, grass, resource nodes, and water do not read as repeated placeholders. Added repeatable labeled variation actors for tree canopies/trunks, rounded bushes, grass mats, rock slabs, and a freshwater surface using multiple prototype mesh silhouettes, unique scales, rotations, and Ground Zero material families; extended the natural-environment verifier to require variation coverage. - [x] Add first-pass environment asset variation so trees, bushes, grass, resource nodes, and water do not read as repeated placeholders. Added repeatable labeled variation actors for tree canopies/trunks, rounded bushes, grass mats, rock slabs, and a freshwater surface using multiple prototype mesh silhouettes, unique scales, rotations, and Ground Zero material families; extended the natural-environment verifier to require variation coverage.
- [x] Replace `LevelPrototyping` cube/cylinder mesh dependencies in Agrarian setup scripts and prototype Blueprints with Agrarian-native placeholder environment meshes. Added repeatable Agrarian-native placeholder mesh assets under `/Game/Agrarian/Environment/PlaceholderMeshes`, updated playable Blueprint and Ground Zero setup scripts to use those native paths, regenerated affected Blueprints/map content, and added verification that Blueprint, foliage, and variation meshes no longer point at template mesh paths. - [x] Replace `LevelPrototyping` cube/cylinder mesh dependencies in Agrarian setup scripts and prototype Blueprints with Agrarian-native placeholder environment meshes. Added repeatable Agrarian-native placeholder mesh assets under `/Game/Agrarian/Environment/PlaceholderMeshes`, updated playable Blueprint and Ground Zero setup scripts to use those native paths, regenerated affected Blueprints/map content, and added verification that Blueprint, foliage, and variation meshes no longer point at template mesh paths.
- [ ] Add weather exposure zones if needed. - [x] Add weather exposure zones if needed. Added native `AAgrarianWeatherExposureZone` volumes with exposure multipliers and temperature offsets, wired survival to apply the strongest overlapping zone after shelter protection, exposed current zone effects on the dev HUD, placed three Ground Zero zones for ridge, coastal-wind, and drainage cooling cases, and added verification for zone placement plus docs.
- [ ] Add landmark or ruin placeholder. - [ ] Add landmark or ruin placeholder.
- [ ] Add spawn area with validation that the player spawns above sea level, above terrain by a safe offset, away from water, away from steep slopes, away from dense resource clusters, and with a known safe fallback coordinate. - [ ] Add spawn area with validation that the player spawns above sea level, above terrain by a safe offset, away from water, away from steep slopes, away from dense resource clusters, and with a known safe fallback coordinate.
- [ ] Add performance profiling markers. - [ ] Add performance profiling markers.
Binary file not shown.
+9
View File
@@ -155,6 +155,15 @@ percentage, and trend the care-history shelter quality field toward the active
protection level. The dev HUD shows current shelter protection so weather protection level. The dev HUD shows current shelter protection so weather
pressure can be tuned during MVP tests. pressure can be tuned during MVP tests.
Weather exposure zones use `AAgrarianWeatherExposureZone` volumes placed by the
Ground Zero setup script. They describe local ridge, coastal-wind, and drainage
cooling effects with an exposure multiplier and temperature offset. Server-side
survival ticks select the strongest overlapping zone effect, replicate the
current multiplier and offset, then apply them to ambient body-temperature drift
and cold damage after shelter protection. This lets future generated tiles add
biome, slope, elevation, hydrology, and coastal modifiers without changing the
core survival calculation.
First-pass sky and lighting use `AAgrarianSkyLightingController`. The controller First-pass sky and lighting use `AAgrarianSkyLightingController`. The controller
owns movable sun, skylight, and exponential-height-fog components and reads the owns movable sun, skylight, and exponential-height-fog components and reads the
replicated `AAgrarianGameState` time, active tile sunrise/sunset, weather state, replicated `AAgrarianGameState` time, active tile sunrise/sunset, weather state,
+57
View File
@@ -258,6 +258,36 @@ WATER_SOURCE_ACTORS = [
}, },
] ]
WEATHER_EXPOSURE_ZONES = [
{
"label": "AGR_GZ_WeatherExposure_Ridge_01",
"zone_id": "ground_zero_ridge_exposed",
"location_xy": unreal.Vector(36000.0, 29200.0, 0.0),
"z_offset": 450.0,
"extent": unreal.Vector(11500.0, 9200.0, 2200.0),
"exposure_multiplier": 1.45,
"temperature_offset_c": -1.8,
},
{
"label": "AGR_GZ_WeatherExposure_CoastalWind_01",
"zone_id": "ground_zero_coastal_wind",
"location_xy": unreal.Vector(-31500.0, -14600.0, 0.0),
"z_offset": 350.0,
"extent": unreal.Vector(10500.0, 7600.0, 1600.0),
"exposure_multiplier": 1.25,
"temperature_offset_c": -0.9,
},
{
"label": "AGR_GZ_WeatherExposure_DrainageCool_01",
"zone_id": "ground_zero_drainage_cool",
"location_xy": unreal.Vector(-7200.0, 10400.0, 0.0),
"z_offset": 260.0,
"extent": unreal.Vector(7200.0, 5400.0, 1100.0),
"exposure_multiplier": 1.1,
"temperature_offset_c": -1.2,
},
]
ENVIRONMENT_VARIATION_ACTORS = [ ENVIRONMENT_VARIATION_ACTORS = [
{ {
"label": "AGR_GZ_EnvVar_Tree_Canopy_01", "label": "AGR_GZ_EnvVar_Tree_Canopy_01",
@@ -690,6 +720,30 @@ def spawn_environment_variation_actor(spec, height_values, materials):
return actor return actor
def spawn_weather_exposure_zone(spec, height_values):
location_xy = spec["location_xy"]
z = terrain_z_cm(height_values, location_xy.x, location_xy.y) + spec.get("z_offset", 0.0)
actor = unreal.AgrarianEditorAutomationLibrary.spawn_actor_in_editor_world(
unreal.AgrarianWeatherExposureZone,
unreal.Vector(location_xy.x, location_xy.y, z),
unreal.Rotator(0.0, 0.0, 0.0),
spec["label"],
)
if not actor:
raise RuntimeError(f"Could not spawn {spec['label']}")
set_actor_label(actor, spec["label"])
actor.set_editor_property("exposure_zone_id", spec["zone_id"])
actor.set_editor_property("exposure_multiplier", spec["exposure_multiplier"])
actor.set_editor_property("temperature_offset_c", spec["temperature_offset_c"])
actor.exposure_volume.set_box_extent(spec["extent"], True)
unreal.log(
f"Placed {spec['label']} at {actor.get_actor_location()} "
f"exposure x{spec['exposure_multiplier']} temp {spec['temperature_offset_c']} C"
)
return actor
def main(): def main():
if not unreal.EditorLevelLibrary.load_level(MAP_PATH): if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
raise RuntimeError(f"Could not load map: {MAP_PATH}") raise RuntimeError(f"Could not load map: {MAP_PATH}")
@@ -700,6 +754,7 @@ def main():
labels.update(LEGACY_DEMO_LIGHTING_LABELS) labels.update(LEGACY_DEMO_LIGHTING_LABELS)
labels.update(spec["label"] for spec in BIOME_RESOURCE_ACTORS) labels.update(spec["label"] for spec in BIOME_RESOURCE_ACTORS)
labels.update(spec["label"] for spec in WATER_SOURCE_ACTORS) labels.update(spec["label"] for spec in WATER_SOURCE_ACTORS)
labels.update(spec["label"] for spec in WEATHER_EXPOSURE_ZONES)
labels.update(spec["label"] for spec in ENVIRONMENT_VARIATION_ACTORS) labels.update(spec["label"] for spec in ENVIRONMENT_VARIATION_ACTORS)
labels.add(FOLIAGE_LABEL) labels.add(FOLIAGE_LABEL)
remove_existing_demo_actors(labels) remove_existing_demo_actors(labels)
@@ -712,6 +767,8 @@ def main():
spawn_demo_actor(spec, height_values, materials) spawn_demo_actor(spec, height_values, materials)
for spec in WATER_SOURCE_ACTORS: for spec in WATER_SOURCE_ACTORS:
spawn_demo_actor(spec, height_values, materials) spawn_demo_actor(spec, height_values, materials)
for spec in WEATHER_EXPOSURE_ZONES:
spawn_weather_exposure_zone(spec, height_values)
for spec in ENVIRONMENT_VARIATION_ACTORS: for spec in ENVIRONMENT_VARIATION_ACTORS:
spawn_environment_variation_actor(spec, height_values, materials) spawn_environment_variation_actor(spec, height_values, materials)
for spec in DEMO_ACTORS: for spec in DEMO_ACTORS:
+96
View File
@@ -0,0 +1,96 @@
import unreal
MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test"
EXPECTED_ZONES = {
"AGR_GZ_WeatherExposure_Ridge_01": {
"zone_id": "ground_zero_ridge_exposed",
"exposure_multiplier_min": 1.4,
"temperature_offset_max": -1.0,
},
"AGR_GZ_WeatherExposure_CoastalWind_01": {
"zone_id": "ground_zero_coastal_wind",
"exposure_multiplier_min": 1.2,
"temperature_offset_max": -0.5,
},
"AGR_GZ_WeatherExposure_DrainageCool_01": {
"zone_id": "ground_zero_drainage_cool",
"exposure_multiplier_min": 1.05,
"temperature_offset_max": -0.5,
},
}
def get_actor_label(actor):
try:
return actor.get_actor_label()
except Exception:
return actor.get_name()
def main():
if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
raise RuntimeError(f"Could not load map: {MAP_PATH}")
failures = []
actors = unreal.EditorLevelLibrary.get_all_level_actors()
zones_by_label = {
get_actor_label(actor): actor
for actor in actors
if isinstance(actor, unreal.AgrarianWeatherExposureZone)
}
if len(zones_by_label) != len(EXPECTED_ZONES):
failures.append(f"expected {len(EXPECTED_ZONES)} weather exposure zones, found {len(zones_by_label)}")
for label, expected in EXPECTED_ZONES.items():
zone = zones_by_label.get(label)
if not zone:
failures.append(f"missing weather exposure zone: {label}")
continue
zone_id = str(zone.get_editor_property("exposure_zone_id"))
if zone_id != expected["zone_id"]:
failures.append(f"{label} zone id expected {expected['zone_id']}, got {zone_id}")
exposure_multiplier = zone.get_editor_property("exposure_multiplier")
if exposure_multiplier < expected["exposure_multiplier_min"]:
failures.append(
f"{label} exposure multiplier expected >= {expected['exposure_multiplier_min']}, got {exposure_multiplier}"
)
temperature_offset_c = zone.get_editor_property("temperature_offset_c")
if temperature_offset_c > expected["temperature_offset_max"]:
failures.append(
f"{label} temperature offset expected <= {expected['temperature_offset_max']}, got {temperature_offset_c}"
)
if not zone.exposure_volume:
failures.append(f"{label} has no exposure volume")
continue
extent = zone.exposure_volume.get_unscaled_box_extent()
if min(extent.x, extent.y, extent.z) <= 0.0:
failures.append(f"{label} has invalid exposure volume extent {extent}")
roadmap = unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir()) + "AGRARIAN_DEVELOPMENT_ROADMAP.md"
technical_design = unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir()) + "Docs/TechnicalDesignDocument.md"
for path, snippet in [
(roadmap, "[x] Add weather exposure zones if needed"),
(technical_design, "Weather exposure zones"),
]:
with open(path, "r", encoding="utf-8") as handle:
text = handle.read()
if snippet not in text:
failures.append(f"{path} missing `{snippet}`")
if failures:
raise RuntimeError("Weather exposure zone verification failed: " + "; ".join(failures))
unreal.log(
"Weather exposure zone verification complete: "
f"{len(zones_by_label)} zones with exposure multipliers and temperature offsets."
)
main()
+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("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("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("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("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("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)); 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("Thirst: %.0f"), Survival.Thirst), X, Y);
DrawLine(FString::Printf(TEXT("Temp: %.1f C"), Survival.BodyTemperature), 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("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("Injury: %.0f"), Survival.InjurySeverity), X, Y);
DrawLine(FString::Printf(TEXT("Sick: %.0f"), Survival.SicknessSeverity), X, Y); DrawLine(FString::Printf(TEXT("Sick: %.0f"), Survival.SicknessSeverity), X, Y);
const FAgrarianCareHistorySnapshot& Care = SurvivalComponent->CareHistory; const FAgrarianCareHistorySnapshot& Care = SurvivalComponent->CareHistory;
@@ -3,6 +3,7 @@
#include "AgrarianSurvivalComponent.h" #include "AgrarianSurvivalComponent.h"
#include "AgrarianGameState.h" #include "AgrarianGameState.h"
#include "AgrarianShelterActor.h" #include "AgrarianShelterActor.h"
#include "AgrarianWeatherExposureZone.h"
#include "Components/BoxComponent.h" #include "Components/BoxComponent.h"
#include "Engine/World.h" #include "Engine/World.h"
#include "Net/UnrealNetwork.h" #include "Net/UnrealNetwork.h"
@@ -35,6 +36,8 @@ void UAgrarianSurvivalComponent::TickComponent(float DeltaTime, ELevelTick TickT
Survival.Thirst -= ThirstDecayPerMinute * Minutes; Survival.Thirst -= ThirstDecayPerMinute * Minutes;
Survival.Stamina += StaminaRecoveryPerSecond * DeltaTime; Survival.Stamina += StaminaRecoveryPerSecond * DeltaTime;
CurrentWeatherProtection = CalculateCurrentWeatherProtection(); CurrentWeatherProtection = CalculateCurrentWeatherProtection();
CurrentWeatherExposureMultiplier = CalculateCurrentWeatherExposureMultiplier();
CurrentWeatherTemperatureOffsetC = CalculateCurrentWeatherTemperatureOffsetC();
CareHistory.ShelterQuality = FMath::FInterpTo(CareHistory.ShelterQuality, CurrentWeatherProtection, DeltaTime, 0.02f); CareHistory.ShelterQuality = FMath::FInterpTo(CareHistory.ShelterQuality, CurrentWeatherProtection, DeltaTime, 0.02f);
if (Survival.Stamina <= LowStaminaExhaustionThreshold) if (Survival.Stamina <= LowStaminaExhaustionThreshold)
@@ -62,7 +65,8 @@ void UAgrarianSurvivalComponent::TickComponent(float DeltaTime, ELevelTick TickT
if (const AAgrarianGameState* AgrarianGameState = World->GetGameState<AAgrarianGameState>()) if (const AAgrarianGameState* AgrarianGameState = World->GetGameState<AAgrarianGameState>())
{ {
const float ExposureProtectionMultiplier = 1.0f - FMath::Clamp(CurrentWeatherProtection, 0.0f, 1.0f); 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); 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) 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) if (Survival.SicknessSeverity >= 60.0f)
@@ -98,6 +102,8 @@ void UAgrarianSurvivalComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProp
DOREPLIFETIME(UAgrarianSurvivalComponent, Survival); DOREPLIFETIME(UAgrarianSurvivalComponent, Survival);
DOREPLIFETIME(UAgrarianSurvivalComponent, CareHistory); DOREPLIFETIME(UAgrarianSurvivalComponent, CareHistory);
DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherProtection); DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherProtection);
DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherExposureMultiplier);
DOREPLIFETIME(UAgrarianSurvivalComponent, CurrentWeatherTemperatureOffsetC);
} }
bool UAgrarianSurvivalComponent::IsAlive() const bool UAgrarianSurvivalComponent::IsAlive() const
@@ -261,6 +267,66 @@ float UAgrarianSurvivalComponent::CalculateCurrentWeatherProtection() const
return BestProtection; 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() void UAgrarianSurvivalComponent::OnRep_Survival()
{ {
BroadcastSurvivalChanged(); BroadcastSurvivalChanged();
@@ -33,6 +33,12 @@ public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = "Agrarian|Survival|Shelter") UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = "Agrarian|Survival|Shelter")
float CurrentWeatherProtection = 0.0f; 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")) UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Survival|Rates", meta = (ClampMin = "0"))
float HungerDecayPerMinute = 0.55f; float HungerDecayPerMinute = 0.55f;
@@ -108,6 +114,12 @@ public:
UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Shelter") UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Shelter")
float CalculateCurrentWeatherProtection() const; float CalculateCurrentWeatherProtection() const;
UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Weather Exposure")
float CalculateCurrentWeatherExposureMultiplier() const;
UFUNCTION(BlueprintPure, Category = "Agrarian|Survival|Weather Exposure")
float CalculateCurrentWeatherTemperatureOffsetC() const;
protected: protected:
UFUNCTION() UFUNCTION()
void OnRep_Survival(); 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;
};