diff --git a/src/main/java/com/livingworld/modules/recovery/RecoveryModule.java b/src/main/java/com/livingworld/modules/recovery/RecoveryModule.java index cd59566..fadf0ea 100644 --- a/src/main/java/com/livingworld/modules/recovery/RecoveryModule.java +++ b/src/main/java/com/livingworld/modules/recovery/RecoveryModule.java @@ -46,9 +46,10 @@ public final class RecoveryModule implements SimulationModule { /** Extra recovery progress per point of ecosystemHealth above 50. */ private static final double HEALTH_PROGRESS_BONUS = 0.02; /** Damage accumulated per tick when conditions are badly violated. */ - private static final double DAMAGE_PER_BAD_TICK = 3.0; - /** Damage decays this fraction per tick when conditions are OK. */ - private static final double DAMAGE_DECAY_RATE = 0.05; + private static final double DAMAGE_PER_BAD_TICK = 5.0; + /** Damage decays this fraction per tick when conditions are OK. + * Kept low so damage sticks around during oscillating conditions. */ + private static final double DAMAGE_DECAY_RATE = 0.03; private static final double CHANGE_THRESHOLD = 0.01; private static final ModuleMetadata METADATA = new ModuleMetadata( @@ -112,10 +113,12 @@ public final class RecoveryModule implements SimulationModule { // Damage decays passively when conditions are good (bypass accumulateDamage // to avoid triggering regression with a zero-damage call that might fire at 70+). + // Use 4-arg constructor to preserve the dynamic succession cap. data = new RecoveryRegionData( data.getSuccessionStage(), data.getRecoveryProgress(), - Math.max(0.0, data.getDamageAccumulation() * (1.0 - DAMAGE_DECAY_RATE))); + Math.max(0.0, data.getDamageAccumulation() * (1.0 - DAMAGE_DECAY_RATE)), + data.getMaxSuccessionStage()); } else if (data.getSuccessionStage().conditionsMissedForRegression(soil, poll, veg)) { // Conditions are bad: accumulate damage toward regression. diff --git a/src/main/java/com/livingworld/modules/vegetation/VegetationModule.java b/src/main/java/com/livingworld/modules/vegetation/VegetationModule.java index c978aea..d28e24d 100644 --- a/src/main/java/com/livingworld/modules/vegetation/VegetationModule.java +++ b/src/main/java/com/livingworld/modules/vegetation/VegetationModule.java @@ -53,8 +53,15 @@ public final class VegetationModule implements SimulationModule { // --- die-off thresholds --- private static final double DIEOFF_SOIL_THRESHOLD = 20.0; private static final double DIEOFF_POLLUTION_THRESHOLD = 30.0; - private static final double GRASS_DIEOFF_RATE = 0.30; - private static final double DEAD_ACCUMULATION_RATE = 0.20; + // All living tiers die in bad conditions; trees die slowest. + private static final double GRASS_DIEOFF_RATE = 1.00; + private static final double FLOWER_DIEOFF_RATE = 0.50; + private static final double SHRUB_DIEOFF_RATE = 0.25; + private static final double TREE_DIEOFF_RATE = 0.10; + private static final double DEAD_ACCUMULATION_RATE = 0.50; + // Trees also die-off when conditions are severe (heavy pollution or near-zero soil). + private static final double TREE_SEVERE_POLLUTION = 60.0; + private static final double TREE_SEVERE_SOIL = 10.0; // --- decomposition --- private static final double DEAD_DECOMPOSITION_RATE = 0.01; @@ -131,6 +138,13 @@ public final class VegetationModule implements SimulationModule { if (badConditions) { data.setGrassPressure(data.getGrassPressure() - GRASS_DIEOFF_RATE); + data.setFlowerPressure(data.getFlowerPressure() - FLOWER_DIEOFF_RATE); + data.setShrubPressure(data.getShrubPressure() - SHRUB_DIEOFF_RATE); + // Trees die only under severe stress to reflect their resilience. + if (metrics.getPollutionScore() > TREE_SEVERE_POLLUTION + || metrics.getSoilQuality() < TREE_SEVERE_SOIL) { + data.setTreePressure(data.getTreePressure() - TREE_DIEOFF_RATE); + } data.setDeadVegetation(data.getDeadVegetation() + DEAD_ACCUMULATION_RATE); }