Add Ground Zero weather exposure zones
This commit is contained in:
@@ -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.
@@ -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,
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user