Public Access
Implement Legacy-inspired Age status instability effects (#67)
Build / build (push) Canceled after 0s
Build / build (push) Canceled after 0s
This commit is contained in:
1 parent
8b32735c2e
commit
066ef2b758
7 files changed
+360
-2
No files matched your search
@@ -22,6 +22,7 @@ import net.dnorberg.rivencraft.compat.SereneSeasonsCompatibility;
|
||||
import net.dnorberg.rivencraft.book.AgeDimensionService;
|
||||
import net.dnorberg.rivencraft.book.AgeWeatherService;
|
||||
import net.dnorberg.rivencraft.book.AgeInstabilityRuntime;
|
||||
import net.dnorberg.rivencraft.book.AgeStatusEffectProviders;
|
||||
import net.dnorberg.rivencraft.book.AgeRuntimeProfiles;
|
||||
import net.dnorberg.rivencraft.writing.SymbolCatalog;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
@@ -38,6 +39,7 @@ public final class Rivencraft {
|
||||
|
||||
public Rivencraft(IEventBus modEventBus, ModContainer modContainer) {
|
||||
AgeDimensionService.register();
|
||||
AgeStatusEffectProviders.register();
|
||||
modContainer.registerConfig(ModConfig.Type.SERVER, RivencraftConfig.SPEC);
|
||||
modContainer.registerConfig(ModConfig.Type.CLIENT, RivencraftClientConfig.SPEC);
|
||||
modEventBus.addListener(ModNetworking::register);
|
||||
@@ -54,12 +56,17 @@ public final class Rivencraft {
|
||||
NeoForge.EVENT_BUS.addListener(BookStandConversionHandler::onRightClickBlock);
|
||||
NeoForge.EVENT_BUS.addListener(AgeWeatherService::onLevelTickPost);
|
||||
NeoForge.EVENT_BUS.addListener(AgeInstabilityRuntime::onLevelTickPost);
|
||||
NeoForge.EVENT_BUS.addListener(AgeStatusEffectProviders::onServerTickPost);
|
||||
NeoForge.EVENT_BUS.addListener(AgeStatusEffectProviders::onEntityTravel);
|
||||
NeoForge.EVENT_BUS.addListener(AgeStatusEffectProviders::onPlayerChangedDimension);
|
||||
NeoForge.EVENT_BUS.addListener(AgeStatusEffectProviders::onPlayerLoggedOut);
|
||||
NeoForge.EVENT_BUS.addListener(SereneSeasonsCompatibility::onLevelTickPost);
|
||||
NeoForge.EVENT_BUS.addListener(AgeCelestialAngleSync::onPlayerLoggedIn);
|
||||
NeoForge.EVENT_BUS.addListener(AgeCelestialAngleSync::onPlayerChangedDimension);
|
||||
NeoForge.EVENT_BUS.addListener(AgeLifecycleEvents::onPlayerLoggedOut);
|
||||
NeoForge.EVENT_BUS.addListener(AgeLifecycleEvents::onServerStopping);
|
||||
NeoForge.EVENT_BUS.addListener(AgeInstabilityRuntime::onServerStopping);
|
||||
NeoForge.EVENT_BUS.addListener(AgeStatusEffectProviders::onServerStopping);
|
||||
NeoForge.EVENT_BUS.addListener(RivencraftCommands::register);
|
||||
NeoForge.EVENT_BUS.addListener(AgeRuntimeProfiles::register);
|
||||
NeoForge.EVENT_BUS.addListener(SymbolCatalog::register);
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package net.dnorberg.rivencraft.book;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.config.RivencraftConfig;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.neoforged.neoforge.gametest.GameTestHolder;
|
||||
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
|
||||
|
||||
/** Focused mapping, targeting, and isolation checks for issue #67. */
|
||||
@GameTestHolder(Rivencraft.MOD_ID)
|
||||
@PrefixGameTestTemplate(false)
|
||||
public final class AgeStatusEffectGameTests {
|
||||
private AgeStatusEffectGameTests() {}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void legacyPotionMappingRetainsShortDurationsAndSkyRule(GameTestHelper helper) {
|
||||
helper.assertValueEqual(AgeStatusEffectProviders.legacyDuration(id("wither")), 30,
|
||||
"Legacy Wither lasts only 30 ticks per application");
|
||||
helper.assertValueEqual(AgeStatusEffectProviders.legacyDuration(id("poison_global")), 80,
|
||||
"Global Poison retains Legacy's 80-tick duration");
|
||||
helper.assertValueEqual(AgeStatusEffectProviders.legacyDuration(id("enemyregen_global")), 200,
|
||||
"The enemy regeneration card retains its 200-tick duration");
|
||||
helper.assertTrue(AgeStatusEffectProviders.localRequiresSky(id("poison"))
|
||||
&& !AgeStatusEffectProviders.localRequiresSky(id("poison_global")),
|
||||
"Only local potion cards require open sky");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void generalDebuffsIncludeLivingEntitiesButCreativePlayersAreExempt(GameTestHelper helper) {
|
||||
var player = helper.makeMockPlayer(GameType.SURVIVAL);
|
||||
var cow = helper.spawnWithNoFreeWill(EntityType.COW, new BlockPos(1, 1, 1));
|
||||
var hostile = helper.spawnWithNoFreeWill(EntityType.SPIDER, new BlockPos(2, 1, 1));
|
||||
helper.assertTrue(player.isAlive(), "Mock survival player must be alive");
|
||||
helper.assertTrue(player.level() == helper.getLevel(), "Mock survival player must be in the test level");
|
||||
helper.assertTrue(!player.isCreative() && !player.isSpectator(),
|
||||
"Mock survival player must not be creative or spectator");
|
||||
helper.assertTrue(RivencraftConfig.statusEffectsPlayers(), "Player status effects must be enabled");
|
||||
helper.assertTrue(AgeStatusEffectProviders.eligibleTarget(helper.getLevel(), player, id("poison_global")),
|
||||
"A survival player is eligible for a general Legacy debuff");
|
||||
helper.assertTrue(AgeStatusEffectProviders.eligibleTarget(helper.getLevel(), cow, id("poison_global")),
|
||||
"General Legacy debuffs are not limited to players");
|
||||
helper.assertTrue(hostile.isAlive() && hostile.level() == helper.getLevel(),
|
||||
"Mock hostile mob must be alive in the test level");
|
||||
helper.assertTrue(RivencraftConfig.statusEffectsHostiles(), "Hostile status effects must be enabled");
|
||||
helper.assertTrue(hostile.getEffect(MobEffects.REGENERATION) == null,
|
||||
"Mock hostile mob must not already have regeneration");
|
||||
helper.assertTrue(hostile.canBeAffected(new MobEffectInstance(MobEffects.REGENERATION, 200, 0)),
|
||||
"Mock hostile mob must accept regeneration");
|
||||
helper.assertTrue(AgeStatusEffectProviders.eligibleTarget(helper.getLevel(), hostile,
|
||||
id("enemyregen_global")), "Hostile mobs can receive the enemy buff");
|
||||
helper.assertTrue(!AgeStatusEffectProviders.eligibleTarget(helper.getLevel(), cow,
|
||||
id("enemyregen_global")), "Passive mobs do not receive the adapted enemy buff");
|
||||
var creative = helper.makeMockPlayer(GameType.CREATIVE);
|
||||
helper.assertTrue(!AgeStatusEffectProviders.eligibleTarget(helper.getLevel(), creative,
|
||||
id("poison_global")), "Creative players must be exempt");
|
||||
var spectator = helper.makeMockPlayer(GameType.SPECTATOR);
|
||||
helper.assertTrue(!AgeStatusEffectProviders.eligibleTarget(helper.getLevel(), spectator,
|
||||
id("poison_global")), "Spectators must be exempt");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void overworldCannotRunAnAgeStatusProvider(GameTestHelper helper) {
|
||||
var player = helper.makeMockServerPlayerInLevel();
|
||||
player.setGameMode(GameType.SURVIVAL);
|
||||
UUID ageId = UUID.randomUUID();
|
||||
AgeDefinition age = new AgeDefinition(ageId, "Isolated", 100L, AgeGenerationProfiles.STANDARD,
|
||||
AgeDefinition.CURRENT_GENERATION_VERSION, 0L, AgeDefinition.dimensionId(ageId), null,
|
||||
AgeTraits.LEGACY_DEFAULTS);
|
||||
AgeInstabilityRuntime.Budget budget = new AgeInstabilityRuntime.Budget(1);
|
||||
var context = new AgeInstabilityRuntime.Context(helper.getLevel(), age,
|
||||
List.of(player.chunkPosition()), helper.getLevel().getServer().getTickCount(), budget);
|
||||
AgeStatusEffectProviders.apply(context, id("poison_global"), 1);
|
||||
helper.assertTrue(player.getEffect(MobEffects.POISON) == null,
|
||||
"An Age provider must not apply in the Overworld");
|
||||
helper.assertValueEqual(budget.remaining(), 1,
|
||||
"A rejected cross-world attempt must not consume the Age budget");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package net.dnorberg.rivencraft.book;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.WeakHashMap;
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.config.RivencraftConfig;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.neoforged.neoforge.event.entity.EntityTravelToDimensionEvent;
|
||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
|
||||
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
||||
|
||||
/** Legacy potion cards adapted to bounded, Age-local server effects. */
|
||||
public final class AgeStatusEffectProviders {
|
||||
private static final int MAX_TRACKED_AGES = 64;
|
||||
private static final Map<ResourceLocation, Spec> SPECS = createSpecs();
|
||||
private static final Map<ServerLevel, List<Lease>> LEASES = new WeakHashMap<>();
|
||||
|
||||
private AgeStatusEffectProviders() {}
|
||||
|
||||
public static void register() {
|
||||
SPECS.keySet().forEach(id -> AgeInstabilityRuntime.register(id, false, AgeStatusEffectProviders::apply));
|
||||
}
|
||||
|
||||
static void apply(AgeInstabilityRuntime.Context context, ResourceLocation id, int level) {
|
||||
if (!RivencraftConfig.statusEffectsEnabled()
|
||||
|| !AgeInstabilityRuntime.isAgeDimension(context.level().dimension().location())
|
||||
|| !context.age().dimensionId().equals(context.level().dimension().location())) return;
|
||||
Spec spec = SPECS.get(id);
|
||||
if (spec == null || spec.target() == Target.HOSTILE && !RivencraftConfig.statusEffectsHostiles()
|
||||
|| spec.target() == Target.GENERAL && !RivencraftConfig.statusEffectsPlayers()
|
||||
&& !RivencraftConfig.statusEffectsNonplayers()) return;
|
||||
List<Lease> leases = LEASES.get(context.level());
|
||||
if (leases == null) {
|
||||
if (LEASES.size() >= MAX_TRACKED_AGES) return;
|
||||
leases = new ArrayList<>();
|
||||
LEASES.put(context.level(), leases);
|
||||
}
|
||||
prune(context.level(), leases, context.serverTick(), false);
|
||||
if (leases.size() >= RivencraftConfig.statusEffectsMaxActive()) return;
|
||||
|
||||
Random random = new Random(context.age().seed() ^ (context.serverTick() * 0x9E3779B97F4A7C15L)
|
||||
^ id.hashCode());
|
||||
ChunkPos chunk = context.loadedChunks().get(random.nextInt(context.loadedChunks().size()));
|
||||
AABB bounds = new AABB(chunk.getMinBlockX(), context.level().getMinBuildHeight(), chunk.getMinBlockZ(),
|
||||
chunk.getMaxBlockX() + 1, context.level().getMaxBuildHeight(), chunk.getMaxBlockZ() + 1);
|
||||
int duration = Math.min(spec.legacyDuration(), RivencraftConfig.statusEffectsMaxDuration());
|
||||
int amplifier = Math.max(0, level - 1);
|
||||
MobEffectInstance instance = new MobEffectInstance(spec.effect(), duration, amplifier);
|
||||
List<LivingEntity> candidates = new ArrayList<>();
|
||||
context.level().getEntities(EntityTypeTest.forClass(LivingEntity.class), bounds,
|
||||
entity -> eligible(context.level(), entity, spec, instance), candidates,
|
||||
RivencraftConfig.statusEffectsMaxCandidates());
|
||||
if (candidates.isEmpty()) return;
|
||||
candidates.sort(Comparator.comparing(Entity::getUUID));
|
||||
LivingEntity target = candidates.get(random.nextInt(candidates.size()));
|
||||
if (!context.budget().claim() || !target.addEffect(instance)) return;
|
||||
leases.add(new Lease(new WeakReference<>(target), instance,
|
||||
context.serverTick() + duration, spec.target()));
|
||||
}
|
||||
|
||||
private static boolean eligible(ServerLevel level, LivingEntity entity, Spec spec,
|
||||
MobEffectInstance instance) {
|
||||
if (!entity.isAlive() || entity.isRemoved() || entity.level() != level
|
||||
|| entity.getEffect(spec.effect()) != null || !entity.canBeAffected(instance)
|
||||
|| !spec.global() && !level.canSeeSky(entity.blockPosition())) return false;
|
||||
return allowedByPolicy(spec.target(), entity);
|
||||
}
|
||||
|
||||
private static boolean allowedByPolicy(Target target, LivingEntity entity) {
|
||||
if (entity instanceof Player player) {
|
||||
return target == Target.GENERAL && RivencraftConfig.statusEffectsPlayers()
|
||||
&& !player.isCreative() && !player.isSpectator();
|
||||
}
|
||||
return target == Target.GENERAL ? RivencraftConfig.statusEffectsNonplayers()
|
||||
: entity instanceof Monster && RivencraftConfig.statusEffectsHostiles();
|
||||
}
|
||||
|
||||
public static void onServerTickPost(ServerTickEvent.Post event) {
|
||||
if (event.getServer().getTickCount() % 20 != 0) return;
|
||||
boolean disabled = !RivencraftConfig.instabilityEnabled() || !RivencraftConfig.statusEffectsEnabled();
|
||||
long tick = event.getServer().getTickCount();
|
||||
for (var entry : LEASES.entrySet()) prune(entry.getKey(), entry.getValue(), tick, disabled);
|
||||
LEASES.values().removeIf(List::isEmpty);
|
||||
}
|
||||
|
||||
public static void onEntityTravel(EntityTravelToDimensionEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
if (entity instanceof LivingEntity living && !entity.level().dimension().equals(event.getDimension())) {
|
||||
clearEntity(living);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
|
||||
if (event.getEntity() instanceof ServerPlayer player) clearEntity(player);
|
||||
}
|
||||
|
||||
public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) {
|
||||
if (event.getEntity() instanceof ServerPlayer player) clearEntity(player);
|
||||
}
|
||||
|
||||
public static void onServerStopping(ServerStoppingEvent event) {
|
||||
long tick = event.getServer().getTickCount();
|
||||
for (var entry : LEASES.entrySet()) prune(entry.getKey(), entry.getValue(), tick, true);
|
||||
LEASES.clear();
|
||||
}
|
||||
|
||||
private static void clearEntity(LivingEntity entity) {
|
||||
for (List<Lease> leases : LEASES.values()) {
|
||||
Iterator<Lease> iterator = leases.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Lease lease = iterator.next();
|
||||
if (lease.entity().get() != entity) continue;
|
||||
removeOwnedEffect(entity, lease);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void prune(ServerLevel level, List<Lease> leases, long tick, boolean removeAll) {
|
||||
Iterator<Lease> iterator = leases.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Lease lease = iterator.next();
|
||||
LivingEntity entity = lease.entity().get();
|
||||
if (entity == null || entity.isRemoved() || entity.getEffect(lease.instance().getEffect()) == null) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
boolean revoke = removeAll || entity.level() != level
|
||||
|| !allowedByPolicy(lease.target(), entity);
|
||||
if (revoke || tick >= lease.expiresAt()) {
|
||||
removeOwnedEffect(entity, lease);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeOwnedEffect(LivingEntity entity, Lease lease) {
|
||||
// addEffect stores the supplied instance when no effect exists. Do not remove
|
||||
// a replacement from another mod (or a stronger/extended effect).
|
||||
if (entity.getEffect(lease.instance().getEffect()) == lease.instance()
|
||||
&& lease.instance().getAmplifier() == lease.amplifier()
|
||||
&& lease.instance().getDuration() <= lease.originalDuration()) {
|
||||
entity.removeEffect(lease.instance().getEffect());
|
||||
}
|
||||
}
|
||||
|
||||
static int legacyDuration(ResourceLocation id) {
|
||||
Spec spec = SPECS.get(id);
|
||||
return spec == null ? -1 : spec.legacyDuration();
|
||||
}
|
||||
|
||||
static boolean localRequiresSky(ResourceLocation id) {
|
||||
Spec spec = SPECS.get(id);
|
||||
return spec != null && !spec.global();
|
||||
}
|
||||
|
||||
static boolean eligibleTarget(ServerLevel level, LivingEntity entity, ResourceLocation id) {
|
||||
Spec spec = SPECS.get(id);
|
||||
return spec != null && eligible(level, entity, spec,
|
||||
new MobEffectInstance(spec.effect(), spec.legacyDuration(), 0));
|
||||
}
|
||||
|
||||
private static Map<ResourceLocation, Spec> createSpecs() {
|
||||
Map<ResourceLocation, Spec> specs = new HashMap<>();
|
||||
addPair(specs, "blindness", MobEffects.BLINDNESS, 60);
|
||||
addPair(specs, "fatigue", MobEffects.DIG_SLOWDOWN, 80);
|
||||
addPair(specs, "hunger", MobEffects.HUNGER, 80);
|
||||
addPair(specs, "nausea", MobEffects.CONFUSION, 60);
|
||||
addPair(specs, "poison", MobEffects.POISON, 80);
|
||||
addPair(specs, "slow", MobEffects.MOVEMENT_SLOWDOWN, 80);
|
||||
addPair(specs, "weakness", MobEffects.WEAKNESS, 80);
|
||||
addPair(specs, "wither", MobEffects.WITHER, 30);
|
||||
specs.put(id("enemyregen_global"), new Spec(MobEffects.REGENERATION, 200, true, Target.HOSTILE));
|
||||
specs.put(id("enemyresist_global"), new Spec(MobEffects.DAMAGE_RESISTANCE, 200, true, Target.HOSTILE));
|
||||
return Map.copyOf(specs);
|
||||
}
|
||||
|
||||
private static void addPair(Map<ResourceLocation, Spec> specs, String name,
|
||||
Holder<MobEffect> effect, int duration) {
|
||||
specs.put(id(name), new Spec(effect, duration, false, Target.GENERAL));
|
||||
specs.put(id(name + "_global"), new Spec(effect, duration, true, Target.GENERAL));
|
||||
}
|
||||
|
||||
private static ResourceLocation id(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, path);
|
||||
}
|
||||
|
||||
private enum Target { GENERAL, HOSTILE }
|
||||
private record Spec(Holder<MobEffect> effect, int legacyDuration, boolean global, Target target) {}
|
||||
private record Lease(WeakReference<LivingEntity> entity, MobEffectInstance instance, long expiresAt,
|
||||
Target target, int amplifier, int originalDuration) {
|
||||
private Lease(WeakReference<LivingEntity> entity, MobEffectInstance instance, long expiresAt,
|
||||
Target target) {
|
||||
this(entity, instance, expiresAt, target, instance.getAmplifier(), instance.getDuration());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,13 @@ public final class RivencraftConfig {
|
||||
private static final ModConfigSpec.IntValue INSTABILITY_MAX_ACTIONS;
|
||||
private static final ModConfigSpec.IntValue INSTABILITY_MAX_LEVEL;
|
||||
private static final ModConfigSpec.IntValue INSTABILITY_MAX_CHUNKS;
|
||||
private static final ModConfigSpec.BooleanValue STATUS_EFFECTS_ENABLED;
|
||||
private static final ModConfigSpec.BooleanValue STATUS_EFFECTS_PLAYERS;
|
||||
private static final ModConfigSpec.BooleanValue STATUS_EFFECTS_NONPLAYERS;
|
||||
private static final ModConfigSpec.BooleanValue STATUS_EFFECTS_HOSTILES;
|
||||
private static final ModConfigSpec.IntValue STATUS_EFFECTS_MAX_ACTIVE;
|
||||
private static final ModConfigSpec.IntValue STATUS_EFFECTS_MAX_CANDIDATES;
|
||||
private static final ModConfigSpec.IntValue STATUS_EFFECTS_MAX_DURATION;
|
||||
|
||||
static {
|
||||
ModConfigSpec.Builder builder = new ModConfigSpec.Builder();
|
||||
@@ -66,6 +73,22 @@ public final class RivencraftConfig {
|
||||
INSTABILITY_MAX_CHUNKS = builder.comment("Maximum already-loaded candidate chunks inspected per Age run.")
|
||||
.defineInRange("max_loaded_chunks", 16, 1, 64);
|
||||
builder.pop();
|
||||
builder.push("instability_status_effects");
|
||||
STATUS_EFFECTS_ENABLED = builder.comment("Allow bounded instability status effects in loaded Ages.")
|
||||
.define("enabled", true);
|
||||
STATUS_EFFECTS_PLAYERS = builder.comment("Allow instability status effects on survival/adventure players.")
|
||||
.define("affect_players", true);
|
||||
STATUS_EFFECTS_NONPLAYERS = builder.comment("Allow general instability status effects on non-player living entities, as in Legacy.")
|
||||
.define("affect_nonplayers", true);
|
||||
STATUS_EFFECTS_HOSTILES = builder.comment("Allow regeneration and resistance on hostile mobs.")
|
||||
.define("buff_hostiles", true);
|
||||
STATUS_EFFECTS_MAX_ACTIVE = builder.comment("Maximum Rivencraft-owned active effect leases per Age.")
|
||||
.defineInRange("max_active_per_age", 32, 1, 128);
|
||||
STATUS_EFFECTS_MAX_CANDIDATES = builder.comment("Maximum living entities examined in one chosen loaded chunk.")
|
||||
.defineInRange("max_candidates_per_attempt", 8, 1, 16);
|
||||
STATUS_EFFECTS_MAX_DURATION = builder.comment("Maximum duration in ticks for one instability status effect.")
|
||||
.defineInRange("max_duration_ticks", 200, 20, 200);
|
||||
builder.pop();
|
||||
SPEC = builder.build();
|
||||
}
|
||||
|
||||
@@ -96,4 +119,11 @@ public final class RivencraftConfig {
|
||||
public static int instabilityMaxActions() { return INSTABILITY_MAX_ACTIONS.get(); }
|
||||
public static int instabilityMaxLevel() { return INSTABILITY_MAX_LEVEL.get(); }
|
||||
public static int instabilityMaxChunks() { return INSTABILITY_MAX_CHUNKS.get(); }
|
||||
public static boolean statusEffectsEnabled() { return STATUS_EFFECTS_ENABLED.get(); }
|
||||
public static boolean statusEffectsPlayers() { return STATUS_EFFECTS_PLAYERS.get(); }
|
||||
public static boolean statusEffectsNonplayers() { return STATUS_EFFECTS_NONPLAYERS.get(); }
|
||||
public static boolean statusEffectsHostiles() { return STATUS_EFFECTS_HOSTILES.get(); }
|
||||
public static int statusEffectsMaxActive() { return STATUS_EFFECTS_MAX_ACTIVE.get(); }
|
||||
public static int statusEffectsMaxCandidates() { return STATUS_EFFECTS_MAX_CANDIDATES.get(); }
|
||||
public static int statusEffectsMaxDuration() { return STATUS_EFFECTS_MAX_DURATION.get(); }
|
||||
}
|
||||
Reference in new issue
Block a user