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);
|
false);
|
||||||
return 1;
|
return 1;
|
||||||
})))
|
})))
|
||||||
.then(RegionSetCommand.build(regionManager)));
|
.then(RegionSetCommand.build(regionManager))
|
||||||
|
.then(EcoDemoCommand.build(regionManager)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int toggleHud(CommandSourceStack source, Function<UUID, Boolean> hudToggle) {
|
private static int toggleHud(CommandSourceStack source, Function<UUID, Boolean> hudToggle) {
|
||||||
|
|||||||
Reference in New Issue
Block a user