Add /lw demo degrade/recover for one-command ecosystem transformation demo
EcoDemoCommand provides two complete condition presets applied in one command:
/lw demo degrade — forces stage=MATURE_FOREST with full lush vegetation,
then applies dead soil (fertility=5, contamination=8,
erosion=8), severe drought (availability=5, risk=90),
and clean air. soilQuality collapses to ~0, triggering
VegetationModule badConditions and RecoveryModule
conditionsMissedForRegression every cycle. Region
visibly regresses toward BARREN.
/lw demo recover — caps succession at MATURE_FOREST, resets soil to
fertility=85/contamination=0/erosion=0, water to
availability=85/drought=5, seeds minimal grass.
soilQuality immediately hits ~85, unlocking full
vegetation succession chain and rapid stage advancement.
Full demo sequence:
/lw speed 20
/lw demo degrade (watch ~30s: MATURE_FOREST → BARREN)
/lw demo recover
/lw speed 50 (watch ~30s: BARREN → MATURE_FOREST)
/lw speed reset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
package com.livingworld.commands;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.livingworld.core.LivingWorldConstants;
|
||||
import com.livingworld.modules.atmosphere.AtmosphereModule;
|
||||
import com.livingworld.modules.atmosphere.AtmosphereRegionData;
|
||||
import com.livingworld.modules.pollution.PollutionModule;
|
||||
import com.livingworld.modules.pollution.PollutionRegionData;
|
||||
import com.livingworld.modules.recovery.RecoveryModule;
|
||||
import com.livingworld.modules.recovery.RecoveryRegionData;
|
||||
import com.livingworld.modules.recovery.SuccessionStage;
|
||||
import com.livingworld.modules.soil.SoilModule;
|
||||
import com.livingworld.modules.soil.SoilRegionData;
|
||||
import com.livingworld.modules.vegetation.VegetationModule;
|
||||
import com.livingworld.modules.vegetation.VegetationRegionData;
|
||||
import com.livingworld.modules.water.WaterModule;
|
||||
import com.livingworld.modules.water.WaterRegionData;
|
||||
import com.livingworld.regions.Region;
|
||||
import com.livingworld.regions.RegionCoordinate;
|
||||
import com.livingworld.regions.RegionManager;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
/**
|
||||
* One-command demo presets for watching ecosystem transformation.
|
||||
*
|
||||
* <pre>
|
||||
* /lw demo degrade — forces MATURE_FOREST then applies severe drought + dead soil
|
||||
* so the region visibly regresses toward BARREN.
|
||||
* Recommended speed: /lw speed 20
|
||||
*
|
||||
* /lw demo recover — resets soil and water to lush conditions so the region
|
||||
* advances from wherever it is back toward MATURE_FOREST.
|
||||
* Recommended speed: /lw speed 50
|
||||
* </pre>
|
||||
*/
|
||||
public final class EcoDemoCommand {
|
||||
|
||||
private EcoDemoCommand() {}
|
||||
|
||||
public static LiteralArgumentBuilder<CommandSourceStack> build(Supplier<RegionManager> rm) {
|
||||
return Commands.literal("demo")
|
||||
.then(Commands.literal("degrade")
|
||||
.executes(ctx -> runDegrade(ctx.getSource(), rm.get())))
|
||||
.then(Commands.literal("recover")
|
||||
.executes(ctx -> runRecover(ctx.getSource(), rm.get())));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Presets
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private static int runDegrade(CommandSourceStack src, RegionManager rm) {
|
||||
return applyPreset(src, rm, "DEGRADE", region -> {
|
||||
// Force starting point: peak forest.
|
||||
region.getModuleData().put(RecoveryModule.MODULE_ID,
|
||||
new RecoveryRegionData(SuccessionStage.MATURE_FOREST, 0.0, 0.0,
|
||||
SuccessionStage.MATURE_FOREST));
|
||||
|
||||
// Lush vegetation so it visibly dies rather than starting at zero.
|
||||
region.getModuleData().put(VegetationModule.MODULE_ID,
|
||||
new VegetationRegionData(100.0, 80.0, 80.0, 70.0, 0.0));
|
||||
|
||||
// Dead soil: near-zero fertility, high erosion and contamination.
|
||||
// soilQuality ≈ 5 − 8×0.4 − 8×0.3 = 5 − 3.2 − 2.4 = −0.6 → 0
|
||||
// That floor triggers badConditions in VegetationModule and
|
||||
// conditionsMissedForRegression in RecoveryModule every cycle.
|
||||
SoilRegionData soil = SoilRegionData.defaults();
|
||||
soil.setFertility(5.0);
|
||||
soil.setMoisture(5.0);
|
||||
soil.setContamination(8.0);
|
||||
soil.setErosion(8.0);
|
||||
region.getModuleData().put(SoilModule.MODULE_ID, soil);
|
||||
|
||||
// Severe drought — water availability near zero.
|
||||
WaterRegionData water = WaterRegionData.defaults();
|
||||
water.setWaterAvailability(5.0);
|
||||
water.setDroughtRisk(90.0);
|
||||
water.setPurificationCapacity(5.0);
|
||||
region.getModuleData().put(WaterModule.MODULE_ID, water);
|
||||
|
||||
// Clear pollution so it doesn't confuse the read-out (soil drives the decay here).
|
||||
region.getModuleData().put(PollutionModule.MODULE_ID, PollutionRegionData.defaults());
|
||||
|
||||
// Dry sky: low rain so per-player weather reflects the drought.
|
||||
AtmosphereRegionData atm = new AtmosphereRegionData(0.05, 0.0);
|
||||
region.getModuleData().put(AtmosphereModule.MODULE_ID, atm);
|
||||
}, "Stage forced to MATURE_FOREST | Soil destroyed | Drought=90 | Use /lw speed 20 to watch");
|
||||
}
|
||||
|
||||
private static int runRecover(CommandSourceStack src, RegionManager rm) {
|
||||
return applyPreset(src, rm, "RECOVER", region -> {
|
||||
// Raise cap — region must be allowed to reach MATURE_FOREST again.
|
||||
RecoveryRegionData rec = region.getModuleData()
|
||||
.get(RecoveryModule.MODULE_ID, RecoveryRegionData.class)
|
||||
.orElseGet(RecoveryRegionData::defaults);
|
||||
rec.setMaxSuccessionStage(SuccessionStage.MATURE_FOREST);
|
||||
region.getModuleData().put(RecoveryModule.MODULE_ID, rec);
|
||||
|
||||
// Rich, clean soil — contamination and erosion both zeroed.
|
||||
SoilRegionData soil = SoilRegionData.defaults();
|
||||
soil.setFertility(85.0);
|
||||
soil.setMoisture(75.0);
|
||||
soil.setContamination(0.0);
|
||||
soil.setErosion(0.0);
|
||||
region.getModuleData().put(SoilModule.MODULE_ID, soil);
|
||||
|
||||
// Plentiful water, no drought.
|
||||
WaterRegionData water = WaterRegionData.defaults();
|
||||
water.setWaterAvailability(85.0);
|
||||
water.setDroughtRisk(5.0);
|
||||
water.setPurificationCapacity(80.0);
|
||||
region.getModuleData().put(WaterModule.MODULE_ID, water);
|
||||
|
||||
// Clean air.
|
||||
region.getModuleData().put(PollutionModule.MODULE_ID, PollutionRegionData.defaults());
|
||||
|
||||
// Good rain signal in the atmosphere.
|
||||
AtmosphereRegionData atm = new AtmosphereRegionData(0.75, 0.0);
|
||||
region.getModuleData().put(AtmosphereModule.MODULE_ID, atm);
|
||||
|
||||
// Seed minimal grass so vegetation succession has something to start from.
|
||||
// (Shrubs and trees grow naturally from here.)
|
||||
VegetationRegionData veg = region.getModuleData()
|
||||
.get(VegetationModule.MODULE_ID, VegetationRegionData.class)
|
||||
.orElseGet(VegetationRegionData::defaults);
|
||||
veg.setGrassPressure(Math.max(veg.getGrassPressure(), 5.0));
|
||||
veg.setDeadVegetation(0.0);
|
||||
region.getModuleData().put(VegetationModule.MODULE_ID, veg);
|
||||
}, "Cap raised to MATURE_FOREST | Soil+water restored | Use /lw speed 50 to watch");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private static int applyPreset(CommandSourceStack src, RegionManager rm,
|
||||
String label, RegionMutation mutation, String hint) {
|
||||
if (rm == null) { src.sendFailure(Component.literal("Region manager not ready.")); return 0; }
|
||||
ServerPlayer player;
|
||||
try { player = src.getPlayerOrException(); }
|
||||
catch (CommandSyntaxException e) {
|
||||
src.sendFailure(Component.literal("/lw demo must be run by a player."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
String dimId = player.level().dimension().location().toString();
|
||||
int rx = (int) Math.floor(player.getX() / (LivingWorldConstants.DEFAULT_REGION_SIZE_CHUNKS * 16.0));
|
||||
int rz = (int) Math.floor(player.getZ() / (LivingWorldConstants.DEFAULT_REGION_SIZE_CHUNKS * 16.0));
|
||||
RegionCoordinate coord = new RegionCoordinate(dimId, rx, rz);
|
||||
|
||||
Region region = rm.getOrCreateRegion(coord);
|
||||
mutation.apply(region);
|
||||
rm.markDirty(region);
|
||||
|
||||
src.sendSuccess(() -> Component.literal(
|
||||
"[LW] Demo:" + label + " applied to (" + rx + "," + rz + ") — " + hint), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface RegionMutation { void apply(Region region); }
|
||||
}
|
||||
@@ -143,7 +143,8 @@ public final class LivingWorldCommandRoot {
|
||||
false);
|
||||
return 1;
|
||||
})))
|
||||
.then(RegionSetCommand.build(regionManager)));
|
||||
.then(RegionSetCommand.build(regionManager))
|
||||
.then(EcoDemoCommand.build(regionManager)));
|
||||
}
|
||||
|
||||
private static int toggleHud(CommandSourceStack source, Function<UUID, Boolean> hudToggle) {
|
||||
|
||||
Reference in New Issue
Block a user